-
Notifications
You must be signed in to change notification settings - Fork 875
feat(provisioner): propagate trace info #17166
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
If tracing is enabled, propagate the trace information to the terraform provisioner via environment variables. This sets the `TRACEPARENT` environment variable using the default W3C trace propagators. Users can choose to continue the trace by adding new spans in the provisioner by reading from the environment like: ctx := env.ContextWithRemoteSpanContext(context.Background(), os.Environ())
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package terraform | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"slices" | ||
"strings" | ||
"unicode" | ||
|
||
"go.opentelemetry.io/otel" | ||
"go.opentelemetry.io/otel/propagation" | ||
) | ||
|
||
// TODO: replace this with the upstream OTEL env propagation when it is | ||
// released. | ||
|
||
// envCarrier is a propagation.TextMapCarrier that is used to extract or | ||
// inject tracing environment variables. This is used with a | ||
// propagation.TextMapPropagator | ||
type envCarrier struct { | ||
Env []string | ||
} | ||
|
||
var _ propagation.TextMapCarrier = (*envCarrier)(nil) | ||
|
||
func toKey(key string) string { | ||
key = strings.ToUpper(key) | ||
key = strings.ReplaceAll(key, "-", "_") | ||
return strings.Map(func(r rune) rune { | ||
if unicode.IsLetter(r) || unicode.IsNumber(r) || r == '_' { | ||
return r | ||
} | ||
return -1 | ||
}, key) | ||
} | ||
|
||
func (c *envCarrier) Set(key, value string) { | ||
if c == nil { | ||
return | ||
} | ||
key = toKey(key) | ||
for i, e := range c.Env { | ||
if strings.HasPrefix(e, key+"=") { | ||
// don't directly update the slice so we don't modify the slice | ||
// passed in | ||
newEnv := slices.Clone(c.Env) | ||
newEnv = append(newEnv[:i], append([]string{fmt.Sprintf("%s=%s", key, value)}, newEnv[i+1:]...)...) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Once you've cloned the initial slice, seems like it would be simpler to just modify in place, rather than re-slice and append. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hah, I think that code went through too many refactors and came out nonsense. I have simplified it. |
||
c.Env = newEnv | ||
return | ||
} | ||
} | ||
c.Env = append(c.Env, fmt.Sprintf("%s=%s", key, value)) | ||
} | ||
|
||
func (*envCarrier) Get(_ string) string { | ||
// Get not necessary to inject environment variables | ||
panic("Not implemented") | ||
} | ||
|
||
func (*envCarrier) Keys() []string { | ||
// Keys not necessary to inject environment variables | ||
panic("Not implemented") | ||
} | ||
|
||
// otelEnvInject will add add any necessary environment variables for the span | ||
// found in the Context. If environment variables are already present | ||
// in `environ` then they will be updated. If no variables are found the | ||
// new ones will be appended. The new environment will be returned, `environ` | ||
// will never be modified. | ||
func otelEnvInject(ctx context.Context, environ []string) []string { | ||
c := &envCarrier{Env: environ} | ||
otel.GetTextMapPropagator().Inject(ctx, c) | ||
spikecurtis marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return c.Env | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package terraform // nolint:testpackage | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. file should be named |
||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
"go.opentelemetry.io/otel" | ||
"go.opentelemetry.io/otel/propagation" | ||
sdktrace "go.opentelemetry.io/otel/sdk/trace" | ||
"go.opentelemetry.io/otel/trace" | ||
) | ||
|
||
type testIDGenerator struct{} | ||
|
||
var _ sdktrace.IDGenerator = (*testIDGenerator)(nil) | ||
|
||
func (testIDGenerator) NewIDs(ctx context.Context) (trace.TraceID, trace.SpanID) { | ||
coryb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
traceID, _ := trace.TraceIDFromHex("60d19e9e9abf2197c1d6d8f93e28ee2a") | ||
spanID, _ := trace.SpanIDFromHex("a028bd951229a46f") | ||
return traceID, spanID | ||
} | ||
|
||
func (testIDGenerator) NewSpanID(ctx context.Context, traceID trace.TraceID) trace.SpanID { | ||
coryb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
spanID, _ := trace.SpanIDFromHex("a028bd951229a46f") | ||
return spanID | ||
} | ||
|
||
func TestOtelEnvInject(t *testing.T) { | ||
t.Parallel() | ||
testTraceProvider := sdktrace.NewTracerProvider( | ||
sdktrace.WithSampler(sdktrace.AlwaysSample()), | ||
sdktrace.WithIDGenerator(testIDGenerator{}), | ||
) | ||
|
||
tracer := testTraceProvider.Tracer("example") | ||
ctx, span := tracer.Start(context.Background(), "testing") | ||
defer span.End() | ||
|
||
input := []string{"PATH=/usr/bin:/bin"} | ||
|
||
otel.SetTextMapPropagator(propagation.TraceContext{}) | ||
got := otelEnvInject(ctx, input) | ||
require.Equal(t, []string{ | ||
"PATH=/usr/bin:/bin", | ||
"TRACEPARENT=00-60d19e9e9abf2197c1d6d8f93e28ee2a-a028bd951229a46f-01", | ||
}, got) | ||
|
||
// verify we update rather than append | ||
input = []string{ | ||
"PATH=/usr/bin:/bin", | ||
"TRACEPARENT=origTraceParent", | ||
"TERM=xterm", | ||
} | ||
|
||
otel.SetTextMapPropagator(propagation.TraceContext{}) | ||
got = otelEnvInject(ctx, input) | ||
require.Equal(t, []string{ | ||
"PATH=/usr/bin:/bin", | ||
"TRACEPARENT=00-60d19e9e9abf2197c1d6d8f93e28ee2a-a028bd951229a46f-01", | ||
"TERM=xterm", | ||
}, got) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