浏览代码

Merge pull request #13771 from tiborvass/daemon-cli

New `docker daemon` command
Jessie Frazelle 10 年之前
父节点
当前提交
7674f21686
共有 86 个文件被更改,包括 1118 次插入701 次删除
  1. 4 2
      api/client/attach.go
  2. 4 3
      api/client/build.go
  3. 77 165
      api/client/cli.go
  4. 0 12
      api/client/client.go
  5. 2 1
      api/client/commit.go
  6. 2 1
      api/client/cp.go
  7. 2 1
      api/client/create.go
  8. 2 1
      api/client/diff.go
  9. 2 1
      api/client/events.go
  10. 4 3
      api/client/exec.go
  11. 2 1
      api/client/export.go
  12. 0 34
      api/client/help.go
  13. 2 1
      api/client/history.go
  14. 2 1
      api/client/images.go
  15. 2 1
      api/client/import.go
  16. 2 1
      api/client/info.go
  17. 11 3
      api/client/inspect.go
  18. 2 1
      api/client/kill.go
  19. 2 1
      api/client/load.go
  20. 2 1
      api/client/login.go
  21. 2 1
      api/client/logout.go
  22. 2 1
      api/client/logs.go
  23. 2 1
      api/client/pause.go
  24. 2 1
      api/client/port.go
  25. 2 1
      api/client/ps.go
  26. 2 1
      api/client/pull.go
  27. 2 1
      api/client/push.go
  28. 2 1
      api/client/rename.go
  29. 2 1
      api/client/restart.go
  30. 2 1
      api/client/rm.go
  31. 2 1
      api/client/rmi.go
  32. 3 2
      api/client/run.go
  33. 2 1
      api/client/save.go
  34. 2 1
      api/client/search.go
  35. 3 2
      api/client/start.go
  36. 2 1
      api/client/stats.go
  37. 2 1
      api/client/stop.go
  38. 2 1
      api/client/tag.go
  39. 2 1
      api/client/top.go
  40. 2 1
      api/client/unpause.go
  41. 4 3
      api/client/version.go
  42. 2 1
      api/client/wait.go
  43. 200 0
      cli/cli.go
  44. 12 0
      cli/client.go
  45. 20 0
      cli/common.go
  46. 17 17
      daemon/config.go
  47. 3 3
      daemon/config_experimental.go
  48. 20 19
      daemon/config_linux.go
  49. 3 1
      daemon/config_stub.go
  50. 2 2
      daemon/config_windows.go
  51. 22 5
      docker/client.go
  52. 101 0
      docker/common.go
  53. 135 29
      docker/daemon.go
  54. 12 0
      docker/daemon_none.go
  55. 33 102
      docker/docker.go
  56. 46 124
      docker/flags.go
  57. 0 14
      docker/log.go
  58. 2 2
      docs/articles/basics.md
  59. 4 5
      docs/articles/configuring.md
  60. 1 1
      docs/articles/https.md
  61. 2 2
      docs/articles/networking.md
  62. 2 2
      docs/articles/registry_mirror.md
  63. 1 1
      docs/articles/systemd.md
  64. 1 1
      docs/installation/binaries.md
  65. 1 1
      docs/installation/ubuntulinux.md
  66. 2 2
      docs/project/set-up-dev-env.md
  67. 1 1
      docs/project/test-and-docs.md
  68. 1 1
      docs/reference/api/docker_remote_api_v1.20.md
  69. 3 0
      docs/reference/commandline/cli.md
  70. 28 31
      docs/reference/commandline/daemon.md
  71. 1 1
      docs/userguide/labels-custom-metadata.md
  72. 1 1
      integration-cli/docker_cli_build_test.go
  73. 103 15
      integration-cli/docker_cli_daemon_test.go
  74. 25 13
      integration-cli/docker_cli_help_test.go
  75. 11 0
      integration-cli/docker_cli_run_test.go
  76. 24 15
      integration-cli/docker_utils.go
  77. 7 0
      opts/hosts_unix.go
  78. 7 0
      opts/hosts_windows.go
  79. 14 11
      opts/opts.go
  80. 1 1
      opts/opts_test.go
  81. 8 5
      opts/ulimit.go
  82. 1 1
      opts/ulimit_test.go
  83. 51 1
      pkg/mflag/flag.go
  84. 11 4
      pkg/tlsconfig/config.go
  85. 3 3
      registry/config.go
  86. 1 3
      runconfig/parse.go

+ 4 - 2
api/client/attach.go

@@ -8,6 +8,7 @@ import (
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/signal"
 	"github.com/docker/docker/pkg/signal"
 )
 )
@@ -16,9 +17,10 @@ import (
 //
 //
 // Usage: docker attach [OPTIONS] CONTAINER
 // Usage: docker attach [OPTIONS] CONTAINER
 func (cli *DockerCli) CmdAttach(args ...string) error {
 func (cli *DockerCli) CmdAttach(args ...string) error {
-	cmd := cli.Subcmd("attach", []string{"CONTAINER"}, "Attach to a running container", true)
+	cmd := Cli.Subcmd("attach", []string{"CONTAINER"}, "Attach to a running container", true)
 	noStdin := cmd.Bool([]string{"#nostdin", "-no-stdin"}, false, "Do not attach STDIN")
 	noStdin := cmd.Bool([]string{"#nostdin", "-no-stdin"}, false, "Do not attach STDIN")
 	proxy := cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy all received signals to the process")
 	proxy := cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy all received signals to the process")
+
 	cmd.Require(flag.Exact, 1)
 	cmd.Require(flag.Exact, 1)
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)
@@ -75,7 +77,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
 		return err
 		return err
 	}
 	}
 	if status != 0 {
 	if status != 0 {
-		return StatusError{StatusCode: status}
+		return Cli.StatusError{StatusCode: status}
 	}
 	}
 
 
 	return nil
 	return nil

+ 4 - 3
api/client/build.go

@@ -18,6 +18,7 @@ import (
 	"strings"
 	"strings"
 
 
 	"github.com/docker/docker/api"
 	"github.com/docker/docker/api"
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/graph/tags"
 	"github.com/docker/docker/graph/tags"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/archive"
@@ -46,7 +47,7 @@ const (
 //
 //
 // Usage: docker build [OPTIONS] PATH | URL | -
 // Usage: docker build [OPTIONS] PATH | URL | -
 func (cli *DockerCli) CmdBuild(args ...string) error {
 func (cli *DockerCli) CmdBuild(args ...string) error {
-	cmd := cli.Subcmd("build", []string{"PATH | URL | -"}, "Build a new image from the source code at PATH", true)
+	cmd := Cli.Subcmd("build", []string{"PATH | URL | -"}, "Build a new image from the source code at PATH", true)
 	tag := cmd.String([]string{"t", "-tag"}, "", "Repository name (and optionally a tag) for the image")
 	tag := cmd.String([]string{"t", "-tag"}, "", "Repository name (and optionally a tag) for the image")
 	suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the verbose output generated by the containers")
 	suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the verbose output generated by the containers")
 	noCache := cmd.Bool([]string{"#no-cache", "-no-cache"}, false, "Do not use cache when building the image")
 	noCache := cmd.Bool([]string{"#no-cache", "-no-cache"}, false, "Do not use cache when building the image")
@@ -64,7 +65,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 	flCgroupParent := cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container")
 	flCgroupParent := cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container")
 
 
 	ulimits := make(map[string]*ulimit.Ulimit)
 	ulimits := make(map[string]*ulimit.Ulimit)
-	flUlimits := opts.NewUlimitOpt(ulimits)
+	flUlimits := opts.NewUlimitOpt(&ulimits)
 	cmd.Var(flUlimits, []string{"-ulimit"}, "Ulimit options")
 	cmd.Var(flUlimits, []string{"-ulimit"}, "Ulimit options")
 
 
 	cmd.Require(flag.Exact, 1)
 	cmd.Require(flag.Exact, 1)
@@ -325,7 +326,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 		if jerr.Code == 0 {
 		if jerr.Code == 0 {
 			jerr.Code = 1
 			jerr.Code = 1
 		}
 		}
-		return StatusError{Status: jerr.Message, StatusCode: jerr.Code}
+		return Cli.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
 	}
 	}
 	return err
 	return err
 }
 }

+ 77 - 165
api/client/cli.go

@@ -2,25 +2,28 @@ package client
 
 
 import (
 import (
 	"crypto/tls"
 	"crypto/tls"
-	"encoding/json"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
 	"net/http"
 	"net/http"
 	"net/url"
 	"net/url"
-	"reflect"
+	"os"
 	"strings"
 	"strings"
-	"text/template"
 
 
+	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/cliconfig"
-	flag "github.com/docker/docker/pkg/mflag"
+	"github.com/docker/docker/opts"
 	"github.com/docker/docker/pkg/sockets"
 	"github.com/docker/docker/pkg/sockets"
 	"github.com/docker/docker/pkg/term"
 	"github.com/docker/docker/pkg/term"
+	"github.com/docker/docker/pkg/tlsconfig"
 )
 )
 
 
 // DockerCli represents the docker command line client.
 // DockerCli represents the docker command line client.
 // Instances of the client can be returned from NewDockerCli.
 // Instances of the client can be returned from NewDockerCli.
 type DockerCli struct {
 type DockerCli struct {
+	// initializing closure
+	init func() error
+
 	// proto holds the client protocol i.e. unix.
 	// proto holds the client protocol i.e. unix.
 	proto string
 	proto string
 	// addr holds the client address.
 	// addr holds the client address.
@@ -55,116 +58,11 @@ type DockerCli struct {
 	transport *http.Transport
 	transport *http.Transport
 }
 }
 
 
-var funcMap = template.FuncMap{
-	"json": func(v interface{}) string {
-		a, _ := json.Marshal(v)
-		return string(a)
-	},
-}
-
-func (cli *DockerCli) Out() io.Writer {
-	return cli.out
-}
-
-func (cli *DockerCli) Err() io.Writer {
-	return cli.err
-}
-
-func (cli *DockerCli) getMethod(args ...string) (func(...string) error, bool) {
-	camelArgs := make([]string, len(args))
-	for i, s := range args {
-		if len(s) == 0 {
-			return nil, false
-		}
-		camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:])
-	}
-	methodName := "Cmd" + strings.Join(camelArgs, "")
-	method := reflect.ValueOf(cli).MethodByName(methodName)
-	if !method.IsValid() {
-		return nil, false
-	}
-	return method.Interface().(func(...string) error), true
-}
-
-// Cmd executes the specified command.
-func (cli *DockerCli) Cmd(args ...string) error {
-	if len(args) > 1 {
-		method, exists := cli.getMethod(args[:2]...)
-		if exists {
-			return method(args[2:]...)
-		}
-	}
-	if len(args) > 0 {
-		method, exists := cli.getMethod(args[0])
-		if !exists {
-			return fmt.Errorf("docker: '%s' is not a docker command.\nSee 'docker --help'.", args[0])
-		}
-		return method(args[1:]...)
+func (cli *DockerCli) Initialize() error {
+	if cli.init == nil {
+		return nil
 	}
 	}
-	return cli.CmdHelp()
-}
-
-// Subcmd is a subcommand of the main "docker" command.
-// A subcommand represents an action that can be performed
-// from the Docker command line client.
-//
-// Multiple subcommand synopses may be provided with one 'Usage' line being
-// printed for each in the following way:
-//
-//	Usage:	docker <subcmd-name> [OPTIONS] <synopsis 0>
-// 		docker <subcmd-name> [OPTIONS] <synopsis 1>
-// 		...
-//
-// If no undeprecated flags are added to the returned FlagSet, "[OPTIONS]" will
-// not be included on the usage synopsis lines. If no synopses are given, only
-// one usage synopsis line will be printed with nothing following the
-// "[OPTIONS]" section
-//
-// To see all available subcommands, run "docker --help".
-func (cli *DockerCli) 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() {
-		options := ""
-		if flags.FlagCountUndeprecated() > 0 {
-			options = " [OPTIONS]"
-		}
-
-		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%s", lead, name, options, synopsis)
-		}
-
-		fmt.Fprintf(flags.Out(), "\n\n%s\n", description)
-	}
-
-	return flags
+	return cli.init()
 }
 }
 
 
 // CheckTtyInput checks if we are trying to attach to a container tty
 // CheckTtyInput checks if we are trying to attach to a container tty
@@ -187,64 +85,78 @@ func (cli *DockerCli) PsFormat() string {
 // The key file, protocol (i.e. unix) and address are passed in as strings, along with the tls.Config. If the tls.Config
 // 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.
 // 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).
 // 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, keyFile string, proto, addr string, tlsConfig *tls.Config) *DockerCli {
-	var (
-		inFd          uintptr
-		outFd         uintptr
-		isTerminalIn  = false
-		isTerminalOut = false
-		scheme        = "http"
-		basePath      = ""
-	)
-
-	if tlsConfig != nil {
-		scheme = "https"
-	}
-	if in != nil {
-		inFd, isTerminalIn = term.GetFdInfo(in)
+func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientFlags) *DockerCli {
+	cli := &DockerCli{
+		in:      in,
+		out:     out,
+		err:     err,
+		keyFile: clientFlags.Common.TrustKey,
 	}
 	}
 
 
-	if out != nil {
-		outFd, isTerminalOut = term.GetFdInfo(out)
-	}
+	cli.init = func() error {
+		clientFlags.PostParse()
 
 
-	if err == nil {
-		err = out
-	}
+		hosts := clientFlags.Common.Hosts
 
 
-	// The transport is created here for reuse during the client session.
-	tr := &http.Transport{
-		TLSClientConfig: tlsConfig,
-	}
-	sockets.ConfigureTCPTransport(tr, proto, addr)
+		switch len(hosts) {
+		case 0:
+			defaultHost := os.Getenv("DOCKER_HOST")
+			if defaultHost == "" {
+				defaultHost = opts.DefaultHost
+			}
+			defaultHost, err := opts.ValidateHost(defaultHost)
+			if err != nil {
+				return err
+			}
+			hosts = []string{defaultHost}
+		case 1:
+			// only accept one host to talk to
+		default:
+			return errors.New("Please specify only one -H")
+		}
 
 
-	configFile, e := cliconfig.Load(cliconfig.ConfigDir())
-	if e != nil {
-		fmt.Fprintf(err, "WARNING: Error loading config file:%v\n", e)
-	}
+		protoAddrParts := strings.SplitN(hosts[0], "://", 2)
+		cli.proto, cli.addr = protoAddrParts[0], protoAddrParts[1]
 
 
-	if proto == "tcp" {
-		// error is checked in pkg/parsers already
-		parsed, _ := url.Parse("tcp://" + addr)
-		addr = parsed.Host
-		basePath = parsed.Path
-	}
+		if cli.proto == "tcp" {
+			// error is checked in pkg/parsers already
+			parsed, _ := url.Parse("tcp://" + cli.addr)
+			cli.addr = parsed.Host
+			cli.basePath = parsed.Path
+		}
 
 
-	return &DockerCli{
-		proto:         proto,
-		addr:          addr,
-		basePath:      basePath,
-		configFile:    configFile,
-		in:            in,
-		out:           out,
-		err:           err,
-		keyFile:       keyFile,
-		inFd:          inFd,
-		outFd:         outFd,
-		isTerminalIn:  isTerminalIn,
-		isTerminalOut: isTerminalOut,
-		tlsConfig:     tlsConfig,
-		scheme:        scheme,
-		transport:     tr,
+		if clientFlags.Common.TLSOptions != nil {
+			cli.scheme = "https"
+			var e error
+			cli.tlsConfig, e = tlsconfig.Client(*clientFlags.Common.TLSOptions)
+			if e != nil {
+				return e
+			}
+		} else {
+			cli.scheme = "http"
+		}
+
+		if cli.in != nil {
+			cli.inFd, cli.isTerminalIn = term.GetFdInfo(cli.in)
+		}
+		if cli.out != nil {
+			cli.outFd, cli.isTerminalOut = term.GetFdInfo(cli.out)
+		}
+
+		// The transport is created here for reuse during the client session.
+		cli.transport = &http.Transport{
+			TLSClientConfig: cli.tlsConfig,
+		}
+		sockets.ConfigureTCPTransport(cli.transport, cli.proto, cli.addr)
+
+		configFile, e := cliconfig.Load(cliconfig.ConfigDir())
+		if e != nil {
+			fmt.Fprintf(cli.err, "WARNING: Error loading config file:%v\n", e)
+		}
+		cli.configFile = configFile
+
+		return nil
 	}
 	}
+
+	return cli
 }
 }

+ 0 - 12
api/client/client.go

@@ -3,15 +3,3 @@
 // Run "docker help SUBCOMMAND" or "docker SUBCOMMAND --help" to see more information on any Docker subcommand, including the full list of options supported for the subcommand.
 // Run "docker help SUBCOMMAND" or "docker SUBCOMMAND --help" to see more information on any Docker subcommand, including the full list of options supported for the subcommand.
 // See https://docs.docker.com/installation/ for instructions on installing Docker.
 // See https://docs.docker.com/installation/ for instructions on installing Docker.
 package client
 package client
-
-import "fmt"
-
-// An 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)
-}

+ 2 - 1
api/client/commit.go

