Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions cli-plugins/manager/cobra.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ package manager

import (
"fmt"
"net/url"
"os"
"strings"
"sync"

"github.com/docker/cli/cli/command"
"github.com/spf13/cobra"
"go.opentelemetry.io/otel/attribute"
)

const (
Expand All @@ -30,6 +33,10 @@ const (
// is, one which failed it's candidate test) and contains the
// reason for the failure.
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
Expand Down Expand Up @@ -98,3 +105,44 @@ func AddPluginCommandStubs(dockerCli command.Cli, rootCmd *cobra.Command) (err e
})
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 {
// 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
attrsSlice := make([]string, attrs.Len())
for iter := attrs.Iter(); iter.Next(); {
i, v := iter.IndexedAttribute()
attrsSlice[i] = string(v.Key) + "=" + url.PathEscape(v.Value.AsString())
}
env = append(env, ResourceAttributesEnvvar+"="+strings.Join(attrsSlice, ","))
}
return env
}
17 changes: 12 additions & 5 deletions cli-plugins/manager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,17 @@ import (
"golang.org/x/sync/errgroup"
)

// ReexecEnvvar is the name of an ennvar which is set to the command
// used to originally invoke the docker CLI when executing a
// plugin. Assuming $PATH and $CWD remain unchanged this should allow
// the plugin to re-execute the original CLI.
const ReexecEnvvar = "DOCKER_CLI_PLUGIN_ORIGINAL_CLI_COMMAND"
const (
// ReexecEnvvar is the name of an ennvar which is set to the command
// used to originally invoke the docker CLI when executing a
// plugin. Assuming $PATH and $CWD remain unchanged this should allow
// 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.
type errPluginNotFound string
Expand Down Expand Up @@ -236,6 +242,7 @@ func PluginRunCommand(dockerCli command.Cli, name string, rootcmd *cobra.Command

cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, ReexecEnvvar+"="+os.Args[0])
cmd.Env = appendPluginResourceAttributesEnvvar(cmd.Env, rootcmd, plugin)

return cmd, nil
}
Expand Down
11 changes: 7 additions & 4 deletions cmd/docker/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -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?
fwargs, fwosargs, forwarded := forwardBuilder(builderAlias, args, osargs)
fwargs, fwosargs, alias, forwarded := forwardBuilder(builderAlias, args, osargs)
if !forwarded {
return args, osargs, nil, nil
}
Expand Down Expand Up @@ -116,10 +116,13 @@ func processBuilder(dockerCli command.Cli, cmd *cobra.Command, args, osargs []st
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
}

func forwardBuilder(alias string, args, osargs []string) ([]string, []string, bool) {
func forwardBuilder(alias string, args, osargs []string) ([]string, []string, string, bool) {
aliases := [][2][]string{
{
{"builder"},
Expand All @@ -137,10 +140,10 @@ func forwardBuilder(alias string, args, osargs []string) ([]string, []string, bo
for _, al := range aliases {
if fwargs, changed := command.StringSliceReplaceAt(args, al[0], al[1], 0); changed {
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
Expand Down
2 changes: 1 addition & 1 deletion vendor.mod
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ require (
github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a
github.com/tonistiigi/go-rosetta v0.0.0-20200727161949-f79598599c5d
github.com/xeipuuv/gojsonschema v1.2.0
go.opentelemetry.io/otel v1.21.0
golang.org/x/sync v0.6.0
golang.org/x/sys v0.16.0
golang.org/x/term v0.15.0
Expand Down Expand Up @@ -77,7 +78,6 @@ require (
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // 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/otel v1.21.0 // indirect
go.opentelemetry.io/otel/metric v1.21.0 // indirect
go.opentelemetry.io/otel/trace v1.21.0 // indirect
golang.org/x/crypto v0.17.0 // indirect
Expand Down