Przeglądaj źródła

Merge pull request #25354 from dnephin/remove-mflag-from-dockerd

Convert docker and dockerd commands to spf13/cobra
Daniel Nephin 9 lat temu
rodzic
commit
25587906d1
69 zmienionych plików z 1082 dodań i 3412 usunięć
  1. 34 41
      api/client/cli.go
  2. 71 0
      api/client/command/commands.go
  3. 0 6
      api/client/commands.go
  4. 0 1
      api/client/container/attach.go
  5. 0 1
      api/client/container/commit.go
  6. 0 1
      api/client/container/create.go
  7. 1 4
      api/client/container/diff.go
  8. 0 1
      api/client/container/logs.go
  9. 1 4
      api/client/container/pause.go
  10. 0 2
      api/client/container/port.go
  11. 0 2
      api/client/container/rename.go
  12. 0 1
      api/client/container/run.go
  13. 0 1
      api/client/container/start.go
  14. 0 1
      api/client/container/stop.go
  15. 0 1
      api/client/container/top.go
  16. 0 2
      api/client/container/unpause.go
  17. 0 2
      api/client/container/wait.go
  18. 0 191
      cli/cli.go
  19. 57 0
      cli/cobra.go
  20. 0 163
      cli/cobraadaptor/adaptor.go
  21. 14 1
      cli/error.go
  22. 0 21
      cli/flagerrors.go
  23. 9 8
      cli/flags/client.go
  24. 34 40
      cli/flags/common.go
  25. 0 19
      cli/usage.go
  26. 0 18
      cmd/docker/daemon.go
  27. 13 2
      cmd/docker/daemon_none.go
  28. 29 9
      cmd/docker/daemon_unix.go
  29. 59 66
      cmd/docker/docker.go
  30. 8 2
      cmd/docker/docker_test.go
  31. 0 22
      cmd/docker/usage.go
  32. 0 15
      cmd/docker/usage_test.go
  33. 37 54
      cmd/dockerd/daemon.go
  34. 96 241
      cmd/dockerd/daemon_test.go
  35. 70 200
      cmd/dockerd/daemon_unix_test.go
  36. 64 43
      cmd/dockerd/docker.go
  37. 8 1
      cmd/dockerd/service_unsupported.go
  38. 22 13
      cmd/dockerd/service_windows.go
  39. 53 45
      daemon/config.go
  40. 4 2
      daemon/config_experimental.go
  41. 4 6
      daemon/config_solaris.go
  42. 4 2
      daemon/config_stub.go
  43. 22 40
      daemon/config_test.go
  44. 34 35
      daemon/config_unix.go
  45. 7 10
      daemon/config_windows.go
  46. 1 1
      hack/vendor.sh
  47. 6 6
      integration-cli/docker_cli_build_test.go
  48. 4 4
      integration-cli/docker_cli_cp_test.go
  49. 27 41
      integration-cli/docker_cli_daemon_test.go
  50. 16 34
      integration-cli/docker_cli_help_test.go
  51. 6 13
      integration-cli/docker_cli_run_test.go
  52. 10 6
      man/generate.go
  53. 5 0
      opts/ip.go
  54. 0 27
      pkg/mflag/LICENSE
  55. 0 40
      pkg/mflag/README.md
  56. 0 37
      pkg/mflag/example/example.go
  57. 0 1280
      pkg/mflag/flag.go
  58. 0 527
      pkg/mflag/flag_test.go
  59. 24 9
      pkg/testutil/assert/assert.go
  60. 36 0
      pkg/testutil/tempfile/tempfile.go
  61. 6 6
      registry/config.go
  62. 3 3
      registry/config_unix.go
  63. 2 2
      registry/config_windows.go
  64. 5 0
      runconfig/opts/runtime.go
  65. 12 0
      vendor/src/github.com/spf13/cobra/.gitignore
  66. 35 7
      vendor/src/github.com/spf13/cobra/bash_completions.go
  67. 2 2
      vendor/src/github.com/spf13/cobra/bash_completions.md
  68. 4 0
      vendor/src/github.com/spf13/cobra/cobra.go
  69. 123 27
      vendor/src/github.com/spf13/cobra/command.go

+ 34 - 41
api/client/cli.go