@@ -6,6 +6,7 @@ import (
 	"net/url"
 	"net/url"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/opts"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/parsers"
@@ -17,7 +18,7 @@ import (
 //
 //
 // Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
 // Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
 func (cli *DockerCli) CmdCommit(args ...string) error {
 func (cli *DockerCli) CmdCommit(args ...string) error {
-	cmd := cli.Subcmd("commit", []string{"CONTAINER [REPOSITORY[:TAG]]"}, "Create a new image from a container's changes", true)
+	cmd := Cli.Subcmd("commit", []string{"CONTAINER [REPOSITORY[:TAG]]"}, "Create a new image from a container's changes", true)
 	flPause := cmd.Bool([]string{"p", "-pause"}, true, "Pause container during commit")
 	flPause := cmd.Bool([]string{"p", "-pause"}, true, "Pause container during commit")
 	flComment := cmd.String([]string{"m", "-message"}, "", "Commit message")
 	flComment := cmd.String([]string{"m", "-message"}, "", "Commit message")
 	flAuthor := cmd.String([]string{"a", "#author", "-author"}, "", "Author (e.g., \"John Hannibal Smith <hannibal@a-team.com>\")")
 	flAuthor := cmd.String([]string{"a", "#author", "-author"}, "", "Author (e.g., \"John Hannibal Smith <hannibal@a-team.com>\")")

+ 2 - 1
api/client/cp.go

@@ -12,6 +12,7 @@ import (
 	"strings"
 	"strings"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/archive"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 )
@@ -37,7 +38,7 @@ const (
 // 	docker cp CONTAINER:PATH LOCALPATH|-
 // 	docker cp CONTAINER:PATH LOCALPATH|-
 // 	docker cp LOCALPATH|- CONTAINER:PATH
 // 	docker cp LOCALPATH|- CONTAINER:PATH
 func (cli *DockerCli) CmdCp(args ...string) error {
 func (cli *DockerCli) CmdCp(args ...string) error {
-	cmd := cli.Subcmd(
+	cmd := Cli.Subcmd(
 		"cp",
 		"cp",
 		[]string{"CONTAINER:PATH LOCALPATH|-", "LOCALPATH|- CONTAINER:PATH"},
 		[]string{"CONTAINER:PATH LOCALPATH|-", "LOCALPATH|- CONTAINER:PATH"},
 		strings.Join([]string{
 		strings.Join([]string{

+ 2 - 1
api/client/create.go

@@ -10,6 +10,7 @@ import (
 	"strings"
 	"strings"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/graph/tags"
 	"github.com/docker/docker/graph/tags"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
@@ -137,7 +138,7 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc
 //
 //
 // Usage: docker create [OPTIONS] IMAGE [COMMAND] [ARG...]
 // Usage: docker create [OPTIONS] IMAGE [COMMAND] [ARG...]
 func (cli *DockerCli) CmdCreate(args ...string) error {
 func (cli *DockerCli) CmdCreate(args ...string) error {
-	cmd := cli.Subcmd("create", []string{"IMAGE [COMMAND] [ARG...]"}, "Create a new container", true)
+	cmd := Cli.Subcmd("create", []string{"IMAGE [COMMAND] [ARG...]"}, "Create a new container", true)
 
 
 	// These are flags not stored in Config/HostConfig
 	// These are flags not stored in Config/HostConfig
 	var (
 	var (

+ 2 - 1
api/client/diff.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"fmt"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/archive"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 )
@@ -17,7 +18,7 @@ import (
 //
 //
 // Usage: docker diff CONTAINER
 // Usage: docker diff CONTAINER
 func (cli *DockerCli) CmdDiff(args ...string) error {
 func (cli *DockerCli) CmdDiff(args ...string) error {
-	cmd := cli.Subcmd("diff", []string{"CONTAINER"}, "Inspect changes on a container's filesystem", true)
+	cmd := Cli.Subcmd("diff", []string{"CONTAINER"}, "Inspect changes on a container's filesystem", true)
 	cmd.Require(flag.Exact, 1)
 	cmd.Require(flag.Exact, 1)
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)

+ 2 - 1
api/client/events.go

@@ -4,6 +4,7 @@ import (
 	"net/url"
 	"net/url"
 	"time"
 	"time"
 
 
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/opts"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/parsers/filters"
 	"github.com/docker/docker/pkg/parsers/filters"
@@ -14,7 +15,7 @@ import (
 //
 //
 // Usage: docker events [OPTIONS]
 // Usage: docker events [OPTIONS]
 func (cli *DockerCli) CmdEvents(args ...string) error {
 func (cli *DockerCli) CmdEvents(args ...string) error {
-	cmd := cli.Subcmd("events", nil, "Get real time events from the server", true)
+	cmd := Cli.Subcmd("events", nil, "Get real time events from the server", true)
 	since := cmd.String([]string{"#since", "-since"}, "", "Show all events created since timestamp")
 	since := cmd.String([]string{"#since", "-since"}, "", "Show all events created since timestamp")
 	until := cmd.String([]string{"-until"}, "", "Stream events until this timestamp")
 	until := cmd.String([]string{"-until"}, "", "Stream events until this timestamp")
 	flFilter := opts.NewListOpts(nil)
 	flFilter := opts.NewListOpts(nil)

+ 4 - 3
api/client/exec.go

@@ -7,6 +7,7 @@ import (
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/pkg/promise"
 	"github.com/docker/docker/pkg/promise"
 	"github.com/docker/docker/runconfig"
 	"github.com/docker/docker/runconfig"
 )
 )
@@ -15,12 +16,12 @@ import (
 //
 //
 // Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
 // Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
 func (cli *DockerCli) CmdExec(args ...string) error {
 func (cli *DockerCli) CmdExec(args ...string) error {
-	cmd := cli.Subcmd("exec", []string{"CONTAINER COMMAND [ARG...]"}, "Run a command in a running container", true)
+	cmd := Cli.Subcmd("exec", []string{"CONTAINER COMMAND [ARG...]"}, "Run a command in a running container", true)
 
 
 	execConfig, err := runconfig.ParseExec(cmd, args)
 	execConfig, err := runconfig.ParseExec(cmd, args)
 	// just in case the ParseExec does not exit
 	// just in case the ParseExec does not exit
 	if execConfig.Container == "" || err != nil {
 	if execConfig.Container == "" || err != nil {
-		return StatusError{StatusCode: 1}
+		return Cli.StatusError{StatusCode: 1}
 	}
 	}
 
 
 	serverResp, err := cli.call("POST", "/containers/"+execConfig.Container+"/exec", execConfig, nil)
 	serverResp, err := cli.call("POST", "/containers/"+execConfig.Container+"/exec", execConfig, nil)
@@ -126,7 +127,7 @@ func (cli *DockerCli) CmdExec(args ...string) error {
 	}
 	}
 
 
 	if status != 0 {
 	if status != 0 {
-		return StatusError{StatusCode: status}
+		return Cli.StatusError{StatusCode: status}
 	}
 	}
 
 
 	return nil
 	return nil

+ 2 - 1
api/client/export.go

@@ -5,6 +5,7 @@ import (
 	"io"
 	"io"
 	"os"
 	"os"
 
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 )
 
 
@@ -14,7 +15,7 @@ import (
 //
 //
 // Usage: docker export [OPTIONS] CONTAINER
 // Usage: docker export [OPTIONS] CONTAINER
 func (cli *DockerCli) CmdExport(args ...string) error {
 func (cli *DockerCli) CmdExport(args ...string) error {
-	cmd := cli.Subcmd("export", []string{"CONTAINER"}, "Export the contents of a container's filesystem as a tar archive", true)
+	cmd := Cli.Subcmd("export", []string{"CONTAINER"}, "Export the contents of a container's filesystem as a tar archive", true)
 	outfile := cmd.String([]string{"o", "-output"}, "", "Write to a file, instead of STDOUT")
 	outfile := cmd.String([]string{"o", "-output"}, "", "Write to a file, instead of STDOUT")
 	cmd.Require(flag.Exact, 1)
 	cmd.Require(flag.Exact, 1)
 
 

+ 0 - 34
api/client/help.go

@@ -1,34 +0,0 @@
-package client
-
-import (
-	"fmt"
-
-	flag "github.com/docker/docker/pkg/mflag"
-)
-
-// 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 *DockerCli) CmdHelp(args ...string) error {
-	if len(args) > 1 {
-		method, exists := cli.getMethod(args[:2]...)
-		if exists {
-			method("--help")
-			return nil
-		}
-	}
-	if len(args) > 0 {
-		method, exists := cli.getMethod(args[0])
-		if !exists {
-			return fmt.Errorf("docker: '%s' is not a docker command. See 'docker --help'.", args[0])
-		}
-		method("--help")
-		return nil
-	}
-
-	flag.Usage()
-
-	return nil
-}

+ 2 - 1
api/client/history.go

@@ -7,6 +7,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringutils"
 	"github.com/docker/docker/pkg/stringutils"
@@ -17,7 +18,7 @@ import (
 //
 //
 // Usage: docker history [OPTIONS] IMAGE
 // Usage: docker history [OPTIONS] IMAGE
 func (cli *DockerCli) CmdHistory(args ...string) error {
 func (cli *DockerCli) CmdHistory(args ...string) error {
-	cmd := cli.Subcmd("history", []string{"IMAGE"}, "Show the history of an image", true)
+	cmd := Cli.Subcmd("history", []string{"IMAGE"}, "Show the history of an image", true)
 	human := cmd.Bool([]string{"H", "-human"}, true, "Print sizes and dates in human readable format")
 	human := cmd.Bool([]string{"H", "-human"}, true, "Print sizes and dates in human readable format")
 	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
 	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
 	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
 	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")

+ 2 - 1
api/client/images.go

@@ -8,6 +8,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/opts"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/parsers"
@@ -21,7 +22,7 @@ import (
 //
 //
 // Usage: docker images [OPTIONS] [REPOSITORY]
 // Usage: docker images [OPTIONS] [REPOSITORY]
 func (cli *DockerCli) CmdImages(args ...string) error {
 func (cli *DockerCli) CmdImages(args ...string) error {
-	cmd := cli.Subcmd("images", []string{"[REPOSITORY]"}, "List images", true)
+	cmd := Cli.Subcmd("images", []string{"[REPOSITORY]"}, "List images", true)
 	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
 	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
 	all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (default hides intermediate images)")
 	all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (default hides intermediate images)")
 	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
 	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")

+ 2 - 1
api/client/import.go

@@ -6,6 +6,7 @@ import (
 	"net/url"
 	"net/url"
 	"os"
 	"os"
 
 
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/opts"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/parsers"
@@ -19,7 +20,7 @@ import (
 //
 //
 // Usage: docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]
 // Usage: docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]
 func (cli *DockerCli) CmdImport(args ...string) error {
 func (cli *DockerCli) CmdImport(args ...string) error {
-	cmd := cli.Subcmd("import", []string{"file|URL|- [REPOSITORY[:TAG]]"}, "Create an empty filesystem image and import the contents of the\ntarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then\noptionally tag it.", true)
+	cmd := Cli.Subcmd("import", []string{"file|URL|- [REPOSITORY[:TAG]]"}, "Create an empty filesystem image and import the contents of the\ntarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then\noptionally tag it.", true)
 	flChanges := opts.NewListOpts(nil)
 	flChanges := opts.NewListOpts(nil)
 	cmd.Var(&flChanges, []string{"c", "-change"}, "Apply Dockerfile instruction to the created image")
 	cmd.Var(&flChanges, []string{"c", "-change"}, "Apply Dockerfile instruction to the created image")
 	cmd.Require(flag.Min, 1)
 	cmd.Require(flag.Min, 1)

+ 2 - 1
api/client/info.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"fmt"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/pkg/httputils"
 	"github.com/docker/docker/pkg/httputils"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/ioutils"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
@@ -15,7 +16,7 @@ import (
 //
 //
 // Usage: docker info
 // Usage: docker info
 func (cli *DockerCli) CmdInfo(args ...string) error {
 func (cli *DockerCli) CmdInfo(args ...string) error {
-	cmd := cli.Subcmd("info", nil, "Display system-wide information", true)
+	cmd := Cli.Subcmd("info", nil, "Display system-wide information", true)
 	cmd.Require(flag.Exact, 0)
 	cmd.Require(flag.Exact, 0)
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)

+ 11 - 3
api/client/inspect.go

@@ -9,14 +9,22 @@ import (
 	"text/template"
 	"text/template"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 )
 
 
+var funcMap = template.FuncMap{
+	"json": func(v interface{}) string {
+		a, _ := json.Marshal(v)
+		return string(a)
+	},
+}
+
 // CmdInspect displays low-level information on one or more containers or images.
 // CmdInspect displays low-level information on one or more containers or images.
 //
 //
 // Usage: docker inspect [OPTIONS] CONTAINER|IMAGE [CONTAINER|IMAGE...]
 // Usage: docker inspect [OPTIONS] CONTAINER|IMAGE [CONTAINER|IMAGE...]
 func (cli *DockerCli) CmdInspect(args ...string) error {
 func (cli *DockerCli) CmdInspect(args ...string) error {
-	cmd := cli.Subcmd("inspect", []string{"CONTAINER|IMAGE [CONTAINER|IMAGE...]"}, "Return low-level information on a container or image", true)
+	cmd := Cli.Subcmd("inspect", []string{"CONTAINER|IMAGE [CONTAINER|IMAGE...]"}, "Return low-level information on a container or image", true)
 	tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template")
 	tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template")
 	inspectType := cmd.String([]string{"-type"}, "", "Return JSON for specified type, (e.g image or container)")
 	inspectType := cmd.String([]string{"-type"}, "", "Return JSON for specified type, (e.g image or container)")
 	cmd.Require(flag.Min, 1)
 	cmd.Require(flag.Min, 1)
@@ -29,7 +37,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
 
 
 	if *tmplStr != "" {
 	if *tmplStr != "" {
 		if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil {
 		if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil {
-			return StatusError{StatusCode: 64,
+			return Cli.StatusError{StatusCode: 64,
 				Status: "Template parsing error: " + err.Error()}
 				Status: "Template parsing error: " + err.Error()}
 		}
 		}
 	}
 	}
@@ -143,7 +151,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
 	}
 	}
 
 
 	if status != 0 {
 	if status != 0 {
-		return StatusError{StatusCode: status}
+		return Cli.StatusError{StatusCode: status}
 	}
 	}
 	return nil
 	return nil
 }
 }

+ 2 - 1
api/client/kill.go

@@ -3,6 +3,7 @@ package client
 import (
 import (
 	"fmt"
 	"fmt"
 
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 )
 
 
@@ -10,7 +11,7 @@ import (
 //
 //
 // Usage: docker kill [OPTIONS] CONTAINER [CONTAINER...]
 // Usage: docker kill [OPTIONS] CONTAINER [CONTAINER...]
 func (cli *DockerCli) CmdKill(args ...string) error {
 func (cli *DockerCli) CmdKill(args ...string) error {
-	cmd := cli.Subcmd("kill", []string{"CONTAINER [CONTAINER...]"}, "Kill a running container using SIGKILL or a specified signal", true)
+	cmd := Cli.Subcmd("kill", []string{"CONTAINER [CONTAINER...]"}, "Kill a running container using SIGKILL or a specified signal", true)
 	signal := cmd.String([]string{"s", "-signal"}, "KILL", "Signal to send to the container")
 	signal := cmd.String([]string{"s", "-signal"}, "KILL", "Signal to send to the container")
 	cmd.Require(flag.Min, 1)
 	cmd.Require(flag.Min, 1)
 
 

+ 2 - 1
api/client/load.go

@@ -4,6 +4,7 @@ import (
 	"io"
 	"io"
 	"os"
 	"os"
 
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 )
 
 
@@ -13,7 +14,7 @@ import (
 //
 //
 // Usage: docker load [OPTIONS]
 // Usage: docker load [OPTIONS]
 func (cli *DockerCli) CmdLoad(args ...string) error {
 func (cli *DockerCli) CmdLoad(args ...string) error {
-	cmd := cli.Subcmd("load", nil, "Load an image from a tar archive or STDIN", true)
+	cmd := Cli.Subcmd("load", nil, "Load an image from a tar archive or STDIN", true)
 	infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN")
 	infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN")
 	cmd.Require(flag.Exact, 0)
 	cmd.Require(flag.Exact, 0)
 
 

+ 2 - 1
api/client/login.go

@@ -9,6 +9,7 @@ import (
 	"strings"
 	"strings"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/cliconfig"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/term"
 	"github.com/docker/docker/pkg/term"
@@ -21,7 +22,7 @@ import (
 //
 //
 // Usage: docker login SERVER
 // Usage: docker login SERVER
 func (cli *DockerCli) CmdLogin(args ...string) error {
 func (cli *DockerCli) CmdLogin(args ...string) error {
-	cmd := cli.Subcmd("login", []string{"[SERVER]"}, "Register or log in to a Docker registry server, if no server is\nspecified \""+registry.INDEXSERVER+"\" is the default.", true)
+	cmd := Cli.Subcmd("login", []string{"[SERVER]"}, "Register or log in to a Docker registry server, if no server is\nspecified \""+registry.INDEXSERVER+"\" is the default.", true)
 	cmd.Require(flag.Max, 1)
 	cmd.Require(flag.Max, 1)
 
 
 	var username, password, email string
 	var username, password, email string

+ 2 - 1
api/client/logout.go

@@ -3,6 +3,7 @@ package client
 import (
 import (
 	"fmt"
 	"fmt"
 
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
 )
 )
@@ -13,7 +14,7 @@ import (
 //
 //
 // Usage: docker logout [SERVER]
 // Usage: docker logout [SERVER]
 func (cli *DockerCli) CmdLogout(args ...string) error {
 func (cli *DockerCli) CmdLogout(args ...string) error {
-	cmd := cli.Subcmd("logout", []string{"[SERVER]"}, "Log out from a Docker registry, if no server is\nspecified \""+registry.INDEXSERVER+"\" is the default.", true)
+	cmd := Cli.Subcmd("logout", []string{"[SERVER]"}, "Log out from a Docker registry, if no server is\nspecified \""+registry.INDEXSERVER+"\" is the default.", true)
 	cmd.Require(flag.Max, 1)
 	cmd.Require(flag.Max, 1)
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)

+ 2 - 1
api/client/logs.go

@@ -7,6 +7,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/timeutils"
 	"github.com/docker/docker/pkg/timeutils"
 )
 )
@@ -15,7 +16,7 @@ import (
 //
 //
 // docker logs [OPTIONS] CONTAINER
 // docker logs [OPTIONS] CONTAINER
 func (cli *DockerCli) CmdLogs(args ...string) error {
 func (cli *DockerCli) CmdLogs(args ...string) error {
-	cmd := cli.Subcmd("logs", []string{"CONTAINER"}, "Fetch the logs of a container", true)
+	cmd := Cli.Subcmd("logs", []string{"CONTAINER"}, "Fetch the logs of a container", true)
 	follow := cmd.Bool([]string{"f", "-follow"}, false, "Follow log output")
 	follow := cmd.Bool([]string{"f", "-follow"}, false, "Follow log output")
 	since := cmd.String([]string{"-since"}, "", "Show logs since timestamp")
 	since := cmd.String([]string{"-since"}, "", "Show logs since timestamp")
 	times := cmd.Bool([]string{"t", "-timestamps"}, false, "Show timestamps")
 	times := cmd.Bool([]string{"t", "-timestamps"}, false, "Show timestamps")

+ 2 - 1
api/client/pause.go

@@ -3,6 +3,7 @@ package client
 import (
 import (
 	"fmt"
 	"fmt"
 
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 )
 
 
@@ -10,7 +11,7 @@ import (
 //
 //
 // Usage: docker pause CONTAINER [CONTAINER...]
 // Usage: docker pause CONTAINER [CONTAINER...]
 func (cli *DockerCli) CmdPause(args ...string) error {
 func (cli *DockerCli) CmdPause(args ...string) error {
-	cmd := cli.Subcmd("pause", []string{"CONTAINER [CONTAINER...]"}, "Pause all processes within a container", true)
+	cmd := Cli.Subcmd("pause", []string{"CONTAINER [CONTAINER...]"}, "Pause all processes within a container", true)
 	cmd.Require(flag.Min, 1)
 	cmd.Require(flag.Min, 1)
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)

+ 2 - 1
api/client/port.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"fmt"
 	"strings"
 	"strings"
 
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/nat"
 	"github.com/docker/docker/pkg/nat"
 )
 )
@@ -14,7 +15,7 @@ import (
 //
 //
 // Usage: docker port CONTAINER [PRIVATE_PORT[/PROTO]]
 // Usage: docker port CONTAINER [PRIVATE_PORT[/PROTO]]
 func (cli *DockerCli) CmdPort(args ...string) error {
 func (cli *DockerCli) CmdPort(args ...string) error {
-	cmd := cli.Subcmd("port", []string{"CONTAINER [PRIVATE_PORT[/PROTO]]"}, "List port mappings for the CONTAINER, or lookup the public-facing port that\nis NAT-ed to the PRIVATE_PORT", true)
+	cmd := Cli.Subcmd("port", []string{"CONTAINER [PRIVATE_PORT[/PROTO]]"}, "List port mappings for the CONTAINER, or lookup the public-facing port that\nis NAT-ed to the PRIVATE_PORT", true)
 	cmd.Require(flag.Min, 1)
 	cmd.Require(flag.Min, 1)
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)

+ 2 - 1
api/client/ps.go

@@ -7,6 +7,7 @@ import (
 
 
 	"github.com/docker/docker/api/client/ps"
 	"github.com/docker/docker/api/client/ps"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/opts"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/parsers/filters"
 	"github.com/docker/docker/pkg/parsers/filters"
@@ -22,7 +23,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
 		psFilterArgs = filters.Args{}
 		psFilterArgs = filters.Args{}
 		v            = url.Values{}
 		v            = url.Values{}
 
 
-		cmd      = cli.Subcmd("ps", nil, "List containers", true)
+		cmd      = Cli.Subcmd("ps", nil, "List containers", true)
 		quiet    = cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
 		quiet    = cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
 		size     = cmd.Bool([]string{"s", "-size"}, false, "Display total file sizes")
 		size     = cmd.Bool([]string{"s", "-size"}, false, "Display total file sizes")
 		all      = cmd.Bool([]string{"a", "-all"}, false, "Show all containers (default shows just running)")
 		all      = cmd.Bool([]string{"a", "-all"}, false, "Show all containers (default shows just running)")

+ 2 - 1
api/client/pull.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"fmt"
 	"net/url"
 	"net/url"
 
 
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/graph/tags"
 	"github.com/docker/docker/graph/tags"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/parsers"
@@ -15,7 +16,7 @@ import (
 //
 //
 // Usage: docker pull [OPTIONS] IMAGENAME[:TAG|@DIGEST]
 // Usage: docker pull [OPTIONS] IMAGENAME[:TAG|@DIGEST]
 func (cli *DockerCli) CmdPull(args ...string) error {
 func (cli *DockerCli) CmdPull(args ...string) error {
-	cmd := cli.Subcmd("pull", []string{"NAME[:TAG|@DIGEST]"}, "Pull an image or a repository from a registry", true)
+	cmd := Cli.Subcmd("pull", []string{"NAME[:TAG|@DIGEST]"}, "Pull an image or a repository from a registry", true)
 	allTags := cmd.Bool([]string{"a", "-all-tags"}, false, "Download all tagged images in the repository")
 	allTags := cmd.Bool([]string{"a", "-all-tags"}, false, "Download all tagged images in the repository")
 	cmd.Require(flag.Exact, 1)
 	cmd.Require(flag.Exact, 1)
 
 

+ 2 - 1
api/client/push.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"fmt"
 	"net/url"
 	"net/url"
 
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
@@ -13,7 +14,7 @@ import (
 //
 //
 // Usage: docker push NAME[:TAG]
 // Usage: docker push NAME[:TAG]
 func (cli *DockerCli) CmdPush(args ...string) error {
 func (cli *DockerCli) CmdPush(args ...string) error {
-	cmd := cli.Subcmd("push", []string{"NAME[:TAG]"}, "Push an image or a repository to a registry", true)
+	cmd := Cli.Subcmd("push", []string{"NAME[:TAG]"}, "Push an image or a repository to a registry", true)
 	cmd.Require(flag.Exact, 1)
 	cmd.Require(flag.Exact, 1)
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)

+ 2 - 1
api/client/rename.go

@@ -3,6 +3,7 @@ package client
 import (
 import (
 	"fmt"
 	"fmt"
 
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 )
 
 
@@ -10,7 +11,7 @@ import (
 //
 //
 // Usage: docker rename OLD_NAME NEW_NAME
 // Usage: docker rename OLD_NAME NEW_NAME
 func (cli *DockerCli) CmdRename(args ...string) error {
 func (cli *DockerCli) CmdRename(args ...string) error {
-	cmd := cli.Subcmd("rename", []string{"OLD_NAME NEW_NAME"}, "Rename a container", true)
+	cmd := Cli.Subcmd("rename", []string{"OLD_NAME NEW_NAME"}, "Rename a container", true)
 	cmd.Require(flag.Exact, 2)
 	cmd.Require(flag.Exact, 2)
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)

+ 2 - 1
api/client/restart.go

@@ -5,6 +5,7 @@ import (
 	"net/url"
 	"net/url"
 	"strconv"
 	"strconv"
 
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 )
 
 
@@ -12,7 +13,7 @@ import (
 //
 //
 // Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...]
 // Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...]
 func (cli *DockerCli) CmdRestart(args ...string) error {
 func (cli *DockerCli) CmdRestart(args ...string) error {
-	cmd := cli.Subcmd("restart", []string{"CONTAINER [CONTAINER...]"}, "Restart a running container", true)
+	cmd := Cli.Subcmd("restart", []string{"CONTAINER [CONTAINER...]"}, "Restart a running container", true)
 	nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Seconds to wait for stop before killing the container")
 	nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Seconds to wait for stop before killing the container")
 	cmd.Require(flag.Min, 1)
 	cmd.Require(flag.Min, 1)
 
 

+ 2 - 1
api/client/rm.go

@@ -5,6 +5,7 @@ import (
 	"net/url"
 	"net/url"
 	"strings"
 	"strings"
 
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 )
 
 
@@ -12,7 +13,7 @@ import (
 //
 //
 // Usage: docker rm [OPTIONS] CONTAINER [CONTAINER...]
 // Usage: docker rm [OPTIONS] CONTAINER [CONTAINER...]
 func (cli *DockerCli) CmdRm(args ...string) error {
 func (cli *DockerCli) CmdRm(args ...string) error {
-	cmd := cli.Subcmd("rm", []string{"CONTAINER [CONTAINER...]"}, "Remove one or more containers", true)
+	cmd := Cli.Subcmd("rm", []string{"CONTAINER [CONTAINER...]"}, "Remove one or more containers", true)
 	v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated with the container")
 	v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated with the container")
 	link := cmd.Bool([]string{"l", "#link", "-link"}, false, "Remove the specified link")
 	link := cmd.Bool([]string{"l", "#link", "-link"}, false, "Remove the specified link")
 	force := cmd.Bool([]string{"f", "-force"}, false, "Force the removal of a running container (uses SIGKILL)")
 	force := cmd.Bool([]string{"f", "-force"}, false, "Force the removal of a running container (uses SIGKILL)")

+ 2 - 1
api/client/rmi.go

@@ -6,6 +6,7 @@ import (
 	"net/url"
 	"net/url"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 )
 
 
@@ -13,7 +14,7 @@ import (
 //
 //
 // Usage: docker rmi [OPTIONS] IMAGE [IMAGE...]
 // Usage: docker rmi [OPTIONS] IMAGE [IMAGE...]
 func (cli *DockerCli) CmdRmi(args ...string) error {
 func (cli *DockerCli) CmdRmi(args ...string) error {
-	cmd := cli.Subcmd("rmi", []string{"IMAGE [IMAGE...]"}, "Remove one or more images", true)
+	cmd := Cli.Subcmd("rmi", []string{"IMAGE [IMAGE...]"}, "Remove one or more images", true)
 	force := cmd.Bool([]string{"f", "-force"}, false, "Force removal of the image")
 	force := cmd.Bool([]string{"f", "-force"}, false, "Force removal of the image")
 	noprune := cmd.Bool([]string{"-no-prune"}, false, "Do not delete untagged parents")
 	noprune := cmd.Bool([]string{"-no-prune"}, false, "Do not delete untagged parents")
 	cmd.Require(flag.Min, 1)
 	cmd.Require(flag.Min, 1)

+ 3 - 2
api/client/run.go

@@ -8,6 +8,7 @@ import (
 	"runtime"
 	"runtime"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/pkg/promise"
 	"github.com/docker/docker/pkg/promise"
 	"github.com/docker/docker/pkg/signal"
 	"github.com/docker/docker/pkg/signal"
@@ -39,7 +40,7 @@ func (cid *cidFile) Write(id string) error {
 //
 //
 // Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
 // Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
 func (cli *DockerCli) CmdRun(args ...string) error {
 func (cli *DockerCli) CmdRun(args ...string) error {
-	cmd := cli.Subcmd("run", []string{"IMAGE [COMMAND] [ARG...]"}, "Run a command in a new container", true)
+	cmd := Cli.Subcmd("run", []string{"IMAGE [COMMAND] [ARG...]"}, "Run a command in a new container", true)
 
 
 	// These are flags not stored in Config/HostConfig
 	// These are flags not stored in Config/HostConfig
 	var (
 	var (
@@ -249,7 +250,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 		}
 		}
 	}
 	}
 	if status != 0 {
 	if status != 0 {
-		return StatusError{StatusCode: status}
+		return Cli.StatusError{StatusCode: status}
 	}
 	}
 	return nil
 	return nil
 }
 }

+ 2 - 1
api/client/save.go

@@ -6,6 +6,7 @@ import (
 	"net/url"
 	"net/url"
 	"os"
 	"os"
 
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 )
 
 
@@ -15,7 +16,7 @@ import (
 //
 //
 // Usage: docker save [OPTIONS] IMAGE [IMAGE...]
 // Usage: docker save [OPTIONS] IMAGE [IMAGE...]
 func (cli *DockerCli) CmdSave(args ...string) error {
 func (cli *DockerCli) CmdSave(args ...string) error {
-	cmd := cli.Subcmd("save", []string{"IMAGE [IMAGE...]"}, "Save an image(s) to a tar archive (streamed to STDOUT by default)", true)
+	cmd := Cli.Subcmd("save", []string{"IMAGE [IMAGE...]"}, "Save an image(s) to a tar archive (streamed to STDOUT by default)", true)
 	outfile := cmd.String([]string{"o", "-output"}, "", "Write to an file, instead of STDOUT")
 	outfile := cmd.String([]string{"o", "-output"}, "", "Write to an file, instead of STDOUT")
 	cmd.Require(flag.Min, 1)
 	cmd.Require(flag.Min, 1)
 
 

+ 2 - 1
api/client/search.go

@@ -8,6 +8,7 @@ import (
 	"strings"
 	"strings"
 	"text/tabwriter"
 	"text/tabwriter"
 
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/stringutils"
 	"github.com/docker/docker/pkg/stringutils"
@@ -25,7 +26,7 @@ func (r ByStars) Less(i, j int) bool { return r[i].StarCount < r[j].StarCount }
 //
 //
 // Usage: docker search [OPTIONS] TERM
 // Usage: docker search [OPTIONS] TERM
 func (cli *DockerCli) CmdSearch(args ...string) error {
 func (cli *DockerCli) CmdSearch(args ...string) error {
-	cmd := cli.Subcmd("search", []string{"TERM"}, "Search the Docker Hub for images", true)
+	cmd := Cli.Subcmd("search", []string{"TERM"}, "Search the Docker Hub for images", true)
 	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
 	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
 	trusted := cmd.Bool([]string{"#t", "#trusted", "#-trusted"}, false, "Only show trusted builds")
 	trusted := cmd.Bool([]string{"#t", "#trusted", "#-trusted"}, false, "Only show trusted builds")
 	automated := cmd.Bool([]string{"-automated"}, false, "Only show automated builds")
 	automated := cmd.Bool([]string{"-automated"}, false, "Only show automated builds")

+ 3 - 2
api/client/start.go

@@ -9,6 +9,7 @@ import (
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/promise"
 	"github.com/docker/docker/pkg/promise"
 	"github.com/docker/docker/pkg/signal"
 	"github.com/docker/docker/pkg/signal"
@@ -44,7 +45,7 @@ func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
 //
 //
 // Usage: docker start [OPTIONS] CONTAINER [CONTAINER...]
 // Usage: docker start [OPTIONS] CONTAINER [CONTAINER...]
 func (cli *DockerCli) CmdStart(args ...string) error {
 func (cli *DockerCli) CmdStart(args ...string) error {
-	cmd := cli.Subcmd("start", []string{"CONTAINER [CONTAINER...]"}, "Start one or more stopped containers", true)
+	cmd := Cli.Subcmd("start", []string{"CONTAINER [CONTAINER...]"}, "Start one or more stopped containers", true)
 	attach := cmd.Bool([]string{"a", "-attach"}, false, "Attach STDOUT/STDERR and forward signals")
 	attach := cmd.Bool([]string{"a", "-attach"}, false, "Attach STDOUT/STDERR and forward signals")
 	openStdin := cmd.Bool([]string{"i", "-interactive"}, false, "Attach container's STDIN")
 	openStdin := cmd.Bool([]string{"i", "-interactive"}, false, "Attach container's STDIN")
 	cmd.Require(flag.Min, 1)
 	cmd.Require(flag.Min, 1)
@@ -162,7 +163,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
 			return err
 			return err
 		}
 		}
 		if status != 0 {
 		if status != 0 {
-			return StatusError{StatusCode: status}
+			return Cli.StatusError{StatusCode: status}
 		}
 		}
 	}
 	}
 	return nil
 	return nil

+ 2 - 1
api/client/stats.go

@@ -12,6 +12,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/units"
 	"github.com/docker/docker/pkg/units"
 )
 )
@@ -124,7 +125,7 @@ func (s *containerStats) Display(w io.Writer) error {
 //
 //
 // Usage: docker stats CONTAINER [CONTAINER...]
 // Usage: docker stats CONTAINER [CONTAINER...]
 func (cli *DockerCli) CmdStats(args ...string) error {
 func (cli *DockerCli) CmdStats(args ...string) error {
-	cmd := cli.Subcmd("stats", []string{"CONTAINER [CONTAINER...]"}, "Display a live stream of one or more containers' resource usage statistics", true)
+	cmd := Cli.Subcmd("stats", []string{"CONTAINER [CONTAINER...]"}, "Display a live stream of one or more containers' resource usage statistics", true)
 	noStream := cmd.Bool([]string{"-no-stream"}, false, "Disable streaming stats and only pull the first result")
 	noStream := cmd.Bool([]string{"-no-stream"}, false, "Disable streaming stats and only pull the first result")
 	cmd.Require(flag.Min, 1)
 	cmd.Require(flag.Min, 1)
 
 

+ 2 - 1
api/client/stop.go

@@ -5,6 +5,7 @@ import (
 	"net/url"
 	"net/url"
 	"strconv"
 	"strconv"
 
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 )
 
 
@@ -14,7 +15,7 @@ import (
 //
 //
 // Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...]
 // Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...]
 func (cli *DockerCli) CmdStop(args ...string) error {
 func (cli *DockerCli) CmdStop(args ...string) error {
-	cmd := cli.Subcmd("stop", []string{"CONTAINER [CONTAINER...]"}, "Stop a running container by sending SIGTERM and then SIGKILL after a\ngrace period", true)
+	cmd := Cli.Subcmd("stop", []string{"CONTAINER [CONTAINER...]"}, "Stop a running container by sending SIGTERM and then SIGKILL after a\ngrace period", true)
 	nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Seconds to wait for stop before killing it")
 	nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Seconds to wait for stop before killing it")
 	cmd.Require(flag.Min, 1)
 	cmd.Require(flag.Min, 1)
 
 

+ 2 - 1
api/client/tag.go

@@ -3,6 +3,7 @@ package client
 import (
 import (
 	"net/url"
 	"net/url"
 
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/registry"
@@ -12,7 +13,7 @@ import (
 //
 //
 // Usage: docker tag [OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]
 // Usage: docker tag [OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]
 func (cli *DockerCli) CmdTag(args ...string) error {
 func (cli *DockerCli) CmdTag(args ...string) error {
-	cmd := cli.Subcmd("tag", []string{"IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]"}, "Tag an image into a repository", true)
+	cmd := Cli.Subcmd("tag", []string{"IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]"}, "Tag an image into a repository", true)
 	force := cmd.Bool([]string{"f", "#force", "-force"}, false, "Force")
 	force := cmd.Bool([]string{"f", "#force", "-force"}, false, "Force")
 	cmd.Require(flag.Exact, 2)
 	cmd.Require(flag.Exact, 2)
 
 

+ 2 - 1
api/client/top.go

@@ -8,6 +8,7 @@ import (
 	"text/tabwriter"
 	"text/tabwriter"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 )
 
 
@@ -15,7 +16,7 @@ import (
 //
 //
 // Usage: docker top CONTAINER
 // Usage: docker top CONTAINER
 func (cli *DockerCli) CmdTop(args ...string) error {
 func (cli *DockerCli) CmdTop(args ...string) error {
-	cmd := cli.Subcmd("top", []string{"CONTAINER [ps OPTIONS]"}, "Display the running processes of a container", true)
+	cmd := Cli.Subcmd("top", []string{"CONTAINER [ps OPTIONS]"}, "Display the running processes of a container", true)
 	cmd.Require(flag.Min, 1)
 	cmd.Require(flag.Min, 1)
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)

+ 2 - 1
api/client/unpause.go

@@ -3,6 +3,7 @@ package client
 import (
 import (
 	"fmt"
 	"fmt"
 
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 )
 
 
@@ -10,7 +11,7 @@ import (
 //
 //
 // Usage: docker unpause CONTAINER [CONTAINER...]
 // Usage: docker unpause CONTAINER [CONTAINER...]
 func (cli *DockerCli) CmdUnpause(args ...string) error {
 func (cli *DockerCli) CmdUnpause(args ...string) error {
-	cmd := cli.Subcmd("unpause", []string{"CONTAINER [CONTAINER...]"}, "Unpause all processes within a container", true)
+	cmd := Cli.Subcmd("unpause", []string{"CONTAINER [CONTAINER...]"}, "Unpause all processes within a container", true)
 	cmd.Require(flag.Min, 1)
 	cmd.Require(flag.Min, 1)
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)

+ 4 - 3
api/client/version.go

@@ -8,6 +8,7 @@ import (
 	"github.com/docker/docker/api"
 	"github.com/docker/docker/api"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/autogen/dockerversion"
 	"github.com/docker/docker/autogen/dockerversion"
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/utils"
 	"github.com/docker/docker/utils"
 )
 )
@@ -42,7 +43,7 @@ type VersionData struct {
 //
 //
 // Usage: docker version
 // Usage: docker version
 func (cli *DockerCli) CmdVersion(args ...string) (err error) {
 func (cli *DockerCli) CmdVersion(args ...string) (err error) {
-	cmd := cli.Subcmd("version", nil, "Show the Docker version information.", true)
+	cmd := Cli.Subcmd("version", nil, "Show the Docker version information.", true)
 	tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template")
 	tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template")
 	cmd.Require(flag.Exact, 0)
 	cmd.Require(flag.Exact, 0)
 
 
@@ -53,7 +54,7 @@ func (cli *DockerCli) CmdVersion(args ...string) (err error) {
 
 
 	var tmpl *template.Template
 	var tmpl *template.Template
 	if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil {
 	if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil {
-		return StatusError{StatusCode: 64,
+		return Cli.StatusError{StatusCode: 64,
 			Status: "Template parsing error: " + err.Error()}
 			Status: "Template parsing error: " + err.Error()}
 	}
 	}
 
 
@@ -85,7 +86,7 @@ func (cli *DockerCli) CmdVersion(args ...string) (err error) {
 	defer serverResp.body.Close()
 	defer serverResp.body.Close()
 
 
 	if err = json.NewDecoder(serverResp.body).Decode(&vd.Server); err != nil {
 	if err = json.NewDecoder(serverResp.body).Decode(&vd.Server); err != nil {
-		return StatusError{StatusCode: 1,
+		return Cli.StatusError{StatusCode: 1,
 			Status: "Error reading remote version: " + err.Error()}
 			Status: "Error reading remote version: " + err.Error()}
 	}
 	}
 
 

+ 2 - 1
api/client/wait.go

@@ -3,6 +3,7 @@ package client
 import (
 import (
 	"fmt"
 	"fmt"
 
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 )
 
 
@@ -12,7 +13,7 @@ import (
 //
 //
 // Usage: docker wait CONTAINER [CONTAINER...]
 // Usage: docker wait CONTAINER [CONTAINER...]
 func (cli *DockerCli) CmdWait(args ...string) error {
 func (cli *DockerCli) CmdWait(args ...string) error {
-	cmd := cli.Subcmd("wait", []string{"CONTAINER [CONTAINER...]"}, "Block until a container stops, then print its exit code.", true)
+	cmd := Cli.Subcmd("wait", []string{"CONTAINER [CONTAINER...]"}, "Block until a container stops, then print its exit code.", true)
 	cmd.Require(flag.Min, 1)
 	cmd.Require(flag.Min, 1)
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)

+ 200 - 0
cli/cli.go

@@ -0,0 +1,200 @@
+package cli
+
+import (
+	"errors"
+	"fmt"
+	"io"
+	"os"
+	"reflect"
+	"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{}
+
+// 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
+}
+
+// initErr is an error returned upon initialization of a handler implementing Initializer.
+type initErr struct{ error }
+
+func (err initErr) Error() string {
+	return err.Error()
+}
+
+func (cli *Cli) command(args ...string) (func(...string) error, error) {
+	for _, c := range cli.handlers {
+		if c == nil {
+			continue
+		}
+		camelArgs := make([]string, len(args))
+		for i, s := range args {
+			if len(s) == 0 {
+				return nil, errors.New("empty command")
+			}
+			camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:])
+		}
+		methodName := "Cmd" + strings.Join(camelArgs, "")
+		method := reflect.ValueOf(c).MethodByName(methodName)
+		if method.IsValid() {
+			if c, ok := c.(Initializer); ok {
+				if err := c.Initialize(); err != nil {
+					return nil, initErr{err}
+				}
+			}
+			return method.Interface().(func(...string) error), nil
+		}
+	}
+	return nil, errors.New("command not found")
+}
+
+// Run executes the specified command.
+func (cli *Cli) Run(args ...string) error {
+	if len(args) > 1 {
+		command, err := cli.command(args[:2]...)
+		switch err := err.(type) {
+		case nil:
+			return command(args[2:]...)
+		case initErr:
+			return err.error
+		}
+	}
+	if len(args) > 0 {
+		command, err := cli.command(args[0])
+		switch err := err.(type) {
+		case nil:
+			return command(args[1:]...)
+		case initErr:
+			return err.error
+		}
+		cli.noSuchCommand(args[0])
+	}
+	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)
+}
+
+// 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]...)
+		switch err := err.(type) {
+		case nil:
+			command("--help")
+			return nil
+		case initErr:
+			return err.error
+		}
+	}
+	if len(args) > 0 {
+		command, err := cli.command(args[0])
+		switch err := err.(type) {
+		case nil:
+			command("--help")
+			return nil
+		case initErr:
+			return err.error
+		}
+		cli.noSuchCommand(args[0])
+	}
+
+	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() {
+		options := ""
+		if flags.FlagCountUndeprecated() > 0 {
+			options = " [OPTIONS]"
+		}
+
+		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%s", lead, name, options, synopsis)
+		}
+
+		fmt.Fprintf(flags.Out(), "\n\n%s\n", description)
+	}
+
+	return flags
+}
+
+// An 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)
+}

+ 12 - 0
cli/client.go

@@ -0,0 +1,12 @@
+package cli
+
+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()
+
+	ConfigDir string
+}

+ 20 - 0
cli/common.go

@@ -0,0 +1,20 @@
+package cli
+
+import (
+	flag "github.com/docker/docker/pkg/mflag"
+	"github.com/docker/docker/pkg/tlsconfig"
+)
+
+// CommonFlags represents flags that are common to both the client and the daemon.
+type CommonFlags struct {
+	FlagSet   *flag.FlagSet
+	PostParse func()
+
+	Debug      bool
+	Hosts      []string
+	LogLevel   string
+	TLS        bool
+	TLSVerify  bool
+	TLSOptions *tlsconfig.Options
+	TrustKey   string
+}

+ 17 - 17
daemon/config.go

@@ -41,22 +41,22 @@ type CommonConfig struct {
 // the current process.
 // the current process.
 // Subsequent calls to `flag.Parse` will populate config with values parsed
 // Subsequent calls to `flag.Parse` will populate config with values parsed
 // from the command-line.
 // from the command-line.
-func (config *Config) InstallCommonFlags() {
-	opts.ListVar(&config.GraphOptions, []string{"-storage-opt"}, "Set storage driver options")
-	opts.ListVar(&config.ExecOptions, []string{"-exec-opt"}, "Set exec driver options")
-	flag.StringVar(&config.Pidfile, []string{"p", "-pidfile"}, defaultPidFile, "Path to use for daemon PID file")
-	flag.StringVar(&config.Root, []string{"g", "-graph"}, defaultGraph, "Root of the Docker runtime")
-	flag.StringVar(&config.ExecRoot, []string{"-exec-root"}, "/var/run/docker", "Root of the Docker execdriver")
-	flag.BoolVar(&config.AutoRestart, []string{"#r", "#-restart"}, true, "--restart on the daemon has been deprecated in favor of --restart policies on docker run")
-	flag.StringVar(&config.GraphDriver, []string{"s", "-storage-driver"}, "", "Storage driver to use")
-	flag.StringVar(&config.ExecDriver, []string{"e", "-exec-driver"}, defaultExec, "Exec driver to use")
-	flag.IntVar(&config.Mtu, []string{"#mtu", "-mtu"}, 0, "Set the containers network MTU")
-	flag.BoolVar(&config.EnableCors, []string{"#api-enable-cors", "#-api-enable-cors"}, false, "Enable CORS headers in the remote API, this is deprecated by --api-cors-header")
-	flag.StringVar(&config.CorsHeaders, []string{"-api-cors-header"}, "", "Set CORS headers in the remote API")
+func (config *Config) InstallCommonFlags(cmd *flag.FlagSet, usageFn func(string) string) {
+	cmd.Var(opts.NewListOptsRef(&config.GraphOptions, nil), []string{"-storage-opt"}, usageFn("Set storage driver options"))
+	cmd.Var(opts.NewListOptsRef(&config.ExecOptions, nil), []string{"-exec-opt"}, usageFn("Set exec driver 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.StringVar(&config.ExecRoot, []string{"-exec-root"}, "/var/run/docker", usageFn("Root of the Docker execdriver"))
+	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.StringVar(&config.ExecDriver, []string{"e", "-exec-driver"}, defaultExec, usageFn("Exec driver to use"))
+	cmd.IntVar(&config.Mtu, []string{"#mtu", "-mtu"}, 0, usageFn("Set the containers network MTU"))
+	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.CorsHeaders, []string{"-api-cors-header"}, "", usageFn("Set CORS headers in the remote API"))
 	// FIXME: why the inconsistency between "hosts" and "sockets"?
 	// FIXME: why the inconsistency between "hosts" and "sockets"?
-	opts.IPListVar(&config.Dns, []string{"#dns", "-dns"}, "DNS server to use")
-	opts.DNSSearchListVar(&config.DnsSearch, []string{"-dns-search"}, "DNS search domains to use")
-	opts.LabelListVar(&config.Labels, []string{"-label"}, "Set key=value labels to the daemon")
-	flag.StringVar(&config.LogConfig.Type, []string{"-log-driver"}, "json-file", "Default driver for container logs")
-	opts.LogOptsVar(config.LogConfig.Config, []string{"-log-opt"}, "Set log driver options")
+	cmd.Var(opts.NewListOptsRef(&config.Dns, opts.ValidateIPAddress), []string{"#dns", "-dns"}, usageFn("DNS server to use"))
+	cmd.Var(opts.NewListOptsRef(&config.DnsSearch, opts.ValidateDNSSearch), []string{"-dns-search"}, usageFn("DNS search domains to use"))
+	cmd.Var(opts.NewListOptsRef(&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.NewMapOpts(config.LogConfig.Config, nil), []string{"-log-opt"}, usageFn("Set log driver options"))
 }
 }

+ 3 - 3
daemon/config_experimental.go

@@ -4,7 +4,7 @@ package daemon
 
 
 import flag "github.com/docker/docker/pkg/mflag"
 import flag "github.com/docker/docker/pkg/mflag"
 
 
-func (config *Config) attachExperimentalFlags() {
-	flag.StringVar(&config.DefaultNetwork, []string{"-default-network"}, "", "Set default network")
-	flag.StringVar(&config.NetworkKVStore, []string{"-kv-store"}, "", "Set KV Store configuration")
+func (config *Config) attachExperimentalFlags(cmd *flag.FlagSet, usageFn func(string) string) {
+	cmd.StringVar(&config.DefaultNetwork, []string{"-default-network"}, "", usageFn("Set default network"))
+	cmd.StringVar(&config.NetworkKVStore, []string{"-kv-store"}, "", usageFn("Set KV Store configuration"))
 }
 }

+ 20 - 19
daemon/config_linux.go

@@ -49,27 +49,28 @@ type bridgeConfig struct {
 // the current process.
 // the current process.
 // Subsequent calls to `flag.Parse` will populate config with values parsed
 // Subsequent calls to `flag.Parse` will populate config with values parsed
 // from the command-line.
 // from the command-line.
-func (config *Config) InstallFlags() {
+func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) string) {
 	// First handle install flags which are consistent cross-platform
 	// First handle install flags which are consistent cross-platform
-	config.InstallCommonFlags()
+	config.InstallCommonFlags(cmd, usageFn)
 
 
 	// Then platform-specific install flags
 	// Then platform-specific install flags
-	flag.BoolVar(&config.EnableSelinuxSupport, []string{"-selinux-enabled"}, false, "Enable selinux support")
-	flag.StringVar(&config.SocketGroup, []string{"G", "-group"}, "docker", "Group for the unix socket")
+	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]*ulimit.Ulimit)
 	config.Ulimits = make(map[string]*ulimit.Ulimit)
-	opts.UlimitMapVar(config.Ulimits, []string{"-default-ulimit"}, "Set default ulimits for containers")
-	flag.BoolVar(&config.Bridge.EnableIPTables, []string{"#iptables", "-iptables"}, true, "Enable addition of iptables rules")
-	flag.BoolVar(&config.Bridge.EnableIPForward, []string{"#ip-forward", "-ip-forward"}, true, "Enable net.ipv4.ip_forward")
-	flag.BoolVar(&config.Bridge.EnableIPMasq, []string{"-ip-masq"}, true, "Enable IP masquerading")
-	flag.BoolVar(&config.Bridge.EnableIPv6, []string{"-ipv6"}, false, "Enable IPv6 networking")
-	flag.StringVar(&config.Bridge.IP, []string{"#bip", "-bip"}, "", "Specify network bridge IP")
-	flag.StringVar(&config.Bridge.Iface, []string{"b", "-bridge"}, "", "Attach containers to a network bridge")
-	flag.StringVar(&config.Bridge.FixedCIDR, []string{"-fixed-cidr"}, "", "IPv4 subnet for fixed IPs")
-	flag.StringVar(&config.Bridge.FixedCIDRv6, []string{"-fixed-cidr-v6"}, "", "IPv6 subnet for fixed IPs")
-	opts.IPVar(&config.Bridge.DefaultGatewayIPv4, []string{"-default-gateway"}, "", "Container default gateway IPv4 address")
-	opts.IPVar(&config.Bridge.DefaultGatewayIPv6, []string{"-default-gateway-v6"}, "", "Container default gateway IPv6 address")
-	flag.BoolVar(&config.Bridge.InterContainerCommunication, []string{"#icc", "-icc"}, true, "Enable inter-container communication")
-	opts.IPVar(&config.Bridge.DefaultIP, []string{"#ip", "-ip"}, "0.0.0.0", "Default IP when binding container ports")
-	flag.BoolVar(&config.Bridge.EnableUserlandProxy, []string{"-userland-proxy"}, true, "Use userland proxy for loopback traffic")
-	config.attachExperimentalFlags()
+	cmd.Var(opts.NewUlimitOpt(&config.Ulimits), []string{"-default-ulimit"}, usageFn("Set default ulimits for containers"))
+	cmd.BoolVar(&config.Bridge.EnableIPTables, []string{"#iptables", "-iptables"}, true, usageFn("Enable addition of iptables rules"))
+	cmd.BoolVar(&config.Bridge.EnableIPForward, []string{"#ip-forward", "-ip-forward"}, true, usageFn("Enable net.ipv4.ip_forward"))
+	cmd.BoolVar(&config.Bridge.EnableIPMasq, []string{"-ip-masq"}, true, usageFn("Enable IP masquerading"))
+	cmd.BoolVar(&config.Bridge.EnableIPv6, []string{"-ipv6"}, false, usageFn("Enable IPv6 networking"))
+	cmd.StringVar(&config.Bridge.IP, []string{"#bip", "-bip"}, "", usageFn("Specify network bridge IP"))
+	cmd.StringVar(&config.Bridge.Iface, []string{"b", "-bridge"}, "", usageFn("Attach containers to a network bridge"))
+	cmd.StringVar(&config.Bridge.FixedCIDR, []string{"-fixed-cidr"}, "", usageFn("IPv4 subnet for fixed IPs"))
+	cmd.StringVar(&config.Bridge.FixedCIDRv6, []string{"-fixed-cidr-v6"}, "", usageFn("IPv6 subnet for fixed IPs"))
+	cmd.Var(opts.NewIpOpt(&config.Bridge.DefaultGatewayIPv4, ""), []string{"-default-gateway"}, usageFn("Container default gateway IPv4 address"))
+	cmd.Var(opts.NewIpOpt(&config.Bridge.DefaultGatewayIPv6, ""), []string{"-default-gateway-v6"}, usageFn("Container default gateway IPv6 address"))
+	cmd.BoolVar(&config.Bridge.InterContainerCommunication, []string{"#icc", "-icc"}, true, usageFn("Enable inter-container communication"))
+	cmd.Var(opts.NewIpOpt(&config.Bridge.DefaultIP, "0.0.0.0"), []string{"#ip", "-ip"}, usageFn("Default IP when binding container ports"))
+	cmd.BoolVar(&config.Bridge.EnableUserlandProxy, []string{"-userland-proxy"}, true, usageFn("Use userland proxy for loopback traffic"))
+
+	config.attachExperimentalFlags(cmd, usageFn)
 }
 }

+ 3 - 1
daemon/config_stub.go

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

+ 2 - 2
daemon/config_windows.go

@@ -32,9 +32,9 @@ type Config struct {
 // the current process.
 // the current process.
 // Subsequent calls to `flag.Parse` will populate config with values parsed
 // Subsequent calls to `flag.Parse` will populate config with values parsed
 // from the command-line.
 // from the command-line.
-func (config *Config) InstallFlags() {
+func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) string) {
 	// First handle install flags which are consistent cross-platform
 	// First handle install flags which are consistent cross-platform
-	config.InstallCommonFlags()
+	config.InstallCommonFlags(cmd, usageFn)
 
 
 	// Then platform-specific install flags.
 	// Then platform-specific install flags.
 	flag.StringVar(&config.Bridge.VirtualSwitchName, []string{"b", "-bridge"}, "", "Attach containers to a virtual switch")
 	flag.StringVar(&config.Bridge.VirtualSwitchName, []string{"b", "-bridge"}, "", "Attach containers to a virtual switch")

+ 22 - 5
docker/client.go

@@ -1,11 +1,28 @@
-// +build !daemon
-
 package main
 package main
 
 
 import (
 import (
-	"log" // see gh#8745, client needs to use go log pkg
+	"path/filepath"
+
+	"github.com/docker/docker/cli"
+	"github.com/docker/docker/cliconfig"
+	flag "github.com/docker/docker/pkg/mflag"
 )
 )
 
 
-func mainDaemon() {
-	log.Fatal("This is a client-only binary - running the Docker daemon is not supported.")
+var clientFlags = &cli.ClientFlags{FlagSet: new(flag.FlagSet), Common: commonFlags}
+
+func init() {
+	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)
+		}
+
+		if clientFlags.Common.TrustKey == "" {
+			clientFlags.Common.TrustKey = filepath.Join(cliconfig.ConfigDir(), defaultTrustKeyFile)
+		}
+	}
 }
 }

+ 101 - 0
docker/common.go

@@ -0,0 +1,101 @@
+package main
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+
+	"github.com/Sirupsen/logrus"
+	"github.com/docker/docker/cli"
+	"github.com/docker/docker/cliconfig"
+	"github.com/docker/docker/opts"
+	flag "github.com/docker/docker/pkg/mflag"
+	"github.com/docker/docker/pkg/tlsconfig"
+)
+
+const (
+	defaultTrustKeyFile = "key.json"
+	defaultCaFile       = "ca.pem"
+	defaultKeyFile      = "key.pem"
+	defaultCertFile     = "cert.pem"
+)
+
+var (
+	daemonFlags *flag.FlagSet
+	commonFlags = &cli.CommonFlags{FlagSet: new(flag.FlagSet)}
+
+	dockerCertPath  = os.Getenv("DOCKER_CERT_PATH")
+	dockerTLSVerify = os.Getenv("DOCKER_TLS_VERIFY") != ""
+)
+
+func init() {
+	if dockerCertPath == "" {
+		dockerCertPath = cliconfig.ConfigDir()
+	}
+
+	commonFlags.PostParse = postParseCommon
+
+	cmd := commonFlags.FlagSet
+
+	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 flag.String([]string{"i", "-identity"}, "", "Path to libtrust 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.NewListOptsRef(&commonFlags.Hosts, opts.ValidateHost), []string{"H", "-host"}, "Daemon socket(s) to connect to")
+}
+
+func postParseCommon() {
+	cmd := commonFlags.FlagSet
+
+	if commonFlags.LogLevel != "" {
+		lvl, err := logrus.ParseLevel(commonFlags.LogLevel)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "Unable to parse logging level: %s\n", commonFlags.LogLevel)
+			os.Exit(1)
+		}
+		logrus.SetLevel(lvl)
+	} else {
+		logrus.SetLevel(logrus.InfoLevel)
+	}
+
+	if commonFlags.Debug {
+		os.Setenv("DEBUG", "1")
+		logrus.SetLevel(logrus.DebugLevel)
+	}
+
+	// Regardless of whether the user sets it to true or false, if they
+	// specify --tlsverify at all then we need to turn on tls
+	// TLSVerify can be true even if not set due to DOCKER_TLS_VERIFY env var, so we need to check that here as well
+	if cmd.IsSet("-tlsverify") || commonFlags.TLSVerify {
+		commonFlags.TLS = true
+	}
+
+	if !commonFlags.TLS {
+		commonFlags.TLSOptions = nil
+	} else {
+		tlsOptions := commonFlags.TLSOptions
+		tlsOptions.InsecureSkipVerify = !commonFlags.TLSVerify
+
+		// Reset CertFile and KeyFile to empty string if the user did not specify
+		// the respective flags and the respective default files were not found.
+		if !cmd.IsSet("-tlscert") {
+			if _, err := os.Stat(tlsOptions.CertFile); os.IsNotExist(err) {
+				tlsOptions.CertFile = ""
+			}
+		}
+		if !cmd.IsSet("-tlskey") {
+			if _, err := os.Stat(tlsOptions.KeyFile); os.IsNotExist(err) {
+				tlsOptions.KeyFile = ""
+			}
+		}
+	}
+}

+ 135 - 29
docker/daemon.go

@@ -8,14 +8,18 @@ import (
 	"io"
 	"io"
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
+	"runtime"
+	"strings"
 	"time"
 	"time"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	apiserver "github.com/docker/docker/api/server"
 	apiserver "github.com/docker/docker/api/server"
 	"github.com/docker/docker/autogen/dockerversion"
 	"github.com/docker/docker/autogen/dockerversion"
+	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/daemon"
 	"github.com/docker/docker/daemon"
 	"github.com/docker/docker/daemon/logger"
 	"github.com/docker/docker/daemon/logger"
+	"github.com/docker/docker/opts"
 	flag "github.com/docker/docker/pkg/mflag"
 	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"
@@ -26,17 +30,63 @@ import (
 	"github.com/docker/docker/utils"
 	"github.com/docker/docker/utils"
 )
 )
 
 
+const daemonUsage = "       docker daemon [ --help | ... ]\n"
+
 var (
 var (
-	daemonCfg   = &daemon.Config{}
-	registryCfg = &registry.Options{}
+	flDaemon              = flag.Bool([]string{"#d", "#-daemon"}, false, "Enable daemon mode (deprecated; use docker daemon)")
+	daemonCli cli.Handler = NewDaemonCli()
 )
 )
 
 
-func init() {
-	if daemonCfg.LogConfig.Config == nil {
-		daemonCfg.LogConfig.Config = make(map[string]string)
+// TODO: remove once `-d` is retired
+func handleGlobalDaemonFlag() {
+	// This block makes sure that if the deprecated daemon flag `--daemon` is absent,
+	// then all daemon-specific flags are absent as well.
+	if !*flDaemon && daemonFlags != nil {
+		flag.CommandLine.Visit(func(fl *flag.Flag) {
+			for _, name := range fl.Names {
+				name := strings.TrimPrefix(name, "#")
+				if daemonFlags.Lookup(name) != nil {
+					// daemon flag was NOT specified, but daemon-specific flags were
+					// so let's error out
+					fmt.Fprintf(os.Stderr, "docker: the daemon flag '-%s' must follow the 'docker daemon' command.\n", name)
+					os.Exit(1)
+				}
+			}
+		})
+	}
+
+	if *flDaemon {
+		if *flHelp {
+			// We do not show the help output here, instead, we tell the user about the new daemon command,
+			// because the help output is so long they would not see the warning anyway.
+			fmt.Fprintln(os.Stderr, "Please use 'docker daemon --help' instead.")
+			os.Exit(0)
+		}
+		daemonCli.(*DaemonCli).CmdDaemon(flag.Args()...)
+		os.Exit(0)
+	}
+}
+
+func presentInHelp(usage string) string { return usage }
+func absentFromHelp(string) string      { return "" }
+
+// NewDaemonCli returns a pre-configured daemon CLI
+func NewDaemonCli() *DaemonCli {
+	daemonFlags = cli.Subcmd("daemon", nil, "Enable daemon mode", true)
+
+	// TODO(tiborvass): remove InstallFlags?
+	daemonConfig := new(daemon.Config)
+	daemonConfig.InstallFlags(daemonFlags, presentInHelp)
+	daemonConfig.InstallFlags(flag.CommandLine, absentFromHelp)
+	registryOptions := new(registry.Options)
+	registryOptions.InstallFlags(daemonFlags, presentInHelp)
+	registryOptions.InstallFlags(flag.CommandLine, absentFromHelp)
+	daemonFlags.Require(flag.Exact, 0)
+
+	return &DaemonCli{
+		Config:          daemonConfig,
+		registryOptions: registryOptions,
 	}
 	}
-	daemonCfg.InstallFlags()
-	registryCfg.InstallFlags()
 }
 }
 
 
 func migrateKey() (err error) {
 func migrateKey() (err error) {
@@ -79,14 +129,56 @@ func migrateKey() (err error) {
 	return nil
 	return nil
 }
 }
 
 
-func mainDaemon() {
-	if utils.ExperimentalBuild() {
-		logrus.Warn("Running experimental build")
+// DaemonCli represents the daemon CLI.
+type DaemonCli struct {
+	*daemon.Config
+	registryOptions *registry.Options
+}
+
+func getGlobalFlag() (globalFlag *flag.Flag) {
+	defer func() {
+		if x := recover(); x != nil {
+			switch f := x.(type) {
+			case *flag.Flag:
+				globalFlag = f
+			default:
+				panic(x)
+			}
+		}
+	}()
+	visitor := func(f *flag.Flag) { panic(f) }
+	commonFlags.FlagSet.Visit(visitor)
+	clientFlags.FlagSet.Visit(visitor)
+	return
+}
+
+// CmdDaemon is the daemon command, called the raw arguments after `docker daemon`.
+func (cli *DaemonCli) CmdDaemon(args ...string) error {
+	if *flDaemon {
+		// allow legacy forms `docker -D -d` and `docker -d -D`
+		logrus.Warn("please use 'docker daemon' instead.")
+	} else if !commonFlags.FlagSet.IsEmpty() || !clientFlags.FlagSet.IsEmpty() {
+		// deny `docker -D daemon`
+		illegalFlag := getGlobalFlag()
+		fmt.Fprintf(os.Stderr, "invalid flag '-%s'.\nSee 'docker daemon --help'.\n", illegalFlag.Names[0])
+		os.Exit(1)
+	} else {
+		// allow new form `docker daemon -D`
+		flag.Merge(daemonFlags, commonFlags.FlagSet)
+	}
+
+	daemonFlags.ParseFlags(args, true)
+	commonFlags.PostParse()
+
+	if len(commonFlags.Hosts) == 0 {
+		commonFlags.Hosts = []string{opts.DefaultHost}
+	}
+	if commonFlags.TrustKey == "" {
+		commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), defaultTrustKeyFile)
 	}
 	}
 
 
-	if flag.NArg() != 0 {
-		flag.Usage()
-		return
+	if utils.ExperimentalBuild() {
+		logrus.Warn("Running experimental build")
 	}
 	}
 
 
 	logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: timeutils.RFC3339NanoFixed})
 	logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: timeutils.RFC3339NanoFixed})
@@ -95,15 +187,15 @@ func mainDaemon() {
 		logrus.Fatalf("Failed to set umask: %v", err)
 		logrus.Fatalf("Failed to set umask: %v", err)
 	}
 	}
 
 
-	if len(daemonCfg.LogConfig.Config) > 0 {
-		if err := logger.ValidateLogOpts(daemonCfg.LogConfig.Type, daemonCfg.LogConfig.Config); err != nil {
+	if len(cli.LogConfig.Config) > 0 {
+		if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil {
 			logrus.Fatalf("Failed to set log opts: %v", err)
 			logrus.Fatalf("Failed to set log opts: %v", err)
 		}
 		}
 	}
 	}
 
 
 	var pfile *pidfile.PidFile
 	var pfile *pidfile.PidFile
-	if daemonCfg.Pidfile != "" {
-		pf, err := pidfile.New(daemonCfg.Pidfile)
+	if cli.Pidfile != "" {
+		pf, err := pidfile.New(cli.Pidfile)
 		if err != nil {
 		if err != nil {
 			logrus.Fatalf("Error starting daemon: %v", err)
 			logrus.Fatalf("Error starting daemon: %v", err)
 		}
 		}
@@ -115,21 +207,26 @@ func mainDaemon() {
 		}()
 		}()
 	}
 	}
 
 
+	if cli.LogConfig.Config == nil {
+		cli.LogConfig.Config = make(map[string]string)
+	}
+
 	serverConfig := &apiserver.ServerConfig{
 	serverConfig := &apiserver.ServerConfig{
 		Logging:     true,
 		Logging:     true,
-		EnableCors:  daemonCfg.EnableCors,
-		CorsHeaders: daemonCfg.CorsHeaders,
+		EnableCors:  cli.EnableCors,
+		CorsHeaders: cli.CorsHeaders,
 		Version:     dockerversion.VERSION,
 		Version:     dockerversion.VERSION,
 	}
 	}
-	serverConfig = setPlatformServerConfig(serverConfig, daemonCfg)
+	serverConfig = setPlatformServerConfig(serverConfig, cli.Config)
 
 
-	if *flTLS {
-		if *flTLSVerify {
-			tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert
+	if commonFlags.TLSOptions != nil {
+		if !commonFlags.TLSOptions.InsecureSkipVerify {
+			// server requires and verifies client's certificate
+			commonFlags.TLSOptions.ClientAuth = tls.RequireAndVerifyClientCert
 		}
 		}
-		tlsConfig, err := tlsconfig.Server(tlsOptions)
+		tlsConfig, err := tlsconfig.Server(*commonFlags.TLSOptions)
 		if err != nil {
 		if err != nil {
-			logrus.Fatal(err)
+			logrus.Fatalf("foobar: %v", err)
 		}
 		}
 		serverConfig.TLSConfig = tlsConfig
 		serverConfig.TLSConfig = tlsConfig
 	}
 	}
@@ -141,7 +238,7 @@ func mainDaemon() {
 	// daemon doesn't exit
 	// daemon doesn't exit
 	serveAPIWait := make(chan error)
 	serveAPIWait := make(chan error)
 	go func() {
 	go func() {
-		if err := api.ServeApi(flHosts); err != nil {
+		if err := api.ServeApi(commonFlags.Hosts); err != nil {
 			logrus.Errorf("ServeAPI error: %v", err)
 			logrus.Errorf("ServeAPI error: %v", err)
 			serveAPIWait <- err
 			serveAPIWait <- err
 			return
 			return
@@ -152,10 +249,10 @@ func mainDaemon() {
 	if err := migrateKey(); err != nil {
 	if err := migrateKey(); err != nil {
 		logrus.Fatal(err)
 		logrus.Fatal(err)
 	}
 	}
-	daemonCfg.TrustKeyPath = *flTrustKey
+	cli.TrustKeyPath = commonFlags.TrustKey
 
 
-	registryService := registry.NewService(registryCfg)
-	d, err := daemon.NewDaemon(daemonCfg, registryService)
+	registryService := registry.NewService(cli.registryOptions)
+	d, err := daemon.NewDaemon(cli.Config, registryService)
 	if err != nil {
 	if err != nil {
 		if pfile != nil {
 		if pfile != nil {
 			if err := pfile.Remove(); err != nil {
 			if err := pfile.Remove(); err != nil {
@@ -201,6 +298,7 @@ func mainDaemon() {
 		}
 		}
 		logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI)
 		logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI)
 	}
 	}
+	return nil
 }
 }
 
 
 // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case
 // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case
@@ -219,3 +317,11 @@ func shutdownDaemon(d *daemon.Daemon, timeout time.Duration) {
 		logrus.Error("Force shutdown daemon")
 		logrus.Error("Force shutdown daemon")
 	}
 	}
 }
 }
+
+func getDaemonConfDir() string {
+	// TODO: update for Windows daemon
+	if runtime.GOOS == "windows" {
+		return cliconfig.ConfigDir()
+	}
+	return "/etc/docker"
+}

+ 12 - 0
docker/daemon_none.go

@@ -0,0 +1,12 @@
+// +build !daemon
+
+package main
+
+import "github.com/docker/docker/cli"
+
+const daemonUsage = ""
+
+var daemonCli cli.Handler
+
+// TODO: remove once `-d` is retired
+func handleGlobalDaemonFlag() {}

+ 33 - 102
docker/docker.go

@@ -1,31 +1,20 @@
 package main
 package main
 
 
 import (
 import (
-	"crypto/tls"
 	"fmt"
 	"fmt"
 	"os"
 	"os"
-	"runtime"
-	"strings"
+	"sort"
 
 
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/client"
 	"github.com/docker/docker/api/client"
 	"github.com/docker/docker/autogen/dockerversion"
 	"github.com/docker/docker/autogen/dockerversion"
-	"github.com/docker/docker/cliconfig"
-	"github.com/docker/docker/opts"
+	"github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	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/pkg/tlsconfig"
 	"github.com/docker/docker/utils"
 	"github.com/docker/docker/utils"
 )
 )
 
 
-const (
-	defaultTrustKeyFile = "key.json"
-	defaultCaFile       = "ca.pem"
-	defaultKeyFile      = "key.pem"
-	defaultCertFile     = "cert.pem"
-)
-
 func main() {
 func main() {
 	if reexec.Init() {
 	if reexec.Init() {
 		return
 		return
@@ -34,116 +23,58 @@ func main() {
 	// Set terminal emulation based on platform as required.
 	// Set terminal emulation based on platform as required.
 	stdin, stdout, stderr := term.StdStreams()
 	stdin, stdout, stderr := term.StdStreams()
 
 
-	initLogging(stderr)
+	logrus.SetOutput(stderr)
 
 
-	flag.Parse()
-	// FIXME: validate daemon flags here
+	flag.Merge(flag.CommandLine, clientFlags.FlagSet, commonFlags.FlagSet)
 
 
-	if *flVersion {
-		showVersion()
-		return
-	}
+	flag.Usage = func() {
+		fmt.Fprint(os.Stdout, "Usage: docker [OPTIONS] COMMAND [arg...]\n"+daemonUsage+"       docker [ -h | --help | -v | --version ]\n\n")
+		fmt.Fprint(os.Stdout, "A self-sufficient runtime for containers.\n\nOptions:\n")
 
 
-	if *flConfigDir != "" {
-		cliconfig.SetConfigDir(*flConfigDir)
-	}
+		flag.CommandLine.SetOutput(os.Stdout)
+		flag.PrintDefaults()
 
 
-	if *flLogLevel != "" {
-		lvl, err := logrus.ParseLevel(*flLogLevel)
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "Unable to parse logging level: %s\n", *flLogLevel)
-			os.Exit(1)
-		}
-		setLogLevel(lvl)
-	} else {
-		setLogLevel(logrus.InfoLevel)
-	}
+		help := "\nCommands:\n"
 
 
-	if *flDebug {
-		os.Setenv("DEBUG", "1")
-		setLogLevel(logrus.DebugLevel)
-	}
+		// TODO(tiborvass): no need to sort if we ensure dockerCommands is sorted
+		sort.Sort(byName(dockerCommands))
 
 
-	if len(flHosts) == 0 {
-		defaultHost := os.Getenv("DOCKER_HOST")
-		if defaultHost == "" || *flDaemon {
-			if runtime.GOOS != "windows" {
-				// If we do not have a host, default to unix socket
-				defaultHost = fmt.Sprintf("unix://%s", opts.DefaultUnixSocket)
-			} else {
-				// If we do not have a host, default to TCP socket on Windows
-				defaultHost = fmt.Sprintf("tcp://%s:%d", opts.DefaultHTTPHost, opts.DefaultHTTPPort)
-			}
-		}
-		defaultHost, err := opts.ValidateHost(defaultHost)
-		if err != nil {
-			if *flDaemon {
-				logrus.Fatal(err)
-			} else {
-				fmt.Fprint(os.Stderr, err)
-			}
-			os.Exit(1)
+		for _, cmd := range dockerCommands {
+			help += fmt.Sprintf("    %-10.10s%s\n", cmd.name, cmd.description)
 		}
 		}
-		flHosts = append(flHosts, defaultHost)
-	}
-
-	setDefaultConfFlag(flTrustKey, defaultTrustKeyFile)
 
 
-	// Regardless of whether the user sets it to true or false, if they
-	// specify --tlsverify at all then we need to turn on tls
-	// *flTlsVerify can be true even if not set due to DOCKER_TLS_VERIFY env var, so we need to check that here as well
-	if flag.IsSet("-tlsverify") || *flTLSVerify {
-		*flTLS = true
+		help += "\nRun 'docker COMMAND --help' for more information on a command."
+		fmt.Fprintf(os.Stdout, "%s\n", help)
 	}
 	}
 
 
-	if *flDaemon {
-		if *flHelp {
-			flag.Usage()
-			return
-		}
-		mainDaemon()
+	flag.Parse()
+
+	if *flVersion {
+		showVersion()
 		return
 		return
 	}
 	}
 
 
-	// From here on, we assume we're a client, not a server.
+	clientCli := client.NewDockerCli(stdin, stdout, stderr, clientFlags)
+	// TODO: remove once `-d` is retired
+	handleGlobalDaemonFlag()
 
 
-	if len(flHosts) > 1 {
-		fmt.Fprintf(os.Stderr, "Please specify only one -H")
-		os.Exit(0)
-	}
-	protoAddrParts := strings.SplitN(flHosts[0], "://", 2)
-
-	var tlsConfig *tls.Config
-	if *flTLS {
-		tlsOptions.InsecureSkipVerify = !*flTLSVerify
-		if !flag.IsSet("-tlscert") {
-			if _, err := os.Stat(tlsOptions.CertFile); os.IsNotExist(err) {
-				tlsOptions.CertFile = ""
-			}
-		}
-		if !flag.IsSet("-tlskey") {
-			if _, err := os.Stat(tlsOptions.KeyFile); os.IsNotExist(err) {
-				tlsOptions.KeyFile = ""
-			}
-		}
-		var err error
-		tlsConfig, err = tlsconfig.Client(tlsOptions)
-		if err != nil {
-			fmt.Fprintln(stderr, err)
-			os.Exit(1)
-		}
+	if *flHelp {
+		// if global flag --help is present, regardless of what other options and commands there are,
+		// just print the usage.
+		flag.Usage()
+		return
 	}
 	}
-	cli := client.NewDockerCli(stdin, stdout, stderr, *flTrustKey, protoAddrParts[0], protoAddrParts[1], tlsConfig)
 
 
-	if err := cli.Cmd(flag.Args()...); err != nil {
-		if sterr, ok := err.(client.StatusError); ok {
+	c := cli.New(clientCli, daemonCli)
+	if err := c.Run(flag.Args()...); err != nil {
+		if sterr, ok := err.(cli.StatusError); ok {
 			if sterr.Status != "" {
 			if sterr.Status != "" {
-				fmt.Fprintln(cli.Err(), sterr.Status)
+				fmt.Fprintln(os.Stderr, sterr.Status)
 				os.Exit(1)
 				os.Exit(1)
 			}
 			}
 			os.Exit(sterr.StatusCode)
 			os.Exit(sterr.StatusCode)
 		}
 		}
-		fmt.Fprintln(cli.Err(), err)
+		fmt.Fprintln(os.Stderr, err)
 		os.Exit(1)
 		os.Exit(1)
 	}
 	}
 }
 }

+ 46 - 124
docker/flags.go

@@ -1,16 +1,10 @@
 package main
 package main
 
 
-import (
-	"fmt"
-	"os"
-	"path/filepath"
-	"runtime"
-	"sort"
+import flag "github.com/docker/docker/pkg/mflag"
 
 
-	"github.com/docker/docker/cliconfig"
-	"github.com/docker/docker/opts"
-	flag "github.com/docker/docker/pkg/mflag"
-	"github.com/docker/docker/pkg/tlsconfig"
+var (
+	flHelp    = flag.Bool([]string{"h", "-help"}, false, "Print usage")
+	flVersion = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit")
 )
 )
 
 
 type command struct {
 type command struct {
@@ -24,118 +18,46 @@ 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) 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 }
 func (a byName) Less(i, j int) bool { return a[i].name < a[j].name }
 
 
-var (
-	dockerCertPath  = os.Getenv("DOCKER_CERT_PATH")
-	dockerTlSVerify = os.Getenv("DOCKER_TLS_VERIFY") != ""
-
-	dockerCommands = []command{
-		{"attach", "Attach to a running container"},
-		{"build", "Build an image from a Dockerfile"},
-		{"commit", "Create a new image from a container's changes"},
-		{"cp", "Copy files/folders from a container to a HOSTDIR or to STDOUT"},
-		{"create", "Create a new container"},
-		{"diff", "Inspect changes on a container's filesystem"},
-		{"events", "Get real time events from the server"},
-		{"exec", "Run a command in a running container"},
-		{"export", "Export a container's filesystem as a tar archive"},
-		{"history", "Show the history of an image"},
-		{"images", "List images"},
-		{"import", "Import the contents from a tarball to create a filesystem image"},
-		{"info", "Display system-wide information"},
-		{"inspect", "Return low-level information on a container or image"},
-		{"kill", "Kill a running container"},
-		{"load", "Load an image from a tar archive or STDIN"},
-		{"login", "Register or log in to a Docker registry"},
-		{"logout", "Log out from a Docker registry"},
-		{"logs", "Fetch the logs of a container"},
-		{"port", "List port mappings or a specific mapping for the CONTAINER"},
-		{"pause", "Pause all processes within a container"},
-		{"ps", "List containers"},
-		{"pull", "Pull an image or a repository from a registry"},
-		{"push", "Push an image or a repository to a registry"},
-		{"rename", "Rename a container"},
-		{"restart", "Restart a running container"},
-		{"rm", "Remove one or more containers"},
-		{"rmi", "Remove one or more images"},
-		{"run", "Run a command in a new container"},
-		{"save", "Save an image(s) to a tar archive"},
-		{"search", "Search the Docker Hub for images"},
-		{"start", "Start one or more stopped containers"},
-		{"stats", "Display a live stream of container(s) resource usage statistics"},
-		{"stop", "Stop a running container"},
-		{"tag", "Tag an image into a repository"},
-		{"top", "Display the running processes of a container"},
-		{"unpause", "Unpause all processes within a container"},
-		{"version", "Show the Docker version information"},
-		{"wait", "Block until a container stops, then print its exit code"},
-	}
-)
-
-func init() {
-	if dockerCertPath == "" {
-		dockerCertPath = cliconfig.ConfigDir()
-	}
-}
-
-func getDaemonConfDir() string {
-	// TODO: update for Windows daemon
-	if runtime.GOOS == "windows" {
-		return cliconfig.ConfigDir()
-	}
-	return "/etc/docker"
-}
-
-var (
-	flConfigDir = flag.String([]string{"-config"}, cliconfig.ConfigDir(), "Location of client config files")
-	flVersion   = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit")
-	flDaemon    = flag.Bool([]string{"d", "-daemon"}, false, "Enable daemon mode")
-	flDebug     = flag.Bool([]string{"D", "-debug"}, false, "Enable debug mode")
-	flLogLevel  = flag.String([]string{"l", "-log-level"}, "info", "Set the logging level")
-	flTLS       = flag.Bool([]string{"-tls"}, false, "Use TLS; implied by --tlsverify")
-	flHelp      = flag.Bool([]string{"h", "-help"}, false, "Print usage")
-	flTLSVerify = flag.Bool([]string{"-tlsverify"}, dockerTlSVerify, "Use TLS and verify the remote")
-
-	// these are initialized in init() below since their default values depend on dockerCertPath which isn't fully initialized until init() runs
-	tlsOptions tlsconfig.Options
-	flTrustKey *string
-	flHosts    []string
-)
-
-func setDefaultConfFlag(flag *string, def string) {
-	if *flag == "" {
-		if *flDaemon {
-			*flag = filepath.Join(getDaemonConfDir(), def)
-		} else {
-			*flag = filepath.Join(cliconfig.ConfigDir(), def)
-		}
-	}
-}
-
-func init() {
-	var placeholderTrustKey string
-	// TODO use flag flag.String([]string{"i", "-identity"}, "", "Path to libtrust key file")
-	flTrustKey = &placeholderTrustKey
-
-	flag.StringVar(&tlsOptions.CAFile, []string{"-tlscacert"}, filepath.Join(dockerCertPath, defaultCaFile), "Trust certs signed only by this CA")
-	flag.StringVar(&tlsOptions.CertFile, []string{"-tlscert"}, filepath.Join(dockerCertPath, defaultCertFile), "Path to TLS certificate file")
-	flag.StringVar(&tlsOptions.KeyFile, []string{"-tlskey"}, filepath.Join(dockerCertPath, defaultKeyFile), "Path to TLS key file")
-	opts.HostListVar(&flHosts, []string{"H", "-host"}, "Daemon socket(s) to connect to")
-
-	flag.Usage = func() {
-		fmt.Fprint(os.Stdout, "Usage: docker [OPTIONS] COMMAND [arg...]\n\nA self-sufficient runtime for containers.\n\nOptions:\n")
-
-		flag.CommandLine.SetOutput(os.Stdout)
-		flag.PrintDefaults()
-
-		help := "\nCommands:\n"
-
-		sort.Sort(byName(dockerCommands))
-
-		for _, cmd := range dockerCommands {
-			help += fmt.Sprintf("    %-10.10s%s\n", cmd.name, cmd.description)
-		}
-
-		help += "\nRun 'docker COMMAND --help' for more information on a command."
-		fmt.Fprintf(os.Stdout, "%s\n", help)
-	}
+// TODO(tiborvass): do not show 'daemon' on client-only binaries
+// and deduplicate description in dockerCommands and cli subcommands
+var dockerCommands = []command{
+	{"attach", "Attach to a running container"},
+	{"build", "Build an image from a Dockerfile"},
+	{"commit", "Create a new image from a container's changes"},
+	{"cp", "Copy files/folders from a container to a HOSTDIR or to STDOUT"},
+	{"create", "Create a new container"},
+	{"diff", "Inspect changes on a container's filesystem"},
+	{"events", "Get real time events from the server"},
+	{"exec", "Run a command in a running container"},
+	{"export", "Export a container's filesystem as a tar archive"},
+	{"history", "Show the history of an image"},
+	{"images", "List images"},
+	{"import", "Import the contents from a tarball to create a filesystem image"},
+	{"info", "Display system-wide information"},
+	{"inspect", "Return low-level information on a container or image"},
+	{"kill", "Kill a running container"},
+	{"load", "Load an image from a tar archive or STDIN"},
+	{"login", "Register or log in to a Docker registry"},
+	{"logout", "Log out from a Docker registry"},
+	{"logs", "Fetch the logs of a container"},
+	{"port", "List port mappings or a specific mapping for the CONTAINER"},
+	{"pause", "Pause all processes within a container"},
+	{"ps", "List containers"},
+	{"pull", "Pull an image or a repository from a registry"},
+	{"push", "Push an image or a repository to a registry"},
+	{"rename", "Rename a container"},
+	{"restart", "Restart a running container"},
+	{"rm", "Remove one or more containers"},
+	{"rmi", "Remove one or more images"},
+	{"run", "Run a command in a new container"},
+	{"save", "Save an image(s) to a tar archive"},
+	{"search", "Search the Docker Hub for images"},
+	{"start", "Start one or more stopped containers"},
+	{"stats", "Display a live stream of container(s) resource usage statistics"},
+	{"stop", "Stop a running container"},
+	{"tag", "Tag an image into a repository"},
+	{"top", "Display the running processes of a container"},
+	{"unpause", "Unpause all processes within a container"},
+	{"version", "Show the Docker version information"},
+	{"wait", "Block until a container stops, then print its exit code"},
 }
 }

+ 0 - 14
docker/log.go

@@ -1,14 +0,0 @@
-package main
-
-import (
-	"github.com/Sirupsen/logrus"
-	"io"
-)
-
-func setLogLevel(lvl logrus.Level) {
-	logrus.SetLevel(lvl)
-}
-
-func initLogging(stderr io.Writer) {
-	logrus.SetOutput(stderr)
-}

+ 2 - 2
docs/articles/basics.md

@@ -103,7 +103,7 @@ when no `-H` was passed in.
 
 
 Run Docker in daemon mode:
 Run Docker in daemon mode:
 
 
-    $ sudo <path to>/docker -H 0.0.0.0:5555 -d &
+    $ sudo <path to>/docker daemon -H 0.0.0.0:5555 &
 
 
 Download an `ubuntu` image:
 Download an `ubuntu` image:
 
 
@@ -113,7 +113,7 @@ You can use multiple `-H`, for example, if you want to listen on both
 TCP and a Unix socket
 TCP and a Unix socket
 
 
     # Run docker in daemon mode
     # Run docker in daemon mode
-    $ sudo <path to>/docker -H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock -d &
+    $ sudo <path to>/docker daemon -H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock &
     # Download an ubuntu image, use default Unix socket
     # Download an ubuntu image, use default Unix socket
     $ docker pull ubuntu
     $ docker pull ubuntu
     # OR use the TCP port
     # OR use the TCP port

+ 4 - 5
docs/articles/configuring.md

@@ -24,7 +24,7 @@ or `systemd` to manage the `docker` daemon's start and stop.
 The `docker` daemon can be run directly using the `-d` option. By default it listens on
 The `docker` daemon can be run directly using the `-d` option. By default it listens on
 the Unix socket `unix:///var/run/docker.sock`
 the Unix socket `unix:///var/run/docker.sock`
 
 
-    $ docker -d
+    $ docker daemon
 
 
     INFO[0000] +job init_networkdriver()
     INFO[0000] +job init_networkdriver()
     INFO[0000] +job serveapi(unix:///var/run/docker.sock)
     INFO[0000] +job serveapi(unix:///var/run/docker.sock)
@@ -34,10 +34,9 @@ the Unix socket `unix:///var/run/docker.sock`
 
 
 ### Configuring the docker daemon directly
 ### Configuring the docker daemon directly
 
 
-If you're running the `docker` daemon directly by running `docker -d` instead
+If you're running the `docker` daemon directly by running `docker daemon` instead
 of using a process manager, you can append the configuration options to the `docker` run
 of using a process manager, you can append the configuration options to the `docker` run
-command directly. Just like the `-d` option, other options can be passed to the `docker`
-daemon to configure it.
+command directly. Other options can be passed to the `docker` daemon to configure it.
 
 
 Some of the daemon's options are:
 Some of the daemon's options are:
 
 
@@ -50,7 +49,7 @@ Some of the daemon's options are:
 
 
 Here is a an example of running the `docker` daemon with configuration options:
 Here is a an example of running the `docker` daemon with configuration options:
 
 
-    $ docker -d -D --tls=true --tlscert=/var/docker/server.pem --tlskey=/var/docker/serverkey.pem -H tcp://192.168.59.3:2376
+    $ docker daemon -D --tls=true --tlscert=/var/docker/server.pem --tlskey=/var/docker/serverkey.pem -H tcp://192.168.59.3:2376
 
 
 These options :
 These options :
 
 

+ 1 - 1
docs/articles/https.md

@@ -136,7 +136,7 @@ prevent accidental damage:
 Now you can make the Docker daemon only accept connections from clients
 Now you can make the Docker daemon only accept connections from clients
 providing a certificate trusted by our CA:
 providing a certificate trusted by our CA:
 
 
-    $ docker -d --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem \
+    $ docker daemon --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem \
       -H=0.0.0.0:2376
       -H=0.0.0.0:2376
 
 
 To be able to connect to Docker and validate its certificate, you now
 To be able to connect to Docker and validate its certificate, you now

+ 2 - 2
docs/articles/networking.md

@@ -503,7 +503,7 @@ To assign globally routable IPv6 addresses to your containers you have to
 specify an IPv6 subnet to pick the addresses from. Set the IPv6 subnet via the
 specify an IPv6 subnet to pick the addresses from. Set the IPv6 subnet via the
 `--fixed-cidr-v6` parameter when starting Docker daemon:
 `--fixed-cidr-v6` parameter when starting Docker daemon:
 
 
-    docker -d --ipv6 --fixed-cidr-v6="2001:db8:1::/64"
+    docker daemon --ipv6 --fixed-cidr-v6="2001:db8:1::/64"
 
 
 The subnet for Docker containers should at least have a size of `/80`. This way
 The subnet for Docker containers should at least have a size of `/80`. This way
 an IPv6 address can end with the container's MAC address and you prevent NDP
 an IPv6 address can end with the container's MAC address and you prevent NDP
@@ -589,7 +589,7 @@ Let's split up the configurable address range into two subnets
 `2001:db8::c000/125` and `2001:db8::c008/125`. The first one can be used by the
 `2001:db8::c000/125` and `2001:db8::c008/125`. The first one can be used by the
 host itself, the latter by Docker:
 host itself, the latter by Docker:
 
 
-    docker -d --ipv6 --fixed-cidr-v6 2001:db8::c008/125
+    docker daemon --ipv6 --fixed-cidr-v6 2001:db8::c008/125
 
 
 You notice the Docker subnet is within the subnet managed by your router that
 You notice the Docker subnet is within the subnet managed by your router that
 is connected to `eth0`. This means all devices (containers) with the addresses
 is connected to `eth0`. This means all devices (containers) with the addresses

+ 2 - 2
docs/articles/registry_mirror.md

@@ -36,11 +36,11 @@ There are two steps to set up and use a local registry mirror.
 You will need to pass the `--registry-mirror` option to your Docker daemon on
 You will need to pass the `--registry-mirror` option to your Docker daemon on
 startup:
 startup:
 
 
-    docker --registry-mirror=http://<my-docker-mirror-host> -d
+    docker daemon --registry-mirror=http://<my-docker-mirror-host>
 
 
 For example, if your mirror is serving on `http://10.0.0.2:5000`, you would run:
 For example, if your mirror is serving on `http://10.0.0.2:5000`, you would run:
 
 
-    docker --registry-mirror=http://10.0.0.2:5000 -d
+    docker daemon --registry-mirror=http://10.0.0.2:5000
 
 
 **NOTE:**
 **NOTE:**
 Depending on your local host setup, you may be able to add the
 Depending on your local host setup, you may be able to add the

+ 1 - 1
docs/articles/systemd.md

@@ -70,7 +70,7 @@ In this example, we'll assume that your `docker.service` file looks something li
     [Service]
     [Service]
     Type=notify
     Type=notify
     EnvironmentFile=-/etc/sysconfig/docker
     EnvironmentFile=-/etc/sysconfig/docker
-    ExecStart=/usr/bin/docker -d -H fd:// $OPTIONS
+    ExecStart=/usr/bin/docker daemon -H fd:// $OPTIONS
     LimitNOFILE=1048576
     LimitNOFILE=1048576
     LimitNPROC=1048576
     LimitNPROC=1048576
 
 

+ 1 - 1
docs/installation/binaries.md

@@ -174,7 +174,7 @@ For example:
 ## Run the Docker daemon
 ## Run the Docker daemon
 
 
     # start the docker in daemon mode from the directory you unpacked
     # start the docker in daemon mode from the directory you unpacked
-    $ sudo ./docker -d &
+    $ sudo ./docker daemon &
 
 
 ## Giving non-root access
 ## Giving non-root access
 
 

+ 1 - 1
docs/installation/ubuntulinux.md

@@ -185,7 +185,7 @@ To create the `docker` group and add your user:
 
 
 	If this fails with a message similar to this:
 	If this fails with a message similar to this:
 
 
-		Cannot connect to the Docker daemon. Is 'docker -d' running on this host?
+		Cannot connect to the Docker daemon. Is 'docker daemon' running on this host?
 
 
 	Check that the `DOCKER_HOST` environment variable is not set for your shell.
 	Check that the `DOCKER_HOST` environment variable is not set for your shell.
 	If it is, unset it.
 	If it is, unset it.

+ 2 - 2
docs/project/set-up-dev-env.md

@@ -285,7 +285,7 @@ with the `make.sh` script.
 
 
 8. Start a `docker` daemon running inside your container.
 8. Start a `docker` daemon running inside your container.
 
 
-        root@5f8630b873fe:/go/src/github.com/docker/docker#  docker -dD
+        root@5f8630b873fe:/go/src/github.com/docker/docker#  docker daemon -D
 
 
     The `-dD` flag starts the daemon in debug mode. You'll find this useful
     The `-dD` flag starts the daemon in debug mode. You'll find this useful
     when debugging your code.
     when debugging your code.
@@ -411,7 +411,7 @@ onto the `/go` directory inside the container.
 
 
     * copy the binary inside the development container using
     * copy the binary inside the development container using
       `cp bundles/1.5.0-dev/binary/docker /usr/bin`
       `cp bundles/1.5.0-dev/binary/docker /usr/bin`
-    * start `docker -dD` to launch the Docker daemon inside the container
+    * start `docker daemon -D` to launch the Docker daemon inside the container
     * run `docker ps` on local host to get the development container's name
     * run `docker ps` on local host to get the development container's name
     * connect to your running container `docker exec -it container_name bash`
     * connect to your running container `docker exec -it container_name bash`
     * use the `docker run hello-world` command to create and run a container 
     * use the `docker run hello-world` command to create and run a container 

+ 1 - 1
docs/project/test-and-docs.md

@@ -120,7 +120,7 @@ Run the entire test suite on your current repository:
         PASS
         PASS
         coverage: 70.8% of statements
         coverage: 70.8% of statements
         ---> Making bundle: test-docker-py (in bundles/1.5.0-dev/test-docker-py)
         ---> Making bundle: test-docker-py (in bundles/1.5.0-dev/test-docker-py)
-        +++ exec docker --daemon --debug --host unix:///go/src/github.com/docker/docker/bundles/1.5.0-dev/test-docker-py/docker.sock --storage-driver vfs --exec-driver native --pidfile /go/src/github.com/docker/docker/bundles/1.5.0-dev/test-docker-py/docker.pid
+        +++ exec docker daemon --debug --host unix:///go/src/github.com/docker/docker/bundles/1.5.0-dev/test-docker-py/docker.sock --storage-driver vfs --exec-driver native --pidfile /go/src/github.com/docker/docker/bundles/1.5.0-dev/test-docker-py/docker.pid
         .................................................................
         .................................................................
         ----------------------------------------------------------------------
         ----------------------------------------------------------------------
         Ran 65 tests in 89.266s
         Ran 65 tests in 89.266s

+ 1 - 1
docs/reference/api/docker_remote_api_v1.20.md

@@ -2269,4 +2269,4 @@ To set cross origin requests to the remote api please give values to
 `--api-cors-header` when running Docker in daemon mode. Set * (asterisk) allows all,
 `--api-cors-header` when running Docker in daemon mode. Set * (asterisk) allows all,
 default or blank means CORS disabled
 default or blank means CORS disabled
 
 
-    $ docker -d -H="192.168.1.9:2375" --api-cors-header="http://foo.bar"
+    $ docker daemon -H="192.168.1.9:2375" --api-cors-header="http://foo.bar"

+ 3 - 0
docs/reference/commandline/cli.md

@@ -19,6 +19,9 @@ or execute `docker help`:
 
 
     $ docker
     $ docker
       Usage: docker [OPTIONS] COMMAND [arg...]
       Usage: docker [OPTIONS] COMMAND [arg...]
+             docker daemon [ --help | ... ]
+             docker [ -h | --help | -v | --version ]
+
         -H, --host=[]: The socket(s) to bind to in daemon mode, specified using one or more tcp://host:port, unix:///path/to/socket, fd://* or fd://socketfd.
         -H, --host=[]: The socket(s) to bind to in daemon mode, specified using one or more tcp://host:port, unix:///path/to/socket, fd://* or fd://socketfd.
 
 
       A self-sufficient runtime for Linux containers.
       A self-sufficient runtime for Linux containers.

+ 28 - 31
docs/reference/commandline/daemon.md

@@ -10,7 +10,7 @@ parent = "smn_cli"
 
 
 # daemon
 # daemon
 
 
-    Usage: docker [OPTIONS] COMMAND [arg...]
+    Usage: docker daemon [OPTIONS]
 
 
     A self-sufficient runtime for linux containers.
     A self-sufficient runtime for linux containers.
 
 
@@ -18,9 +18,7 @@ parent = "smn_cli"
       --api-cors-header=""                   Set CORS headers in the remote API
       --api-cors-header=""                   Set CORS headers in the remote API
       -b, --bridge=""                        Attach containers to a network bridge
       -b, --bridge=""                        Attach containers to a network bridge
       --bip=""                               Specify network bridge IP
       --bip=""                               Specify network bridge IP
-      --config=~/.docker                     Location of client config files
       -D, --debug=false                      Enable debug mode
       -D, --debug=false                      Enable debug mode
-      -d, --daemon=false                     Enable daemon mode
       --default-gateway=""                   Container default gateway IPv4 address
       --default-gateway=""                   Container default gateway IPv4 address
       --default-gateway-v6=""                Container default gateway IPv6 address
       --default-gateway-v6=""                Container default gateway IPv6 address
       --dns=[]                               DNS server to use
       --dns=[]                               DNS server to use
@@ -58,15 +56,14 @@ parent = "smn_cli"
       --tlskey="~/.docker/key.pem"           Path to TLS key file
       --tlskey="~/.docker/key.pem"           Path to TLS key file
       --tlsverify=false                      Use TLS and verify the remote
       --tlsverify=false                      Use TLS and verify the remote
       --userland-proxy=true                  Use userland proxy for loopback traffic
       --userland-proxy=true                  Use userland proxy for loopback traffic
-      -v, --version=false                    Print version information and quit
 
 
 Options with [] may be specified multiple times.
 Options with [] may be specified multiple times.
 
 
 The Docker daemon is the persistent process that manages containers. Docker
 The Docker daemon is the persistent process that manages containers. Docker
 uses the same binary for both the daemon and client. To run the daemon you
 uses the same binary for both the daemon and client. To run the daemon you
-provide the `-d` flag.
+type `docker daemon`.
 
 
-To run the daemon with debug output, use `docker -d -D`.
+To run the daemon with debug output, use `docker daemon -D`.
 
 
 ## Daemon socket option
 ## Daemon socket option
 
 
@@ -94,8 +91,8 @@ communication with the daemon.
 
 
 On Systemd based systems, you can communicate with the daemon via
 On Systemd based systems, you can communicate with the daemon via
 [Systemd socket activation](http://0pointer.de/blog/projects/socket-activation.html),
 [Systemd socket activation](http://0pointer.de/blog/projects/socket-activation.html),
-use `docker -d -H fd://`. Using `fd://` will work perfectly for most setups but
-you can also specify individual sockets: `docker -d -H fd://3`. If the
+use `docker daemon -H fd://`. Using `fd://` will work perfectly for most setups but
+you can also specify individual sockets: `docker daemon -H fd://3`. If the
 specified socket activated files aren't found, then Docker will exit. You can
 specified socket activated files aren't found, then Docker will exit. You can
 find examples of using Systemd socket activation with Docker and Systemd in the
 find examples of using Systemd socket activation with Docker and Systemd in the
 [Docker source tree](https://github.com/docker/docker/tree/master/contrib/init/systemd/).
 [Docker source tree](https://github.com/docker/docker/tree/master/contrib/init/systemd/).
@@ -104,7 +101,7 @@ You can configure the Docker daemon to listen to multiple sockets at the same
 time using multiple `-H` options:
 time using multiple `-H` options:
 
 
     # listen using the default unix socket, and on 2 specific IP addresses on this host.
     # listen using the default unix socket, and on 2 specific IP addresses on this host.
-    docker -d -H unix:///var/run/docker.sock -H tcp://192.168.59.106 -H tcp://10.10.10.2
+    docker daemon -H unix:///var/run/docker.sock -H tcp://192.168.59.106 -H tcp://10.10.10.2
 
 
 The Docker client will honor the `DOCKER_HOST` environment variable to set the
 The Docker client will honor the `DOCKER_HOST` environment variable to set the
 `-H` flag for the client.
 `-H` flag for the client.
@@ -152,16 +149,16 @@ article explains how to tune your existing setup without the use of options.
 
 
 The `btrfs` driver is very fast for `docker build` - but like `devicemapper`
 The `btrfs` driver is very fast for `docker build` - but like `devicemapper`
 does not share executable memory between devices. Use
 does not share executable memory between devices. Use
-`docker -d -s btrfs -g /mnt/btrfs_partition`.
+`docker daemon -s btrfs -g /mnt/btrfs_partition`.
 
 
 The `zfs` driver is probably not fast as `btrfs` but has a longer track record
 The `zfs` driver is probably not fast as `btrfs` but has a longer track record
 on stability. Thanks to `Single Copy ARC` shared blocks between clones will be
 on stability. Thanks to `Single Copy ARC` shared blocks between clones will be
-cached only once. Use `docker -d -s zfs`. To select a different zfs filesystem
+cached only once. Use `docker daemon -s zfs`. To select a different zfs filesystem
 set `zfs.fsname` option as described in [Storage driver options](#storage-driver-options).
 set `zfs.fsname` option as described in [Storage driver options](#storage-driver-options).
 
 
 The `overlay` is a very fast union filesystem. It is now merged in the main
 The `overlay` is a very fast union filesystem. It is now merged in the main
 Linux kernel as of [3.18.0](https://lkml.org/lkml/2014/10/26/137). Call
 Linux kernel as of [3.18.0](https://lkml.org/lkml/2014/10/26/137). Call
-`docker -d -s overlay` to use it.
+`docker daemon -s overlay` to use it.
 
 
 > **Note:**
 > **Note:**
 > As promising as `overlay` is, the feature is still quite young and should not
 > As promising as `overlay` is, the feature is still quite young and should not
@@ -196,7 +193,7 @@ options for `zfs` start with `zfs`.
 
 
      Example use:
      Example use:
 
 
-        docker -d --storage-opt dm.thinpooldev=/dev/mapper/thin-pool
+        docker daemon --storage-opt dm.thinpooldev=/dev/mapper/thin-pool
 
 
  *  `dm.basesize`
  *  `dm.basesize`
 
 
@@ -216,7 +213,7 @@ options for `zfs` start with `zfs`.
 
 
     Example use:
     Example use:
 
 
-        $ docker -d --storage-opt dm.basesize=20G
+        $ docker daemon --storage-opt dm.basesize=20G
 
 
  *  `dm.loopdatasize`
  *  `dm.loopdatasize`
 
 
@@ -229,7 +226,7 @@ options for `zfs` start with `zfs`.
 
 
     Example use:
     Example use:
 
 
-        $ docker -d --storage-opt dm.loopdatasize=200G
+        $ docker daemon --storage-opt dm.loopdatasize=200G
 
 
  *  `dm.loopmetadatasize`
  *  `dm.loopmetadatasize`
 
 
@@ -242,7 +239,7 @@ options for `zfs` start with `zfs`.
 
 
     Example use:
     Example use:
 
 
-        $ docker -d --storage-opt dm.loopmetadatasize=4G
+        $ docker daemon --storage-opt dm.loopmetadatasize=4G
 
 
  *  `dm.fs`
  *  `dm.fs`
 
 
@@ -251,7 +248,7 @@ options for `zfs` start with `zfs`.
 
 
     Example use:
     Example use:
 
 
-        $ docker -d --storage-opt dm.fs=xfs
+        $ docker daemon --storage-opt dm.fs=xfs
 
 
  *  `dm.mkfsarg`
  *  `dm.mkfsarg`
 
 
@@ -259,7 +256,7 @@ options for `zfs` start with `zfs`.
 
 
     Example use:
     Example use:
 
 
-        $ docker -d --storage-opt "dm.mkfsarg=-O ^has_journal"
+        $ docker daemon --storage-opt "dm.mkfsarg=-O ^has_journal"
 
 
  *  `dm.mountopt`
  *  `dm.mountopt`
 
 
@@ -267,7 +264,7 @@ options for `zfs` start with `zfs`.
 
 
     Example use:
     Example use:
 
 
-        $ docker -d --storage-opt dm.mountopt=nodiscard
+        $ docker daemon --storage-opt dm.mountopt=nodiscard
 
 
  *  `dm.datadev`
  *  `dm.datadev`
 
 
@@ -281,7 +278,7 @@ options for `zfs` start with `zfs`.
 
 
     Example use:
     Example use:
 
 
-        $ docker -d --storage-opt dm.datadev=/dev/sdb1 --storage-opt dm.metadatadev=/dev/sdc1
+        $ docker daemon --storage-opt dm.datadev=/dev/sdb1 --storage-opt dm.metadatadev=/dev/sdc1
 
 
  *  `dm.metadatadev`
  *  `dm.metadatadev`
 
 
@@ -299,7 +296,7 @@ options for `zfs` start with `zfs`.
 
 
     Example use:
     Example use:
 
 
-        $ docker -d --storage-opt dm.datadev=/dev/sdb1 --storage-opt dm.metadatadev=/dev/sdc1
+        $ docker daemon --storage-opt dm.datadev=/dev/sdb1 --storage-opt dm.metadatadev=/dev/sdc1
 
 
  *  `dm.blocksize`
  *  `dm.blocksize`
 
 
@@ -308,7 +305,7 @@ options for `zfs` start with `zfs`.
 
 
     Example use:
     Example use:
 
 
-        $ docker -d --storage-opt dm.blocksize=512K
+        $ docker daemon --storage-opt dm.blocksize=512K
 
 
  *  `dm.blkdiscard`
  *  `dm.blkdiscard`
 
 
@@ -322,7 +319,7 @@ options for `zfs` start with `zfs`.
 
 
     Example use:
     Example use:
 
 
-        $ docker -d --storage-opt dm.blkdiscard=false
+        $ docker daemon --storage-opt dm.blkdiscard=false
 
 
  *  `dm.override_udev_sync_check`
  *  `dm.override_udev_sync_check`
 
 
@@ -348,7 +345,7 @@ options for `zfs` start with `zfs`.
     To allow the `docker` daemon to start, regardless of `udev` sync not being
     To allow the `docker` daemon to start, regardless of `udev` sync not being
     supported, set `dm.override_udev_sync_check` to true:
     supported, set `dm.override_udev_sync_check` to true:
 
 
-        $ docker -d --storage-opt dm.override_udev_sync_check=true
+        $ docker daemon --storage-opt dm.override_udev_sync_check=true
 
 
     When this value is `true`, the  `devicemapper` continues and simply warns
     When this value is `true`, the  `devicemapper` continues and simply warns
     you the errors are happening.
     you the errors are happening.
@@ -373,7 +370,7 @@ Currently supported options of `zfs`:
 
 
     Example use:
     Example use:
 
 
-        $ docker -d -s zfs --storage-opt zfs.fsname=zroot/docker
+        $ docker daemon -s zfs --storage-opt zfs.fsname=zroot/docker
 
 
 ## Docker execdriver option
 ## Docker execdriver option
 
 
@@ -397,17 +394,17 @@ it is not available, the system uses `cgroupfs`. By default, if no option is
 specified, the execdriver first tries `systemd` and falls back to `cgroupfs`.
 specified, the execdriver first tries `systemd` and falls back to `cgroupfs`.
 This example sets the execdriver to `cgroupfs`:
 This example sets the execdriver to `cgroupfs`:
 
 
-    $ sudo docker -d --exec-opt native.cgroupdriver=cgroupfs
+    $ sudo docker daemon --exec-opt native.cgroupdriver=cgroupfs
 
 
 Setting this option applies to all containers the daemon launches.
 Setting this option applies to all containers the daemon launches.
 
 
 ## Daemon DNS options
 ## Daemon DNS options
 
 
 To set the DNS server for all Docker containers, use
 To set the DNS server for all Docker containers, use
-`docker -d --dns 8.8.8.8`.
+`docker daemon --dns 8.8.8.8`.
 
 
 To set the DNS search domain for all Docker containers, use
 To set the DNS search domain for all Docker containers, use
-`docker -d --dns-search example.com`.
+`docker daemon --dns-search example.com`.
 
 
 ## Insecure registries
 ## Insecure registries
 
 
@@ -456,7 +453,7 @@ need to be added to your Docker host's configuration:
 1. Install the `ca-certificates` package for your distribution
 1. Install the `ca-certificates` package for your distribution
 2. Ask your network admin for the proxy's CA certificate and append them to
 2. Ask your network admin for the proxy's CA certificate and append them to
    `/etc/pki/tls/certs/ca-bundle.crt`
    `/etc/pki/tls/certs/ca-bundle.crt`
-3. Then start your Docker daemon with `HTTPS_PROXY=http://username:password@proxy:port/ docker -d`.
+3. Then start your Docker daemon with `HTTPS_PROXY=http://username:password@proxy:port/ docker daemon`.
    The `username:` and `password@` are optional - and are only needed if your
    The `username:` and `password@` are optional - and are only needed if your
    proxy is set up to require authentication.
    proxy is set up to require authentication.
 
 
@@ -486,9 +483,9 @@ Docker supports softlinks for the Docker data directory (`/var/lib/docker`) and
 for `/var/lib/docker/tmp`. The `DOCKER_TMPDIR` and the data directory can be
 for `/var/lib/docker/tmp`. The `DOCKER_TMPDIR` and the data directory can be
 set like this:
 set like this:
 
 
-    DOCKER_TMPDIR=/mnt/disk2/tmp /usr/local/bin/docker -d -D -g /var/lib/docker -H unix:// > /var/lib/boot2docker/docker.log 2>&1
+    DOCKER_TMPDIR=/mnt/disk2/tmp /usr/local/bin/docker daemon -D -g /var/lib/docker -H unix:// > /var/lib/boot2docker/docker.log 2>&1
     # or
     # or
     export DOCKER_TMPDIR=/mnt/disk2/tmp
     export DOCKER_TMPDIR=/mnt/disk2/tmp
-    /usr/local/bin/docker -d -D -g /var/lib/docker -H unix:// > /var/lib/boot2docker/docker.log 2>&1
+    /usr/local/bin/docker daemon -D -g /var/lib/docker -H unix:// > /var/lib/boot2docker/docker.log 2>&1
 
 
 
 

+ 1 - 1
docs/userguide/labels-custom-metadata.md

@@ -164,7 +164,7 @@ List all images with `vendor` `ACME`:
 ## Daemon labels
 ## Daemon labels
 
 
 
 
-    docker -d \
+    docker daemon \
       --dns 8.8.8.8 \
       --dns 8.8.8.8 \
       --dns 8.8.4.4 \
       --dns 8.8.4.4 \
       -H unix:///var/run/docker.sock \
       -H unix:///var/run/docker.sock \

+ 1 - 1
integration-cli/docker_cli_build_test.go

@@ -79,7 +79,7 @@ func (s *DockerSuite) TestBuildShCmdJSONEntrypoint(c *check.C) {
 	out, _ := dockerCmd(c, "run", "--rm", name)
 	out, _ := dockerCmd(c, "run", "--rm", name)
 
 
 	if strings.TrimSpace(out) != "/bin/sh -c echo test" {
 	if strings.TrimSpace(out) != "/bin/sh -c echo test" {
-		c.Fatal("CMD did not contain /bin/sh -c")
+		c.Fatalf("CMD did not contain /bin/sh -c : %s", out)
 	}
 	}
 
 
 }
 }

+ 103 - 15
integration-cli/docker_cli_daemon_test.go

@@ -324,6 +324,100 @@ func (s *DockerDaemonSuite) TestDaemonLogLevelWrong(c *check.C) {
 	c.Assert(s.d.Start("--log-level=bogus"), check.NotNil, check.Commentf("Daemon shouldn't start with wrong log level"))
 	c.Assert(s.d.Start("--log-level=bogus"), check.NotNil, check.Commentf("Daemon shouldn't start with wrong log level"))
 }
 }
 
 
+func (s *DockerSuite) TestDaemonStartWithBackwardCompatibility(c *check.C) {
+
+	var validCommandArgs = [][]string{
+		{"--selinux-enabled", "-l", "info"},
+		{"--insecure-registry", "daemon"},
+	}
+
+	var invalidCommandArgs = [][]string{
+		{"--selinux-enabled", "--storage-opt"},
+		{"-D", "-b"},
+		{"--config", "/tmp"},
+	}
+
+	for _, args := range validCommandArgs {
+		d := NewDaemon(c)
+		d.Command = "--daemon"
+		if err := d.Start(args...); err != nil {
+			c.Fatalf("Daemon should have started successfully with --daemon %v: %v", args, err)
+		}
+		d.Stop()
+	}
+
+	for _, args := range invalidCommandArgs {
+		d := NewDaemon(c)
+		if err := d.Start(args...); err == nil {
+			d.Stop()
+			c.Fatalf("Daemon should have failed to start with %v", args)
+		}
+	}
+}
+
+func (s *DockerSuite) TestDaemonStartWithDaemonCommand(c *check.C) {
+
+	type kind int
+
+	const (
+		common kind = iota
+		daemon
+	)
+
+	var flags = []map[kind][]string{
+		{common: {"-l", "info"}, daemon: {"--selinux-enabled"}},
+		{common: {"-D"}, daemon: {"--selinux-enabled", "-r"}},
+		{common: {"-D"}, daemon: {"--restart"}},
+		{common: {"--debug"}, daemon: {"--log-driver=json-file", "--log-opt=max-size=1k"}},
+	}
+
+	var invalidGlobalFlags = [][]string{
+		//Invalid because you cannot pass daemon flags as global flags.
+		{"--selinux-enabled", "-l", "info"},
+		{"-D", "-r"},
+		{"--config", "/tmp"},
+	}
+
+	// `docker daemon -l info --selinux-enabled`
+	// should NOT error out
+	for _, f := range flags {
+		d := NewDaemon(c)
+		args := append(f[common], f[daemon]...)
+		if err := d.Start(args...); err != nil {
+			c.Fatalf("Daemon should have started successfully with %v: %v", args, err)
+		}
+		d.Stop()
+	}
+
+	// `docker -l info daemon --selinux-enabled`
+	// should error out
+	for _, f := range flags {
+		d := NewDaemon(c)
+		d.GlobalFlags = f[common]
+		if err := d.Start(f[daemon]...); err == nil {
+			d.Stop()
+			c.Fatalf("Daemon should have failed to start with docker %v daemon %v", d.GlobalFlags, f[daemon])
+		}
+	}
+
+	for _, f := range invalidGlobalFlags {
+		cmd := exec.Command(dockerBinary, append(f, "daemon")...)
+		errch := make(chan error)
+		var err error
+		go func() {
+			errch <- cmd.Run()
+		}()
+		select {
+		case <-time.After(time.Second):
+			cmd.Process.Kill()
+		case err = <-errch:
+		}
+		if err == nil {
+			c.Fatalf("Daemon should have failed to start with docker %v daemon", f)
+		}
+	}
+}
+
 func (s *DockerDaemonSuite) TestDaemonLogLevelDebug(c *check.C) {
 func (s *DockerDaemonSuite) TestDaemonLogLevelDebug(c *check.C) {
 	if err := s.d.Start("--log-level=debug"); err != nil {
 	if err := s.d.Start("--log-level=debug"); err != nil {
 		c.Fatal(err)
 		c.Fatal(err)
@@ -382,7 +476,7 @@ func (s *DockerDaemonSuite) TestDaemonAllocatesListeningPort(c *check.C) {
 		{"localhost", "127.0.0.1", "1235"},
 		{"localhost", "127.0.0.1", "1235"},
 	}
 	}
 
 
-	cmdArgs := []string{}
+	cmdArgs := make([]string, 0, len(listeningPorts)*2)
 	for _, hostDirective := range listeningPorts {
 	for _, hostDirective := range listeningPorts {
 		cmdArgs = append(cmdArgs, "--host", fmt.Sprintf("tcp://%s:%s", hostDirective[0], hostDirective[2]))
 		cmdArgs = append(cmdArgs, "--host", fmt.Sprintf("tcp://%s:%s", hostDirective[0], hostDirective[2]))
 	}
 	}
@@ -798,8 +892,7 @@ func (s *DockerDaemonSuite) TestDaemonLinksIpTablesRulesWhenLinkAndUnlink(c *che
 	c.Assert(err, check.IsNil, check.Commentf(out))
 	c.Assert(err, check.IsNil, check.Commentf(out))
 	defer deleteInterface(c, bridgeName)
 	defer deleteInterface(c, bridgeName)
 
 
-	args := []string{"--bridge", bridgeName, "--icc=false"}
-	err = s.d.StartWithBusybox(args...)
+	err = s.d.StartWithBusybox("--bridge", bridgeName, "--icc=false")
 	c.Assert(err, check.IsNil)
 	c.Assert(err, check.IsNil)
 	defer s.d.Restart()
 	defer s.d.Restart()
 
 
@@ -1210,7 +1303,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartKillWait(c *check.C) {
 // TestHttpsInfo connects via two-way authenticated HTTPS to the info endpoint
 // TestHttpsInfo connects via two-way authenticated HTTPS to the info endpoint
 func (s *DockerDaemonSuite) TestHttpsInfo(c *check.C) {
 func (s *DockerDaemonSuite) TestHttpsInfo(c *check.C) {
 	const (
 	const (
-		testDaemonHTTPSAddr = "localhost:4271"
+		testDaemonHTTPSAddr = "tcp://localhost:4271"
 	)
 	)
 
 
 	if err := s.d.Start("--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-cert.pem",
 	if err := s.d.Start("--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-cert.pem",
@@ -1218,9 +1311,7 @@ func (s *DockerDaemonSuite) TestHttpsInfo(c *check.C) {
 		c.Fatalf("Could not start daemon with busybox: %v", err)
 		c.Fatalf("Could not start daemon with busybox: %v", err)
 	}
 	}
 
 
-	//force tcp protocol
-	host := fmt.Sprintf("tcp://%s", testDaemonHTTPSAddr)
-	daemonArgs := []string{"--host", host, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-cert.pem", "--tlskey", "fixtures/https/client-key.pem"}
+	daemonArgs := []string{"--host", testDaemonHTTPSAddr, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-cert.pem", "--tlskey", "fixtures/https/client-key.pem"}
 	out, err := s.d.CmdWithArgs(daemonArgs, "info")
 	out, err := s.d.CmdWithArgs(daemonArgs, "info")
 	if err != nil {
 	if err != nil {
 		c.Fatalf("Error Occurred: %s and output: %s", err, out)
 		c.Fatalf("Error Occurred: %s and output: %s", err, out)
@@ -1232,16 +1323,15 @@ func (s *DockerDaemonSuite) TestHttpsInfo(c *check.C) {
 func (s *DockerDaemonSuite) TestHttpsInfoRogueCert(c *check.C) {
 func (s *DockerDaemonSuite) TestHttpsInfoRogueCert(c *check.C) {
 	const (
 	const (
 		errBadCertificate   = "remote error: bad certificate"
 		errBadCertificate   = "remote error: bad certificate"
-		testDaemonHTTPSAddr = "localhost:4271"
+		testDaemonHTTPSAddr = "tcp://localhost:4271"
 	)
 	)
+
 	if err := s.d.Start("--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-cert.pem",
 	if err := s.d.Start("--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-cert.pem",
 		"--tlskey", "fixtures/https/server-key.pem", "-H", testDaemonHTTPSAddr); err != nil {
 		"--tlskey", "fixtures/https/server-key.pem", "-H", testDaemonHTTPSAddr); err != nil {
 		c.Fatalf("Could not start daemon with busybox: %v", err)
 		c.Fatalf("Could not start daemon with busybox: %v", err)
 	}
 	}
 
 
-	//force tcp protocol
-	host := fmt.Sprintf("tcp://%s", testDaemonHTTPSAddr)
-	daemonArgs := []string{"--host", host, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-rogue-cert.pem", "--tlskey", "fixtures/https/client-rogue-key.pem"}
+	daemonArgs := []string{"--host", testDaemonHTTPSAddr, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-rogue-cert.pem", "--tlskey", "fixtures/https/client-rogue-key.pem"}
 	out, err := s.d.CmdWithArgs(daemonArgs, "info")
 	out, err := s.d.CmdWithArgs(daemonArgs, "info")
 	if err == nil || !strings.Contains(out, errBadCertificate) {
 	if err == nil || !strings.Contains(out, errBadCertificate) {
 		c.Fatalf("Expected err: %s, got instead: %s and output: %s", errBadCertificate, err, out)
 		c.Fatalf("Expected err: %s, got instead: %s and output: %s", errBadCertificate, err, out)
@@ -1253,16 +1343,14 @@ func (s *DockerDaemonSuite) TestHttpsInfoRogueCert(c *check.C) {
 func (s *DockerDaemonSuite) TestHttpsInfoRogueServerCert(c *check.C) {
 func (s *DockerDaemonSuite) TestHttpsInfoRogueServerCert(c *check.C) {
 	const (
 	const (
 		errCaUnknown             = "x509: certificate signed by unknown authority"
 		errCaUnknown             = "x509: certificate signed by unknown authority"
-		testDaemonRogueHTTPSAddr = "localhost:4272"
+		testDaemonRogueHTTPSAddr = "tcp://localhost:4272"
 	)
 	)
 	if err := s.d.Start("--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-rogue-cert.pem",
 	if err := s.d.Start("--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-rogue-cert.pem",
 		"--tlskey", "fixtures/https/server-rogue-key.pem", "-H", testDaemonRogueHTTPSAddr); err != nil {
 		"--tlskey", "fixtures/https/server-rogue-key.pem", "-H", testDaemonRogueHTTPSAddr); err != nil {
 		c.Fatalf("Could not start daemon with busybox: %v", err)
 		c.Fatalf("Could not start daemon with busybox: %v", err)
 	}
 	}
 
 
-	//force tcp protocol
-	host := fmt.Sprintf("tcp://%s", testDaemonRogueHTTPSAddr)
-	daemonArgs := []string{"--host", host, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-rogue-cert.pem", "--tlskey", "fixtures/https/client-rogue-key.pem"}
+	daemonArgs := []string{"--host", testDaemonRogueHTTPSAddr, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-rogue-cert.pem", "--tlskey", "fixtures/https/client-rogue-key.pem"}
 	out, err := s.d.CmdWithArgs(daemonArgs, "info")
 	out, err := s.d.CmdWithArgs(daemonArgs, "info")
 	if err == nil || !strings.Contains(out, errCaUnknown) {
 	if err == nil || !strings.Contains(out, errCaUnknown) {
 		c.Fatalf("Expected err: %s, got instead: %s and output: %s", errCaUnknown, err, out)
 		c.Fatalf("Expected err: %s, got instead: %s and output: %s", errCaUnknown, err, out)

+ 25 - 13
integration-cli/docker_cli_help_test.go

@@ -89,10 +89,18 @@ func (s *DockerSuite) TestHelpTextVerify(c *check.C) {
 			c.Fatalf("Missing 'Commands:' in:\n%s", out)
 			c.Fatalf("Missing 'Commands:' in:\n%s", out)
 		}
 		}
 
 
-		// Grab all chars starting at "Commands:"
-		// Skip first line, its "Commands:"
 		cmds := []string{}
 		cmds := []string{}
-		for _, cmd := range strings.Split(out[i:], "\n")[1:] {
+		// Grab all chars starting at "Commands:"
+		helpOut := strings.Split(out[i:], "\n")
+		// First line is just "Commands:"
+		if isLocalDaemon {
+			// Replace first line with "daemon" command since it's not part of the list of commands.
+			helpOut[0] = " daemon"
+		} else {
+			// Skip first line
+			helpOut = helpOut[1:]
+		}
+		for _, cmd := range helpOut {
 			var stderr string
 			var stderr string
 
 
 			// Stop on blank line or non-idented line
 			// Stop on blank line or non-idented line
@@ -192,9 +200,10 @@ func (s *DockerSuite) TestHelpTextVerify(c *check.C) {
 				// lead to incorrect test result (like false negative).
 				// lead to incorrect test result (like false negative).
 				// Whatever the reason, skip trying to run w/o args and
 				// Whatever the reason, skip trying to run w/o args and
 				// jump to trying with a bogus arg.
 				// jump to trying with a bogus arg.
-				skipNoArgs := map[string]string{
-					"events": "",
-					"load":   "",
+				skipNoArgs := map[string]struct{}{
+					"daemon": {},
+					"events": {},
+					"load":   {},
 				}
 				}
 
 
 				ec = 0
 				ec = 0
@@ -230,6 +239,9 @@ func (s *DockerSuite) TestHelpTextVerify(c *check.C) {
 		}
 		}
 
 
 		expected := 39
 		expected := 39
+		if isLocalDaemon {
+			expected++ // for the daemon command
+		}
 		if len(cmds) != expected {
 		if len(cmds) != expected {
 			c.Fatalf("Wrong # of cmds(%d), it should be: %d\nThe list:\n%q",
 			c.Fatalf("Wrong # of cmds(%d), it should be: %d\nThe list:\n%q",
 				len(cmds), expected, cmds)
 				len(cmds), expected, cmds)
@@ -246,7 +258,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) {
 	cmd := exec.Command(dockerBinary)
 	cmd := exec.Command(dockerBinary)
 	stdout, stderr, ec, err := runCommandWithStdoutStderr(cmd)
 	stdout, stderr, ec, err := runCommandWithStdoutStderr(cmd)
 	if len(stdout) == 0 || len(stderr) != 0 || ec != 0 || err != nil {
 	if len(stdout) == 0 || len(stderr) != 0 || ec != 0 || err != nil {
-		c.Fatalf("Bad results from 'docker'\nec:%d\nstdout:%s\nstderr:%s\nerr:%q", ec, stdout, stderr, err)
+		c.Fatalf("Bad results from 'docker'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err)
 	}
 	}
 	// Be really pick
 	// Be really pick
 	if strings.HasSuffix(stdout, "\n\n") {
 	if strings.HasSuffix(stdout, "\n\n") {
@@ -257,7 +269,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) {
 	cmd = exec.Command(dockerBinary, "help")
 	cmd = exec.Command(dockerBinary, "help")
 	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
 	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
 	if len(stdout) == 0 || len(stderr) != 0 || ec != 0 || err != nil {
 	if len(stdout) == 0 || len(stderr) != 0 || ec != 0 || err != nil {
-		c.Fatalf("Bad results from 'docker help'\nec:%d\nstdout:%s\nstderr:%s\nerr:%q", ec, stdout, stderr, err)
+		c.Fatalf("Bad results from 'docker help'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err)
 	}
 	}
 	// Be really pick
 	// Be really pick
 	if strings.HasSuffix(stdout, "\n\n") {
 	if strings.HasSuffix(stdout, "\n\n") {
@@ -268,7 +280,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) {
 	cmd = exec.Command(dockerBinary, "--help")
 	cmd = exec.Command(dockerBinary, "--help")
 	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
 	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
 	if len(stdout) == 0 || len(stderr) != 0 || ec != 0 || err != nil {
 	if len(stdout) == 0 || len(stderr) != 0 || ec != 0 || err != nil {
-		c.Fatalf("Bad results from 'docker --help'\nec:%d\nstdout:%s\nstderr:%s\nerr:%q", ec, stdout, stderr, err)
+		c.Fatalf("Bad results from 'docker --help'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err)
 	}
 	}
 	// Be really pick
 	// Be really pick
 	if strings.HasSuffix(stdout, "\n\n") {
 	if strings.HasSuffix(stdout, "\n\n") {
@@ -280,7 +292,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) {
 	cmd = exec.Command(dockerBinary, "inspect", "busybox")
 	cmd = exec.Command(dockerBinary, "inspect", "busybox")
 	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
 	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
 	if len(stdout) == 0 || len(stderr) != 0 || ec != 0 || err != nil {
 	if len(stdout) == 0 || len(stderr) != 0 || ec != 0 || err != nil {
-		c.Fatalf("Bad results from 'docker inspect busybox'\nec:%d\nstdout:%s\nstderr:%s\nerr:%q", ec, stdout, stderr, err)
+		c.Fatalf("Bad results from 'docker inspect busybox'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err)
 	}
 	}
 	// Be really pick
 	// Be really pick
 	if strings.HasSuffix(stdout, "\n\n") {
 	if strings.HasSuffix(stdout, "\n\n") {
@@ -292,7 +304,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) {
 	cmd = exec.Command(dockerBinary, "rm")
 	cmd = exec.Command(dockerBinary, "rm")
 	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
 	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
 	if len(stdout) != 0 || len(stderr) == 0 || ec == 0 || err == nil {
 	if len(stdout) != 0 || len(stderr) == 0 || ec == 0 || err == nil {
-		c.Fatalf("Bad results from 'docker rm'\nec:%d\nstdout:%s\nstderr:%s\nerr:%q", ec, stdout, stderr, err)
+		c.Fatalf("Bad results from 'docker rm'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err)
 	}
 	}
 	// Should not contain full help text but should contain info about
 	// Should not contain full help text but should contain info about
 	// # of args and Usage line
 	// # of args and Usage line
@@ -305,7 +317,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) {
 	cmd = exec.Command(dockerBinary, "rm", "NoSuchContainer")
 	cmd = exec.Command(dockerBinary, "rm", "NoSuchContainer")
 	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
 	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
 	if len(stdout) != 0 || len(stderr) == 0 || ec == 0 || err == nil {
 	if len(stdout) != 0 || len(stderr) == 0 || ec == 0 || err == nil {
-		c.Fatalf("Bad results from 'docker rm NoSuchContainer'\nec:%d\nstdout:%s\nstderr:%s\nerr:%q", ec, stdout, stderr, err)
+		c.Fatalf("Bad results from 'docker rm NoSuchContainer'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err)
 	}
 	}
 	// Be really picky
 	// Be really picky
 	if strings.HasSuffix(stderr, "\n\n") {
 	if strings.HasSuffix(stderr, "\n\n") {
@@ -316,7 +328,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) {
 	cmd = exec.Command(dockerBinary, "BadCmd")
 	cmd = exec.Command(dockerBinary, "BadCmd")
 	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
 	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
 	if len(stdout) != 0 || len(stderr) == 0 || ec == 0 || err == nil {
 	if len(stdout) != 0 || len(stderr) == 0 || ec == 0 || err == nil {
-		c.Fatalf("Bad results from 'docker BadCmd'\nec:%d\nstdout:%s\nstderr:%s\nerr:%q", ec, stdout, stderr, err)
+		c.Fatalf("Bad results from 'docker BadCmd'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err)
 	}
 	}
 	if stderr != "docker: 'BadCmd' is not a docker command.\nSee 'docker --help'.\n" {
 	if stderr != "docker: 'BadCmd' is not a docker command.\nSee 'docker --help'.\n" {
 		c.Fatalf("Unexcepted output for 'docker badCmd'\nstderr:%s", stderr)
 		c.Fatalf("Unexcepted output for 'docker badCmd'\nstderr:%s", stderr)

+ 11 - 0
integration-cli/docker_cli_run_test.go

@@ -217,6 +217,17 @@ func (s *DockerSuite) TestRunLinksContainerWithContainerId(c *check.C) {
 	}
 	}
 }
 }
 
 
+// Issue 9677.
+func (s *DockerSuite) TestRunWithDaemonFlags(c *check.C) {
+	out, _, err := dockerCmdWithError(c, "--selinux-enabled", "run", "-i", "-t", "busybox", "true")
+	if err != nil {
+		if !strings.Contains(out, "must follow the 'docker daemon' command") && // daemon
+			!strings.Contains(out, "flag provided but not defined: --selinux-enabled") { // no daemon (client-only)
+			c.Fatal(err, out)
+		}
+	}
+}
+
 // Regression test for #4979
 // Regression test for #4979
 func (s *DockerSuite) TestRunWithVolumesFromExited(c *check.C) {
 func (s *DockerSuite) TestRunWithVolumesFromExited(c *check.C) {
 	out, exitCode := dockerCmd(c, "run", "--name", "test-data", "--volume", "/some/dir", "busybox", "touch", "/some/dir/file")
 	out, exitCode := dockerCmd(c, "run", "--name", "test-data", "--volume", "/some/dir", "busybox", "touch", "/some/dir/file")

+ 24 - 15
integration-cli/docker_utils.go

@@ -29,6 +29,12 @@ import (
 
 
 // Daemon represents a Docker daemon for the testing framework.
 // Daemon represents a Docker daemon for the testing framework.
 type Daemon struct {
 type Daemon struct {
+	// Defaults to "daemon"
+	// Useful to set to --daemon or -d for checking backwards compatability
+	Command     string
+	GlobalFlags []string
+
+	id             string
 	c              *check.C
 	c              *check.C
 	logFile        *os.File
 	logFile        *os.File
 	folder         string
 	folder         string
@@ -59,7 +65,8 @@ func NewDaemon(c *check.C) *Daemon {
 		c.Fatal("Please set the DEST environment variable")
 		c.Fatal("Please set the DEST environment variable")
 	}
 	}
 
 
-	dir := filepath.Join(dest, fmt.Sprintf("d%d", time.Now().UnixNano()%100000000))
+	id := fmt.Sprintf("d%d", time.Now().UnixNano()%100000000)
+	dir := filepath.Join(dest, id)
 	daemonFolder, err := filepath.Abs(dir)
 	daemonFolder, err := filepath.Abs(dir)
 	if err != nil {
 	if err != nil {
 		c.Fatalf("Could not make %q an absolute path: %v", dir, err)
 		c.Fatalf("Could not make %q an absolute path: %v", dir, err)
@@ -77,6 +84,8 @@ func NewDaemon(c *check.C) *Daemon {
 	}
 	}
 
 
 	return &Daemon{
 	return &Daemon{
+		Command:       "daemon",
+		id:            id,
 		c:             c,
 		c:             c,
 		folder:        daemonFolder,
 		folder:        daemonFolder,
 		storageDriver: os.Getenv("DOCKER_GRAPHDRIVER"),
 		storageDriver: os.Getenv("DOCKER_GRAPHDRIVER"),
@@ -90,22 +99,22 @@ func NewDaemon(c *check.C) *Daemon {
 func (d *Daemon) Start(arg ...string) error {
 func (d *Daemon) Start(arg ...string) error {
 	dockerBinary, err := exec.LookPath(dockerBinary)
 	dockerBinary, err := exec.LookPath(dockerBinary)
 	if err != nil {
 	if err != nil {
-		d.c.Fatalf("could not find docker binary in $PATH: %v", err)
+		d.c.Fatalf("[%s] could not find docker binary in $PATH: %v", d.id, err)
 	}
 	}
 
 
-	args := []string{
+	args := append(d.GlobalFlags,
+		d.Command,
 		"--host", d.sock(),
 		"--host", d.sock(),
-		"--daemon",
 		"--graph", fmt.Sprintf("%s/graph", d.folder),
 		"--graph", fmt.Sprintf("%s/graph", d.folder),
 		"--pidfile", fmt.Sprintf("%s/docker.pid", d.folder),
 		"--pidfile", fmt.Sprintf("%s/docker.pid", d.folder),
 		fmt.Sprintf("--userland-proxy=%t", d.userlandProxy),
 		fmt.Sprintf("--userland-proxy=%t", d.userlandProxy),
-	}
+	)
 
 
 	// If we don't explicitly set the log-level or debug flag(-D) then
 	// If we don't explicitly set the log-level or debug flag(-D) then
 	// turn on debug mode
 	// turn on debug mode
 	foundIt := false
 	foundIt := false
 	for _, a := range arg {
 	for _, a := range arg {
-		if strings.Contains(a, "--log-level") || strings.Contains(a, "-D") {
+		if strings.Contains(a, "--log-level") || strings.Contains(a, "-D") || strings.Contains(a, "--debug") {
 			foundIt = true
 			foundIt = true
 		}
 		}
 	}
 	}
@@ -125,21 +134,21 @@ func (d *Daemon) Start(arg ...string) error {
 
 
 	d.logFile, err = os.OpenFile(filepath.Join(d.folder, "docker.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
 	d.logFile, err = os.OpenFile(filepath.Join(d.folder, "docker.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
 	if err != nil {
 	if err != nil {
-		d.c.Fatalf("Could not create %s/docker.log: %v", d.folder, err)
+		d.c.Fatalf("[%s] Could not create %s/docker.log: %v", d.id, d.folder, err)
 	}
 	}
 
 
 	d.cmd.Stdout = d.logFile
 	d.cmd.Stdout = d.logFile
 	d.cmd.Stderr = d.logFile
 	d.cmd.Stderr = d.logFile
 
 
 	if err := d.cmd.Start(); err != nil {
 	if err := d.cmd.Start(); err != nil {
-		return fmt.Errorf("could not start daemon container: %v", err)
+		return fmt.Errorf("[%s] could not start daemon container: %v", d.id, err)
 	}
 	}
 
 
 	wait := make(chan error)
 	wait := make(chan error)
 
 
 	go func() {
 	go func() {
 		wait <- d.cmd.Wait()
 		wait <- d.cmd.Wait()
-		d.c.Log("exiting daemon")
+		d.c.Logf("[%s] exiting daemon", d.id)
 		close(wait)
 		close(wait)
 	}()
 	}()
 
 
@@ -149,14 +158,14 @@ func (d *Daemon) Start(arg ...string) error {
 	// make sure daemon is ready to receive requests
 	// make sure daemon is ready to receive requests
 	startTime := time.Now().Unix()
 	startTime := time.Now().Unix()
 	for {
 	for {
-		d.c.Log("waiting for daemon to start")
+		d.c.Logf("[%s] waiting for daemon to start", d.id)
 		if time.Now().Unix()-startTime > 5 {
 		if time.Now().Unix()-startTime > 5 {
 			// After 5 seconds, give up
 			// After 5 seconds, give up
-			return errors.New("Daemon exited and never started")
+			return fmt.Errorf("[%s] Daemon exited and never started", d.id)
 		}
 		}
 		select {
 		select {
 		case <-time.After(2 * time.Second):
 		case <-time.After(2 * time.Second):
-			return errors.New("timeout: daemon does not respond")
+			return fmt.Errorf("[%s] timeout: daemon does not respond", d.id)
 		case <-tick:
 		case <-tick:
 			c, err := net.Dial("unix", filepath.Join(d.folder, "docker.sock"))
 			c, err := net.Dial("unix", filepath.Join(d.folder, "docker.sock"))
 			if err != nil {
 			if err != nil {
@@ -168,7 +177,7 @@ func (d *Daemon) Start(arg ...string) error {
 
 
 			req, err := http.NewRequest("GET", "/_ping", nil)
 			req, err := http.NewRequest("GET", "/_ping", nil)
 			if err != nil {
 			if err != nil {
-				d.c.Fatalf("could not create new request: %v", err)
+				d.c.Fatalf("[%s] could not create new request: %v", d.id, err)
 			}
 			}
 
 
 			resp, err := client.Do(req)
 			resp, err := client.Do(req)
@@ -176,10 +185,10 @@ func (d *Daemon) Start(arg ...string) error {
 				continue
 				continue
 			}
 			}
 			if resp.StatusCode != http.StatusOK {
 			if resp.StatusCode != http.StatusOK {
-				d.c.Logf("received status != 200 OK: %s", resp.Status)
+				d.c.Logf("[%s] received status != 200 OK: %s", d.id, resp.Status)
 			}
 			}
 
 
-			d.c.Log("daemon started")
+			d.c.Logf("[%s] daemon started", d.id)
 			return nil
 			return nil
 		}
 		}
 	}
 	}

+ 7 - 0
opts/hosts_unix.go

@@ -0,0 +1,7 @@
+// +build !windows
+
+package opts
+
+import "fmt"
+
+var DefaultHost = fmt.Sprintf("unix://%s", DefaultUnixSocket)

+ 7 - 0
opts/hosts_windows.go

@@ -0,0 +1,7 @@
+// +build windows
+
+package opts
+
+import "fmt"
+
+var DefaultHost = fmt.Sprintf("tcp://%s:%d", DefaultHTTPHost, DefaultHTTPPort)

+ 14 - 11
opts/opts.go

@@ -32,37 +32,37 @@ var (
 // ListVar Defines a flag with the specified names and usage, and put the value
 // ListVar Defines a flag with the specified names and usage, and put the value
 // list into ListOpts that will hold the values.
 // list into ListOpts that will hold the values.
 func ListVar(values *[]string, names []string, usage string) {
 func ListVar(values *[]string, names []string, usage string) {
-	flag.Var(newListOptsRef(values, nil), names, usage)
+	flag.Var(NewListOptsRef(values, nil), names, usage)
 }
 }
 
 
 // MapVar Defines a flag with the specified names and usage, and put the value
 // MapVar Defines a flag with the specified names and usage, and put the value
 // map into MapOpt that will hold the values (key,value).
 // map into MapOpt that will hold the values (key,value).
 func MapVar(values map[string]string, names []string, usage string) {
 func MapVar(values map[string]string, names []string, usage string) {
-	flag.Var(newMapOpt(values, nil), names, usage)
+	flag.Var(NewMapOpts(values, nil), names, usage)
 }
 }
 
 
 // LogOptsVar Defines a flag with the specified names and usage for --log-opts,
 // LogOptsVar Defines a flag with the specified names and usage for --log-opts,
 // and put the value map into MapOpt that will hold the values (key,value).
 // and put the value map into MapOpt that will hold the values (key,value).
 func LogOptsVar(values map[string]string, names []string, usage string) {
 func LogOptsVar(values map[string]string, names []string, usage string) {
-	flag.Var(newMapOpt(values, nil), names, usage)
+	flag.Var(NewMapOpts(values, nil), names, usage)
 }
 }
 
 
 // HostListVar Defines a flag with the specified names and usage and put the
 // HostListVar Defines a flag with the specified names and usage and put the
 // value into a ListOpts that will hold the values, validating the Host format.
 // value into a ListOpts that will hold the values, validating the Host format.
 func HostListVar(values *[]string, names []string, usage string) {
 func HostListVar(values *[]string, names []string, usage string) {
-	flag.Var(newListOptsRef(values, ValidateHost), names, usage)
+	flag.Var(NewListOptsRef(values, ValidateHost), names, usage)
 }
 }
 
 
 // IPListVar Defines a flag with the specified names and usage and put the
 // IPListVar Defines a flag with the specified names and usage and put the
 // value into a ListOpts that will hold the values, validating the IP format.
 // value into a ListOpts that will hold the values, validating the IP format.
 func IPListVar(values *[]string, names []string, usage string) {
 func IPListVar(values *[]string, names []string, usage string) {
-	flag.Var(newListOptsRef(values, ValidateIPAddress), names, usage)
+	flag.Var(NewListOptsRef(values, ValidateIPAddress), names, usage)
 }
 }
 
 
 // DNSSearchListVar Defines a flag with the specified names and usage and put the
 // DNSSearchListVar Defines a flag with the specified names and usage and put the
 // value into a ListOpts that will hold the values, validating the DNS search format.
 // value into a ListOpts that will hold the values, validating the DNS search format.
 func DNSSearchListVar(values *[]string, names []string, usage string) {
 func DNSSearchListVar(values *[]string, names []string, usage string) {
-	flag.Var(newListOptsRef(values, ValidateDNSSearch), names, usage)
+	flag.Var(NewListOptsRef(values, ValidateDNSSearch), names, usage)
 }
 }
 
 
 // IPVar Defines a flag with the specified names and usage for IP and will use
 // IPVar Defines a flag with the specified names and usage for IP and will use
@@ -74,12 +74,12 @@ func IPVar(value *net.IP, names []string, defaultValue, usage string) {
 // LabelListVar Defines a flag with the specified names and usage and put the
 // LabelListVar Defines a flag with the specified names and usage and put the
 // value into a ListOpts that will hold the values, validating the label format.
 // value into a ListOpts that will hold the values, validating the label format.
 func LabelListVar(values *[]string, names []string, usage string) {
 func LabelListVar(values *[]string, names []string, usage string) {
-	flag.Var(newListOptsRef(values, ValidateLabel), names, usage)
+	flag.Var(NewListOptsRef(values, ValidateLabel), names, usage)
 }
 }
 
 
 // UlimitMapVar Defines a flag with the specified names and usage for --ulimit,
 // UlimitMapVar Defines a flag with the specified names and usage for --ulimit,
 // and put the value map into a UlimitOpt that will hold the values.
 // and put the value map into a UlimitOpt that will hold the values.
-func UlimitMapVar(values map[string]*ulimit.Ulimit, names []string, usage string) {
+func UlimitMapVar(values *map[string]*ulimit.Ulimit, names []string, usage string) {
 	flag.Var(NewUlimitOpt(values), names, usage)
 	flag.Var(NewUlimitOpt(values), names, usage)
 }
 }
 
 
@@ -92,10 +92,10 @@ type ListOpts struct {
 // NewListOpts Create a new ListOpts with the specified validator.
 // NewListOpts Create a new ListOpts with the specified validator.
 func NewListOpts(validator ValidatorFctType) ListOpts {
 func NewListOpts(validator ValidatorFctType) ListOpts {
 	var values []string
 	var values []string
-	return *newListOptsRef(&values, validator)
+	return *NewListOptsRef(&values, validator)
 }
 }
 
 
-func newListOptsRef(values *[]string, validator ValidatorFctType) *ListOpts {
+func NewListOptsRef(values *[]string, validator ValidatorFctType) *ListOpts {
 	return &ListOpts{
 	return &ListOpts{
 		values:    values,
 		values:    values,
 		validator: validator,
 		validator: validator,
@@ -191,7 +191,10 @@ func (opts *MapOpts) String() string {
 	return fmt.Sprintf("%v", map[string]string((opts.values)))
 	return fmt.Sprintf("%v", map[string]string((opts.values)))
 }
 }
 
 
-func newMapOpt(values map[string]string, validator ValidatorFctType) *MapOpts {
+func NewMapOpts(values map[string]string, validator ValidatorFctType) *MapOpts {
+	if values == nil {
+		values = make(map[string]string)
+	}
 	return &MapOpts{
 	return &MapOpts{
 		values:    values,
 		values:    values,
 		validator: validator,
 		validator: validator,

+ 1 - 1
opts/opts_test.go

@@ -32,7 +32,7 @@ func TestValidateIPAddress(t *testing.T) {
 
 
 func TestMapOpts(t *testing.T) {
 func TestMapOpts(t *testing.T) {
 	tmpMap := make(map[string]string)
 	tmpMap := make(map[string]string)
-	o := newMapOpt(tmpMap, logOptsValidator)
+	o := NewMapOpts(tmpMap, logOptsValidator)
 	o.Set("max-size=1")
 	o.Set("max-size=1")
 	if o.String() != "map[max-size:1]" {
 	if o.String() != "map[max-size:1]" {
 		t.Errorf("%s != [map[max-size:1]", o.String())
 		t.Errorf("%s != [map[max-size:1]", o.String())

+ 8 - 5
opts/ulimit.go

@@ -7,10 +7,13 @@ import (
 )
 )
 
 
 type UlimitOpt struct {
 type UlimitOpt struct {
-	values map[string]*ulimit.Ulimit
+	values *map[string]*ulimit.Ulimit
 }
 }
 
 
-func NewUlimitOpt(ref map[string]*ulimit.Ulimit) *UlimitOpt {
+func NewUlimitOpt(ref *map[string]*ulimit.Ulimit) *UlimitOpt {
+	if ref == nil {
+		ref = &map[string]*ulimit.Ulimit{}
+	}
 	return &UlimitOpt{ref}
 	return &UlimitOpt{ref}
 }
 }
 
 
@@ -20,14 +23,14 @@ func (o *UlimitOpt) Set(val string) error {
 		return err
 		return err
 	}
 	}
 
 
-	o.values[l.Name] = l
+	(*o.values)[l.Name] = l
 
 
 	return nil
 	return nil
 }
 }
 
 
 func (o *UlimitOpt) String() string {
 func (o *UlimitOpt) String() string {
 	var out []string
 	var out []string
-	for _, v := range o.values {
+	for _, v := range *o.values {
 		out = append(out, v.String())
 		out = append(out, v.String())
 	}
 	}
 
 
@@ -36,7 +39,7 @@ func (o *UlimitOpt) String() string {
 
 
 func (o *UlimitOpt) GetList() []*ulimit.Ulimit {
 func (o *UlimitOpt) GetList() []*ulimit.Ulimit {
 	var ulimits []*ulimit.Ulimit
 	var ulimits []*ulimit.Ulimit
-	for _, v := range o.values {
+	for _, v := range *o.values {
 		ulimits = append(ulimits, v)
 		ulimits = append(ulimits, v)
 	}
 	}
 
 

+ 1 - 1
opts/ulimit_test.go

@@ -11,7 +11,7 @@ func TestUlimitOpt(t *testing.T) {
 		"nofile": {"nofile", 1024, 512},
 		"nofile": {"nofile", 1024, 512},
 	}
 	}
 
 
-	ulimitOpt := NewUlimitOpt(ulimitMap)
+	ulimitOpt := NewUlimitOpt(&ulimitMap)
 
 
 	expected := "[nofile=512:1024]"
 	expected := "[nofile=512:1024]"
 	if ulimitOpt.String() != expected {
 	if ulimitOpt.String() != expected {

+ 51 - 1
pkg/mflag/flag.go

@@ -526,7 +526,7 @@ func (f *FlagSet) PrintDefaults() {
 				names = append(names, name)
 				names = append(names, name)
 			}
 			}
 		}
 		}
-		if len(names) > 0 {
+		if len(names) > 0 && len(flag.Usage) > 0 {
 			val := flag.DefValue
 			val := flag.DefValue
 
 
 			if home != "" && strings.HasPrefix(val, home) {
 			if home != "" && strings.HasPrefix(val, home) {
@@ -1143,3 +1143,53 @@ func (f *FlagSet) Init(name string, errorHandling ErrorHandling) {
 	f.name = name
 	f.name = name
 	f.errorHandling = errorHandling
 	f.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
+}
+
+func Merge(dest *FlagSet, flagsets ...*FlagSet) error {
+	for _, fset := range flagsets {
+		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}
+			dest.formal[k] = &newF
+		}
+	}
+	return nil
+}
+
+func (f *FlagSet) IsEmpty() bool {
+	return len(f.actual) == 0
+}

+ 11 - 4
pkg/tlsconfig/config.go

@@ -17,11 +17,18 @@ import (
 
 
 // Options represents the information needed to create client and server TLS configurations.
 // Options represents the information needed to create client and server TLS configurations.
 type Options struct {
 type Options struct {
+	CAFile string
+
+	// If either CertFile or KeyFile is empty, Client() will not load them
+	// preventing the client from authenticating to the server.
+	// However, Server() requires them and will error out if they are empty.
+	CertFile string
+	KeyFile  string
+
+	// client-only option
 	InsecureSkipVerify bool
 	InsecureSkipVerify bool
-	ClientAuth         tls.ClientAuthType
-	CAFile             string
-	CertFile           string
-	KeyFile            string
+	// server-only option
+	ClientAuth tls.ClientAuthType
 }
 }
 
 
 // Extra (server-side) accepted CBC cipher suites - will phase out in the future
 // Extra (server-side) accepted CBC cipher suites - will phase out in the future

+ 3 - 3
registry/config.go

@@ -43,11 +43,11 @@ var (
 
 
 // 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.
-func (options *Options) InstallFlags() {
+func (options *Options) InstallFlags(cmd *flag.FlagSet, usageFn func(string) string) {
 	options.Mirrors = opts.NewListOpts(ValidateMirror)
 	options.Mirrors = opts.NewListOpts(ValidateMirror)
-	flag.Var(&options.Mirrors, []string{"-registry-mirror"}, "Preferred Docker registry mirror")
+	cmd.Var(&options.Mirrors, []string{"-registry-mirror"}, usageFn("Preferred Docker registry mirror"))
 	options.InsecureRegistries = opts.NewListOpts(ValidateIndexName)
 	options.InsecureRegistries = opts.NewListOpts(ValidateIndexName)
-	flag.Var(&options.InsecureRegistries, []string{"-insecure-registry"}, "Enable insecure registry communication")
+	cmd.Var(&options.InsecureRegistries, []string{"-insecure-registry"}, usageFn("Enable insecure registry communication"))
 }
 }
 
 
 type netIPNet net.IPNet
 type netIPNet net.IPNet

+ 1 - 3
runconfig/parse.go

@@ -9,7 +9,6 @@ import (
 	flag "github.com/docker/docker/pkg/mflag"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/nat"
 	"github.com/docker/docker/pkg/nat"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/parsers"
-	"github.com/docker/docker/pkg/ulimit"
 	"github.com/docker/docker/pkg/units"
 	"github.com/docker/docker/pkg/units"
 )
 )
 
 
@@ -48,8 +47,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 		flLabels  = opts.NewListOpts(opts.ValidateEnv)
 		flLabels  = opts.NewListOpts(opts.ValidateEnv)
 		flDevices = opts.NewListOpts(opts.ValidateDevice)
 		flDevices = opts.NewListOpts(opts.ValidateDevice)
 
 
-		ulimits   = make(map[string]*ulimit.Ulimit)
-		flUlimits = opts.NewUlimitOpt(ulimits)
+		flUlimits = opts.NewUlimitOpt(nil)
 
 
 		flPublish     = opts.NewListOpts(nil)
 		flPublish     = opts.NewListOpts(nil)
 		flExpose      = opts.NewListOpts(nil)
 		flExpose      = opts.NewListOpts(nil)