Skip to content

Commit 25f07f8

Browse files
authored
Add tracing to host services (#222)
Adds otel child span support for tracing host services execution
1 parent 947a70e commit 25f07f8

File tree

17 files changed

+181
-104
lines changed

17 files changed

+181
-104
lines changed

agent/agent.go

+7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import (
1919
"github.com/nats-io/nats.go"
2020
"github.com/synadia-io/nex/agent/providers"
2121
agentapi "github.com/synadia-io/nex/internal/agent-api"
22+
"go.opentelemetry.io/otel"
23+
"go.opentelemetry.io/otel/propagation"
2224
)
2325

2426
const defaultAgentHandshakeTimeoutMillis = 500
@@ -344,6 +346,11 @@ func (a *Agent) handleHealthz(w http.ResponseWriter, req *http.Request) {
344346
func (a *Agent) init() error {
345347
a.installSignalHandlers()
346348

349+
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
350+
propagation.TraceContext{},
351+
propagation.Baggage{},
352+
))
353+
347354
err := a.requestHandshake()
348355
if err != nil {
349356
a.LogError(fmt.Sprintf("Failed to handshake with node: %s", err))

agent/providers/api.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package providers
22

33
import (
4+
"context"
45
"errors"
56

67
"github.com/synadia-io/nex/agent/providers/lib"
@@ -27,7 +28,7 @@ type ExecutionProvider interface {
2728
Deploy() error
2829

2930
// Execute a deployed function, if supported by the execution provider implementation (e.g., "v8" and "wasm" types)
30-
Execute(subject string, payload []byte) ([]byte, error)
31+
Execute(ctx context.Context, payload []byte) ([]byte, error)
3132

3233
// Undeploy a workload, giving it a chance to gracefully clean up after itself (if applicable)
3334
Undeploy() error

agent/providers/lib/elf.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package lib
22

33
import (
4+
"context"
45
"debug/elf"
56
"errors"
67
"fmt"
@@ -86,7 +87,7 @@ func (e *ELF) removeWorkload() {
8687
_ = os.Remove(e.tmpFilename)
8788
}
8889

89-
func (e *ELF) Execute(subject string, payload []byte) ([]byte, error) {
90+
func (e *ELF) Execute(ctx context.Context, payload []byte) ([]byte, error) {
9091
return nil, errors.New("ELF execution provider does not support execution via trigger subjects")
9192
}
9293

agent/providers/lib/oci.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package lib
22

33
import (
4+
"context"
45
"errors"
56

67
agentapi "github.com/synadia-io/nex/internal/agent-api"
@@ -14,7 +15,7 @@ func (o *OCI) Deploy() error {
1415
return errors.New("oci execution provider not yet implemented")
1516
}
1617

17-
func (o *OCI) Execute(subject string, payload []byte) ([]byte, error) {
18+
func (o *OCI) Execute(ctx context.Context, payload []byte) ([]byte, error) {
1819
return nil, errors.New("oci execution provider does not support execution via trigger subjects")
1920
}
2021

agent/providers/lib/v8.go

+56-39
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package lib
44

55
import (
6+
"context"
67
"encoding/json"
78
"errors"
89
"fmt"
@@ -18,6 +19,8 @@ import (
1819
hostservices "github.com/synadia-io/nex/host-services"
1920
"github.com/synadia-io/nex/host-services/builtins"
2021
agentapi "github.com/synadia-io/nex/internal/agent-api"
22+
"go.opentelemetry.io/otel"
23+
"go.opentelemetry.io/otel/propagation"
2124
v8 "rogchap.com/v8go"
2225
)
2326

@@ -96,20 +99,26 @@ func (v *V8) Deploy() error {
9699

97100
subject := fmt.Sprintf("agentint.%s.trigger", v.vmID)
98101
_, err := v.nc.Subscribe(subject, func(msg *nats.Msg) {
102+
ctx := context.WithValue(context.Background(), agentapi.NexTriggerSubject, msg.Header.Get(agentapi.NexTriggerSubject)) //nolint:all
103+
ctx = otel.GetTextMapPropagator().Extract(ctx, propagation.HeaderCarrier(msg.Header))
104+
99105
startTime := time.Now()
100-
val, err := v.Execute(msg.Header.Get(agentapi.NexTriggerSubject), msg.Data)
106+
val, err := v.Execute(ctx, msg.Data)
101107
if err != nil {
102108
_, _ = v.stderr.Write([]byte(fmt.Sprintf("failed to execute function on trigger subject %s: %s", subject, err.Error())))
103109
return
104110
}
105111

106112
runtimeNanos := time.Since(startTime).Nanoseconds()
107113

114+
header := nats.Header{
115+
agentapi.NexRuntimeNs: []string{strconv.FormatInt(runtimeNanos, 10)},
116+
}
117+
otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(header))
118+
108119
err = msg.RespondMsg(&nats.Msg{
109-
Data: val,
110-
Header: nats.Header{
111-
agentapi.NexRuntimeNs: []string{strconv.FormatInt(runtimeNanos, 10)},
112-
},
120+
Data: val,
121+
Header: header,
113122
})
114123
if err != nil {
115124
_, _ = v.stderr.Write([]byte(fmt.Sprintf("failed to write %d-byte response: %s", len(val), err.Error())))
@@ -127,12 +136,20 @@ func (v *V8) Deploy() error {
127136
// Trigger execution of the deployed function; expects a `Validate` to have succeeded and `ubs` to be non-nil.
128137
// The executed function can optionally return a value, in which case it will be deemed a reply and returned
129138
// to the caller. In the case of a nil or empty value returned by the function, no reply will be sent.
130-
func (v *V8) Execute(subject string, payload []byte) ([]byte, error) {
139+
func (v *V8) Execute(ctx context.Context, payload []byte) ([]byte, error) {
131140
if v.ubs == nil {
132141
return nil, fmt.Errorf("invalid state for execution; no compiled code available for vm: %s", v.name)
133142
}
134143

135-
ctx, err := v.newV8Context()
144+
var subject string
145+
sub, ok := ctx.Value(agentapi.NexTriggerSubject).(string)
146+
if ok {
147+
subject = sub
148+
} else {
149+
return nil, fmt.Errorf("failed to initialize context in vm; no trigger subject provided in context: %s", v.name)
150+
}
151+
152+
v8ctx, err := v.newV8Context(ctx)
136153
if err != nil {
137154
return nil, fmt.Errorf("failed to initialize context in vm: %s", err.Error())
138155
}
@@ -141,7 +158,7 @@ func (v *V8) Execute(subject string, payload []byte) ([]byte, error) {
141158
errs := make(chan error, 1)
142159

143160
go func() {
144-
val, err := v.ubs.Run(ctx)
161+
val, err := v.ubs.Run(v8ctx)
145162
if err != nil {
146163
errs <- err
147164
return
@@ -153,7 +170,7 @@ func (v *V8) Execute(subject string, payload []byte) ([]byte, error) {
153170
return
154171
}
155172

156-
argv1, err := v8.NewValue(ctx.Isolate(), subject)
173+
argv1, err := v8.NewValue(v8ctx.Isolate(), subject)
157174
if err != nil {
158175
errs <- err
159176
return
@@ -166,7 +183,7 @@ func (v *V8) Execute(subject string, payload []byte) ([]byte, error) {
166183
return
167184
}
168185

169-
val, err = fn.Call(ctx.Global(), argv1, argv2)
186+
val, err = fn.Call(v8ctx.Global(), argv1, argv2)
170187
if err != nil {
171188
errs <- err
172189
return
@@ -270,10 +287,10 @@ func (v *V8) initUtils() {
270287
v.utils[v8FunctionUInt8ArrayToString] = uint8arrtostrfn
271288
}
272289

273-
func (v *V8) newV8Context() (*v8.Context, error) {
290+
func (v *V8) newV8Context(ctx context.Context) (*v8.Context, error) {
274291
global := v8.NewObjectTemplate(v.iso)
275292

276-
hostServices, err := v.newHostServicesTemplate()
293+
hostServices, err := v.newHostServicesTemplate(ctx)
277294
if err != nil {
278295
return nil, err
279296
}
@@ -286,63 +303,63 @@ func (v *V8) newV8Context() (*v8.Context, error) {
286303
return v8.NewContext(v.iso, global), nil
287304
}
288305

289-
func (v *V8) newHostServicesTemplate() (*v8.ObjectTemplate, error) {
306+
func (v *V8) newHostServicesTemplate(ctx context.Context) (*v8.ObjectTemplate, error) {
290307
hostServices := v8.NewObjectTemplate(v.iso)
291308

292-
err := hostServices.Set(hostServicesHTTPObjectName, v.newHTTPObjectTemplate())
309+
err := hostServices.Set(hostServicesHTTPObjectName, v.newHTTPObjectTemplate(ctx))
293310
if err != nil {
294311
return nil, err
295312
}
296313

297-
err = hostServices.Set(hostServicesKVObjectName, v.newKeyValueObjectTemplate())
314+
err = hostServices.Set(hostServicesKVObjectName, v.newKeyValueObjectTemplate(ctx))
298315
if err != nil {
299316
return nil, err
300317
}
301318

302-
err = hostServices.Set(hostServicesMessagingObjectName, v.newMessagingObjectTemplate())
319+
err = hostServices.Set(hostServicesMessagingObjectName, v.newMessagingObjectTemplate(ctx))
303320
if err != nil {
304321
return nil, err
305322
}
306323

307-
err = hostServices.Set(hostServicesObjectStoreObjectName, v.newObjectStoreObjectTemplate())
324+
err = hostServices.Set(hostServicesObjectStoreObjectName, v.newObjectStoreObjectTemplate(ctx))
308325
if err != nil {
309326
return nil, err
310327
}
311328

312329
return hostServices, nil
313330
}
314331

315-
func (v *V8) newHTTPObjectTemplate() *v8.ObjectTemplate {
332+
func (v *V8) newHTTPObjectTemplate(ctx context.Context) *v8.ObjectTemplate {
316333
http := v8.NewObjectTemplate(v.iso)
317334

318335
_ = http.Set(hostServicesHTTPGetFunctionName, v8.NewFunctionTemplate(
319-
v.iso, v.genHttpClientFunc(hostServicesHTTPGetFunctionName),
336+
v.iso, v.genHttpClientFunc(ctx, hostServicesHTTPGetFunctionName),
320337
))
321338

322339
_ = http.Set(hostServicesHTTPPostFunctionName, v8.NewFunctionTemplate(
323-
v.iso, v.genHttpClientFunc(hostServicesHTTPPostFunctionName),
340+
v.iso, v.genHttpClientFunc(ctx, hostServicesHTTPPostFunctionName),
324341
))
325342

326343
_ = http.Set(hostServicesHTTPPutFunctionName, v8.NewFunctionTemplate(
327-
v.iso, v.genHttpClientFunc(hostServicesHTTPPutFunctionName),
344+
v.iso, v.genHttpClientFunc(ctx, hostServicesHTTPPutFunctionName),
328345
))
329346

330347
_ = http.Set(hostServicesHTTPPatchFunctionName, v8.NewFunctionTemplate(
331-
v.iso, v.genHttpClientFunc(hostServicesHTTPPatchFunctionName),
348+
v.iso, v.genHttpClientFunc(ctx, hostServicesHTTPPatchFunctionName),
332349
))
333350

334351
_ = http.Set(hostServicesHTTPDeleteFunctionName, v8.NewFunctionTemplate(
335-
v.iso, v.genHttpClientFunc(hostServicesHTTPDeleteFunctionName),
352+
v.iso, v.genHttpClientFunc(ctx, hostServicesHTTPDeleteFunctionName),
336353
))
337354

338355
_ = http.Set(hostServicesHTTPHeadFunctionName, v8.NewFunctionTemplate(
339-
v.iso, v.genHttpClientFunc(hostServicesHTTPHeadFunctionName),
356+
v.iso, v.genHttpClientFunc(ctx, hostServicesHTTPHeadFunctionName),
340357
))
341358

342359
return http
343360
}
344361

345-
func (v *V8) genHttpClientFunc(method string) func(info *v8.FunctionCallbackInfo) *v8.Value {
362+
func (v *V8) genHttpClientFunc(ctx context.Context, method string) func(info *v8.FunctionCallbackInfo) *v8.Value {
346363
return func(info *v8.FunctionCallbackInfo) *v8.Value {
347364
args := info.Args()
348365
if len(args) == 0 {
@@ -367,7 +384,7 @@ func (v *V8) genHttpClientFunc(method string) func(info *v8.FunctionCallbackInfo
367384
}
368385
}
369386

370-
httpresp, err := v.builtins.SimpleHttpRequest(method, url.String(), payload)
387+
httpresp, err := v.builtins.SimpleHttpRequest(ctx, method, url.String(), payload)
371388
if err != nil {
372389
val, _ := v8.NewValue(v.iso, err.Error())
373390
return v.iso.ThrowException(val)
@@ -419,7 +436,7 @@ func (v *V8) genHttpClientFunc(method string) func(info *v8.FunctionCallbackInfo
419436
}
420437
}
421438

422-
func (v *V8) newKeyValueObjectTemplate() *v8.ObjectTemplate {
439+
func (v *V8) newKeyValueObjectTemplate(ctx context.Context) *v8.ObjectTemplate {
423440
kv := v8.NewObjectTemplate(v.iso)
424441

425442
_ = kv.Set(hostServicesKVGetFunctionName, v8.NewFunctionTemplate(v.iso, func(info *v8.FunctionCallbackInfo) *v8.Value {
@@ -431,7 +448,7 @@ func (v *V8) newKeyValueObjectTemplate() *v8.ObjectTemplate {
431448

432449
key := args[0].String()
433450

434-
resp, err := v.builtins.KVGet(key)
451+
resp, err := v.builtins.KVGet(ctx, key)
435452
if err != nil {
436453
val, _ := v8.NewValue(v.iso, err.Error())
437454
return v.iso.ThrowException(val)
@@ -462,7 +479,7 @@ func (v *V8) newKeyValueObjectTemplate() *v8.ObjectTemplate {
462479
return v.iso.ThrowException(val)
463480
}
464481

465-
kvresp, err := v.builtins.KVSet(key, value)
482+
kvresp, err := v.builtins.KVSet(ctx, key, value)
466483
if err != nil {
467484
val, _ := v8.NewValue(v.iso, err.Error())
468485
return v.iso.ThrowException(val)
@@ -485,7 +502,7 @@ func (v *V8) newKeyValueObjectTemplate() *v8.ObjectTemplate {
485502

486503
key := args[0].String()
487504

488-
kvresp, err := v.builtins.KVDelete(key)
505+
kvresp, err := v.builtins.KVDelete(ctx, key)
489506
if err != nil {
490507
val, _ := v8.NewValue(v.iso, err.Error())
491508
return v.iso.ThrowException(val)
@@ -501,7 +518,7 @@ func (v *V8) newKeyValueObjectTemplate() *v8.ObjectTemplate {
501518

502519
_ = kv.Set(hostServicesKVKeysFunctionName, v8.NewFunctionTemplate(v.iso, func(info *v8.FunctionCallbackInfo) *v8.Value {
503520

504-
resp, err := v.builtins.KVKeys()
521+
resp, err := v.builtins.KVKeys(ctx)
505522
if err != nil {
506523
val, _ := v8.NewValue(v.iso, err.Error())
507524
return v.iso.ThrowException(val)
@@ -522,7 +539,7 @@ func (v *V8) newKeyValueObjectTemplate() *v8.ObjectTemplate {
522539
return kv
523540
}
524541

525-
func (v *V8) newMessagingObjectTemplate() *v8.ObjectTemplate {
542+
func (v *V8) newMessagingObjectTemplate(ctx context.Context) *v8.ObjectTemplate {
526543
messaging := v8.NewObjectTemplate(v.iso)
527544

528545
_ = messaging.Set(hostServicesMessagingPublishFunctionName, v8.NewFunctionTemplate(v.iso, func(info *v8.FunctionCallbackInfo) *v8.Value {
@@ -539,7 +556,7 @@ func (v *V8) newMessagingObjectTemplate() *v8.ObjectTemplate {
539556
return v.iso.ThrowException(val)
540557
}
541558

542-
err = v.builtins.MessagingPublish(subject, payload)
559+
err = v.builtins.MessagingPublish(ctx, subject, payload)
543560
if err != nil {
544561
val, _ := v8.NewValue(v.iso, err.Error())
545562
return v.iso.ThrowException(val)
@@ -562,7 +579,7 @@ func (v *V8) newMessagingObjectTemplate() *v8.ObjectTemplate {
562579
return v.iso.ThrowException(val)
563580
}
564581

565-
resp, err := v.builtins.MessagingRequest(subject, payload)
582+
resp, err := v.builtins.MessagingRequest(ctx, subject, payload)
566583
if err != nil {
567584
val, _ := v8.NewValue(v.iso, err.Error())
568585
return v.iso.ThrowException(val)
@@ -664,7 +681,7 @@ func (v *V8) newMessagingObjectTemplate() *v8.ObjectTemplate {
664681
return messaging
665682
}
666683

667-
func (v *V8) newObjectStoreObjectTemplate() *v8.ObjectTemplate {
684+
func (v *V8) newObjectStoreObjectTemplate(ctx context.Context) *v8.ObjectTemplate {
668685
objectStore := v8.NewObjectTemplate(v.iso)
669686

670687
_ = objectStore.Set(hostServicesObjectStoreGetFunctionName, v8.NewFunctionTemplate(v.iso, func(info *v8.FunctionCallbackInfo) *v8.Value {
@@ -675,7 +692,7 @@ func (v *V8) newObjectStoreObjectTemplate() *v8.ObjectTemplate {
675692
}
676693

677694
name := args[0].String()
678-
resp, err := v.builtins.ObjectGet(name)
695+
resp, err := v.builtins.ObjectGet(ctx, name)
679696
if err != nil {
680697
val, _ := v8.NewValue(v.iso, err.Error())
681698
return v.iso.ThrowException(val)
@@ -706,7 +723,7 @@ func (v *V8) newObjectStoreObjectTemplate() *v8.ObjectTemplate {
706723
return v.iso.ThrowException(val)
707724
}
708725

709-
resp, err := v.builtins.ObjectPut(name, value)
726+
resp, err := v.builtins.ObjectPut(ctx, name, value)
710727

711728
if err != nil {
712729
val, _ := v8.NewValue(v.iso, err.Error())
@@ -733,7 +750,7 @@ func (v *V8) newObjectStoreObjectTemplate() *v8.ObjectTemplate {
733750
}
734751

735752
name := args[0].String()
736-
err := v.builtins.ObjectDelete(name)
753+
err := v.builtins.ObjectDelete(ctx, name)
737754
if err != nil {
738755
val, _ := v8.NewValue(v.iso, err.Error())
739756
return v.iso.ThrowException(val)
@@ -743,7 +760,7 @@ func (v *V8) newObjectStoreObjectTemplate() *v8.ObjectTemplate {
743760
}))
744761

745762
_ = objectStore.Set(hostServicesObjectStoreListFunctionName, v8.NewFunctionTemplate(v.iso, func(info *v8.FunctionCallbackInfo) *v8.Value {
746-
resp, err := v.builtins.ObjectList()
763+
resp, err := v.builtins.ObjectList(ctx)
747764
if err != nil {
748765
val, _ := v8.NewValue(v.iso, err.Error())
749766
return v.iso.ThrowException(val)

0 commit comments

Comments
 (0)