From 9e7ce93d0423af7524f2606253bb3a5b21b43752 Mon Sep 17 00:00:00 2001 From: Dean Chen <862469039@qq.com> Date: Mon, 14 Sep 2026 19:37:03 +0500 Subject: [PATCH 1/2] service/update: pass QueryRegistry through to the API client docker service update --image was not resolving tags to digests, so updating a mutable tag left the spec unchanged and Swarm did not roll new tasks. Signed-off-by: Dean Chen <862469039@qq.com> --- cli/command/service/update.go | 13 ++++--- cli/command/service/update_test.go | 55 ++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/cli/command/service/update.go b/cli/command/service/update.go index 2e7a0ee25fce..dcc5dd236ede 100644 --- a/cli/command/service/update.go +++ b/cli/command/service/update.go @@ -232,14 +232,13 @@ func runUpdate(ctx context.Context, dockerCLI command.Cli, flags *pflag.FlagSet, registryAuthFrom = string(swarm.RegistryAuthFromSpec) } - response, err := apiClient.ServiceUpdate(ctx, res.Service.ID, client.ServiceUpdateOptions{ - Version: res.Service.Version, - Spec: *spec, + updateOpts.Version = res.Service.Version + updateOpts.Spec = *spec + updateOpts.EncodedRegistryAuth = encodedAuth + updateOpts.RegistryAuthFrom = swarm.RegistryAuthSource(registryAuthFrom) + updateOpts.Rollback = rollbackAction - EncodedRegistryAuth: encodedAuth, - RegistryAuthFrom: swarm.RegistryAuthSource(registryAuthFrom), - Rollback: rollbackAction, - }) + response, err := apiClient.ServiceUpdate(ctx, res.Service.ID, updateOpts) if err != nil { return err } diff --git a/cli/command/service/update_test.go b/cli/command/service/update_test.go index 51ba0eaec24f..1cebbf85ba54 100644 --- a/cli/command/service/update_test.go +++ b/cli/command/service/update_test.go @@ -3,12 +3,15 @@ package service import ( "context" "fmt" + "io" "net/netip" "slices" "strconv" "testing" "time" + "github.com/docker/cli/internal/test" + "github.com/docker/cli/internal/test/builders" "github.com/moby/moby/api/types/container" "github.com/moby/moby/api/types/mount" "github.com/moby/moby/api/types/network" @@ -1768,3 +1771,55 @@ func TestUpdateHostsRemoveRepeatedHost(t *testing.T) { // is listed multiple times in the same entry. assert.Check(t, is.DeepEqual([]string{"127.0.0.1 host2", "127.0.0.2 host2"}, hosts)) } + +func TestUpdatePassesQueryRegistry(t *testing.T) { + testCases := []struct { + name string + setFlags [][2]string + wantQuery bool + }{ + { + name: "image-update-queries-registry", + setFlags: [][2]string{{"image", "nginx:latest"}}, + wantQuery: true, + }, + { + name: "no-resolve-image", + setFlags: [][2]string{{"image", "nginx:latest"}, {"no-resolve-image", "true"}}, + wantQuery: false, + }, + { + name: "no-image-change", + setFlags: nil, + wantQuery: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var got client.ServiceUpdateOptions + cli := test.NewFakeCli(&fakeClient{ + serviceInspectFunc: func(ctx context.Context, serviceID string, options client.ServiceInspectOptions) (client.ServiceInspectResult, error) { + return client.ServiceInspectResult{ + Service: *builders.Service(builders.ServiceID(serviceID), builders.ServiceImage("nginx:old")), + }, nil + }, + serviceUpdateFunc: func(ctx context.Context, serviceID string, options client.ServiceUpdateOptions) (client.ServiceUpdateResult, error) { + got = options + return client.ServiceUpdateResult{}, nil + }, + }) + cmd := newUpdateCommand(cli) + cmd.SetArgs([]string{"service-id"}) + cmd.SetOut(io.Discard) + cmd.SetErr(io.Discard) + assert.NilError(t, cmd.Flags().Set("detach", "true")) + assert.NilError(t, cmd.Flags().Set("quiet", "true")) + for _, f := range tc.setFlags { + assert.NilError(t, cmd.Flags().Set(f[0], f[1])) + } + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal(got.QueryRegistry, tc.wantQuery)) + }) + } +} From f44a5c365f7df10fb48d0123250fafb90797addc Mon Sep 17 00:00:00 2001 From: Dean Chen <862469039@qq.com> Date: Tue, 15 Sep 2026 01:30:09 +0500 Subject: [PATCH 2/2] service/update: alias clitest import in QueryRegistry test revive flags import-shadowing against the existing `type test` in this file. Signed-off-by: Dean Chen <862469039@qq.com> --- cli/command/service/update_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/command/service/update_test.go b/cli/command/service/update_test.go index 1cebbf85ba54..be37ca14646f 100644 --- a/cli/command/service/update_test.go +++ b/cli/command/service/update_test.go @@ -10,7 +10,7 @@ import ( "testing" "time" - "github.com/docker/cli/internal/test" + clitest "github.com/docker/cli/internal/test" "github.com/docker/cli/internal/test/builders" "github.com/moby/moby/api/types/container" "github.com/moby/moby/api/types/mount" @@ -1798,7 +1798,7 @@ func TestUpdatePassesQueryRegistry(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { var got client.ServiceUpdateOptions - cli := test.NewFakeCli(&fakeClient{ + cli := clitest.NewFakeCli(&fakeClient{ serviceInspectFunc: func(ctx context.Context, serviceID string, options client.ServiceInspectOptions) (client.ServiceInspectResult, error) { return client.ServiceInspectResult{ Service: *builders.Service(builders.ServiceID(serviceID), builders.ServiceImage("nginx:old")),