Merge pull request #3564 from thaJeztah/update_engine_hide_swarm_commands

hide swarm-related commands based on the current swarm status and role
This commit is contained in:
Sebastiaan van Stijn 2022-05-12 12:44:06 +02:00 committed by GitHub
commit 030eed90a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 82 additions and 12 deletions

View File

@ -25,7 +25,8 @@ import (
dopts "github.com/docker/cli/opts" dopts "github.com/docker/cli/opts"
"github.com/docker/docker/api" "github.com/docker/docker/api"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/client" "github.com/docker/docker/client"
"github.com/docker/go-connections/tlsconfig" "github.com/docker/go-connections/tlsconfig"
"github.com/moby/term" "github.com/moby/term"
@ -132,6 +133,7 @@ func (cli *DockerCli) loadConfigFile() {
// ServerInfo returns the server version details for the host this client is // ServerInfo returns the server version details for the host this client is
// connected to // connected to
func (cli *DockerCli) ServerInfo() ServerInfo { func (cli *DockerCli) ServerInfo() ServerInfo {
// TODO(thaJeztah) make ServerInfo() lazily load the info (ping only when needed)
return cli.serverInfo return cli.serverInfo
} }
@ -170,7 +172,7 @@ func (cli *DockerCli) ManifestStore() manifeststore.Store {
// RegistryClient returns a client for communicating with a Docker distribution // RegistryClient returns a client for communicating with a Docker distribution
// registry // registry
func (cli *DockerCli) RegistryClient(allowInsecure bool) registryclient.RegistryClient { func (cli *DockerCli) RegistryClient(allowInsecure bool) registryclient.RegistryClient {
resolver := func(ctx context.Context, index *registrytypes.IndexInfo) types.AuthConfig { resolver := func(ctx context.Context, index *registry.IndexInfo) types.AuthConfig {
return ResolveAuthConfig(ctx, cli, index) return ResolveAuthConfig(ctx, cli, index)
} }
return registryclient.NewRegistryClient(resolver, UserAgent(), allowInsecure) return registryclient.NewRegistryClient(resolver, UserAgent(), allowInsecure)
@ -336,6 +338,7 @@ func (cli *DockerCli) initializeFromClient() {
HasExperimental: ping.Experimental, HasExperimental: ping.Experimental,
OSType: ping.OSType, OSType: ping.OSType,
BuildkitVersion: ping.BuilderVersion, BuildkitVersion: ping.BuilderVersion,
SwarmStatus: ping.SwarmStatus,
} }
cli.client.NegotiateAPIVersionPing(ping) cli.client.NegotiateAPIVersionPing(ping)
} }
@ -376,6 +379,15 @@ type ServerInfo struct {
HasExperimental bool HasExperimental bool
OSType string OSType string
BuildkitVersion types.BuilderVersion BuildkitVersion types.BuilderVersion
// SwarmStatus provides information about the current swarm status of the
// engine, obtained from the "Swarm" header in the API response.
//
// It can be a nil struct if the API version does not provide this header
// in the ping response, or if an error occurred, in which case the client
// should use other ways to get the current swarm status, such as the /swarm
// endpoint.
SwarmStatus *swarm.Status
} }
// NewDockerCli returns a DockerCli instance with all operators applied on it. // NewDockerCli returns a DockerCli instance with all operators applied on it.

View File

@ -159,7 +159,7 @@ func TestInitializeFromClient(t *testing.T) {
cli := &DockerCli{client: apiclient} cli := &DockerCli{client: apiclient}
cli.initializeFromClient() cli.initializeFromClient()
assert.DeepEqual(t, cli.serverInfo, testcase.expectedServer) assert.DeepEqual(t, cli.ServerInfo(), testcase.expectedServer)
assert.Equal(t, apiclient.negotiated, testcase.negotiated) assert.Equal(t, apiclient.negotiated, testcase.negotiated)
}) })
} }

View File

