cli/command: add WithUserAgent option
Add support to the `cli/command` package to accept a custom User Agent to pass to the underlying client. This is used as the `UpstreamClient` portion of the `User-Agent` when the Moby daemon makes requests. For example, pushing and pulling images with Compose might result in the registry seeing a `User-Agent` value of: ``` docker/24.0.7 go/go1.20.10 git-commit/311b9ff kernel/6.5.13-linuxkit os/linux arch/arm64 UpstreamClient(docker-cli-plugin-compose/v2.24.0) ``` Signed-off-by: Milas Bowman <milas.bowman@docker.com> Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
committed by
Sebastiaan van Stijn
parent
8fbb70ae56
commit
048e931b42
@ -77,6 +77,7 @@ type DockerCli struct {
|
||||
dockerEndpoint docker.Endpoint
|
||||
contextStoreConfig *store.Config
|
||||
initTimeout time.Duration
|
||||
userAgent string
|
||||
res telemetryResource
|
||||
|
||||
// baseCtx is the base context used for internal operations. In the future
|
||||
@ -312,10 +313,10 @@ func NewAPIClientFromFlags(opts *cliflags.ClientOptions, configFile *configfile.
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to resolve docker endpoint: %w", err)
|
||||
}
|
||||
return newAPIClientFromEndpoint(endpoint, configFile)
|
||||
return newAPIClientFromEndpoint(endpoint, configFile, client.WithUserAgent(UserAgent()))
|
||||
}
|
||||
|
||||
func newAPIClientFromEndpoint(ep docker.Endpoint, configFile *configfile.ConfigFile) (client.APIClient, error) {
|
||||
func newAPIClientFromEndpoint(ep docker.Endpoint, configFile *configfile.ConfigFile, extraOpts ...client.Opt) (client.APIClient, error) {
|
||||
opts, err := ep.ClientOpts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -330,7 +331,7 @@ func newAPIClientFromEndpoint(ep docker.Endpoint, configFile *configfile.ConfigF
|
||||
if withCustomHeaders != nil {
|
||||
opts = append(opts, withCustomHeaders)
|
||||
}
|
||||
opts = append(opts, client.WithUserAgent(UserAgent()))
|
||||
opts = append(opts, extraOpts...)
|
||||
return client.NewClientWithOpts(opts...)
|
||||
}
|
||||
|
||||
@ -551,7 +552,8 @@ func (cli *DockerCli) initialize() error {
|
||||
return
|
||||
}
|
||||
if cli.client == nil {
|
||||
if cli.client, cli.initErr = newAPIClientFromEndpoint(cli.dockerEndpoint, cli.configFile); cli.initErr != nil {
|
||||
ops := []client.Opt{client.WithUserAgent(cli.userAgent)}
|
||||
if cli.client, cli.initErr = newAPIClientFromEndpoint(cli.dockerEndpoint, cli.configFile, ops...); cli.initErr != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -598,6 +600,7 @@ func NewDockerCli(ops ...CLIOption) (*DockerCli, error) {
|
||||
WithContentTrustFromEnv(),
|
||||
WithDefaultContextStoreConfig(),
|
||||
WithStandardStreams(),
|
||||
WithUserAgent(UserAgent()),
|
||||
}
|
||||
ops = append(defaultOps, ops...)
|
||||
|
||||
@ -621,7 +624,7 @@ func getServerHost(hosts []string, defaultToTLS bool) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// UserAgent returns the user agent string used for making API requests
|
||||
// UserAgent returns the default user agent string used for making API requests.
|
||||
func UserAgent() string {
|
||||
return "Docker-Client/" + version.Version + " (" + runtime.GOOS + ")"
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package command
|
||||
import (
|
||||
"context"
|
||||
"encoding/csv"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@ -236,3 +237,14 @@ func withCustomHeadersFromEnv() (client.Opt, error) {
|
||||
// see https://github.com/docker/cli/pull/5098#issuecomment-2147403871 (when updating, also update the WARNING in the function and env-var GoDoc)
|
||||
return client.WithHTTPHeaders(env), nil
|
||||
}
|
||||
|
||||
// WithUserAgent configures the User-Agent string for cli HTTP requests.
|
||||
func WithUserAgent(userAgent string) CLIOption {
|
||||
return func(cli *DockerCli) error {
|
||||
if userAgent == "" {
|
||||
return errors.New("user agent cannot be blank")
|
||||
}
|
||||
cli.userAgent = userAgent
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -373,3 +373,26 @@ func TestSetGoDebug(t *testing.T) {
|
||||
assert.Equal(t, "val1,val2=1", os.Getenv("GODEBUG"))
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewDockerCliWithCustomUserAgent(t *testing.T) {
|
||||
var received string
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
received = r.UserAgent()
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer ts.Close()
|
||||
host := strings.Replace(ts.URL, "http://", "tcp://", 1)
|
||||
opts := &flags.ClientOptions{Hosts: []string{host}}
|
||||
|
||||
cli, err := NewDockerCli(
|
||||
WithUserAgent("fake-agent/0.0.1"),
|
||||
)
|
||||
assert.NilError(t, err)
|
||||
cli.currentContext = DefaultContextName
|
||||
cli.options = opts
|
||||
cli.configFile = &configfile.ConfigFile{}
|
||||
|
||||
_, err = cli.Client().Ping(context.Background())
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, received, "fake-agent/0.0.1")
|
||||
}
|
||||
|
||||
@ -53,6 +53,7 @@ func newRegistryClient(dockerCLI command.Cli, allowInsecure bool) registryclient
|
||||
resolver := func(ctx context.Context, index *registry.IndexInfo) registry.AuthConfig {
|
||||
return command.ResolveAuthConfig(dockerCLI.ConfigFile(), index)
|
||||
}
|
||||
// FIXME(thaJeztah): this should use the userAgent as configured on the dockerCLI.
|
||||
return registryclient.NewRegistryClient(resolver, command.UserAgent(), allowInsecure)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user