DockerCLI/cli/command/telemetry_docker.go

138 lines
3.8 KiB
Go
Raw Normal View History

// FIXME(jsternberg): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.21
package command
import (
"context"
"net/url"
"os"
"path"
"github.com/pkg/errors"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
const (
otelContextFieldName string = "otel"
otelExporterOTLPEndpoint string = "OTEL_EXPORTER_OTLP_ENDPOINT"
debugEnvVarPrefix string = "DOCKER_CLI_"
)
// dockerExporterOTLPEndpoint retrieves the OTLP endpoint used for the docker reporter
// from the current context.
func dockerExporterOTLPEndpoint(cli Cli) (endpoint string, secure bool) {
meta, err := cli.ContextStore().GetMetadata(cli.CurrentContext())
if err != nil {
otel.Handle(err)
return "", false
}
var otelCfg any
switch m := meta.Metadata.(type) {
case DockerContext:
otelCfg = m.AdditionalFields[otelContextFieldName]
case map[string]any:
otelCfg = m[otelContextFieldName]
}
if otelCfg != nil {
otelMap, ok := otelCfg.(map[string]any)
if !ok {
otel.Handle(errors.Errorf(
"unexpected type for field %q: %T (expected: %T)",
otelContextFieldName,
otelCfg,
otelMap,
))
}
// keys from https://opentelemetry.io/docs/concepts/sdk-configuration/otlp-exporter-configuration/
endpoint, _ = otelMap[otelExporterOTLPEndpoint].(string)
}
// Override with env var value if it exists AND IS SET
// (ignore otel defaults for this override when the key exists but is empty)
if override := os.Getenv(debugEnvVarPrefix + otelExporterOTLPEndpoint); override != "" {
endpoint = override
}
if endpoint == "" {
return "", false
}
// Parse the endpoint. The docker config expects the endpoint to be
// in the form of a URL to match the environment variable, but this
// option doesn't correspond directly to WithEndpoint.
//
// We pretend we're the same as the environment reader.
u, err := url.Parse(endpoint)
if err != nil {
otel.Handle(errors.Errorf("docker otel endpoint is invalid: %s", err))
return "", false
}
switch u.Scheme {
case "unix":
// Unix sockets are a bit weird. OTEL seems to imply they
// can be used as an environment variable and are handled properly,
// but they don't seem to be as the behavior of the environment variable
// is to strip the scheme from the endpoint, but the underlying implementation
// needs the scheme to use the correct resolver.
//
// We'll just handle this in a special way and add the unix:// back to the endpoint.
linting: fmt.Sprintf can be replaced with string concatenation (perfsprint) cli/registry/client/endpoint.go:128:34: fmt.Sprintf can be replaced with string concatenation (perfsprint) req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", th.token)) ^ cli/command/telemetry_docker.go:88:14: fmt.Sprintf can be replaced with string concatenation (perfsprint) endpoint = fmt.Sprintf("unix://%s", path.Join(u.Host, u.Path)) ^ cli/command/cli_test.go:195:47: fmt.Sprintf can be replaced with string concatenation (perfsprint) opts := &flags.ClientOptions{Hosts: []string{fmt.Sprintf("unix://%s", socket)}} ^ cli/command/registry_test.go:59:24: fmt.Sprintf can be replaced with string concatenation (perfsprint) inputServerAddress: fmt.Sprintf("https://%s", testAuthConfigs[1].ServerAddress), ^ cli/command/container/opts_test.go:338:35: fmt.Sprintf can be replaced with string concatenation (perfsprint) if config, _, _ := mustParse(t, fmt.Sprintf("--hostname=%s", hostname)); config.Hostname != expectedHostname { ^ cli/command/context/options.go:79:24: fmt.Sprintf can be replaced with string concatenation (perfsprint) errs = append(errs, fmt.Sprintf("%s: unrecognized config key", k)) ^ cli/command/image/build.go:461:68: fmt.Sprintf can be replaced with string concatenation (perfsprint) line = dockerfileFromLinePattern.ReplaceAllLiteralString(line, fmt.Sprintf("FROM %s", reference.FamiliarString(trustedRef))) ^ cli/command/image/remove_test.go:21:9: fmt.Sprintf can be replaced with string concatenation (perfsprint) return fmt.Sprintf("Error: No such image: %s", n.imageID) ^ cli/command/image/build/context.go:229:102: fmt.Sprintf can be replaced with string concatenation (perfsprint) progReader := progress.NewProgressReader(response.Body, progressOutput, response.ContentLength, "", fmt.Sprintf("Downloading build context from remote url: %s", remoteURL)) ^ cli/command/service/logs.go:215:16: fmt.Sprintf can be replaced with string concatenation (perfsprint) taskName += fmt.Sprintf(".%s", task.ID) ^ cli/command/service/logs.go:217:16: fmt.Sprintf can be replaced with string concatenation (perfsprint) taskName += fmt.Sprintf(".%s", stringid.TruncateID(task.ID)) ^ cli/command/service/progress/progress_test.go:877:18: fmt.Sprintf can be replaced with string concatenation (perfsprint) ID: fmt.Sprintf("task%s", nodeID), ^ cli/command/stack/swarm/remove.go:61:24: fmt.Sprintf can be replaced with string concatenation (perfsprint) errs = append(errs, fmt.Sprintf("Failed to remove some resources from stack: %s", namespace)) ^ cli/command/swarm/ipnet_slice_test.go:32:9: fmt.Sprintf can be replaced with string concatenation (perfsprint) arg := fmt.Sprintf("--cidrs=%s", strings.Join(vals, ",")) ^ cli/command/swarm/ipnet_slice_test.go:137:30: fmt.Sprintf can be replaced with string concatenation (perfsprint) if err := f.Parse([]string{fmt.Sprintf("--cidrs=%s", strings.Join(test.FlagArg, ","))}); err != nil { ^ cli/compose/schema/schema.go:105:11: fmt.Sprintf can be replaced with string concatenation (perfsprint) return fmt.Sprintf("must be a %s", humanReadableType(expectedType)) ^ cli/manifest/store/store.go:165:9: fmt.Sprintf can be replaced with string concatenation (perfsprint) return fmt.Sprintf("No such manifest: %s", n.object) ^ e2e/image/push_test.go:340:4: fmt.Sprintf can be replaced with string concatenation (perfsprint) fmt.Sprintf("NOTARY_ROOT_PASSPHRASE=%s", pwd), ^ e2e/image/push_test.go:341:4: fmt.Sprintf can be replaced with string concatenation (perfsprint) fmt.Sprintf("NOTARY_TARGETS_PASSPHRASE=%s", pwd), ^ e2e/image/push_test.go:342:4: fmt.Sprintf can be replaced with string concatenation (perfsprint) fmt.Sprintf("NOTARY_SNAPSHOT_PASSPHRASE=%s", pwd), ^ e2e/image/push_test.go:343:4: fmt.Sprintf can be replaced with string concatenation (perfsprint) fmt.Sprintf("NOTARY_DELEGATION_PASSPHRASE=%s", pwd), ^ e2e/plugin/trust_test.go:23:16: fmt.Sprintf can be replaced with string concatenation (perfsprint) pluginName := fmt.Sprintf("%s/plugin-content-trust", registryPrefix) ^ e2e/plugin/trust_test.go:53:8: fmt.Sprintf can be replaced with string concatenation (perfsprint) Out: fmt.Sprintf("Installed plugin %s", pluginName), ^ e2e/trust/revoke_test.go:62:57: fmt.Sprintf can be replaced with string concatenation (perfsprint) icmd.RunCommand("docker", "tag", fixtures.AlpineImage, fmt.Sprintf("%s:v1", revokeRepo)).Assert(t, icmd.Success) ^ e2e/trust/revoke_test.go:64:49: fmt.Sprintf can be replaced with string concatenation (perfsprint) icmd.Command("docker", "-D", "trust", "sign", fmt.Sprintf("%s:v1", revokeRepo)), ^ e2e/trust/revoke_test.go:68:58: fmt.Sprintf can be replaced with string concatenation (perfsprint) icmd.RunCommand("docker", "tag", fixtures.BusyboxImage, fmt.Sprintf("%s:v2", revokeRepo)).Assert(t, icmd.Success) ^ e2e/trust/revoke_test.go:70:49: fmt.Sprintf can be replaced with string concatenation (perfsprint) icmd.Command("docker", "-D", "trust", "sign", fmt.Sprintf("%s:v2", revokeRepo)), ^ e2e/trust/sign_test.go:36:47: fmt.Sprintf can be replaced with string concatenation (perfsprint) assert.Check(t, is.Contains(result.Stdout(), fmt.Sprintf("v1: digest: sha256:%s", fixtures.AlpineSha))) ^ e2e/trust/sign_test.go:53:47: fmt.Sprintf can be replaced with string concatenation (perfsprint) assert.Check(t, is.Contains(result.Stdout(), fmt.Sprintf("v1: digest: sha256:%s", fixtures.BusyboxSha))) ^ e2e/trust/sign_test.go:65:47: fmt.Sprintf can be replaced with string concatenation (perfsprint) assert.Check(t, is.Contains(result.Stdout(), fmt.Sprintf("v1: digest: sha256:%s", fixtures.AlpineSha))) ^ opts/file.go:21:9: fmt.Sprintf can be replaced with string concatenation (perfsprint) return fmt.Sprintf("poorly formatted environment: %s", e.msg) ^ opts/hosts_test.go:26:31: fmt.Sprintf can be replaced with string concatenation (perfsprint) "tcp://host:": fmt.Sprintf("tcp://host:%s", defaultHTTPPort), ^ Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-06-10 15:07:37 -04:00
endpoint = "unix://" + path.Join(u.Host, u.Path)
case "https":
secure = true
fallthrough
case "http":
endpoint = path.Join(u.Host, u.Path)
}
return endpoint, secure
}
func dockerSpanExporter(ctx context.Context, cli Cli) []sdktrace.TracerProviderOption {
endpoint, secure := dockerExporterOTLPEndpoint(cli)
if endpoint == "" {
return nil
}
opts := []otlptracegrpc.Option{
otlptracegrpc.WithEndpoint(endpoint),
}
if !secure {
opts = append(opts, otlptracegrpc.WithInsecure())
}
exp, err := otlptracegrpc.New(ctx, opts...)
if err != nil {
otel.Handle(err)
return nil
}
return []sdktrace.TracerProviderOption{sdktrace.WithBatcher(exp, sdktrace.WithExportTimeout(exportTimeout))}
}
func dockerMetricExporter(ctx context.Context, cli Cli) []sdkmetric.Option {
endpoint, secure := dockerExporterOTLPEndpoint(cli)
if endpoint == "" {
return nil
}
opts := []otlpmetricgrpc.Option{
otlpmetricgrpc.WithEndpoint(endpoint),
}
if !secure {
opts = append(opts, otlpmetricgrpc.WithInsecure())
}
exp, err := otlpmetricgrpc.New(ctx, opts...)
if err != nil {
otel.Handle(err)
return nil
}
return []sdkmetric.Option{sdkmetric.WithReader(newCLIReader(exp))}
}