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 <jonathan.sternberg@docker.com>
This commit is contained in:
Jonathan A. Sternberg 2025-02-19 10:19:00 -06:00
parent cfe0605616
commit 8890a1c929
No known key found for this signature in database
GPG Key ID: 6603D4B96394F6B1
3 changed files with 54 additions and 15 deletions

View File

@ -107,7 +107,7 @@ func AddPluginCommandStubs(dockerCli command.Cli, rootCmd *cobra.Command) (err e
} }
const ( const (
dockerCliAttributePrefix = attribute.Key("docker.cli") dockerCliAttributePrefix = command.DockerCliAttributePrefix
cobraCommandPath = attribute.Key("cobra.command_path") 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(); { for iter := attrSet.Iter(); iter.Next(); {
attr := iter.Attribute() attr := iter.Attribute()
kvs = append(kvs, attribute.KeyValue{ kvs = append(kvs, attribute.KeyValue{
Key: dockerCliAttributePrefix + "." + attr.Key, Key: dockerCliAttributePrefix + attr.Key,
Value: attr.Value, 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 { func appendPluginResourceAttributesEnvvar(env []string, cmd *cobra.Command, plugin Plugin) []string {
if attrs := getPluginResourceAttributes(cmd, plugin); attrs.Len() > 0 { 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. // Construct baggage members for each of the attributes.
// Ignore any failures as these aren't significant and // Ignore any failures as these aren't significant and
// represent an internal issue. // represent an internal issue.
var b baggage.Baggage members := make([]baggage.Member, 0, attrs.Len())
for iter := attrs.Iter(); iter.Next(); { for iter := attrs.Iter(); iter.Next(); {
attr := iter.Attribute() attr := iter.Attribute()
m, err := baggage.NewMemberRaw(string(attr.Key), attr.Value.AsString()) 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) otel.Handle(err)
continue continue
} }
members = append(members, m)
newB, err := b.SetMember(m)
if err != nil {
otel.Handle(err)
continue
}
b = newB
} }
// Combine plugin added resource attributes with ones found in the environment // 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 != "" { if v := strings.TrimSpace(os.Getenv(ResourceAttributesEnvvar)); v != "" {
attrsSlice = append(attrsSlice, v) attrsSlice = append(attrsSlice, v)
} }
if v := b.String(); v != "" { if b, err := baggage.New(members...); err != nil {
attrsSlice = append(attrsSlice, v) otel.Handle(err)
} else if b.Len() > 0 {
attrsSlice = append(attrsSlice, b.String())
} }
if len(attrsSlice) > 0 { if len(attrsSlice) > 0 {

View File

@ -26,7 +26,7 @@ const (
// ResourceAttributesEnvvar is the name of the envvar that includes additional // ResourceAttributesEnvvar is the name of the envvar that includes additional
// resource attributes for OTEL. // resource attributes for OTEL.
ResourceAttributesEnvvar = "OTEL_RESOURCE_ATTRIBUTES" ResourceAttributesEnvvar = command.ResourceAttributesEnvvar
) )
// errPluginNotFound is the error returned when a plugin could not be found. // errPluginNotFound is the error returned when a plugin could not be found.

View File

@ -11,6 +11,7 @@ import (
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv" "strconv"
"strings"
"sync" "sync"
"time" "time"
@ -292,6 +293,7 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...CLIOption)
if cli.enableGlobalTracer { if cli.enableGlobalTracer {
cli.createGlobalTracerProvider(cli.baseCtx) cli.createGlobalTracerProvider(cli.baseCtx)
} }
filterResourceAttributesEnvvar()
return nil return nil
} }
@ -591,3 +593,46 @@ func DefaultContextStoreConfig() store.Config {
defaultStoreEndpoints..., 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, ",")
}