diff --git a/command/service/update.go b/command/service/update.go index b529331500..ab8391e038 100644 --- a/command/service/update.go +++ b/command/service/update.go @@ -1,6 +1,7 @@ package service import ( + "errors" "fmt" "sort" "strings" @@ -10,6 +11,7 @@ import ( "github.com/docker/docker/api/types/container" mounttypes "github.com/docker/docker/api/types/mount" "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/api/types/versions" "github.com/docker/docker/cli" "github.com/docker/docker/cli/command" "github.com/docker/docker/client" @@ -95,7 +97,6 @@ func newListOptsVar() *opts.ListOpts { func runUpdate(dockerCli *command.DockerCli, flags *pflag.FlagSet, serviceID string) error { apiClient := dockerCli.Client() ctx := context.Background() - updateOpts := types.ServiceUpdateOptions{} service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID) if err != nil { @@ -107,12 +108,44 @@ func runUpdate(dockerCli *command.DockerCli, flags *pflag.FlagSet, serviceID str return err } + // There are two ways to do user-requested rollback. The old way is + // client-side, but with a sufficiently recent daemon we prefer + // server-side, because it will honor the rollback parameters. + var ( + clientSideRollback bool + serverSideRollback bool + ) + spec := &service.Spec if rollback { - spec = service.PreviousSpec - if spec == nil { - return fmt.Errorf("service does not have a previous specification to roll back to") + // Rollback can't be combined with other flags. + otherFlagsPassed := false + flags.VisitAll(func(f *pflag.Flag) { + if f.Name == "rollback" { + return + } + if flags.Changed(f.Name) { + otherFlagsPassed = true + } + }) + if otherFlagsPassed { + return errors.New("other flags may not be combined with --rollback") } + + if versions.LessThan(dockerCli.Client().ClientVersion(), "1.27") { + clientSideRollback = true + spec = service.PreviousSpec + if spec == nil { + return fmt.Errorf("service does not have a previous specification to roll back to") + } + } else { + serverSideRollback = true + } + } + + updateOpts := types.ServiceUpdateOptions{} + if serverSideRollback { + updateOpts.Rollback = "previous" } err = updateService(flags, spec) @@ -147,7 +180,7 @@ func runUpdate(dockerCli *command.DockerCli, flags *pflag.FlagSet, serviceID str return err } updateOpts.EncodedRegistryAuth = encodedAuth - } else if rollback { + } else if clientSideRollback { updateOpts.RegistryAuthFrom = types.RegistryAuthFromPreviousSpec } else { updateOpts.RegistryAuthFrom = types.RegistryAuthFromSpec