@ -16,7 +16,7 @@ func NewConfigCommand(dockerCli command.Cli) *cobra.Command {
RunE: command.ShowHelp(dockerCli.Err()), RunE: command.ShowHelp(dockerCli.Err()),
Annotations: map[string]string{ Annotations: map[string]string{
"version": "1.30", "version": "1.30",
"swarm": "", "swarm": "manager",
}, },
} }
cmd.AddCommand( cmd.AddCommand(

View File

@ -20,7 +20,7 @@ func NewNodeCommand(dockerCli command.Cli) *cobra.Command {
RunE: command.ShowHelp(dockerCli.Err()), RunE: command.ShowHelp(dockerCli.Err()),
Annotations: map[string]string{ Annotations: map[string]string{
"version": "1.24", "version": "1.24",
"swarm": "", "swarm": "manager",
}, },
} }
cmd.AddCommand( cmd.AddCommand(

View File

@ -16,7 +16,7 @@ func NewSecretCommand(dockerCli command.Cli) *cobra.Command {
RunE: command.ShowHelp(dockerCli.Err()), RunE: command.ShowHelp(dockerCli.Err()),
Annotations: map[string]string{ Annotations: map[string]string{
"version": "1.25", "version": "1.25",
"swarm": "", "swarm": "manager",
}, },
} }
cmd.AddCommand( cmd.AddCommand(

View File

@ -16,7 +16,7 @@ func NewServiceCommand(dockerCli command.Cli) *cobra.Command {
RunE: command.ShowHelp(dockerCli.Err()), RunE: command.ShowHelp(dockerCli.Err()),
Annotations: map[string]string{ Annotations: map[string]string{
"version": "1.24", "version": "1.24",
"swarm": "", "swarm": "manager",
}, },
} }
cmd.AddCommand( cmd.AddCommand(

View File

@ -17,7 +17,7 @@ func NewStackCommand(dockerCli command.Cli) *cobra.Command {
RunE: command.ShowHelp(dockerCli.Err()), RunE: command.ShowHelp(dockerCli.Err()),
Annotations: map[string]string{ Annotations: map[string]string{
"version": "1.25", "version": "1.25",
"swarm": "", "swarm": "manager",
}, },
} }
defaultHelpFunc := cmd.HelpFunc() defaultHelpFunc := cmd.HelpFunc()

View File

@ -35,7 +35,10 @@ func newCACommand(dockerCli command.Cli) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runCA(dockerCli, cmd.Flags(), opts) 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() flags := cmd.Flags()

View File

@ -16,7 +16,7 @@ func NewSwarmCommand(dockerCli command.Cli) *cobra.Command {
RunE: command.ShowHelp(dockerCli.Err()), RunE: command.ShowHelp(dockerCli.Err()),
Annotations: map[string]string{ Annotations: map[string]string{
"version": "1.24", "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( cmd.AddCommand(

View File

@ -39,6 +39,10 @@ func newInitCommand(dockerCli command.Cli) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runInit(dockerCli, cmd.Flags(), opts) 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() flags := cmd.Flags()

View File

@ -36,6 +36,10 @@ func newJoinCommand(dockerCli command.Cli) *cobra.Command {
opts.remote = args[0] opts.remote = args[0]
return runJoin(dockerCli, cmd.Flags(), opts) 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() flags := cmd.Flags()

View File

@ -28,6 +28,10 @@ func newJoinTokenCommand(dockerCli command.Cli) *cobra.Command {
opts.role = args[0] opts.role = args[0]
return runJoinToken(dockerCli, opts) return runJoinToken(dockerCli, opts)
}, },
Annotations: map[string]string{
"version": "1.24",
"swarm": "manager",
},
} }
flags := cmd.Flags() flags := cmd.Flags()

View File

@ -23,6 +23,10 @@ func newLeaveCommand(dockerCli command.Cli) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runLeave(dockerCli, opts) return runLeave(dockerCli, opts)
}, },
Annotations: map[string]string{
"version": "1.24",
"swarm": "active",
},
} }
flags := cmd.Flags() flags := cmd.Flags()

View File

@ -24,6 +24,10 @@ func newUnlockCommand(dockerCli command.Cli) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runUnlock(dockerCli) return runUnlock(dockerCli)
}, },
Annotations: map[string]string{
"version": "1.24",
"swarm": "manager",
},
} }
return cmd return cmd

View File

@ -27,6 +27,10 @@ func newUnlockKeyCommand(dockerCli command.Cli) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runUnlockKey(dockerCli, opts) return runUnlockKey(dockerCli, opts)
}, },
Annotations: map[string]string{
"version": "1.24",
"swarm": "manager",
},
} }
flags := cmd.Flags() flags := cmd.Flags()

View File

@ -28,6 +28,10 @@ func newUpdateCommand(dockerCli command.Cli) *cobra.Command {
} }
return nil return nil
}, },
Annotations: map[string]string{
"version": "1.24",
"swarm": "manager",
},
} }
cmd.Flags().BoolVar(&opts.autolock, flagAutolock, false, "Change manager autolocking setting (true|false)") cmd.Flags().BoolVar(&opts.autolock, flagAutolock, false, "Change manager autolocking setting (true|false)")

View File

@ -311,6 +311,31 @@ func hideUnsupportedFeatures(cmd *cobra.Command, details versionDetails) error {
var ( var (
notExperimental = func(_ string) bool { return !details.ServerInfo().HasExperimental } notExperimental = func(_ string) bool { return !details.ServerInfo().HasExperimental }
notOSType = func(v string) bool { return v != details.ServerInfo().OSType } 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) } 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, notExperimental, "experimental")
hideFlagIf(f, notOSType, "ostype") hideFlagIf(f, notOSType, "ostype")
hideFlagIf(f, notSwarmStatus, "swarm")
hideFlagIf(f, versionOlderThan, "version") hideFlagIf(f, versionOlderThan, "version")
}) })
for _, subcmd := range cmd.Commands() { for _, subcmd := range cmd.Commands() {
hideSubcommandIf(subcmd, notExperimental, "experimental") hideSubcommandIf(subcmd, notExperimental, "experimental")
hideSubcommandIf(subcmd, notOSType, "ostype") hideSubcommandIf(subcmd, notOSType, "ostype")
hideSubcommandIf(subcmd, notSwarmStatus, "swarm")
hideSubcommandIf(subcmd, versionOlderThan, "version") hideSubcommandIf(subcmd, versionOlderThan, "version")
} }
return nil return nil