瀏覽代碼

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

Convert docker and dockerd commands to spf13/cobra
Daniel Nephin 9 年之前
父節點
當前提交
25587906d1
共有 69 個文件被更改,包括 1082 次插入3412 次删除
  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"
 	"io"
 	"net/http"
 	"net/http"
 	"os"
 	"os"
+	"path/filepath"
 	"runtime"
 	"runtime"
 
 
 	"github.com/docker/docker/api"
 	"github.com/docker/docker/api"
@@ -14,7 +15,7 @@ import (
 	"github.com/docker/docker/cliconfig/configfile"
 	"github.com/docker/docker/cliconfig/configfile"
 	"github.com/docker/docker/cliconfig/credentials"
 	"github.com/docker/docker/cliconfig/credentials"
 	"github.com/docker/docker/dockerversion"
 	"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/docker/pkg/term"
 	"github.com/docker/engine-api/client"
 	"github.com/docker/engine-api/client"
 	"github.com/docker/go-connections/sockets"
 	"github.com/docker/go-connections/sockets"
@@ -53,15 +54,6 @@ type DockerCli struct {
 	outState *term.State
 	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
 // Client returns the APIClient
 func (cli *DockerCli) Client() client.APIClient {
 func (cli *DockerCli) Client() client.APIClient {
 	return cli.client
 	return cli.client
@@ -155,40 +147,41 @@ func (cli *DockerCli) restoreTerminal(in io.Closer) error {
 	return nil
 	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
 // 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
 // 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 {
 	if err != nil {
 		return &client.Client{}, err
 		return &client.Client{}, err
 	}
 	}
@@ -222,7 +215,7 @@ func NewAPIClientFromFlags(clientFlags *cliflags.ClientFlags, configFile *config
 		verStr = tmpStr
 		verStr = tmpStr
 	}
 	}
 
 
-	httpClient, err := newHTTPClient(host, clientFlags.Common.TLSOptions)
+	httpClient, err := newHTTPClient(host, opts.TLSOptions)
 	if err != nil {
 	if err != nil {
 		return &client.Client{}, err
 		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")
 		return "", errors.New("Please specify only one -H")
 	}
 	}
 
 
-	host, err = opts.ParseHost(tlsOptions != nil, host)
+	host, err = dopts.ParseHost(tlsOptions != nil, host)
 	return
 	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)
 			return runAttach(dockerCli, &opts)
 		},
 		},
 	}
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
 
 
 	flags := cmd.Flags()
 	flags := cmd.Flags()
 	flags.BoolVar(&opts.noStdin, "no-stdin", false, "Do not attach STDIN")
 	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)
 			return runCommit(dockerCli, &opts)
 		},
 		},
 	}
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
 
 
 	flags := cmd.Flags()
 	flags := cmd.Flags()
 	flags.SetInterspersed(false)
 	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)
 			return runCreate(dockerCli, cmd.Flags(), &opts, copts)
 		},
 		},
 	}
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
 
 
 	flags := cmd.Flags()
 	flags := cmd.Flags()
 	flags.SetInterspersed(false)
 	flags.SetInterspersed(false)

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

@@ -19,7 +19,7 @@ type diffOptions struct {
 func NewDiffCommand(dockerCli *client.DockerCli) *cobra.Command {
 func NewDiffCommand(dockerCli *client.DockerCli) *cobra.Command {
 	var opts diffOptions
 	var opts diffOptions
 
 
-	cmd := &cobra.Command{
+	return &cobra.Command{
 		Use:   "diff CONTAINER",
 		Use:   "diff CONTAINER",
 		Short: "Inspect changes on a container's filesystem",
 		Short: "Inspect changes on a container's filesystem",
 		Args:  cli.ExactArgs(1),
 		Args:  cli.ExactArgs(1),
@@ -28,9 +28,6 @@ func NewDiffCommand(dockerCli *client.DockerCli) *cobra.Command {
 			return runDiff(dockerCli, &opts)
 			return runDiff(dockerCli, &opts)
 		},
 		},
 	}
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
-
-	return cmd
 }
 }
 
 
 func runDiff(dockerCli *client.DockerCli, opts *diffOptions) error {
 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)
 			return runLogs(dockerCli, &opts)
 		},
 		},
 	}
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
 
 
 	flags := cmd.Flags()
 	flags := cmd.Flags()
 	flags.BoolVarP(&opts.follow, "follow", "f", false, "Follow log output")
 	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 {
 func NewPauseCommand(dockerCli *client.DockerCli) *cobra.Command {
 	var opts pauseOptions
 	var opts pauseOptions
 
 
-	cmd := &cobra.Command{
+	return &cobra.Command{
 		Use:   "pause CONTAINER [CONTAINER...]",
 		Use:   "pause CONTAINER [CONTAINER...]",
 		Short: "Pause all processes within one or more containers",
 		Short: "Pause all processes within one or more containers",
 		Args:  cli.RequiresMinArgs(1),
 		Args:  cli.RequiresMinArgs(1),
@@ -28,9 +28,6 @@ func NewPauseCommand(dockerCli *client.DockerCli) *cobra.Command {
 			return runPause(dockerCli, &opts)
 			return runPause(dockerCli, &opts)
 		},
 		},
 	}
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
-
-	return cmd
 }
 }
 
 
 func runPause(dockerCli *client.DockerCli, opts *pauseOptions) error {
 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)
 			return runPort(dockerCli, &opts)
 		},
 		},
 	}
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
-
 	return cmd
 	return cmd
 }
 }
 
 

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

