github.com/flavio/docker@v0.1.3-0.20170117145210-f63d1a6eec47/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 cliconfig "github.com/docker/docker/cli/config" 14 "github.com/docker/docker/cli/debug" 15 cliflags "github.com/docker/docker/cli/flags" 16 "github.com/docker/docker/dockerversion" 17 "github.com/docker/docker/pkg/term" 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.Dir(), "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.SetDir(opts.ConfigDir) 130 } 131 132 if opts.Common.Debug { 133 debug.Enable() 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 }