diff --git a/cli/command/cli.go b/cli/command/cli.go index 783e516f3d..74d0fa4f76 100644 --- a/cli/command/cli.go +++ b/cli/command/cli.go @@ -44,28 +44,17 @@ type Cli interface { // DockerCli is an instance the docker command line client. // Instances of the client can be returned from NewDockerCli. type DockerCli struct { - configFile *configfile.ConfigFile - in *InStream - out *OutStream - err io.Writer - keyFile string - client client.APIClient - hasExperimental bool - osType string - defaultVersion string + configFile *configfile.ConfigFile + in *InStream + out *OutStream + err io.Writer + keyFile string + client client.APIClient + defaultVersion string + server ServerInfo } -// HasExperimental returns true if experimental features are accessible. -func (cli *DockerCli) HasExperimental() bool { - return cli.hasExperimental -} - -// OSType returns the operating system the daemon is running on. -func (cli *DockerCli) OSType() string { - return cli.osType -} - -// DefaultVersion returns api.defaultVersion of DOCKER_API_VERSION if specified. +// DefaultVersion returns api.defaultVersion or DOCKER_API_VERSION if specified. func (cli *DockerCli) DefaultVersion() string { return cli.defaultVersion } @@ -102,6 +91,12 @@ func (cli *DockerCli) ConfigFile() *configfile.ConfigFile { return cli.configFile } +// ServerInfo returns the server version details for the host this client is +// connected to +func (cli *DockerCli) ServerInfo() ServerInfo { + return cli.server +} + // GetAllCredentials returns all of the credentials stored in all of the // configured credential stores. func (cli *DockerCli) GetAllCredentials() (map[string]types.AuthConfig, error) { @@ -161,7 +156,6 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions) error { if err != nil { return err } - cli.defaultVersion = cli.client.ClientVersion() if opts.Common.TrustKey == "" { @@ -171,8 +165,10 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions) error { } if ping, err := cli.client.Ping(context.Background()); err == nil { - cli.hasExperimental = ping.Experimental - cli.osType = ping.OSType + cli.server = ServerInfo{ + HasExperimental: ping.Experimental, + OSType: ping.OSType, + } // since the new header was added in 1.25, assume server is 1.24 if header is not present. if ping.APIVersion == "" { @@ -184,9 +180,17 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions) error { cli.client.UpdateClientVersion(ping.APIVersion) } } + return nil } +// ServerInfo stores details about the supported features and platform of the +// server +type ServerInfo struct { + HasExperimental bool + OSType string +} + // NewDockerCli returns a DockerCli instance with IO output and error streams set by in, out and err. func NewDockerCli(in io.ReadCloser, out, err io.Writer) *DockerCli { return &DockerCli{in: NewInStream(in), out: NewOutStream(out), err: err} diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index 8d589d4416..96283debc4 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -14,6 +14,7 @@ import ( cliconfig "github.com/docker/docker/cli/config" "github.com/docker/docker/cli/debug" cliflags "github.com/docker/docker/cli/flags" + "github.com/docker/docker/client" "github.com/docker/docker/dockerversion" "github.com/docker/docker/pkg/term" "github.com/spf13/cobra" @@ -49,7 +50,7 @@ func newDockerCommand(dockerCli *command.DockerCli) *cobra.Command { if err := dockerCli.Initialize(opts); err != nil { return err } - return isSupported(cmd, dockerCli.Client().ClientVersion(), dockerCli.OSType(), dockerCli.HasExperimental()) + return isSupported(cmd, dockerCli) }, } cli.SetupRootCommand(cmd) @@ -80,7 +81,7 @@ func setFlagErrorFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *p flagErrorFunc := cmd.FlagErrorFunc() cmd.SetFlagErrorFunc(func(cmd *cobra.Command, err error) error { initializeDockerCli(dockerCli, flags, opts) - if err := isSupported(cmd, dockerCli.Client().ClientVersion(), dockerCli.OSType(), dockerCli.HasExperimental()); err != nil { + if err := isSupported(cmd, dockerCli); err != nil { return err } return flagErrorFunc(cmd, err) @@ -90,12 +91,12 @@ func setFlagErrorFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *p func setHelpFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *pflag.FlagSet, opts *cliflags.ClientOptions) { cmd.SetHelpFunc(func(ccmd *cobra.Command, args []string) { initializeDockerCli(dockerCli, flags, opts) - if err := isSupported(ccmd, dockerCli.Client().ClientVersion(), dockerCli.OSType(), dockerCli.HasExperimental()); err != nil { + if err := isSupported(ccmd, dockerCli); err != nil { ccmd.Println(err) return } - hideUnsupportedFeatures(ccmd, dockerCli.Client().ClientVersion(), dockerCli.OSType(), dockerCli.HasExperimental()) + hideUnsupportedFeatures(ccmd, dockerCli) if err := ccmd.Help(); err != nil { ccmd.Println(err) @@ -122,7 +123,7 @@ func setValidateArgs(dockerCli *command.DockerCli, cmd *cobra.Command, flags *pf cmdArgs := ccmd.Args ccmd.Args = func(cmd *cobra.Command, args []string) error { initializeDockerCli(dockerCli, flags, opts) - if err := isSupported(cmd, dockerCli.Client().ClientVersion(), dockerCli.OSType(), dockerCli.HasExperimental()); err != nil { + if err := isSupported(cmd, dockerCli); err != nil { return err } return cmdArgs(cmd, args) @@ -198,7 +199,16 @@ func dockerPreRun(opts *cliflags.ClientOptions) { } } -func hideUnsupportedFeatures(cmd *cobra.Command, clientVersion, osType string, hasExperimental bool) { +type versionDetails interface { + Client() client.APIClient + ServerInfo() command.ServerInfo +} + +func hideUnsupportedFeatures(cmd *cobra.Command, details versionDetails) { + clientVersion := details.Client().ClientVersion() + osType := details.ServerInfo().OSType + hasExperimental := details.ServerInfo().HasExperimental + cmd.Flags().VisitAll(func(f *pflag.Flag) { // hide experimental flags if !hasExperimental { @@ -228,7 +238,11 @@ func hideUnsupportedFeatures(cmd *cobra.Command, clientVersion, osType string, h } } -func isSupported(cmd *cobra.Command, clientVersion, osType string, hasExperimental bool) error { +func isSupported(cmd *cobra.Command, details versionDetails) error { + clientVersion := details.Client().ClientVersion() + osType := details.ServerInfo().OSType + hasExperimental := details.ServerInfo().HasExperimental + // Check recursively so that, e.g., `docker stack ls` returns the same output as `docker stack` for curr := cmd; curr != nil; curr = curr.Parent() { if cmdVersion, ok := curr.Tags["version"]; ok && versions.LessThan(clientVersion, cmdVersion) {