From 8890a1c9292ed5f2bf45df44a0e57d345f563d31 Mon Sep 17 00:00:00 2001 From: "Jonathan A. Sternberg" Date: Wed, 19 Feb 2025 10:19:00 -0600 Subject: [PATCH] cli-plugins: remove docker.cli specific otel attributes after usage Remove the `docker.cli` prefixed attributes from `OTEL_RESOURCE_ATTRIBUTES` after the telemetry provider has been created within a plugin. This prevents accidentally sending the attributes to something downstream for the user. This also fixes an issue with compose where the self-injected `OTEL_RESOURCE_ATTRIBUTES` would override an existing attribute in the environment file because the "user environment" overrode the environment file, but the "user environment" was created by the `docker` tool rather than by the user's environment. When `OTEL_RESOURCE_ATTRIBUTES` is empty after pruning, the environment variable is unset. Signed-off-by: Jonathan A. Sternberg --- cli-plugins/manager/cobra.go | 22 ++++++----------- cli-plugins/manager/manager.go | 2 +- cli/command/cli.go | 45 ++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/cli-plugins/manager/cobra.go b/cli-plugins/manager/cobra.go index 08a4de7ed9..ec45045b06 100644 --- a/cli-plugins/manager/cobra.go +++ b/cli-plugins/manager/cobra.go @@ -107,7 +107,7 @@ func AddPluginCommandStubs(dockerCli command.Cli, rootCmd *cobra.Command) (err e } const ( - dockerCliAttributePrefix = attribute.Key("docker.cli") + dockerCliAttributePrefix = command.DockerCliAttributePrefix cobraCommandPath = attribute.Key("cobra.command_path") ) @@ -126,7 +126,7 @@ func getPluginResourceAttributes(cmd *cobra.Command, plugin Plugin) attribute.Se for iter := attrSet.Iter(); iter.Next(); { attr := iter.Attribute() kvs = append(kvs, attribute.KeyValue{ - Key: dockerCliAttributePrefix + "." + attr.Key, + Key: dockerCliAttributePrefix + attr.Key, Value: attr.Value, }) } @@ -135,12 +135,10 @@ func getPluginResourceAttributes(cmd *cobra.Command, plugin Plugin) attribute.Se func appendPluginResourceAttributesEnvvar(env []string, cmd *cobra.Command, plugin Plugin) []string { if attrs := getPluginResourceAttributes(cmd, plugin); attrs.Len() > 0 { - // values in environment variables need to be in baggage format - // otel/baggage package can be used after update to v1.22, currently it encodes incorrectly // Construct baggage members for each of the attributes. // Ignore any failures as these aren't significant and // represent an internal issue. - var b baggage.Baggage + members := make([]baggage.Member, 0, attrs.Len()) for iter := attrs.Iter(); iter.Next(); { attr := iter.Attribute() m, err := baggage.NewMemberRaw(string(attr.Key), attr.Value.AsString()) @@ -148,13 +146,7 @@ func appendPluginResourceAttributesEnvvar(env []string, cmd *cobra.Command, plug otel.Handle(err) continue } - - newB, err := b.SetMember(m) - if err != nil { - otel.Handle(err) - continue - } - b = newB + members = append(members, m) } // Combine plugin added resource attributes with ones found in the environment @@ -165,8 +157,10 @@ func appendPluginResourceAttributesEnvvar(env []string, cmd *cobra.Command, plug if v := strings.TrimSpace(os.Getenv(ResourceAttributesEnvvar)); v != "" { attrsSlice = append(attrsSlice, v) } - if v := b.String(); v != "" { - attrsSlice = append(attrsSlice, v) + if b, err := baggage.New(members...); err != nil { + otel.Handle(err) + } else if b.Len() > 0 { + attrsSlice = append(attrsSlice, b.String()) } if len(attrsSlice) > 0 { diff --git a/cli-plugins/manager/manager.go b/cli-plugins/manager/manager.go index 9f795bc498..09b5b14e73 100644 --- a/cli-plugins/manager/manager.go +++ b/cli-plugins/manager/manager.go @@ -26,7 +26,7 @@ const ( // ResourceAttributesEnvvar is the name of the envvar that includes additional // resource attributes for OTEL. - ResourceAttributesEnvvar = "OTEL_RESOURCE_ATTRIBUTES" + ResourceAttributesEnvvar = command.ResourceAttributesEnvvar ) // errPluginNotFound is the error returned when a plugin could not be found. diff --git a/cli/command/cli.go b/cli/command/cli.go index 227720fa07..1452e1a240 100644 --- a/cli/command/cli.go +++ b/cli/command/cli.go @@ -11,6 +11,7 @@ import ( "path/filepath" "runtime" "strconv" + "strings" "sync" "time" @@ -292,6 +293,7 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...CLIOption) if cli.enableGlobalTracer { cli.createGlobalTracerProvider(cli.baseCtx) } + filterResourceAttributesEnvvar() return nil } @@ -591,3 +593,46 @@ func DefaultContextStoreConfig() store.Config { defaultStoreEndpoints..., ) } + +const ( + // ResourceAttributesEnvvar is the name of the envvar that includes additional + // resource attributes for OTEL. + ResourceAttributesEnvvar = "OTEL_RESOURCE_ATTRIBUTES" + + // DockerCliAttributePrefix is the prefix for any docker cli OTEL attributes. + DockerCliAttributePrefix = "docker.cli." +) + +func filterResourceAttributesEnvvar() { + if v := os.Getenv(ResourceAttributesEnvvar); v != "" { + if filtered := filterResourceAttributes(v); filtered != "" { + os.Setenv(ResourceAttributesEnvvar, filtered) + } else { + os.Unsetenv(ResourceAttributesEnvvar) + } + } +} + +func filterResourceAttributes(s string) string { + if trimmed := strings.TrimSpace(s); trimmed == "" { + return trimmed + } + + pairs := strings.Split(s, ",") + elems := make([]string, 0, len(pairs)) + for _, p := range pairs { + k, _, found := strings.Cut(p, "=") + if !found { + // Do not interact with invalid otel resources. + elems = append(elems, p) + continue + } + + // Skip attributes that have our docker.cli prefix. + if strings.HasPrefix(k, DockerCliAttributePrefix) { + continue + } + elems = append(elems, p) + } + return strings.Join(elems, ",") +}