diff --git a/cli/command/config/cmd.go b/cli/command/config/cmd.go index b241229798..cc19bb2325 100644 --- a/cli/command/config/cmd.go +++ b/cli/command/config/cmd.go @@ -16,7 +16,7 @@ func NewConfigCommand(dockerCli command.Cli) *cobra.Command { RunE: command.ShowHelp(dockerCli.Err()), Annotations: map[string]string{ "version": "1.30", - "swarm": "", + "swarm": "manager", }, } cmd.AddCommand( diff --git a/cli/command/node/cmd.go b/cli/command/node/cmd.go index f96c9a6bf3..0e519ae440 100644 --- a/cli/command/node/cmd.go +++ b/cli/command/node/cmd.go @@ -20,7 +20,7 @@ func NewNodeCommand(dockerCli command.Cli) *cobra.Command { RunE: command.ShowHelp(dockerCli.Err()), Annotations: map[string]string{ "version": "1.24", - "swarm": "", + "swarm": "manager", }, } cmd.AddCommand( diff --git a/cli/command/secret/cmd.go b/cli/command/secret/cmd.go index 937fcdb150..b7e38e3073 100644 --- a/cli/command/secret/cmd.go +++ b/cli/command/secret/cmd.go @@ -16,7 +16,7 @@ func NewSecretCommand(dockerCli command.Cli) *cobra.Command { RunE: command.ShowHelp(dockerCli.Err()), Annotations: map[string]string{ "version": "1.25", - "swarm": "", + "swarm": "manager", }, } cmd.AddCommand( diff --git a/cli/command/service/cmd.go b/cli/command/service/cmd.go index 23132d9928..69472c52b2 100644 --- a/cli/command/service/cmd.go +++ b/cli/command/service/cmd.go @@ -16,7 +16,7 @@ func NewServiceCommand(dockerCli command.Cli) *cobra.Command { RunE: command.ShowHelp(dockerCli.Err()), Annotations: map[string]string{ "version": "1.24", - "swarm": "", + "swarm": "manager", }, } cmd.AddCommand( diff --git a/cli/command/stack/cmd.go b/cli/command/stack/cmd.go index 3c28523172..3503454312 100644 --- a/cli/command/stack/cmd.go +++ b/cli/command/stack/cmd.go @@ -17,7 +17,7 @@ func NewStackCommand(dockerCli command.Cli) *cobra.Command { RunE: command.ShowHelp(dockerCli.Err()), Annotations: map[string]string{ "version": "1.25", - "swarm": "", + "swarm": "manager", }, } defaultHelpFunc := cmd.HelpFunc() diff --git a/cli/command/swarm/ca.go b/cli/command/swarm/ca.go index 1c01f68460..62fba6a4cd 100644 --- a/cli/command/swarm/ca.go +++ b/cli/command/swarm/ca.go @@ -35,7 +35,10 @@ func newCACommand(dockerCli command.Cli) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return runCA(dockerCli, cmd.Flags(), opts) }, - Annotations: map[string]string{"version": "1.30"}, + Annotations: map[string]string{ + "version": "1.30", + "swarm": "manager", + }, } flags := cmd.Flags() diff --git a/cli/command/swarm/cmd.go b/cli/command/swarm/cmd.go index 89bf5c3c73..e78e33d003 100644 --- a/cli/command/swarm/cmd.go +++ b/cli/command/swarm/cmd.go @@ -16,7 +16,7 @@ func NewSwarmCommand(dockerCli command.Cli) *cobra.Command { RunE: command.ShowHelp(dockerCli.Err()), Annotations: map[string]string{ "version": "1.24", - "swarm": "", + "swarm": "", // swarm command itself does not require swarm to be enabled (so swarm init and join is always available on API 1.24 and up) }, } cmd.AddCommand( diff --git a/cli/command/swarm/init.go b/cli/command/swarm/init.go index 683a90519a..333bf34039 100644 --- a/cli/command/swarm/init.go +++ b/cli/command/swarm/init.go @@ -39,6 +39,10 @@ func newInitCommand(dockerCli command.Cli) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return runInit(dockerCli, cmd.Flags(), opts) }, + Annotations: map[string]string{ + "version": "1.24", + "swarm": "", // swarm init does not require swarm to be active, and is always available on API 1.24 and up + }, } flags := cmd.Flags() diff --git a/cli/command/swarm/join.go b/cli/command/swarm/join.go index e6c43f03de..59effb21d1 100644 --- a/cli/command/swarm/join.go +++ b/cli/command/swarm/join.go @@ -36,6 +36,10 @@ func newJoinCommand(dockerCli command.Cli) *cobra.Command { opts.remote = args[0] return runJoin(dockerCli, cmd.Flags(), opts) }, + Annotations: map[string]string{ + "version": "1.24", + "swarm": "", // swarm join does not require swarm to be active, and is always available on API 1.24 and up + }, } flags := cmd.Flags() diff --git a/cli/command/swarm/join_token.go b/cli/command/swarm/join_token.go index f8ed93cf00..afd64d55ab 100644 --- a/cli/command/swarm/join_token.go +++ b/cli/command/swarm/join_token.go @@ -28,6 +28,10 @@ func newJoinTokenCommand(dockerCli command.Cli) *cobra.Command { opts.role = args[0] return runJoinToken(dockerCli, opts) }, + Annotations: map[string]string{ + "version": "1.24", + "swarm": "manager", + }, } flags := cmd.Flags() diff --git a/cli/command/swarm/leave.go b/cli/command/swarm/leave.go index af6e0753b5..782f46c325 100644 --- a/cli/command/swarm/leave.go +++ b/cli/command/swarm/leave.go @@ -23,6 +23,10 @@ func newLeaveCommand(dockerCli command.Cli) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return runLeave(dockerCli, opts) }, + Annotations: map[string]string{ + "version": "1.24", + "swarm": "active", + }, } flags := cmd.Flags() diff --git a/cli/command/swarm/unlock.go b/cli/command/swarm/unlock.go index 5cf266ab41..369852dfb2 100644 --- a/cli/command/swarm/unlock.go +++ b/cli/command/swarm/unlock.go @@ -24,6 +24,10 @@ func newUnlockCommand(dockerCli command.Cli) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return runUnlock(dockerCli) }, + Annotations: map[string]string{ + "version": "1.24", + "swarm": "manager", + }, } return cmd diff --git a/cli/command/swarm/unlock_key.go b/cli/command/swarm/unlock_key.go index be5d9ea284..672d333fa8 100644 --- a/cli/command/swarm/unlock_key.go +++ b/cli/command/swarm/unlock_key.go @@ -27,6 +27,10 @@ func newUnlockKeyCommand(dockerCli command.Cli) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { return runUnlockKey(dockerCli, opts) }, + Annotations: map[string]string{ + "version": "1.24", + "swarm": "manager", + }, } flags := cmd.Flags() diff --git a/cli/command/swarm/update.go b/cli/command/swarm/update.go index 6b9ad728f6..c2bc6b3352 100644 --- a/cli/command/swarm/update.go +++ b/cli/command/swarm/update.go @@ -28,6 +28,10 @@ func newUpdateCommand(dockerCli command.Cli) *cobra.Command { } return nil }, + Annotations: map[string]string{ + "version": "1.24", + "swarm": "manager", + }, } cmd.Flags().BoolVar(&opts.autolock, flagAutolock, false, "Change manager autolocking setting (true|false)") diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index e28b95a3f4..a1ae91c19f 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -309,8 +309,33 @@ func hideSubcommandIf(subcmd *cobra.Command, condition func(string) bool, annota func hideUnsupportedFeatures(cmd *cobra.Command, details versionDetails) error { var ( - notExperimental = func(_ string) bool { return !details.ServerInfo().HasExperimental } - notOSType = func(v string) bool { return v != details.ServerInfo().OSType } + notExperimental = func(_ string) bool { return !details.ServerInfo().HasExperimental } + notOSType = func(v string) bool { return v != details.ServerInfo().OSType } + notSwarmStatus = func(v string) bool { + s := details.ServerInfo().SwarmStatus + if s == nil { + // engine did not return swarm status header + return false + } + switch v { + case "manager": + // requires the node to be a manager + return !s.ControlAvailable + case "active": + // requires swarm to be active on the node (e.g. for swarm leave) + // only hide the command if we're sure the node is "inactive" + // for any other status, assume the "leave" command can still + // be used. + return s.NodeState == "inactive" + case "": + // some swarm commands, such as "swarm init" and "swarm join" + // are swarm-related, but do not require swarm to be active + return false + default: + // ignore any other value for the "swarm" annotation + return false + } + } versionOlderThan = func(v string) bool { return versions.LessThan(details.Client().ClientVersion(), v) } ) @@ -328,12 +353,14 @@ func hideUnsupportedFeatures(cmd *cobra.Command, details versionDetails) error { hideFlagIf(f, notExperimental, "experimental") hideFlagIf(f, notOSType, "ostype") + hideFlagIf(f, notSwarmStatus, "swarm") hideFlagIf(f, versionOlderThan, "version") }) for _, subcmd := range cmd.Commands() { hideSubcommandIf(subcmd, notExperimental, "experimental") hideSubcommandIf(subcmd, notOSType, "ostype") + hideSubcommandIf(subcmd, notSwarmStatus, "swarm") hideSubcommandIf(subcmd, versionOlderThan, "version") } return nil