diff --git a/cli/command/container/exec_test.go b/cli/command/container/exec_test.go index fb8c5c7467d4..ed34e4777fe6 100644 --- a/cli/command/container/exec_test.go +++ b/cli/command/container/exec_test.go @@ -162,7 +162,7 @@ func TestRunExec(t *testing.T) { testcases := []struct { doc string options ExecOptions - client fakeClient + client *fakeClient expectedError string expectedOut string expectedErr string @@ -172,12 +172,12 @@ func TestRunExec(t *testing.T) { options: withDefaultOpts(ExecOptions{ Detach: true, }), - client: fakeClient{execCreateFunc: execCreateWithID}, + client: &fakeClient{execCreateFunc: execCreateWithID}, }, { doc: "inspect error", options: NewExecOptions(), - client: fakeClient{ + client: &fakeClient{ inspectFunc: func(string) (types.ContainerJSON, error) { return types.ContainerJSON{}, errors.New("failed inspect") }, @@ -188,12 +188,13 @@ func TestRunExec(t *testing.T) { doc: "missing exec ID", options: NewExecOptions(), expectedError: "exec ID empty", + client: &fakeClient{}, }, } for _, testcase := range testcases { t.Run(testcase.doc, func(t *testing.T) { - fakeCLI := test.NewFakeCli(&testcase.client) + fakeCLI := test.NewFakeCli(testcase.client) err := RunExec(context.TODO(), fakeCLI, "thecontainer", testcase.options) if testcase.expectedError != "" { diff --git a/cli/command/container/logs_test.go b/cli/command/container/logs_test.go index e1d238e6f90a..4a27cfaf5db4 100644 --- a/cli/command/container/logs_test.go +++ b/cli/command/container/logs_test.go @@ -30,7 +30,7 @@ func TestRunLogs(t *testing.T) { testcases := []struct { doc string options *logsOptions - client fakeClient + client *fakeClient expectedError string expectedOut string expectedErr string @@ -39,13 +39,13 @@ func TestRunLogs(t *testing.T) { doc: "successful logs", expectedOut: "foo", options: &logsOptions{}, - client: fakeClient{logFunc: logFn("foo"), inspectFunc: inspectFn}, + client: &fakeClient{logFunc: logFn("foo"), inspectFunc: inspectFn}, }, } for _, testcase := range testcases { t.Run(testcase.doc, func(t *testing.T) { - cli := test.NewFakeCli(&testcase.client) + cli := test.NewFakeCli(testcase.client) err := runLogs(context.TODO(), cli, testcase.options) if testcase.expectedError != "" { diff --git a/cli/command/registry/login_test.go b/cli/command/registry/login_test.go index db88af3117b8..9d87ad766773 100644 --- a/cli/command/registry/login_test.go +++ b/cli/command/registry/login_test.go @@ -29,11 +29,11 @@ type fakeClient struct { client.Client } -func (c fakeClient) Info(context.Context) (system.Info, error) { +func (c *fakeClient) Info(context.Context) (system.Info, error) { return system.Info{}, nil } -func (c fakeClient) RegistryLogin(_ context.Context, auth registrytypes.AuthConfig) (registrytypes.AuthenticateOKBody, error) { +func (c *fakeClient) RegistryLogin(_ context.Context, auth registrytypes.AuthConfig) (registrytypes.AuthenticateOKBody, error) { if auth.Password == expiredPassword { return registrytypes.AuthenticateOKBody{}, errors.New("Invalid Username or Password") } diff --git a/vendor.mod b/vendor.mod index 16c353a20ce2..cf291d77d8c8 100644 --- a/vendor.mod +++ b/vendor.mod @@ -12,7 +12,7 @@ require ( github.com/creack/pty v1.1.21 github.com/distribution/reference v0.6.0 github.com/docker/distribution v2.8.3+incompatible - github.com/docker/docker v27.0.0-rc.2.0.20240620105908-1a1f3cff45ec+incompatible // master (v27.0-dev) + github.com/docker/docker v27.0.0-rc.2.0.20240620115510-018d93decfb5+incompatible // master (v27.0-dev) github.com/docker/docker-credential-helpers v0.8.2 github.com/docker/go-connections v0.5.0 github.com/docker/go-units v0.5.0 diff --git a/vendor.sum b/vendor.sum index 0865547c7cd2..f71996f78a9a 100644 --- a/vendor.sum +++ b/vendor.sum @@ -57,8 +57,8 @@ github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5 github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v27.0.0-rc.2.0.20240620105908-1a1f3cff45ec+incompatible h1:lN0HDqbNJr4SrjQyG7dko0wE6hxVSXXCNAw8DTO1Wm4= -github.com/docker/docker v27.0.0-rc.2.0.20240620105908-1a1f3cff45ec+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v27.0.0-rc.2.0.20240620115510-018d93decfb5+incompatible h1:u7LstJjIAyW2WqbehgZbp/aJFlt0IP53bXOes3XajVY= +github.com/docker/docker v27.0.0-rc.2.0.20240620115510-018d93decfb5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0= diff --git a/vendor/github.com/docker/docker/client/client.go b/vendor/github.com/docker/docker/client/client.go index f2eeb6c5702e..60d91bc65b5a 100644 --- a/vendor/github.com/docker/docker/client/client.go +++ b/vendor/github.com/docker/docker/client/client.go @@ -49,6 +49,8 @@ import ( "net/url" "path" "strings" + "sync" + "sync/atomic" "time" "github.com/docker/docker/api" @@ -131,7 +133,10 @@ type Client struct { negotiateVersion bool // negotiated indicates that API version negotiation took place - negotiated bool + negotiated atomic.Bool + + // negotiateLock is used to single-flight the version negotiation process + negotiateLock sync.Mutex tp trace.TracerProvider @@ -266,7 +271,16 @@ func (cli *Client) Close() error { // be negotiated when making the actual requests, and for which cases // we cannot do the negotiation lazily. func (cli *Client) checkVersion(ctx context.Context) error { - if !cli.manualOverride && cli.negotiateVersion && !cli.negotiated { + if !cli.manualOverride && cli.negotiateVersion && !cli.negotiated.Load() { + // Ensure exclusive write access to version and negotiated fields + cli.negotiateLock.Lock() + defer cli.negotiateLock.Unlock() + + // May have been set during last execution of critical zone + if cli.negotiated.Load() { + return nil + } + ping, err := cli.Ping(ctx) if err != nil { return err @@ -312,6 +326,10 @@ func (cli *Client) ClientVersion() string { // added (1.24). func (cli *Client) NegotiateAPIVersion(ctx context.Context) { if !cli.manualOverride { + // Avoid concurrent modification of version-related fields + cli.negotiateLock.Lock() + defer cli.negotiateLock.Unlock() + ping, err := cli.Ping(ctx) if err != nil { // FIXME(thaJeztah): Ping returns an error when failing to connect to the API; we should not swallow the error here, and instead returning it. @@ -336,6 +354,10 @@ func (cli *Client) NegotiateAPIVersion(ctx context.Context) { // added (1.24). func (cli *Client) NegotiateAPIVersionPing(pingResponse types.Ping) { if !cli.manualOverride { + // Avoid concurrent modification of version-related fields + cli.negotiateLock.Lock() + defer cli.negotiateLock.Unlock() + cli.negotiateAPIVersionPing(pingResponse) } } @@ -361,7 +383,7 @@ func (cli *Client) negotiateAPIVersionPing(pingResponse types.Ping) { // Store the results, so that automatic API version negotiation (if enabled) // won't be performed on the next request. if cli.negotiateVersion { - cli.negotiated = true + cli.negotiated.Store(true) } } diff --git a/vendor/modules.txt b/vendor/modules.txt index cbecda72bb7f..70af7450520a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -53,7 +53,7 @@ github.com/docker/distribution/registry/client/transport github.com/docker/distribution/registry/storage/cache github.com/docker/distribution/registry/storage/cache/memory github.com/docker/distribution/uuid -# github.com/docker/docker v27.0.0-rc.2.0.20240620105908-1a1f3cff45ec+incompatible +# github.com/docker/docker v27.0.0-rc.2.0.20240620115510-018d93decfb5+incompatible ## explicit github.com/docker/docker/api github.com/docker/docker/api/types