@@ -30,8 +30,6 @@ func NewRenameCommand(dockerCli *client.DockerCli) *cobra.Command {
 			return runRename(dockerCli, &opts)
 			return runRename(dockerCli, &opts)
 		},
 		},
 	}
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
-
 	return cmd
 	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)
 			return runRun(dockerCli, cmd.Flags(), &opts, copts)
 		},
 		},
 	}
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
 
 
 	flags := cmd.Flags()
 	flags := cmd.Flags()
 	flags.SetInterspersed(false)
 	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)
 			return runStart(dockerCli, &opts)
 		},
 		},
 	}
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
 
 
 	flags := cmd.Flags()
 	flags := cmd.Flags()
 	flags.BoolVarP(&opts.attach, "attach", "a", false, "Attach STDOUT/STDERR and forward signals")
 	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)
 			return runStop(dockerCli, &opts)
 		},
 		},
 	}
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
 
 
 	flags := cmd.Flags()
 	flags := cmd.Flags()
 	flags.IntVarP(&opts.time, "time", "t", 10, "Seconds to wait for stop before killing it")
 	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)
 			return runTop(dockerCli, &opts)
 		},
 		},
 	}
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
 
 
 	flags := cmd.Flags()
 	flags := cmd.Flags()
 	flags.SetInterspersed(false)
 	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)
 			return runUnpause(dockerCli, &opts)
 		},
 		},
 	}
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
-
 	return cmd
 	return cmd
 }
 }
 
 

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

@@ -28,8 +28,6 @@ func NewWaitCommand(dockerCli *client.DockerCli) *cobra.Command {
 			return runWait(dockerCli, &opts)
 			return runWait(dockerCli, &opts)
 		},
 		},
 	}
 	}
-	cmd.SetFlagErrorFunc(flagErrorFunc)
-
 	return cmd
 	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
 package cli
 
 
-import "strings"
+import (
+	"fmt"
+	"strings"
+)
 
 
 // Errors is a list of errors.
 // 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,
 // 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, ", ")
 	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
 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
 	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/Sirupsen/logrus"
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/opts"
-	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/go-connections/tlsconfig"
 	"github.com/docker/go-connections/tlsconfig"
