From c4df0d17bb9f7a35063180e50a01577c7ff281b2 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 28 Aug 2025 14:08:03 +0200 Subject: [PATCH 1/5] cli/command/stack: remove deprecated RunList and options.List These were deprecated in f0e5a0d6545399477660087e2db69ebbf831666d and d16c56066427be5e988c197d40c93475446bcdda and were only used internally. Signed-off-by: Sebastiaan van Stijn --- cli/command/stack/list.go | 21 ++++++++------------- cli/command/stack/options/opts.go | 8 -------- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/cli/command/stack/list.go b/cli/command/stack/list.go index 622d18165b..3306a51b80 100644 --- a/cli/command/stack/list.go +++ b/cli/command/stack/list.go @@ -9,16 +9,18 @@ import ( "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/completion" "github.com/docker/cli/cli/command/stack/formatter" - "github.com/docker/cli/cli/command/stack/options" "github.com/docker/cli/cli/command/stack/swarm" flagsHelper "github.com/docker/cli/cli/flags" "github.com/fvbommel/sortorder" "github.com/spf13/cobra" ) -type listOptions = options.List +// listOptions holds docker stack ls options +type listOptions struct { + format string +} -func newListCommand(dockerCli command.Cli) *cobra.Command { +func newListCommand(dockerCLI command.Cli) *cobra.Command { opts := listOptions{} cmd := &cobra.Command{ @@ -27,23 +29,16 @@ func newListCommand(dockerCli command.Cli) *cobra.Command { Short: "List stacks", Args: cli.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - return runList(cmd.Context(), dockerCli, opts) + return runList(cmd.Context(), dockerCLI, opts) }, ValidArgsFunction: completion.NoComplete, } flags := cmd.Flags() - flags.StringVar(&opts.Format, "format", "", flagsHelper.FormatHelp) + flags.StringVar(&opts.format, "format", "", flagsHelper.FormatHelp) return cmd } -// RunList performs a stack list against the specified swarm cluster -// -// Deprecated: this function was for internal use and will be removed in the next release. -func RunList(ctx context.Context, dockerCLI command.Cli, opts options.List) error { - return runList(ctx, dockerCLI, opts) -} - // runList performs a stack list against the specified swarm cluster func runList(ctx context.Context, dockerCLI command.Cli, opts listOptions) error { stacks, err := swarm.GetStacks(ctx, dockerCLI.Client()) @@ -54,7 +49,7 @@ func runList(ctx context.Context, dockerCLI command.Cli, opts listOptions) error } func format(out io.Writer, opts listOptions, stacks []formatter.Stack) error { - fmt := formatter.Format(opts.Format) + fmt := formatter.Format(opts.format) if fmt == "" || fmt == formatter.TableFormatKey { fmt = formatter.SwarmStackTableFormat } diff --git a/cli/command/stack/options/opts.go b/cli/command/stack/options/opts.go index bd239e892f..36a01f4e02 100644 --- a/cli/command/stack/options/opts.go +++ b/cli/command/stack/options/opts.go @@ -23,14 +23,6 @@ type Config struct { SkipInterpolation bool } -// List holds docker stack ls options -// -// Deprecated: this type was for internal use and will be removed in the next release. -type List struct { - Format string - AllNamespaces bool -} - // PS holds docker stack ps options // // Deprecated: this type was for internal use and will be removed in the next release. From 2a05951680fc4108211d4c748b40a8e6e020abb0 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 28 Aug 2025 14:23:41 +0200 Subject: [PATCH 2/5] cli/command/stack: remove deprecated RunServices and swarm.GetServices These were deprecated in f0e5a0d6545399477660087e2db69ebbf831666d, 036d3a6bab54fdffd9804ab2367fb9a14e62893b, and d16c56066427be5e988c197d40c93475446bcdda and were only used internally. Signed-off-by: Sebastiaan van Stijn --- cli/command/stack/common.go | 10 ++++ cli/command/stack/options/opts.go | 10 ---- cli/command/stack/services.go | 48 +++++++++---------- .../{swarm/services.go => services_utils.go} | 19 ++------ 4 files changed, 37 insertions(+), 50 deletions(-) rename cli/command/stack/{swarm/services.go => services_utils.go} (74%) diff --git a/cli/command/stack/common.go b/cli/command/stack/common.go index 6de410da84..40b1db2cfc 100644 --- a/cli/command/stack/common.go +++ b/cli/command/stack/common.go @@ -4,6 +4,10 @@ import ( "fmt" "strings" "unicode" + + "github.com/docker/cli/cli/compose/convert" + "github.com/docker/cli/opts" + "github.com/moby/moby/api/types/filters" ) // validateStackName checks if the provided string is a valid stack name (namespace). @@ -29,3 +33,9 @@ func validateStackNames(namespaces []string) error { func quotesOrWhitespace(r rune) bool { return unicode.IsSpace(r) || r == '"' || r == '\'' } + +func getStackFilterFromOpt(namespace string, opt opts.FilterOpt) filters.Args { + filter := opt.Value() + filter.Add("label", convert.LabelNamespace+"="+namespace) + return filter +} diff --git a/cli/command/stack/options/opts.go b/cli/command/stack/options/opts.go index 36a01f4e02..58848883fa 100644 --- a/cli/command/stack/options/opts.go +++ b/cli/command/stack/options/opts.go @@ -42,13 +42,3 @@ type Remove struct { Namespaces []string Detach bool } - -// Services holds docker stack services options -// -// Deprecated: this type was for internal use and will be removed in the next release. -type Services struct { - Quiet bool - Format string - Filter opts.FilterOpt - Namespace string -} diff --git a/cli/command/stack/services.go b/cli/command/stack/services.go index f10cfa3105..13823ae645 100644 --- a/cli/command/stack/services.go +++ b/cli/command/stack/services.go @@ -7,30 +7,33 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/command/formatter" "github.com/docker/cli/cli/command/service" - "github.com/docker/cli/cli/command/stack/formatter" - "github.com/docker/cli/cli/command/stack/options" - "github.com/docker/cli/cli/command/stack/swarm" flagsHelper "github.com/docker/cli/cli/flags" cliopts "github.com/docker/cli/opts" "github.com/fvbommel/sortorder" - swarmtypes "github.com/moby/moby/api/types/swarm" + "github.com/moby/moby/api/types/swarm" "github.com/spf13/cobra" ) -// servicesOptions holds docker stack services options -type servicesOptions = options.Services +// serviceListOptions holds docker stack services options +type serviceListOptions = struct { + quiet bool + format string + filter cliopts.FilterOpt + namespace string +} func newServicesCommand(dockerCLI command.Cli) *cobra.Command { - opts := servicesOptions{Filter: cliopts.NewFilterOpt()} + opts := serviceListOptions{filter: cliopts.NewFilterOpt()} cmd := &cobra.Command{ Use: "services [OPTIONS] STACK", Short: "List the services in the stack", Args: cli.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - opts.Namespace = args[0] - if err := validateStackName(opts.Namespace); err != nil { + opts.namespace = args[0] + if err := validateStackName(opts.namespace); err != nil { return err } return runServices(cmd.Context(), dockerCLI, opts) @@ -40,41 +43,34 @@ func newServicesCommand(dockerCLI command.Cli) *cobra.Command { }, } flags := cmd.Flags() - flags.BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs") - flags.StringVar(&opts.Format, "format", "", flagsHelper.FormatHelp) - flags.VarP(&opts.Filter, "filter", "f", "Filter output based on conditions provided") + flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only display IDs") + flags.StringVar(&opts.format, "format", "", flagsHelper.FormatHelp) + flags.VarP(&opts.filter, "filter", "f", "Filter output based on conditions provided") return cmd } -// RunServices performs a stack services against the specified swarm cluster -// -// Deprecated: this function was for internal use and will be removed in the next release. -func RunServices(ctx context.Context, dockerCLI command.Cli, opts options.Services) error { - return runServices(ctx, dockerCLI, opts) -} - // runServices performs a stack services against the specified swarm cluster -func runServices(ctx context.Context, dockerCLI command.Cli, opts servicesOptions) error { - services, err := swarm.GetServices(ctx, dockerCLI, opts) +func runServices(ctx context.Context, dockerCLI command.Cli, opts serviceListOptions) error { + services, err := getServices(ctx, dockerCLI.Client(), opts) if err != nil { return err } return formatWrite(dockerCLI, services, opts) } -func formatWrite(dockerCLI command.Cli, services []swarmtypes.Service, opts servicesOptions) error { +func formatWrite(dockerCLI command.Cli, services []swarm.Service, opts serviceListOptions) error { // if no services in the stack, print message and exit 0 if len(services) == 0 { - _, _ = fmt.Fprintln(dockerCLI.Err(), "Nothing found in stack:", opts.Namespace) + _, _ = fmt.Fprintln(dockerCLI.Err(), "Nothing found in stack:", opts.namespace) return nil } sort.Slice(services, func(i, j int) bool { return sortorder.NaturalLess(services[i].Spec.Name, services[j].Spec.Name) }) - f := opts.Format + f := opts.format if len(f) == 0 { - if len(dockerCLI.ConfigFile().ServicesFormat) > 0 && !opts.Quiet { + if len(dockerCLI.ConfigFile().ServicesFormat) > 0 && !opts.quiet { f = dockerCLI.ConfigFile().ServicesFormat } else { f = formatter.TableFormatKey @@ -83,7 +79,7 @@ func formatWrite(dockerCLI command.Cli, services []swarmtypes.Service, opts serv servicesCtx := formatter.Context{ Output: dockerCLI.Out(), - Format: service.NewListFormat(f, opts.Quiet), + Format: service.NewListFormat(f, opts.quiet), } return service.ListFormatWrite(servicesCtx, services) } diff --git a/cli/command/stack/swarm/services.go b/cli/command/stack/services_utils.go similarity index 74% rename from cli/command/stack/swarm/services.go rename to cli/command/stack/services_utils.go index 0cfa062a49..4fd10f2d30 100644 --- a/cli/command/stack/swarm/services.go +++ b/cli/command/stack/services_utils.go @@ -1,31 +1,22 @@ -package swarm +package stack import ( "context" - "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/service" - "github.com/docker/cli/cli/command/stack/options" "github.com/moby/moby/api/types/swarm" "github.com/moby/moby/client" ) -// GetServices is the swarm implementation of listing stack services -// -// Deprecated: this function was for internal use and will be removed in the next release. -func GetServices(ctx context.Context, dockerCLI command.Cli, opts options.Services) ([]swarm.Service, error) { - var ( - err error - apiClient = dockerCLI.Client() - ) - +// getServices is the swarm implementation of listing stack services +func getServices(ctx context.Context, apiClient client.APIClient, opts serviceListOptions) ([]swarm.Service, error) { listOpts := client.ServiceListOptions{ - Filters: getStackFilterFromOpt(opts.Namespace, opts.Filter), + Filters: getStackFilterFromOpt(opts.namespace, opts.filter), // When not running "quiet", also get service status (number of running // and desired tasks). Note that this is only supported on API v1.41 and // up; older API versions ignore this option, and we will have to collect // the information manually below. - Status: !opts.Quiet, + Status: !opts.quiet, } services, err := apiClient.ServiceList(ctx, listOpts) From c3be589c1638073b475acbcd69c26fa569bbf33c Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 28 Aug 2025 14:33:21 +0200 Subject: [PATCH 3/5] cli/command/stack/swarm: remove deprecated RunPS and options.PS These were deprecated in f0e5a0d6545399477660087e2db69ebbf831666d and 036d3a6bab54fdffd9804ab2367fb9a14e62893b and were only used internally. Signed-off-by: Sebastiaan van Stijn --- cli/command/stack/options/opts.go | 14 ------- cli/command/stack/ps.go | 61 ++++++++++++++++++++++++------- cli/command/stack/swarm/common.go | 7 ---- cli/command/stack/swarm/ps.go | 36 ------------------ 4 files changed, 48 insertions(+), 70 deletions(-) delete mode 100644 cli/command/stack/swarm/ps.go diff --git a/cli/command/stack/options/opts.go b/cli/command/stack/options/opts.go index 58848883fa..8d1fbd6e8b 100644 --- a/cli/command/stack/options/opts.go +++ b/cli/command/stack/options/opts.go @@ -1,7 +1,5 @@ package options -import "github.com/docker/cli/opts" - // Deploy holds docker stack deploy options // // Deprecated: this type was for internal use and will be removed in the next release. @@ -23,18 +21,6 @@ type Config struct { SkipInterpolation bool } -// PS holds docker stack ps options -// -// Deprecated: this type was for internal use and will be removed in the next release. -type PS struct { - Filter opts.FilterOpt - NoTrunc bool - Namespace string - NoResolve bool - Quiet bool - Format string -} - // Remove holds docker stack remove options // // Deprecated: this type was for internal use and will be removed in the next release. diff --git a/cli/command/stack/ps.go b/cli/command/stack/ps.go index 8a91cc75f8..1e9ddd12bc 100644 --- a/cli/command/stack/ps.go +++ b/cli/command/stack/ps.go @@ -1,38 +1,73 @@ package stack import ( + "context" + "fmt" + "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/command/stack/options" - "github.com/docker/cli/cli/command/stack/swarm" + "github.com/docker/cli/cli/command/idresolver" + "github.com/docker/cli/cli/command/task" flagsHelper "github.com/docker/cli/cli/flags" cliopts "github.com/docker/cli/opts" + "github.com/moby/moby/client" "github.com/spf13/cobra" ) -func newPsCommand(dockerCli command.Cli) *cobra.Command { - opts := options.PS{Filter: cliopts.NewFilterOpt()} +// psOptions holds docker stack ps options +type psOptions struct { + filter cliopts.FilterOpt + noTrunc bool + namespace string + noResolve bool + quiet bool + format string +} + +func newPsCommand(dockerCLI command.Cli) *cobra.Command { + opts := psOptions{filter: cliopts.NewFilterOpt()} cmd := &cobra.Command{ Use: "ps [OPTIONS] STACK", Short: "List the tasks in the stack", Args: cli.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - opts.Namespace = args[0] - if err := validateStackName(opts.Namespace); err != nil { + opts.namespace = args[0] + if err := validateStackName(opts.namespace); err != nil { return err } - return swarm.RunPS(cmd.Context(), dockerCli, opts) + return runPS(cmd.Context(), dockerCLI, opts) }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return completeNames(dockerCli)(cmd, args, toComplete) + return completeNames(dockerCLI)(cmd, args, toComplete) }, } flags := cmd.Flags() - flags.BoolVar(&opts.NoTrunc, "no-trunc", false, "Do not truncate output") - flags.BoolVar(&opts.NoResolve, "no-resolve", false, "Do not map IDs to Names") - flags.VarP(&opts.Filter, "filter", "f", "Filter output based on conditions provided") - flags.BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display task IDs") - flags.StringVar(&opts.Format, "format", "", flagsHelper.FormatHelp) + flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Do not truncate output") + flags.BoolVar(&opts.noResolve, "no-resolve", false, "Do not map IDs to Names") + flags.VarP(&opts.filter, "filter", "f", "Filter output based on conditions provided") + flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only display task IDs") + flags.StringVar(&opts.format, "format", "", flagsHelper.FormatHelp) return cmd } + +// runPS is the swarm implementation of docker stack ps +func runPS(ctx context.Context, dockerCLI command.Cli, opts psOptions) error { + apiClient := dockerCLI.Client() + tasks, err := apiClient.TaskList(ctx, client.TaskListOptions{ + Filters: getStackFilterFromOpt(opts.namespace, opts.filter), + }) + if err != nil { + return err + } + + if len(tasks) == 0 { + return fmt.Errorf("nothing found in stack: %s", opts.namespace) + } + + if opts.format == "" { + opts.format = task.DefaultFormat(dockerCLI.ConfigFile(), opts.quiet) + } + + return task.Print(ctx, dockerCLI, tasks, idresolver.New(apiClient, opts.noResolve), !opts.noTrunc, opts.quiet, opts.format) +} diff --git a/cli/command/stack/swarm/common.go b/cli/command/stack/swarm/common.go index 3b195ad95b..3d0578aa50 100644 --- a/cli/command/stack/swarm/common.go +++ b/cli/command/stack/swarm/common.go @@ -4,7 +4,6 @@ import ( "context" "github.com/docker/cli/cli/compose/convert" - "github.com/docker/cli/opts" "github.com/moby/moby/api/types/filters" "github.com/moby/moby/api/types/network" "github.com/moby/moby/api/types/swarm" @@ -17,12 +16,6 @@ func getStackFilter(namespace string) filters.Args { return filter } -func getStackFilterFromOpt(namespace string, opt opts.FilterOpt) filters.Args { - filter := opt.Value() - filter.Add("label", convert.LabelNamespace+"="+namespace) - return filter -} - func getAllStacksFilter() filters.Args { filter := filters.NewArgs() filter.Add("label", convert.LabelNamespace) diff --git a/cli/command/stack/swarm/ps.go b/cli/command/stack/swarm/ps.go deleted file mode 100644 index 6d7d2d48a9..0000000000 --- a/cli/command/stack/swarm/ps.go +++ /dev/null @@ -1,36 +0,0 @@ -package swarm - -import ( - "context" - "fmt" - - "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/command/idresolver" - "github.com/docker/cli/cli/command/stack/options" - "github.com/docker/cli/cli/command/task" - "github.com/moby/moby/client" -) - -// RunPS is the swarm implementation of docker stack ps -// -// Deprecated: this function was for internal use and will be removed in the next release. -func RunPS(ctx context.Context, dockerCLI command.Cli, opts options.PS) error { - filter := getStackFilterFromOpt(opts.Namespace, opts.Filter) - - apiClient := dockerCLI.Client() - tasks, err := apiClient.TaskList(ctx, client.TaskListOptions{Filters: filter}) - if err != nil { - return err - } - - if len(tasks) == 0 { - return fmt.Errorf("nothing found in stack: %s", opts.Namespace) - } - - format := opts.Format - if len(format) == 0 { - format = task.DefaultFormat(dockerCLI.ConfigFile(), opts.Quiet) - } - - return task.Print(ctx, dockerCLI, tasks, idresolver.New(apiClient, opts.NoResolve), !opts.NoTrunc, opts.Quiet, format) -} From 2066dbcfe8e024a9fe8f670ac7cde5977af0cca6 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 29 Aug 2025 12:03:29 +0200 Subject: [PATCH 4/5] cli/command/stack/swarm: inline validateResolveImageFlag It was only used in a single place, and possibly incorrect. Let's inline it to put the logic where it's used. Signed-off-by: Sebastiaan van Stijn --- cli/command/stack/swarm/deploy.go | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/cli/command/stack/swarm/deploy.go b/cli/command/stack/swarm/deploy.go index 0cd2f93179..3528023a2b 100644 --- a/cli/command/stack/swarm/deploy.go +++ b/cli/command/stack/swarm/deploy.go @@ -26,12 +26,17 @@ const ( // // Deprecated: this function was for internal use and will be removed in the next release. func RunDeploy(ctx context.Context, dockerCLI command.Cli, flags *pflag.FlagSet, opts *options.Deploy, cfg *composetypes.Config) error { - if err := validateResolveImageFlag(opts); err != nil { - return err + switch opts.ResolveImage { + case ResolveImageAlways, ResolveImageChanged, ResolveImageNever: + // valid options. + default: + return errors.Errorf("Invalid option %s for flag --resolve-image", opts.ResolveImage) } + // 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") { + // TODO(thaJeztah): should this error if "opts.ResolveImage" is already other (unsupported) values? opts.ResolveImage = ResolveImageNever } @@ -43,16 +48,6 @@ func RunDeploy(ctx context.Context, dockerCLI command.Cli, flags *pflag.FlagSet, return deployCompose(ctx, dockerCLI, opts, cfg) } -// validateResolveImageFlag validates the opts.resolveImage command line option -func validateResolveImageFlag(opts *options.Deploy) error { - switch opts.ResolveImage { - case ResolveImageAlways, ResolveImageChanged, ResolveImageNever: - return nil - default: - return errors.Errorf("Invalid option %s for flag --resolve-image", opts.ResolveImage) - } -} - // checkDaemonIsSwarmManager does an Info API call to verify that the daemon is // a swarm manager. This is necessary because we must create networks before we // create services, but the API call for creating a network does not return a From 77205e782ab5b27a4172faa80f54395106ca5dd1 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 29 Aug 2025 14:33:01 +0200 Subject: [PATCH 5/5] cli/command/stack/swarm: deployServices: use struct-literal for options Signed-off-by: Sebastiaan van Stijn --- cli/command/stack/swarm/deploy_composefile.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/cli/command/stack/swarm/deploy_composefile.go b/cli/command/stack/swarm/deploy_composefile.go index 3124e8e8ff..b18aeb7d51 100644 --- a/cli/command/stack/swarm/deploy_composefile.go +++ b/cli/command/stack/swarm/deploy_composefile.go @@ -265,14 +265,13 @@ func deployServices(ctx context.Context, dockerCLI command.Cli, services map[str } else { _, _ = fmt.Fprintln(out, "Creating service", name) - createOpts := client.ServiceCreateOptions{EncodedRegistryAuth: encodedAuth} - // query registry if flag disabling it was not set - if resolveImage == ResolveImageAlways || resolveImage == ResolveImageChanged { - createOpts.QueryRegistry = true - } + queryRegistry := resolveImage == ResolveImageAlways || resolveImage == ResolveImageChanged - response, err := apiClient.ServiceCreate(ctx, serviceSpec, createOpts) + response, err := apiClient.ServiceCreate(ctx, serviceSpec, client.ServiceCreateOptions{ + EncodedRegistryAuth: encodedAuth, + QueryRegistry: queryRegistry, + }) if err != nil { return nil, fmt.Errorf("failed to create service %s: %w", name, err) }