From 08784d7e0ec0e398c2eff368ae2d727ed1de4ef3 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Wed, 22 Jun 2016 13:08:04 -0400 Subject: [PATCH] Convert docker root command to use pflag and cobra Fix the daemon proxy for cobra commands. Signed-off-by: Daniel Nephin --- daemon.go | 18 -------- daemon_none.go | 14 +++++- daemon_unix.go | 40 ++++++++++++----- docker.go | 118 +++++++++++++++++++++---------------------------- docker_test.go | 12 ++++- 5 files changed, 103 insertions(+), 99 deletions(-) delete mode 100644 daemon.go diff --git a/daemon.go b/daemon.go deleted file mode 100644 index 8fe3484761..0000000000 --- a/daemon.go +++ /dev/null @@ -1,18 +0,0 @@ -package main - -const daemonBinary = "dockerd" - -// DaemonProxy acts as a cli.Handler to proxy calls to the daemon binary -type DaemonProxy struct{} - -// NewDaemonProxy returns a new handler -func NewDaemonProxy() DaemonProxy { - return DaemonProxy{} -} - -// Command returns a cli command handler if one exists -func (p DaemonProxy) Command(name string) func(...string) error { - return map[string]func(...string) error{ - "daemon": p.CmdDaemon, - }[name] -} diff --git a/daemon_none.go b/daemon_none.go index d66bf1a546..c57896ed71 100644 --- a/daemon_none.go +++ b/daemon_none.go @@ -4,12 +4,22 @@ package main import ( "fmt" + "github.com/spf13/cobra" "runtime" "strings" ) -// CmdDaemon reports on an error on windows, because there is no exec -func (p DaemonProxy) CmdDaemon(args ...string) error { +func newDaemonCommand() *cobra.Command { + return &cobra.Command{ + Use: "daemon", + Hidden: true, + RunE: func(cmd *cobra.Command, args []string) error { + return runDaemon() + }, + } +} + +func runDaemon() error { return fmt.Errorf( "`docker daemon` is not supported on %s. Please run `dockerd` directly", strings.Title(runtime.GOOS)) diff --git a/daemon_unix.go b/daemon_unix.go index d515b82914..30a40a8611 100644 --- a/daemon_unix.go +++ b/daemon_unix.go @@ -3,23 +3,37 @@ package main import ( + "fmt" + "os" "os/exec" "path/filepath" "syscall" + + "github.com/spf13/cobra" ) -// CmdDaemon execs dockerd with the same flags -func (p DaemonProxy) CmdDaemon(args ...string) error { - // Special case for handling `docker help daemon`. When pkg/mflag is removed - // we can support this on the daemon side, but that is not possible with - // pkg/mflag because it uses os.Exit(1) instead of returning an error on - // unexpected args. - if len(args) == 0 || args[0] != "--help" { - // Use os.Args[1:] so that "global" args are passed to dockerd - args = stripDaemonArg(os.Args[1:]) - } +const daemonBinary = "dockerd" +func newDaemonCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "daemon", + Hidden: true, + RunE: func(cmd *cobra.Command, args []string) error { + return runDaemon() + }, + } + cmd.SetHelpFunc(helpFunc) + return cmd +} + +// CmdDaemon execs dockerd with the same flags +func runDaemon() error { + // Use os.Args[1:] so that "global" args are passed to dockerd + return execDaemon(stripDaemonArg(os.Args[1:])) +} + +func execDaemon(args []string) error { binaryPath, err := findDaemonBinary() if err != nil { return err @@ -31,6 +45,12 @@ func (p DaemonProxy) CmdDaemon(args ...string) error { os.Environ()) } +func helpFunc(cmd *cobra.Command, args []string) { + if err := execDaemon([]string{"--help"}); err != nil { + fmt.Fprintf(os.Stderr, "%s\n", err.Error()) + } +} + // findDaemonBinary looks for the path to the dockerd binary starting with // the directory of the current executable (if one exists) and followed by $PATH func findDaemonBinary() (string, error) { diff --git a/docker.go b/docker.go index 7346d913ef..bd04a3a1bd 100644 --- a/docker.go +++ b/docker.go @@ -12,64 +12,52 @@ import ( cliflags "github.com/docker/docker/cli/flags" "github.com/docker/docker/cliconfig" "github.com/docker/docker/dockerversion" - flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/term" "github.com/docker/docker/utils" + "github.com/spf13/cobra" + "github.com/spf13/pflag" ) -var ( - commonFlags = cliflags.InitCommonFlags() - clientFlags = initClientFlags(commonFlags) - flHelp = flag.Bool([]string{"h", "-help"}, false, "Print usage") - flVersion = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit") -) +func newDockerCommand(dockerCli *client.DockerCli, opts *cliflags.ClientOptions) *cobra.Command { + cmd := &cobra.Command{ + Use: "docker [OPTIONS] COMMAND [arg...]", + Short: "A self-sufficient runtime for containers.", + SilenceUsage: true, + SilenceErrors: true, + Args: cli.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + if opts.Version { + showVersion() + return nil + } + fmt.Fprintf(dockerCli.Err(), "\n"+cmd.UsageString()) + return nil + }, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + dockerPreRun(cmd.Flags(), opts) + return dockerCli.Initialize(opts) + }, + } + cobraadaptor.SetupRootCommand(cmd, dockerCli) + + flags := cmd.Flags() + flags.BoolVarP(&opts.Version, "version", "v", false, "Print version information and quit") + flags.StringVar(&opts.ConfigDir, "config", cliconfig.ConfigDir(), "Location of client config files") + opts.Common.InstallFlags(flags) + + return cmd +} func main() { // Set terminal emulation based on platform as required. stdin, stdout, stderr := term.StdStreams() - logrus.SetOutput(stderr) - flag.Merge(flag.CommandLine, clientFlags.FlagSet, commonFlags.FlagSet) + opts := cliflags.NewClientOptions() + dockerCli := client.NewDockerCli(stdin, stdout, stderr, opts) + cmd := newDockerCommand(dockerCli, opts) - cobraAdaptor := cobraadaptor.NewCobraAdaptor(clientFlags) - - flag.Usage = func() { - fmt.Fprint(stdout, "Usage: docker [OPTIONS] COMMAND [arg...]\n docker [ --help | -v | --version ]\n\n") - fmt.Fprint(stdout, "A self-sufficient runtime for containers.\n\nOptions:\n") - - flag.CommandLine.SetOutput(stdout) - flag.PrintDefaults() - - help := "\nCommands:\n" - - dockerCommands := append(cli.DockerCommandUsage, cobraAdaptor.Usage()...) - for _, cmd := range sortCommands(dockerCommands) { - help += fmt.Sprintf(" %-10.10s%s\n", cmd.Name, cmd.Description) - } - - help += "\nRun 'docker COMMAND --help' for more information on a command." - fmt.Fprintf(stdout, "%s\n", help) - } - - flag.Parse() - - if *flVersion { - showVersion() - return - } - - if *flHelp { - // if global flag --help is present, regardless of what other options and commands there are, - // just print the usage. - flag.Usage() - return - } - - clientCli := client.NewDockerCli(stdin, stdout, stderr, clientFlags) - - c := cli.New(clientCli, NewDaemonProxy(), cobraAdaptor) - if err := c.Run(flag.Args()...); err != nil { + if err := cmd.Execute(); err != nil { if sterr, ok := err.(cli.StatusError); ok { if sterr.Status != "" { fmt.Fprintln(stderr, sterr.Status) @@ -94,26 +82,22 @@ func showVersion() { } } -func initClientFlags(commonFlags *cliflags.CommonFlags) *cliflags.ClientFlags { - clientFlags := &cliflags.ClientFlags{FlagSet: new(flag.FlagSet), Common: commonFlags} - client := clientFlags.FlagSet - client.StringVar(&clientFlags.ConfigDir, []string{"-config"}, cliconfig.ConfigDir(), "Location of client config files") +func dockerPreRun(flags *pflag.FlagSet, opts *cliflags.ClientOptions) { + opts.Common.SetDefaultOptions(flags) + cliflags.SetDaemonLogLevel(opts.Common.LogLevel) - clientFlags.PostParse = func() { - clientFlags.Common.PostParse() - cliflags.SetDaemonLogLevel(commonOpts.LogLevel) - - if clientFlags.ConfigDir != "" { - cliconfig.SetConfigDir(clientFlags.ConfigDir) - } - - if clientFlags.Common.TrustKey == "" { - clientFlags.Common.TrustKey = filepath.Join(cliconfig.ConfigDir(), cliflags.DefaultTrustKeyFile) - } - - if clientFlags.Common.Debug { - utils.EnableDebug() - } + // TODO: remove this, set a default in New, and pass it in opts + if opts.ConfigDir != "" { + cliconfig.SetConfigDir(opts.ConfigDir) + } + + if opts.Common.TrustKey == "" { + opts.Common.TrustKey = filepath.Join( + cliconfig.ConfigDir(), + cliflags.DefaultTrustKeyFile) + } + + if opts.Common.Debug { + utils.EnableDebug() } - return clientFlags } diff --git a/docker_test.go b/docker_test.go index 5708c96cb5..a1e84f1396 100644 --- a/docker_test.go +++ b/docker_test.go @@ -6,13 +6,21 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/docker/utils" + + "github.com/docker/docker/api/client" + cliflags "github.com/docker/docker/cli/flags" ) func TestClientDebugEnabled(t *testing.T) { defer utils.DisableDebug() - clientFlags.Common.FlagSet.Parse([]string{"-D"}) - clientFlags.PostParse() + opts := cliflags.NewClientOptions() + cmd := newDockerCommand(&client.DockerCli{}, opts) + + opts.Common.Debug = true + if err := cmd.PersistentPreRunE(cmd, []string{}); err != nil { + t.Fatalf("Unexpected error: %s", err.Error()) + } if os.Getenv("DEBUG") != "1" { t.Fatal("expected debug enabled, got false")