diff --git a/cli-plugins/examples/helloworld/main.go b/cli-plugins/examples/helloworld/main.go index 0461e01c8c1d..c68b57cdbbe7 100644 --- a/cli-plugins/examples/helloworld/main.go +++ b/cli-plugins/examples/helloworld/main.go @@ -12,7 +12,8 @@ import ( ) func main() { - plugin.Run(func(dockerCli command.Cli) *cobra.Command { + ctx := context.Background() + plugin.Run(ctx, func(ctx context.Context, dockerCli command.Cli) *cobra.Command { goodbye := &cobra.Command{ Use: "goodbye", Short: "Say Goodbye instead of Hello", diff --git a/cli-plugins/plugin/plugin.go b/cli-plugins/plugin/plugin.go index 54471b41b09b..d41c7e5f7cee 100644 --- a/cli-plugins/plugin/plugin.go +++ b/cli-plugins/plugin/plugin.go @@ -65,14 +65,14 @@ func RunPlugin(dockerCli *command.DockerCli, plugin *cobra.Command, meta manager } // Run is the top-level entry point to the CLI plugin framework. It should be called from your plugin's `main()` function. -func Run(makeCmd func(command.Cli) *cobra.Command, meta manager.Metadata) { +func Run(ctx context.Context, makeCmd func(context.Context, command.Cli) *cobra.Command, meta manager.Metadata) { dockerCli, err := command.NewDockerCli() if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } - plugin := makeCmd(dockerCli) + plugin := makeCmd(ctx, dockerCli) if err := RunPlugin(dockerCli, plugin, meta); err != nil { if sterr, ok := err.(cli.StatusError); ok { @@ -92,7 +92,7 @@ func Run(makeCmd func(command.Cli) *cobra.Command, meta manager.Metadata) { } func withPluginClientConn(name string) command.CLIOption { - return command.WithInitializeClient(func(dockerCli *command.DockerCli) (client.APIClient, error) { + return command.WithInitializeClient(func(ctx context.Context, dockerCli *command.DockerCli) (client.APIClient, error) { cmd := "docker" if x := os.Getenv(manager.ReexecEnvvar); x != "" { cmd = x @@ -119,7 +119,7 @@ func withPluginClientConn(name string) command.CLIOption { return nil, err } - return client.NewClientWithOpts(client.WithDialContext(helper.Dialer)) + return client.NewClientWithOpts(ctx, client.WithDialContext(helper.Dialer)) }) } diff --git a/cli/command/cli.go b/cli/command/cli.go index aa3b28d58124..20cc46315806 100644 --- a/cli/command/cli.go +++ b/cli/command/cli.go @@ -100,11 +100,12 @@ func (cli *DockerCli) DefaultVersion() string { // CurrentVersion returns the API version currently negotiated, or the default // version otherwise. func (cli *DockerCli) CurrentVersion() string { + ctx := context.TODO() _ = cli.initialize() if cli.client == nil { return api.DefaultVersion } - return cli.client.ClientVersion() + return cli.client.ClientVersion(ctx) } // Client returns the APIClient @@ -203,10 +204,10 @@ func (cli *DockerCli) RegistryClient(allowInsecure bool) registryclient.Registry } // WithInitializeClient is passed to DockerCli.Initialize by callers who wish to set a particular API Client for use by the CLI. -func WithInitializeClient(makeClient func(dockerCli *DockerCli) (client.APIClient, error)) CLIOption { +func WithInitializeClient(makeClient func(ctx context.Context, dockerCli *DockerCli) (client.APIClient, error)) CLIOption { return func(dockerCli *DockerCli) error { var err error - dockerCli.client, err = makeClient(dockerCli) + dockerCli.client, err = makeClient(dockerCli.baseCtx, dockerCli) return err } } @@ -245,7 +246,7 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...CLIOption) } // NewAPIClientFromFlags creates a new APIClient from command line flags -func NewAPIClientFromFlags(opts *cliflags.ClientOptions, configFile *configfile.ConfigFile) (client.APIClient, error) { +func NewAPIClientFromFlags(ctx context.Context, opts *cliflags.ClientOptions, configFile *configfile.ConfigFile) (client.APIClient, error) { if opts.Context != "" && len(opts.Hosts) > 0 { return nil, errors.New("conflicting options: either specify --host or --context, not both") } @@ -261,10 +262,10 @@ func NewAPIClientFromFlags(opts *cliflags.ClientOptions, configFile *configfile. if err != nil { return nil, errors.Wrap(err, "unable to resolve docker endpoint") } - return newAPIClientFromEndpoint(endpoint, configFile) + return newAPIClientFromEndpoint(ctx, endpoint, configFile) } -func newAPIClientFromEndpoint(ep docker.Endpoint, configFile *configfile.ConfigFile) (client.APIClient, error) { +func newAPIClientFromEndpoint(ctx context.Context, ep docker.Endpoint, configFile *configfile.ConfigFile) (client.APIClient, error) { opts, err := ep.ClientOpts() if err != nil { return nil, err @@ -273,7 +274,7 @@ func newAPIClientFromEndpoint(ep docker.Endpoint, configFile *configfile.ConfigF opts = append(opts, client.WithHTTPHeaders(configFile.HTTPHeaders)) } opts = append(opts, client.WithUserAgent(UserAgent())) - return client.NewClientWithOpts(opts...) + return client.NewClientWithOpts(ctx, opts...) } func resolveDockerEndpoint(s store.Reader, contextName string) (docker.Endpoint, error) { @@ -337,7 +338,7 @@ func (cli *DockerCli) initializeFromClient() { cli.serverInfo = ServerInfo{HasExperimental: true} if ping.APIVersion != "" { - cli.client.NegotiateAPIVersionPing(ping) + cli.client.NegotiateAPIVersionPing(cli.baseCtx, ping) } return } @@ -348,7 +349,7 @@ func (cli *DockerCli) initializeFromClient() { BuildkitVersion: ping.BuilderVersion, SwarmStatus: ping.SwarmStatus, } - cli.client.NegotiateAPIVersionPing(ping) + cli.client.NegotiateAPIVersionPing(cli.baseCtx, ping) } // NotaryClient provides a Notary Repository to interact with signed metadata for an image @@ -444,7 +445,7 @@ func (cli *DockerCli) initialize() error { return } if cli.client == nil { - if cli.client, cli.initErr = newAPIClientFromEndpoint(cli.dockerEndpoint, cli.configFile); cli.initErr != nil { + if cli.client, cli.initErr = newAPIClientFromEndpoint(cli.baseCtx, cli.dockerEndpoint, cli.configFile); cli.initErr != nil { return } } diff --git a/cli/command/cli_test.go b/cli/command/cli_test.go index 7a0b4e727e2e..a46816627dfc 100644 --- a/cli/command/cli_test.go +++ b/cli/command/cli_test.go @@ -31,11 +31,14 @@ func TestNewAPIClientFromFlags(t *testing.T) { if runtime.GOOS == "windows" { host = "npipe://./" } + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + opts := &flags.ClientOptions{Hosts: []string{host}} - apiClient, err := NewAPIClientFromFlags(opts, &configfile.ConfigFile{}) + apiClient, err := NewAPIClientFromFlags(ctx, opts, &configfile.ConfigFile{}) assert.NilError(t, err) - assert.Equal(t, apiClient.DaemonHost(), host) - assert.Equal(t, apiClient.ClientVersion(), api.DefaultVersion) + assert.Equal(t, apiClient.DaemonHost(ctx), host) + assert.Equal(t, apiClient.ClientVersion(ctx), api.DefaultVersion) } func TestNewAPIClientFromFlagsForDefaultSchema(t *testing.T) { @@ -44,11 +47,14 @@ func TestNewAPIClientFromFlagsForDefaultSchema(t *testing.T) { if runtime.GOOS == "windows" { slug = "tcp://127.0.0.1" } + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + opts := &flags.ClientOptions{Hosts: []string{host}} - apiClient, err := NewAPIClientFromFlags(opts, &configfile.ConfigFile{}) + apiClient, err := NewAPIClientFromFlags(ctx, opts, &configfile.ConfigFile{}) assert.NilError(t, err) - assert.Equal(t, apiClient.DaemonHost(), slug+host) - assert.Equal(t, apiClient.ClientVersion(), api.DefaultVersion) + assert.Equal(t, apiClient.DaemonHost(ctx), slug+host) + assert.Equal(t, apiClient.ClientVersion(ctx), api.DefaultVersion) } func TestNewAPIClientFromFlagsWithCustomHeaders(t *testing.T) { @@ -69,10 +75,13 @@ func TestNewAPIClientFromFlagsWithCustomHeaders(t *testing.T) { }, } - apiClient, err := NewAPIClientFromFlags(opts, configFile) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + apiClient, err := NewAPIClientFromFlags(ctx, opts, configFile) assert.NilError(t, err) - assert.Equal(t, apiClient.DaemonHost(), host) - assert.Equal(t, apiClient.ClientVersion(), api.DefaultVersion) + assert.Equal(t, apiClient.DaemonHost(ctx), host) + assert.Equal(t, apiClient.ClientVersion(ctx), api.DefaultVersion) // verify User-Agent is not appended to the configfile. see https://github.com/docker/cli/pull/2756 assert.DeepEqual(t, configFile.HTTPHeaders, map[string]string{"My-Header": "Custom-Value"}) @@ -90,12 +99,14 @@ func TestNewAPIClientFromFlagsWithAPIVersionFromEnv(t *testing.T) { customVersion := "v3.3.3" t.Setenv("DOCKER_API_VERSION", customVersion) t.Setenv("DOCKER_HOST", ":2375") + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() opts := &flags.ClientOptions{} configFile := &configfile.ConfigFile{} - apiclient, err := NewAPIClientFromFlags(opts, configFile) + apiclient, err := NewAPIClientFromFlags(ctx, opts, configFile) assert.NilError(t, err) - assert.Equal(t, apiclient.ClientVersion(), customVersion) + assert.Equal(t, apiclient.ClientVersion(ctx), customVersion) } type fakeClient struct { @@ -109,11 +120,11 @@ func (c *fakeClient) Ping(_ context.Context) (types.Ping, error) { return c.pingFunc() } -func (c *fakeClient) ClientVersion() string { +func (c *fakeClient) ClientVersion(_ context.Context) string { return c.version } -func (c *fakeClient) NegotiateAPIVersionPing(types.Ping) { +func (c *fakeClient) NegotiateAPIVersionPing(_ context.Context, _ types.Ping) { c.negotiated = true } @@ -176,10 +187,13 @@ func TestInitializeFromClientHangs(t *testing.T) { l, err := net.Listen("unix", socket) assert.NilError(t, err) - receiveReqCh := make(chan bool) - timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Second) + ctx, cancel := context.WithCancel(context.Background()) defer cancel() + receiveReqCh := make(chan bool) + timeoutCtx, timeoutCancel := context.WithTimeout(ctx, time.Second) + defer timeoutCancel() + // Simulate a server that hangs on connections. ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { select { @@ -194,7 +208,7 @@ func TestInitializeFromClientHangs(t *testing.T) { opts := &flags.ClientOptions{Hosts: []string{fmt.Sprintf("unix://%s", socket)}} configFile := &configfile.ConfigFile{} - apiClient, err := NewAPIClientFromFlags(opts, configFile) + apiClient, err := NewAPIClientFromFlags(ctx, opts, configFile) assert.NilError(t, err) initializedCh := make(chan bool) @@ -302,8 +316,8 @@ func TestNewDockerCliAndOperators(t *testing.T) { func TestInitializeShouldAlwaysCreateTheContextStore(t *testing.T) { cli, err := NewDockerCli() assert.NilError(t, err) - assert.NilError(t, cli.Initialize(flags.NewClientOptions(), WithInitializeClient(func(cli *DockerCli) (client.APIClient, error) { - return client.NewClientWithOpts() + assert.NilError(t, cli.Initialize(flags.NewClientOptions(), WithInitializeClient(func(ctx context.Context, cli *DockerCli) (client.APIClient, error) { + return client.NewClientWithOpts(ctx) }))) assert.Check(t, cli.ContextStore() != nil) } diff --git a/cli/command/container/client_test.go b/cli/command/container/client_test.go index c45f34040f43..0b1f779e673d 100644 --- a/cli/command/container/client_test.go +++ b/cli/command/container/client_test.go @@ -128,7 +128,7 @@ func (f *fakeClient) ContainerLogs(_ context.Context, containerID string, option return nil, nil } -func (f *fakeClient) ClientVersion() string { +func (f *fakeClient) ClientVersion(_ context.Context) string { return f.Version } diff --git a/cli/command/container/create.go b/cli/command/container/create.go index 70bf509c4e61..a3aa0a51da1a 100644 --- a/cli/command/container/create.go +++ b/cli/command/container/create.go @@ -85,7 +85,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet, reportError(dockerCli.Err(), "create", err.Error(), true) return cli.StatusError{StatusCode: 125} } - proxyConfig := dockerCli.ConfigFile().ParseProxyConfig(dockerCli.Client().DaemonHost(), opts.ConvertKVStringsToMapWithNil(copts.env.GetAll())) + proxyConfig := dockerCli.ConfigFile().ParseProxyConfig(dockerCli.Client().DaemonHost(ctx), opts.ConvertKVStringsToMapWithNil(copts.env.GetAll())) newEnv := []string{} for k, v := range proxyConfig { if v == nil { @@ -100,7 +100,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet, reportError(dockerCli.Err(), "create", err.Error(), true) return cli.StatusError{StatusCode: 125} } - if err = validateAPIVersion(containerCfg, dockerCli.Client().ClientVersion()); err != nil { + if err = validateAPIVersion(containerCfg, dockerCli.Client().ClientVersion(ctx)); err != nil { reportError(dockerCli.Err(), "create", err.Error(), true) return cli.StatusError{StatusCode: 125} } @@ -236,7 +236,7 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c // create. It will produce an error if you try to set a platform on older API // versions, so check the API version here to maintain backwards // compatibility for CLI users. - if options.platform != "" && versions.GreaterThanOrEqualTo(dockerCli.Client().ClientVersion(), "1.41") { + if options.platform != "" && versions.GreaterThanOrEqualTo(dockerCli.Client().ClientVersion(ctx), "1.41") { p, err := platforms.Parse(options.platform) if err != nil { return "", errors.Wrap(err, "error parsing specified platform") diff --git a/cli/command/container/run.go b/cli/command/container/run.go index 2c4e8d41ea8f..fae684edaec9 100644 --- a/cli/command/container/run.go +++ b/cli/command/container/run.go @@ -94,7 +94,7 @@ func runRun(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet, ro reportError(dockerCli.Err(), "run", err.Error(), true) return cli.StatusError{StatusCode: 125} } - proxyConfig := dockerCli.ConfigFile().ParseProxyConfig(dockerCli.Client().DaemonHost(), opts.ConvertKVStringsToMapWithNil(copts.env.GetAll())) + proxyConfig := dockerCli.ConfigFile().ParseProxyConfig(dockerCli.Client().DaemonHost(ctx), opts.ConvertKVStringsToMapWithNil(copts.env.GetAll())) newEnv := []string{} for k, v := range proxyConfig { if v == nil { diff --git a/cli/command/container/utils.go b/cli/command/container/utils.go index 31b9df4aad63..8e0002d27b0f 100644 --- a/cli/command/container/utils.go +++ b/cli/command/container/utils.go @@ -22,7 +22,7 @@ func waitExitOrRemoved(ctx context.Context, apiClient client.APIClient, containe // Older versions used the Events API, and even older versions did not // support server-side removal. This legacyWaitExitOrRemoved method // preserves that old behavior and any issues it may have. - if versions.LessThan(apiClient.ClientVersion(), "1.30") { + if versions.LessThan(apiClient.ClientVersion(ctx), "1.30") { return legacyWaitExitOrRemoved(ctx, apiClient, containerID, waitRemove) } @@ -81,7 +81,7 @@ func legacyWaitExitOrRemoved(ctx context.Context, apiClient client.APIClient, co } if !waitRemove { stopProcessing = true - } else if versions.LessThan(apiClient.ClientVersion(), "1.25") { + } else if versions.LessThan(apiClient.ClientVersion(ctx), "1.25") { // If we are talking to an older daemon, `AutoRemove` is not supported. // We need to fall back to the old behavior, which is client-side removal go func() { diff --git a/cli/command/context/create.go b/cli/command/context/create.go index 1c93a1881aa3..9cde4a367462 100644 --- a/cli/command/context/create.go +++ b/cli/command/context/create.go @@ -5,6 +5,7 @@ package context import ( "bytes" + "context" "fmt" "github.com/docker/cli/cli" @@ -47,7 +48,8 @@ func newCreateCommand(dockerCLI command.Cli) *cobra.Command { Args: cli.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { opts.Name = args[0] - return RunCreate(dockerCLI, opts) + ctx := cmd.Context() + return RunCreate(ctx, dockerCLI, opts) }, Long: longCreateDescription(), ValidArgsFunction: completion.NoComplete, @@ -60,7 +62,7 @@ func newCreateCommand(dockerCLI command.Cli) *cobra.Command { } // RunCreate creates a Docker context -func RunCreate(dockerCLI command.Cli, o *CreateOptions) error { +func RunCreate(ctx context.Context, dockerCLI command.Cli, o *CreateOptions) error { s := dockerCLI.ContextStore() err := checkContextNameForCreation(s, o.Name) if err != nil { @@ -72,7 +74,7 @@ func RunCreate(dockerCLI command.Cli, o *CreateOptions) error { case o.From != "": err = createFromExistingContext(s, o.From, o) default: - err = createNewContext(s, o) + err = createNewContext(ctx, s, o) } if err == nil { fmt.Fprintln(dockerCLI.Out(), o.Name) @@ -81,11 +83,11 @@ func RunCreate(dockerCLI command.Cli, o *CreateOptions) error { return err } -func createNewContext(contextStore store.ReaderWriter, o *CreateOptions) error { +func createNewContext(ctx context.Context, contextStore store.ReaderWriter, o *CreateOptions) error { if o.Docker == nil { return errors.New("docker endpoint configuration is required") } - dockerEP, dockerTLS, err := getDockerEndpointMetadataAndTLS(contextStore, o.Docker) + dockerEP, dockerTLS, err := getDockerEndpointMetadataAndTLS(ctx, contextStore, o.Docker) if err != nil { return errors.Wrap(err, "unable to create docker endpoint config") } diff --git a/cli/command/context/create_test.go b/cli/command/context/create_test.go index 3a7cb238258d..e28ec5e556f6 100644 --- a/cli/command/context/create_test.go +++ b/cli/command/context/create_test.go @@ -4,6 +4,7 @@ package context import ( + "context" "fmt" "testing" @@ -93,10 +94,14 @@ func TestCreate(t *testing.T) { expecterErr: `unable to parse docker host`, }, } + + ctx, cancel := context.WithCancel(context.TODO()) + defer cancel() + for _, tc := range tests { tc := tc t.Run(tc.options.Name, func(t *testing.T) { - err := RunCreate(cli, &tc.options) + err := RunCreate(ctx, cli, &tc.options) if tc.expecterErr == "" { assert.NilError(t, err) } else { @@ -114,8 +119,10 @@ func assertContextCreateLogging(t *testing.T, cli *test.FakeCli, n string) { func TestCreateOrchestratorEmpty(t *testing.T) { cli := makeFakeCli(t) + ctx, cancel := context.WithCancel(context.TODO()) + defer cancel() - err := RunCreate(cli, &CreateOptions{ + err := RunCreate(ctx, cli, &CreateOptions{ Name: "test", Docker: map[string]string{}, }) @@ -141,9 +148,12 @@ func TestCreateFromContext(t *testing.T) { }, } + ctx, cancel := context.WithCancel(context.TODO()) + defer cancel() + cli := makeFakeCli(t) cli.ResetOutputBuffers() - assert.NilError(t, RunCreate(cli, &CreateOptions{ + assert.NilError(t, RunCreate(ctx, cli, &CreateOptions{ Name: "original", Description: "original description", Docker: map[string]string{ @@ -153,7 +163,7 @@ func TestCreateFromContext(t *testing.T) { assertContextCreateLogging(t, cli, "original") cli.ResetOutputBuffers() - assert.NilError(t, RunCreate(cli, &CreateOptions{ + assert.NilError(t, RunCreate(ctx, cli, &CreateOptions{ Name: "dummy", Description: "dummy description", Docker: map[string]string{ @@ -168,7 +178,7 @@ func TestCreateFromContext(t *testing.T) { c := c t.Run(c.name, func(t *testing.T) { cli.ResetOutputBuffers() - err := RunCreate(cli, &CreateOptions{ + err := RunCreate(ctx, cli, &CreateOptions{ From: "original", Name: c.name, Description: c.description, @@ -189,6 +199,9 @@ func TestCreateFromContext(t *testing.T) { } func TestCreateFromCurrent(t *testing.T) { + ctx, cancel := context.WithCancel(context.TODO()) + defer cancel() + cases := []struct { name string description string @@ -208,7 +221,7 @@ func TestCreateFromCurrent(t *testing.T) { cli := makeFakeCli(t) cli.ResetOutputBuffers() - assert.NilError(t, RunCreate(cli, &CreateOptions{ + assert.NilError(t, RunCreate(ctx, cli, &CreateOptions{ Name: "original", Description: "original description", Docker: map[string]string{ @@ -223,7 +236,7 @@ func TestCreateFromCurrent(t *testing.T) { c := c t.Run(c.name, func(t *testing.T) { cli.ResetOutputBuffers() - err := RunCreate(cli, &CreateOptions{ + err := RunCreate(ctx, cli, &CreateOptions{ Name: c.name, Description: c.description, }) diff --git a/cli/command/context/list_test.go b/cli/command/context/list_test.go index 98ac2e480c55..1e35eac5b238 100644 --- a/cli/command/context/list_test.go +++ b/cli/command/context/list_test.go @@ -1,6 +1,7 @@ package context import ( + "context" "testing" "github.com/docker/cli/cli/command" @@ -10,8 +11,10 @@ import ( func createTestContext(t *testing.T, cli command.Cli, name string) { t.Helper() + ctx, cancel := context.WithCancel(context.TODO()) + defer cancel() - err := RunCreate(cli, &CreateOptions{ + err := RunCreate(ctx, cli, &CreateOptions{ Name: name, Description: "description of " + name, Docker: map[string]string{keyHost: "https://someswarmserver.example.com"}, diff --git a/cli/command/context/options.go b/cli/command/context/options.go index ce79d57a4fe5..79896a96a07e 100644 --- a/cli/command/context/options.go +++ b/cli/command/context/options.go @@ -1,11 +1,12 @@ package context import ( + "context" "fmt" "strconv" "strings" - "github.com/docker/cli/cli/context" + dockerContext "github.com/docker/cli/cli/context" "github.com/docker/cli/cli/context/docker" "github.com/docker/cli/cli/context/store" "github.com/docker/docker/client" @@ -85,7 +86,7 @@ func validateConfig(config map[string]string, allowedKeys map[string]struct{}) e return errors.New(strings.Join(errs, "\n")) } -func getDockerEndpoint(contextStore store.Reader, config map[string]string) (docker.Endpoint, error) { +func getDockerEndpoint(ctx context.Context, contextStore store.Reader, config map[string]string) (docker.Endpoint, error) { if err := validateConfig(config, allowedDockerConfigKeys); err != nil { return docker.Endpoint{}, err } @@ -99,7 +100,7 @@ func getDockerEndpoint(contextStore store.Reader, config map[string]string) (doc } return docker.Endpoint{}, errors.Errorf("unable to get endpoint from context %q", contextName) } - tlsData, err := context.TLSDataFromFiles(config[keyCA], config[keyCert], config[keyKey]) + tlsData, err := dockerContext.TLSDataFromFiles(config[keyCA], config[keyCert], config[keyKey]) if err != nil { return docker.Endpoint{}, err } @@ -119,14 +120,14 @@ func getDockerEndpoint(contextStore store.Reader, config map[string]string) (doc if err != nil { return docker.Endpoint{}, errors.Wrap(err, "invalid docker endpoint options") } - if _, err := client.NewClientWithOpts(opts...); err != nil { + if _, err := client.NewClientWithOpts(ctx, opts...); err != nil { return docker.Endpoint{}, errors.Wrap(err, "unable to apply docker endpoint options") } return ep, nil } -func getDockerEndpointMetadataAndTLS(contextStore store.Reader, config map[string]string) (docker.EndpointMeta, *store.EndpointTLSData, error) { - ep, err := getDockerEndpoint(contextStore, config) +func getDockerEndpointMetadataAndTLS(ctx context.Context, contextStore store.Reader, config map[string]string) (docker.EndpointMeta, *store.EndpointTLSData, error) { + ep, err := getDockerEndpoint(ctx, contextStore, config) if err != nil { return docker.EndpointMeta{}, nil, err } diff --git a/cli/command/context/update.go b/cli/command/context/update.go index 98eba7d1ecaa..a6aa145b5cd8 100644 --- a/cli/command/context/update.go +++ b/cli/command/context/update.go @@ -2,6 +2,7 @@ package context import ( "bytes" + "context" "fmt" "github.com/docker/cli/cli" @@ -41,7 +42,8 @@ func newUpdateCommand(dockerCli command.Cli) *cobra.Command { Args: cli.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { opts.Name = args[0] - return RunUpdate(dockerCli, opts) + ctx := cmd.Context() + return RunUpdate(ctx, dockerCli, opts) }, Long: longUpdateDescription(), } @@ -52,7 +54,7 @@ func newUpdateCommand(dockerCli command.Cli) *cobra.Command { } // RunUpdate updates a Docker context -func RunUpdate(dockerCLI command.Cli, o *UpdateOptions) error { +func RunUpdate(ctx context.Context, dockerCLI command.Cli, o *UpdateOptions) error { if err := store.ValidateContextName(o.Name); err != nil { return err } @@ -74,7 +76,7 @@ func RunUpdate(dockerCLI command.Cli, o *UpdateOptions) error { tlsDataToReset := make(map[string]*store.EndpointTLSData) if o.Docker != nil { - dockerEP, dockerTLS, err := getDockerEndpointMetadataAndTLS(s, o.Docker) + dockerEP, dockerTLS, err := getDockerEndpointMetadataAndTLS(ctx, s, o.Docker) if err != nil { return errors.Wrap(err, "unable to create docker endpoint config") } diff --git a/cli/command/context/update_test.go b/cli/command/context/update_test.go index 5bad6dec2f79..371bfc0b8313 100644 --- a/cli/command/context/update_test.go +++ b/cli/command/context/update_test.go @@ -1,6 +1,7 @@ package context import ( + "context" "testing" "github.com/docker/cli/cli/command" @@ -10,15 +11,19 @@ import ( ) func TestUpdateDescriptionOnly(t *testing.T) { + ctx, cancel := context.WithCancel(context.TODO()) + defer cancel() + cli := makeFakeCli(t) - err := RunCreate(cli, &CreateOptions{ + err := RunCreate(ctx, cli, &CreateOptions{ Name: "test", Docker: map[string]string{}, }) assert.NilError(t, err) cli.OutBuffer().Reset() cli.ErrBuffer().Reset() - assert.NilError(t, RunUpdate(cli, &UpdateOptions{ + + assert.NilError(t, RunUpdate(ctx, cli, &UpdateOptions{ Name: "test", Description: "description", })) @@ -35,7 +40,11 @@ func TestUpdateDescriptionOnly(t *testing.T) { func TestUpdateDockerOnly(t *testing.T) { cli := makeFakeCli(t) createTestContext(t, cli, "test") - assert.NilError(t, RunUpdate(cli, &UpdateOptions{ + + ctx, cancel := context.WithCancel(context.TODO()) + defer cancel() + + assert.NilError(t, RunUpdate(ctx, cli, &UpdateOptions{ Name: "test", Docker: map[string]string{ keyHost: "tcp://some-host", @@ -52,12 +61,17 @@ func TestUpdateDockerOnly(t *testing.T) { func TestUpdateInvalidDockerHost(t *testing.T) { cli := makeFakeCli(t) - err := RunCreate(cli, &CreateOptions{ + + ctx, cancel := context.WithCancel(context.TODO()) + defer cancel() + + err := RunCreate(ctx, cli, &CreateOptions{ Name: "test", Docker: map[string]string{}, }) assert.NilError(t, err) - err = RunUpdate(cli, &UpdateOptions{ + + err = RunUpdate(ctx, cli, &UpdateOptions{ Name: "test", Docker: map[string]string{ keyHost: "some///invalid/host", diff --git a/cli/command/context/use_test.go b/cli/command/context/use_test.go index 0d95ee2cb33b..5db65580d07b 100644 --- a/cli/command/context/use_test.go +++ b/cli/command/context/use_test.go @@ -2,6 +2,7 @@ package context import ( "bytes" + "context" "errors" "io" "os" @@ -19,11 +20,14 @@ import ( ) func TestUse(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + configDir := t.TempDir() configFilePath := filepath.Join(configDir, "config.json") testCfg := configfile.New(configFilePath) cli := makeFakeCli(t, withCliConfig(testCfg)) - err := RunCreate(cli, &CreateOptions{ + err := RunCreate(ctx, cli, &CreateOptions{ Name: "test", Docker: map[string]string{}, }) @@ -84,12 +88,14 @@ func TestUseDefaultWithoutConfigFile(t *testing.T) { func TestUseHostOverride(t *testing.T) { t.Setenv("DOCKER_HOST", "tcp://ed:2375/") + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() configDir := t.TempDir() configFilePath := filepath.Join(configDir, "config.json") testCfg := configfile.New(configFilePath) cli := makeFakeCli(t, withCliConfig(testCfg)) - err := RunCreate(cli, &CreateOptions{ + err := RunCreate(ctx, cli, &CreateOptions{ Name: "test", Docker: map[string]string{}, }) @@ -119,6 +125,9 @@ func TestUseHostOverride(t *testing.T) { func TestUseHostOverrideEmpty(t *testing.T) { t.Setenv("DOCKER_HOST", "") + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + configDir := t.TempDir() config.SetDir(configDir) @@ -136,7 +145,7 @@ func TestUseHostOverrideEmpty(t *testing.T) { assert.NilError(t, cli.Initialize(flags.NewClientOptions())) } loadCli() - err := RunCreate(cli, &CreateOptions{ + err := RunCreate(ctx, cli, &CreateOptions{ Name: "test", Docker: map[string]string{"host": socketPath}, }) @@ -153,5 +162,5 @@ func TestUseHostOverrideEmpty(t *testing.T) { assert.Assert(t, is.Contains(out.String(), "test")) apiclient := cli.Client() - assert.Equal(t, apiclient.DaemonHost(), socketPath) + assert.Equal(t, apiclient.DaemonHost(ctx), socketPath) } diff --git a/cli/command/image/build.go b/cli/command/image/build.go index 61a5ed4dbb86..b93c086092b4 100644 --- a/cli/command/image/build.go +++ b/cli/command/image/build.go @@ -325,7 +325,7 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions) for k, auth := range creds { authConfigs[k] = registrytypes.AuthConfig(auth) } - buildOptions := imageBuildOptions(dockerCli, options) + buildOptions := imageBuildOptions(ctx, dockerCli, options) buildOptions.Version = types.BuilderV1 buildOptions.Dockerfile = relDockerfile buildOptions.AuthConfigs = authConfigs @@ -529,7 +529,7 @@ func replaceDockerfileForContentTrust(ctx context.Context, inputTarStream io.Rea return pipeReader } -func imageBuildOptions(dockerCli command.Cli, options buildOptions) types.ImageBuildOptions { +func imageBuildOptions(ctx context.Context, dockerCli command.Cli, options buildOptions) types.ImageBuildOptions { configFile := dockerCli.ConfigFile() return types.ImageBuildOptions{ Memory: options.memory.Value(), @@ -549,7 +549,7 @@ func imageBuildOptions(dockerCli command.Cli, options buildOptions) types.ImageB CgroupParent: options.cgroupParent, ShmSize: options.shmSize.Value(), Ulimits: options.ulimits.GetList(), - BuildArgs: configFile.ParseProxyConfig(dockerCli.Client().DaemonHost(), opts.ConvertKVStringsToMapWithNil(options.buildArgs.GetAll())), + BuildArgs: configFile.ParseProxyConfig(dockerCli.Client().DaemonHost(ctx), opts.ConvertKVStringsToMapWithNil(options.buildArgs.GetAll())), Labels: opts.ConvertKVStringsToMap(options.labels.GetAll()), CacheFrom: options.cacheFrom, SecurityOpt: options.securityOpt, diff --git a/cli/command/plugin/install.go b/cli/command/plugin/install.go index f1aa9d872593..6d9ea7da5fda 100644 --- a/cli/command/plugin/install.go +++ b/cli/command/plugin/install.go @@ -136,13 +136,12 @@ func runInstall(ctx context.Context, dockerCli command.Cli, opts pluginOptions) return nil } -func acceptPrivileges(dockerCli command.Cli, name string) func(privileges types.PluginPrivileges) (bool, error) { - return func(privileges types.PluginPrivileges) (bool, error) { +func acceptPrivileges(dockerCli command.Cli, name string) func(ctx context.Context, privileges types.PluginPrivileges) (bool, error) { + return func(ctx context.Context, privileges types.PluginPrivileges) (bool, error) { fmt.Fprintf(dockerCli.Out(), "Plugin %q is requesting the following privileges:\n", name) for _, privilege := range privileges { fmt.Fprintf(dockerCli.Out(), " - %s: %v\n", privilege.Name, privilege.Value) } - ctx := context.TODO() return command.PromptForConfirmation(ctx, dockerCli.In(), dockerCli.Out(), "Do you grant the above permissions?") } } diff --git a/cli/command/registry.go b/cli/command/registry.go index a1a499eee46a..3afda4d970f9 100644 --- a/cli/command/registry.go +++ b/cli/command/registry.go @@ -2,6 +2,7 @@ package command import ( "bufio" + "context" "fmt" "io" "os" @@ -27,7 +28,7 @@ const patSuggest = "You can log in with your password or a Personal Access " + // RegistryAuthenticationPrivilegedFunc returns a RequestPrivilegeFunc from the specified registry index info // for the given command. func RegistryAuthenticationPrivilegedFunc(cli Cli, index *registrytypes.IndexInfo, cmdName string) types.RequestPrivilegeFunc { - return func() (string, error) { + return func(ctx context.Context) (string, error) { fmt.Fprintf(cli.Out(), "\nPlease login prior to %s:\n", cmdName) indexServer := registry.GetAuthConfigKey(index) isDefaultRegistry := indexServer == registry.IndexServer diff --git a/cli/command/service/create.go b/cli/command/service/create.go index 6e49558609a1..08341f1db26f 100644 --- a/cli/command/service/create.go +++ b/cli/command/service/create.go @@ -85,7 +85,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet, return err } - if err = validateAPIVersion(service, dockerCli.Client().ClientVersion()); err != nil { + if err = validateAPIVersion(service, dockerCli.Client().ClientVersion(ctx)); err != nil { return err } @@ -118,7 +118,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet, } // query registry if flag disabling it was not set - if !opts.noResolveImage && versions.GreaterThanOrEqualTo(apiClient.ClientVersion(), "1.30") { + if !opts.noResolveImage && versions.GreaterThanOrEqualTo(apiClient.ClientVersion(ctx), "1.30") { createOpts.QueryRegistry = true } @@ -133,7 +133,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet, fmt.Fprintf(dockerCli.Out(), "%s\n", response.ID) - if opts.detach || versions.LessThan(apiClient.ClientVersion(), "1.29") { + if opts.detach || versions.LessThan(apiClient.ClientVersion(ctx), "1.29") { return nil } diff --git a/cli/command/service/rollback.go b/cli/command/service/rollback.go index 06be65bb9084..7bfcb95f12f1 100644 --- a/cli/command/service/rollback.go +++ b/cli/command/service/rollback.go @@ -58,7 +58,7 @@ func runRollback(ctx context.Context, dockerCli command.Cli, options *serviceOpt fmt.Fprintf(dockerCli.Out(), "%s\n", serviceID) - if options.detach || versions.LessThan(apiClient.ClientVersion(), "1.29") { + if options.detach || versions.LessThan(apiClient.ClientVersion(ctx), "1.29") { return nil } diff --git a/cli/command/service/scale.go b/cli/command/service/scale.go index fd863049893d..5ad80bc46da4 100644 --- a/cli/command/service/scale.go +++ b/cli/command/service/scale.go @@ -78,7 +78,7 @@ func runScale(ctx context.Context, dockerCli command.Cli, options *scaleOptions, } if len(serviceIDs) > 0 { - if !options.detach && versions.GreaterThanOrEqualTo(dockerCli.Client().ClientVersion(), "1.29") { + if !options.detach && versions.GreaterThanOrEqualTo(dockerCli.Client().ClientVersion(ctx), "1.29") { for _, serviceID := range serviceIDs { if err := WaitOnService(ctx, dockerCli, serviceID, false); err != nil { errs = append(errs, fmt.Sprintf("%s: %v", serviceID, err)) diff --git a/cli/command/service/update.go b/cli/command/service/update.go index d25229802e18..c2a0f9da0cd3 100644 --- a/cli/command/service/update.go +++ b/cli/command/service/update.go @@ -164,7 +164,7 @@ func runUpdate(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet, return errors.New("other flags may not be combined with --rollback") } - if versions.LessThan(apiClient.ClientVersion(), "1.28") { + if versions.LessThan(apiClient.ClientVersion(ctx), "1.28") { clientSideRollback = true spec = service.PreviousSpec if spec == nil { @@ -189,7 +189,7 @@ func runUpdate(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet, if err := resolveServiceImageDigestContentTrust(dockerCli, spec); err != nil { return err } - if !options.noResolveImage && versions.GreaterThanOrEqualTo(apiClient.ClientVersion(), "1.30") { + if !options.noResolveImage && versions.GreaterThanOrEqualTo(apiClient.ClientVersion(ctx), "1.30") { updateOpts.QueryRegistry = true } } @@ -245,7 +245,7 @@ func runUpdate(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet, fmt.Fprintf(dockerCli.Out(), "%s\n", serviceID) - if options.detach || versions.LessThan(apiClient.ClientVersion(), "1.29") { + if options.detach || versions.LessThan(apiClient.ClientVersion(ctx), "1.29") { return nil } diff --git a/cli/command/stack/client_test.go b/cli/command/stack/client_test.go index 4cb8a3b5db2e..050f4516266d 100644 --- a/cli/command/stack/client_test.go +++ b/cli/command/stack/client_test.go @@ -50,7 +50,7 @@ func (cli *fakeClient) ServerVersion(context.Context) (types.Version, error) { }, nil } -func (cli *fakeClient) ClientVersion() string { +func (cli *fakeClient) ClientVersion(_ context.Context) string { return cli.version } diff --git a/cli/command/stack/swarm/client_test.go b/cli/command/stack/swarm/client_test.go index afb93458f061..e19e9ba5635f 100644 --- a/cli/command/stack/swarm/client_test.go +++ b/cli/command/stack/swarm/client_test.go @@ -50,7 +50,7 @@ func (cli *fakeClient) ServerVersion(context.Context) (types.Version, error) { }, nil } -func (cli *fakeClient) ClientVersion() string { +func (cli *fakeClient) ClientVersion(ctx context.Context) string { return cli.version } diff --git a/cli/command/stack/swarm/deploy.go b/cli/command/stack/swarm/deploy.go index 7fe52f67fd72..c184c837b29e 100644 --- a/cli/command/stack/swarm/deploy.go +++ b/cli/command/stack/swarm/deploy.go @@ -29,7 +29,7 @@ func RunDeploy(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet, } // client side image resolution should not be done when the supported // server version is older than 1.30 - if versions.LessThan(dockerCli.Client().ClientVersion(), "1.30") { + if versions.LessThan(dockerCli.Client().ClientVersion(ctx), "1.30") { opts.ResolveImage = ResolveImageNever } diff --git a/cli/command/stack/swarm/remove.go b/cli/command/stack/swarm/remove.go index 5a901fbf8c55..124308f96b4f 100644 --- a/cli/command/stack/swarm/remove.go +++ b/cli/command/stack/swarm/remove.go @@ -32,7 +32,7 @@ func RunRemove(ctx context.Context, dockerCli command.Cli, opts options.Remove) } var secrets []swarm.Secret - if versions.GreaterThanOrEqualTo(client.ClientVersion(), "1.25") { + if versions.GreaterThanOrEqualTo(client.ClientVersion(ctx), "1.25") { secrets, err = getStackSecrets(ctx, client, namespace) if err != nil { return err @@ -40,7 +40,7 @@ func RunRemove(ctx context.Context, dockerCli command.Cli, opts options.Remove) } var configs []swarm.Config - if versions.GreaterThanOrEqualTo(client.ClientVersion(), "1.30") { + if versions.GreaterThanOrEqualTo(client.ClientVersion(ctx), "1.30") { configs, err = getStackConfigs(ctx, client, namespace) if err != nil { return err diff --git a/cli/command/system/client_test.go b/cli/command/system/client_test.go index 01dc4b3d25fc..a9853758a922 100644 --- a/cli/command/system/client_test.go +++ b/cli/command/system/client_test.go @@ -23,7 +23,7 @@ func (cli *fakeClient) ServerVersion(ctx context.Context) (types.Version, error) return cli.serverVersion(ctx) } -func (cli *fakeClient) ClientVersion() string { +func (cli *fakeClient) ClientVersion(context.Context) string { return cli.version } diff --git a/cli/command/system/dial_stdio.go b/cli/command/system/dial_stdio.go index d4193ce8af78..20c7abd9d1f6 100644 --- a/cli/command/system/dial_stdio.go +++ b/cli/command/system/dial_stdio.go @@ -32,7 +32,7 @@ func runDialStdio(ctx context.Context, dockerCli command.Cli) error { ctx, cancel := context.WithCancel(ctx) defer cancel() - dialer := dockerCli.Client().Dialer() + dialer := dockerCli.Client().Dialer(ctx) conn, err := dialer(ctx) if err != nil { return errors.Wrap(err, "failed to open the raw stream connection") diff --git a/cli/command/system/prune.go b/cli/command/system/prune.go index b3200ecfad86..7768157c4af7 100644 --- a/cli/command/system/prune.go +++ b/cli/command/system/prune.go @@ -39,7 +39,8 @@ func newPruneCommand(dockerCli command.Cli) *cobra.Command { Short: "Remove unused data", Args: cli.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - options.pruneBuildCache = versions.GreaterThanOrEqualTo(dockerCli.Client().ClientVersion(), "1.31") + ctx := cmd.Context() + options.pruneBuildCache = versions.GreaterThanOrEqualTo(dockerCli.Client().ClientVersion(ctx), "1.31") return runPrune(cmd.Context(), dockerCli, options) }, Annotations: map[string]string{"version": "1.25"}, diff --git a/cli/compose/convert/service.go b/cli/compose/convert/service.go index 708c5a7d202f..d79628dcb37a 100644 --- a/cli/compose/convert/service.go +++ b/cli/compose/convert/service.go @@ -42,7 +42,7 @@ func Services( return nil, errors.Wrapf(err, "service %s", service.Name) } - serviceSpec, err := Service(apiClient.ClientVersion(), namespace, service, config.Networks, config.Volumes, secrets, configs) + serviceSpec, err := Service(apiClient.ClientVersion(ctx), namespace, service, config.Networks, config.Volumes, secrets, configs) if err != nil { return nil, errors.Wrapf(err, "service %s", service.Name) } diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index cfc53a6fa170..9f5ffe3a043e 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -1,6 +1,7 @@ package main import ( + "context" "fmt" "net" "os" @@ -25,6 +26,16 @@ import ( ) func main() { + ctx := context.Background() + otelShutdown, err := setupOTelSDK(ctx) + if err != nil { + return + } + // Handle shutdown properly so nothing leaks. + defer func() { + otelShutdown(context.Background()) + }() + dockerCli, err := command.NewDockerCli() if err != nil { fmt.Fprintln(os.Stderr, err) diff --git a/cmd/docker/otel.go b/cmd/docker/otel.go new file mode 100644 index 000000000000..30ddf4ae79b7 --- /dev/null +++ b/cmd/docker/otel.go @@ -0,0 +1,104 @@ +package main + +import ( + "context" + "errors" + "time" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" + "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/resource" + "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.24.0" +) + +// setupOTelSDK bootstraps the OpenTelemetry pipeline. +// If it does not return an error, make sure to call shutdown for proper cleanup. +func setupOTelSDK(ctx context.Context) (shutdown func(context.Context) error, err error) { + var shutdownFuncs []func(context.Context) error + + // shutdown calls cleanup functions registered via shutdownFuncs. + // The errors from the calls are joined. + // Each registered cleanup will be invoked once. + shutdown = func(ctx context.Context) error { + var err error + for _, fn := range shutdownFuncs { + err = errors.Join(err, fn(ctx)) + } + shutdownFuncs = nil + return err + } + + // handleErr calls shutdown for cleanup and makes sure that all errors are returned. + handleErr := func(inErr error) { + err = errors.Join(inErr, shutdown(ctx)) + } + + // Set up propagator. + prop := newPropagator() + otel.SetTextMapPropagator(prop) + + exporter, err := otlptrace.New( + context.Background(), + otlptracehttp.NewClient(otlptracehttp.WithInsecure()), + ) + + // rpcmetricsObserver := rpcmetrics.NewObserver(metricsFactory, rpcmetrics.DefaultNameNormalizer) + + res, err := resource.New( + context.Background(), + resource.WithSchemaURL(semconv.SchemaURL), + resource.WithAttributes(semconv.ServiceNameKey.String("TEST")), + resource.WithTelemetrySDK(), + resource.WithHost(), + resource.WithOSType(), + ) + if err != nil { + panic(err) + } + + tp := trace.NewTracerProvider( + trace.WithBatcher(exporter, trace.WithBatchTimeout(1000*time.Millisecond)), + // trace.WithSpanProcessor(rpcmetricsObserver), + trace.WithResource(res), + ) + + shutdownFuncs = append(shutdownFuncs, tp.Shutdown) + otel.SetTracerProvider(tp) + + // Set up meter provider. + meterProvider, err := newMeterProvider() + if err != nil { + handleErr(err) + return + } + shutdownFuncs = append(shutdownFuncs, meterProvider.Shutdown) + otel.SetMeterProvider(meterProvider) + + return +} + +func newPropagator() propagation.TextMapPropagator { + return propagation.NewCompositeTextMapPropagator( + propagation.TraceContext{}, + propagation.Baggage{}, + ) +} + +func newMeterProvider() (*metric.MeterProvider, error) { + metricExporter, err := stdoutmetric.New() + if err != nil { + return nil, err + } + + meterProvider := metric.NewMeterProvider( + metric.WithReader(metric.NewPeriodicReader(metricExporter, + // Default is 1m. Set to 3s for demonstrative purposes. + metric.WithInterval(3*time.Second))), + ) + return meterProvider, nil +} diff --git a/e2e/cli-plugins/plugins/nopersistentprerun/main.go b/e2e/cli-plugins/plugins/nopersistentprerun/main.go index 5f07bf7e012e..4e63d1d86149 100644 --- a/e2e/cli-plugins/plugins/nopersistentprerun/main.go +++ b/e2e/cli-plugins/plugins/nopersistentprerun/main.go @@ -1,6 +1,7 @@ package main import ( + "context" "fmt" "github.com/docker/cli/cli-plugins/manager" @@ -10,7 +11,8 @@ import ( ) func main() { - plugin.Run(func(dockerCli command.Cli) *cobra.Command { + ctx := context.Background() + plugin.Run(ctx, func(ctx context.Context, dockerCli command.Cli) *cobra.Command { cmd := &cobra.Command{ Use: "nopersistentprerun", Short: "Testing without PersistentPreRun hooks", diff --git a/e2e/cli-plugins/plugins/presocket/main.go b/e2e/cli-plugins/plugins/presocket/main.go index 6cdf87a42402..5986c4a9cb72 100644 --- a/e2e/cli-plugins/plugins/presocket/main.go +++ b/e2e/cli-plugins/plugins/presocket/main.go @@ -1,6 +1,7 @@ package main import ( + "context" "fmt" "os" "os/signal" @@ -14,14 +15,15 @@ import ( ) func main() { - plugin.Run(RootCmd, manager.Metadata{ + ctx := context.TODO() + plugin.Run(ctx, RootCmd, manager.Metadata{ SchemaVersion: "0.1.0", Vendor: "Docker Inc.", Version: "test", }) } -func RootCmd(dockerCli command.Cli) *cobra.Command { +func RootCmd(ctx context.Context, dockerCli command.Cli) *cobra.Command { cmd := cobra.Command{ Use: "presocket", Short: "testing plugin that does not connect to the socket", diff --git a/internal/test/cli.go b/internal/test/cli.go index 2315a7a1f22b..005f0fd720cb 100644 --- a/internal/test/cli.go +++ b/internal/test/cli.go @@ -40,6 +40,8 @@ type FakeCli struct { dockerEndpoint docker.Endpoint } +var _ command.Cli = (*FakeCli)(nil) + // NewFakeCli returns a fake for the command.Cli interface func NewFakeCli(apiClient client.APIClient, opts ...func(*FakeCli)) *FakeCli { outBuffer := new(bytes.Buffer)