diff --git a/executor/oci/spec.go b/executor/oci/spec.go index 80003108..a01ae6c8 100644 --- a/executor/oci/spec.go +++ b/executor/oci/spec.go @@ -15,6 +15,7 @@ import ( "github.com/moby/buildkit/executor" "github.com/moby/buildkit/snapshot" "github.com/moby/buildkit/util/network" + traceexec "github.com/moby/buildkit/util/tracing/exec" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/selinux/go-selinux" "github.com/pkg/errors" @@ -76,6 +77,8 @@ func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mou hostname = meta.Hostname } + meta.Env = append(meta.Env, traceexec.Environ(ctx)...) + opts = append(opts, oci.WithProcessArgs(meta.Args...), oci.WithEnv(meta.Env), diff --git a/util/appcontext/appcontext.go b/util/appcontext/appcontext.go index e74da2cd..3c67f923 100644 --- a/util/appcontext/appcontext.go +++ b/util/appcontext/appcontext.go @@ -22,7 +22,12 @@ func Context() context.Context { const exitLimit = 3 retries := 0 - ctx, cancel := context.WithCancel(context.Background()) + ctx := context.Background() + for _, f := range inits { + ctx = f(ctx) + } + + ctx, cancel := context.WithCancel(ctx) appContextCache = ctx go func() { diff --git a/util/appcontext/register.go b/util/appcontext/register.go new file mode 100644 index 00000000..22f9e836 --- /dev/null +++ b/util/appcontext/register.go @@ -0,0 +1,14 @@ +package appcontext + +import ( + "context" +) + +type Initializer func(context.Context) context.Context + +var inits []Initializer + +// Register stores a new context initializer that runs on app context creation +func Register(f Initializer) { + inits = append(inits, f) +} diff --git a/util/tracing/env/traceenv.go b/util/tracing/env/traceenv.go new file mode 100644 index 00000000..1c4e2f44 --- /dev/null +++ b/util/tracing/env/traceenv.go @@ -0,0 +1,54 @@ +package detect + +import ( + "context" + "os" + + "github.com/moby/buildkit/util/appcontext" + "go.opentelemetry.io/otel/propagation" +) + +const ( + traceparentHeader = "traceparent" + tracestateHeader = "tracestate" +) + +func init() { + appcontext.Register(initContext) +} + +func initContext(ctx context.Context) context.Context { + // previously defined in https://github.com/open-telemetry/opentelemetry-swift/blob/4ea467ed4b881d7329bf2254ca7ed7f2d9d6e1eb/Sources/OpenTelemetrySdk/Trace/Propagation/EnvironmentContextPropagator.swift#L14-L15 + parent := os.Getenv("OTEL_TRACE_PARENT") + state := os.Getenv("OTEL_TRACE_STATE") + + if parent == "" { + return ctx + } + + tc := propagation.TraceContext{} + return tc.Extract(ctx, &textMap{parent: parent, state: state}) +} + +type textMap struct { + parent string + state string +} + +func (tm *textMap) Get(key string) string { + switch key { + case traceparentHeader: + return tm.parent + case tracestateHeader: + return tm.state + default: + return "" + } +} + +func (tm *textMap) Set(key string, value string) { +} + +func (tm *textMap) Keys() []string { + return []string{traceparentHeader, tracestateHeader} +} diff --git a/util/tracing/exec/traceexec.go b/util/tracing/exec/traceexec.go new file mode 100644 index 00000000..80020fba --- /dev/null +++ b/util/tracing/exec/traceexec.go @@ -0,0 +1,68 @@ +package detect + +import ( + "context" + + "go.opentelemetry.io/otel/propagation" +) + +const ( + traceparentHeader = "traceparent" + tracestateHeader = "tracestate" +) + +// Environ returns list of environment variables that need to be sent to the child process +// in order for it to pick up cross-process tracing from same state. +func Environ(ctx context.Context) []string { + var tm textMap + tc := propagation.TraceContext{} + tc.Inject(ctx, &tm) + + var env []string + + // previously defined in https://github.com/open-telemetry/opentelemetry-swift/blob/4ea467ed4b881d7329bf2254ca7ed7f2d9d6e1eb/Sources/OpenTelemetrySdk/Trace/Propagation/EnvironmentContextPropagator.swift#L14-L15 + if tm.parent != "" { + env = append(env, "OTEL_TRACE_PARENT="+tm.parent) + } + if tm.state != "" { + env = append(env, "OTEL_TRACE_STATE="+tm.state) + } + + return env +} + +type textMap struct { + parent string + state string +} + +func (tm *textMap) Get(key string) string { + switch key { + case traceparentHeader: + return tm.parent + case tracestateHeader: + return tm.state + default: + return "" + } +} + +func (tm *textMap) Set(key string, value string) { + switch key { + case traceparentHeader: + tm.parent = value + case tracestateHeader: + tm.state = value + } +} + +func (tm *textMap) Keys() []string { + var k []string + if tm.parent != "" { + k = append(k, traceparentHeader) + } + if tm.state != "" { + k = append(k, tracestateHeader) + } + return k +}