mirror of https://github.com/docker/cli.git
plugins: set OTEL_RESOURCE_ATTRIBUTES when invoking a plugin
When a plugin is invoked, the docker cli will now set `OTEL_RESOURCE_ATTRIBUTES` to pass OTEL resource attribute names to the plugin as additional resource attributes. At the moment, the only resource attribute passed is `cobra.command_path`. All resource attributes passed by the CLI are prepended with the namespace `docker.cli` to avoid clashing with existing ones the plugin uses or ones defined by the user. For aliased commands like the various builder commands, the command path is overwritten to match with the original name (such as `docker builder`) instead of the forwarded name (such as `docker buildx build`). Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
This commit is contained in:
parent
eb306df13d
commit
85dcacd78f
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -30,6 +31,10 @@ const (
|
||||||
// is, one which failed it's candidate test) and contains the
|
// is, one which failed it's candidate test) and contains the
|
||||||
// reason for the failure.
|
// reason for the failure.
|
||||||
CommandAnnotationPluginInvalid = "com.docker.cli.plugin-invalid"
|
CommandAnnotationPluginInvalid = "com.docker.cli.plugin-invalid"
|
||||||
|
|
||||||
|
// CommandAnnotationPluginCommandPath is added to overwrite the
|
||||||
|
// command path for a plugin invocation.
|
||||||
|
CommandAnnotationPluginCommandPath = "com.docker.cli.plugin.command_path"
|
||||||
)
|
)
|
||||||
|
|
||||||
var pluginCommandStubsOnce sync.Once
|
var pluginCommandStubsOnce sync.Once
|
||||||
|
@ -98,3 +103,38 @@ func AddPluginCommandStubs(dockerCli command.Cli, rootCmd *cobra.Command) (err e
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
dockerCliAttributePrefix = attribute.Key("docker.cli")
|
||||||
|
|
||||||
|
cobraCommandPath = attribute.Key("cobra.command_path")
|
||||||
|
)
|
||||||
|
|
||||||
|
func getPluginResourceAttributes(cmd *cobra.Command, plugin Plugin) attribute.Set {
|
||||||
|
commandPath := cmd.Annotations[CommandAnnotationPluginCommandPath]
|
||||||
|
if commandPath == "" {
|
||||||
|
commandPath = fmt.Sprintf("%s %s", cmd.CommandPath(), plugin.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
attrSet := attribute.NewSet(
|
||||||
|
cobraCommandPath.String(commandPath),
|
||||||
|
)
|
||||||
|
|
||||||
|
kvs := make([]attribute.KeyValue, 0, attrSet.Len())
|
||||||
|
for iter := attrSet.Iter(); iter.Next(); {
|
||||||
|
attr := iter.Attribute()
|
||||||
|
kvs = append(kvs, attribute.KeyValue{
|
||||||
|
Key: dockerCliAttributePrefix + "." + attr.Key,
|
||||||
|
Value: attr.Value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return attribute.NewSet(kvs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendPluginResourceAttributesEnvvar(env []string, cmd *cobra.Command, plugin Plugin) []string {
|
||||||
|
if attrs := getPluginResourceAttributes(cmd, plugin); attrs.Len() > 0 {
|
||||||
|
envVarVal := attrs.Encoded(attribute.DefaultEncoder())
|
||||||
|
env = append(env, ResourceAttributesEnvvar+"="+envVarVal)
|
||||||
|
}
|
||||||
|
return env
|
||||||
|
}
|
||||||
|
|
|
@ -17,11 +17,17 @@ import (
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReexecEnvvar is the name of an ennvar which is set to the command
|
const (
|
||||||
// used to originally invoke the docker CLI when executing a
|
// ReexecEnvvar is the name of an ennvar which is set to the command
|
||||||
// plugin. Assuming $PATH and $CWD remain unchanged this should allow
|
// used to originally invoke the docker CLI when executing a
|
||||||
// the plugin to re-execute the original CLI.
|
// plugin. Assuming $PATH and $CWD remain unchanged this should allow
|
||||||
const ReexecEnvvar = "DOCKER_CLI_PLUGIN_ORIGINAL_CLI_COMMAND"
|
// the plugin to re-execute the original CLI.
|
||||||
|
ReexecEnvvar = "DOCKER_CLI_PLUGIN_ORIGINAL_CLI_COMMAND"
|
||||||
|
|
||||||
|
// ResourceAttributesEnvvar is the name of the envvar that includes additional
|
||||||
|
// resource attributes for OTEL.
|
||||||
|
ResourceAttributesEnvvar = "OTEL_RESOURCE_ATTRIBUTES"
|
||||||
|
)
|
||||||
|
|
||||||
// errPluginNotFound is the error returned when a plugin could not be found.
|
// errPluginNotFound is the error returned when a plugin could not be found.
|
||||||
type errPluginNotFound string
|
type errPluginNotFound string
|
||||||
|
@ -236,6 +242,7 @@ func PluginRunCommand(dockerCli command.Cli, name string, rootcmd *cobra.Command
|
||||||
|
|
||||||
cmd.Env = os.Environ()
|
cmd.Env = os.Environ()
|
||||||
cmd.Env = append(cmd.Env, ReexecEnvvar+"="+os.Args[0])
|
cmd.Env = append(cmd.Env, ReexecEnvvar+"="+os.Args[0])
|
||||||
|
cmd.Env = appendPluginResourceAttributesEnvvar(cmd.Env, rootcmd, plugin)
|
||||||
|
|
||||||
return cmd, nil
|
return cmd, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ func processBuilder(dockerCli command.Cli, cmd *cobra.Command, args, osargs []st
|
||||||
}
|
}
|
||||||
|
|
||||||
// is this a build that should be forwarded to the builder?
|
// is this a build that should be forwarded to the builder?
|
||||||
fwargs, fwosargs, forwarded := forwardBuilder(builderAlias, args, osargs)
|
fwargs, fwosargs, alias, forwarded := forwardBuilder(builderAlias, args, osargs)
|
||||||
if !forwarded {
|
if !forwarded {
|
||||||
return args, osargs, nil, nil
|
return args, osargs, nil, nil
|
||||||
}
|
}
|
||||||
|
@ -116,10 +116,13 @@ func processBuilder(dockerCli command.Cli, cmd *cobra.Command, args, osargs []st
|
||||||
envs = append([]string{"BUILDX_BUILDER=" + dockerCli.CurrentContext()}, envs...)
|
envs = append([]string{"BUILDX_BUILDER=" + dockerCli.CurrentContext()}, envs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// overwrite the command path for this plugin using the alias name.
|
||||||
|
cmd.Annotations[pluginmanager.CommandAnnotationPluginCommandPath] = fmt.Sprintf("%s %s", cmd.CommandPath(), alias)
|
||||||
|
|
||||||
return fwargs, fwosargs, envs, nil
|
return fwargs, fwosargs, envs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func forwardBuilder(alias string, args, osargs []string) ([]string, []string, bool) {
|
func forwardBuilder(alias string, args, osargs []string) ([]string, []string, string, bool) {
|
||||||
aliases := [][2][]string{
|
aliases := [][2][]string{
|
||||||
{
|
{
|
||||||
{"builder"},
|
{"builder"},
|
||||||
|
@ -137,10 +140,10 @@ func forwardBuilder(alias string, args, osargs []string) ([]string, []string, bo
|
||||||
for _, al := range aliases {
|
for _, al := range aliases {
|
||||||
if fwargs, changed := command.StringSliceReplaceAt(args, al[0], al[1], 0); changed {
|
if fwargs, changed := command.StringSliceReplaceAt(args, al[0], al[1], 0); changed {
|
||||||
fwosargs, _ := command.StringSliceReplaceAt(osargs, al[0], al[1], -1)
|
fwosargs, _ := command.StringSliceReplaceAt(osargs, al[0], al[1], -1)
|
||||||
return fwargs, fwosargs, true
|
return fwargs, fwosargs, al[0][0], true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return args, osargs, false
|
return args, osargs, "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
// hasBuilderName checks if a builder name is defined in args or env vars
|
// hasBuilderName checks if a builder name is defined in args or env vars
|
||||||
|
|
|
@ -37,6 +37,7 @@ require (
|
||||||
github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a
|
github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a
|
||||||
github.com/tonistiigi/go-rosetta v0.0.0-20200727161949-f79598599c5d
|
github.com/tonistiigi/go-rosetta v0.0.0-20200727161949-f79598599c5d
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0
|
github.com/xeipuuv/gojsonschema v1.2.0
|
||||||
|
go.opentelemetry.io/otel v1.21.0
|
||||||
golang.org/x/sync v0.6.0
|
golang.org/x/sync v0.6.0
|
||||||
golang.org/x/sys v0.16.0
|
golang.org/x/sys v0.16.0
|
||||||
golang.org/x/term v0.15.0
|
golang.org/x/term v0.15.0
|
||||||
|
@ -77,7 +78,6 @@ require (
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||||
go.etcd.io/etcd/raft/v3 v3.5.6 // indirect
|
go.etcd.io/etcd/raft/v3 v3.5.6 // indirect
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect
|
||||||
go.opentelemetry.io/otel v1.21.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/metric v1.21.0 // indirect
|
go.opentelemetry.io/otel/metric v1.21.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.21.0 // indirect
|
go.opentelemetry.io/otel/trace v1.21.0 // indirect
|
||||||
golang.org/x/crypto v0.17.0 // indirect
|
golang.org/x/crypto v0.17.0 // indirect
|
||||||
|
|
Loading…
Reference in New Issue