github.com/olljanat/moby@v1.13.1/cmd/docker/docker.go (about)

     1  package main
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  
     8  	"github.com/Sirupsen/logrus"
     9  	"github.com/docker/docker/api/types/versions"
    10  	"github.com/docker/docker/cli"
    11  	"github.com/docker/docker/cli/command"
    12  	"github.com/docker/docker/cli/command/commands"
    13  	cliflags "github.com/docker/docker/cli/flags"
    14  	"github.com/docker/docker/cliconfig"
    15  	"github.com/docker/docker/dockerversion"
    16  	"github.com/docker/docker/pkg/term"
    17  	"github.com/docker/docker/utils"
    18  	"github.com/spf13/cobra"
    19  	"github.com/spf13/pflag"
    20  )
    21  
    22  func newDockerCommand(dockerCli *command.DockerCli) *cobra.Command {
    23  	opts := cliflags.NewClientOptions()
    24  	var flags *pflag.FlagSet
    25  
    26  	cmd := &cobra.Command{
    27  		Use:              "docker [OPTIONS] COMMAND [ARG...]",
    28  		Short:            "A self-sufficient runtime for containers",
    29  		SilenceUsage:     true,
    30  		SilenceErrors:    true,
    31  		TraverseChildren: true,
    32  		Args:             noArgs,
    33  		RunE: func(cmd *cobra.Command, args []string) error {
    34  			if opts.Version {
    35  				showVersion()
    36  				return nil
    37  			}
    38  			return dockerCli.ShowHelp(cmd, args)
    39  		},
    40  		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
    41  			// daemon command is special, we redirect directly to another binary
    42  			if cmd.Name() == "daemon" {
    43  				return nil
    44  			}
    45  			// flags must be the top-level command flags, not cmd.Flags()
    46  			opts.Common.SetDefaultOptions(flags)
    47  			dockerPreRun(opts)
    48  			if err := dockerCli.Initialize(opts); err != nil {
    49  				return err
    50  			}
    51  			return isSupported(cmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental())
    52  		},
    53  	}
    54  	cli.SetupRootCommand(cmd)
    55  
    56  	cmd.SetHelpFunc(func(ccmd *cobra.Command, args []string) {
    57  		if dockerCli.Client() == nil { // when using --help, PersistenPreRun is not called, so initialization is needed.
    58  			// flags must be the top-level command flags, not cmd.Flags()
    59  			opts.Common.SetDefaultOptions(flags)
    60  			dockerPreRun(opts)
    61  			dockerCli.Initialize(opts)
    62  		}
    63  
    64  		if err := isSupported(ccmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental()); err != nil {
    65  			ccmd.Println(err)
    66  			return
    67  		}
    68  
    69  		hideUnsupportedFeatures(ccmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental())
    70  
    71  		if err := ccmd.Help(); err != nil {
    72  			ccmd.Println(err)
    73  		}
    74  	})
    75  
    76  	flags = cmd.Flags()
    77  	flags.BoolVarP(&opts.Version, "version", "v", false, "Print version information and quit")
    78  	flags.StringVar(&opts.ConfigDir, "config", cliconfig.ConfigDir(), "Location of client config files")
    79  	opts.Common.InstallFlags(flags)
    80  
    81  	cmd.SetOutput(dockerCli.Out())
    82  	cmd.AddCommand(newDaemonCommand())
    83  	commands.AddCommands(cmd, dockerCli)
    84  
    85  	return cmd
    86  }
    87  
    88  func noArgs(cmd *cobra.Command, args []string) error {
    89  	if len(args) == 0 {
    90  		return nil
    91  	}
    92  	return fmt.Errorf(
    93  		"docker: '%s' is not a docker command.\nSee 'docker --help'", args[0])
    94  }
    95  
    96  func main() {
    97  	// Set terminal emulation based on platform as required.
    98  	stdin, stdout, stderr := term.StdStreams()
    99  	logrus.SetOutput(stderr)
   100  
   101  	dockerCli := command.NewDockerCli(stdin, stdout, stderr)
   102  	cmd := newDockerCommand(dockerCli)
   103  
   104  	if err := cmd.Execute(); err != nil {
   105  		if sterr, ok := err.(cli.StatusError); ok {
   106  			if sterr.Status != "" {
   107  				fmt.Fprintln(stderr, sterr.Status)
   108  			}
   109  			// StatusError should only be used for errors, and all errors should
   110  			// have a non-zero exit status, so never exit with 0
   111  			if sterr.StatusCode == 0 {
   112  				os.Exit(1)
   113  			}
   114  			os.Exit(sterr.StatusCode)
   115  		}
   116  		fmt.Fprintln(stderr, err)
   117  		os.Exit(1)
   118  	}
   119  }
   120  
   121  func showVersion() {
   122  	fmt.Printf("Docker version %s, build %s\n", dockerversion.Version, dockerversion.GitCommit)
   123  }
   124  
   125  func dockerPreRun(opts *cliflags.ClientOptions) {
   126  	cliflags.SetLogLevel(opts.Common.LogLevel)
   127  
   128  	if opts.ConfigDir != "" {
   129  		cliconfig.SetConfigDir(opts.ConfigDir)
   130  	}
   131  
   132  	if opts.Common.Debug {
   133  		utils.EnableDebug()
   134  	}
   135  }
   136  
   137  func hideUnsupportedFeatures(cmd *cobra.Command, clientVersion string, hasExperimental bool) {
   138  	cmd.Flags().VisitAll(func(f *pflag.Flag) {
   139  		// hide experimental flags
   140  		if !hasExperimental {
   141  			if _, ok := f.Annotations["experimental"]; ok {
   142  				f.Hidden = true
   143  			}
   144  		}
   145  
   146  		// hide flags not supported by the server
   147  		if flagVersion, ok := f.Annotations["version"]; ok && len(flagVersion) == 1 && versions.LessThan(clientVersion, flagVersion[0]) {
   148  			f.Hidden = true
   149  		}
   150  
   151  	})
   152  
   153  	for _, subcmd := range cmd.Commands() {
   154  		// hide experimental subcommands
   155  		if !hasExperimental {
   156  			if _, ok := subcmd.Tags["experimental"]; ok {
   157  				subcmd.Hidden = true
   158  			}
   159  		}
   160  
   161  		// hide subcommands not supported by the server
   162  		if subcmdVersion, ok := subcmd.Tags["version"]; ok && versions.LessThan(clientVersion, subcmdVersion) {
   163  			subcmd.Hidden = true
   164  		}
   165  	}
   166  }
   167  
   168  func isSupported(cmd *cobra.Command, clientVersion string, hasExperimental bool) error {
   169  	if !hasExperimental {
   170  		if _, ok := cmd.Tags["experimental"]; ok {
   171  			return errors.New("only supported with experimental daemon")
   172  		}
   173  	}
   174  
   175  	if cmdVersion, ok := cmd.Tags["version"]; ok && versions.LessThan(clientVersion, cmdVersion) {
   176  		return fmt.Errorf("only supported with daemon version >= %s", cmdVersion)
   177  	}
   178  
   179  	return nil
   180  }