@@ -6,6 +6,7 @@ import (
 	"io"
 	"net/http"
 	"os"
+	"path/filepath"
 	"runtime"
 
 	"github.com/docker/docker/api"
@@ -14,7 +15,7 @@ import (
 	"github.com/docker/docker/cliconfig/configfile"
 	"github.com/docker/docker/cliconfig/credentials"
 	"github.com/docker/docker/dockerversion"
-	"github.com/docker/docker/opts"
+	dopts "github.com/docker/docker/opts"
 	"github.com/docker/docker/pkg/term"
 	"github.com/docker/engine-api/client"
 	"github.com/docker/go-connections/sockets"
@@ -53,15 +54,6 @@ type DockerCli struct {
 	outState *term.State
 }
 
-// Initialize calls the init function that will setup the configuration for the client
-// such as the TLS, tcp and other parameters used to run the client.
-func (cli *DockerCli) Initialize() error {
-	if cli.init == nil {
-		return nil
-	}
-	return cli.init()
-}
-
 // Client returns the APIClient
 func (cli *DockerCli) Client() client.APIClient {
 	return cli.client
@@ -155,40 +147,41 @@ func (cli *DockerCli) restoreTerminal(in io.Closer) error {
 	return nil
 }
 
-// NewDockerCli returns a DockerCli instance with IO output and error streams set by in, out and err.
-// The key file, protocol (i.e. unix) and address are passed in as strings, along with the tls.Config. If the tls.Config
-// is set the client scheme will be set to https.
-// The client will be given a 32-second timeout (see https://github.com/docker/docker/pull/8035).
-func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cliflags.ClientFlags) *DockerCli {
-	cli := &DockerCli{
-		in:      in,
-		out:     out,
-		err:     err,
-		keyFile: clientFlags.Common.TrustKey,
+// Initialize the dockerCli runs initialization that must happen after command
+// line flags are parsed.
+func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions) error {
+	cli.configFile = LoadDefaultConfigFile(cli.err)
+
+	client, err := NewAPIClientFromFlags(opts.Common, cli.configFile)
+	if err != nil {
+		return err
 	}
 
-	cli.init = func() error {
-		clientFlags.PostParse()
-		cli.configFile = LoadDefaultConfigFile(err)
+	cli.client = client
 
-		client, err := NewAPIClientFromFlags(clientFlags, cli.configFile)
-		if err != nil {
-			return err
-		}
+	if cli.in != nil {
+		cli.inFd, cli.isTerminalIn = term.GetFdInfo(cli.in)
+	}
+	if cli.out != nil {
+		cli.outFd, cli.isTerminalOut = term.GetFdInfo(cli.out)
+	}
 
-		cli.client = client
+	if opts.Common.TrustKey == "" {
+		cli.keyFile = filepath.Join(cliconfig.ConfigDir(), cliflags.DefaultTrustKeyFile)
+	} else {
+		cli.keyFile = opts.Common.TrustKey
+	}
 
-		if cli.in != nil {
-			cli.inFd, cli.isTerminalIn = term.GetFdInfo(cli.in)
-		}
-		if cli.out != nil {
-			cli.outFd, cli.isTerminalOut = term.GetFdInfo(cli.out)
-		}
+	return nil
+}
 
-		return nil
+// 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:  in,
+		out: out,
+		err: err,
 	}
-
-	return cli
 }
 
 // LoadDefaultConfigFile attempts to load the default config file and returns
@@ -205,8 +198,8 @@ func LoadDefaultConfigFile(err io.Writer) *configfile.ConfigFile {
 }
 
 // NewAPIClientFromFlags creates a new APIClient from command line flags
-func NewAPIClientFromFlags(clientFlags *cliflags.ClientFlags, configFile *configfile.ConfigFile) (client.APIClient, error) {
-	host, err := getServerHost(clientFlags.Common.Hosts, clientFlags.Common.TLSOptions)
+func NewAPIClientFromFlags(opts *cliflags.CommonOptions, configFile *configfile.ConfigFile) (client.APIClient, error) {
+	host, err := getServerHost(opts.Hosts, opts.TLSOptions)
 	if err != nil {
 		return &client.Client{}, err
 	}
@@ -222,7 +215,7 @@ func NewAPIClientFromFlags(clientFlags *cliflags.ClientFlags, configFile *config
 		verStr = tmpStr
 	}
 
-	httpClient, err := newHTTPClient(host, clientFlags.Common.TLSOptions)
+	httpClient, err := newHTTPClient(host, opts.TLSOptions)
 	if err != nil {
 		return &client.Client{}, err
 	}
@@ -240,7 +233,7 @@ func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (host string,
 		return "", errors.New("Please specify only one -H")
 	}
 
-	host, err = opts.ParseHost(tlsOptions != nil, host)
+	host, err = dopts.ParseHost(tlsOptions != nil, host)
 	return
 }
 

+ 71 - 0
api/client/command/commands.go

@@ -0,0 +1,71 @@
+package command
+
+import (
+	"github.com/docker/docker/api/client"
+	"github.com/docker/docker/api/client/container"
+	"github.com/docker/docker/api/client/image"
+	"github.com/docker/docker/api/client/network"
+	"github.com/docker/docker/api/client/node"
+	"github.com/docker/docker/api/client/plugin"
+	"github.com/docker/docker/api/client/registry"
+	"github.com/docker/docker/api/client/service"
+	"github.com/docker/docker/api/client/stack"
+	"github.com/docker/docker/api/client/swarm"
+	"github.com/docker/docker/api/client/system"
+	"github.com/docker/docker/api/client/volume"
+	"github.com/spf13/cobra"
+)
+
+// AddCommands adds all the commands from api/client to the root command
+func AddCommands(cmd *cobra.Command, dockerCli *client.DockerCli) {
+	cmd.AddCommand(
+		node.NewNodeCommand(dockerCli),
+		service.NewServiceCommand(dockerCli),
+		stack.NewStackCommand(dockerCli),
+		stack.NewTopLevelDeployCommand(dockerCli),
+		swarm.NewSwarmCommand(dockerCli),
+		container.NewAttachCommand(dockerCli),
+		container.NewCommitCommand(dockerCli),
+		container.NewCopyCommand(dockerCli),
+		container.NewCreateCommand(dockerCli),
+		container.NewDiffCommand(dockerCli),
+		container.NewExecCommand(dockerCli),
+		container.NewExportCommand(dockerCli),
+		container.NewKillCommand(dockerCli),
+		container.NewLogsCommand(dockerCli),
+		container.NewPauseCommand(dockerCli),
+		container.NewPortCommand(dockerCli),
+		container.NewPsCommand(dockerCli),
+		container.NewRenameCommand(dockerCli),
+		container.NewRestartCommand(dockerCli),
+		container.NewRmCommand(dockerCli),
+		container.NewRunCommand(dockerCli),
+		container.NewStartCommand(dockerCli),
+		container.NewStatsCommand(dockerCli),
+		container.NewStopCommand(dockerCli),
+		container.NewTopCommand(dockerCli),
+		container.NewUnpauseCommand(dockerCli),
+		container.NewUpdateCommand(dockerCli),
+		container.NewWaitCommand(dockerCli),
+		image.NewBuildCommand(dockerCli),
+		image.NewHistoryCommand(dockerCli),
+		image.NewImagesCommand(dockerCli),
+		image.NewLoadCommand(dockerCli),
+		image.NewRemoveCommand(dockerCli),
+		image.NewSaveCommand(dockerCli),
+		image.NewPullCommand(dockerCli),
+		image.NewPushCommand(dockerCli),
+		image.NewSearchCommand(dockerCli),
+		image.NewImportCommand(dockerCli),
+		image.NewTagCommand(dockerCli),
+		network.NewNetworkCommand(dockerCli),
+		system.NewEventsCommand(dockerCli),
+		system.NewInspectCommand(dockerCli),
+		registry.NewLoginCommand(dockerCli),
+		registry.NewLogoutCommand(dockerCli),
+		system.NewVersionCommand(dockerCli),
+		volume.NewVolumeCommand(dockerCli),
+		system.NewInfoCommand(dockerCli),
+	)
+	plugin.NewPluginCommand(cmd, dockerCli)
+}

+ 0 - 6
api/client/commands.go

@@ -1,6 +0,0 @@
-package client
-
-// Command returns a cli command handler if one exists
-func (cli *DockerCli) Command(name string) func(...string) error {
-	return map[string]func(...string) error{}[name]
-}

+ 0 - 1
api/client/container/attach.go

@@ -36,7 +36,6 @@ func NewAttachCommand(dockerCli *client.DockerCli) *cobra.Command {
 			return runAttach(dockerCli, &opts)
 		},
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
 
 	flags := cmd.Flags()
 	flags.BoolVar(&opts.noStdin, "no-stdin", false, "Do not attach STDIN")

+ 0 - 1
api/client/container/commit.go

@@ -38,7 +38,6 @@ func NewCommitCommand(dockerCli *client.DockerCli) *cobra.Command {
 			return runCommit(dockerCli, &opts)
 		},
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
 
 	flags := cmd.Flags()
 	flags.SetInterspersed(false)

+ 0 - 1
api/client/container/create.go

@@ -43,7 +43,6 @@ func NewCreateCommand(dockerCli *client.DockerCli) *cobra.Command {
 			return runCreate(dockerCli, cmd.Flags(), &opts, copts)
 		},
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
 
 	flags := cmd.Flags()
 	flags.SetInterspersed(false)

+ 1 - 4
api/client/container/diff.go

@@ -19,7 +19,7 @@ type diffOptions struct {
 func NewDiffCommand(dockerCli *client.DockerCli) *cobra.Command {
 	var opts diffOptions
 
-	cmd := &cobra.Command{
+	return &cobra.Command{
 		Use:   "diff CONTAINER",
 		Short: "Inspect changes on a container's filesystem",
 		Args:  cli.ExactArgs(1),
@@ -28,9 +28,6 @@ func NewDiffCommand(dockerCli *client.DockerCli) *cobra.Command {
 			return runDiff(dockerCli, &opts)
 		},
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
-
-	return cmd
 }
 
 func runDiff(dockerCli *client.DockerCli, opts *diffOptions) error {

+ 0 - 1
api/client/container/logs.go

@@ -41,7 +41,6 @@ func NewLogsCommand(dockerCli *client.DockerCli) *cobra.Command {
 			return runLogs(dockerCli, &opts)
 		},
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
 
 	flags := cmd.Flags()
 	flags.BoolVarP(&opts.follow, "follow", "f", false, "Follow log output")

+ 1 - 4
api/client/container/pause.go

@@ -19,7 +19,7 @@ type pauseOptions struct {
 func NewPauseCommand(dockerCli *client.DockerCli) *cobra.Command {
 	var opts pauseOptions
 
-	cmd := &cobra.Command{
+	return &cobra.Command{
 		Use:   "pause CONTAINER [CONTAINER...]",
 		Short: "Pause all processes within one or more containers",
 		Args:  cli.RequiresMinArgs(1),
@@ -28,9 +28,6 @@ func NewPauseCommand(dockerCli *client.DockerCli) *cobra.Command {
 			return runPause(dockerCli, &opts)
 		},
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
-
-	return cmd
 }
 
 func runPause(dockerCli *client.DockerCli, opts *pauseOptions) error {

+ 0 - 2
api/client/container/port.go

@@ -34,8 +34,6 @@ func NewPortCommand(dockerCli *client.DockerCli) *cobra.Command {
 			return runPort(dockerCli, &opts)
 		},
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
-
 	return cmd
 }
 

+ 0 - 2
api/client/container/rename.go

@@ -30,8 +30,6 @@ func NewRenameCommand(dockerCli *client.DockerCli) *cobra.Command {
 			return runRename(dockerCli, &opts)
 		},
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
-
 	return cmd
 }
 

+ 0 - 1
api/client/container/run.go

@@ -48,7 +48,6 @@ func NewRunCommand(dockerCli *client.DockerCli) *cobra.Command {
 			return runRun(dockerCli, cmd.Flags(), &opts, copts)
 		},
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
 
 	flags := cmd.Flags()
 	flags.SetInterspersed(false)

+ 0 - 1
api/client/container/start.go

@@ -37,7 +37,6 @@ func NewStartCommand(dockerCli *client.DockerCli) *cobra.Command {
 			return runStart(dockerCli, &opts)
 		},
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
 
 	flags := cmd.Flags()
 	flags.BoolVarP(&opts.attach, "attach", "a", false, "Attach STDOUT/STDERR and forward signals")

+ 0 - 1
api/client/container/stop.go

@@ -31,7 +31,6 @@ func NewStopCommand(dockerCli *client.DockerCli) *cobra.Command {
 			return runStop(dockerCli, &opts)
 		},
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
 
 	flags := cmd.Flags()
 	flags.IntVarP(&opts.time, "time", "t", 10, "Seconds to wait for stop before killing it")

+ 0 - 1
api/client/container/top.go

@@ -32,7 +32,6 @@ func NewTopCommand(dockerCli *client.DockerCli) *cobra.Command {
 			return runTop(dockerCli, &opts)
 		},
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
 
 	flags := cmd.Flags()
 	flags.SetInterspersed(false)

+ 0 - 2
api/client/container/unpause.go

@@ -28,8 +28,6 @@ func NewUnpauseCommand(dockerCli *client.DockerCli) *cobra.Command {
 			return runUnpause(dockerCli, &opts)
 		},
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
-
 	return cmd
 }
 

+ 0 - 2
api/client/container/wait.go

@@ -28,8 +28,6 @@ func NewWaitCommand(dockerCli *client.DockerCli) *cobra.Command {
 			return runWait(dockerCli, &opts)
 		},
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
-
 	return cmd
 }
 

+ 0 - 191
cli/cli.go

@@ -1,191 +0,0 @@
-package cli
-
-import (
-	"errors"
-	"fmt"
-	"io"
-	"os"
-	"strings"
-
-	flag "github.com/docker/docker/pkg/mflag"
-)
-
-// Cli represents a command line interface.
-type Cli struct {
-	Stderr   io.Writer
-	handlers []Handler
-	Usage    func()
-}
-
-// Handler holds the different commands Cli will call
-// It should have methods with names starting with `Cmd` like:
-// 	func (h myHandler) CmdFoo(args ...string) error
-type Handler interface {
-	Command(name string) func(...string) error
-}
-
-// Initializer can be optionally implemented by a Handler to
-// initialize before each call to one of its commands.
-type Initializer interface {
-	Initialize() error
-}
-
-// New instantiates a ready-to-use Cli.
-func New(handlers ...Handler) *Cli {
-	// make the generic Cli object the first cli handler
-	// in order to handle `docker help` appropriately
-	cli := new(Cli)
-	cli.handlers = append([]Handler{cli}, handlers...)
-	return cli
-}
-
-var errCommandNotFound = errors.New("command not found")
-
-func (cli *Cli) command(args ...string) (func(...string) error, error) {
-	for _, c := range cli.handlers {
-		if c == nil {
-			continue
-		}
-		if cmd := c.Command(strings.Join(args, " ")); cmd != nil {
-			if ci, ok := c.(Initializer); ok {
-				if err := ci.Initialize(); err != nil {
-					return nil, err
-				}
-			}
-			return cmd, nil
-		}
-	}
-	return nil, errCommandNotFound
-}
-
-// Run executes the specified command.
-func (cli *Cli) Run(args ...string) error {
-	if len(args) > 1 {
-		command, err := cli.command(args[:2]...)
-		if err == nil {
-			return command(args[2:]...)
-		}
-		if err != errCommandNotFound {
-			return err
-		}
-	}
-	if len(args) > 0 {
-		command, err := cli.command(args[0])
-		if err != nil {
-			if err == errCommandNotFound {
-				cli.noSuchCommand(args[0])
-				return nil
-			}
-			return err
-		}
-		return command(args[1:]...)
-	}
-	return cli.CmdHelp()
-}
-
-func (cli *Cli) noSuchCommand(command string) {
-	if cli.Stderr == nil {
-		cli.Stderr = os.Stderr
-	}
-	fmt.Fprintf(cli.Stderr, "docker: '%s' is not a docker command.\nSee 'docker --help'.\n", command)
-	os.Exit(1)
-}
-
-// Command returns a command handler, or nil if the command does not exist
-func (cli *Cli) Command(name string) func(...string) error {
-	return map[string]func(...string) error{
-		"help": cli.CmdHelp,
-	}[name]
-}
-
-// CmdHelp displays information on a Docker command.
-//
-// If more than one command is specified, information is only shown for the first command.
-//
-// Usage: docker help COMMAND or docker COMMAND --help
-func (cli *Cli) CmdHelp(args ...string) error {
-	if len(args) > 1 {
-		command, err := cli.command(args[:2]...)
-		if err == nil {
-			command("--help")
-			return nil
-		}
-		if err != errCommandNotFound {
-			return err
-		}
-	}
-	if len(args) > 0 {
-		command, err := cli.command(args[0])
-		if err != nil {
-			if err == errCommandNotFound {
-				cli.noSuchCommand(args[0])
-				return nil
-			}
-			return err
-		}
-		command("--help")
-		return nil
-	}
-
-	if cli.Usage == nil {
-		flag.Usage()
-	} else {
-		cli.Usage()
-	}
-
-	return nil
-}
-
-// Subcmd is a subcommand of the main "docker" command.
-// A subcommand represents an action that can be performed
-// from the Docker command line client.
-//
-// To see all available subcommands, run "docker --help".
-func Subcmd(name string, synopses []string, description string, exitOnError bool) *flag.FlagSet {
-	var errorHandling flag.ErrorHandling
-	if exitOnError {
-		errorHandling = flag.ExitOnError
-	} else {
-		errorHandling = flag.ContinueOnError
-	}
-	flags := flag.NewFlagSet(name, errorHandling)
-	flags.Usage = func() {
-		flags.ShortUsage()
-		flags.PrintDefaults()
-	}
-
-	flags.ShortUsage = func() {
-		if len(synopses) == 0 {
-			synopses = []string{""}
-		}
-
-		// Allow for multiple command usage synopses.
-		for i, synopsis := range synopses {
-			lead := "\t"
-			if i == 0 {
-				// First line needs the word 'Usage'.
-				lead = "Usage:\t"
-			}
-
-			if synopsis != "" {
-				synopsis = " " + synopsis
-			}
-
-			fmt.Fprintf(flags.Out(), "\n%sdocker %s%s", lead, name, synopsis)
-		}
-
-		fmt.Fprintf(flags.Out(), "\n\n%s\n", description)
-	}
-
-	return flags
-}
-
-// StatusError reports an unsuccessful exit by a command.
-type StatusError struct {
-	Status     string
-	StatusCode int
-}
-
-func (e StatusError) Error() string {
-	return fmt.Sprintf("Status: %s, Code: %d", e.Status, e.StatusCode)
-}

+ 57 - 0
cli/cobra.go

@@ -0,0 +1,57 @@
+package cli
+
+import (
+	"fmt"
+
+	"github.com/spf13/cobra"
+)
+
+// SetupRootCommand sets default usage, help, and error handling for the
+// root command.
+func SetupRootCommand(rootCmd *cobra.Command) {
+	rootCmd.SetUsageTemplate(usageTemplate)
+	rootCmd.SetHelpTemplate(helpTemplate)
+	rootCmd.SetFlagErrorFunc(FlagErrorFunc)
+
+	rootCmd.PersistentFlags().BoolP("help", "h", false, "Print usage")
+	rootCmd.PersistentFlags().MarkShorthandDeprecated("help", "please use --help")
+}
+
+// FlagErrorFunc prints an error messages which matches the format of the
+// docker/docker/cli error messages
+func FlagErrorFunc(cmd *cobra.Command, err error) error {
+	if err == nil {
+		return err
+	}
+
+	usage := ""
+	if cmd.HasSubCommands() {
+		usage = "\n\n" + cmd.UsageString()
+	}
+	return StatusError{
+		Status:     fmt.Sprintf("%s\nSee '%s --help'.%s", err, cmd.CommandPath(), usage),
+		StatusCode: 125,
+	}
+}
+
+var usageTemplate = `Usage:	{{if not .HasSubCommands}}{{.UseLine}}{{end}}{{if .HasSubCommands}}{{ .CommandPath}} COMMAND{{end}}
+
+{{ .Short | trim }}{{if gt .Aliases 0}}
+
+Aliases:
+  {{.NameAndAliases}}{{end}}{{if .HasExample}}
+
+Examples:
+{{ .Example }}{{end}}{{if .HasFlags}}
+
+Options:
+{{.Flags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasAvailableSubCommands}}
+
+Commands:{{range .Commands}}{{if .IsAvailableCommand}}
+  {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasSubCommands }}
+
+Run '{{.CommandPath}} COMMAND --help' for more information on a command.{{end}}
+`
+
+var helpTemplate = `
+{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`

+ 0 - 163
cli/cobraadaptor/adaptor.go

@@ -1,163 +0,0 @@
-package cobraadaptor
-
-import (
-	"github.com/docker/docker/api/client"
-	"github.com/docker/docker/api/client/container"
-	"github.com/docker/docker/api/client/image"
-	"github.com/docker/docker/api/client/network"
-	"github.com/docker/docker/api/client/node"
-	"github.com/docker/docker/api/client/plugin"
-	"github.com/docker/docker/api/client/registry"
-	"github.com/docker/docker/api/client/service"
-	"github.com/docker/docker/api/client/stack"
-	"github.com/docker/docker/api/client/swarm"
-	"github.com/docker/docker/api/client/system"
-	"github.com/docker/docker/api/client/volume"
-	"github.com/docker/docker/cli"
-	cliflags "github.com/docker/docker/cli/flags"
-	"github.com/docker/docker/pkg/term"
-	"github.com/spf13/cobra"
-)
-
-// CobraAdaptor is an adaptor for supporting spf13/cobra commands in the
-// docker/cli framework
-type CobraAdaptor struct {
-	rootCmd   *cobra.Command
-	dockerCli *client.DockerCli
-}
-
-// NewCobraAdaptor returns a new handler
-func NewCobraAdaptor(clientFlags *cliflags.ClientFlags) CobraAdaptor {
-	stdin, stdout, stderr := term.StdStreams()
-	dockerCli := client.NewDockerCli(stdin, stdout, stderr, clientFlags)
-
-	var rootCmd = &cobra.Command{
-		Use:           "docker [OPTIONS]",
-		Short:         "A self-sufficient runtime for containers",
-		SilenceUsage:  true,
-		SilenceErrors: true,
-	}
-	rootCmd.SetUsageTemplate(usageTemplate)
-	rootCmd.SetHelpTemplate(helpTemplate)
-	rootCmd.SetFlagErrorFunc(cli.FlagErrorFunc)
-	rootCmd.SetOutput(stdout)
-	rootCmd.AddCommand(
-		node.NewNodeCommand(dockerCli),
-		service.NewServiceCommand(dockerCli),
-		stack.NewStackCommand(dockerCli),
-		stack.NewTopLevelDeployCommand(dockerCli),
-		swarm.NewSwarmCommand(dockerCli),
-		container.NewAttachCommand(dockerCli),
-		container.NewCommitCommand(dockerCli),
-		container.NewCopyCommand(dockerCli),
-		container.NewCreateCommand(dockerCli),
-		container.NewDiffCommand(dockerCli),
-		container.NewExecCommand(dockerCli),
-		container.NewExportCommand(dockerCli),
-		container.NewKillCommand(dockerCli),
-		container.NewLogsCommand(dockerCli),
-		container.NewPauseCommand(dockerCli),
-		container.NewPortCommand(dockerCli),
-		container.NewPsCommand(dockerCli),
-		container.NewRenameCommand(dockerCli),
-		container.NewRestartCommand(dockerCli),
-		container.NewRmCommand(dockerCli),
-		container.NewRunCommand(dockerCli),
-		container.NewStartCommand(dockerCli),
-		container.NewStatsCommand(dockerCli),
-		container.NewStopCommand(dockerCli),
-		container.NewTopCommand(dockerCli),
-		container.NewUnpauseCommand(dockerCli),
-		container.NewUpdateCommand(dockerCli),
-		container.NewWaitCommand(dockerCli),
-		image.NewBuildCommand(dockerCli),
-		image.NewHistoryCommand(dockerCli),
-		image.NewImagesCommand(dockerCli),
-		image.NewLoadCommand(dockerCli),
-		image.NewRemoveCommand(dockerCli),
-		image.NewSaveCommand(dockerCli),
-		image.NewPullCommand(dockerCli),
-		image.NewPushCommand(dockerCli),
-		image.NewSearchCommand(dockerCli),
-		image.NewImportCommand(dockerCli),
-		image.NewTagCommand(dockerCli),
-		network.NewNetworkCommand(dockerCli),
-		system.NewEventsCommand(dockerCli),
-		system.NewInspectCommand(dockerCli),
-		registry.NewLoginCommand(dockerCli),
-		registry.NewLogoutCommand(dockerCli),
-		system.NewVersionCommand(dockerCli),
-		volume.NewVolumeCommand(dockerCli),
-		system.NewInfoCommand(dockerCli),
-	)
-	plugin.NewPluginCommand(rootCmd, dockerCli)
-
-	rootCmd.PersistentFlags().BoolP("help", "h", false, "Print usage")
-	rootCmd.PersistentFlags().MarkShorthandDeprecated("help", "please use --help")
-
-	return CobraAdaptor{
-		rootCmd:   rootCmd,
-		dockerCli: dockerCli,
-	}
-}
-
-// Usage returns the list of commands and their short usage string for
-// all top level cobra commands.
-func (c CobraAdaptor) Usage() []cli.Command {
-	cmds := []cli.Command{}
-	for _, cmd := range c.rootCmd.Commands() {
-		if cmd.Name() != "" {
-			cmds = append(cmds, cli.Command{Name: cmd.Name(), Description: cmd.Short})
-		}
-	}
-	return cmds
-}
-
-func (c CobraAdaptor) run(cmd string, args []string) error {
-	if err := c.dockerCli.Initialize(); err != nil {
-		return err
-	}
-	// Prepend the command name to support normal cobra command delegation
-	c.rootCmd.SetArgs(append([]string{cmd}, args...))
-	return c.rootCmd.Execute()
-}
-
-// Command returns a cli command handler if one exists
-func (c CobraAdaptor) Command(name string) func(...string) error {
-	for _, cmd := range c.rootCmd.Commands() {
-		if cmd.Name() == name {
-			return func(args ...string) error {
-				return c.run(name, args)
-			}
-		}
-	}
-	return nil
-}
-
-// GetRootCommand returns the root command. Required to generate the man pages
-// and reference docs from a script outside this package.
-func (c CobraAdaptor) GetRootCommand() *cobra.Command {
-	return c.rootCmd
-}
-
-var usageTemplate = `Usage:	{{if not .HasSubCommands}}{{.UseLine}}{{end}}{{if .HasSubCommands}}{{ .CommandPath}} COMMAND{{end}}
-
-{{ .Short | trim }}{{if gt .Aliases 0}}
-
-Aliases:
-  {{.NameAndAliases}}{{end}}{{if .HasExample}}
-
-Examples:
-{{ .Example }}{{end}}{{if .HasFlags}}
-
-Options:
-{{.Flags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasAvailableSubCommands}}
-
-Commands:{{range .Commands}}{{if .IsAvailableCommand}}
-  {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasSubCommands }}
-
-Run '{{.CommandPath}} COMMAND --help' for more information on a command.{{end}}
-`
-
-var helpTemplate = `
-{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`

+ 14 - 1
cli/error.go

@@ -1,6 +1,9 @@
 package cli
 
-import "strings"
+import (
+	"fmt"
+	"strings"
+)
 
 // Errors is a list of errors.
 // Useful in a loop if you don't want to return the error right away and you want to display after the loop,
@@ -18,3 +21,13 @@ func (errList Errors) Error() string {
 	}
 	return strings.Join(out, ", ")
 }
+
+// StatusError reports an unsuccessful exit by a command.
+type StatusError struct {
+	Status     string
+	StatusCode int
+}
+
+func (e StatusError) Error() string {
+	return fmt.Sprintf("Status: %s, Code: %d", e.Status, e.StatusCode)
+}

+ 0 - 21
cli/flagerrors.go

@@ -1,21 +0,0 @@
-package cli
-
-import (
-	"fmt"
-
-	"github.com/spf13/cobra"
-)
-
-// FlagErrorFunc prints an error messages which matches the format of the
-// docker/docker/cli error messages
-func FlagErrorFunc(cmd *cobra.Command, err error) error {
-	if err == nil {
-		return err
-	}
-
-	usage := ""
-	if cmd.HasSubCommands() {
-		usage = "\n\n" + cmd.UsageString()
-	}
-	return fmt.Errorf("%s\nSee '%s --help'.%s", err, cmd.CommandPath(), usage)
-}

+ 9 - 8
cli/flags/client.go

@@ -1,12 +1,13 @@
 package flags
 
-import flag "github.com/docker/docker/pkg/mflag"
-
-// ClientFlags represents flags for the docker client.
-type ClientFlags struct {
-	FlagSet   *flag.FlagSet
-	Common    *CommonFlags
-	PostParse func()
-
+// ClientOptions are the options used to configure the client cli
+type ClientOptions struct {
+	Common    *CommonOptions
 	ConfigDir string
+	Version   bool
+}
+
+// NewClientOptions returns a new ClientOptions
+func NewClientOptions() *ClientOptions {
+	return &ClientOptions{Common: NewCommonOptions()}
 }

+ 34 - 40
cli/flags/common.go

@@ -8,8 +8,8 @@ import (
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/opts"
-	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/go-connections/tlsconfig"
+	"github.com/spf13/pflag"
 )
 
 const (
@@ -21,8 +21,8 @@ const (
 	DefaultKeyFile = "key.pem"
 	// DefaultCertFile is the default filename for the cert pem file
 	DefaultCertFile = "cert.pem"
-	// TLSVerifyKey is the default flag name for the tls verification option
-	TLSVerifyKey = "tlsverify"
+	// FlagTLSVerify is the flag name for the tls verification option
+	FlagTLSVerify = "tlsverify"
 )
 
 var (
@@ -30,11 +30,8 @@ var (
 	dockerTLSVerify = os.Getenv("DOCKER_TLS_VERIFY") != ""
 )
 
-// CommonFlags are flags common to both the client and the daemon.
-type CommonFlags struct {
-	FlagSet   *flag.FlagSet
-	PostParse func()
-
+// CommonOptions are options common to both the client and the daemon.
+type CommonOptions struct {
 	Debug      bool
 	Hosts      []string
 	LogLevel   string
@@ -44,62 +41,59 @@ type CommonFlags struct {
 	TrustKey   string
 }
 
-// InitCommonFlags initializes flags common to both client and daemon
-func InitCommonFlags() *CommonFlags {
-	var commonFlags = &CommonFlags{FlagSet: new(flag.FlagSet)}
+// NewCommonOptions returns a new CommonOptions
+func NewCommonOptions() *CommonOptions {
+	return &CommonOptions{}
+}
 
+// InstallFlags adds flags for the common options on the FlagSet
+func (commonOpts *CommonOptions) InstallFlags(flags *pflag.FlagSet) {
 	if dockerCertPath == "" {
 		dockerCertPath = cliconfig.ConfigDir()
 	}
 
-	commonFlags.PostParse = func() { postParseCommon(commonFlags) }
-
-	cmd := commonFlags.FlagSet
+	flags.BoolVarP(&commonOpts.Debug, "debug", "D", false, "Enable debug mode")
+	flags.StringVarP(&commonOpts.LogLevel, "log-level", "l", "info", "Set the logging level")
+	flags.BoolVar(&commonOpts.TLS, "tls", false, "Use TLS; implied by --tlsverify")
+	flags.BoolVar(&commonOpts.TLSVerify, FlagTLSVerify, dockerTLSVerify, "Use TLS and verify the remote")
 
-	cmd.BoolVar(&commonFlags.Debug, []string{"D", "-debug"}, false, "Enable debug mode")
-	cmd.StringVar(&commonFlags.LogLevel, []string{"l", "-log-level"}, "info", "Set the logging level")
-	cmd.BoolVar(&commonFlags.TLS, []string{"-tls"}, false, "Use TLS; implied by --tlsverify")
-	cmd.BoolVar(&commonFlags.TLSVerify, []string{"-tlsverify"}, dockerTLSVerify, "Use TLS and verify the remote")
+	// TODO use flag flags.String("identity"}, "i", "", "Path to libtrust key file")
 
-	// TODO use flag flag.String([]string{"i", "-identity"}, "", "Path to libtrust key file")
+	commonOpts.TLSOptions = &tlsconfig.Options{}
+	tlsOptions := commonOpts.TLSOptions
+	flags.StringVar(&tlsOptions.CAFile, "tlscacert", filepath.Join(dockerCertPath, DefaultCaFile), "Trust certs signed only by this CA")
+	flags.StringVar(&tlsOptions.CertFile, "tlscert", filepath.Join(dockerCertPath, DefaultCertFile), "Path to TLS certificate file")
+	flags.StringVar(&tlsOptions.KeyFile, "tlskey", filepath.Join(dockerCertPath, DefaultKeyFile), "Path to TLS key file")
 
-	var tlsOptions tlsconfig.Options
-	commonFlags.TLSOptions = &tlsOptions
-	cmd.StringVar(&tlsOptions.CAFile, []string{"-tlscacert"}, filepath.Join(dockerCertPath, DefaultCaFile), "Trust certs signed only by this CA")
-	cmd.StringVar(&tlsOptions.CertFile, []string{"-tlscert"}, filepath.Join(dockerCertPath, DefaultCertFile), "Path to TLS certificate file")
-	cmd.StringVar(&tlsOptions.KeyFile, []string{"-tlskey"}, filepath.Join(dockerCertPath, DefaultKeyFile), "Path to TLS key file")
-
-	cmd.Var(opts.NewNamedListOptsRef("hosts", &commonFlags.Hosts, opts.ValidateHost), []string{"H", "-host"}, "Daemon socket(s) to connect to")
-	return commonFlags
+	hostOpt := opts.NewNamedListOptsRef("hosts", &commonOpts.Hosts, opts.ValidateHost)
+	flags.VarP(hostOpt, "host", "H", "Daemon socket(s) to connect to")
 }
 
-func postParseCommon(commonFlags *CommonFlags) {
-	cmd := commonFlags.FlagSet
-
-	SetDaemonLogLevel(commonFlags.LogLevel)
-
+// SetDefaultOptions sets default values for options after flag parsing is
+// complete
+func (commonOpts *CommonOptions) SetDefaultOptions(flags *pflag.FlagSet) {
 	// Regardless of whether the user sets it to true or false, if they
 	// specify --tlsverify at all then we need to turn on tls
 	// TLSVerify can be true even if not set due to DOCKER_TLS_VERIFY env var, so we need
 	// to check that here as well
-	if cmd.IsSet("-"+TLSVerifyKey) || commonFlags.TLSVerify {
-		commonFlags.TLS = true
+	if flags.Changed(FlagTLSVerify) || commonOpts.TLSVerify {
+		commonOpts.TLS = true
 	}
 
-	if !commonFlags.TLS {
-		commonFlags.TLSOptions = nil
+	if !commonOpts.TLS {
+		commonOpts.TLSOptions = nil
 	} else {
-		tlsOptions := commonFlags.TLSOptions
-		tlsOptions.InsecureSkipVerify = !commonFlags.TLSVerify
+		tlsOptions := commonOpts.TLSOptions
+		tlsOptions.InsecureSkipVerify = !commonOpts.TLSVerify
 
 		// Reset CertFile and KeyFile to empty string if the user did not specify
 		// the respective flags and the respective default files were not found.
-		if !cmd.IsSet("-tlscert") {
+		if !flags.Changed("tlscert") {
 			if _, err := os.Stat(tlsOptions.CertFile); os.IsNotExist(err) {
 				tlsOptions.CertFile = ""
 			}
 		}
-		if !cmd.IsSet("-tlskey") {
+		if !flags.Changed("tlskey") {
 			if _, err := os.Stat(tlsOptions.KeyFile); os.IsNotExist(err) {
 				tlsOptions.KeyFile = ""
 			}

+ 0 - 19
cli/usage.go

@@ -1,19 +0,0 @@
-package cli
-
-// Command is the struct containing the command name and description
-type Command struct {
-	Name        string
-	Description string
-}
-
-// DockerCommandUsage lists the top level docker commands and their short usage
-var DockerCommandUsage = []Command{}
-
-// DockerCommands stores all the docker command
-var DockerCommands = make(map[string]Command)
-
-func init() {
-	for _, cmd := range DockerCommandUsage {
-		DockerCommands[cmd.Name] = cmd
-	}
-}

+ 0 - 18
cmd/docker/daemon.go

@@ -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]
-}

+ 13 - 2
cmd/docker/daemon_none.go

@@ -6,10 +6,21 @@ import (
 	"fmt"
 	"runtime"
 	"strings"
+
+	"github.com/spf13/cobra"
 )
 
-// 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))

+ 29 - 9
cmd/docker/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) {

+ 59 - 66
cmd/docker/docker.go

@@ -3,73 +3,77 @@ package main
 import (
 	"fmt"
 	"os"
-	"path/filepath"
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/client"
+	"github.com/docker/docker/api/client/command"
 	"github.com/docker/docker/cli"
-	"github.com/docker/docker/cli/cobraadaptor"
 	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 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)
-
-	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()
+func newDockerCommand(dockerCli *client.DockerCli) *cobra.Command {
+	opts := cliflags.NewClientOptions()
+	var flags *pflag.FlagSet
+
+	cmd := &cobra.Command{
+		Use:              "docker [OPTIONS] COMMAND [arg...]",
+		Short:            "A self-sufficient runtime for containers.",
+		SilenceUsage:     true,
+		SilenceErrors:    true,
+		TraverseChildren: true,
+		Args:             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 {
+			// flags must be the top-level command flags, not cmd.Flags()
+			opts.Common.SetDefaultOptions(flags)
+			dockerPreRun(opts)
+			return dockerCli.Initialize(opts)
+		},
+	}
+	cli.SetupRootCommand(cmd)
 
-		help := "\nCommands:\n"
+	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)
 
-		dockerCommands := append(cli.DockerCommandUsage, cobraAdaptor.Usage()...)
-		for _, cmd := range sortCommands(dockerCommands) {
-			help += fmt.Sprintf("    %-10.10s%s\n", cmd.Name, cmd.Description)
-		}
+	cmd.SetOutput(dockerCli.Out())
+	cmd.AddCommand(newDaemonCommand())
+	command.AddCommands(cmd, dockerCli)
 
-		help += "\nRun 'docker COMMAND --help' for more information on a command."
-		fmt.Fprintf(stdout, "%s\n", help)
-	}
-
-	flag.Parse()
+	return cmd
+}
 
-	if *flVersion {
-		showVersion()
-		return
+func noArgs(cmd *cobra.Command, args []string) error {
+	if len(args) == 0 {
+		return nil
 	}
+	return fmt.Errorf(
+		"docker: '%s' is not a docker command.\nSee 'docker --help'%s", args[0], ".")
+}
 
-	if *flHelp {
-		// if global flag --help is present, regardless of what other options and commands there are,
-		// just print the usage.
-		flag.Usage()
-		return
-	}
+func main() {
+	// Set terminal emulation based on platform as required.
+	stdin, stdout, stderr := term.StdStreams()
+	logrus.SetOutput(stderr)
 
-	clientCli := client.NewDockerCli(stdin, stdout, stderr, clientFlags)
+	dockerCli := client.NewDockerCli(stdin, stdout, stderr)
+	cmd := newDockerCommand(dockerCli)
 
-	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,25 +98,14 @@ 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")
-
-	clientFlags.PostParse = func() {
-		clientFlags.Common.PostParse()
-
-		if clientFlags.ConfigDir != "" {
-			cliconfig.SetConfigDir(clientFlags.ConfigDir)
-		}
+func dockerPreRun(opts *cliflags.ClientOptions) {
+	cliflags.SetDaemonLogLevel(opts.Common.LogLevel)
 
-		if clientFlags.Common.TrustKey == "" {
-			clientFlags.Common.TrustKey = filepath.Join(cliconfig.ConfigDir(), cliflags.DefaultTrustKeyFile)
-		}
+	if opts.ConfigDir != "" {
+		cliconfig.SetConfigDir(opts.ConfigDir)
+	}
 
-		if clientFlags.Common.Debug {
-			utils.EnableDebug()
-		}
+	if opts.Common.Debug {
+		utils.EnableDebug()
 	}
-	return clientFlags
 }

+ 8 - 2
cmd/docker/docker_test.go

@@ -6,13 +6,19 @@ import (
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/utils"
+
+	"github.com/docker/docker/api/client"
 )
 
 func TestClientDebugEnabled(t *testing.T) {
 	defer utils.DisableDebug()
 
-	clientFlags.Common.FlagSet.Parse([]string{"-D"})
-	clientFlags.PostParse()
+	cmd := newDockerCommand(&client.DockerCli{})
+	cmd.Flags().Set("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")

+ 0 - 22
cmd/docker/usage.go

@@ -1,22 +0,0 @@
-package main
-
-import (
-	"sort"
-
-	"github.com/docker/docker/cli"
-)
-
-type byName []cli.Command
-
-func (a byName) Len() int           { return len(a) }
-func (a byName) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
-func (a byName) Less(i, j int) bool { return a[i].Name < a[j].Name }
-
-// TODO(tiborvass): do not show 'daemon' on client-only binaries
-
-func sortCommands(commands []cli.Command) []cli.Command {
-	dockerCommands := make([]cli.Command, len(commands))
-	copy(dockerCommands, commands)
-	sort.Sort(byName(dockerCommands))
-	return dockerCommands
-}

+ 0 - 15
cmd/docker/usage_test.go

@@ -1,15 +0,0 @@
-package main
-
-import (
-	"sort"
-	"testing"
-
-	"github.com/docker/docker/cli"
-)
-
-// Tests if the subcommands of docker are sorted
-func TestDockerSubcommandsAreSorted(t *testing.T) {
-	if !sort.IsSorted(byName(cli.DockerCommandUsage)) {
-		t.Fatal("Docker subcommands are not in sorted order")
-	}
-}

+ 37 - 54
cmd/dockerd/daemon.go

@@ -6,7 +6,6 @@ import (
 	"io"
 	"os"
 	"path/filepath"
-	"runtime"
 	"strings"
 	"time"
 
@@ -31,11 +30,10 @@ import (
 	"github.com/docker/docker/daemon/logger"
 	"github.com/docker/docker/dockerversion"
 	"github.com/docker/docker/libcontainerd"
-	"github.com/docker/docker/opts"
+	dopts "github.com/docker/docker/opts"
 	"github.com/docker/docker/pkg/authorization"
 	"github.com/docker/docker/pkg/jsonlog"
 	"github.com/docker/docker/pkg/listeners"
-	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/pidfile"
 	"github.com/docker/docker/pkg/signal"
 	"github.com/docker/docker/pkg/system"
@@ -43,46 +41,27 @@ import (
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/utils"
 	"github.com/docker/go-connections/tlsconfig"
+	"github.com/spf13/pflag"
 )
 
 const (
-	daemonConfigFileFlag = "-config-file"
+	flagDaemonConfigFile = "config-file"
 )
 
 // DaemonCli represents the daemon CLI.
 type DaemonCli struct {
 	*daemon.Config
-	commonFlags *cliflags.CommonFlags
-	configFile  *string
+	configFile *string
+	flags      *pflag.FlagSet
 
 	api             *apiserver.Server
 	d               *daemon.Daemon
 	authzMiddleware *authorization.Middleware // authzMiddleware enables to dynamically reload the authorization plugins
 }
 
-func presentInHelp(usage string) string { return usage }
-func absentFromHelp(string) string      { return "" }
-
-// NewDaemonCli returns a pre-configured daemon CLI
+// NewDaemonCli returns a daemon CLI
 func NewDaemonCli() *DaemonCli {
-	// TODO(tiborvass): remove InstallFlags?
-	daemonConfig := new(daemon.Config)
-	daemonConfig.LogConfig.Config = make(map[string]string)
-	daemonConfig.ClusterOpts = make(map[string]string)
-
-	daemonConfig.InstallFlags(flag.CommandLine, presentInHelp)
-	configFile := flag.CommandLine.String([]string{daemonConfigFileFlag}, defaultDaemonConfigFile, "Daemon configuration file")
-	flag.CommandLine.Require(flag.Exact, 0)
-
-	if runtime.GOOS != "linux" {
-		daemonConfig.V2Only = true
-	}
-
-	return &DaemonCli{
-		Config:      daemonConfig,
-		commonFlags: cliflags.InitCommonFlags(),
-		configFile:  configFile,
-	}
+	return &DaemonCli{}
 }
 
 func migrateKey() (err error) {
@@ -126,24 +105,25 @@ func migrateKey() (err error) {
 	return nil
 }
 
-func (cli *DaemonCli) start() (err error) {
+func (cli *DaemonCli) start(opts daemonOptions) (err error) {
 	stopc := make(chan bool)
 	defer close(stopc)
 
 	// warn from uuid package when running the daemon
 	uuid.Loggerf = logrus.Warnf
 
-	flags := flag.CommandLine
-	cli.commonFlags.PostParse()
+	opts.common.SetDefaultOptions(opts.flags)
 
-	if cli.commonFlags.TrustKey == "" {
-		cli.commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), cliflags.DefaultTrustKeyFile)
+	if opts.common.TrustKey == "" {
+		opts.common.TrustKey = filepath.Join(
+			getDaemonConfDir(),
+			cliflags.DefaultTrustKeyFile)
 	}
-	cliConfig, err := loadDaemonCliConfig(cli.Config, flags, cli.commonFlags, *cli.configFile)
-	if err != nil {
+	if cli.Config, err = loadDaemonCliConfig(opts); err != nil {
 		return err
 	}
-	cli.Config = cliConfig
+	cli.configFile = &opts.configFile
+	cli.flags = opts.flags
 
 	if cli.Config.Debug {
 		utils.EnableDebug()
@@ -215,7 +195,7 @@ func (cli *DaemonCli) start() (err error) {
 
 	for i := 0; i < len(cli.Config.Hosts); i++ {
 		var err error
-		if cli.Config.Hosts[i], err = opts.ParseHost(cli.Config.TLS, cli.Config.Hosts[i]); err != nil {
+		if cli.Config.Hosts[i], err = dopts.ParseHost(cli.Config.TLS, cli.Config.Hosts[i]); err != nil {
 			return fmt.Errorf("error parsing -H %s : %v", cli.Config.Hosts[i], err)
 		}
 
@@ -250,7 +230,8 @@ func (cli *DaemonCli) start() (err error) {
 	if err := migrateKey(); err != nil {
 		return err
 	}
-	cli.TrustKeyPath = cli.commonFlags.TrustKey
+	// FIXME: why is this down here instead of with the other TrustKey logic above?
+	cli.TrustKeyPath = opts.common.TrustKey
 
 	registryService := registry.NewService(cli.Config.ServiceOptions)
 	containerdRemote, err := libcontainerd.New(cli.getLibcontainerdRoot(), cli.getPlatformRemoteOptions()...)
@@ -341,7 +322,7 @@ func (cli *DaemonCli) reloadConfig() {
 		}
 	}
 
-	if err := daemon.ReloadConfiguration(*cli.configFile, flag.CommandLine, reload); err != nil {
+	if err := daemon.ReloadConfiguration(*cli.configFile, cli.flags, reload); err != nil {
 		logrus.Error(err)
 	}
 }
@@ -367,25 +348,27 @@ func shutdownDaemon(d *daemon.Daemon, timeout time.Duration) {
 	}
 }
 
-func loadDaemonCliConfig(config *daemon.Config, flags *flag.FlagSet, commonConfig *cliflags.CommonFlags, configFile string) (*daemon.Config, error) {
-	config.Debug = commonConfig.Debug
-	config.Hosts = commonConfig.Hosts
-	config.LogLevel = commonConfig.LogLevel
-	config.TLS = commonConfig.TLS
-	config.TLSVerify = commonConfig.TLSVerify
+func loadDaemonCliConfig(opts daemonOptions) (*daemon.Config, error) {
+	config := opts.daemonConfig
+	flags := opts.flags
+	config.Debug = opts.common.Debug
+	config.Hosts = opts.common.Hosts
+	config.LogLevel = opts.common.LogLevel
+	config.TLS = opts.common.TLS
+	config.TLSVerify = opts.common.TLSVerify
 	config.CommonTLSOptions = daemon.CommonTLSOptions{}
 
-	if commonConfig.TLSOptions != nil {
-		config.CommonTLSOptions.CAFile = commonConfig.TLSOptions.CAFile
-		config.CommonTLSOptions.CertFile = commonConfig.TLSOptions.CertFile
-		config.CommonTLSOptions.KeyFile = commonConfig.TLSOptions.KeyFile
+	if opts.common.TLSOptions != nil {
+		config.CommonTLSOptions.CAFile = opts.common.TLSOptions.CAFile
+		config.CommonTLSOptions.CertFile = opts.common.TLSOptions.CertFile
+		config.CommonTLSOptions.KeyFile = opts.common.TLSOptions.KeyFile
 	}
 
-	if configFile != "" {
-		c, err := daemon.MergeDaemonConfigurations(config, flags, configFile)
+	if opts.configFile != "" {
+		c, err := daemon.MergeDaemonConfigurations(config, flags, opts.configFile)
 		if err != nil {
-			if flags.IsSet(daemonConfigFileFlag) || !os.IsNotExist(err) {
-				return nil, fmt.Errorf("unable to configure the Docker daemon with file %s: %v\n", configFile, err)
+			if flags.Changed(flagDaemonConfigFile) || !os.IsNotExist(err) {
+				return nil, fmt.Errorf("unable to configure the Docker daemon with file %s: %v\n", opts.configFile, err)
 			}
 		}
 		// the merged configuration can be nil if the config file didn't exist.
@@ -401,7 +384,7 @@ func loadDaemonCliConfig(config *daemon.Config, flags *flag.FlagSet, commonConfi
 
 	// Regardless of whether the user sets it to true or false, if they
 	// specify TLSVerify at all then we need to turn on TLS
-	if config.IsValueSet(cliflags.TLSVerifyKey) {
+	if config.IsValueSet(cliflags.FlagTLSVerify) {
 		config.TLS = true
 	}
 

+ 96 - 241
cmd/dockerd/daemon_test.go

@@ -1,290 +1,145 @@
 package main
 
 import (
-	"io/ioutil"
-	"os"
-	"strings"
 	"testing"
 
 	"github.com/Sirupsen/logrus"
 	cliflags "github.com/docker/docker/cli/flags"
 	"github.com/docker/docker/daemon"
-	"github.com/docker/docker/opts"
-	"github.com/docker/docker/pkg/mflag"
-	"github.com/docker/go-connections/tlsconfig"
+	"github.com/docker/docker/pkg/testutil/assert"
+	"github.com/docker/docker/pkg/testutil/tempfile"
+	"github.com/spf13/pflag"
 )
 
+func defaultOptions(configFile string) daemonOptions {
+	opts := daemonOptions{
+		daemonConfig: &daemon.Config{},
+		flags:        &pflag.FlagSet{},
+		common:       cliflags.NewCommonOptions(),
+	}
+	opts.common.InstallFlags(opts.flags)
+	opts.daemonConfig.InstallFlags(opts.flags)
+	opts.flags.StringVar(&opts.configFile, flagDaemonConfigFile, defaultDaemonConfigFile, "")
+	opts.configFile = configFile
+	return opts
+}
+
 func TestLoadDaemonCliConfigWithoutOverriding(t *testing.T) {
-	c := &daemon.Config{}
-	common := &cliflags.CommonFlags{
-		Debug: true,
-	}
+	opts := defaultOptions("")
+	opts.common.Debug = true
 
-	flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
-	loadedConfig, err := loadDaemonCliConfig(c, flags, common, "/tmp/fooobarbaz")
-	if err != nil {
-		t.Fatal(err)
-	}
-	if loadedConfig == nil {
-		t.Fatalf("expected configuration %v, got nil", c)
-	}
+	loadedConfig, err := loadDaemonCliConfig(opts)
+	assert.NilError(t, err)
+	assert.NotNil(t, loadedConfig)
 	if !loadedConfig.Debug {
 		t.Fatalf("expected debug to be copied from the common flags, got false")
 	}
 }
 
 func TestLoadDaemonCliConfigWithTLS(t *testing.T) {
-	c := &daemon.Config{}
-	common := &cliflags.CommonFlags{
-		TLS: true,
-		TLSOptions: &tlsconfig.Options{
-			CAFile: "/tmp/ca.pem",
-		},
-	}
-
-	flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
-	loadedConfig, err := loadDaemonCliConfig(c, flags, common, "/tmp/fooobarbaz")
-	if err != nil {
-		t.Fatal(err)
-	}
-	if loadedConfig == nil {
-		t.Fatalf("expected configuration %v, got nil", c)
-	}
-	if loadedConfig.CommonTLSOptions.CAFile != "/tmp/ca.pem" {
-		t.Fatalf("expected /tmp/ca.pem, got %s: %q", loadedConfig.CommonTLSOptions.CAFile, loadedConfig)
-	}
+	opts := defaultOptions("")
+	opts.common.TLSOptions.CAFile = "/tmp/ca.pem"
+	opts.common.TLS = true
+
+	loadedConfig, err := loadDaemonCliConfig(opts)
+	assert.NilError(t, err)
+	assert.NotNil(t, loadedConfig)
+	assert.Equal(t, loadedConfig.CommonTLSOptions.CAFile, "/tmp/ca.pem")
 }
 
 func TestLoadDaemonCliConfigWithConflicts(t *testing.T) {
-	c := &daemon.Config{}
-	common := &cliflags.CommonFlags{}
-	f, err := ioutil.TempFile("", "docker-config-")
-	if err != nil {
-		t.Fatal(err)
-	}
-	configFile := f.Name()
-	defer os.Remove(configFile)
-
-	f.Write([]byte(`{"labels": ["l3=foo"]}`))
-	f.Close()
-
-	var labels []string
+	tempFile := tempfile.NewTempFile(t, "config", `{"labels": ["l3=foo"]}`)
+	defer tempFile.Remove()
+	configFile := tempFile.Name()
 
-	flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
-	flags.String([]string{daemonConfigFileFlag}, "", "")
-	flags.Var(opts.NewNamedListOptsRef("labels", &labels, opts.ValidateLabel), []string{"-label"}, "")
+	opts := defaultOptions(configFile)
+	flags := opts.flags
 
-	flags.Set(daemonConfigFileFlag, configFile)
-	if err := flags.Set("-label", "l1=bar"); err != nil {
-		t.Fatal(err)
-	}
-	if err := flags.Set("-label", "l2=baz"); err != nil {
-		t.Fatal(err)
-	}
+	assert.NilError(t, flags.Set(flagDaemonConfigFile, configFile))
+	assert.NilError(t, flags.Set("label", "l1=bar"))
+	assert.NilError(t, flags.Set("label", "l2=baz"))
 
-	_, err = loadDaemonCliConfig(c, flags, common, configFile)
-	if err == nil {
-		t.Fatalf("expected configuration error, got nil")
-	}
-	if !strings.Contains(err.Error(), "labels") {
-		t.Fatalf("expected labels conflict, got %v", err)
-	}
+	_, err := loadDaemonCliConfig(opts)
+	assert.Error(t, err, "as a flag and in the configuration file: labels")
 }
 
 func TestLoadDaemonCliConfigWithTLSVerify(t *testing.T) {
-	c := &daemon.Config{}
-	common := &cliflags.CommonFlags{
-		TLSOptions: &tlsconfig.Options{
-			CAFile: "/tmp/ca.pem",
-		},
-	}
-
-	f, err := ioutil.TempFile("", "docker-config-")
-	if err != nil {
-		t.Fatal(err)
-	}
-	configFile := f.Name()
-	defer os.Remove(configFile)
-
-	f.Write([]byte(`{"tlsverify": true}`))
-	f.Close()
+	tempFile := tempfile.NewTempFile(t, "config", `{"tlsverify": true}`)
+	defer tempFile.Remove()
 
-	flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
-	flags.Bool([]string{"-tlsverify"}, false, "")
-	loadedConfig, err := loadDaemonCliConfig(c, flags, common, configFile)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if loadedConfig == nil {
-		t.Fatalf("expected configuration %v, got nil", c)
-	}
+	opts := defaultOptions(tempFile.Name())
+	opts.common.TLSOptions.CAFile = "/tmp/ca.pem"
 
-	if !loadedConfig.TLS {
-		t.Fatalf("expected TLS enabled, got %q", loadedConfig)
-	}
+	loadedConfig, err := loadDaemonCliConfig(opts)
+	assert.NilError(t, err)
+	assert.NotNil(t, loadedConfig)
+	assert.Equal(t, loadedConfig.TLS, true)
 }
 
 func TestLoadDaemonCliConfigWithExplicitTLSVerifyFalse(t *testing.T) {
-	c := &daemon.Config{}
-	common := &cliflags.CommonFlags{
-		TLSOptions: &tlsconfig.Options{
-			CAFile: "/tmp/ca.pem",
-		},
-	}
+	tempFile := tempfile.NewTempFile(t, "config", `{"tlsverify": false}`)
+	defer tempFile.Remove()
 
-	f, err := ioutil.TempFile("", "docker-config-")
-	if err != nil {
-		t.Fatal(err)
-	}
-	configFile := f.Name()
-	defer os.Remove(configFile)
-
-	f.Write([]byte(`{"tlsverify": false}`))
-	f.Close()
-
-	flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
-	flags.Bool([]string{"-tlsverify"}, false, "")
-	loadedConfig, err := loadDaemonCliConfig(c, flags, common, configFile)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if loadedConfig == nil {
-		t.Fatalf("expected configuration %v, got nil", c)
-	}
+	opts := defaultOptions(tempFile.Name())
+	opts.common.TLSOptions.CAFile = "/tmp/ca.pem"
 
-	if !loadedConfig.TLS {
-		t.Fatalf("expected TLS enabled, got %q", loadedConfig)
-	}
+	loadedConfig, err := loadDaemonCliConfig(opts)
+	assert.NilError(t, err)
+	assert.NotNil(t, loadedConfig)
+	assert.Equal(t, loadedConfig.TLS, true)
 }
 
 func TestLoadDaemonCliConfigWithoutTLSVerify(t *testing.T) {
-	c := &daemon.Config{}
-	common := &cliflags.CommonFlags{
-		TLSOptions: &tlsconfig.Options{
-			CAFile: "/tmp/ca.pem",
-		},
-	}
-
-	f, err := ioutil.TempFile("", "docker-config-")
-	if err != nil {
-		t.Fatal(err)
-	}
-	configFile := f.Name()
-	defer os.Remove(configFile)
+	tempFile := tempfile.NewTempFile(t, "config", `{}`)
+	defer tempFile.Remove()
 
-	f.Write([]byte(`{}`))
-	f.Close()
+	opts := defaultOptions(tempFile.Name())
+	opts.common.TLSOptions.CAFile = "/tmp/ca.pem"
 
-	flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
-	loadedConfig, err := loadDaemonCliConfig(c, flags, common, configFile)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if loadedConfig == nil {
-		t.Fatalf("expected configuration %v, got nil", c)
-	}
-
-	if loadedConfig.TLS {
-		t.Fatalf("expected TLS disabled, got %q", loadedConfig)
-	}
+	loadedConfig, err := loadDaemonCliConfig(opts)
+	assert.NilError(t, err)
+	assert.NotNil(t, loadedConfig)
+	assert.Equal(t, loadedConfig.TLS, false)
 }
 
 func TestLoadDaemonCliConfigWithLogLevel(t *testing.T) {
-	c := &daemon.Config{}
-	common := &cliflags.CommonFlags{}
-
-	f, err := ioutil.TempFile("", "docker-config-")
-	if err != nil {
-		t.Fatal(err)
-	}
-	configFile := f.Name()
-	defer os.Remove(configFile)
-
-	f.Write([]byte(`{"log-level": "warn"}`))
-	f.Close()
-
-	flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
-	flags.String([]string{"-log-level"}, "", "")
-	loadedConfig, err := loadDaemonCliConfig(c, flags, common, configFile)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if loadedConfig == nil {
-		t.Fatalf("expected configuration %v, got nil", c)
-	}
-	if loadedConfig.LogLevel != "warn" {
-		t.Fatalf("expected warn log level, got %v", loadedConfig.LogLevel)
-	}
-
-	if logrus.GetLevel() != logrus.WarnLevel {
-		t.Fatalf("expected warn log level, got %v", logrus.GetLevel())
-	}
+	tempFile := tempfile.NewTempFile(t, "config", `{"log-level": "warn"}`)
+	defer tempFile.Remove()
+
+	opts := defaultOptions(tempFile.Name())
+	loadedConfig, err := loadDaemonCliConfig(opts)
+	assert.NilError(t, err)
+	assert.NotNil(t, loadedConfig)
+	assert.Equal(t, loadedConfig.LogLevel, "warn")
+	assert.Equal(t, logrus.GetLevel(), logrus.WarnLevel)
 }
 
 func TestLoadDaemonConfigWithEmbeddedOptions(t *testing.T) {
-	c := &daemon.Config{}
-	common := &cliflags.CommonFlags{}
-
-	flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
-	flags.String([]string{"-tlscacert"}, "", "")
-	flags.String([]string{"-log-driver"}, "", "")
-
-	f, err := ioutil.TempFile("", "docker-config-")
-	if err != nil {
-		t.Fatal(err)
-	}
-	configFile := f.Name()
-	defer os.Remove(configFile)
-
-	f.Write([]byte(`{"tlscacert": "/etc/certs/ca.pem", "log-driver": "syslog"}`))
-	f.Close()
-
-	loadedConfig, err := loadDaemonCliConfig(c, flags, common, configFile)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if loadedConfig == nil {
-		t.Fatal("expected configuration, got nil")
-	}
-	if loadedConfig.CommonTLSOptions.CAFile != "/etc/certs/ca.pem" {
-		t.Fatalf("expected CA file path /etc/certs/ca.pem, got %v", loadedConfig.CommonTLSOptions.CAFile)
-	}
-	if loadedConfig.LogConfig.Type != "syslog" {
-		t.Fatalf("expected LogConfig type syslog, got %v", loadedConfig.LogConfig.Type)
-	}
+	content := `{"tlscacert": "/etc/certs/ca.pem", "log-driver": "syslog"}`
+	tempFile := tempfile.NewTempFile(t, "config", content)
+	defer tempFile.Remove()
+
+	opts := defaultOptions(tempFile.Name())
+	loadedConfig, err := loadDaemonCliConfig(opts)
+	assert.NilError(t, err)
+	assert.NotNil(t, loadedConfig)
+	assert.Equal(t, loadedConfig.CommonTLSOptions.CAFile, "/etc/certs/ca.pem")
+	assert.Equal(t, loadedConfig.LogConfig.Type, "syslog")
 }
 
 func TestLoadDaemonConfigWithRegistryOptions(t *testing.T) {
-	c := &daemon.Config{}
-	common := &cliflags.CommonFlags{}
-	flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
-	c.ServiceOptions.InstallCliFlags(flags, absentFromHelp)
-
-	f, err := ioutil.TempFile("", "docker-config-")
-	if err != nil {
-		t.Fatal(err)
-	}
-	configFile := f.Name()
-	defer os.Remove(configFile)
-
-	f.Write([]byte(`{"registry-mirrors": ["https://mirrors.docker.com"], "insecure-registries": ["https://insecure.docker.com"]}`))
-	f.Close()
-
-	loadedConfig, err := loadDaemonCliConfig(c, flags, common, configFile)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if loadedConfig == nil {
-		t.Fatal("expected configuration, got nil")
-	}
-
-	m := loadedConfig.Mirrors
-	if len(m) != 1 {
-		t.Fatalf("expected 1 mirror, got %d", len(m))
-	}
-
-	r := loadedConfig.InsecureRegistries
-	if len(r) != 1 {
-		t.Fatalf("expected 1 insecure registries, got %d", len(r))
-	}
+	content := `{
+		"registry-mirrors": ["https://mirrors.docker.com"],
+		"insecure-registries": ["https://insecure.docker.com"]
+	}`
+	tempFile := tempfile.NewTempFile(t, "config", content)
+	defer tempFile.Remove()
+
+	opts := defaultOptions(tempFile.Name())
+	loadedConfig, err := loadDaemonCliConfig(opts)
+	assert.NilError(t, err)
+	assert.NotNil(t, loadedConfig)
+
+	assert.Equal(t, len(loadedConfig.Mirrors), 1)
+	assert.Equal(t, len(loadedConfig.InsecureRegistries), 1)
 }

+ 70 - 200
cmd/dockerd/daemon_unix_test.go

@@ -3,240 +3,110 @@
 package main
 
 import (
-	"io/ioutil"
-	"os"
 	"testing"
 
-	cliflags "github.com/docker/docker/cli/flags"
 	"github.com/docker/docker/daemon"
-	"github.com/docker/docker/opts"
-	"github.com/docker/docker/pkg/mflag"
+	"github.com/docker/docker/pkg/testutil/assert"
+	"github.com/docker/docker/pkg/testutil/tempfile"
 )
 
 func TestLoadDaemonCliConfigWithDaemonFlags(t *testing.T) {
-	c := &daemon.Config{}
-	common := &cliflags.CommonFlags{
-		Debug:    true,
-		LogLevel: "info",
-	}
-
-	f, err := ioutil.TempFile("", "docker-config-")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	configFile := f.Name()
-	f.Write([]byte(`{"log-opts": {"max-size": "1k"}}`))
-	f.Close()
-
-	flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
-	flags.String([]string{daemonConfigFileFlag}, "", "")
-	flags.BoolVar(&c.EnableSelinuxSupport, []string{"-selinux-enabled"}, true, "")
-	flags.StringVar(&c.LogConfig.Type, []string{"-log-driver"}, "json-file", "")
-	flags.Var(opts.NewNamedMapOpts("log-opts", c.LogConfig.Config, nil), []string{"-log-opt"}, "")
-	flags.Set(daemonConfigFileFlag, configFile)
-
-	loadedConfig, err := loadDaemonCliConfig(c, flags, common, configFile)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if loadedConfig == nil {
-		t.Fatalf("expected configuration %v, got nil", c)
-	}
-	if !loadedConfig.Debug {
-		t.Fatalf("expected debug mode, got false")
-	}
-	if loadedConfig.LogLevel != "info" {
-		t.Fatalf("expected info log level, got %v", loadedConfig.LogLevel)
-	}
-	if !loadedConfig.EnableSelinuxSupport {
-		t.Fatalf("expected enabled selinux support, got disabled")
-	}
-	if loadedConfig.LogConfig.Type != "json-file" {
-		t.Fatalf("expected LogConfig type json-file, got %v", loadedConfig.LogConfig.Type)
-	}
-	if maxSize := loadedConfig.LogConfig.Config["max-size"]; maxSize != "1k" {
-		t.Fatalf("expected log max-size `1k`, got %s", maxSize)
-	}
+	content := `{"log-opts": {"max-size": "1k"}}`
+	tempFile := tempfile.NewTempFile(t, "config", content)
+	defer tempFile.Remove()
+
+	opts := defaultOptions(tempFile.Name())
+	opts.common.Debug = true
+	opts.common.LogLevel = "info"
+	assert.NilError(t, opts.flags.Set("selinux-enabled", "true"))
+
+	loadedConfig, err := loadDaemonCliConfig(opts)
+	assert.NilError(t, err)
+	assert.NotNil(t, loadedConfig)
+
+	assert.Equal(t, loadedConfig.Debug, true)
+	assert.Equal(t, loadedConfig.LogLevel, "info")
+	assert.Equal(t, loadedConfig.EnableSelinuxSupport, true)
+	assert.Equal(t, loadedConfig.LogConfig.Type, "json-file")
+	assert.Equal(t, loadedConfig.LogConfig.Config["max-size"], "1k")
 }
 
 func TestLoadDaemonConfigWithNetwork(t *testing.T) {
-	c := &daemon.Config{}
-	common := &cliflags.CommonFlags{}
-	flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
-	flags.String([]string{"-bip"}, "", "")
-	flags.String([]string{"-ip"}, "", "")
-
-	f, err := ioutil.TempFile("", "docker-config-")
-	if err != nil {
-		t.Fatal(err)
-	}
+	content := `{"bip": "127.0.0.2", "ip": "127.0.0.1"}`
+	tempFile := tempfile.NewTempFile(t, "config", content)
+	defer tempFile.Remove()
 
-	configFile := f.Name()
-	f.Write([]byte(`{"bip": "127.0.0.2", "ip": "127.0.0.1"}`))
-	f.Close()
+	opts := defaultOptions(tempFile.Name())
+	loadedConfig, err := loadDaemonCliConfig(opts)
+	assert.NilError(t, err)
+	assert.NotNil(t, loadedConfig)
 
-	loadedConfig, err := loadDaemonCliConfig(c, flags, common, configFile)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if loadedConfig == nil {
-		t.Fatalf("expected configuration %v, got nil", c)
-	}
-	if loadedConfig.IP != "127.0.0.2" {
-		t.Fatalf("expected IP 127.0.0.2, got %v", loadedConfig.IP)
-	}
-	if loadedConfig.DefaultIP.String() != "127.0.0.1" {
-		t.Fatalf("expected DefaultIP 127.0.0.1, got %s", loadedConfig.DefaultIP)
-	}
+	assert.Equal(t, loadedConfig.IP, "127.0.0.2")
+	assert.Equal(t, loadedConfig.DefaultIP.String(), "127.0.0.1")
 }
 
 func TestLoadDaemonConfigWithMapOptions(t *testing.T) {
-	c := &daemon.Config{}
-	common := &cliflags.CommonFlags{}
-	flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
-
-	flags.Var(opts.NewNamedMapOpts("cluster-store-opts", c.ClusterOpts, nil), []string{"-cluster-store-opt"}, "")
-	flags.Var(opts.NewNamedMapOpts("log-opts", c.LogConfig.Config, nil), []string{"-log-opt"}, "")
-
-	f, err := ioutil.TempFile("", "docker-config-")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	configFile := f.Name()
-	f.Write([]byte(`{
+	content := `{
 		"cluster-store-opts": {"kv.cacertfile": "/var/lib/docker/discovery_certs/ca.pem"},
 		"log-opts": {"tag": "test"}
-}`))
-	f.Close()
+}`
+	tempFile := tempfile.NewTempFile(t, "config", content)
+	defer tempFile.Remove()
 
-	loadedConfig, err := loadDaemonCliConfig(c, flags, common, configFile)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if loadedConfig == nil {
-		t.Fatal("expected configuration, got nil")
-	}
-	if loadedConfig.ClusterOpts == nil {
-		t.Fatal("expected cluster options, got nil")
-	}
+	opts := defaultOptions(tempFile.Name())
+	loadedConfig, err := loadDaemonCliConfig(opts)
+	assert.NilError(t, err)
+	assert.NotNil(t, loadedConfig)
+	assert.NotNil(t, loadedConfig.ClusterOpts)
 
 	expectedPath := "/var/lib/docker/discovery_certs/ca.pem"
-	if caPath := loadedConfig.ClusterOpts["kv.cacertfile"]; caPath != expectedPath {
-		t.Fatalf("expected %s, got %s", expectedPath, caPath)
-	}
-
-	if loadedConfig.LogConfig.Config == nil {
-		t.Fatal("expected log config options, got nil")
-	}
-	if tag := loadedConfig.LogConfig.Config["tag"]; tag != "test" {
-		t.Fatalf("expected log tag `test`, got %s", tag)
-	}
+	assert.Equal(t, loadedConfig.ClusterOpts["kv.cacertfile"], expectedPath)
+	assert.NotNil(t, loadedConfig.LogConfig.Config)
+	assert.Equal(t, loadedConfig.LogConfig.Config["tag"], "test")
 }
 
 func TestLoadDaemonConfigWithTrueDefaultValues(t *testing.T) {
-	c := &daemon.Config{}
-	common := &cliflags.CommonFlags{}
-	flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
-	flags.BoolVar(&c.EnableUserlandProxy, []string{"-userland-proxy"}, true, "")
-
-	f, err := ioutil.TempFile("", "docker-config-")
-	if err != nil {
-		t.Fatal(err)
-	}
+	content := `{ "userland-proxy": false }`
+	tempFile := tempfile.NewTempFile(t, "config", content)
+	defer tempFile.Remove()
 
-	if err := flags.ParseFlags([]string{}, false); err != nil {
-		t.Fatal(err)
-	}
+	opts := defaultOptions(tempFile.Name())
+	loadedConfig, err := loadDaemonCliConfig(opts)
+	assert.NilError(t, err)
+	assert.NotNil(t, loadedConfig)
+	assert.NotNil(t, loadedConfig.ClusterOpts)
 
-	configFile := f.Name()
-	f.Write([]byte(`{
-		"userland-proxy": false
-}`))
-	f.Close()
-
-	loadedConfig, err := loadDaemonCliConfig(c, flags, common, configFile)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if loadedConfig == nil {
-		t.Fatal("expected configuration, got nil")
-	}
-
-	if loadedConfig.EnableUserlandProxy {
-		t.Fatal("expected userland proxy to be disabled, got enabled")
-	}
+	assert.Equal(t, loadedConfig.EnableUserlandProxy, false)
 
 	// make sure reloading doesn't generate configuration
 	// conflicts after normalizing boolean values.
-	err = daemon.ReloadConfiguration(configFile, flags, func(reloadedConfig *daemon.Config) {
-		if reloadedConfig.EnableUserlandProxy {
-			t.Fatal("expected userland proxy to be disabled, got enabled")
-		}
-	})
-	if err != nil {
-		t.Fatal(err)
+	reload := func(reloadedConfig *daemon.Config) {
+		assert.Equal(t, reloadedConfig.EnableUserlandProxy, false)
 	}
+	assert.NilError(t, daemon.ReloadConfiguration(opts.configFile, opts.flags, reload))
 }
 
 func TestLoadDaemonConfigWithTrueDefaultValuesLeaveDefaults(t *testing.T) {
-	c := &daemon.Config{}
-	common := &cliflags.CommonFlags{}
-	flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
-	flags.BoolVar(&c.EnableUserlandProxy, []string{"-userland-proxy"}, true, "")
-
-	f, err := ioutil.TempFile("", "docker-config-")
-	if err != nil {
-		t.Fatal(err)
-	}
+	tempFile := tempfile.NewTempFile(t, "config", `{}`)
+	defer tempFile.Remove()
 
-	if err := flags.ParseFlags([]string{}, false); err != nil {
-		t.Fatal(err)
-	}
+	opts := defaultOptions(tempFile.Name())
+	loadedConfig, err := loadDaemonCliConfig(opts)
+	assert.NilError(t, err)
+	assert.NotNil(t, loadedConfig)
+	assert.NotNil(t, loadedConfig.ClusterOpts)
 
-	configFile := f.Name()
-	f.Write([]byte(`{}`))
-	f.Close()
-
-	loadedConfig, err := loadDaemonCliConfig(c, flags, common, configFile)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if loadedConfig == nil {
-		t.Fatal("expected configuration, got nil")
-	}
-
-	if !loadedConfig.EnableUserlandProxy {
-		t.Fatal("expected userland proxy to be enabled, got disabled")
-	}
+	assert.Equal(t, loadedConfig.EnableUserlandProxy, true)
 }
 
 func TestLoadDaemonConfigWithLegacyRegistryOptions(t *testing.T) {
-	c := &daemon.Config{}
-	common := &cliflags.CommonFlags{}
-	flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
-	c.ServiceOptions.InstallCliFlags(flags, absentFromHelp)
-
-	f, err := ioutil.TempFile("", "docker-config-")
-	if err != nil {
-		t.Fatal(err)
-	}
-	configFile := f.Name()
-	defer os.Remove(configFile)
-
-	f.Write([]byte(`{"disable-legacy-registry": true}`))
-	f.Close()
-
-	loadedConfig, err := loadDaemonCliConfig(c, flags, common, configFile)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if loadedConfig == nil {
-		t.Fatal("expected configuration, got nil")
-	}
-
-	if !loadedConfig.V2Only {
-		t.Fatal("expected disable-legacy-registry to be true, got false")
-	}
+	content := `{"disable-legacy-registry": true}`
+	tempFile := tempfile.NewTempFile(t, "config", content)
+	defer tempFile.Remove()
+
+	opts := defaultOptions(tempFile.Name())
+	loadedConfig, err := loadDaemonCliConfig(opts)
+	assert.NilError(t, err)
+	assert.NotNil(t, loadedConfig)
+	assert.Equal(t, loadedConfig.V2Only, true)
 }

+ 64 - 43
cmd/dockerd/docker.go

@@ -5,72 +5,76 @@ import (
 	"os"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/docker/cli"
+	cliflags "github.com/docker/docker/cli/flags"
+	"github.com/docker/docker/daemon"
 	"github.com/docker/docker/dockerversion"
-	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/reexec"
 	"github.com/docker/docker/pkg/term"
 	"github.com/docker/docker/utils"
+	"github.com/spf13/cobra"
+	"github.com/spf13/pflag"
 )
 
-var (
-	daemonCli = NewDaemonCli()
-	flHelp    = flag.Bool([]string{"h", "-help"}, false, "Print usage")
-	flVersion = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit")
-)
+type daemonOptions struct {
+	version      bool
+	configFile   string
+	daemonConfig *daemon.Config
+	common       *cliflags.CommonOptions
+	flags        *pflag.FlagSet
+}
 
-func main() {
-	if reexec.Init() {
-		return
+func newDaemonCommand() *cobra.Command {
+	opts := daemonOptions{
+		daemonConfig: daemon.NewConfig(),
+		common:       cliflags.NewCommonOptions(),
 	}
 
-	// Set terminal emulation based on platform as required.
-	_, stdout, stderr := term.StdStreams()
-
-	logrus.SetOutput(stderr)
-
-	flag.Merge(flag.CommandLine, daemonCli.commonFlags.FlagSet)
-
-	flag.Usage = func() {
-		fmt.Fprint(stdout, "Usage: dockerd [OPTIONS]\n\n")
-		fmt.Fprint(stdout, "A self-sufficient runtime for containers.\n\nOptions:\n")
-
-		flag.CommandLine.SetOutput(stdout)
-		flag.PrintDefaults()
-	}
-	flag.CommandLine.ShortUsage = func() {
-		fmt.Fprint(stderr, "\nUsage:\tdockerd [OPTIONS]\n")
+	cmd := &cobra.Command{
+		Use:           "dockerd [OPTIONS]",
+		Short:         "A self-sufficient runtime for containers.",
+		SilenceUsage:  true,
+		SilenceErrors: true,
+		Args:          cli.NoArgs,
+		RunE: func(cmd *cobra.Command, args []string) error {
+			opts.flags = cmd.Flags()
+			return runDaemon(opts)
+		},
 	}
+	cli.SetupRootCommand(cmd)
 
-	if err := flag.CommandLine.ParseFlags(os.Args[1:], false); err != nil {
-		os.Exit(1)
-	}
+	flags := cmd.Flags()
+	flags.BoolVarP(&opts.version, "version", "v", false, "Print version information and quit")
+	flags.StringVar(&opts.configFile, flagDaemonConfigFile, defaultDaemonConfigFile, "Daemon configuration file")
+	opts.common.InstallFlags(flags)
+	opts.daemonConfig.InstallFlags(flags)
+	installServiceFlags(flags)
+
+	return cmd
+}
 
-	if *flVersion {
+func runDaemon(opts daemonOptions) error {
+	if opts.version {
 		showVersion()
-		return
+		return nil
 	}
 
-	if *flHelp {
-		// if global flag --help is present, regardless of what other options and commands there are,
-		// just print the usage.
-		flag.Usage()
-		return
-	}
+	daemonCli := NewDaemonCli()
 
 	// On Windows, this may be launching as a service or with an option to
 	// register the service.
-	stop, err := initService()
+	stop, err := initService(daemonCli)
 	if err != nil {
 		logrus.Fatal(err)
 	}
 
-	if !stop {
-		err = daemonCli.start()
-		notifyShutdown(err)
-		if err != nil {
-			logrus.Fatal(err)
-		}
+	if stop {
+		return nil
 	}
+
+	err = daemonCli.start(opts)
+	notifyShutdown(err)
+	return err
 }
 
 func showVersion() {
@@ -80,3 +84,20 @@ func showVersion() {
 		fmt.Printf("Docker version %s, build %s\n", dockerversion.Version, dockerversion.GitCommit)
 	}
 }
+
+func main() {
+	if reexec.Init() {
+		return
+	}
+
+	// Set terminal emulation based on platform as required.
+	_, stdout, stderr := term.StdStreams()
+	logrus.SetOutput(stderr)
+
+	cmd := newDaemonCommand()
+	cmd.SetOutput(stdout)
+	if err := cmd.Execute(); err != nil {
+		fmt.Fprintf(stderr, "%s\n", err)
+		os.Exit(1)
+	}
+}

+ 8 - 1
cmd/dockerd/service_unsupported.go

@@ -2,6 +2,13 @@
 
 package main
 
-func initService() (bool, error) {
+import (
+	"github.com/spf13/pflag"
+)
+
+func initService(daemonCli *DaemonCli) (bool, error) {
 	return false, nil
 }
+
+func installServiceFlags(flags *pflag.FlagSet) {
+}

+ 22 - 13
cmd/dockerd/service_windows.go

@@ -11,7 +11,7 @@ import (
 	"syscall"
 
 	"github.com/Sirupsen/logrus"
-	flag "github.com/docker/docker/pkg/mflag"
+	"github.com/spf13/pflag"
 	"golang.org/x/sys/windows"
 	"golang.org/x/sys/windows/svc"
 	"golang.org/x/sys/windows/svc/debug"
@@ -20,10 +20,10 @@ import (
 )
 
 var (
-	flServiceName       = flag.String([]string{"-service-name"}, "docker", "Set the Windows service name")
-	flRegisterService   = flag.Bool([]string{"-register-service"}, false, "Register the service and exit")
-	flUnregisterService = flag.Bool([]string{"-unregister-service"}, false, "Unregister the service and exit")
-	flRunService        = flag.Bool([]string{"-run-service"}, false, "")
+	flServiceName       *string
+	flRegisterService   *bool
+	flUnregisterService *bool
+	flRunService        *bool
 
 	setStdHandle = syscall.NewLazyDLL("kernel32.dll").NewProc("SetStdHandle")
 	oldStderr    syscall.Handle
@@ -44,9 +44,17 @@ const (
 	eventExtraOffset = 10 // Add this to any event to get a string that supports extended data
 )
 
+func installServiceFlags(flags *pflag.FlagSet) {
+	flServiceName = flags.String("service-name", "docker", "Set the Windows service name")
+	flRegisterService = flags.Bool("register-service", false, "Register the service and exit")
+	flUnregisterService = flags.Bool("unregister-service", false, "Unregister the service and exit")
+	flRunService = flags.Bool("run-service", false, "")
+}
+
 type handler struct {
-	tosvc   chan bool
-	fromsvc chan error
+	tosvc     chan bool
+	fromsvc   chan error
+	daemonCli *DaemonCli
 }
 
 type etwHook struct {
@@ -203,7 +211,7 @@ func unregisterService() error {
 	return nil
 }
 
-func initService() (bool, error) {
+func initService(daemonCli *DaemonCli) (bool, error) {
 	if *flUnregisterService {
 		if *flRegisterService {
 			return true, errors.New("--register-service and --unregister-service cannot be used together")
@@ -225,8 +233,9 @@ func initService() (bool, error) {
 	}
 
 	h := &handler{
-		tosvc:   make(chan bool),
-		fromsvc: make(chan error),
+		tosvc:     make(chan bool),
+		fromsvc:   make(chan error),
+		daemonCli: daemonCli,
 	}
 
 	var log *eventlog.Log
@@ -261,7 +270,7 @@ func initService() (bool, error) {
 
 func (h *handler) started() error {
 	// This must be delayed until daemonCli initializes Config.Root
-	err := initPanicFile(filepath.Join(daemonCli.Config.Root, "panic.log"))
+	err := initPanicFile(filepath.Join(h.daemonCli.Config.Root, "panic.log"))
 	if err != nil {
 		return err
 	}
@@ -298,12 +307,12 @@ Loop:
 		case c := <-r:
 			switch c.Cmd {
 			case svc.Cmd(windows.SERVICE_CONTROL_PARAMCHANGE):
-				daemonCli.reloadConfig()
+				h.daemonCli.reloadConfig()
 			case svc.Interrogate:
 				s <- c.CurrentStatus
 			case svc.Stop, svc.Shutdown:
 				s <- svc.Status{State: svc.StopPending, Accepts: 0}
-				daemonCli.stop()
+				h.daemonCli.stop()
 			}
 		}
 	}

+ 53 - 45
daemon/config.go

@@ -6,15 +6,16 @@ import (
 	"fmt"
 	"io"
 	"io/ioutil"
+	"runtime"
 	"strings"
 	"sync"
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/pkg/discovery"
-	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/registry"
 	"github.com/imdario/mergo"
+	"github.com/spf13/pflag"
 )
 
 const (
@@ -145,39 +146,37 @@ type CommonConfig struct {
 	valuesSet  map[string]interface{}
 }
 
-// InstallCommonFlags adds command-line options to the top-level flag parser for
-// the current process.
-// Subsequent calls to `flag.Parse` will populate config with values parsed
-// from the command-line.
-func (config *Config) InstallCommonFlags(cmd *flag.FlagSet, usageFn func(string) string) {
+// InstallCommonFlags adds flags to the pflag.FlagSet to configure the daemon
+func (config *Config) InstallCommonFlags(flags *pflag.FlagSet) {
 	var maxConcurrentDownloads, maxConcurrentUploads int
 
-	config.ServiceOptions.InstallCliFlags(cmd, usageFn)
-
-	cmd.Var(opts.NewNamedListOptsRef("storage-opts", &config.GraphOptions, nil), []string{"-storage-opt"}, usageFn("Storage driver options"))
-	cmd.Var(opts.NewNamedListOptsRef("authorization-plugins", &config.AuthorizationPlugins, nil), []string{"-authorization-plugin"}, usageFn("Authorization plugins to load"))
-	cmd.Var(opts.NewNamedListOptsRef("exec-opts", &config.ExecOptions, nil), []string{"-exec-opt"}, usageFn("Runtime execution options"))
-	cmd.StringVar(&config.Pidfile, []string{"p", "-pidfile"}, defaultPidFile, usageFn("Path to use for daemon PID file"))
-	cmd.StringVar(&config.Root, []string{"g", "-graph"}, defaultGraph, usageFn("Root of the Docker runtime"))
-	cmd.BoolVar(&config.AutoRestart, []string{"#r", "#-restart"}, true, usageFn("--restart on the daemon has been deprecated in favor of --restart policies on docker run"))
-	cmd.StringVar(&config.GraphDriver, []string{"s", "-storage-driver"}, "", usageFn("Storage driver to use"))
-	cmd.IntVar(&config.Mtu, []string{"#mtu", "-mtu"}, 0, usageFn("Set the containers network MTU"))
-	cmd.BoolVar(&config.RawLogs, []string{"-raw-logs"}, false, usageFn("Full timestamps without ANSI coloring"))
+	config.ServiceOptions.InstallCliFlags(flags)
+
+	flags.Var(opts.NewNamedListOptsRef("storage-opts", &config.GraphOptions, nil), "storage-opt", "Storage driver options")
+	flags.Var(opts.NewNamedListOptsRef("authorization-plugins", &config.AuthorizationPlugins, nil), "authorization-plugin", "Authorization plugins to load")
+	flags.Var(opts.NewNamedListOptsRef("exec-opts", &config.ExecOptions, nil), "exec-opt", "Runtime execution options")
+	flags.StringVarP(&config.Pidfile, "pidfile", "p", defaultPidFile, "Path to use for daemon PID file")
+	flags.StringVarP(&config.Root, "graph", "g", defaultGraph, "Root of the Docker runtime")
+	flags.BoolVarP(&config.AutoRestart, "restart", "r", true, "--restart on the daemon has been deprecated in favor of --restart policies on docker run")
+	flags.MarkDeprecated("restart", "Please use a restart policy on docker run")
+	flags.StringVarP(&config.GraphDriver, "storage-driver", "s", "", "Storage driver to use")
+	flags.IntVar(&config.Mtu, "mtu", 0, "Set the containers network MTU")
+	flags.BoolVar(&config.RawLogs, "raw-logs", false, "Full timestamps without ANSI coloring")
 	// FIXME: why the inconsistency between "hosts" and "sockets"?
-	cmd.Var(opts.NewListOptsRef(&config.DNS, opts.ValidateIPAddress), []string{"#dns", "-dns"}, usageFn("DNS server to use"))
-	cmd.Var(opts.NewNamedListOptsRef("dns-opts", &config.DNSOptions, nil), []string{"-dns-opt"}, usageFn("DNS options to use"))
-	cmd.Var(opts.NewListOptsRef(&config.DNSSearch, opts.ValidateDNSSearch), []string{"-dns-search"}, usageFn("DNS search domains to use"))
-	cmd.Var(opts.NewNamedListOptsRef("labels", &config.Labels, opts.ValidateLabel), []string{"-label"}, usageFn("Set key=value labels to the daemon"))
-	cmd.StringVar(&config.LogConfig.Type, []string{"-log-driver"}, "json-file", usageFn("Default driver for container logs"))
-	cmd.Var(opts.NewNamedMapOpts("log-opts", config.LogConfig.Config, nil), []string{"-log-opt"}, usageFn("Default log driver options for containers"))
-	cmd.StringVar(&config.ClusterAdvertise, []string{"-cluster-advertise"}, "", usageFn("Address or interface name to advertise"))
-	cmd.StringVar(&config.ClusterStore, []string{"-cluster-store"}, "", usageFn("URL of the distributed storage backend"))
-	cmd.Var(opts.NewNamedMapOpts("cluster-store-opts", config.ClusterOpts, nil), []string{"-cluster-store-opt"}, usageFn("Set cluster store options"))
-	cmd.StringVar(&config.CorsHeaders, []string{"-api-cors-header"}, "", usageFn("Set CORS headers in the remote API"))
-	cmd.IntVar(&maxConcurrentDownloads, []string{"-max-concurrent-downloads"}, defaultMaxConcurrentDownloads, usageFn("Set the max concurrent downloads for each pull"))
-	cmd.IntVar(&maxConcurrentUploads, []string{"-max-concurrent-uploads"}, defaultMaxConcurrentUploads, usageFn("Set the max concurrent uploads for each push"))
-
-	cmd.StringVar(&config.SwarmDefaultAdvertiseAddr, []string{"-swarm-default-advertise-addr"}, "", usageFn("Set default address or interface for swarm advertised address"))
+	flags.Var(opts.NewListOptsRef(&config.DNS, opts.ValidateIPAddress), "dns", "DNS server to use")
+	flags.Var(opts.NewNamedListOptsRef("dns-opts", &config.DNSOptions, nil), "dns-opt", "DNS options to use")
+	flags.Var(opts.NewListOptsRef(&config.DNSSearch, opts.ValidateDNSSearch), "dns-search", "DNS search domains to use")
+	flags.Var(opts.NewNamedListOptsRef("labels", &config.Labels, opts.ValidateLabel), "label", "Set key=value labels to the daemon")
+	flags.StringVar(&config.LogConfig.Type, "log-driver", "json-file", "Default driver for container logs")
+	flags.Var(opts.NewNamedMapOpts("log-opts", config.LogConfig.Config, nil), "log-opt", "Default log driver options for containers")
+	flags.StringVar(&config.ClusterAdvertise, "cluster-advertise", "", "Address or interface name to advertise")
+	flags.StringVar(&config.ClusterStore, "cluster-store", "", "URL of the distributed storage backend")
+	flags.Var(opts.NewNamedMapOpts("cluster-store-opts", config.ClusterOpts, nil), "cluster-store-opt", "Set cluster store options")
+	flags.StringVar(&config.CorsHeaders, "api-cors-header", "", "Set CORS headers in the remote API")
+	flags.IntVar(&maxConcurrentDownloads, "max-concurrent-downloads", defaultMaxConcurrentDownloads, "Set the max concurrent downloads for each pull")
+	flags.IntVar(&maxConcurrentUploads, "max-concurrent-uploads", defaultMaxConcurrentUploads, "Set the max concurrent uploads for each push")
+
+	flags.StringVar(&config.SwarmDefaultAdvertiseAddr, "swarm-default-advertise-addr", "", "Set default address or interface for swarm advertised address")
 
 	config.MaxConcurrentDownloads = &maxConcurrentDownloads
 	config.MaxConcurrentUploads = &maxConcurrentUploads
@@ -193,6 +192,18 @@ func (config *Config) IsValueSet(name string) bool {
 	return ok
 }
 
+// NewConfig returns a new fully initialized Config struct
+func NewConfig() *Config {
+	config := Config{}
+	config.LogConfig.Config = make(map[string]string)
+	config.ClusterOpts = make(map[string]string)
+
+	if runtime.GOOS != "linux" {
+		config.V2Only = true
+	}
+	return &config
+}
+
 func parseClusterAdvertiseSettings(clusterStore, clusterAdvertise string) (string, error) {
 	if clusterAdvertise == "" {
 		return "", errDiscoveryDisabled
@@ -209,7 +220,7 @@ func parseClusterAdvertiseSettings(clusterStore, clusterAdvertise string) (strin
 }
 
 // ReloadConfiguration reads the configuration in the host and reloads the daemon and server.
-func ReloadConfiguration(configFile string, flags *flag.FlagSet, reload func(*Config)) error {
+func ReloadConfiguration(configFile string, flags *pflag.FlagSet, reload func(*Config)) error {
 	logrus.Infof("Got signal to reload configuration, reloading from: %s", configFile)
 	newConfig, err := getConflictFreeConfiguration(configFile, flags)
 	if err != nil {
@@ -234,7 +245,7 @@ type boolValue interface {
 // loads the file configuration in an isolated structure,
 // and merges the configuration provided from flags on top
 // if there are no conflicts.
-func MergeDaemonConfigurations(flagsConfig *Config, flags *flag.FlagSet, configFile string) (*Config, error) {
+func MergeDaemonConfigurations(flagsConfig *Config, flags *pflag.FlagSet, configFile string) (*Config, error) {
 	fileConfig, err := getConflictFreeConfiguration(configFile, flags)
 	if err != nil {
 		return nil, err
@@ -261,7 +272,7 @@ func MergeDaemonConfigurations(flagsConfig *Config, flags *flag.FlagSet, configF
 // getConflictFreeConfiguration loads the configuration from a JSON file.
 // It compares that configuration with the one provided by the flags,
 // and returns an error if there are conflicts.
-func getConflictFreeConfiguration(configFile string, flags *flag.FlagSet) (*Config, error) {
+func getConflictFreeConfiguration(configFile string, flags *pflag.FlagSet) (*Config, error) {
 	b, err := ioutil.ReadFile(configFile)
 	if err != nil {
 		return nil, err
@@ -289,7 +300,7 @@ func getConflictFreeConfiguration(configFile string, flags *flag.FlagSet) (*Conf
 		// TODO: Rewrite configuration logic to avoid same issue with other nullable values, like numbers.
 		namedOptions := make(map[string]interface{})
 		for key, value := range configSet {
-			f := flags.Lookup("-" + key)
+			f := flags.Lookup(key)
 			if f == nil { // ignore named flags that don't match
 				namedOptions[key] = value
 				continue
@@ -301,7 +312,7 @@ func getConflictFreeConfiguration(configFile string, flags *flag.FlagSet) (*Conf
 		}
 		if len(namedOptions) > 0 {
 			// set also default for mergeVal flags that are boolValue at the same time.
-			flags.VisitAll(func(f *flag.Flag) {
+			flags.VisitAll(func(f *pflag.Flag) {
 				if opt, named := f.Value.(opts.NamedOption); named {
 					v, set := namedOptions[opt.Name()]
 					_, boolean := f.Value.(boolValue)
@@ -339,12 +350,11 @@ func configValuesSet(config map[string]interface{}) map[string]interface{} {
 // findConfigurationConflicts iterates over the provided flags searching for
 // duplicated configurations and unknown keys. It returns an error with all the conflicts if
 // it finds any.
-func findConfigurationConflicts(config map[string]interface{}, flags *flag.FlagSet) error {
+func findConfigurationConflicts(config map[string]interface{}, flags *pflag.FlagSet) error {
 	// 1. Search keys from the file that we don't recognize as flags.
 	unknownKeys := make(map[string]interface{})
 	for key, value := range config {
-		flagName := "-" + key
-		if flag := flags.Lookup(flagName); flag == nil {
+		if flag := flags.Lookup(key); flag == nil {
 			unknownKeys[key] = value
 		}
 	}
@@ -352,7 +362,7 @@ func findConfigurationConflicts(config map[string]interface{}, flags *flag.FlagS
 	// 2. Discard values that implement NamedOption.
 	// Their configuration name differs from their flag name, like `labels` and `label`.
 	if len(unknownKeys) > 0 {
-		unknownNamedConflicts := func(f *flag.Flag) {
+		unknownNamedConflicts := func(f *pflag.Flag) {
 			if namedOption, ok := f.Value.(opts.NamedOption); ok {
 				if _, valid := unknownKeys[namedOption.Name()]; valid {
 					delete(unknownKeys, namedOption.Name())
@@ -376,17 +386,15 @@ func findConfigurationConflicts(config map[string]interface{}, flags *flag.FlagS
 	}
 
 	// 3. Search keys that are present as a flag and as a file option.
-	duplicatedConflicts := func(f *flag.Flag) {
+	duplicatedConflicts := func(f *pflag.Flag) {
 		// search option name in the json configuration payload if the value is a named option
 		if namedOption, ok := f.Value.(opts.NamedOption); ok {
 			if optsValue, ok := config[namedOption.Name()]; ok {
 				conflicts = append(conflicts, printConflict(namedOption.Name(), f.Value.String(), optsValue))
 			}
 		} else {
-			// search flag name in the json configuration payload without trailing dashes
-			for _, name := range f.Names {
-				name = strings.TrimLeft(name, "-")
-
+			// search flag name in the json configuration payload
+			for _, name := range []string{f.Name, f.Shorthand} {
 				if value, ok := config[name]; ok {
 					conflicts = append(conflicts, printConflict(name, f.Value.String(), value))
 					break

+ 4 - 2
daemon/config_experimental.go

@@ -2,7 +2,9 @@
 
 package daemon
 
-import flag "github.com/docker/docker/pkg/mflag"
+import (
+	"github.com/spf13/pflag"
+)
 
-func (config *Config) attachExperimentalFlags(cmd *flag.FlagSet, usageFn func(string) string) {
+func (config *Config) attachExperimentalFlags(cmd *pflag.FlagSet) {
 }

+ 4 - 6
daemon/config_solaris.go

@@ -1,7 +1,7 @@
 package daemon
 
 import (
-	flag "github.com/docker/docker/pkg/mflag"
+	"github.com/spf13/pflag"
 )
 
 var (
@@ -28,14 +28,12 @@ type bridgeConfig struct {
 
 // InstallFlags adds command-line options to the top-level flag parser for
 // the current process.
-// Subsequent calls to `flag.Parse` will populate config with values parsed
-// from the command-line.
-func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) string) {
+func (config *Config) InstallFlags(flags *pflag.FlagSet) {
 	// First handle install flags which are consistent cross-platform
-	config.InstallCommonFlags(cmd, usageFn)
+	config.InstallCommonFlags(flags)
 
 	// Then platform-specific install flags
-	config.attachExperimentalFlags(cmd, usageFn)
+	config.attachExperimentalFlags(flags)
 }
 
 // GetExecRoot returns the user configured Exec-root

+ 4 - 2
daemon/config_stub.go

@@ -2,7 +2,9 @@
 
 package daemon
 
-import flag "github.com/docker/docker/pkg/mflag"
+import (
+	"github.com/spf13/pflag"
+)
 
-func (config *Config) attachExperimentalFlags(cmd *flag.FlagSet, usageFn func(string) string) {
+func (config *Config) attachExperimentalFlags(cmd *pflag.FlagSet) {
 }

+ 22 - 40
daemon/config_test.go

@@ -7,7 +7,8 @@ import (
 	"testing"
 
 	"github.com/docker/docker/opts"
-	"github.com/docker/docker/pkg/mflag"
+	"github.com/docker/docker/pkg/testutil/assert"
+	"github.com/spf13/pflag"
 )
 
 func TestDaemonConfigurationMerge(t *testing.T) {
@@ -87,42 +88,26 @@ func TestParseClusterAdvertiseSettings(t *testing.T) {
 
 func TestFindConfigurationConflicts(t *testing.T) {
 	config := map[string]interface{}{"authorization-plugins": "foobar"}
-	flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
+	flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
 
-	flags.String([]string{"-authorization-plugins"}, "", "")
-	if err := flags.Set("-authorization-plugins", "asdf"); err != nil {
-		t.Fatal(err)
-	}
+	flags.String("authorization-plugins", "", "")
+	assert.NilError(t, flags.Set("authorization-plugins", "asdf"))
 
-	err := findConfigurationConflicts(config, flags)
-	if err == nil {
-		t.Fatal("expected error, got nil")
-	}
-	if !strings.Contains(err.Error(), "authorization-plugins: (from flag: asdf, from file: foobar)") {
-		t.Fatalf("expected authorization-plugins conflict, got %v", err)
-	}
+	assert.Error(t,
+		findConfigurationConflicts(config, flags),
+		"authorization-plugins: (from flag: asdf, from file: foobar)")
 }
 
 func TestFindConfigurationConflictsWithNamedOptions(t *testing.T) {
 	config := map[string]interface{}{"hosts": []string{"qwer"}}
-	flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
+	flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
 
 	var hosts []string
-	flags.Var(opts.NewNamedListOptsRef("hosts", &hosts, opts.ValidateHost), []string{"H", "-host"}, "Daemon socket(s) to connect to")
-	if err := flags.Set("-host", "tcp://127.0.0.1:4444"); err != nil {
-		t.Fatal(err)
-	}
-	if err := flags.Set("H", "unix:///var/run/docker.sock"); err != nil {
-		t.Fatal(err)
-	}
+	flags.VarP(opts.NewNamedListOptsRef("hosts", &hosts, opts.ValidateHost), "host", "H", "Daemon socket(s) to connect to")
+	assert.NilError(t, flags.Set("host", "tcp://127.0.0.1:4444"))
+	assert.NilError(t, flags.Set("host", "unix:///var/run/docker.sock"))
 
-	err := findConfigurationConflicts(config, flags)
-	if err == nil {
-		t.Fatal("expected error, got nil")
-	}
-	if !strings.Contains(err.Error(), "hosts") {
-		t.Fatalf("expected hosts conflict, got %v", err)
-	}
+	assert.Error(t, findConfigurationConflicts(config, flags), "hosts")
 }
 
 func TestDaemonConfigurationMergeConflicts(t *testing.T) {
@@ -135,8 +120,8 @@ func TestDaemonConfigurationMergeConflicts(t *testing.T) {
 	f.Write([]byte(`{"debug": true}`))
 	f.Close()
 
-	flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
-	flags.Bool([]string{"debug"}, false, "")
+	flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
+	flags.Bool("debug", false, "")
 	flags.Set("debug", "false")
 
 	_, err = MergeDaemonConfigurations(&Config{}, flags, configFile)
@@ -158,8 +143,8 @@ func TestDaemonConfigurationMergeConflictsWithInnerStructs(t *testing.T) {
 	f.Write([]byte(`{"tlscacert": "/etc/certificates/ca.pem"}`))
 	f.Close()
 
-	flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
-	flags.String([]string{"tlscacert"}, "", "")
+	flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
+	flags.String("tlscacert", "", "")
 	flags.Set("tlscacert", "~/.docker/ca.pem")
 
 	_, err = MergeDaemonConfigurations(&Config{}, flags, configFile)
@@ -173,9 +158,9 @@ func TestDaemonConfigurationMergeConflictsWithInnerStructs(t *testing.T) {
 
 func TestFindConfigurationConflictsWithUnknownKeys(t *testing.T) {
 	config := map[string]interface{}{"tls-verify": "true"}
-	flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
+	flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
 
-	flags.Bool([]string{"-tlsverify"}, false, "")
+	flags.Bool("tlsverify", false, "")
 	err := findConfigurationConflicts(config, flags)
 	if err == nil {
 		t.Fatal("expected error, got nil")
@@ -188,18 +173,15 @@ func TestFindConfigurationConflictsWithUnknownKeys(t *testing.T) {
 func TestFindConfigurationConflictsWithMergedValues(t *testing.T) {
 	var hosts []string
 	config := map[string]interface{}{"hosts": "tcp://127.0.0.1:2345"}
-	base := mflag.NewFlagSet("base", mflag.ContinueOnError)
-	base.Var(opts.NewNamedListOptsRef("hosts", &hosts, nil), []string{"H", "-host"}, "")
-
-	flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
-	mflag.Merge(flags, base)
+	flags := pflag.NewFlagSet("base", pflag.ContinueOnError)
+	flags.VarP(opts.NewNamedListOptsRef("hosts", &hosts, nil), "host", "H", "")
 
 	err := findConfigurationConflicts(config, flags)
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	flags.Set("-host", "unix:///var/run/docker.sock")
+	flags.Set("host", "unix:///var/run/docker.sock")
 	err = findConfigurationConflicts(config, flags)
 	if err == nil {
 		t.Fatal("expected error, got nil")

+ 34 - 35
daemon/config_unix.go

@@ -7,10 +7,10 @@ import (
 	"net"
 
 	"github.com/docker/docker/opts"
-	flag "github.com/docker/docker/pkg/mflag"
 	runconfigopts "github.com/docker/docker/runconfig/opts"
 	"github.com/docker/engine-api/types"
-	"github.com/docker/go-units"
+	units "github.com/docker/go-units"
+	"github.com/spf13/pflag"
 )
 
 var (
@@ -56,44 +56,43 @@ type bridgeConfig struct {
 	InterContainerCommunication bool   `json:"icc,omitempty"`
 }
 
-// InstallFlags adds command-line options to the top-level flag parser for
-// the current process.
-// Subsequent calls to `flag.Parse` will populate config with values parsed
-// from the command-line.
-func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) string) {
+// InstallFlags adds flags to the pflag.FlagSet to configure the daemon
+func (config *Config) InstallFlags(flags *pflag.FlagSet) {
 	// First handle install flags which are consistent cross-platform
-	config.InstallCommonFlags(cmd, usageFn)
+	config.InstallCommonFlags(flags)
 
-	// Then platform-specific install flags
-	cmd.BoolVar(&config.EnableSelinuxSupport, []string{"-selinux-enabled"}, false, usageFn("Enable selinux support"))
-	cmd.StringVar(&config.SocketGroup, []string{"G", "-group"}, "docker", usageFn("Group for the unix socket"))
 	config.Ulimits = make(map[string]*units.Ulimit)
-	cmd.Var(runconfigopts.NewUlimitOpt(&config.Ulimits), []string{"-default-ulimit"}, usageFn("Default ulimits for containers"))
-	cmd.BoolVar(&config.bridgeConfig.EnableIPTables, []string{"#iptables", "-iptables"}, true, usageFn("Enable addition of iptables rules"))
-	cmd.BoolVar(&config.bridgeConfig.EnableIPForward, []string{"#ip-forward", "-ip-forward"}, true, usageFn("Enable net.ipv4.ip_forward"))
-	cmd.BoolVar(&config.bridgeConfig.EnableIPMasq, []string{"-ip-masq"}, true, usageFn("Enable IP masquerading"))
-	cmd.BoolVar(&config.bridgeConfig.EnableIPv6, []string{"-ipv6"}, false, usageFn("Enable IPv6 networking"))
-	cmd.StringVar(&config.ExecRoot, []string{"-exec-root"}, defaultExecRoot, usageFn("Root directory for execution state files"))
-	cmd.StringVar(&config.bridgeConfig.IP, []string{"#bip", "-bip"}, "", usageFn("Specify network bridge IP"))
-	cmd.StringVar(&config.bridgeConfig.Iface, []string{"b", "-bridge"}, "", usageFn("Attach containers to a network bridge"))
-	cmd.StringVar(&config.bridgeConfig.FixedCIDR, []string{"-fixed-cidr"}, "", usageFn("IPv4 subnet for fixed IPs"))
-	cmd.StringVar(&config.bridgeConfig.FixedCIDRv6, []string{"-fixed-cidr-v6"}, "", usageFn("IPv6 subnet for fixed IPs"))
-	cmd.Var(opts.NewIPOpt(&config.bridgeConfig.DefaultGatewayIPv4, ""), []string{"-default-gateway"}, usageFn("Container default gateway IPv4 address"))
-	cmd.Var(opts.NewIPOpt(&config.bridgeConfig.DefaultGatewayIPv6, ""), []string{"-default-gateway-v6"}, usageFn("Container default gateway IPv6 address"))
-	cmd.BoolVar(&config.bridgeConfig.InterContainerCommunication, []string{"#icc", "-icc"}, true, usageFn("Enable inter-container communication"))
-	cmd.Var(opts.NewIPOpt(&config.bridgeConfig.DefaultIP, "0.0.0.0"), []string{"#ip", "-ip"}, usageFn("Default IP when binding container ports"))
-	cmd.BoolVar(&config.bridgeConfig.EnableUserlandProxy, []string{"-userland-proxy"}, true, usageFn("Use userland proxy for loopback traffic"))
-	cmd.BoolVar(&config.EnableCors, []string{"#api-enable-cors", "#-api-enable-cors"}, false, usageFn("Enable CORS headers in the remote API, this is deprecated by --api-cors-header"))
-	cmd.StringVar(&config.CgroupParent, []string{"-cgroup-parent"}, "", usageFn("Set parent cgroup for all containers"))
-	cmd.StringVar(&config.RemappedRoot, []string{"-userns-remap"}, "", usageFn("User/Group setting for user namespaces"))
-	cmd.StringVar(&config.ContainerdAddr, []string{"-containerd"}, "", usageFn("Path to containerd socket"))
-	cmd.BoolVar(&config.LiveRestoreEnabled, []string{"-live-restore"}, false, usageFn("Enable live restore of docker when containers are still running"))
 	config.Runtimes = make(map[string]types.Runtime)
-	cmd.Var(runconfigopts.NewNamedRuntimeOpt("runtimes", &config.Runtimes, stockRuntimeName), []string{"-add-runtime"}, usageFn("Register an additional OCI compatible runtime"))
-	cmd.StringVar(&config.DefaultRuntime, []string{"-default-runtime"}, stockRuntimeName, usageFn("Default OCI runtime for containers"))
-	cmd.IntVar(&config.OOMScoreAdjust, []string{"-oom-score-adjust"}, -500, usageFn("Set the oom_score_adj for the daemon"))
 
-	config.attachExperimentalFlags(cmd, usageFn)
+	// Then platform-specific install flags
+	flags.BoolVar(&config.EnableSelinuxSupport, "selinux-enabled", false, "Enable selinux support")
+	flags.StringVarP(&config.SocketGroup, "group", "G", "docker", "Group for the unix socket")
+	flags.Var(runconfigopts.NewUlimitOpt(&config.Ulimits), "default-ulimit", "Default ulimits for containers")
+	flags.BoolVar(&config.bridgeConfig.EnableIPTables, "iptables", true, "Enable addition of iptables rules")
+	flags.BoolVar(&config.bridgeConfig.EnableIPForward, "ip-forward", true, "Enable net.ipv4.ip_forward")
+	flags.BoolVar(&config.bridgeConfig.EnableIPMasq, "ip-masq", true, "Enable IP masquerading")
+	flags.BoolVar(&config.bridgeConfig.EnableIPv6, "ipv6", false, "Enable IPv6 networking")
+	flags.StringVar(&config.ExecRoot, "exec-root", defaultExecRoot, "Root directory for execution state files")
+	flags.StringVar(&config.bridgeConfig.IP, "bip", "", "Specify network bridge IP")
+	flags.StringVarP(&config.bridgeConfig.Iface, "bridge", "b", "", "Attach containers to a network bridge")
+	flags.StringVar(&config.bridgeConfig.FixedCIDR, "fixed-cidr", "", "IPv4 subnet for fixed IPs")
+	flags.StringVar(&config.bridgeConfig.FixedCIDRv6, "fixed-cidr-v6", "", "IPv6 subnet for fixed IPs")
+	flags.Var(opts.NewIPOpt(&config.bridgeConfig.DefaultGatewayIPv4, ""), "default-gateway", "Container default gateway IPv4 address")
+	flags.Var(opts.NewIPOpt(&config.bridgeConfig.DefaultGatewayIPv6, ""), "default-gateway-v6", "Container default gateway IPv6 address")
+	flags.BoolVar(&config.bridgeConfig.InterContainerCommunication, "icc", true, "Enable inter-container communication")
+	flags.Var(opts.NewIPOpt(&config.bridgeConfig.DefaultIP, "0.0.0.0"), "ip", "Default IP when binding container ports")
+	flags.BoolVar(&config.bridgeConfig.EnableUserlandProxy, "userland-proxy", true, "Use userland proxy for loopback traffic")
+	flags.BoolVar(&config.EnableCors, "api-enable-cors", false, "Enable CORS headers in the remote API, this is deprecated by --api-cors-header")
+	flags.MarkDeprecated("api-enable-cors", "Please use --api-cors-header")
+	flags.StringVar(&config.CgroupParent, "cgroup-parent", "", "Set parent cgroup for all containers")
+	flags.StringVar(&config.RemappedRoot, "userns-remap", "", "User/Group setting for user namespaces")
+	flags.StringVar(&config.ContainerdAddr, "containerd", "", "Path to containerd socket")
+	flags.BoolVar(&config.LiveRestoreEnabled, "live-restore", false, "Enable live restore of docker when containers are still running")
+	flags.Var(runconfigopts.NewNamedRuntimeOpt("runtimes", &config.Runtimes, stockRuntimeName), "add-runtime", "Register an additional OCI compatible runtime")
+	flags.StringVar(&config.DefaultRuntime, "default-runtime", stockRuntimeName, "Default OCI runtime for containers")
+	flags.IntVar(&config.OOMScoreAdjust, "oom-score-adjust", -500, "Set the oom_score_adj for the daemon")
+
+	config.attachExperimentalFlags(flags)
 }
 
 // GetRuntime returns the runtime path and arguments for a given

+ 7 - 10
daemon/config_windows.go

@@ -3,8 +3,8 @@ package daemon
 import (
 	"os"
 
-	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/engine-api/types"
+	"github.com/spf13/pflag"
 )
 
 var (
@@ -28,18 +28,15 @@ type Config struct {
 	// for the Windows daemon.)
 }
 
-// InstallFlags adds command-line options to the top-level flag parser for
-// the current process.
-// Subsequent calls to `flag.Parse` will populate config with values parsed
-// from the command-line.
-func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) string) {
+// InstallFlags adds flags to the pflag.FlagSet to configure the daemon
+func (config *Config) InstallFlags(flags *pflag.FlagSet) {
 	// First handle install flags which are consistent cross-platform
-	config.InstallCommonFlags(cmd, usageFn)
+	config.InstallCommonFlags(flags)
 
 	// Then platform-specific install flags.
-	cmd.StringVar(&config.bridgeConfig.FixedCIDR, []string{"-fixed-cidr"}, "", usageFn("IPv4 subnet for fixed IPs"))
-	cmd.StringVar(&config.bridgeConfig.Iface, []string{"b", "-bridge"}, "", "Attach containers to a virtual switch")
-	cmd.StringVar(&config.SocketGroup, []string{"G", "-group"}, "", usageFn("Users or groups that can access the named pipe"))
+	flags.StringVar(&config.bridgeConfig.FixedCIDR, "fixed-cidr", "", "IPv4 subnet for fixed IPs")
+	flags.StringVarP(&config.bridgeConfig.Iface, "bridge", "b", "", "Attach containers to a virtual switch")
+	flags.StringVarP(&config.SocketGroup, "group", "G", "", "Users or groups that can access the named pipe")
 }
 
 // GetRuntime returns the runtime path and arguments for a given

+ 1 - 1
hack/vendor.sh

@@ -162,7 +162,7 @@ clone git github.com/matttproud/golang_protobuf_extensions fc2b8d3a73c4867e51861
 clone git github.com/pkg/errors 01fa4104b9c248c8945d14d9f128454d5b28d595
 
 # cli
-clone git github.com/spf13/cobra 75205f23b3ea70dc7ae5e900d074e010c23c37e9 https://github.com/dnephin/cobra.git
+clone git github.com/spf13/cobra v1.4.1 https://github.com/dnephin/cobra.git
 clone git github.com/spf13/pflag cb88ea77998c3f024757528e3305022ab50b43be
 clone git github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
 clone git github.com/flynn-archive/go-shlex 3f9db97f856818214da2e1057f8ad84803971cff

+ 6 - 6
integration-cli/docker_cli_build_test.go

@@ -1555,12 +1555,12 @@ func (s *DockerSuite) TestBuildWithInaccessibleFilesInContext(c *check.C) {
 			c.Fatalf("failed to chmod file to 700: %s", err)
 		}
 
-		buildCmd := exec.Command("su", "unprivilegeduser", "-c", fmt.Sprintf("%s build -t %s .", dockerBinary, name))
-		buildCmd.Dir = ctx.Dir
-		if out, _, err := runCommandWithOutput(buildCmd); err != nil {
-			c.Fatalf("build should have worked: %s %s", err, out)
-		}
-
+		result := icmd.RunCmd(icmd.Cmd{
+			Dir: ctx.Dir,
+			Command: []string{"su", "unprivilegeduser", "-c",
+				fmt.Sprintf("%s build -t %s .", dockerBinary, name)},
+		})
+		result.Assert(c, icmd.Expected{})
 	}
 }
 

+ 4 - 4
integration-cli/docker_cli_cp_test.go

@@ -11,6 +11,7 @@ import (
 	"strings"
 
 	"github.com/docker/docker/pkg/integration/checker"
+	icmd "github.com/docker/docker/pkg/integration/cmd"
 	"github.com/go-check/check"
 )
 
@@ -399,10 +400,9 @@ func (s *DockerSuite) TestCpUnprivilegedUser(c *check.C) {
 
 	c.Assert(os.Chmod(tmpdir, 0777), checker.IsNil)
 
-	path := cpTestName
-
-	_, _, err = runCommandWithOutput(exec.Command("su", "unprivilegeduser", "-c", dockerBinary+" cp "+containerID+":"+path+" "+tmpdir))
-	c.Assert(err, checker.IsNil, check.Commentf("couldn't copy with unprivileged user: %s:%s", containerID, path))
+	result := icmd.RunCommand("su", "unprivilegeduser", "-c",
+		fmt.Sprintf("%s cp %s:%s %s", dockerBinary, containerID, cpTestName, tmpdir))
+	result.Assert(c, icmd.Expected{})
 }
 
 func (s *DockerSuite) TestCpSpecialFiles(c *check.C) {

+ 27 - 41
integration-cli/docker_cli_daemon_test.go

@@ -23,6 +23,7 @@ import (
 	"github.com/docker/docker/pkg/integration/checker"
 	icmd "github.com/docker/docker/pkg/integration/cmd"
 	"github.com/docker/docker/pkg/mount"
+	"github.com/docker/docker/pkg/testutil/tempfile"
 	"github.com/docker/go-units"
 	"github.com/docker/libnetwork/iptables"
 	"github.com/docker/libtrust"
@@ -352,10 +353,8 @@ func (s *DockerDaemonSuite) TestDaemonIptablesCreate(c *check.C) {
 func (s *DockerSuite) TestDaemonIPv6Enabled(c *check.C) {
 	testRequires(c, IPv6)
 
-	if err := setupV6(); err != nil {
-		c.Fatal("Could not set up host for IPv6 tests")
-	}
-
+	setupV6(c)
+	defer teardownV6(c)
 	d := NewDaemon(c)
 
 	if err := d.StartWithBusybox("--ipv6"); err != nil {
@@ -412,11 +411,6 @@ func (s *DockerSuite) TestDaemonIPv6Enabled(c *check.C) {
 	if ip := net.ParseIP(out); ip != nil {
 		c.Fatalf("Container should not have a global IPv6 address: %v", out)
 	}
-
-	if err := teardownV6(); err != nil {
-		c.Fatal("Could not perform teardown for IPv6 tests")
-	}
-
 }
 
 // TestDaemonIPv6FixedCIDR checks that when the daemon is started with --ipv6=true and a fixed CIDR
@@ -424,16 +418,16 @@ func (s *DockerSuite) TestDaemonIPv6Enabled(c *check.C) {
 func (s *DockerDaemonSuite) TestDaemonIPv6FixedCIDR(c *check.C) {
 	// IPv6 setup is messing with local bridge address.
 	testRequires(c, SameHostDaemon)
-	err := setupV6()
-	c.Assert(err, checker.IsNil, check.Commentf("Could not set up host for IPv6 tests"))
+	setupV6(c)
+	defer teardownV6(c)
 
-	err = s.d.StartWithBusybox("--ipv6", "--fixed-cidr-v6='2001:db8:2::/64'", "--default-gateway-v6='2001:db8:2::100'")
+	err := s.d.StartWithBusybox("--ipv6", "--fixed-cidr-v6=2001:db8:2::/64", "--default-gateway-v6=2001:db8:2::100")
 	c.Assert(err, checker.IsNil, check.Commentf("Could not start daemon with busybox: %v", err))
 
 	out, err := s.d.Cmd("run", "-itd", "--name=ipv6test", "busybox:latest")
 	c.Assert(err, checker.IsNil, check.Commentf("Could not run container: %s, %v", out, err))
 
-	out, err = s.d.Cmd("inspect", "--format", "'{{.NetworkSettings.Networks.bridge.GlobalIPv6Address}}'", "ipv6test")
+	out, err = s.d.Cmd("inspect", "--format", "{{.NetworkSettings.Networks.bridge.GlobalIPv6Address}}", "ipv6test")
 	out = strings.Trim(out, " \r\n'")
 
 	c.Assert(err, checker.IsNil, check.Commentf(out))
@@ -441,13 +435,10 @@ func (s *DockerDaemonSuite) TestDaemonIPv6FixedCIDR(c *check.C) {
 	ip := net.ParseIP(out)
 	c.Assert(ip, checker.NotNil, check.Commentf("Container should have a global IPv6 address"))
 
-	out, err = s.d.Cmd("inspect", "--format", "'{{.NetworkSettings.Networks.bridge.IPv6Gateway}}'", "ipv6test")
+	out, err = s.d.Cmd("inspect", "--format", "{{.NetworkSettings.Networks.bridge.IPv6Gateway}}", "ipv6test")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
 	c.Assert(strings.Trim(out, " \r\n'"), checker.Equals, "2001:db8:2::100", check.Commentf("Container should have a global IPv6 gateway"))
-
-	err = teardownV6()
-	c.Assert(err, checker.IsNil, check.Commentf("Could not perform teardown for IPv6 tests"))
 }
 
 // TestDaemonIPv6FixedCIDRAndMac checks that when the daemon is started with ipv6 fixed CIDR
@@ -455,21 +446,18 @@ func (s *DockerDaemonSuite) TestDaemonIPv6FixedCIDR(c *check.C) {
 func (s *DockerDaemonSuite) TestDaemonIPv6FixedCIDRAndMac(c *check.C) {
 	// IPv6 setup is messing with local bridge address.
 	testRequires(c, SameHostDaemon)
-	err := setupV6()
-	c.Assert(err, checker.IsNil)
+	setupV6(c)
+	defer teardownV6(c)
 
-	err = s.d.StartWithBusybox("--ipv6", "--fixed-cidr-v6='2001:db8:1::/64'")
+	err := s.d.StartWithBusybox("--ipv6", "--fixed-cidr-v6=2001:db8:1::/64")
 	c.Assert(err, checker.IsNil)
 
 	out, err := s.d.Cmd("run", "-itd", "--name=ipv6test", "--mac-address", "AA:BB:CC:DD:EE:FF", "busybox")
 	c.Assert(err, checker.IsNil)
 
-	out, err = s.d.Cmd("inspect", "--format", "'{{.NetworkSettings.Networks.bridge.GlobalIPv6Address}}'", "ipv6test")
+	out, err = s.d.Cmd("inspect", "--format", "{{.NetworkSettings.Networks.bridge.GlobalIPv6Address}}", "ipv6test")
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.Trim(out, " \r\n'"), checker.Equals, "2001:db8:1::aabb:ccdd:eeff")
-
-	err = teardownV6()
-	c.Assert(err, checker.IsNil)
 }
 
 func (s *DockerDaemonSuite) TestDaemonLogLevelWrong(c *check.C) {
@@ -1724,13 +1712,15 @@ func (s *DockerDaemonSuite) TestDaemonNoTlsCliTlsVerifyWithEnv(c *check.C) {
 
 }
 
-func setupV6() error {
+func setupV6(c *check.C) {
 	// Hack to get the right IPv6 address on docker0, which has already been created
-	return exec.Command("ip", "addr", "add", "fe80::1/64", "dev", "docker0").Run()
+	result := icmd.RunCommand("ip", "addr", "add", "fe80::1/64", "dev", "docker0")
+	result.Assert(c, icmd.Expected{})
 }
 
-func teardownV6() error {
-	return exec.Command("ip", "addr", "del", "fe80::1/64", "dev", "docker0").Run()
+func teardownV6(c *check.C) {
+	result := icmd.RunCommand("ip", "addr", "del", "fe80::1/64", "dev", "docker0")
+	result.Assert(c, icmd.Expected{})
 }
 
 func (s *DockerDaemonSuite) TestDaemonRestartWithContainerWithRestartPolicyAlways(c *check.C) {
@@ -2362,31 +2352,26 @@ func (s *DockerSuite) TestDaemonDiscoveryBackendConfigReload(c *check.C) {
 	testRequires(c, SameHostDaemon, DaemonIsLinux)
 
 	// daemon config file
-	daemonConfig := `{ "debug" : false }`
-	configFilePath := "test.json"
-
-	configFile, err := os.Create(configFilePath)
-	c.Assert(err, checker.IsNil)
-	fmt.Fprintf(configFile, "%s", daemonConfig)
+	tmpfile := tempfile.NewTempFile(c, "config-test", `{ "debug" : false }`)
+	defer tmpfile.Remove()
 
 	d := NewDaemon(c)
-	err = d.Start(fmt.Sprintf("--config-file=%s", configFilePath))
+	// --log-level needs to be set so that d.Start() doesn't add --debug causing
+	// a conflict with the config
+	err := d.Start("--config-file", tmpfile.Name(), "--log-level=info")
 	c.Assert(err, checker.IsNil)
 	defer d.Stop()
 
 	// daemon config file
-	daemonConfig = `{
+	daemonConfig := `{
 	      "cluster-store": "consul://consuladdr:consulport/some/path",
 	      "cluster-advertise": "192.168.56.100:0",
 	      "debug" : false
 	}`
 
-	configFile.Close()
-	os.Remove(configFilePath)
-
-	configFile, err = os.Create(configFilePath)
+	os.Remove(tmpfile.Name())
+	configFile, err := os.Create(tmpfile.Name())
 	c.Assert(err, checker.IsNil)
-	defer os.Remove(configFilePath)
 	fmt.Fprintf(configFile, "%s", daemonConfig)
 	configFile.Close()
 
@@ -2396,6 +2381,7 @@ func (s *DockerSuite) TestDaemonDiscoveryBackendConfigReload(c *check.C) {
 
 	out, err := d.Cmd("info")
 	c.Assert(err, checker.IsNil)
+
 	c.Assert(out, checker.Contains, fmt.Sprintf("Cluster Store: consul://consuladdr:consulport/some/path"))
 	c.Assert(out, checker.Contains, fmt.Sprintf("Cluster Advertise: 192.168.56.100:0"))
 }

+ 16 - 34
integration-cli/docker_cli_help_test.go

@@ -9,6 +9,7 @@ import (
 
 	"github.com/docker/docker/pkg/homedir"
 	"github.com/docker/docker/pkg/integration/checker"
+	icmd "github.com/docker/docker/pkg/integration/cmd"
 	"github.com/go-check/check"
 )
 
@@ -56,12 +57,7 @@ func (s *DockerSuite) TestHelpTextVerify(c *check.C) {
 		out, _, err := runCommandWithOutput(helpCmd)
 		c.Assert(err, checker.IsNil, check.Commentf(out))
 		lines := strings.Split(out, "\n")
-		foundTooLongLine := false
 		for _, line := range lines {
-			if !foundTooLongLine && len(line) > 80 {
-				c.Logf("Line is too long:\n%s", line)
-				foundTooLongLine = true
-			}
 			// All lines should not end with a space
 			c.Assert(line, checker.Not(checker.HasSuffix), " ", check.Commentf("Line should not end with a space"))
 
@@ -229,14 +225,6 @@ func testCommand(cmd string, newEnvs []string, scanForHome bool, home string) er
 	// Check each line for lots of stuff
 	lines := strings.Split(out, "\n")
 	for _, line := range lines {
-		if len(line) > 107 {
-			return fmt.Errorf("Help for %q is too long:\n%s\n", cmd, line)
-		}
-
-		if scanForHome && strings.Contains(line, `"`+home) {
-			return fmt.Errorf("Help for %q should use ~ instead of %q on:\n%s\n",
-				cmd, home, line)
-		}
 		i := strings.Index(line, "~")
 		if i >= 0 && i != len(line)-1 && line[i+1] != '/' {
 			return fmt.Errorf("Help for %q should not have used ~:\n%s", cmd, line)
@@ -290,10 +278,6 @@ func testCommand(cmd string, newEnvs []string, scanForHome bool, home string) er
 	}
 
 	if _, ok := noShortUsage[cmd]; !ok {
-		// For each command run it w/o any args. It will either return
-		// valid output or print a short-usage
-		var dCmd *exec.Cmd
-
 		// skipNoArgs are ones that we don't want to try w/o
 		// any args. Either because it'll hang the test or
 		// lead to incorrect test result (like false negative).
@@ -305,33 +289,31 @@ func testCommand(cmd string, newEnvs []string, scanForHome bool, home string) er
 			"load":   {},
 		}
 
-		ec := 0
+		var result *icmd.Result
 		if _, ok := skipNoArgs[cmd]; !ok {
-			args = strings.Split(cmd, " ")
-			dCmd = exec.Command(dockerBinary, args...)
-			out, stderr, ec, err = runCommandWithStdoutStderr(dCmd)
+			result = dockerCmdWithResult(strings.Split(cmd, " ")...)
 		}
 
 		// If its ok w/o any args then try again with an arg
-		if ec == 0 {
-			args = strings.Split(cmd+" badArg", " ")
-			dCmd = exec.Command(dockerBinary, args...)
-			out, stderr, ec, err = runCommandWithStdoutStderr(dCmd)
+		if result == nil || result.ExitCode == 0 {
+			result = dockerCmdWithResult(strings.Split(cmd+" badArg", " ")...)
 		}
 
-		if len(out) != 0 || len(stderr) == 0 || ec == 0 || err == nil {
-			return fmt.Errorf("Bad output from %q\nstdout:%q\nstderr:%q\nec:%d\nerr:%q\n", args, out, stderr, ec, err)
-		}
-		// Should have just short usage
-		if !strings.Contains(stderr, "\nUsage:") {
-			return fmt.Errorf("Missing short usage on %q\n:%#v", args, stderr)
+		if err := result.Compare(icmd.Expected{
+			Out:      icmd.None,
+			Err:      "\nUsage:",
+			ExitCode: 1,
+		}); err != nil {
+			return err
 		}
-		// But shouldn't have full usage
+
+		stderr := result.Stderr()
+		// Shouldn't have full usage
 		if strings.Contains(stderr, "--help=false") {
-			return fmt.Errorf("Should not have full usage on %q\n", args)
+			return fmt.Errorf("Should not have full usage on %q:%v", result.Cmd.Args, stderr)
 		}
 		if strings.HasSuffix(stderr, "\n\n") {
-			return fmt.Errorf("Should not have a blank line on %q\n%v", args, stderr)
+			return fmt.Errorf("Should not have a blank line on %q\n%v", result.Cmd.Args, stderr)
 		}
 	}
 

+ 6 - 13
integration-cli/docker_cli_run_test.go

@@ -334,11 +334,8 @@ func (s *DockerSuite) TestUserDefinedNetworkAlias(c *check.C) {
 // Issue 9677.
 func (s *DockerSuite) TestRunWithDaemonFlags(c *check.C) {
 	out, _, err := dockerCmdWithError("--exec-opt", "foo=bar", "run", "-i", "busybox", "true")
-	if err != nil {
-		if !strings.Contains(out, "flag provided but not defined: --exec-opt") { // no daemon (client-only)
-			c.Fatal(err, out)
-		}
-	}
+	c.Assert(err, checker.NotNil)
+	c.Assert(out, checker.Contains, "unknown flag: --exec-opt")
 }
 
 // Regression test for #4979
@@ -2663,15 +2660,11 @@ func (s *DockerSuite) TestRunTLSverify(c *check.C) {
 
 	// Regardless of whether we specify true or false we need to
 	// test to make sure tls is turned on if --tlsverify is specified at all
-	out, code, err := dockerCmdWithError("--tlsverify=false", "ps")
-	if err == nil || code == 0 || !strings.Contains(out, "trying to connect") {
-		c.Fatalf("Should have failed: \net:%v\nout:%v\nerr:%v", code, out, err)
-	}
+	result := dockerCmdWithResult("--tlsverify=false", "ps")
+	result.Assert(c, icmd.Expected{ExitCode: 1, Err: "trying to connect"})
 
-	out, code, err = dockerCmdWithError("--tlsverify=true", "ps")
-	if err == nil || code == 0 || !strings.Contains(out, "cert") {
-		c.Fatalf("Should have failed: \net:%v\nout:%v\nerr:%v", code, out, err)
-	}
+	result = dockerCmdWithResult("--tlsverify=true", "ps")
+	result.Assert(c, icmd.Expected{ExitCode: 1, Err: "cert"})
 }
 
 func (s *DockerSuite) TestRunPortFromDockerRangeInUse(c *check.C) {

+ 10 - 6
man/generate.go

@@ -4,8 +4,10 @@ import (
 	"fmt"
 	"os"
 
-	"github.com/docker/docker/cli/cobraadaptor"
-	cliflags "github.com/docker/docker/cli/flags"
+	"github.com/docker/docker/api/client"
+	"github.com/docker/docker/api/client/command"
+	"github.com/docker/docker/pkg/term"
+	"github.com/spf13/cobra"
 	"github.com/spf13/cobra/doc"
 )
 
@@ -15,10 +17,12 @@ func generateManPages(path string) error {
 		Section: "1",
 		Source:  "Docker Community",
 	}
-	flags := &cliflags.ClientFlags{
-		Common: cliflags.InitCommonFlags(),
-	}
-	cmd := cobraadaptor.NewCobraAdaptor(flags).GetRootCommand()
+
+	stdin, stdout, stderr := term.StdStreams()
+	dockerCli := client.NewDockerCli(stdin, stdout, stderr)
+	cmd := &cobra.Command{Use: "docker"}
+	command.AddCommands(cmd, dockerCli)
+
 	cmd.DisableAutoGenTag = true
 	return doc.GenManTreeFromOpts(cmd, doc.GenManTreeOptions{
 		Header:           header,

+ 5 - 0
opts/ip.go

@@ -40,3 +40,8 @@ func (o *IPOpt) String() string {
 	}
 	return o.IP.String()
 }
+
+// Type returns the type of the option
+func (o *IPOpt) Type() string {
+	return "ip"
+}

+ 0 - 27
pkg/mflag/LICENSE

@@ -1,27 +0,0 @@
-Copyright (c) 2014-2016 The Docker & Go Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
-   * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
-   * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
-   * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 0 - 40
pkg/mflag/README.md

@@ -1,40 +0,0 @@
-Package mflag (aka multiple-flag) implements command-line flag parsing.  
-It's a **hacky** fork of the [official golang package](http://golang.org/pkg/flag/)
-
-It adds:
-
-* both short and long flag version  
-`./example -s red` `./example --string blue`
-
-* multiple names for the same option  
-```
-$>./example -h
-Usage of example:
-  -s, --string="": a simple string
-```
-
-___
-It is very flexible on purpose, so you can do things like:  
-```
-$>./example -h
-Usage of example:
-  -s, -string, --string="": a simple string
-```
-
-Or:  
-```
-$>./example -h
-Usage of example:
-  -oldflag, --newflag="": a simple string
-```
-
-You can also hide some flags from the usage, so if we want only `--newflag`:  
-```
-$>./example -h
-Usage of example:
-  --newflag="": a simple string
-$>./example -oldflag str
-str
-```
-
-See [example.go](example/example.go) for more details.

+ 0 - 37
pkg/mflag/example/example.go

@@ -1,37 +0,0 @@
-package main
-
-import (
-	"fmt"
-
-	flag "github.com/docker/docker/pkg/mflag"
-)
-
-var (
-	i        int
-	str      string
-	b, b2, h bool
-)
-
-func init() {
-	flag.Bool([]string{"#hp", "#-halp"}, false, "display the halp")
-	flag.BoolVar(&b, []string{"b", "#bal", "#bol", "-bal"}, false, "a simple bool")
-	flag.BoolVar(&b, []string{"g", "#gil"}, false, "a simple bool")
-	flag.BoolVar(&b2, []string{"#-bool"}, false, "a simple bool")
-	flag.IntVar(&i, []string{"-integer", "-number"}, -1, "a simple integer")
-	flag.StringVar(&str, []string{"s", "#hidden", "-string"}, "", "a simple string") //-s -hidden and --string will work, but -hidden won't be in the usage
-	flag.BoolVar(&h, []string{"h", "#help", "-help"}, false, "display the help")
-	flag.StringVar(&str, []string{"mode"}, "mode1", "set the mode\nmode1: use the mode1\nmode2: use the mode2\nmode3: use the mode3")
-	flag.Parse()
-}
-func main() {
-	if h {
-		flag.PrintDefaults()
-	} else {
-		fmt.Printf("s/#hidden/-string: %s\n", str)
-		fmt.Printf("b: %t\n", b)
-		fmt.Printf("-bool: %t\n", b2)
-		fmt.Printf("-integer/-number: %d\n", i)
-		fmt.Printf("s/#hidden/-string(via lookup): %s\n", flag.Lookup("s").Value.String())
-		fmt.Printf("ARGS: %v\n", flag.Args())
-	}
-}

+ 0 - 1280
pkg/mflag/flag.go

@@ -1,1280 +0,0 @@
-// Copyright 2014-2016 The Docker & Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//	Package mflag implements command-line flag parsing.
-//
-//	Usage:
-//
-//	Define flags using flag.String(), Bool(), Int(), etc.
-//
-//	This declares an integer flag, -f or --flagname, stored in the pointer ip, with type *int.
-//		import flag "github.com/docker/docker/pkg/mflag"
-//		var ip = flag.Int([]string{"f", "-flagname"}, 1234, "help message for flagname")
-//	If you like, you can bind the flag to a variable using the Var() functions.
-//		var flagvar int
-//		func init() {
-//			// -flaghidden will work, but will be hidden from the usage
-//			flag.IntVar(&flagvar, []string{"f", "#flaghidden", "-flagname"}, 1234, "help message for flagname")
-//		}
-//	Or you can create custom flags that satisfy the Value interface (with
-//	pointer receivers) and couple them to flag parsing by
-//		flag.Var(&flagVal, []string{"name"}, "help message for flagname")
-//	For such flags, the default value is just the initial value of the variable.
-//
-//	You can also add "deprecated" flags, they are still usable, but are not shown
-//	in the usage and will display a warning when you try to use them. `#` before
-//	an option means this option is deprecated, if there is a following option
-//	without `#` ahead, then that's the replacement, if not, it will just be removed:
-//		var ip = flag.Int([]string{"#f", "#flagname", "-flagname"}, 1234, "help message for flagname")
-//	this will display: `Warning: '-f' is deprecated, it will be replaced by '--flagname' soon. See usage.` or
-//	this will display: `Warning: '-flagname' is deprecated, it will be replaced by '--flagname' soon. See usage.`
-//		var ip = flag.Int([]string{"f", "#flagname"}, 1234, "help message for flagname")
-//	will display: `Warning: '-flagname' is deprecated, it will be removed soon. See usage.`
-//	so you can only use `-f`.
-//
-//	You can also group one letter flags, if you declare
-//		var v = flag.Bool([]string{"v", "-verbose"}, false, "help message for verbose")
-//		var s = flag.Bool([]string{"s", "-slow"}, false, "help message for slow")
-//	you will be able to use the -vs or -sv
-//
-//	After all flags are defined, call
-//		flag.Parse()
-//	to parse the command line into the defined flags.
-//
-//	Flags may then be used directly. If you're using the flags themselves,
-//	they are all pointers; if you bind to variables, they're values.
-//		fmt.Println("ip has value ", *ip)
-//		fmt.Println("flagvar has value ", flagvar)
-//
-//	After parsing, the arguments after the flag are available as the
-//	slice flag.Args() or individually as flag.Arg(i).
-//	The arguments are indexed from 0 through flag.NArg()-1.
-//
-//	Command line flag syntax:
-//		-flag
-//		-flag=x
-//		-flag="x"
-//		-flag='x'
-//		-flag x  // non-boolean flags only
-//	One or two minus signs may be used; they are equivalent.
-//	The last form is not permitted for boolean flags because the
-//	meaning of the command
-//		cmd -x *
-//	will change if there is a file called 0, false, etc.  You must
-//	use the -flag=false form to turn off a boolean flag.
-//
-//	Flag parsing stops just before the first non-flag argument
-//	("-" is a non-flag argument) or after the terminator "--".
-//
-//	Integer flags accept 1234, 0664, 0x1234 and may be negative.
-//	Boolean flags may be 1, 0, t, f, true, false, TRUE, FALSE, True, False.
-//	Duration flags accept any input valid for time.ParseDuration.
-//
-//	The default set of command-line flags is controlled by
-//	top-level functions.  The FlagSet type allows one to define
-//	independent sets of flags, such as to implement subcommands
-//	in a command-line interface. The methods of FlagSet are
-//	analogous to the top-level functions for the command-line
-//	flag set.
-
-package mflag
-
-import (
-	"errors"
-	"fmt"
-	"io"
-	"os"
-	"runtime"
-	"sort"
-	"strconv"
-	"strings"
-	"text/tabwriter"
-	"time"
-
-	"github.com/docker/docker/pkg/homedir"
-)
-
-// ErrHelp is the error returned if the flag -help is invoked but no such flag is defined.
-var ErrHelp = errors.New("flag: help requested")
-
-// ErrRetry is the error returned if you need to try letter by letter
-var ErrRetry = errors.New("flag: retry")
-
-// -- bool Value
-type boolValue bool
-
-func newBoolValue(val bool, p *bool) *boolValue {
-	*p = val
-	return (*boolValue)(p)
-}
-
-func (b *boolValue) Set(s string) error {
-	v, err := strconv.ParseBool(s)
-	*b = boolValue(v)
-	return err
-}
-
-func (b *boolValue) Get() interface{} { return bool(*b) }
-
-func (b *boolValue) String() string { return fmt.Sprintf("%v", *b) }
-
-func (b *boolValue) IsBoolFlag() bool { return true }
-
-// optional interface to indicate boolean flags that can be
-// supplied without "=value" text
-type boolFlag interface {
-	Value
-	IsBoolFlag() bool
-}
-
-// -- int Value
-type intValue int
-
-func newIntValue(val int, p *int) *intValue {
-	*p = val
-	return (*intValue)(p)
-}
-
-func (i *intValue) Set(s string) error {
-	v, err := strconv.ParseInt(s, 0, 64)
-	*i = intValue(v)
-	return err
-}
-
-func (i *intValue) Get() interface{} { return int(*i) }
-
-func (i *intValue) String() string { return fmt.Sprintf("%v", *i) }
-
-// -- int64 Value
-type int64Value int64
-
-func newInt64Value(val int64, p *int64) *int64Value {
-	*p = val
-	return (*int64Value)(p)
-}
-
-func (i *int64Value) Set(s string) error {
-	v, err := strconv.ParseInt(s, 0, 64)
-	*i = int64Value(v)
-	return err
-}
-
-func (i *int64Value) Get() interface{} { return int64(*i) }
-
-func (i *int64Value) String() string { return fmt.Sprintf("%v", *i) }
-
-// -- uint Value
-type uintValue uint
-
-func newUintValue(val uint, p *uint) *uintValue {
-	*p = val
-	return (*uintValue)(p)
-}
-
-func (i *uintValue) Set(s string) error {
-	v, err := strconv.ParseUint(s, 0, 64)
-	*i = uintValue(v)
-	return err
-}
-
-func (i *uintValue) Get() interface{} { return uint(*i) }
-
-func (i *uintValue) String() string { return fmt.Sprintf("%v", *i) }
-
-// -- uint64 Value
-type uint64Value uint64
-
-func newUint64Value(val uint64, p *uint64) *uint64Value {
-	*p = val
-	return (*uint64Value)(p)
-}
-
-func (i *uint64Value) Set(s string) error {
-	v, err := strconv.ParseUint(s, 0, 64)
-	*i = uint64Value(v)
-	return err
-}
-
-func (i *uint64Value) Get() interface{} { return uint64(*i) }
-
-func (i *uint64Value) String() string { return fmt.Sprintf("%v", *i) }
-
-// -- uint16 Value
-type uint16Value uint16
-
-func newUint16Value(val uint16, p *uint16) *uint16Value {
-	*p = val
-	return (*uint16Value)(p)
-}
-
-func (i *uint16Value) Set(s string) error {
-	v, err := strconv.ParseUint(s, 0, 16)
-	*i = uint16Value(v)
-	return err
-}
-
-func (i *uint16Value) Get() interface{} { return uint16(*i) }
-
-func (i *uint16Value) String() string { return fmt.Sprintf("%v", *i) }
-
-// -- string Value
-type stringValue string
-
-func newStringValue(val string, p *string) *stringValue {
-	*p = val
-	return (*stringValue)(p)
-}
-
-func (s *stringValue) Set(val string) error {
-	*s = stringValue(val)
-	return nil
-}
-
-func (s *stringValue) Get() interface{} { return string(*s) }
-
-func (s *stringValue) String() string { return fmt.Sprintf("%s", *s) }
-
-// -- float64 Value
-type float64Value float64
-
-func newFloat64Value(val float64, p *float64) *float64Value {
-	*p = val
-	return (*float64Value)(p)
-}
-
-func (f *float64Value) Set(s string) error {
-	v, err := strconv.ParseFloat(s, 64)
-	*f = float64Value(v)
-	return err
-}
-
-func (f *float64Value) Get() interface{} { return float64(*f) }
-
-func (f *float64Value) String() string { return fmt.Sprintf("%v", *f) }
-
-// -- time.Duration Value
-type durationValue time.Duration
-
-func newDurationValue(val time.Duration, p *time.Duration) *durationValue {
-	*p = val
-	return (*durationValue)(p)
-}
-
-func (d *durationValue) Set(s string) error {
-	v, err := time.ParseDuration(s)
-	*d = durationValue(v)
-	return err
-}
-
-func (d *durationValue) Get() interface{} { return time.Duration(*d) }
-
-func (d *durationValue) String() string { return (*time.Duration)(d).String() }
-
-// Value is the interface to the dynamic value stored in a flag.
-// (The default value is represented as a string.)
-//
-// If a Value has an IsBoolFlag() bool method returning true,
-// the command-line parser makes -name equivalent to -name=true
-// rather than using the next command-line argument.
-type Value interface {
-	String() string
-	Set(string) error
-}
-
-// Getter is an interface that allows the contents of a Value to be retrieved.
-// It wraps the Value interface, rather than being part of it, because it
-// appeared after Go 1 and its compatibility rules. All Value types provided
-// by this package satisfy the Getter interface.
-type Getter interface {
-	Value
-	Get() interface{}
-}
-
-// ErrorHandling defines how to handle flag parsing errors.
-type ErrorHandling int
-
-// ErrorHandling strategies available when a flag parsing error occurs
-const (
-	ContinueOnError ErrorHandling = iota
-	ExitOnError
-	PanicOnError
-)
-
-// A FlagSet represents a set of defined flags.  The zero value of a FlagSet
-// has no name and has ContinueOnError error handling.
-type FlagSet struct {
-	// Usage is the function called when an error occurs while parsing flags.
-	// The field is a function (not a method) that may be changed to point to
-	// a custom error handler.
-	Usage      func()
-	ShortUsage func()
-
-	name             string
-	parsed           bool
-	actual           map[string]*Flag
-	formal           map[string]*Flag
-	args             []string // arguments after flags
-	errorHandling    ErrorHandling
-	output           io.Writer // nil means stderr; use Out() accessor
-	nArgRequirements []nArgRequirement
-}
-
-// A Flag represents the state of a flag.
-type Flag struct {
-	Names    []string // name as it appears on command line
-	Usage    string   // help message
-	Value    Value    // value as set
-	DefValue string   // default value (as text); for usage message
-}
-
-type flagSlice []string
-
-func (p flagSlice) Len() int { return len(p) }
-func (p flagSlice) Less(i, j int) bool {
-	pi, pj := strings.TrimPrefix(p[i], "-"), strings.TrimPrefix(p[j], "-")
-	lpi, lpj := strings.ToLower(pi), strings.ToLower(pj)
-	if lpi != lpj {
-		return lpi < lpj
-	}
-	return pi < pj
-}
-func (p flagSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
-
-// sortFlags returns the flags as a slice in lexicographical sorted order.
-func sortFlags(flags map[string]*Flag) []*Flag {
-	var list flagSlice
-
-	// The sorted list is based on the first name, when flag map might use the other names.
-	nameMap := make(map[string]string)
-
-	for n, f := range flags {
-		fName := strings.TrimPrefix(f.Names[0], "#")
-		nameMap[fName] = n
-		if len(f.Names) == 1 {
-			list = append(list, fName)
-			continue
-		}
-
-		found := false
-		for _, name := range list {
-			if name == fName {
-				found = true
-				break
-			}
-		}
-		if !found {
-			list = append(list, fName)
-		}
-	}
-	sort.Sort(list)
-	result := make([]*Flag, len(list))
-	for i, name := range list {
-		result[i] = flags[nameMap[name]]
-	}
-	return result
-}
-
-// Name returns the name of the FlagSet.
-func (fs *FlagSet) Name() string {
-	return fs.name
-}
-
-// Out returns the destination for usage and error messages.
-func (fs *FlagSet) Out() io.Writer {
-	if fs.output == nil {
-		return os.Stderr
-	}
-	return fs.output
-}
-
-// SetOutput sets the destination for usage and error messages.
-// If output is nil, os.Stderr is used.
-func (fs *FlagSet) SetOutput(output io.Writer) {
-	fs.output = output
-}
-
-// VisitAll visits the flags in lexicographical order, calling fn for each.
-// It visits all flags, even those not set.
-func (fs *FlagSet) VisitAll(fn func(*Flag)) {
-	for _, flag := range sortFlags(fs.formal) {
-		fn(flag)
-	}
-}
-
-// VisitAll visits the command-line flags in lexicographical order, calling
-// fn for each.  It visits all flags, even those not set.
-func VisitAll(fn func(*Flag)) {
-	CommandLine.VisitAll(fn)
-}
-
-// Visit visits the flags in lexicographical order, calling fn for each.
-// It visits only those flags that have been set.
-func (fs *FlagSet) Visit(fn func(*Flag)) {
-	for _, flag := range sortFlags(fs.actual) {
-		fn(flag)
-	}
-}
-
-// Visit visits the command-line flags in lexicographical order, calling fn
-// for each.  It visits only those flags that have been set.
-func Visit(fn func(*Flag)) {
-	CommandLine.Visit(fn)
-}
-
-// Lookup returns the Flag structure of the named flag, returning nil if none exists.
-func (fs *FlagSet) Lookup(name string) *Flag {
-	return fs.formal[name]
-}
-
-// IsSet indicates whether the specified flag is set in the given FlagSet
-func (fs *FlagSet) IsSet(name string) bool {
-	return fs.actual[name] != nil
-}
-
-// Lookup returns the Flag structure of the named command-line flag,
-// returning nil if none exists.
-func Lookup(name string) *Flag {
-	return CommandLine.formal[name]
-}
-
-// IsSet indicates whether the specified flag was specified at all on the cmd line.
-func IsSet(name string) bool {
-	return CommandLine.IsSet(name)
-}
-
-type nArgRequirementType int
-
-// Indicator used to pass to BadArgs function
-const (
-	Exact nArgRequirementType = iota
-	Max
-	Min
-)
-
-type nArgRequirement struct {
-	Type nArgRequirementType
-	N    int
-}
-
-// Require adds a requirement about the number of arguments for the FlagSet.
-// The first parameter can be Exact, Max, or Min to respectively specify the exact,
-// the maximum, or the minimal number of arguments required.
-// The actual check is done in FlagSet.CheckArgs().
-func (fs *FlagSet) Require(nArgRequirementType nArgRequirementType, nArg int) {
-	fs.nArgRequirements = append(fs.nArgRequirements, nArgRequirement{nArgRequirementType, nArg})
-}
-
-// CheckArgs uses the requirements set by FlagSet.Require() to validate
-// the number of arguments. If the requirements are not met,
-// an error message string is returned.
-func (fs *FlagSet) CheckArgs() (message string) {
-	for _, req := range fs.nArgRequirements {
-		var arguments string
-		if req.N == 1 {
-			arguments = "1 argument"
-		} else {
-			arguments = fmt.Sprintf("%d arguments", req.N)
-		}
-
-		str := func(kind string) string {
-			return fmt.Sprintf("%q requires %s%s", fs.name, kind, arguments)
-		}
-
-		switch req.Type {
-		case Exact:
-			if fs.NArg() != req.N {
-				return str("")
-			}
-		case Max:
-			if fs.NArg() > req.N {
-				return str("a maximum of ")
-			}
-		case Min:
-			if fs.NArg() < req.N {
-				return str("a minimum of ")
-			}
-		}
-	}
-	return ""
-}
-
-// Set sets the value of the named flag.
-func (fs *FlagSet) Set(name, value string) error {
-	flag, ok := fs.formal[name]
-	if !ok {
-		return fmt.Errorf("no such flag -%v", name)
-	}
-	if err := flag.Value.Set(value); err != nil {
-		return err
-	}
-	if fs.actual == nil {
-		fs.actual = make(map[string]*Flag)
-	}
-	fs.actual[name] = flag
-	return nil
-}
-
-// Set sets the value of the named command-line flag.
-func Set(name, value string) error {
-	return CommandLine.Set(name, value)
-}
-
-// isZeroValue guesses whether the string represents the zero
-// value for a flag. It is not accurate but in practice works OK.
-func isZeroValue(value string) bool {
-	switch value {
-	case "false":
-		return true
-	case "":
-		return true
-	case "0":
-		return true
-	}
-	return false
-}
-
-// PrintDefaults prints, to standard error unless configured
-// otherwise, the default values of all defined flags in the set.
-func (fs *FlagSet) PrintDefaults() {
-	writer := tabwriter.NewWriter(fs.Out(), 20, 1, 3, ' ', 0)
-	home := homedir.Get()
-
-	// Don't substitute when HOME is /
-	if runtime.GOOS != "windows" && home == "/" {
-		home = ""
-	}
-
-	// Add a blank line between cmd description and list of options
-	if fs.FlagCount() > 0 {
-		fmt.Fprintln(writer, "")
-	}
-
-	fs.VisitAll(func(flag *Flag) {
-		names := []string{}
-		for _, name := range flag.Names {
-			if name[0] != '#' {
-				names = append(names, name)
-			}
-		}
-		if len(names) > 0 && len(flag.Usage) > 0 {
-			val := flag.DefValue
-
-			if home != "" && strings.HasPrefix(val, home) {
-				val = homedir.GetShortcutString() + val[len(home):]
-			}
-
-			if isZeroValue(val) {
-				format := "  -%s"
-				fmt.Fprintf(writer, format, strings.Join(names, ", -"))
-			} else {
-				format := "  -%s=%s"
-				fmt.Fprintf(writer, format, strings.Join(names, ", -"), val)
-			}
-			for _, line := range strings.Split(flag.Usage, "\n") {
-				fmt.Fprintln(writer, "\t", line)
-			}
-		}
-	})
-	writer.Flush()
-}
-
-// PrintDefaults prints to standard error the default values of all defined command-line flags.
-func PrintDefaults() {
-	CommandLine.PrintDefaults()
-}
-
-// defaultUsage is the default function to print a usage message.
-func defaultUsage(fs *FlagSet) {
-	if fs.name == "" {
-		fmt.Fprintf(fs.Out(), "Usage:\n")
-	} else {
-		fmt.Fprintf(fs.Out(), "Usage of %s:\n", fs.name)
-	}
-	fs.PrintDefaults()
-}
-
-// NOTE: Usage is not just defaultUsage(CommandLine)
-// because it serves (via godoc flag Usage) as the example
-// for how to write your own usage function.
-
-// Usage prints to standard error a usage message documenting all defined command-line flags.
-// The function is a variable that may be changed to point to a custom function.
-var Usage = func() {
-	fmt.Fprintf(CommandLine.Out(), "Usage of %s:\n", os.Args[0])
-	PrintDefaults()
-}
-
-// ShortUsage prints to standard error a usage message documenting the standard command layout
-// The function is a variable that may be changed to point to a custom function.
-var ShortUsage = func() {
-	fmt.Fprintf(CommandLine.output, "Usage of %s:\n", os.Args[0])
-}
-
-// FlagCount returns the number of flags that have been defined.
-func (fs *FlagSet) FlagCount() int { return len(sortFlags(fs.formal)) }
-
-// FlagCountUndeprecated returns the number of undeprecated flags that have been defined.
-func (fs *FlagSet) FlagCountUndeprecated() int {
-	count := 0
-	for _, flag := range sortFlags(fs.formal) {
-		for _, name := range flag.Names {
-			if name[0] != '#' {
-				count++
-				break
-			}
-		}
-	}
-	return count
-}
-
-// NFlag returns the number of flags that have been set.
-func (fs *FlagSet) NFlag() int { return len(fs.actual) }
-
-// NFlag returns the number of command-line flags that have been set.
-func NFlag() int { return len(CommandLine.actual) }
-
-// Arg returns the i'th argument.  Arg(0) is the first remaining argument
-// after flags have been processed.
-func (fs *FlagSet) Arg(i int) string {
-	if i < 0 || i >= len(fs.args) {
-		return ""
-	}
-	return fs.args[i]
-}
-
-// Arg returns the i'th command-line argument.  Arg(0) is the first remaining argument
-// after flags have been processed.
-func Arg(i int) string {
-	return CommandLine.Arg(i)
-}
-
-// NArg is the number of arguments remaining after flags have been processed.
-func (fs *FlagSet) NArg() int { return len(fs.args) }
-
-// NArg is the number of arguments remaining after flags have been processed.
-func NArg() int { return len(CommandLine.args) }
-
-// Args returns the non-flag arguments.
-func (fs *FlagSet) Args() []string { return fs.args }
-
-// Args returns the non-flag command-line arguments.
-func Args() []string { return CommandLine.args }
-
-// BoolVar defines a bool flag with specified name, default value, and usage string.
-// The argument p points to a bool variable in which to store the value of the flag.
-func (fs *FlagSet) BoolVar(p *bool, names []string, value bool, usage string) {
-	fs.Var(newBoolValue(value, p), names, usage)
-}
-
-// BoolVar defines a bool flag with specified name, default value, and usage string.
-// The argument p points to a bool variable in which to store the value of the flag.
-func BoolVar(p *bool, names []string, value bool, usage string) {
-	CommandLine.Var(newBoolValue(value, p), names, usage)
-}
-
-// Bool defines a bool flag with specified name, default value, and usage string.
-// The return value is the address of a bool variable that stores the value of the flag.
-func (fs *FlagSet) Bool(names []string, value bool, usage string) *bool {
-	p := new(bool)
-	fs.BoolVar(p, names, value, usage)
-	return p
-}
-
-// Bool defines a bool flag with specified name, default value, and usage string.
-// The return value is the address of a bool variable that stores the value of the flag.
-func Bool(names []string, value bool, usage string) *bool {
-	return CommandLine.Bool(names, value, usage)
-}
-
-// IntVar defines an int flag with specified name, default value, and usage string.
-// The argument p points to an int variable in which to store the value of the flag.
-func (fs *FlagSet) IntVar(p *int, names []string, value int, usage string) {
-	fs.Var(newIntValue(value, p), names, usage)
-}
-
-// IntVar defines an int flag with specified name, default value, and usage string.
-// The argument p points to an int variable in which to store the value of the flag.
-func IntVar(p *int, names []string, value int, usage string) {
-	CommandLine.Var(newIntValue(value, p), names, usage)
-}
-
-// Int defines an int flag with specified name, default value, and usage string.
-// The return value is the address of an int variable that stores the value of the flag.
-func (fs *FlagSet) Int(names []string, value int, usage string) *int {
-	p := new(int)
-	fs.IntVar(p, names, value, usage)
-	return p
-}
-
-// Int defines an int flag with specified name, default value, and usage string.
-// The return value is the address of an int variable that stores the value of the flag.
-func Int(names []string, value int, usage string) *int {
-	return CommandLine.Int(names, value, usage)
-}
-
-// Int64Var defines an int64 flag with specified name, default value, and usage string.
-// The argument p points to an int64 variable in which to store the value of the flag.
-func (fs *FlagSet) Int64Var(p *int64, names []string, value int64, usage string) {
-	fs.Var(newInt64Value(value, p), names, usage)
-}
-
-// Int64Var defines an int64 flag with specified name, default value, and usage string.
-// The argument p points to an int64 variable in which to store the value of the flag.
-func Int64Var(p *int64, names []string, value int64, usage string) {
-	CommandLine.Var(newInt64Value(value, p), names, usage)
-}
-
-// Int64 defines an int64 flag with specified name, default value, and usage string.
-// The return value is the address of an int64 variable that stores the value of the flag.
-func (fs *FlagSet) Int64(names []string, value int64, usage string) *int64 {
-	p := new(int64)
-	fs.Int64Var(p, names, value, usage)
-	return p
-}
-
-// Int64 defines an int64 flag with specified name, default value, and usage string.
-// The return value is the address of an int64 variable that stores the value of the flag.
-func Int64(names []string, value int64, usage string) *int64 {
-	return CommandLine.Int64(names, value, usage)
-}
-
-// UintVar defines a uint flag with specified name, default value, and usage string.
-// The argument p points to a uint variable in which to store the value of the flag.
-func (fs *FlagSet) UintVar(p *uint, names []string, value uint, usage string) {
-	fs.Var(newUintValue(value, p), names, usage)
-}
-
-// UintVar defines a uint flag with specified name, default value, and usage string.
-// The argument p points to a uint  variable in which to store the value of the flag.
-func UintVar(p *uint, names []string, value uint, usage string) {
-	CommandLine.Var(newUintValue(value, p), names, usage)
-}
-
-// Uint defines a uint flag with specified name, default value, and usage string.
-// The return value is the address of a uint  variable that stores the value of the flag.
-func (fs *FlagSet) Uint(names []string, value uint, usage string) *uint {
-	p := new(uint)
-	fs.UintVar(p, names, value, usage)
-	return p
-}
-
-// Uint defines a uint flag with specified name, default value, and usage string.
-// The return value is the address of a uint  variable that stores the value of the flag.
-func Uint(names []string, value uint, usage string) *uint {
-	return CommandLine.Uint(names, value, usage)
-}
-
-// Uint64Var defines a uint64 flag with specified name, default value, and usage string.
-// The argument p points to a uint64 variable in which to store the value of the flag.
-func (fs *FlagSet) Uint64Var(p *uint64, names []string, value uint64, usage string) {
-	fs.Var(newUint64Value(value, p), names, usage)
-}
-
-// Uint64Var defines a uint64 flag with specified name, default value, and usage string.
-// The argument p points to a uint64 variable in which to store the value of the flag.
-func Uint64Var(p *uint64, names []string, value uint64, usage string) {
-	CommandLine.Var(newUint64Value(value, p), names, usage)
-}
-
-// Uint64 defines a uint64 flag with specified name, default value, and usage string.
-// The return value is the address of a uint64 variable that stores the value of the flag.
-func (fs *FlagSet) Uint64(names []string, value uint64, usage string) *uint64 {
-	p := new(uint64)
-	fs.Uint64Var(p, names, value, usage)
-	return p
-}
-
-// Uint64 defines a uint64 flag with specified name, default value, and usage string.
-// The return value is the address of a uint64 variable that stores the value of the flag.
-func Uint64(names []string, value uint64, usage string) *uint64 {
-	return CommandLine.Uint64(names, value, usage)
-}
-
-// Uint16Var defines a uint16 flag with specified name, default value, and usage string.
-// The argument p points to a uint16 variable in which to store the value of the flag.
-func (fs *FlagSet) Uint16Var(p *uint16, names []string, value uint16, usage string) {
-	fs.Var(newUint16Value(value, p), names, usage)
-}
-
-// Uint16Var defines a uint16 flag with specified name, default value, and usage string.
-// The argument p points to a uint16 variable in which to store the value of the flag.
-func Uint16Var(p *uint16, names []string, value uint16, usage string) {
-	CommandLine.Var(newUint16Value(value, p), names, usage)
-}
-
-// Uint16 defines a uint16 flag with specified name, default value, and usage string.
-// The return value is the address of a uint16 variable that stores the value of the flag.
-func (fs *FlagSet) Uint16(names []string, value uint16, usage string) *uint16 {
-	p := new(uint16)
-	fs.Uint16Var(p, names, value, usage)
-	return p
-}
-
-// Uint16 defines a uint16 flag with specified name, default value, and usage string.
-// The return value is the address of a uint16 variable that stores the value of the flag.
-func Uint16(names []string, value uint16, usage string) *uint16 {
-	return CommandLine.Uint16(names, value, usage)
-}
-
-// StringVar defines a string flag with specified name, default value, and usage string.
-// The argument p points to a string variable in which to store the value of the flag.
-func (fs *FlagSet) StringVar(p *string, names []string, value string, usage string) {
-	fs.Var(newStringValue(value, p), names, usage)
-}
-
-// StringVar defines a string flag with specified name, default value, and usage string.
-// The argument p points to a string variable in which to store the value of the flag.
-func StringVar(p *string, names []string, value string, usage string) {
-	CommandLine.Var(newStringValue(value, p), names, usage)
-}
-
-// String defines a string flag with specified name, default value, and usage string.
-// The return value is the address of a string variable that stores the value of the flag.
-func (fs *FlagSet) String(names []string, value string, usage string) *string {
-	p := new(string)
-	fs.StringVar(p, names, value, usage)
-	return p
-}
-
-// String defines a string flag with specified name, default value, and usage string.
-// The return value is the address of a string variable that stores the value of the flag.
-func String(names []string, value string, usage string) *string {
-	return CommandLine.String(names, value, usage)
-}
-
-// Float64Var defines a float64 flag with specified name, default value, and usage string.
-// The argument p points to a float64 variable in which to store the value of the flag.
-func (fs *FlagSet) Float64Var(p *float64, names []string, value float64, usage string) {
-	fs.Var(newFloat64Value(value, p), names, usage)
-}
-
-// Float64Var defines a float64 flag with specified name, default value, and usage string.
-// The argument p points to a float64 variable in which to store the value of the flag.
-func Float64Var(p *float64, names []string, value float64, usage string) {
-	CommandLine.Var(newFloat64Value(value, p), names, usage)
-}
-
-// Float64 defines a float64 flag with specified name, default value, and usage string.
-// The return value is the address of a float64 variable that stores the value of the flag.
-func (fs *FlagSet) Float64(names []string, value float64, usage string) *float64 {
-	p := new(float64)
-	fs.Float64Var(p, names, value, usage)
-	return p
-}
-
-// Float64 defines a float64 flag with specified name, default value, and usage string.
-// The return value is the address of a float64 variable that stores the value of the flag.
-func Float64(names []string, value float64, usage string) *float64 {
-	return CommandLine.Float64(names, value, usage)
-}
-
-// DurationVar defines a time.Duration flag with specified name, default value, and usage string.
-// The argument p points to a time.Duration variable in which to store the value of the flag.
-func (fs *FlagSet) DurationVar(p *time.Duration, names []string, value time.Duration, usage string) {
-	fs.Var(newDurationValue(value, p), names, usage)
-}
-
-// DurationVar defines a time.Duration flag with specified name, default value, and usage string.
-// The argument p points to a time.Duration variable in which to store the value of the flag.
-func DurationVar(p *time.Duration, names []string, value time.Duration, usage string) {
-	CommandLine.Var(newDurationValue(value, p), names, usage)
-}
-
-// Duration defines a time.Duration flag with specified name, default value, and usage string.
-// The return value is the address of a time.Duration variable that stores the value of the flag.
-func (fs *FlagSet) Duration(names []string, value time.Duration, usage string) *time.Duration {
-	p := new(time.Duration)
-	fs.DurationVar(p, names, value, usage)
-	return p
-}
-
-// Duration defines a time.Duration flag with specified name, default value, and usage string.
-// The return value is the address of a time.Duration variable that stores the value of the flag.
-func Duration(names []string, value time.Duration, usage string) *time.Duration {
-	return CommandLine.Duration(names, value, usage)
-}
-
-// Var defines a flag with the specified name and usage string. The type and
-// value of the flag are represented by the first argument, of type Value, which
-// typically holds a user-defined implementation of Value. For instance, the
-// caller could create a flag that turns a comma-separated string into a slice
-// of strings by giving the slice the methods of Value; in particular, Set would
-// decompose the comma-separated string into the slice.
-func (fs *FlagSet) Var(value Value, names []string, usage string) {
-	// Remember the default value as a string; it won't change.
-	flag := &Flag{names, usage, value, value.String()}
-	for _, name := range names {
-		name = strings.TrimPrefix(name, "#")
-		_, alreadythere := fs.formal[name]
-		if alreadythere {
-			var msg string
-			if fs.name == "" {
-				msg = fmt.Sprintf("flag redefined: %s", name)
-			} else {
-				msg = fmt.Sprintf("%s flag redefined: %s", fs.name, name)
-			}
-			fmt.Fprintln(fs.Out(), msg)
-			panic(msg) // Happens only if flags are declared with identical names
-		}
-		if fs.formal == nil {
-			fs.formal = make(map[string]*Flag)
-		}
-		fs.formal[name] = flag
-	}
-}
-
-// Var defines a flag with the specified name and usage string. The type and
-// value of the flag are represented by the first argument, of type Value, which
-// typically holds a user-defined implementation of Value. For instance, the
-// caller could create a flag that turns a comma-separated string into a slice
-// of strings by giving the slice the methods of Value; in particular, Set would
-// decompose the comma-separated string into the slice.
-func Var(value Value, names []string, usage string) {
-	CommandLine.Var(value, names, usage)
-}
-
-// failf prints to standard error a formatted error and usage message and
-// returns the error.
-func (fs *FlagSet) failf(format string, a ...interface{}) error {
-	err := fmt.Errorf(format, a...)
-	fmt.Fprintln(fs.Out(), err)
-	if os.Args[0] == fs.name {
-		fmt.Fprintf(fs.Out(), "See '%s --help'.\n", os.Args[0])
-	} else {
-		fmt.Fprintf(fs.Out(), "See '%s %s --help'.\n", os.Args[0], fs.name)
-	}
-	return err
-}
-
-// usage calls the Usage method for the flag set, or the usage function if
-// the flag set is CommandLine.
-func (fs *FlagSet) usage() {
-	if fs == CommandLine {
-		Usage()
-	} else if fs.Usage == nil {
-		defaultUsage(fs)
-	} else {
-		fs.Usage()
-	}
-}
-
-func trimQuotes(str string) string {
-	if len(str) == 0 {
-		return str
-	}
-	type quote struct {
-		start, end byte
-	}
-
-	// All valid quote types.
-	quotes := []quote{
-		// Double quotes
-		{
-			start: '"',
-			end:   '"',
-		},
-
-		// Single quotes
-		{
-			start: '\'',
-			end:   '\'',
-		},
-	}
-
-	for _, quote := range quotes {
-		// Only strip if outermost match.
-		if str[0] == quote.start && str[len(str)-1] == quote.end {
-			str = str[1 : len(str)-1]
-			break
-		}
-	}
-
-	return str
-}
-
-// parseOne parses one flag. It reports whether a flag was seen.
-func (fs *FlagSet) parseOne() (bool, string, error) {
-	if len(fs.args) == 0 {
-		return false, "", nil
-	}
-	s := fs.args[0]
-	if len(s) == 0 || s[0] != '-' || len(s) == 1 {
-		return false, "", nil
-	}
-	if s[1] == '-' && len(s) == 2 { // "--" terminates the flags
-		fs.args = fs.args[1:]
-		return false, "", nil
-	}
-	name := s[1:]
-	if len(name) == 0 || name[0] == '=' {
-		return false, "", fs.failf("bad flag syntax: %s", s)
-	}
-
-	// it's a flag. does it have an argument?
-	fs.args = fs.args[1:]
-	hasValue := false
-	value := ""
-	if i := strings.Index(name, "="); i != -1 {
-		value = trimQuotes(name[i+1:])
-		hasValue = true
-		name = name[:i]
-	}
-
-	m := fs.formal
-	flag, alreadythere := m[name] // BUG
-	if !alreadythere {
-		if name == "-help" || name == "help" || name == "h" { // special case for nice help message.
-			fs.usage()
-			return false, "", ErrHelp
-		}
-		if len(name) > 0 && name[0] == '-' {
-			return false, "", fs.failf("flag provided but not defined: -%s", name)
-		}
-		return false, name, ErrRetry
-	}
-	if fv, ok := flag.Value.(boolFlag); ok && fv.IsBoolFlag() { // special case: doesn't need an arg
-		if hasValue {
-			if err := fv.Set(value); err != nil {
-				return false, "", fs.failf("invalid boolean value %q for  -%s: %v", value, name, err)
-			}
-		} else {
-			fv.Set("true")
-		}
-	} else {
-		// It must have a value, which might be the next argument.
-		if !hasValue && len(fs.args) > 0 {
-			// value is the next arg
-			hasValue = true
-			value, fs.args = fs.args[0], fs.args[1:]
-		}
-		if !hasValue {
-			return false, "", fs.failf("flag needs an argument: -%s", name)
-		}
-		if err := flag.Value.Set(value); err != nil {
-			return false, "", fs.failf("invalid value %q for flag -%s: %v", value, name, err)
-		}
-	}
-	if fs.actual == nil {
-		fs.actual = make(map[string]*Flag)
-	}
-	fs.actual[name] = flag
-	for i, n := range flag.Names {
-		if n == fmt.Sprintf("#%s", name) {
-			replacement := ""
-			for j := i; j < len(flag.Names); j++ {
-				if flag.Names[j][0] != '#' {
-					replacement = flag.Names[j]
-					break
-				}
-			}
-			if replacement != "" {
-				fmt.Fprintf(fs.Out(), "Warning: '-%s' is deprecated, it will be replaced by '-%s' soon. See usage.\n", name, replacement)
-			} else {
-				fmt.Fprintf(fs.Out(), "Warning: '-%s' is deprecated, it will be removed soon. See usage.\n", name)
-			}
-		}
-	}
-	return true, "", nil
-}
-
-// Parse parses flag definitions from the argument list, which should not
-// include the command name.  Must be called after all flags in the FlagSet
-// are defined and before flags are accessed by the program.
-// The return value will be ErrHelp if -help was set but not defined.
-func (fs *FlagSet) Parse(arguments []string) error {
-	fs.parsed = true
-	fs.args = arguments
-	for {
-		seen, name, err := fs.parseOne()
-		if seen {
-			continue
-		}
-		if err == nil {
-			break
-		}
-		if err == ErrRetry {
-			if len(name) > 1 {
-				err = nil
-				for _, letter := range strings.Split(name, "") {
-					fs.args = append([]string{"-" + letter}, fs.args...)
-					seen2, _, err2 := fs.parseOne()
-					if seen2 {
-						continue
-					}
-					if err2 != nil {
-						err = fs.failf("flag provided but not defined: -%s", name)
-						break
-					}
-				}
-				if err == nil {
-					continue
-				}
-			} else {
-				err = fs.failf("flag provided but not defined: -%s", name)
-			}
-		}
-		switch fs.errorHandling {
-		case ContinueOnError:
-			return err
-		case ExitOnError:
-			os.Exit(125)
-		case PanicOnError:
-			panic(err)
-		}
-	}
-	return nil
-}
-
-// ParseFlags is a utility function that adds a help flag if withHelp is true,
-// calls fs.Parse(args) and prints a relevant error message if there are
-// incorrect number of arguments. It returns error only if error handling is
-// set to ContinueOnError and parsing fails. If error handling is set to
-// ExitOnError, it's safe to ignore the return value.
-func (fs *FlagSet) ParseFlags(args []string, withHelp bool) error {
-	var help *bool
-	if withHelp {
-		help = fs.Bool([]string{"#help", "-help"}, false, "Print usage")
-	}
-	if err := fs.Parse(args); err != nil {
-		return err
-	}
-	if help != nil && *help {
-		fs.SetOutput(os.Stdout)
-		fs.Usage()
-		os.Exit(0)
-	}
-	if str := fs.CheckArgs(); str != "" {
-		fs.SetOutput(os.Stderr)
-		fs.ReportError(str, withHelp)
-		fs.ShortUsage()
-		os.Exit(1)
-	}
-	return nil
-}
-
-// ReportError is a utility method that prints a user-friendly message
-// containing the error that occurred during parsing and a suggestion to get help
-func (fs *FlagSet) ReportError(str string, withHelp bool) {
-	if withHelp {
-		if os.Args[0] == fs.Name() {
-			str += ".\nSee '" + os.Args[0] + " --help'"
-		} else {
-			str += ".\nSee '" + os.Args[0] + " " + fs.Name() + " --help'"
-		}
-	}
-	fmt.Fprintf(fs.Out(), "%s: %s.\n", os.Args[0], str)
-}
-
-// Parsed reports whether fs.Parse has been called.
-func (fs *FlagSet) Parsed() bool {
-	return fs.parsed
-}
-
-// Parse parses the command-line flags from os.Args[1:].  Must be called
-// after all flags are defined and before flags are accessed by the program.
-func Parse() {
-	// Ignore errors; CommandLine is set for ExitOnError.
-	CommandLine.Parse(os.Args[1:])
-}
-
-// Parsed returns true if the command-line flags have been parsed.
-func Parsed() bool {
-	return CommandLine.Parsed()
-}
-
-// CommandLine is the default set of command-line flags, parsed from os.Args.
-// The top-level functions such as BoolVar, Arg, and on are wrappers for the
-// methods of CommandLine.
-var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
-
-// NewFlagSet returns a new, empty flag set with the specified name and
-// error handling property.
-func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet {
-	f := &FlagSet{
-		name:          name,
-		errorHandling: errorHandling,
-	}
-	return f
-}
-
-// Init sets the name and error handling property for a flag set.
-// By default, the zero FlagSet uses an empty name and the
-// ContinueOnError error handling policy.
-func (fs *FlagSet) Init(name string, errorHandling ErrorHandling) {
-	fs.name = name
-	fs.errorHandling = errorHandling
-}
-
-type mergeVal struct {
-	Value
-	key  string
-	fset *FlagSet
-}
-
-func (v mergeVal) Set(s string) error {
-	return v.fset.Set(v.key, s)
-}
-
-func (v mergeVal) IsBoolFlag() bool {
-	if b, ok := v.Value.(boolFlag); ok {
-		return b.IsBoolFlag()
-	}
-	return false
-}
-
-// Name returns the name of a mergeVal.
-// If the original value had a name, return the original name,
-// otherwise, return the key asinged to this mergeVal.
-func (v mergeVal) Name() string {
-	type namedValue interface {
-		Name() string
-	}
-	if nVal, ok := v.Value.(namedValue); ok {
-		return nVal.Name()
-	}
-	return v.key
-}
-
-// Merge is a helper function that merges n FlagSets into a single dest FlagSet
-// In case of name collision between the flagsets it will apply
-// the destination FlagSet's errorHandling behavior.
-func Merge(dest *FlagSet, flagsets ...*FlagSet) error {
-	for _, fset := range flagsets {
-		if fset.formal == nil {
-			continue
-		}
-		for k, f := range fset.formal {
-			if _, ok := dest.formal[k]; ok {
-				var err error
-				if fset.name == "" {
-					err = fmt.Errorf("flag redefined: %s", k)
-				} else {
-					err = fmt.Errorf("%s flag redefined: %s", fset.name, k)
-				}
-				fmt.Fprintln(fset.Out(), err.Error())
-				// Happens only if flags are declared with identical names
-				switch dest.errorHandling {
-				case ContinueOnError:
-					return err
-				case ExitOnError:
-					os.Exit(2)
-				case PanicOnError:
-					panic(err)
-				}
-			}
-			newF := *f
-			newF.Value = mergeVal{f.Value, k, fset}
-			if dest.formal == nil {
-				dest.formal = make(map[string]*Flag)
-			}
-			dest.formal[k] = &newF
-		}
-	}
-	return nil
-}
-
-// IsEmpty reports if the FlagSet is actually empty.
-func (fs *FlagSet) IsEmpty() bool {
-	return len(fs.actual) == 0
-}

+ 0 - 527
pkg/mflag/flag_test.go

@@ -1,527 +0,0 @@
-// Copyright 2014-2016 The Docker & Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package mflag
-
-import (
-	"bytes"
-	"fmt"
-	"os"
-	"sort"
-	"strings"
-	"testing"
-	"time"
-)
-
-// ResetForTesting clears all flag state and sets the usage function as directed.
-// After calling ResetForTesting, parse errors in flag handling will not
-// exit the program.
-func ResetForTesting(usage func()) {
-	CommandLine = NewFlagSet(os.Args[0], ContinueOnError)
-	Usage = usage
-}
-func boolString(s string) string {
-	if s == "0" {
-		return "false"
-	}
-	return "true"
-}
-
-func TestEverything(t *testing.T) {
-	ResetForTesting(nil)
-	Bool([]string{"test_bool"}, false, "bool value")
-	Int([]string{"test_int"}, 0, "int value")
-	Int64([]string{"test_int64"}, 0, "int64 value")
-	Uint([]string{"test_uint"}, 0, "uint value")
-	Uint64([]string{"test_uint64"}, 0, "uint64 value")
-	String([]string{"test_string"}, "0", "string value")
-	Float64([]string{"test_float64"}, 0, "float64 value")
-	Duration([]string{"test_duration"}, 0, "time.Duration value")
-
-	m := make(map[string]*Flag)
-	desired := "0"
-	visitor := func(f *Flag) {
-		for _, name := range f.Names {
-			if len(name) > 5 && name[0:5] == "test_" {
-				m[name] = f
-				ok := false
-				switch {
-				case f.Value.String() == desired:
-					ok = true
-				case name == "test_bool" && f.Value.String() == boolString(desired):
-					ok = true
-				case name == "test_duration" && f.Value.String() == desired+"s":
-					ok = true
-				}
-				if !ok {
-					t.Error("Visit: bad value", f.Value.String(), "for", name)
-				}
-			}
-		}
-	}
-	VisitAll(visitor)
-	if len(m) != 8 {
-		t.Error("VisitAll misses some flags")
-		for k, v := range m {
-			t.Log(k, *v)
-		}
-	}
-	m = make(map[string]*Flag)
-	Visit(visitor)
-	if len(m) != 0 {
-		t.Errorf("Visit sees unset flags")
-		for k, v := range m {
-			t.Log(k, *v)
-		}
-	}
-	// Now set all flags
-	Set("test_bool", "true")
-	Set("test_int", "1")
-	Set("test_int64", "1")
-	Set("test_uint", "1")
-	Set("test_uint64", "1")
-	Set("test_string", "1")
-	Set("test_float64", "1")
-	Set("test_duration", "1s")
-	desired = "1"
-	Visit(visitor)
-	if len(m) != 8 {
-		t.Error("Visit fails after set")
-		for k, v := range m {
-			t.Log(k, *v)
-		}
-	}
-	// Now test they're visited in sort order.
-	var flagNames []string
-	Visit(func(f *Flag) {
-		for _, name := range f.Names {
-			flagNames = append(flagNames, name)
-		}
-	})
-	if !sort.StringsAreSorted(flagNames) {
-		t.Errorf("flag names not sorted: %v", flagNames)
-	}
-}
-
-func TestGet(t *testing.T) {
-	ResetForTesting(nil)
-	Bool([]string{"test_bool"}, true, "bool value")
-	Int([]string{"test_int"}, 1, "int value")
-	Int64([]string{"test_int64"}, 2, "int64 value")
-	Uint([]string{"test_uint"}, 3, "uint value")
-	Uint64([]string{"test_uint64"}, 4, "uint64 value")
-	String([]string{"test_string"}, "5", "string value")
-	Float64([]string{"test_float64"}, 6, "float64 value")
-	Duration([]string{"test_duration"}, 7, "time.Duration value")
-
-	visitor := func(f *Flag) {
-		for _, name := range f.Names {
-			if len(name) > 5 && name[0:5] == "test_" {
-				g, ok := f.Value.(Getter)
-				if !ok {
-					t.Errorf("Visit: value does not satisfy Getter: %T", f.Value)
-					return
-				}
-				switch name {
-				case "test_bool":
-					ok = g.Get() == true
-				case "test_int":
-					ok = g.Get() == int(1)
-				case "test_int64":
-					ok = g.Get() == int64(2)
-				case "test_uint":
-					ok = g.Get() == uint(3)
-				case "test_uint64":
-					ok = g.Get() == uint64(4)
-				case "test_string":
-					ok = g.Get() == "5"
-				case "test_float64":
-					ok = g.Get() == float64(6)
-				case "test_duration":
-					ok = g.Get() == time.Duration(7)
-				}
-				if !ok {
-					t.Errorf("Visit: bad value %T(%v) for %s", g.Get(), g.Get(), name)
-				}
-			}
-		}
-	}
-	VisitAll(visitor)
-}
-
-func testParse(f *FlagSet, t *testing.T) {
-	if f.Parsed() {
-		t.Error("f.Parse() = true before Parse")
-	}
-	boolFlag := f.Bool([]string{"bool"}, false, "bool value")
-	bool2Flag := f.Bool([]string{"bool2"}, false, "bool2 value")
-	f.Bool([]string{"bool3"}, false, "bool3 value")
-	bool4Flag := f.Bool([]string{"bool4"}, false, "bool4 value")
-	intFlag := f.Int([]string{"-int"}, 0, "int value")
-	int64Flag := f.Int64([]string{"-int64"}, 0, "int64 value")
-	uintFlag := f.Uint([]string{"uint"}, 0, "uint value")
-	uint64Flag := f.Uint64([]string{"-uint64"}, 0, "uint64 value")
-	stringFlag := f.String([]string{"string"}, "0", "string value")
-	f.String([]string{"string2"}, "0", "string2 value")
-	singleQuoteFlag := f.String([]string{"squote"}, "", "single quoted value")
-	doubleQuoteFlag := f.String([]string{"dquote"}, "", "double quoted value")
-	mixedQuoteFlag := f.String([]string{"mquote"}, "", "mixed quoted value")
-	mixed2QuoteFlag := f.String([]string{"mquote2"}, "", "mixed2 quoted value")
-	nestedQuoteFlag := f.String([]string{"nquote"}, "", "nested quoted value")
-	nested2QuoteFlag := f.String([]string{"nquote2"}, "", "nested2 quoted value")
-	float64Flag := f.Float64([]string{"float64"}, 0, "float64 value")
-	durationFlag := f.Duration([]string{"duration"}, 5*time.Second, "time.Duration value")
-	extra := "one-extra-argument"
-	args := []string{
-		"-bool",
-		"-bool2=true",
-		"-bool4=false",
-		"--int", "22",
-		"--int64", "0x23",
-		"-uint", "24",
-		"--uint64", "25",
-		"-string", "hello",
-		"-squote='single'",
-		`-dquote="double"`,
-		`-mquote='mixed"`,
-		`-mquote2="mixed2'`,
-		`-nquote="'single nested'"`,
-		`-nquote2='"double nested"'`,
-		"-float64", "2718e28",
-		"-duration", "2m",
-		extra,
-	}
-	if err := f.Parse(args); err != nil {
-		t.Fatal(err)
-	}
-	if !f.Parsed() {
-		t.Error("f.Parse() = false after Parse")
-	}
-	if *boolFlag != true {
-		t.Error("bool flag should be true, is ", *boolFlag)
-	}
-	if *bool2Flag != true {
-		t.Error("bool2 flag should be true, is ", *bool2Flag)
-	}
-	if !f.IsSet("bool2") {
-		t.Error("bool2 should be marked as set")
-	}
-	if f.IsSet("bool3") {
-		t.Error("bool3 should not be marked as set")
-	}
-	if !f.IsSet("bool4") {
-		t.Error("bool4 should be marked as set")
-	}
-	if *bool4Flag != false {
-		t.Error("bool4 flag should be false, is ", *bool4Flag)
-	}
-	if *intFlag != 22 {
-		t.Error("int flag should be 22, is ", *intFlag)
-	}
-	if *int64Flag != 0x23 {
-		t.Error("int64 flag should be 0x23, is ", *int64Flag)
-	}
-	if *uintFlag != 24 {
-		t.Error("uint flag should be 24, is ", *uintFlag)
-	}
-	if *uint64Flag != 25 {
-		t.Error("uint64 flag should be 25, is ", *uint64Flag)
-	}
-	if *stringFlag != "hello" {
-		t.Error("string flag should be `hello`, is ", *stringFlag)
-	}
-	if !f.IsSet("string") {
-		t.Error("string flag should be marked as set")
-	}
-	if f.IsSet("string2") {
-		t.Error("string2 flag should not be marked as set")
-	}
-	if *singleQuoteFlag != "single" {
-		t.Error("single quote string flag should be `single`, is ", *singleQuoteFlag)
-	}
-	if *doubleQuoteFlag != "double" {
-		t.Error("double quote string flag should be `double`, is ", *doubleQuoteFlag)
-	}
-	if *mixedQuoteFlag != `'mixed"` {
-		t.Error("mixed quote string flag should be `'mixed\"`, is ", *mixedQuoteFlag)
-	}
-	if *mixed2QuoteFlag != `"mixed2'` {
-		t.Error("mixed2 quote string flag should be `\"mixed2'`, is ", *mixed2QuoteFlag)
-	}
-	if *nestedQuoteFlag != "'single nested'" {
-		t.Error("nested quote string flag should be `'single nested'`, is ", *nestedQuoteFlag)
-	}
-	if *nested2QuoteFlag != `"double nested"` {
-		t.Error("double quote string flag should be `\"double nested\"`, is ", *nested2QuoteFlag)
-	}
-	if *float64Flag != 2718e28 {
-		t.Error("float64 flag should be 2718e28, is ", *float64Flag)
-	}
-	if *durationFlag != 2*time.Minute {
-		t.Error("duration flag should be 2m, is ", *durationFlag)
-	}
-	if len(f.Args()) != 1 {
-		t.Error("expected one argument, got", len(f.Args()))
-	} else if f.Args()[0] != extra {
-		t.Errorf("expected argument %q got %q", extra, f.Args()[0])
-	}
-}
-
-func testPanic(f *FlagSet, t *testing.T) {
-	f.Int([]string{"-int"}, 0, "int value")
-	if f.Parsed() {
-		t.Error("f.Parse() = true before Parse")
-	}
-	args := []string{
-		"-int", "21",
-	}
-	f.Parse(args)
-}
-
-func TestParsePanic(t *testing.T) {
-	ResetForTesting(func() {})
-	testPanic(CommandLine, t)
-}
-
-func TestParse(t *testing.T) {
-	ResetForTesting(func() { t.Error("bad parse") })
-	testParse(CommandLine, t)
-}
-
-func TestFlagSetParse(t *testing.T) {
-	testParse(NewFlagSet("test", ContinueOnError), t)
-}
-
-// Declare a user-defined flag type.
-type flagVar []string
-
-func (f *flagVar) String() string {
-	return fmt.Sprint([]string(*f))
-}
-
-func (f *flagVar) Set(value string) error {
-	*f = append(*f, value)
-	return nil
-}
-
-func TestUserDefined(t *testing.T) {
-	var flags FlagSet
-	flags.Init("test", ContinueOnError)
-	var v flagVar
-	flags.Var(&v, []string{"v"}, "usage")
-	if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil {
-		t.Error(err)
-	}
-	if len(v) != 3 {
-		t.Fatal("expected 3 args; got ", len(v))
-	}
-	expect := "[1 2 3]"
-	if v.String() != expect {
-		t.Errorf("expected value %q got %q", expect, v.String())
-	}
-}
-
-// Declare a user-defined boolean flag type.
-type boolFlagVar struct {
-	count int
-}
-
-func (b *boolFlagVar) String() string {
-	return fmt.Sprintf("%d", b.count)
-}
-
-func (b *boolFlagVar) Set(value string) error {
-	if value == "true" {
-		b.count++
-	}
-	return nil
-}
-
-func (b *boolFlagVar) IsBoolFlag() bool {
-	return b.count < 4
-}
-
-func TestUserDefinedBool(t *testing.T) {
-	var flags FlagSet
-	flags.Init("test", ContinueOnError)
-	var b boolFlagVar
-	var err error
-	flags.Var(&b, []string{"b"}, "usage")
-	if err = flags.Parse([]string{"-b", "-b", "-b", "-b=true", "-b=false", "-b", "barg", "-b"}); err != nil {
-		if b.count < 4 {
-			t.Error(err)
-		}
-	}
-
-	if b.count != 4 {
-		t.Errorf("want: %d; got: %d", 4, b.count)
-	}
-
-	if err == nil {
-		t.Error("expected error; got none")
-	}
-}
-
-func TestSetOutput(t *testing.T) {
-	var flags FlagSet
-	var buf bytes.Buffer
-	flags.SetOutput(&buf)
-	flags.Init("test", ContinueOnError)
-	flags.Parse([]string{"-unknown"})
-	if out := buf.String(); !strings.Contains(out, "-unknown") {
-		t.Logf("expected output mentioning unknown; got %q", out)
-	}
-}
-
-// This tests that one can reset the flags. This still works but not well, and is
-// superseded by FlagSet.
-func TestChangingArgs(t *testing.T) {
-	ResetForTesting(func() { t.Fatal("bad parse") })
-	oldArgs := os.Args
-	defer func() { os.Args = oldArgs }()
-	os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"}
-	before := Bool([]string{"before"}, false, "")
-	if err := CommandLine.Parse(os.Args[1:]); err != nil {
-		t.Fatal(err)
-	}
-	cmd := Arg(0)
-	os.Args = Args()
-	after := Bool([]string{"after"}, false, "")
-	Parse()
-	args := Args()
-
-	if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" {
-		t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args)
-	}
-}
-
-// Test that -help invokes the usage message and returns ErrHelp.
-func TestHelp(t *testing.T) {
-	var helpCalled = false
-	fs := NewFlagSet("help test", ContinueOnError)
-	fs.Usage = func() { helpCalled = true }
-	var flag bool
-	fs.BoolVar(&flag, []string{"flag"}, false, "regular flag")
-	// Regular flag invocation should work
-	err := fs.Parse([]string{"-flag=true"})
-	if err != nil {
-		t.Fatal("expected no error; got ", err)
-	}
-	if !flag {
-		t.Error("flag was not set by -flag")
-	}
-	if helpCalled {
-		t.Error("help called for regular flag")
-		helpCalled = false // reset for next test
-	}
-	// Help flag should work as expected.
-	err = fs.Parse([]string{"-help"})
-	if err == nil {
-		t.Fatal("error expected")
-	}
-	if err != ErrHelp {
-		t.Fatal("expected ErrHelp; got ", err)
-	}
-	if !helpCalled {
-		t.Fatal("help was not called")
-	}
-	// If we define a help flag, that should override.
-	var help bool
-	fs.BoolVar(&help, []string{"help"}, false, "help flag")
-	helpCalled = false
-	err = fs.Parse([]string{"-help"})
-	if err != nil {
-		t.Fatal("expected no error for defined -help; got ", err)
-	}
-	if helpCalled {
-		t.Fatal("help was called; should not have been for defined help flag")
-	}
-}
-
-// Test the flag count functions.
-func TestFlagCounts(t *testing.T) {
-	fs := NewFlagSet("help test", ContinueOnError)
-	var flag bool
-	fs.BoolVar(&flag, []string{"flag1"}, false, "regular flag")
-	fs.BoolVar(&flag, []string{"#deprecated1"}, false, "regular flag")
-	fs.BoolVar(&flag, []string{"f", "flag2"}, false, "regular flag")
-	fs.BoolVar(&flag, []string{"#d", "#deprecated2"}, false, "regular flag")
-	fs.BoolVar(&flag, []string{"flag3"}, false, "regular flag")
-	fs.BoolVar(&flag, []string{"g", "#flag4", "-flag4"}, false, "regular flag")
-
-	if fs.FlagCount() != 6 {
-		t.Fatal("FlagCount wrong. ", fs.FlagCount())
-	}
-	if fs.FlagCountUndeprecated() != 4 {
-		t.Fatal("FlagCountUndeprecated wrong. ", fs.FlagCountUndeprecated())
-	}
-	if fs.NFlag() != 0 {
-		t.Fatal("NFlag wrong. ", fs.NFlag())
-	}
-	err := fs.Parse([]string{"-fd", "-g", "-flag4"})
-	if err != nil {
-		t.Fatal("expected no error for defined -help; got ", err)
-	}
-	if fs.NFlag() != 4 {
-		t.Fatal("NFlag wrong. ", fs.NFlag())
-	}
-}
-
-// Show up bug in sortFlags
-func TestSortFlags(t *testing.T) {
-	fs := NewFlagSet("help TestSortFlags", ContinueOnError)
-
-	var err error
-
-	var b bool
-	fs.BoolVar(&b, []string{"b", "-banana"}, false, "usage")
-
-	err = fs.Parse([]string{"--banana=true"})
-	if err != nil {
-		t.Fatal("expected no error; got ", err)
-	}
-
-	count := 0
-
-	fs.VisitAll(func(flag *Flag) {
-		count++
-		if flag == nil {
-			t.Fatal("VisitAll should not return a nil flag")
-		}
-	})
-	flagcount := fs.FlagCount()
-	if flagcount != count {
-		t.Fatalf("FlagCount (%d) != number (%d) of elements visited", flagcount, count)
-	}
-	// Make sure its idempotent
-	if flagcount != fs.FlagCount() {
-		t.Fatalf("FlagCount (%d) != fs.FlagCount() (%d) of elements visited", flagcount, fs.FlagCount())
-	}
-
-	count = 0
-	fs.Visit(func(flag *Flag) {
-		count++
-		if flag == nil {
-			t.Fatal("Visit should not return a nil flag")
-		}
-	})
-	nflag := fs.NFlag()
-	if nflag != count {
-		t.Fatalf("NFlag (%d) != number (%d) of elements visited", nflag, count)
-	}
-	if nflag != fs.NFlag() {
-		t.Fatalf("NFlag (%d) != fs.NFlag() (%d) of elements visited", nflag, fs.NFlag())
-	}
-}
-
-func TestMergeFlags(t *testing.T) {
-	base := NewFlagSet("base", ContinueOnError)
-	base.String([]string{"f"}, "", "")
-
-	fs := NewFlagSet("test", ContinueOnError)
-	Merge(fs, base)
-	if len(fs.formal) != 1 {
-		t.Fatalf("FlagCount (%d) != number (1) of elements merged", len(fs.formal))
-	}
-}

+ 24 - 9
pkg/testutil/assert/assert.go

@@ -18,7 +18,7 @@ type TestingT interface {
 // they are not equal.
 func Equal(t TestingT, actual, expected interface{}) {
 	if expected != actual {
-		fatal(t, fmt.Sprintf("Expected '%v' (%T) got '%v' (%T)", expected, expected, actual, actual))
+		fatal(t, "Expected '%v' (%T) got '%v' (%T)", expected, expected, actual, actual)
 	}
 }
 
@@ -26,12 +26,12 @@ func Equal(t TestingT, actual, expected interface{}) {
 // the same items.
 func EqualStringSlice(t TestingT, actual, expected []string) {
 	if len(actual) != len(expected) {
-		t.Fatalf("Expected (length %d): %q\nActual (length %d): %q",
+		fatal(t, "Expected (length %d): %q\nActual (length %d): %q",
 			len(expected), expected, len(actual), actual)
 	}
 	for i, item := range actual {
 		if item != expected[i] {
-			t.Fatalf("Slices differ at element %d, expected %q got %q",
+			fatal(t, "Slices differ at element %d, expected %q got %q",
 				i, expected[i], item)
 		}
 	}
@@ -40,7 +40,7 @@ func EqualStringSlice(t TestingT, actual, expected []string) {
 // NilError asserts that the error is nil, otherwise it fails the test.
 func NilError(t TestingT, err error) {
 	if err != nil {
-		fatal(t, fmt.Sprintf("Expected no error, got: %s", err.Error()))
+		fatal(t, "Expected no error, got: %s", err.Error())
 	}
 }
 
@@ -52,7 +52,7 @@ func Error(t TestingT, err error, contains string) {
 	}
 
 	if !strings.Contains(err.Error(), contains) {
-		fatal(t, fmt.Sprintf("Expected error to contain '%s', got '%s'", contains, err.Error()))
+		fatal(t, "Expected error to contain '%s', got '%s'", contains, err.Error())
 	}
 }
 
@@ -60,11 +60,26 @@ func Error(t TestingT, err error, contains string) {
 // test.
 func Contains(t TestingT, actual, contains string) {
 	if !strings.Contains(actual, contains) {
-		fatal(t, fmt.Sprintf("Expected '%s' to contain '%s'", actual, contains))
+		fatal(t, "Expected '%s' to contain '%s'", actual, contains)
 	}
 }
 
-func fatal(t TestingT, msg string) {
-	_, file, line, _ := runtime.Caller(2)
-	t.Fatalf("%s:%d: %s", filepath.Base(file), line, msg)
+// NotNil fails the test if the object is nil
+func NotNil(t TestingT, obj interface{}) {
+	if obj == nil {
+		fatal(t, "Expected non-nil value.")
+	}
+}
+
+func fatal(t TestingT, format string, args ...interface{}) {
+	t.Fatalf(errorSource()+format, args...)
+}
+
+// See testing.decorate()
+func errorSource() string {
+	_, filename, line, ok := runtime.Caller(3)
+	if !ok {
+		return ""
+	}
+	return fmt.Sprintf("%s:%d: ", filepath.Base(filename), line)
 }

+ 36 - 0
pkg/testutil/tempfile/tempfile.go

@@ -0,0 +1,36 @@
+package tempfile
+
+import (
+	"io/ioutil"
+	"os"
+
+	"github.com/docker/docker/pkg/testutil/assert"
+)
+
+// TempFile is a temporary file that can be used with unit tests. TempFile
+// reduces the boilerplate setup required in each test case by handling
+// setup errors.
+type TempFile struct {
+	File *os.File
+}
+
+// NewTempFile returns a new temp file with contents
+func NewTempFile(t assert.TestingT, prefix string, content string) *TempFile {
+	file, err := ioutil.TempFile("", prefix+"-")
+	assert.NilError(t, err)
+
+	_, err = file.Write([]byte(content))
+	assert.NilError(t, err)
+	file.Close()
+	return &TempFile{File: file}
+}
+
+// Name returns the filename
+func (f *TempFile) Name() string {
+	return f.File.Name()
+}
+
+// Remove removes the file
+func (f *TempFile) Remove() {
+	os.Remove(f.Name())
+}

+ 6 - 6
registry/config.go

@@ -8,9 +8,9 @@ import (
 	"strings"
 
 	"github.com/docker/docker/opts"
-	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/reference"
 	registrytypes "github.com/docker/engine-api/types/registry"
+	"github.com/spf13/pflag"
 )
 
 // ServiceOptions holds command line options.
@@ -70,14 +70,14 @@ var lookupIP = net.LookupIP
 
 // InstallCliFlags adds command-line options to the top-level flag parser for
 // the current process.
-func (options *ServiceOptions) InstallCliFlags(cmd *flag.FlagSet, usageFn func(string) string) {
+func (options *ServiceOptions) InstallCliFlags(flags *pflag.FlagSet) {
 	mirrors := opts.NewNamedListOptsRef("registry-mirrors", &options.Mirrors, ValidateMirror)
-	cmd.Var(mirrors, []string{"-registry-mirror"}, usageFn("Preferred Docker registry mirror"))
-
 	insecureRegistries := opts.NewNamedListOptsRef("insecure-registries", &options.InsecureRegistries, ValidateIndexName)
-	cmd.Var(insecureRegistries, []string{"-insecure-registry"}, usageFn("Enable insecure registry communication"))
 
-	options.installCliPlatformFlags(cmd, usageFn)
+	flags.Var(mirrors, "registry-mirror", "Preferred Docker registry mirror")
+	flags.Var(insecureRegistries, "insecure-registry", "Enable insecure registry communication")
+
+	options.installCliPlatformFlags(flags)
 }
 
 // newServiceConfig returns a new instance of ServiceConfig

+ 3 - 3
registry/config_unix.go

@@ -3,7 +3,7 @@
 package registry
 
 import (
-	flag "github.com/docker/docker/pkg/mflag"
+	"github.com/spf13/pflag"
 )
 
 var (
@@ -20,6 +20,6 @@ func cleanPath(s string) string {
 }
 
 // installCliPlatformFlags handles any platform specific flags for the service.
-func (options *ServiceOptions) installCliPlatformFlags(cmd *flag.FlagSet, usageFn func(string) string) {
-	cmd.BoolVar(&options.V2Only, []string{"-disable-legacy-registry"}, false, usageFn("Disable contacting legacy registries"))
+func (options *ServiceOptions) installCliPlatformFlags(flags *pflag.FlagSet) {
+	flags.BoolVar(&options.V2Only, "disable-legacy-registry", false, "Disable contacting legacy registries")
 }

+ 2 - 2
registry/config_windows.go

@@ -5,7 +5,7 @@ import (
 	"path/filepath"
 	"strings"
 
-	flag "github.com/docker/docker/pkg/mflag"
+	"github.com/spf13/pflag"
 )
 
 // CertsDir is the directory where certificates are stored
@@ -20,6 +20,6 @@ func cleanPath(s string) string {
 }
 
 // installCliPlatformFlags handles any platform specific flags for the service.
-func (options *ServiceOptions) installCliPlatformFlags(cmd *flag.FlagSet, usageFn func(string) string) {
+func (options *ServiceOptions) installCliPlatformFlags(flags *pflag.FlagSet) {
 	// No Windows specific flags.
 }

+ 5 - 0
runconfig/opts/runtime.go

@@ -72,3 +72,8 @@ func (o *RuntimeOpt) GetMap() map[string]types.Runtime {
 
 	return map[string]types.Runtime{}
 }
+
+// Type returns the type of the option
+func (o *RuntimeOpt) Type() string {
+	return "runtime"
+}

+ 12 - 0
vendor/src/github.com/spf13/cobra/.gitignore

@@ -19,6 +19,18 @@ _cgo_export.*
 
 _testmain.go
 
+# Vim files https://github.com/github/gitignore/blob/master/Global/Vim.gitignore
+# swap
+[._]*.s[a-w][a-z]
+[._]s[a-w][a-z]
+# session
+Session.vim
+# temporary
+.netrwhist
+*~
+# auto-generated tag files
+tags
+
 *.exe
 
 cobra.test

+ 35 - 7
vendor/src/github.com/spf13/cobra/bash_completions.go

@@ -116,12 +116,12 @@ __handle_reply()
     fi
 
     local completions
-    if [[ ${#must_have_one_flag[@]} -ne 0 ]]; then
-        completions=("${must_have_one_flag[@]}")
-    elif [[ ${#must_have_one_noun[@]} -ne 0 ]]; then
+    completions=("${commands[@]}")
+    if [[ ${#must_have_one_noun[@]} -ne 0 ]]; then
         completions=("${must_have_one_noun[@]}")
-    else
-        completions=("${commands[@]}")
+    fi
+    if [[ ${#must_have_one_flag[@]} -ne 0 ]]; then
+        completions+=("${must_have_one_flag[@]}")
     fi
     COMPREPLY=( $(compgen -W "${completions[*]}" -- "$cur") )
 
@@ -167,6 +167,11 @@ __handle_flag()
         must_have_one_flag=()
     fi
 
+    # if you set a flag which only applies to this command, don't show subcommands
+    if __contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then
+      commands=()
+    fi
+
     # keep flag value with flagname as flaghash
     if [ -n "${flagvalue}" ] ; then
         flaghash[${flagname}]=${flagvalue}
@@ -263,6 +268,7 @@ func postscript(w io.Writer, name string) error {
     local c=0
     local flags=()
     local two_word_flags=()
+    local local_nonpersistent_flags=()
     local flags_with_completion=()
     local flags_completion=()
     local commands=("%s")
@@ -360,7 +366,7 @@ func writeFlagHandler(name string, annotations map[string][]string, w io.Writer)
 }
 
 func writeShortFlag(flag *pflag.Flag, w io.Writer) error {
-	b := (flag.Value.Type() == "bool")
+	b := (len(flag.NoOptDefVal) > 0)
 	name := flag.Shorthand
 	format := "    "
 	if !b {
@@ -374,7 +380,7 @@ func writeShortFlag(flag *pflag.Flag, w io.Writer) error {
 }
 
 func writeFlag(flag *pflag.Flag, w io.Writer) error {
-	b := (flag.Value.Type() == "bool")
+	b := (len(flag.NoOptDefVal) > 0)
 	name := flag.Name
 	format := "    flags+=(\"--%s"
 	if !b {
@@ -387,9 +393,24 @@ func writeFlag(flag *pflag.Flag, w io.Writer) error {
 	return writeFlagHandler("--"+name, flag.Annotations, w)
 }
 
+func writeLocalNonPersistentFlag(flag *pflag.Flag, w io.Writer) error {
+	b := (len(flag.NoOptDefVal) > 0)
+	name := flag.Name
+	format := "    local_nonpersistent_flags+=(\"--%s"
+	if !b {
+		format += "="
+	}
+	format += "\")\n"
+	if _, err := fmt.Fprintf(w, format, name); err != nil {
+		return err
+	}
+	return nil
+}
+
 func writeFlags(cmd *Command, w io.Writer) error {
 	_, err := fmt.Fprintf(w, `    flags=()
     two_word_flags=()
+    local_nonpersistent_flags=()
     flags_with_completion=()
     flags_completion=()
 
@@ -397,6 +418,7 @@ func writeFlags(cmd *Command, w io.Writer) error {
 	if err != nil {
 		return err
 	}
+	localNonPersistentFlags := cmd.LocalNonPersistentFlags()
 	var visitErr error
 	cmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) {
 		if err := writeFlag(flag, w); err != nil {
@@ -409,6 +431,12 @@ func writeFlags(cmd *Command, w io.Writer) error {
 				return
 			}
 		}
+		if localNonPersistentFlags.Lookup(flag.Name) != nil {
+			if err := writeLocalNonPersistentFlag(flag, w); err != nil {
+				visitErr = err
+				return
+			}
+		}
 	})
 	if visitErr != nil {
 		return visitErr

+ 2 - 2
vendor/src/github.com/spf13/cobra/bash_completions.md

@@ -117,7 +117,7 @@ cmd := &cobra.Command{
 ```
 
 The aliases are not shown to the user on tab completion, but they are accepted as valid nouns by
-the completion aglorithm if entered manually, e.g. in:
+the completion algorithm if entered manually, e.g. in:
 
 ```bash
 # kubectl get rc [tab][tab]
@@ -175,7 +175,7 @@ So while there are many other files in the CWD it only shows me subdirs and thos
 
 # Specifiy custom flag completion
 
-Similar to the filename completion and filtering usingn cobra.BashCompFilenameExt, you can specifiy
+Similar to the filename completion and filtering using cobra.BashCompFilenameExt, you can specifiy
 a custom flag completion function with cobra.BashCompCustom:
 
 ```go

+ 4 - 0
vendor/src/github.com/spf13/cobra/cobra.go

@@ -41,6 +41,10 @@ var initializers []func()
 // Set this to true to enable it
 var EnablePrefixMatching = false
 
+//EnableCommandSorting controls sorting of the slice of commands, which is turned on by default.
+//To disable sorting, set it to false.
+var EnableCommandSorting = true
+
 //AddTemplateFunc adds a template function that's available to Usage and Help
 //template generation.
 func AddTemplateFunc(name string, tmplFunc interface{}) {

+ 123 - 27
vendor/src/github.com/spf13/cobra/command.go

@@ -21,6 +21,7 @@ import (
 	"io"
 	"os"
 	"path/filepath"
+	"sort"
 	"strings"
 
 	flag "github.com/spf13/pflag"
@@ -105,6 +106,8 @@ type Command struct {
 	commandsMaxUseLen         int
 	commandsMaxCommandPathLen int
 	commandsMaxNameLen        int
+	// is commands slice are sorted or not
+	commandsAreSorted bool
 
 	flagErrorBuf *bytes.Buffer
 
@@ -126,6 +129,9 @@ type Command struct {
 
 	// Disable the flag parsing. If this is true all flags will be passed to the command as arguments.
 	DisableFlagParsing bool
+
+	// TraverseChildren parses flags on all parents before executing child command
+	TraverseChildren bool
 }
 
 // os.Args[1:] by default, if desired, can be overridden
@@ -409,13 +415,14 @@ func argsMinusFirstX(args []string, x string) []string {
 	return args
 }
 
-// find the target command given the args and command tree
+func isFlagArg(arg string) bool {
+	return ((len(arg) >= 3 && arg[1] == '-') ||
+	        (len(arg) >= 2 && arg[0] == '-' && arg[1] != '-'))
+}
+
+// Find the target command given the args and command tree
 // Meant to be run on the highest node. Only searches down.
 func (c *Command) Find(args []string) (*Command, []string, error) {
-	if c == nil {
-		return nil, nil, fmt.Errorf("Called find() on a nil Command")
-	}
-
 	var innerfind func(*Command, []string) (*Command, []string)
 
 	innerfind = func(c *Command, innerArgs []string) (*Command, []string) {
@@ -424,28 +431,11 @@ func (c *Command) Find(args []string) (*Command, []string, error) {
 			return c, innerArgs
 		}
 		nextSubCmd := argsWOflags[0]
-		matches := make([]*Command, 0)
-		for _, cmd := range c.commands {
-			if cmd.Name() == nextSubCmd || cmd.HasAlias(nextSubCmd) { // exact name or alias match
-				return innerfind(cmd, argsMinusFirstX(innerArgs, nextSubCmd))
-			}
-			if EnablePrefixMatching {
-				if strings.HasPrefix(cmd.Name(), nextSubCmd) { // prefix match
-					matches = append(matches, cmd)
-				}
-				for _, x := range cmd.Aliases {
-					if strings.HasPrefix(x, nextSubCmd) {
-						matches = append(matches, cmd)
-					}
-				}
-			}
-		}
 
-		// only accept a single prefix match - multiple matches would be ambiguous
-		if len(matches) == 1 {
-			return innerfind(matches[0], argsMinusFirstX(innerArgs, argsWOflags[0]))
+		cmd := c.findNext(nextSubCmd)
+		if cmd != nil {
+			return innerfind(cmd, argsMinusFirstX(innerArgs, nextSubCmd))
 		}
-
 		return c, innerArgs
 	}
 
@@ -456,6 +446,66 @@ func (c *Command) Find(args []string) (*Command, []string, error) {
 	return commandFound, a, nil
 }
 
+func (c *Command) findNext(next string) *Command {
+	matches := make([]*Command, 0)
+	for _, cmd := range c.commands {
+		if cmd.Name() == next || cmd.HasAlias(next) {
+			return cmd
+		}
+		if EnablePrefixMatching && cmd.HasNameOrAliasPrefix(next) {
+			matches = append(matches, cmd)
+		}
+	}
+
+	if len(matches) == 1 {
+		return matches[0]
+	}
+	return nil
+}
+
+// Traverse the command tree to find the command, and parse args for
+// each parent.
+func (c *Command) Traverse(args []string) (*Command, []string, error) {
+	flags := []string{}
+	inFlag := false
+
+	for i, arg := range args {
+		switch {
+		// A long flag with a space separated value
+		case strings.HasPrefix(arg, "--") && !strings.Contains(arg, "="):
+			// TODO: this isn't quite right, we should really check ahead for 'true' or 'false'
+			inFlag = !isBooleanFlag(arg[2:], c.Flags())
+			flags = append(flags, arg)
+			continue
+		// A short flag with a space separated value
+		case strings.HasPrefix(arg, "-") && !strings.Contains(arg, "=") && len(arg) == 2 && !isBooleanShortFlag(arg[1:], c.Flags()):
+			inFlag = true
+			flags = append(flags, arg)
+			continue
+		// The value for a flag
+		case inFlag:
+			inFlag = false
+			flags = append(flags, arg)
+			continue
+		// A flag without a value, or with an `=` separated value
+		case isFlagArg(arg):
+			flags = append(flags, arg)
+			continue
+		}
+
+		cmd := c.findNext(arg)
+		if cmd == nil {
+			return c, args, nil
+		}
+
+		if err := c.ParseFlags(flags); err != nil {
+			return nil, args, err
+		}
+		return cmd.Traverse(args[i+1:])
+	}
+	return c, args, nil
+}
+
 func (c *Command) findSuggestions(arg string) string {
 	if c.DisableSuggestions {
 		return ""
@@ -668,7 +718,12 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
 		args = c.args
 	}
 
-	cmd, flags, err := c.Find(args)
+	var flags []string
+	if c.TraverseChildren {
+		cmd, flags, err = c.Traverse(args)
+	} else {
+		cmd, flags, err = c.Find(args)
+	}
 	if err != nil {
 		// If found parse to a subcommand and then failed, talk about the subcommand
 		if cmd != nil {
@@ -680,6 +735,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
 		}
 		return c, err
 	}
+
 	err = cmd.execute(flags)
 	if err != nil {
 		// Always show help if requested, even if SilenceErrors is in
@@ -754,8 +810,20 @@ func (c *Command) ResetCommands() {
 	c.helpCommand = nil
 }
 
-//Commands returns a slice of child commands.
+// Sorts commands by their names
+type commandSorterByName []*Command
+
+func (c commandSorterByName) Len() int           { return len(c) }
+func (c commandSorterByName) Swap(i, j int)      { c[i], c[j] = c[j], c[i] }
+func (c commandSorterByName) Less(i, j int) bool { return c[i].Name() < c[j].Name() }
+
+// Commands returns a sorted slice of child commands.
 func (c *Command) Commands() []*Command {
+	// do not sort commands if it already sorted or sorting was disabled
+	if EnableCommandSorting && !c.commandsAreSorted{
+		sort.Sort(commandSorterByName(c.commands))
+		c.commandsAreSorted = true
+	}
 	return c.commands
 }
 
@@ -784,6 +852,7 @@ func (c *Command) AddCommand(cmds ...*Command) {
 			x.SetGlobalNormalizationFunc(c.globNormFunc)
 		}
 		c.commands = append(c.commands, x)
+		c.commandsAreSorted = false
 	}
 }
 
@@ -953,6 +1022,20 @@ func (c *Command) HasAlias(s string) bool {
 	return false
 }
 
+// HasNameOrAliasPrefix returns true if the Name or any of aliases start
+// with prefix
+func (c *Command) HasNameOrAliasPrefix(prefix string) bool {
+	if strings.HasPrefix(c.Name(), prefix) {
+		return true
+	}
+	for _, alias := range c.Aliases {
+		if strings.HasPrefix(alias, prefix) {
+			return true
+		}
+	}
+	return false
+}
+
 func (c *Command) NameAndAliases() string {
 	return strings.Join(append([]string{c.Name()}, c.Aliases...), ", ")
 }
@@ -1065,6 +1148,19 @@ func (c *Command) Flags() *flag.FlagSet {
 	return c.flags
 }
 
+// LocalNonPersistentFlags are flags specific to this command which will NOT persist to subcommands
+func (c *Command) LocalNonPersistentFlags() *flag.FlagSet {
+	persistentFlags := c.PersistentFlags()
+
+	out := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
+	c.LocalFlags().VisitAll(func(f *flag.Flag) {
+		if persistentFlags.Lookup(f.Name) == nil {
+			out.AddFlag(f)
+		}
+	})
+	return out
+}
+
 // Get the local FlagSet specifically set in the current command
 func (c *Command) LocalFlags() *flag.FlagSet {
 	c.mergePersistentFlags()