+	"github.com/spf13/pflag"
 )
 )
 
 
 const (
 const (
@@ -21,8 +21,8 @@ const (
 	DefaultKeyFile = "key.pem"
 	DefaultKeyFile = "key.pem"
 	// DefaultCertFile is the default filename for the cert pem file
 	// DefaultCertFile is the default filename for the cert pem file
 	DefaultCertFile = "cert.pem"
 	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 (
 var (
@@ -30,11 +30,8 @@ var (
 	dockerTLSVerify = os.Getenv("DOCKER_TLS_VERIFY") != ""
 	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
 	Debug      bool
 	Hosts      []string
 	Hosts      []string
 	LogLevel   string
 	LogLevel   string
@@ -44,62 +41,59 @@ type CommonFlags struct {
 	TrustKey   string
 	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 == "" {
 	if dockerCertPath == "" {
 		dockerCertPath = cliconfig.ConfigDir()
 		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
 	// Regardless of whether the user sets it to true or false, if they
 	// specify --tlsverify at all then we need to turn on tls
 	// 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
 	// TLSVerify can be true even if not set due to DOCKER_TLS_VERIFY env var, so we need
 	// to check that here as well
 	// 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 {
 	} 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
 		// Reset CertFile and KeyFile to empty string if the user did not specify
 		// the respective flags and the respective default files were not found.
 		// 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) {
 			if _, err := os.Stat(tlsOptions.CertFile); os.IsNotExist(err) {
 				tlsOptions.CertFile = ""
 				tlsOptions.CertFile = ""
 			}
 			}
 		}
 		}
-		if !cmd.IsSet("-tlskey") {
+		if !flags.Changed("tlskey") {
 			if _, err := os.Stat(tlsOptions.KeyFile); os.IsNotExist(err) {
 			if _, err := os.Stat(tlsOptions.KeyFile); os.IsNotExist(err) {
 				tlsOptions.KeyFile = ""
 				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"
 	"fmt"
 	"runtime"
 	"runtime"
 	"strings"
 	"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(
 	return fmt.Errorf(
 		"`docker daemon` is not supported on %s. Please run `dockerd` directly",
 		"`docker daemon` is not supported on %s. Please run `dockerd` directly",
 		strings.Title(runtime.GOOS))
 		strings.Title(runtime.GOOS))

+ 29 - 9
cmd/docker/daemon_unix.go

@@ -3,23 +3,37 @@
 package main
 package main
 
 
 import (
 import (
+	"fmt"
+
 	"os"
 	"os"
 	"os/exec"
 	"os/exec"
 	"path/filepath"
 	"path/filepath"
 	"syscall"
 	"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()
 	binaryPath, err := findDaemonBinary()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -31,6 +45,12 @@ func (p DaemonProxy) CmdDaemon(args ...string) error {
 		os.Environ())
 		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
 // findDaemonBinary looks for the path to the dockerd binary starting with
 // the directory of the current executable (if one exists) and followed by $PATH
 // the directory of the current executable (if one exists) and followed by $PATH
 func findDaemonBinary() (string, error) {
 func findDaemonBinary() (string, error) {

+ 59 - 66
cmd/docker/docker.go

@@ -3,73 +3,77 @@ package main
 import (
 import (
 	"fmt"
 	"fmt"
 	"os"
 	"os"
-	"path/filepath"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/client"
 	"github.com/docker/docker/api/client"
+	"github.com/docker/docker/api/client/command"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli"
-	"github.com/docker/docker/cli/cobraadaptor"
 	cliflags "github.com/docker/docker/cli/flags"
 	cliflags "github.com/docker/docker/cli/flags"
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/dockerversion"
 	"github.com/docker/docker/dockerversion"
-	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/term"
 	"github.com/docker/docker/pkg/term"
 	"github.com/docker/docker/utils"
 	"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, ok := err.(cli.StatusError); ok {
 			if sterr.Status != "" {
 			if sterr.Status != "" {
 				fmt.Fprintln(stderr, 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/Sirupsen/logrus"
 	"github.com/docker/docker/utils"
 	"github.com/docker/docker/utils"
+
+	"github.com/docker/docker/api/client"
 )
 )
 
 
 func TestClientDebugEnabled(t *testing.T) {
 func TestClientDebugEnabled(t *testing.T) {
 	defer utils.DisableDebug()
 	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" {
 	if os.Getenv("DEBUG") != "1" {
 		t.Fatal("expected debug enabled, got false")
 		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"
 	"io"
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
-	"runtime"
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
@@ -31,11 +30,10 @@ import (
 	"github.com/docker/docker/daemon/logger"
 	"github.com/docker/docker/daemon/logger"
 	"github.com/docker/docker/dockerversion"
 	"github.com/docker/docker/dockerversion"
 	"github.com/docker/docker/libcontainerd"
 	"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/authorization"
 	"github.com/docker/docker/pkg/jsonlog"
 	"github.com/docker/docker/pkg/jsonlog"
 	"github.com/docker/docker/pkg/listeners"
 	"github.com/docker/docker/pkg/listeners"
-	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/pidfile"
 	"github.com/docker/docker/pkg/pidfile"
 	"github.com/docker/docker/pkg/signal"
 	"github.com/docker/docker/pkg/signal"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/system"
@@ -43,46 +41,27 @@ import (
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/utils"
 	"github.com/docker/docker/utils"
 	"github.com/docker/go-connections/tlsconfig"
 	"github.com/docker/go-connections/tlsconfig"
+	"github.com/spf13/pflag"
 )
 )
 
 
 const (
 const (
-	daemonConfigFileFlag = "-config-file"
+	flagDaemonConfigFile = "config-file"
 )
 )
 
 
 // DaemonCli represents the daemon CLI.
 // DaemonCli represents the daemon CLI.
 type DaemonCli struct {
 type DaemonCli struct {
 	*daemon.Config
 	*daemon.Config
-	commonFlags *cliflags.CommonFlags
-	configFile  *string
+	configFile *string
+	flags      *pflag.FlagSet
 
 
 	api             *apiserver.Server
 	api             *apiserver.Server
 	d               *daemon.Daemon
 	d               *daemon.Daemon
 	authzMiddleware *authorization.Middleware // authzMiddleware enables to dynamically reload the authorization plugins
 	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 {
 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) {
 func migrateKey() (err error) {
@@ -126,24 +105,25 @@ func migrateKey() (err error) {
 	return nil
 	return nil
 }
 }
 
 
-func (cli *DaemonCli) start() (err error) {
+func (cli *DaemonCli) start(opts daemonOptions) (err error) {
 	stopc := make(chan bool)
 	stopc := make(chan bool)
 	defer close(stopc)
 	defer close(stopc)
 
 
 	// warn from uuid package when running the daemon
 	// warn from uuid package when running the daemon
 	uuid.Loggerf = logrus.Warnf
 	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
 		return err
 	}
 	}
-	cli.Config = cliConfig
+	cli.configFile = &opts.configFile
+	cli.flags = opts.flags
 
 
 	if cli.Config.Debug {
 	if cli.Config.Debug {
 		utils.EnableDebug()
 		utils.EnableDebug()
@@ -215,7 +195,7 @@ func (cli *DaemonCli) start() (err error) {
 
 
 	for i := 0; i < len(cli.Config.Hosts); i++ {
 	for i := 0; i < len(cli.Config.Hosts); i++ {
 		var err error
 		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)
 			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 {
 	if err := migrateKey(); err != nil {
 		return err
 		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)
 	registryService := registry.NewService(cli.Config.ServiceOptions)
 	containerdRemote, err := libcontainerd.New(cli.getLibcontainerdRoot(), cli.getPlatformRemoteOptions()...)
 	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)
 		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{}
 	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 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.
 		// 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
 	// Regardless of whether the user sets it to true or false, if they
 	// specify TLSVerify at all then we need to turn on TLS
 	// specify TLSVerify at all then we need to turn on TLS
-	if config.IsValueSet(cliflags.TLSVerifyKey) {
+	if config.IsValueSet(cliflags.FlagTLSVerify) {
 		config.TLS = true
 		config.TLS = true
 	}
 	}
 
 

+ 96 - 241
cmd/dockerd/daemon_test.go

@@ -1,290 +1,145 @@
 package main
 package main
 
 
 import (
 import (
-	"io/ioutil"
-	"os"
-	"strings"
 	"testing"
 	"testing"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	cliflags "github.com/docker/docker/cli/flags"
 	cliflags "github.com/docker/docker/cli/flags"
 	"github.com/docker/docker/daemon"
 	"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) {
 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 {
 	if !loadedConfig.Debug {
 		t.Fatalf("expected debug to be copied from the common flags, got false")
 		t.Fatalf("expected debug to be copied from the common flags, got false")
 	}
 	}
 }
 }
 
 
 func TestLoadDaemonCliConfigWithTLS(t *testing.T) {
 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) {
 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) {
 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) {
 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) {
 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) {
 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) {
 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) {
 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
 package main
 
 
 import (
 import (
-	"io/ioutil"
-	"os"
 	"testing"
 	"testing"
 
 
-	cliflags "github.com/docker/docker/cli/flags"
 	"github.com/docker/docker/daemon"
 	"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) {
 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) {
 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) {
 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"},
 		"cluster-store-opts": {"kv.cacertfile": "/var/lib/docker/discovery_certs/ca.pem"},
 		"log-opts": {"tag": "test"}
 		"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"
 	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) {
 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
 	// make sure reloading doesn't generate configuration
 	// conflicts after normalizing boolean values.
 	// 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) {
 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) {
 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"
 	"os"
 
 
 	"github.com/Sirupsen/logrus"
 	"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"
 	"github.com/docker/docker/dockerversion"
-	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/reexec"
 	"github.com/docker/docker/pkg/reexec"
 	"github.com/docker/docker/pkg/term"
 	"github.com/docker/docker/pkg/term"
 	"github.com/docker/docker/utils"
 	"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()
 		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
 	// On Windows, this may be launching as a service or with an option to
 	// register the service.
 	// register the service.
-	stop, err := initService()
+	stop, err := initService(daemonCli)
 	if err != nil {
 	if err != nil {
 		logrus.Fatal(err)
 		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() {
 func showVersion() {
@@ -80,3 +84,20 @@ func showVersion() {
 		fmt.Printf("Docker version %s, build %s\n", dockerversion.Version, dockerversion.GitCommit)
 		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
 package main
 
 
-func initService() (bool, error) {
+import (
+	"github.com/spf13/pflag"
+)
+
+func initService(daemonCli *DaemonCli) (bool, error) {
 	return false, nil
 	return false, nil
 }
 }
+
+func installServiceFlags(flags *pflag.FlagSet) {
+}

+ 22 - 13
cmd/dockerd/service_windows.go

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

+ 53 - 45
daemon/config.go

@@ -6,15 +6,16 @@ import (
 	"fmt"
 	"fmt"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
+	"runtime"
 	"strings"
 	"strings"
 	"sync"
 	"sync"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/pkg/discovery"
 	"github.com/docker/docker/pkg/discovery"
-	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
 	"github.com/imdario/mergo"
 	"github.com/imdario/mergo"
+	"github.com/spf13/pflag"
 )
 )
 
 
 const (
 const (
@@ -145,39 +146,37 @@ type CommonConfig struct {
 	valuesSet  map[string]interface{}
 	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
 	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"?
 	// 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.MaxConcurrentDownloads = &maxConcurrentDownloads
 	config.MaxConcurrentUploads = &maxConcurrentUploads
 	config.MaxConcurrentUploads = &maxConcurrentUploads
@@ -193,6 +192,18 @@ func (config *Config) IsValueSet(name string) bool {
 	return ok
 	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) {
 func parseClusterAdvertiseSettings(clusterStore, clusterAdvertise string) (string, error) {
 	if clusterAdvertise == "" {
 	if clusterAdvertise == "" {
 		return "", errDiscoveryDisabled
 		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.
 // 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)
 	logrus.Infof("Got signal to reload configuration, reloading from: %s", configFile)
 	newConfig, err := getConflictFreeConfiguration(configFile, flags)
 	newConfig, err := getConflictFreeConfiguration(configFile, flags)
 	if err != nil {
 	if err != nil {
@@ -234,7 +245,7 @@ type boolValue interface {
 // loads the file configuration in an isolated structure,
 // loads the file configuration in an isolated structure,
 // and merges the configuration provided from flags on top
 // and merges the configuration provided from flags on top
 // if there are no conflicts.
 // 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)
 	fileConfig, err := getConflictFreeConfiguration(configFile, flags)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -261,7 +272,7 @@ func MergeDaemonConfigurations(flagsConfig *Config, flags *flag.FlagSet, configF
 // getConflictFreeConfiguration loads the configuration from a JSON file.
 // getConflictFreeConfiguration loads the configuration from a JSON file.
 // It compares that configuration with the one provided by the flags,
 // It compares that configuration with the one provided by the flags,
 // and returns an error if there are conflicts.
 // 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)
 	b, err := ioutil.ReadFile(configFile)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		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.
 		// TODO: Rewrite configuration logic to avoid same issue with other nullable values, like numbers.
 		namedOptions := make(map[string]interface{})
 		namedOptions := make(map[string]interface{})
 		for key, value := range configSet {
 		for key, value := range configSet {
-			f := flags.Lookup("-" + key)
+			f := flags.Lookup(key)
 			if f == nil { // ignore named flags that don't match
 			if f == nil { // ignore named flags that don't match
 				namedOptions[key] = value
 				namedOptions[key] = value
 				continue
 				continue
@@ -301,7 +312,7 @@ func getConflictFreeConfiguration(configFile string, flags *flag.FlagSet) (*Conf
 		}
 		}
 		if len(namedOptions) > 0 {
 		if len(namedOptions) > 0 {
 			// set also default for mergeVal flags that are boolValue at the same time.
 			// 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 {
 				if opt, named := f.Value.(opts.NamedOption); named {
 					v, set := namedOptions[opt.Name()]
 					v, set := namedOptions[opt.Name()]
 					_, boolean := f.Value.(boolValue)
 					_, 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
 // findConfigurationConflicts iterates over the provided flags searching for
 // duplicated configurations and unknown keys. It returns an error with all the conflicts if
 // duplicated configurations and unknown keys. It returns an error with all the conflicts if
 // it finds any.
 // 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.
 	// 1. Search keys from the file that we don't recognize as flags.
 	unknownKeys := make(map[string]interface{})
 	unknownKeys := make(map[string]interface{})
 	for key, value := range config {
 	for key, value := range config {
-		flagName := "-" + key
-		if flag := flags.Lookup(flagName); flag == nil {
+		if flag := flags.Lookup(key); flag == nil {
 			unknownKeys[key] = value
 			unknownKeys[key] = value
 		}
 		}
 	}
 	}
@@ -352,7 +362,7 @@ func findConfigurationConflicts(config map[string]interface{}, flags *flag.FlagS
 	// 2. Discard values that implement NamedOption.
 	// 2. Discard values that implement NamedOption.
 	// Their configuration name differs from their flag name, like `labels` and `label`.
 	// Their configuration name differs from their flag name, like `labels` and `label`.
 	if len(unknownKeys) > 0 {
 	if len(unknownKeys) > 0 {
-		unknownNamedConflicts := func(f *flag.Flag) {
+		unknownNamedConflicts := func(f *pflag.Flag) {
 			if namedOption, ok := f.Value.(opts.NamedOption); ok {
 			if namedOption, ok := f.Value.(opts.NamedOption); ok {
 				if _, valid := unknownKeys[namedOption.Name()]; valid {
 				if _, valid := unknownKeys[namedOption.Name()]; valid {
 					delete(unknownKeys, namedOption.Name())
 					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.
 	// 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
 		// search option name in the json configuration payload if the value is a named option
 		if namedOption, ok := f.Value.(opts.NamedOption); ok {
 		if namedOption, ok := f.Value.(opts.NamedOption); ok {
 			if optsValue, ok := config[namedOption.Name()]; ok {
 			if optsValue, ok := config[namedOption.Name()]; ok {
 				conflicts = append(conflicts, printConflict(namedOption.Name(), f.Value.String(), optsValue))
 				conflicts = append(conflicts, printConflict(namedOption.Name(), f.Value.String(), optsValue))
 			}
 			}
 		} else {
 		} 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 {
 				if value, ok := config[name]; ok {
 					conflicts = append(conflicts, printConflict(name, f.Value.String(), value))
 					conflicts = append(conflicts, printConflict(name, f.Value.String(), value))
 					break
 					break

+ 4 - 2
daemon/config_experimental.go

@@ -2,7 +2,9 @@
 
 
 package daemon
 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
 package daemon
 
 
 import (
 import (
-	flag "github.com/docker/docker/pkg/mflag"
+	"github.com/spf13/pflag"
 )
 )
 
 
 var (
 var (
@@ -28,14 +28,12 @@ type bridgeConfig struct {
 
 
 // InstallFlags adds command-line options to the top-level flag parser for
 // InstallFlags adds command-line options to the top-level flag parser for
 // the current process.
 // 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
 	// First handle install flags which are consistent cross-platform
-	config.InstallCommonFlags(cmd, usageFn)
+	config.InstallCommonFlags(flags)
 
 
 	// Then platform-specific install flags
 	// Then platform-specific install flags
-	config.attachExperimentalFlags(cmd, usageFn)
+	config.attachExperimentalFlags(flags)
 }
 }
 
 
 // GetExecRoot returns the user configured Exec-root
 // GetExecRoot returns the user configured Exec-root

+ 4 - 2
daemon/config_stub.go

@@ -2,7 +2,9 @@
 
 
 package daemon
 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"
 	"testing"
 
 
 	"github.com/docker/docker/opts"
 	"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) {
 func TestDaemonConfigurationMerge(t *testing.T) {
@@ -87,42 +88,26 @@ func TestParseClusterAdvertiseSettings(t *testing.T) {
 
 
 func TestFindConfigurationConflicts(t *testing.T) {
 func TestFindConfigurationConflicts(t *testing.T) {
 	config := map[string]interface{}{"authorization-plugins": "foobar"}
 	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) {
 func TestFindConfigurationConflictsWithNamedOptions(t *testing.T) {
 	config := map[string]interface{}{"hosts": []string{"qwer"}}
 	config := map[string]interface{}{"hosts": []string{"qwer"}}
-	flags := mflag.NewFlagSet("test", mflag.ContinueOnError)
+	flags := pflag.NewFlagSet("test", pflag.ContinueOnError)
 
 
 	var hosts []string
 	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) {
 func TestDaemonConfigurationMergeConflicts(t *testing.T) {
@@ -135,8 +120,8 @@ func TestDaemonConfigurationMergeConflicts(t *testing.T) {
 	f.Write([]byte(`{"debug": true}`))
 	f.Write([]byte(`{"debug": true}`))
 	f.Close()
 	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")
 	flags.Set("debug", "false")
 
 
 	_, err = MergeDaemonConfigurations(&Config{}, flags, configFile)
 	_, err = MergeDaemonConfigurations(&Config{}, flags, configFile)
@@ -158,8 +143,8 @@ func TestDaemonConfigurationMergeConflictsWithInnerStructs(t *testing.T) {
 	f.Write([]byte(`{"tlscacert": "/etc/certificates/ca.pem"}`))
 	f.Write([]byte(`{"tlscacert": "/etc/certificates/ca.pem"}`))
 	f.Close()
 	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")
 	flags.Set("tlscacert", "~/.docker/ca.pem")
 
 
 	_, err = MergeDaemonConfigurations(&Config{}, flags, configFile)
 	_, err = MergeDaemonConfigurations(&Config{}, flags, configFile)
@@ -173,9 +158,9 @@ func TestDaemonConfigurationMergeConflictsWithInnerStructs(t *testing.T) {
 
 
 func TestFindConfigurationConflictsWithUnknownKeys(t *testing.T) {
 func TestFindConfigurationConflictsWithUnknownKeys(t *testing.T) {
 	config := map[string]interface{}{"tls-verify": "true"}
 	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)
 	err := findConfigurationConflicts(config, flags)
 	if err == nil {
 	if err == nil {
 		t.Fatal("expected error, got nil")
 		t.Fatal("expected error, got nil")
@@ -188,18 +173,15 @@ func TestFindConfigurationConflictsWithUnknownKeys(t *testing.T) {
 func TestFindConfigurationConflictsWithMergedValues(t *testing.T) {
 func TestFindConfigurationConflictsWithMergedValues(t *testing.T) {
 	var hosts []string
 	var hosts []string
 	config := map[string]interface{}{"hosts": "tcp://127.0.0.1:2345"}
 	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)
 	err := findConfigurationConflicts(config, flags)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	flags.Set("-host", "unix:///var/run/docker.sock")
+	flags.Set("host", "unix:///var/run/docker.sock")
 	err = findConfigurationConflicts(config, flags)
 	err = findConfigurationConflicts(config, flags)
 	if err == nil {
 	if err == nil {
 		t.Fatal("expected error, got nil")
 		t.Fatal("expected error, got nil")

+ 34 - 35
daemon/config_unix.go

@@ -7,10 +7,10 @@ import (
 	"net"
 	"net"
 
 
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/opts"
-	flag "github.com/docker/docker/pkg/mflag"
 	runconfigopts "github.com/docker/docker/runconfig/opts"
 	runconfigopts "github.com/docker/docker/runconfig/opts"
 	"github.com/docker/engine-api/types"
 	"github.com/docker/engine-api/types"
-	"github.com/docker/go-units"
+	units "github.com/docker/go-units"
+	"github.com/spf13/pflag"
 )
 )
 
 
 var (
 var (
@@ -56,44 +56,43 @@ type bridgeConfig struct {
 	InterContainerCommunication bool   `json:"icc,omitempty"`
 	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
 	// 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)
 	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)
 	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
 // GetRuntime returns the runtime path and arguments for a given

+ 7 - 10
daemon/config_windows.go

@@ -3,8 +3,8 @@ package daemon
 import (
 import (
 	"os"
 	"os"
 
 
-	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/engine-api/types"
 	"github.com/docker/engine-api/types"
+	"github.com/spf13/pflag"
 )
 )
 
 
 var (
 var (
@@ -28,18 +28,15 @@ type Config struct {
 	// for the Windows daemon.)
 	// 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
 	// First handle install flags which are consistent cross-platform
-	config.InstallCommonFlags(cmd, usageFn)
+	config.InstallCommonFlags(flags)
 
 
 	// Then platform-specific install 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
 // 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
 clone git github.com/pkg/errors 01fa4104b9c248c8945d14d9f128454d5b28d595
 
 
 # cli
 # 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/spf13/pflag cb88ea77998c3f024757528e3305022ab50b43be
 clone git github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
 clone git github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
 clone git github.com/flynn-archive/go-shlex 3f9db97f856818214da2e1057f8ad84803971cff
 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)
 			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"
 	"strings"
 
 
 	"github.com/docker/docker/pkg/integration/checker"
 	"github.com/docker/docker/pkg/integration/checker"
+	icmd "github.com/docker/docker/pkg/integration/cmd"
 	"github.com/go-check/check"
 	"github.com/go-check/check"
 )
 )
 
 
@@ -399,10 +400,9 @@ func (s *DockerSuite) TestCpUnprivilegedUser(c *check.C) {
 
 
 	c.Assert(os.Chmod(tmpdir, 0777), checker.IsNil)
 	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) {
 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"
 	"github.com/docker/docker/pkg/integration/checker"
 	icmd "github.com/docker/docker/pkg/integration/cmd"
 	icmd "github.com/docker/docker/pkg/integration/cmd"
 	"github.com/docker/docker/pkg/mount"
 	"github.com/docker/docker/pkg/mount"
+	"github.com/docker/docker/pkg/testutil/tempfile"
 	"github.com/docker/go-units"
 	"github.com/docker/go-units"
 	"github.com/docker/libnetwork/iptables"
 	"github.com/docker/libnetwork/iptables"
 	"github.com/docker/libtrust"
 	"github.com/docker/libtrust"
@@ -352,10 +353,8 @@ func (s *DockerDaemonSuite) TestDaemonIptablesCreate(c *check.C) {
 func (s *DockerSuite) TestDaemonIPv6Enabled(c *check.C) {
 func (s *DockerSuite) TestDaemonIPv6Enabled(c *check.C) {
 	testRequires(c, IPv6)
 	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)
 	d := NewDaemon(c)
 
 
 	if err := d.StartWithBusybox("--ipv6"); err != nil {
 	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 {
 	if ip := net.ParseIP(out); ip != nil {
 		c.Fatalf("Container should not have a global IPv6 address: %v", out)
 		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
 // 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) {
 func (s *DockerDaemonSuite) TestDaemonIPv6FixedCIDR(c *check.C) {
 	// IPv6 setup is messing with local bridge address.
 	// IPv6 setup is messing with local bridge address.
 	testRequires(c, SameHostDaemon)
 	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))
 	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")
 	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))
 	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'")
 	out = strings.Trim(out, " \r\n'")
 
 
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(err, checker.IsNil, check.Commentf(out))
@@ -441,13 +435,10 @@ func (s *DockerDaemonSuite) TestDaemonIPv6FixedCIDR(c *check.C) {
 	ip := net.ParseIP(out)
 	ip := net.ParseIP(out)
 	c.Assert(ip, checker.NotNil, check.Commentf("Container should have a global IPv6 address"))
 	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(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"))
 	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
 // 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) {
 func (s *DockerDaemonSuite) TestDaemonIPv6FixedCIDRAndMac(c *check.C) {
 	// IPv6 setup is messing with local bridge address.
 	// IPv6 setup is messing with local bridge address.
 	testRequires(c, SameHostDaemon)
 	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)
 	c.Assert(err, checker.IsNil)
 
 
 	out, err := s.d.Cmd("run", "-itd", "--name=ipv6test", "--mac-address", "AA:BB:CC:DD:EE:FF", "busybox")
 	out, err := s.d.Cmd("run", "-itd", "--name=ipv6test", "--mac-address", "AA:BB:CC:DD:EE:FF", "busybox")
 	c.Assert(err, checker.IsNil)
 	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(err, checker.IsNil)
 	c.Assert(strings.Trim(out, " \r\n'"), checker.Equals, "2001:db8:1::aabb:ccdd:eeff")
 	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) {
 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
 	// 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) {
 func (s *DockerDaemonSuite) TestDaemonRestartWithContainerWithRestartPolicyAlways(c *check.C) {
@@ -2362,31 +2352,26 @@ func (s *DockerSuite) TestDaemonDiscoveryBackendConfigReload(c *check.C) {
 	testRequires(c, SameHostDaemon, DaemonIsLinux)
 	testRequires(c, SameHostDaemon, DaemonIsLinux)
 
 
 	// daemon config file
 	// 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)
 	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)
 	c.Assert(err, checker.IsNil)
 	defer d.Stop()
 	defer d.Stop()
 
 
 	// daemon config file
 	// daemon config file
-	daemonConfig = `{
+	daemonConfig := `{
 	      "cluster-store": "consul://consuladdr:consulport/some/path",
 	      "cluster-store": "consul://consuladdr:consulport/some/path",
 	      "cluster-advertise": "192.168.56.100:0",
 	      "cluster-advertise": "192.168.56.100:0",
 	      "debug" : false
 	      "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)
 	c.Assert(err, checker.IsNil)
-	defer os.Remove(configFilePath)
 	fmt.Fprintf(configFile, "%s", daemonConfig)
 	fmt.Fprintf(configFile, "%s", daemonConfig)
 	configFile.Close()
 	configFile.Close()
 
 
@@ -2396,6 +2381,7 @@ func (s *DockerSuite) TestDaemonDiscoveryBackendConfigReload(c *check.C) {
 
 
 	out, err := d.Cmd("info")
 	out, err := d.Cmd("info")
 	c.Assert(err, checker.IsNil)
 	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 Store: consul://consuladdr:consulport/some/path"))
 	c.Assert(out, checker.Contains, fmt.Sprintf("Cluster Advertise: 192.168.56.100:0"))
 	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/homedir"
 	"github.com/docker/docker/pkg/integration/checker"
 	"github.com/docker/docker/pkg/integration/checker"
+	icmd "github.com/docker/docker/pkg/integration/cmd"
 	"github.com/go-check/check"
 	"github.com/go-check/check"
 )
 )
 
 
@@ -56,12 +57,7 @@ func (s *DockerSuite) TestHelpTextVerify(c *check.C) {
 		out, _, err := runCommandWithOutput(helpCmd)
 		out, _, err := runCommandWithOutput(helpCmd)
 		c.Assert(err, checker.IsNil, check.Commentf(out))
 		c.Assert(err, checker.IsNil, check.Commentf(out))
 		lines := strings.Split(out, "\n")
 		lines := strings.Split(out, "\n")
-		foundTooLongLine := false
 		for _, line := range lines {
 		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
 			// All lines should not end with a space
 			c.Assert(line, checker.Not(checker.HasSuffix), " ", check.Commentf("Line 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
 	// Check each line for lots of stuff
 	lines := strings.Split(out, "\n")
 	lines := strings.Split(out, "\n")
 	for _, line := range lines {
 	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, "~")
 		i := strings.Index(line, "~")
 		if i >= 0 && i != len(line)-1 && line[i+1] != '/' {
 		if i >= 0 && i != len(line)-1 && line[i+1] != '/' {
 			return fmt.Errorf("Help for %q should not have used ~:\n%s", cmd, line)
 			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 {
 	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
 		// skipNoArgs are ones that we don't want to try w/o
 		// any args. Either because it'll hang the test or
 		// any args. Either because it'll hang the test or
 		// lead to incorrect test result (like false negative).
 		// lead to incorrect test result (like false negative).
@@ -305,33 +289,31 @@ func testCommand(cmd string, newEnvs []string, scanForHome bool, home string) er
 			"load":   {},
 			"load":   {},
 		}
 		}
 
 
-		ec := 0
+		var result *icmd.Result
 		if _, ok := skipNoArgs[cmd]; !ok {
 		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 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") {
 		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") {
 		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.
 // Issue 9677.
 func (s *DockerSuite) TestRunWithDaemonFlags(c *check.C) {
 func (s *DockerSuite) TestRunWithDaemonFlags(c *check.C) {
 	out, _, err := dockerCmdWithError("--exec-opt", "foo=bar", "run", "-i", "busybox", "true")
 	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
 // 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
 	// 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
 	// 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) {
 func (s *DockerSuite) TestRunPortFromDockerRangeInUse(c *check.C) {

+ 10 - 6
man/generate.go

@@ -4,8 +4,10 @@ import (
 	"fmt"
 	"fmt"
 	"os"
 	"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"
 	"github.com/spf13/cobra/doc"
 )
 )
 
 
@@ -15,10 +17,12 @@ func generateManPages(path string) error {
 		Section: "1",
 		Section: "1",
 		Source:  "Docker Community",
 		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
 	cmd.DisableAutoGenTag = true
 	return doc.GenManTreeFromOpts(cmd, doc.GenManTreeOptions{
 	return doc.GenManTreeFromOpts(cmd, doc.GenManTreeOptions{
 		Header:           header,
 		Header:           header,

+ 5 - 0
opts/ip.go

@@ -40,3 +40,8 @@ func (o *IPOpt) String() string {
 	}
 	}
 	return o.IP.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.
 // they are not equal.
 func Equal(t TestingT, actual, expected interface{}) {
 func Equal(t TestingT, actual, expected interface{}) {
 	if expected != actual {
 	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.
 // the same items.
 func EqualStringSlice(t TestingT, actual, expected []string) {
 func EqualStringSlice(t TestingT, actual, expected []string) {
 	if len(actual) != len(expected) {
 	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)
 			len(expected), expected, len(actual), actual)
 	}
 	}
 	for i, item := range actual {
 	for i, item := range actual {
 		if item != expected[i] {
 		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)
 				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.
 // NilError asserts that the error is nil, otherwise it fails the test.
 func NilError(t TestingT, err error) {
 func NilError(t TestingT, err error) {
 	if err != nil {
 	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) {
 	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.
 // test.
 func Contains(t TestingT, actual, contains string) {
 func Contains(t TestingT, actual, contains string) {
 	if !strings.Contains(actual, contains) {
 	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"
 	"strings"
 
 
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/opts"
-	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/reference"
 	"github.com/docker/docker/reference"
 	registrytypes "github.com/docker/engine-api/types/registry"
 	registrytypes "github.com/docker/engine-api/types/registry"
+	"github.com/spf13/pflag"
 )
 )
 
 
 // ServiceOptions holds command line options.
 // 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
 // InstallCliFlags adds command-line options to the top-level flag parser for
 // the current process.
 // 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)
 	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)
 	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
 // newServiceConfig returns a new instance of ServiceConfig

+ 3 - 3
registry/config_unix.go

@@ -3,7 +3,7 @@
 package registry
 package registry
 
 
 import (
 import (
-	flag "github.com/docker/docker/pkg/mflag"
+	"github.com/spf13/pflag"
 )
 )
 
 
 var (
 var (
@@ -20,6 +20,6 @@ func cleanPath(s string) string {
 }
 }
 
 
 // installCliPlatformFlags handles any platform specific flags for the service.
 // 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"
 	"path/filepath"
 	"strings"
 	"strings"
 
 
-	flag "github.com/docker/docker/pkg/mflag"
+	"github.com/spf13/pflag"
 )
 )
 
 
 // CertsDir is the directory where certificates are stored
 // 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.
 // 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.
 	// 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{}
 	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
 _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
 *.exe
 
 
 cobra.test
 cobra.test

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

@@ -116,12 +116,12 @@ __handle_reply()
     fi
     fi
 
 
     local completions
     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[@]}")
         completions=("${must_have_one_noun[@]}")
-    else
-        completions=("${commands[@]}")
+    fi
+    if [[ ${#must_have_one_flag[@]} -ne 0 ]]; then
+        completions+=("${must_have_one_flag[@]}")
     fi
     fi
     COMPREPLY=( $(compgen -W "${completions[*]}" -- "$cur") )
     COMPREPLY=( $(compgen -W "${completions[*]}" -- "$cur") )
 
 
@@ -167,6 +167,11 @@ __handle_flag()
         must_have_one_flag=()
         must_have_one_flag=()
     fi
     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
     # keep flag value with flagname as flaghash
     if [ -n "${flagvalue}" ] ; then
     if [ -n "${flagvalue}" ] ; then
         flaghash[${flagname}]=${flagvalue}
         flaghash[${flagname}]=${flagvalue}
@@ -263,6 +268,7 @@ func postscript(w io.Writer, name string) error {
     local c=0
     local c=0
     local flags=()
     local flags=()
     local two_word_flags=()
     local two_word_flags=()
+    local local_nonpersistent_flags=()
     local flags_with_completion=()
     local flags_with_completion=()
     local flags_completion=()
     local flags_completion=()
     local commands=("%s")
     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 {
 func writeShortFlag(flag *pflag.Flag, w io.Writer) error {
-	b := (flag.Value.Type() == "bool")
+	b := (len(flag.NoOptDefVal) > 0)
 	name := flag.Shorthand
 	name := flag.Shorthand
 	format := "    "
 	format := "    "
 	if !b {
 	if !b {
@@ -374,7 +380,7 @@ func writeShortFlag(flag *pflag.Flag, w io.Writer) error {
 }
 }
 
 
 func writeFlag(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
 	name := flag.Name
 	format := "    flags+=(\"--%s"
 	format := "    flags+=(\"--%s"
 	if !b {
 	if !b {
@@ -387,9 +393,24 @@ func writeFlag(flag *pflag.Flag, w io.Writer) error {
 	return writeFlagHandler("--"+name, flag.Annotations, w)
 	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 {
 func writeFlags(cmd *Command, w io.Writer) error {
 	_, err := fmt.Fprintf(w, `    flags=()
 	_, err := fmt.Fprintf(w, `    flags=()
     two_word_flags=()
     two_word_flags=()
+    local_nonpersistent_flags=()
     flags_with_completion=()
     flags_with_completion=()
     flags_completion=()
     flags_completion=()
 
 
@@ -397,6 +418,7 @@ func writeFlags(cmd *Command, w io.Writer) error {
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
+	localNonPersistentFlags := cmd.LocalNonPersistentFlags()
 	var visitErr error
 	var visitErr error
 	cmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) {
 	cmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) {
 		if err := writeFlag(flag, w); err != nil {
 		if err := writeFlag(flag, w); err != nil {
@@ -409,6 +431,12 @@ func writeFlags(cmd *Command, w io.Writer) error {
 				return
 				return
 			}
 			}
 		}
 		}
+		if localNonPersistentFlags.Lookup(flag.Name) != nil {
+			if err := writeLocalNonPersistentFlag(flag, w); err != nil {
+				visitErr = err
+				return
+			}
+		}
 	})
 	})
 	if visitErr != nil {
 	if visitErr != nil {
 		return visitErr
 		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 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
 ```bash
 # kubectl get rc [tab][tab]
 # 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
 # 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:
 a custom flag completion function with cobra.BashCompCustom:
 
 
 ```go
 ```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
 // Set this to true to enable it
 var EnablePrefixMatching = false
 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
 //AddTemplateFunc adds a template function that's available to Usage and Help
 //template generation.
 //template generation.
 func AddTemplateFunc(name string, tmplFunc interface{}) {
 func AddTemplateFunc(name string, tmplFunc interface{}) {

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

@@ -21,6 +21,7 @@ import (
 	"io"
 	"io"
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
+	"sort"
 	"strings"
 	"strings"
 
 
 	flag "github.com/spf13/pflag"
 	flag "github.com/spf13/pflag"
@@ -105,6 +106,8 @@ type Command struct {
 	commandsMaxUseLen         int
 	commandsMaxUseLen         int
 	commandsMaxCommandPathLen int
 	commandsMaxCommandPathLen int
 	commandsMaxNameLen        int
 	commandsMaxNameLen        int
+	// is commands slice are sorted or not
+	commandsAreSorted bool
 
 
 	flagErrorBuf *bytes.Buffer
 	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.
 	// Disable the flag parsing. If this is true all flags will be passed to the command as arguments.
 	DisableFlagParsing bool
 	DisableFlagParsing bool
+
+	// TraverseChildren parses flags on all parents before executing child command
+	TraverseChildren bool
 }
 }
 
 
 // os.Args[1:] by default, if desired, can be overridden
 // os.Args[1:] by default, if desired, can be overridden
@@ -409,13 +415,14 @@ func argsMinusFirstX(args []string, x string) []string {
 	return args
 	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.
 // Meant to be run on the highest node. Only searches down.
 func (c *Command) Find(args []string) (*Command, []string, error) {
 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)
 	var innerfind func(*Command, []string) (*Command, []string)
 
 
 	innerfind = func(c *Command, innerArgs []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
 			return c, innerArgs
 		}
 		}
 		nextSubCmd := argsWOflags[0]
 		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
 		return c, innerArgs
 	}
 	}
 
 
@@ -456,6 +446,66 @@ func (c *Command) Find(args []string) (*Command, []string, error) {
 	return commandFound, a, nil
 	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 {
 func (c *Command) findSuggestions(arg string) string {
 	if c.DisableSuggestions {
 	if c.DisableSuggestions {
 		return ""
 		return ""
@@ -668,7 +718,12 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
 		args = c.args
 		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 err != nil {
 		// If found parse to a subcommand and then failed, talk about the subcommand
 		// If found parse to a subcommand and then failed, talk about the subcommand
 		if cmd != nil {
 		if cmd != nil {
@@ -680,6 +735,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
 		}
 		}
 		return c, err
 		return c, err
 	}
 	}
+
 	err = cmd.execute(flags)
 	err = cmd.execute(flags)
 	if err != nil {
 	if err != nil {
 		// Always show help if requested, even if SilenceErrors is in
 		// Always show help if requested, even if SilenceErrors is in
@@ -754,8 +810,20 @@ func (c *Command) ResetCommands() {
 	c.helpCommand = nil
 	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 {
 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
 	return c.commands
 }
 }
 
 
@@ -784,6 +852,7 @@ func (c *Command) AddCommand(cmds ...*Command) {
 			x.SetGlobalNormalizationFunc(c.globNormFunc)
 			x.SetGlobalNormalizationFunc(c.globNormFunc)
 		}
 		}
 		c.commands = append(c.commands, x)
 		c.commands = append(c.commands, x)
+		c.commandsAreSorted = false
 	}
 	}
 }
 }
 
 
@@ -953,6 +1022,20 @@ func (c *Command) HasAlias(s string) bool {
 	return false
 	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 {
 func (c *Command) NameAndAliases() string {
 	return strings.Join(append([]string{c.Name()}, c.Aliases...), ", ")
 	return strings.Join(append([]string{c.Name()}, c.Aliases...), ", ")
 }
 }
@@ -1065,6 +1148,19 @@ func (c *Command) Flags() *flag.FlagSet {
 	return c.flags
 	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
 // Get the local FlagSet specifically set in the current command
 func (c *Command) LocalFlags() *flag.FlagSet {
 func (c *Command) LocalFlags() *flag.FlagSet {
 	c.mergePersistentFlags()
 	c.mergePersistentFlags()