Browse code

cli: new daemon command and new cli package

This patch creates a new cli package that allows to combine both client
and daemon commands (there is only one daemon command: docker daemon).

The `-d` and `--daemon` top-level flags are deprecated and a special
message is added to prompt the user to use `docker daemon`.

Providing top-level daemon-specific flags for client commands result
in an error message prompting the user to use `docker daemon`.

This patch does not break any old but correct usages.

This also makes `-d` and `--daemon` flags, as well as the `daemon`
command illegal in client-only binaries.

Signed-off-by: Tibor Vass <tibor@docker.com>

Tibor Vass authored on 2015/05/05 13:18:28
Showing 67 changed files
... ...
@@ -8,6 +8,7 @@ import (
8 8
 
9 9
 	"github.com/Sirupsen/logrus"
10 10
 	"github.com/docker/docker/api/types"
11
+	Cli "github.com/docker/docker/cli"
11 12
 	flag "github.com/docker/docker/pkg/mflag"
12 13
 	"github.com/docker/docker/pkg/signal"
13 14
 )
... ...
@@ -16,9 +17,10 @@ import (
16 16
 //
17 17
 // Usage: docker attach [OPTIONS] CONTAINER
18 18
 func (cli *DockerCli) CmdAttach(args ...string) error {
19
-	cmd := cli.Subcmd("attach", []string{"CONTAINER"}, "Attach to a running container", true)
19
+	cmd := Cli.Subcmd("attach", []string{"CONTAINER"}, "Attach to a running container", true)
20 20
 	noStdin := cmd.Bool([]string{"#nostdin", "-no-stdin"}, false, "Do not attach STDIN")
21 21
 	proxy := cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy all received signals to the process")
22
+
22 23
 	cmd.Require(flag.Exact, 1)
23 24
 
24 25
 	cmd.ParseFlags(args, true)
... ...
@@ -75,7 +77,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
75 75
 		return err
76 76
 	}
77 77
 	if status != 0 {
78
-		return StatusError{StatusCode: status}
78
+		return Cli.StatusError{StatusCode: status}
79 79
 	}
80 80
 
81 81
 	return nil
... ...
@@ -18,6 +18,7 @@ import (
18 18
 	"strings"
19 19
 
20 20
 	"github.com/docker/docker/api"
21
+	Cli "github.com/docker/docker/cli"
21 22
 	"github.com/docker/docker/graph/tags"
22 23
 	"github.com/docker/docker/opts"
23 24
 	"github.com/docker/docker/pkg/archive"
... ...
@@ -46,7 +47,7 @@ const (
46 46
 //
47 47
 // Usage: docker build [OPTIONS] PATH | URL | -
48 48
 func (cli *DockerCli) CmdBuild(args ...string) error {
49
-	cmd := cli.Subcmd("build", []string{"PATH | URL | -"}, "Build a new image from the source code at PATH", true)
49
+	cmd := Cli.Subcmd("build", []string{"PATH | URL | -"}, "Build a new image from the source code at PATH", true)
50 50
 	tag := cmd.String([]string{"t", "-tag"}, "", "Repository name (and optionally a tag) for the image")
51 51
 	suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the verbose output generated by the containers")
52 52
 	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 {
64 64
 	flCgroupParent := cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container")
65 65
 
66 66
 	ulimits := make(map[string]*ulimit.Ulimit)
67
-	flUlimits := opts.NewUlimitOpt(ulimits)
67
+	flUlimits := opts.NewUlimitOpt(&ulimits)
68 68
 	cmd.Var(flUlimits, []string{"-ulimit"}, "Ulimit options")
69 69
 
70 70
 	cmd.Require(flag.Exact, 1)
... ...
@@ -325,7 +326,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
325 325
 		if jerr.Code == 0 {
326 326
 			jerr.Code = 1
327 327
 		}
328
-		return StatusError{Status: jerr.Message, StatusCode: jerr.Code}
328
+		return Cli.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
329 329
 	}
330 330
 	return err
331 331
 }
... ...
@@ -2,25 +2,28 @@ package client
2 2
 
3 3
 import (
4 4
 	"crypto/tls"
5
-	"encoding/json"
6 5
 	"errors"
7 6
 	"fmt"
8 7
 	"io"
9 8
 	"net/http"
10 9
 	"net/url"
11
-	"reflect"
10
+	"os"
12 11
 	"strings"
13
-	"text/template"
14 12
 
13
+	"github.com/docker/docker/cli"
15 14
 	"github.com/docker/docker/cliconfig"
16
-	flag "github.com/docker/docker/pkg/mflag"
15
+	"github.com/docker/docker/opts"
17 16
 	"github.com/docker/docker/pkg/sockets"
18 17
 	"github.com/docker/docker/pkg/term"
18
+	"github.com/docker/docker/pkg/tlsconfig"
19 19
 )
20 20
 
21 21
 // DockerCli represents the docker command line client.
22 22
 // Instances of the client can be returned from NewDockerCli.
23 23
 type DockerCli struct {
24
+	// initializing closure
25
+	init func() error
26
+
24 27
 	// proto holds the client protocol i.e. unix.
25 28
 	proto string
26 29
 	// addr holds the client address.
... ...
@@ -55,116 +58,11 @@ type DockerCli struct {
55 55
 	transport *http.Transport
56 56
 }
57 57
 
58
-var funcMap = template.FuncMap{
59
-	"json": func(v interface{}) string {
60
-		a, _ := json.Marshal(v)
61
-		return string(a)
62
-	},
63
-}
64
-
65
-func (cli *DockerCli) Out() io.Writer {
66
-	return cli.out
67
-}
68
-
69
-func (cli *DockerCli) Err() io.Writer {
70
-	return cli.err
71
-}
72
-
73
-func (cli *DockerCli) getMethod(args ...string) (func(...string) error, bool) {
74
-	camelArgs := make([]string, len(args))
75
-	for i, s := range args {
76
-		if len(s) == 0 {
77
-			return nil, false
78
-		}
79
-		camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:])
80
-	}
81
-	methodName := "Cmd" + strings.Join(camelArgs, "")
82
-	method := reflect.ValueOf(cli).MethodByName(methodName)
83
-	if !method.IsValid() {
84
-		return nil, false
85
-	}
86
-	return method.Interface().(func(...string) error), true
87
-}
88
-
89
-// Cmd executes the specified command.
90
-func (cli *DockerCli) Cmd(args ...string) error {
91
-	if len(args) > 1 {
92
-		method, exists := cli.getMethod(args[:2]...)
93
-		if exists {
94
-			return method(args[2:]...)
95
-		}
96
-	}
97
-	if len(args) > 0 {
98
-		method, exists := cli.getMethod(args[0])
99
-		if !exists {
100
-			return fmt.Errorf("docker: '%s' is not a docker command.\nSee 'docker --help'.", args[0])
101
-		}
102
-		return method(args[1:]...)
58
+func (cli *DockerCli) Initialize() error {
59
+	if cli.init == nil {
60
+		return nil
103 61
 	}
104
-	return cli.CmdHelp()
105
-}
106
-
107
-// Subcmd is a subcommand of the main "docker" command.
108
-// A subcommand represents an action that can be performed
109
-// from the Docker command line client.
110
-//
111
-// Multiple subcommand synopses may be provided with one 'Usage' line being
112
-// printed for each in the following way:
113
-//
114
-//	Usage:	docker <subcmd-name> [OPTIONS] <synopsis 0>
115
-// 		docker <subcmd-name> [OPTIONS] <synopsis 1>
116
-// 		...
117
-//
118
-// If no undeprecated flags are added to the returned FlagSet, "[OPTIONS]" will
119
-// not be included on the usage synopsis lines. If no synopses are given, only
120
-// one usage synopsis line will be printed with nothing following the
121
-// "[OPTIONS]" section
122
-//
123
-// To see all available subcommands, run "docker --help".
124
-func (cli *DockerCli) Subcmd(name string, synopses []string, description string, exitOnError bool) *flag.FlagSet {
125
-	var errorHandling flag.ErrorHandling
126
-	if exitOnError {
127
-		errorHandling = flag.ExitOnError
128
-	} else {
129
-		errorHandling = flag.ContinueOnError
130
-	}
131
-
132
-	flags := flag.NewFlagSet(name, errorHandling)
133
-
134
-	flags.Usage = func() {
135
-		flags.ShortUsage()
136
-		flags.PrintDefaults()
137
-	}
138
-
139
-	flags.ShortUsage = func() {
140
-		options := ""
141
-		if flags.FlagCountUndeprecated() > 0 {
142
-			options = " [OPTIONS]"
143
-		}
144
-
145
-		if len(synopses) == 0 {
146
-			synopses = []string{""}
147
-		}
148
-
149
-		// Allow for multiple command usage synopses.
150
-		for i, synopsis := range synopses {
151
-			lead := "\t"
152
-			if i == 0 {
153
-				// First line needs the word 'Usage'.
154
-				lead = "Usage:\t"
155
-			}
156
-
157
-			if synopsis != "" {
158
-				synopsis = " " + synopsis
159
-			}
160
-
161
-			fmt.Fprintf(flags.Out(), "\n%sdocker %s%s%s", lead, name, options, synopsis)
162
-		}
163
-
164
-		fmt.Fprintf(flags.Out(), "\n\n%s\n", description)
165
-	}
166
-
167
-	return flags
62
+	return cli.init()
168 63
 }
169 64
 
170 65
 // CheckTtyInput checks if we are trying to attach to a container tty
... ...
@@ -187,64 +85,78 @@ func (cli *DockerCli) PsFormat() string {
187 187
 // The key file, protocol (i.e. unix) and address are passed in as strings, along with the tls.Config. If the tls.Config
188 188
 // is set the client scheme will be set to https.
189 189
 // The client will be given a 32-second timeout (see https://github.com/docker/docker/pull/8035).
190
-func NewDockerCli(in io.ReadCloser, out, err io.Writer, keyFile string, proto, addr string, tlsConfig *tls.Config) *DockerCli {
191
-	var (
192
-		inFd          uintptr
193
-		outFd         uintptr
194
-		isTerminalIn  = false
195
-		isTerminalOut = false
196
-		scheme        = "http"
197
-		basePath      = ""
198
-	)
199
-
200
-	if tlsConfig != nil {
201
-		scheme = "https"
202
-	}
203
-	if in != nil {
204
-		inFd, isTerminalIn = term.GetFdInfo(in)
190
+func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientFlags) *DockerCli {
191
+	cli := &DockerCli{
192
+		in:      in,
193
+		out:     out,
194
+		err:     err,
195
+		keyFile: clientFlags.Common.TrustKey,
205 196
 	}
206 197
 
207
-	if out != nil {
208
-		outFd, isTerminalOut = term.GetFdInfo(out)
209
-	}
198
+	cli.init = func() error {
199
+		clientFlags.PostParse()
210 200
 
211
-	if err == nil {
212
-		err = out
213
-	}
201
+		hosts := clientFlags.Common.Hosts
214 202
 
215
-	// The transport is created here for reuse during the client session.
216
-	tr := &http.Transport{
217
-		TLSClientConfig: tlsConfig,
218
-	}
219
-	sockets.ConfigureTCPTransport(tr, proto, addr)
203
+		switch len(hosts) {
204
+		case 0:
205
+			defaultHost := os.Getenv("DOCKER_HOST")
206
+			if defaultHost == "" {
207
+				defaultHost = opts.DefaultHost
208
+			}
209
+			defaultHost, err := opts.ValidateHost(defaultHost)
210
+			if err != nil {
211
+				return err
212
+			}
213
+			hosts = []string{defaultHost}
214
+		case 1:
215
+			// only accept one host to talk to
216
+		default:
217
+			return errors.New("Please specify only one -H")
218
+		}
220 219
 
221
-	configFile, e := cliconfig.Load(cliconfig.ConfigDir())
222
-	if e != nil {
223
-		fmt.Fprintf(err, "WARNING: Error loading config file:%v\n", e)
224
-	}
220
+		protoAddrParts := strings.SplitN(hosts[0], "://", 2)
221
+		cli.proto, cli.addr = protoAddrParts[0], protoAddrParts[1]
225 222
 
226
-	if proto == "tcp" {
227
-		// error is checked in pkg/parsers already
228
-		parsed, _ := url.Parse("tcp://" + addr)
229
-		addr = parsed.Host
230
-		basePath = parsed.Path
231
-	}
223
+		if cli.proto == "tcp" {
224
+			// error is checked in pkg/parsers already
225
+			parsed, _ := url.Parse("tcp://" + cli.addr)
226
+			cli.addr = parsed.Host
227
+			cli.basePath = parsed.Path
228
+		}
232 229
 
233
-	return &DockerCli{
234
-		proto:         proto,
235
-		addr:          addr,
236
-		basePath:      basePath,
237
-		configFile:    configFile,
238
-		in:            in,
239
-		out:           out,
240
-		err:           err,
241
-		keyFile:       keyFile,
242
-		inFd:          inFd,
243
-		outFd:         outFd,
244
-		isTerminalIn:  isTerminalIn,
245
-		isTerminalOut: isTerminalOut,
246
-		tlsConfig:     tlsConfig,
247
-		scheme:        scheme,
248
-		transport:     tr,
230
+		if clientFlags.Common.TLSOptions != nil {
231
+			cli.scheme = "https"
232
+			var e error
233
+			cli.tlsConfig, e = tlsconfig.Client(*clientFlags.Common.TLSOptions)
234
+			if e != nil {
235
+				return e
236
+			}
237
+		} else {
238
+			cli.scheme = "http"
239
+		}
240
+
241
+		if cli.in != nil {
242
+			cli.inFd, cli.isTerminalIn = term.GetFdInfo(cli.in)
243
+		}
244
+		if cli.out != nil {
245
+			cli.outFd, cli.isTerminalOut = term.GetFdInfo(cli.out)
246
+		}
247
+
248
+		// The transport is created here for reuse during the client session.
249
+		cli.transport = &http.Transport{
250
+			TLSClientConfig: cli.tlsConfig,
251
+		}
252
+		sockets.ConfigureTCPTransport(cli.transport, cli.proto, cli.addr)
253
+
254
+		configFile, e := cliconfig.Load(cliconfig.ConfigDir())
255
+		if e != nil {
256
+			fmt.Fprintf(cli.err, "WARNING: Error loading config file:%v\n", e)
257
+		}
258
+		cli.configFile = configFile
259
+
260
+		return nil
249 261
 	}
262
+
263
+	return cli
250 264
 }
... ...
@@ -3,15 +3,3 @@
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.
4 4
 // See https://docs.docker.com/installation/ for instructions on installing Docker.
5 5
 package client
6
-
7
-import "fmt"
8
-
9
-// An StatusError reports an unsuccessful exit by a command.
10
-type StatusError struct {
11
-	Status     string
12
-	StatusCode int
13
-}
14
-
15
-func (e StatusError) Error() string {
16
-	return fmt.Sprintf("Status: %s, Code: %d", e.Status, e.StatusCode)
17
-}
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"net/url"
7 7
 
8 8
 	"github.com/docker/docker/api/types"
9
+	Cli "github.com/docker/docker/cli"
9 10
 	"github.com/docker/docker/opts"
10 11
 	flag "github.com/docker/docker/pkg/mflag"
11 12
 	"github.com/docker/docker/pkg/parsers"
... ...
@@ -17,7 +18,7 @@ import (
17 17
 //
18 18
 // Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
19 19
 func (cli *DockerCli) CmdCommit(args ...string) error {
20
-	cmd := cli.Subcmd("commit", []string{"CONTAINER [REPOSITORY[:TAG]]"}, "Create a new image from a container's changes", true)
20
+	cmd := Cli.Subcmd("commit", []string{"CONTAINER [REPOSITORY[:TAG]]"}, "Create a new image from a container's changes", true)
21 21
 	flPause := cmd.Bool([]string{"p", "-pause"}, true, "Pause container during commit")
22 22
 	flComment := cmd.String([]string{"m", "-message"}, "", "Commit message")
23 23
 	flAuthor := cmd.String([]string{"a", "#author", "-author"}, "", "Author (e.g., \"John Hannibal Smith <hannibal@a-team.com>\")")
... ...
@@ -12,6 +12,7 @@ import (
12 12
 	"strings"
13 13
 
14 14
 	"github.com/docker/docker/api/types"
15
+	Cli "github.com/docker/docker/cli"
15 16
 	"github.com/docker/docker/pkg/archive"
16 17
 	flag "github.com/docker/docker/pkg/mflag"
17 18
 )
... ...
@@ -37,7 +38,7 @@ const (
37 37
 // 	docker cp CONTAINER:PATH LOCALPATH|-
38 38
 // 	docker cp LOCALPATH|- CONTAINER:PATH
39 39
 func (cli *DockerCli) CmdCp(args ...string) error {
40
-	cmd := cli.Subcmd(
40
+	cmd := Cli.Subcmd(
41 41
 		"cp",
42 42
 		[]string{"CONTAINER:PATH LOCALPATH|-", "LOCALPATH|- CONTAINER:PATH"},
43 43
 		strings.Join([]string{
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	"strings"
11 11
 
12 12
 	"github.com/docker/docker/api/types"
13
+	Cli "github.com/docker/docker/cli"
13 14
 	"github.com/docker/docker/graph/tags"
14 15
 	"github.com/docker/docker/pkg/parsers"
15 16
 	"github.com/docker/docker/registry"
... ...
@@ -137,7 +138,7 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc
137 137
 //
138 138
 // Usage: docker create [OPTIONS] IMAGE [COMMAND] [ARG...]
139 139
 func (cli *DockerCli) CmdCreate(args ...string) error {
140
-	cmd := cli.Subcmd("create", []string{"IMAGE [COMMAND] [ARG...]"}, "Create a new container", true)
140
+	cmd := Cli.Subcmd("create", []string{"IMAGE [COMMAND] [ARG...]"}, "Create a new container", true)
141 141
 
142 142
 	// These are flags not stored in Config/HostConfig
143 143
 	var (
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"fmt"
6 6
 
7 7
 	"github.com/docker/docker/api/types"
8
+	Cli "github.com/docker/docker/cli"
8 9
 	"github.com/docker/docker/pkg/archive"
9 10
 	flag "github.com/docker/docker/pkg/mflag"
10 11
 )
... ...
@@ -17,7 +18,7 @@ import (
17 17
 //
18 18
 // Usage: docker diff CONTAINER
19 19
 func (cli *DockerCli) CmdDiff(args ...string) error {
20
-	cmd := cli.Subcmd("diff", []string{"CONTAINER"}, "Inspect changes on a container's filesystem", true)
20
+	cmd := Cli.Subcmd("diff", []string{"CONTAINER"}, "Inspect changes on a container's filesystem", true)
21 21
 	cmd.Require(flag.Exact, 1)
22 22
 
23 23
 	cmd.ParseFlags(args, true)
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"net/url"
5 5
 	"time"
6 6
 
7
+	Cli "github.com/docker/docker/cli"
7 8
 	"github.com/docker/docker/opts"
8 9
 	flag "github.com/docker/docker/pkg/mflag"
9 10
 	"github.com/docker/docker/pkg/parsers/filters"
... ...
@@ -14,7 +15,7 @@ import (
14 14
 //
15 15
 // Usage: docker events [OPTIONS]
16 16
 func (cli *DockerCli) CmdEvents(args ...string) error {
17
-	cmd := cli.Subcmd("events", nil, "Get real time events from the server", true)
17
+	cmd := Cli.Subcmd("events", nil, "Get real time events from the server", true)
18 18
 	since := cmd.String([]string{"#since", "-since"}, "", "Show all events created since timestamp")
19 19
 	until := cmd.String([]string{"-until"}, "", "Stream events until this timestamp")
20 20
 	flFilter := opts.NewListOpts(nil)
... ...
@@ -7,6 +7,7 @@ import (
7 7
 
8 8
 	"github.com/Sirupsen/logrus"
9 9
 	"github.com/docker/docker/api/types"
10
+	Cli "github.com/docker/docker/cli"
10 11
 	"github.com/docker/docker/pkg/promise"
11 12
 	"github.com/docker/docker/runconfig"
12 13
 )
... ...
@@ -15,12 +16,12 @@ import (
15 15
 //
16 16
 // Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
17 17
 func (cli *DockerCli) CmdExec(args ...string) error {
18
-	cmd := cli.Subcmd("exec", []string{"CONTAINER COMMAND [ARG...]"}, "Run a command in a running container", true)
18
+	cmd := Cli.Subcmd("exec", []string{"CONTAINER COMMAND [ARG...]"}, "Run a command in a running container", true)
19 19
 
20 20
 	execConfig, err := runconfig.ParseExec(cmd, args)
21 21
 	// just in case the ParseExec does not exit
22 22
 	if execConfig.Container == "" || err != nil {
23
-		return StatusError{StatusCode: 1}
23
+		return Cli.StatusError{StatusCode: 1}
24 24
 	}
25 25
 
26 26
 	serverResp, err := cli.call("POST", "/containers/"+execConfig.Container+"/exec", execConfig, nil)
... ...
@@ -126,7 +127,7 @@ func (cli *DockerCli) CmdExec(args ...string) error {
126 126
 	}
127 127
 
128 128
 	if status != 0 {
129
-		return StatusError{StatusCode: status}
129
+		return Cli.StatusError{StatusCode: status}
130 130
 	}
131 131
 
132 132
 	return nil
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"io"
6 6
 	"os"
7 7
 
8
+	Cli "github.com/docker/docker/cli"
8 9
 	flag "github.com/docker/docker/pkg/mflag"
9 10
 )
10 11
 
... ...
@@ -14,7 +15,7 @@ import (
14 14
 //
15 15
 // Usage: docker export [OPTIONS] CONTAINER
16 16
 func (cli *DockerCli) CmdExport(args ...string) error {
17
-	cmd := cli.Subcmd("export", []string{"CONTAINER"}, "Export the contents of a container's filesystem as a tar archive", true)
17
+	cmd := Cli.Subcmd("export", []string{"CONTAINER"}, "Export the contents of a container's filesystem as a tar archive", true)
18 18
 	outfile := cmd.String([]string{"o", "-output"}, "", "Write to a file, instead of STDOUT")
19 19
 	cmd.Require(flag.Exact, 1)
20 20
 
21 21
deleted file mode 100644
... ...
@@ -1,34 +0,0 @@
1
-package client
2
-
3
-import (
4
-	"fmt"
5
-
6
-	flag "github.com/docker/docker/pkg/mflag"
7
-)
8
-
9
-// CmdHelp displays information on a Docker command.
10
-//
11
-// If more than one command is specified, information is only shown for the first command.
12
-//
13
-// Usage: docker help COMMAND or docker COMMAND --help
14
-func (cli *DockerCli) CmdHelp(args ...string) error {
15
-	if len(args) > 1 {
16
-		method, exists := cli.getMethod(args[:2]...)
17
-		if exists {
18
-			method("--help")
19
-			return nil
20
-		}
21
-	}
22
-	if len(args) > 0 {
23
-		method, exists := cli.getMethod(args[0])
24
-		if !exists {
25
-			return fmt.Errorf("docker: '%s' is not a docker command. See 'docker --help'.", args[0])
26
-		}
27
-		method("--help")
28
-		return nil
29
-	}
30
-
31
-	flag.Usage()
32
-
33
-	return nil
34
-}
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	"time"
8 8
 
9 9
 	"github.com/docker/docker/api/types"
10
+	Cli "github.com/docker/docker/cli"
10 11
 	flag "github.com/docker/docker/pkg/mflag"
11 12
 	"github.com/docker/docker/pkg/stringid"
12 13
 	"github.com/docker/docker/pkg/stringutils"
... ...
@@ -17,7 +18,7 @@ import (
17 17
 //
18 18
 // Usage: docker history [OPTIONS] IMAGE
19 19
 func (cli *DockerCli) CmdHistory(args ...string) error {
20
-	cmd := cli.Subcmd("history", []string{"IMAGE"}, "Show the history of an image", true)
20
+	cmd := Cli.Subcmd("history", []string{"IMAGE"}, "Show the history of an image", true)
21 21
 	human := cmd.Bool([]string{"H", "-human"}, true, "Print sizes and dates in human readable format")
22 22
 	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
23 23
 	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"time"
9 9
 
10 10
 	"github.com/docker/docker/api/types"
11
+	Cli "github.com/docker/docker/cli"
11 12
 	"github.com/docker/docker/opts"
12 13
 	flag "github.com/docker/docker/pkg/mflag"
13 14
 	"github.com/docker/docker/pkg/parsers"
... ...
@@ -21,7 +22,7 @@ import (
21 21
 //
22 22
 // Usage: docker images [OPTIONS] [REPOSITORY]
23 23
 func (cli *DockerCli) CmdImages(args ...string) error {
24
-	cmd := cli.Subcmd("images", []string{"[REPOSITORY]"}, "List images", true)
24
+	cmd := Cli.Subcmd("images", []string{"[REPOSITORY]"}, "List images", true)
25 25
 	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
26 26
 	all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (default hides intermediate images)")
27 27
 	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"net/url"
7 7
 	"os"
8 8
 
9
+	Cli "github.com/docker/docker/cli"
9 10
 	"github.com/docker/docker/opts"
10 11
 	flag "github.com/docker/docker/pkg/mflag"
11 12
 	"github.com/docker/docker/pkg/parsers"
... ...
@@ -19,7 +20,7 @@ import (
19 19
 //
20 20
 // Usage: docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]
21 21
 func (cli *DockerCli) CmdImport(args ...string) error {
22
-	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)
22
+	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)
23 23
 	flChanges := opts.NewListOpts(nil)
24 24
 	cmd.Var(&flChanges, []string{"c", "-change"}, "Apply Dockerfile instruction to the created image")
25 25
 	cmd.Require(flag.Min, 1)
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"fmt"
6 6
 
7 7
 	"github.com/docker/docker/api/types"
8
+	Cli "github.com/docker/docker/cli"
8 9
 	"github.com/docker/docker/pkg/httputils"
9 10
 	"github.com/docker/docker/pkg/ioutils"
10 11
 	flag "github.com/docker/docker/pkg/mflag"
... ...
@@ -15,7 +16,7 @@ import (
15 15
 //
16 16
 // Usage: docker info
17 17
 func (cli *DockerCli) CmdInfo(args ...string) error {
18
-	cmd := cli.Subcmd("info", nil, "Display system-wide information", true)
18
+	cmd := Cli.Subcmd("info", nil, "Display system-wide information", true)
19 19
 	cmd.Require(flag.Exact, 0)
20 20
 
21 21
 	cmd.ParseFlags(args, true)
... ...
@@ -9,14 +9,22 @@ import (
9 9
 	"text/template"
10 10
 
11 11
 	"github.com/docker/docker/api/types"
12
+	Cli "github.com/docker/docker/cli"
12 13
 	flag "github.com/docker/docker/pkg/mflag"
13 14
 )
14 15
 
16
+var funcMap = template.FuncMap{
17
+	"json": func(v interface{}) string {
18
+		a, _ := json.Marshal(v)
19
+		return string(a)
20
+	},
21
+}
22
+
15 23
 // CmdInspect displays low-level information on one or more containers or images.
16 24
 //
17 25
 // Usage: docker inspect [OPTIONS] CONTAINER|IMAGE [CONTAINER|IMAGE...]
18 26
 func (cli *DockerCli) CmdInspect(args ...string) error {
19
-	cmd := cli.Subcmd("inspect", []string{"CONTAINER|IMAGE [CONTAINER|IMAGE...]"}, "Return low-level information on a container or image", true)
27
+	cmd := Cli.Subcmd("inspect", []string{"CONTAINER|IMAGE [CONTAINER|IMAGE...]"}, "Return low-level information on a container or image", true)
20 28
 	tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template")
21 29
 	inspectType := cmd.String([]string{"-type"}, "", "Return JSON for specified type, (e.g image or container)")
22 30
 	cmd.Require(flag.Min, 1)
... ...
@@ -29,7 +37,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
29 29
 
30 30
 	if *tmplStr != "" {
31 31
 		if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil {
32
-			return StatusError{StatusCode: 64,
32
+			return Cli.StatusError{StatusCode: 64,
33 33
 				Status: "Template parsing error: " + err.Error()}
34 34
 		}
35 35
 	}
... ...
@@ -143,7 +151,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
143 143
 	}
144 144
 
145 145
 	if status != 0 {
146
-		return StatusError{StatusCode: status}
146
+		return Cli.StatusError{StatusCode: status}
147 147
 	}
148 148
 	return nil
149 149
 }
... ...
@@ -3,6 +3,7 @@ package client
3 3
 import (
4 4
 	"fmt"
5 5
 
6
+	Cli "github.com/docker/docker/cli"
6 7
 	flag "github.com/docker/docker/pkg/mflag"
7 8
 )
8 9
 
... ...
@@ -10,7 +11,7 @@ import (
10 10
 //
11 11
 // Usage: docker kill [OPTIONS] CONTAINER [CONTAINER...]
12 12
 func (cli *DockerCli) CmdKill(args ...string) error {
13
-	cmd := cli.Subcmd("kill", []string{"CONTAINER [CONTAINER...]"}, "Kill a running container using SIGKILL or a specified signal", true)
13
+	cmd := Cli.Subcmd("kill", []string{"CONTAINER [CONTAINER...]"}, "Kill a running container using SIGKILL or a specified signal", true)
14 14
 	signal := cmd.String([]string{"s", "-signal"}, "KILL", "Signal to send to the container")
15 15
 	cmd.Require(flag.Min, 1)
16 16
 
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"io"
5 5
 	"os"
6 6
 
7
+	Cli "github.com/docker/docker/cli"
7 8
 	flag "github.com/docker/docker/pkg/mflag"
8 9
 )
9 10
 
... ...
@@ -13,7 +14,7 @@ import (
13 13
 //
14 14
 // Usage: docker load [OPTIONS]
15 15
 func (cli *DockerCli) CmdLoad(args ...string) error {
16
-	cmd := cli.Subcmd("load", nil, "Load an image from a tar archive or STDIN", true)
16
+	cmd := Cli.Subcmd("load", nil, "Load an image from a tar archive or STDIN", true)
17 17
 	infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN")
18 18
 	cmd.Require(flag.Exact, 0)
19 19
 
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"strings"
10 10
 
11 11
 	"github.com/docker/docker/api/types"
12
+	Cli "github.com/docker/docker/cli"
12 13
 	"github.com/docker/docker/cliconfig"
13 14
 	flag "github.com/docker/docker/pkg/mflag"
14 15
 	"github.com/docker/docker/pkg/term"
... ...
@@ -21,7 +22,7 @@ import (
21 21
 //
22 22
 // Usage: docker login SERVER
23 23
 func (cli *DockerCli) CmdLogin(args ...string) error {
24
-	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)
24
+	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)
25 25
 	cmd.Require(flag.Max, 1)
26 26
 
27 27
 	var username, password, email string
... ...
@@ -3,6 +3,7 @@ package client
3 3
 import (
4 4
 	"fmt"
5 5
 
6
+	Cli "github.com/docker/docker/cli"
6 7
 	flag "github.com/docker/docker/pkg/mflag"
7 8
 	"github.com/docker/docker/registry"
8 9
 )
... ...
@@ -13,7 +14,7 @@ import (
13 13
 //
14 14
 // Usage: docker logout [SERVER]
15 15
 func (cli *DockerCli) CmdLogout(args ...string) error {
16
-	cmd := cli.Subcmd("logout", []string{"[SERVER]"}, "Log out from a Docker registry, if no server is\nspecified \""+registry.INDEXSERVER+"\" is the default.", true)
16
+	cmd := Cli.Subcmd("logout", []string{"[SERVER]"}, "Log out from a Docker registry, if no server is\nspecified \""+registry.INDEXSERVER+"\" is the default.", true)
17 17
 	cmd.Require(flag.Max, 1)
18 18
 
19 19
 	cmd.ParseFlags(args, true)
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	"time"
8 8
 
9 9
 	"github.com/docker/docker/api/types"
10
+	Cli "github.com/docker/docker/cli"
10 11
 	flag "github.com/docker/docker/pkg/mflag"
11 12
 	"github.com/docker/docker/pkg/timeutils"
12 13
 )
... ...
@@ -15,7 +16,7 @@ import (
15 15
 //
16 16
 // docker logs [OPTIONS] CONTAINER
17 17
 func (cli *DockerCli) CmdLogs(args ...string) error {
18
-	cmd := cli.Subcmd("logs", []string{"CONTAINER"}, "Fetch the logs of a container", true)
18
+	cmd := Cli.Subcmd("logs", []string{"CONTAINER"}, "Fetch the logs of a container", true)
19 19
 	follow := cmd.Bool([]string{"f", "-follow"}, false, "Follow log output")
20 20
 	since := cmd.String([]string{"-since"}, "", "Show logs since timestamp")
21 21
 	times := cmd.Bool([]string{"t", "-timestamps"}, false, "Show timestamps")
... ...
@@ -3,6 +3,7 @@ package client
3 3
 import (
4 4
 	"fmt"
5 5
 
6
+	Cli "github.com/docker/docker/cli"
6 7
 	flag "github.com/docker/docker/pkg/mflag"
7 8
 )
8 9
 
... ...
@@ -10,7 +11,7 @@ import (
10 10
 //
11 11
 // Usage: docker pause CONTAINER [CONTAINER...]
12 12
 func (cli *DockerCli) CmdPause(args ...string) error {
13
-	cmd := cli.Subcmd("pause", []string{"CONTAINER [CONTAINER...]"}, "Pause all processes within a container", true)
13
+	cmd := Cli.Subcmd("pause", []string{"CONTAINER [CONTAINER...]"}, "Pause all processes within a container", true)
14 14
 	cmd.Require(flag.Min, 1)
15 15
 
16 16
 	cmd.ParseFlags(args, true)
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"fmt"
6 6
 	"strings"
7 7
 
8
+	Cli "github.com/docker/docker/cli"
8 9
 	flag "github.com/docker/docker/pkg/mflag"
9 10
 	"github.com/docker/docker/pkg/nat"
10 11
 )
... ...
@@ -14,7 +15,7 @@ import (
14 14
 //
15 15
 // Usage: docker port CONTAINER [PRIVATE_PORT[/PROTO]]
16 16
 func (cli *DockerCli) CmdPort(args ...string) error {
17
-	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)
17
+	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)
18 18
 	cmd.Require(flag.Min, 1)
19 19
 
20 20
 	cmd.ParseFlags(args, true)
... ...
@@ -7,6 +7,7 @@ import (
7 7
 
8 8
 	"github.com/docker/docker/api/client/ps"
9 9
 	"github.com/docker/docker/api/types"
10
+	Cli "github.com/docker/docker/cli"
10 11
 	"github.com/docker/docker/opts"
11 12
 	flag "github.com/docker/docker/pkg/mflag"
12 13
 	"github.com/docker/docker/pkg/parsers/filters"
... ...
@@ -22,7 +23,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
22 22
 		psFilterArgs = filters.Args{}
23 23
 		v            = url.Values{}
24 24
 
25
-		cmd      = cli.Subcmd("ps", nil, "List containers", true)
25
+		cmd      = Cli.Subcmd("ps", nil, "List containers", true)
26 26
 		quiet    = cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
27 27
 		size     = cmd.Bool([]string{"s", "-size"}, false, "Display total file sizes")
28 28
 		all      = cmd.Bool([]string{"a", "-all"}, false, "Show all containers (default shows just running)")
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"fmt"
5 5
 	"net/url"
6 6
 
7
+	Cli "github.com/docker/docker/cli"
7 8
 	"github.com/docker/docker/graph/tags"
8 9
 	flag "github.com/docker/docker/pkg/mflag"
9 10
 	"github.com/docker/docker/pkg/parsers"
... ...
@@ -15,7 +16,7 @@ import (
15 15
 //
16 16
 // Usage: docker pull [OPTIONS] IMAGENAME[:TAG|@DIGEST]
17 17
 func (cli *DockerCli) CmdPull(args ...string) error {
18
-	cmd := cli.Subcmd("pull", []string{"NAME[:TAG|@DIGEST]"}, "Pull an image or a repository from a registry", true)
18
+	cmd := Cli.Subcmd("pull", []string{"NAME[:TAG|@DIGEST]"}, "Pull an image or a repository from a registry", true)
19 19
 	allTags := cmd.Bool([]string{"a", "-all-tags"}, false, "Download all tagged images in the repository")
20 20
 	cmd.Require(flag.Exact, 1)
21 21
 
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"fmt"
5 5
 	"net/url"
6 6
 
7
+	Cli "github.com/docker/docker/cli"
7 8
 	flag "github.com/docker/docker/pkg/mflag"
8 9
 	"github.com/docker/docker/pkg/parsers"
9 10
 	"github.com/docker/docker/registry"
... ...
@@ -13,7 +14,7 @@ import (
13 13
 //
14 14
 // Usage: docker push NAME[:TAG]
15 15
 func (cli *DockerCli) CmdPush(args ...string) error {
16
-	cmd := cli.Subcmd("push", []string{"NAME[:TAG]"}, "Push an image or a repository to a registry", true)
16
+	cmd := Cli.Subcmd("push", []string{"NAME[:TAG]"}, "Push an image or a repository to a registry", true)
17 17
 	cmd.Require(flag.Exact, 1)
18 18
 
19 19
 	cmd.ParseFlags(args, true)
... ...
@@ -3,6 +3,7 @@ package client
3 3
 import (
4 4
 	"fmt"
5 5
 
6
+	Cli "github.com/docker/docker/cli"
6 7
 	flag "github.com/docker/docker/pkg/mflag"
7 8
 )
8 9
 
... ...
@@ -10,7 +11,7 @@ import (
10 10
 //
11 11
 // Usage: docker rename OLD_NAME NEW_NAME
12 12
 func (cli *DockerCli) CmdRename(args ...string) error {
13
-	cmd := cli.Subcmd("rename", []string{"OLD_NAME NEW_NAME"}, "Rename a container", true)
13
+	cmd := Cli.Subcmd("rename", []string{"OLD_NAME NEW_NAME"}, "Rename a container", true)
14 14
 	cmd.Require(flag.Exact, 2)
15 15
 
16 16
 	cmd.ParseFlags(args, true)
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"net/url"
6 6
 	"strconv"
7 7
 
8
+	Cli "github.com/docker/docker/cli"
8 9
 	flag "github.com/docker/docker/pkg/mflag"
9 10
 )
10 11
 
... ...
@@ -12,7 +13,7 @@ import (
12 12
 //
13 13
 // Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...]
14 14
 func (cli *DockerCli) CmdRestart(args ...string) error {
15
-	cmd := cli.Subcmd("restart", []string{"CONTAINER [CONTAINER...]"}, "Restart a running container", true)
15
+	cmd := Cli.Subcmd("restart", []string{"CONTAINER [CONTAINER...]"}, "Restart a running container", true)
16 16
 	nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Seconds to wait for stop before killing the container")
17 17
 	cmd.Require(flag.Min, 1)
18 18
 
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"net/url"
6 6
 	"strings"
7 7
 
8
+	Cli "github.com/docker/docker/cli"
8 9
 	flag "github.com/docker/docker/pkg/mflag"
9 10
 )
10 11
 
... ...
@@ -12,7 +13,7 @@ import (
12 12
 //
13 13
 // Usage: docker rm [OPTIONS] CONTAINER [CONTAINER...]
14 14
 func (cli *DockerCli) CmdRm(args ...string) error {
15
-	cmd := cli.Subcmd("rm", []string{"CONTAINER [CONTAINER...]"}, "Remove one or more containers", true)
15
+	cmd := Cli.Subcmd("rm", []string{"CONTAINER [CONTAINER...]"}, "Remove one or more containers", true)
16 16
 	v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated with the container")
17 17
 	link := cmd.Bool([]string{"l", "#link", "-link"}, false, "Remove the specified link")
18 18
 	force := cmd.Bool([]string{"f", "-force"}, false, "Force the removal of a running container (uses SIGKILL)")
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"net/url"
7 7
 
8 8
 	"github.com/docker/docker/api/types"
9
+	Cli "github.com/docker/docker/cli"
9 10
 	flag "github.com/docker/docker/pkg/mflag"
10 11
 )
11 12
 
... ...
@@ -13,7 +14,7 @@ import (
13 13
 //
14 14
 // Usage: docker rmi [OPTIONS] IMAGE [IMAGE...]
15 15
 func (cli *DockerCli) CmdRmi(args ...string) error {
16
-	cmd := cli.Subcmd("rmi", []string{"IMAGE [IMAGE...]"}, "Remove one or more images", true)
16
+	cmd := Cli.Subcmd("rmi", []string{"IMAGE [IMAGE...]"}, "Remove one or more images", true)
17 17
 	force := cmd.Bool([]string{"f", "-force"}, false, "Force removal of the image")
18 18
 	noprune := cmd.Bool([]string{"-no-prune"}, false, "Do not delete untagged parents")
19 19
 	cmd.Require(flag.Min, 1)
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"runtime"
9 9
 
10 10
 	"github.com/Sirupsen/logrus"
11
+	Cli "github.com/docker/docker/cli"
11 12
 	"github.com/docker/docker/opts"
12 13
 	"github.com/docker/docker/pkg/promise"
13 14
 	"github.com/docker/docker/pkg/signal"
... ...
@@ -39,7 +40,7 @@ func (cid *cidFile) Write(id string) error {
39 39
 //
40 40
 // Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
41 41
 func (cli *DockerCli) CmdRun(args ...string) error {
42
-	cmd := cli.Subcmd("run", []string{"IMAGE [COMMAND] [ARG...]"}, "Run a command in a new container", true)
42
+	cmd := Cli.Subcmd("run", []string{"IMAGE [COMMAND] [ARG...]"}, "Run a command in a new container", true)
43 43
 
44 44
 	// These are flags not stored in Config/HostConfig
45 45
 	var (
... ...
@@ -249,7 +250,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
249 249
 		}
250 250
 	}
251 251
 	if status != 0 {
252
-		return StatusError{StatusCode: status}
252
+		return Cli.StatusError{StatusCode: status}
253 253
 	}
254 254
 	return nil
255 255
 }
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"net/url"
7 7
 	"os"
8 8
 
9
+	Cli "github.com/docker/docker/cli"
9 10
 	flag "github.com/docker/docker/pkg/mflag"
10 11
 )
11 12
 
... ...
@@ -15,7 +16,7 @@ import (
15 15
 //
16 16
 // Usage: docker save [OPTIONS] IMAGE [IMAGE...]
17 17
 func (cli *DockerCli) CmdSave(args ...string) error {
18
-	cmd := cli.Subcmd("save", []string{"IMAGE [IMAGE...]"}, "Save an image(s) to a tar archive (streamed to STDOUT by default)", true)
18
+	cmd := Cli.Subcmd("save", []string{"IMAGE [IMAGE...]"}, "Save an image(s) to a tar archive (streamed to STDOUT by default)", true)
19 19
 	outfile := cmd.String([]string{"o", "-output"}, "", "Write to an file, instead of STDOUT")
20 20
 	cmd.Require(flag.Min, 1)
21 21
 
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"strings"
9 9
 	"text/tabwriter"
10 10
 
11
+	Cli "github.com/docker/docker/cli"
11 12
 	flag "github.com/docker/docker/pkg/mflag"
12 13
 	"github.com/docker/docker/pkg/parsers"
13 14
 	"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 }
25 25
 //
26 26
 // Usage: docker search [OPTIONS] TERM
27 27
 func (cli *DockerCli) CmdSearch(args ...string) error {
28
-	cmd := cli.Subcmd("search", []string{"TERM"}, "Search the Docker Hub for images", true)
28
+	cmd := Cli.Subcmd("search", []string{"TERM"}, "Search the Docker Hub for images", true)
29 29
 	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
30 30
 	trusted := cmd.Bool([]string{"#t", "#trusted", "#-trusted"}, false, "Only show trusted builds")
31 31
 	automated := cmd.Bool([]string{"-automated"}, false, "Only show automated builds")
... ...
@@ -9,6 +9,7 @@ import (
9 9
 
10 10
 	"github.com/Sirupsen/logrus"
11 11
 	"github.com/docker/docker/api/types"
12
+	Cli "github.com/docker/docker/cli"
12 13
 	flag "github.com/docker/docker/pkg/mflag"
13 14
 	"github.com/docker/docker/pkg/promise"
14 15
 	"github.com/docker/docker/pkg/signal"
... ...
@@ -44,7 +45,7 @@ func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
44 44
 //
45 45
 // Usage: docker start [OPTIONS] CONTAINER [CONTAINER...]
46 46
 func (cli *DockerCli) CmdStart(args ...string) error {
47
-	cmd := cli.Subcmd("start", []string{"CONTAINER [CONTAINER...]"}, "Start one or more stopped containers", true)
47
+	cmd := Cli.Subcmd("start", []string{"CONTAINER [CONTAINER...]"}, "Start one or more stopped containers", true)
48 48
 	attach := cmd.Bool([]string{"a", "-attach"}, false, "Attach STDOUT/STDERR and forward signals")
49 49
 	openStdin := cmd.Bool([]string{"i", "-interactive"}, false, "Attach container's STDIN")
50 50
 	cmd.Require(flag.Min, 1)
... ...
@@ -162,7 +163,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
162 162
 			return err
163 163
 		}
164 164
 		if status != 0 {
165
-			return StatusError{StatusCode: status}
165
+			return Cli.StatusError{StatusCode: status}
166 166
 		}
167 167
 	}
168 168
 	return nil
... ...
@@ -12,6 +12,7 @@ import (
12 12
 	"time"
13 13
 
14 14
 	"github.com/docker/docker/api/types"
15
+	Cli "github.com/docker/docker/cli"
15 16
 	flag "github.com/docker/docker/pkg/mflag"
16 17
 	"github.com/docker/docker/pkg/units"
17 18
 )
... ...
@@ -124,7 +125,7 @@ func (s *containerStats) Display(w io.Writer) error {
124 124
 //
125 125
 // Usage: docker stats CONTAINER [CONTAINER...]
126 126
 func (cli *DockerCli) CmdStats(args ...string) error {
127
-	cmd := cli.Subcmd("stats", []string{"CONTAINER [CONTAINER...]"}, "Display a live stream of one or more containers' resource usage statistics", true)
127
+	cmd := Cli.Subcmd("stats", []string{"CONTAINER [CONTAINER...]"}, "Display a live stream of one or more containers' resource usage statistics", true)
128 128
 	noStream := cmd.Bool([]string{"-no-stream"}, false, "Disable streaming stats and only pull the first result")
129 129
 	cmd.Require(flag.Min, 1)
130 130
 
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"net/url"
6 6
 	"strconv"
7 7
 
8
+	Cli "github.com/docker/docker/cli"
8 9
 	flag "github.com/docker/docker/pkg/mflag"
9 10
 )
10 11
 
... ...
@@ -14,7 +15,7 @@ import (
14 14
 //
15 15
 // Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...]
16 16
 func (cli *DockerCli) CmdStop(args ...string) error {
17
-	cmd := cli.Subcmd("stop", []string{"CONTAINER [CONTAINER...]"}, "Stop a running container by sending SIGTERM and then SIGKILL after a\ngrace period", true)
17
+	cmd := Cli.Subcmd("stop", []string{"CONTAINER [CONTAINER...]"}, "Stop a running container by sending SIGTERM and then SIGKILL after a\ngrace period", true)
18 18
 	nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Seconds to wait for stop before killing it")
19 19
 	cmd.Require(flag.Min, 1)
20 20
 
... ...
@@ -3,6 +3,7 @@ package client
3 3
 import (
4 4
 	"net/url"
5 5
 
6
+	Cli "github.com/docker/docker/cli"
6 7
 	flag "github.com/docker/docker/pkg/mflag"
7 8
 	"github.com/docker/docker/pkg/parsers"
8 9
 	"github.com/docker/docker/registry"
... ...
@@ -12,7 +13,7 @@ import (
12 12
 //
13 13
 // Usage: docker tag [OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]
14 14
 func (cli *DockerCli) CmdTag(args ...string) error {
15
-	cmd := cli.Subcmd("tag", []string{"IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]"}, "Tag an image into a repository", true)
15
+	cmd := Cli.Subcmd("tag", []string{"IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]"}, "Tag an image into a repository", true)
16 16
 	force := cmd.Bool([]string{"f", "#force", "-force"}, false, "Force")
17 17
 	cmd.Require(flag.Exact, 2)
18 18
 
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"text/tabwriter"
9 9
 
10 10
 	"github.com/docker/docker/api/types"
11
+	Cli "github.com/docker/docker/cli"
11 12
 	flag "github.com/docker/docker/pkg/mflag"
12 13
 )
13 14
 
... ...
@@ -15,7 +16,7 @@ import (
15 15
 //
16 16
 // Usage: docker top CONTAINER
17 17
 func (cli *DockerCli) CmdTop(args ...string) error {
18
-	cmd := cli.Subcmd("top", []string{"CONTAINER [ps OPTIONS]"}, "Display the running processes of a container", true)
18
+	cmd := Cli.Subcmd("top", []string{"CONTAINER [ps OPTIONS]"}, "Display the running processes of a container", true)
19 19
 	cmd.Require(flag.Min, 1)
20 20
 
21 21
 	cmd.ParseFlags(args, true)
... ...
@@ -3,6 +3,7 @@ package client
3 3
 import (
4 4
 	"fmt"
5 5
 
6
+	Cli "github.com/docker/docker/cli"
6 7
 	flag "github.com/docker/docker/pkg/mflag"
7 8
 )
8 9
 
... ...
@@ -10,7 +11,7 @@ import (
10 10
 //
11 11
 // Usage: docker unpause CONTAINER [CONTAINER...]
12 12
 func (cli *DockerCli) CmdUnpause(args ...string) error {
13
-	cmd := cli.Subcmd("unpause", []string{"CONTAINER [CONTAINER...]"}, "Unpause all processes within a container", true)
13
+	cmd := Cli.Subcmd("unpause", []string{"CONTAINER [CONTAINER...]"}, "Unpause all processes within a container", true)
14 14
 	cmd.Require(flag.Min, 1)
15 15
 
16 16
 	cmd.ParseFlags(args, true)
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"github.com/docker/docker/api"
9 9
 	"github.com/docker/docker/api/types"
10 10
 	"github.com/docker/docker/autogen/dockerversion"
11
+	Cli "github.com/docker/docker/cli"
11 12
 	flag "github.com/docker/docker/pkg/mflag"
12 13
 	"github.com/docker/docker/utils"
13 14
 )
... ...
@@ -42,7 +43,7 @@ type VersionData struct {
42 42
 //
43 43
 // Usage: docker version
44 44
 func (cli *DockerCli) CmdVersion(args ...string) (err error) {
45
-	cmd := cli.Subcmd("version", nil, "Show the Docker version information.", true)
45
+	cmd := Cli.Subcmd("version", nil, "Show the Docker version information.", true)
46 46
 	tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template")
47 47
 	cmd.Require(flag.Exact, 0)
48 48
 
... ...
@@ -53,7 +54,7 @@ func (cli *DockerCli) CmdVersion(args ...string) (err error) {
53 53
 
54 54
 	var tmpl *template.Template
55 55
 	if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil {
56
-		return StatusError{StatusCode: 64,
56
+		return Cli.StatusError{StatusCode: 64,
57 57
 			Status: "Template parsing error: " + err.Error()}
58 58
 	}
59 59
 
... ...
@@ -85,7 +86,7 @@ func (cli *DockerCli) CmdVersion(args ...string) (err error) {
85 85
 	defer serverResp.body.Close()
86 86
 
87 87
 	if err = json.NewDecoder(serverResp.body).Decode(&vd.Server); err != nil {
88
-		return StatusError{StatusCode: 1,
88
+		return Cli.StatusError{StatusCode: 1,
89 89
 			Status: "Error reading remote version: " + err.Error()}
90 90
 	}
91 91
 
... ...
@@ -3,6 +3,7 @@ package client
3 3
 import (
4 4
 	"fmt"
5 5
 
6
+	Cli "github.com/docker/docker/cli"
6 7
 	flag "github.com/docker/docker/pkg/mflag"
7 8
 )
8 9
 
... ...
@@ -12,7 +13,7 @@ import (
12 12
 //
13 13
 // Usage: docker wait CONTAINER [CONTAINER...]
14 14
 func (cli *DockerCli) CmdWait(args ...string) error {
15
-	cmd := cli.Subcmd("wait", []string{"CONTAINER [CONTAINER...]"}, "Block until a container stops, then print its exit code.", true)
15
+	cmd := Cli.Subcmd("wait", []string{"CONTAINER [CONTAINER...]"}, "Block until a container stops, then print its exit code.", true)
16 16
 	cmd.Require(flag.Min, 1)
17 17
 
18 18
 	cmd.ParseFlags(args, true)
19 19
new file mode 100644
... ...
@@ -0,0 +1,200 @@
0
+package cli
1
+
2
+import (
3
+	"errors"
4
+	"fmt"
5
+	"io"
6
+	"os"
7
+	"reflect"
8
+	"strings"
9
+
10
+	flag "github.com/docker/docker/pkg/mflag"
11
+)
12
+
13
+// Cli represents a command line interface.
14
+type Cli struct {
15
+	Stderr   io.Writer
16
+	handlers []Handler
17
+	Usage    func()
18
+}
19
+
20
+// Handler holds the different commands Cli will call
21
+// It should have methods with names starting with `Cmd` like:
22
+// 	func (h myHandler) CmdFoo(args ...string) error
23
+type Handler interface{}
24
+
25
+// Initializer can be optionally implemented by a Handler to
26
+// initialize before each call to one of its commands.
27
+type Initializer interface {
28
+	Initialize() error
29
+}
30
+
31
+// New instantiates a ready-to-use Cli.
32
+func New(handlers ...Handler) *Cli {
33
+	// make the generic Cli object the first cli handler
34
+	// in order to handle `docker help` appropriately
35
+	cli := new(Cli)
36
+	cli.handlers = append([]Handler{cli}, handlers...)
37
+	return cli
38
+}
39
+
40
+// initErr is an error returned upon initialization of a handler implementing Initializer.
41
+type initErr struct{ error }
42
+
43
+func (err initErr) Error() string {
44
+	return err.Error()
45
+}
46
+
47
+func (cli *Cli) command(args ...string) (func(...string) error, error) {
48
+	for _, c := range cli.handlers {
49
+		if c == nil {
50
+			continue
51
+		}
52
+		camelArgs := make([]string, len(args))
53
+		for i, s := range args {
54
+			if len(s) == 0 {
55
+				return nil, errors.New("empty command")
56
+			}
57
+			camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:])
58
+		}
59
+		methodName := "Cmd" + strings.Join(camelArgs, "")
60
+		method := reflect.ValueOf(c).MethodByName(methodName)
61
+		if method.IsValid() {
62
+			if c, ok := c.(Initializer); ok {
63
+				if err := c.Initialize(); err != nil {
64
+					return nil, initErr{err}
65
+				}
66
+			}
67
+			return method.Interface().(func(...string) error), nil
68
+		}
69
+	}
70
+	return nil, errors.New("command not found")
71
+}
72
+
73
+// Run executes the specified command.
74
+func (cli *Cli) Run(args ...string) error {
75
+	if len(args) > 1 {
76
+		command, err := cli.command(args[:2]...)
77
+		switch err := err.(type) {
78
+		case nil:
79
+			return command(args[2:]...)
80
+		case initErr:
81
+			return err.error
82
+		}
83
+	}
84
+	if len(args) > 0 {
85
+		command, err := cli.command(args[0])
86
+		switch err := err.(type) {
87
+		case nil:
88
+			return command(args[1:]...)
89
+		case initErr:
90
+			return err.error
91
+		}
92
+		cli.noSuchCommand(args[0])
93
+	}
94
+	return cli.CmdHelp()
95
+}
96
+
97
+func (cli *Cli) noSuchCommand(command string) {
98
+	if cli.Stderr == nil {
99
+		cli.Stderr = os.Stderr
100
+	}
101
+	fmt.Fprintf(cli.Stderr, "docker: '%s' is not a docker command.\nSee 'docker --help'.\n", command)
102
+	os.Exit(1)
103
+}
104
+
105
+// CmdHelp displays information on a Docker command.
106
+//
107
+// If more than one command is specified, information is only shown for the first command.
108
+//
109
+// Usage: docker help COMMAND or docker COMMAND --help
110
+func (cli *Cli) CmdHelp(args ...string) error {
111
+	if len(args) > 1 {
112
+		command, err := cli.command(args[:2]...)
113
+		switch err := err.(type) {
114
+		case nil:
115
+			command("--help")
116
+			return nil
117
+		case initErr:
118
+			return err.error
119
+		}
120
+	}
121
+	if len(args) > 0 {
122
+		command, err := cli.command(args[0])
123
+		switch err := err.(type) {
124
+		case nil:
125
+			command("--help")
126
+			return nil
127
+		case initErr:
128
+			return err.error
129
+		}
130
+		cli.noSuchCommand(args[0])
131
+	}
132
+
133
+	if cli.Usage == nil {
134
+		flag.Usage()
135
+	} else {
136
+		cli.Usage()
137
+	}
138
+
139
+	return nil
140
+}
141
+
142
+// Subcmd is a subcommand of the main "docker" command.
143
+// A subcommand represents an action that can be performed
144
+// from the Docker command line client.
145
+//
146
+// To see all available subcommands, run "docker --help".
147
+func Subcmd(name string, synopses []string, description string, exitOnError bool) *flag.FlagSet {
148
+	var errorHandling flag.ErrorHandling
149
+	if exitOnError {
150
+		errorHandling = flag.ExitOnError
151
+	} else {
152
+		errorHandling = flag.ContinueOnError
153
+	}
154
+	flags := flag.NewFlagSet(name, errorHandling)
155
+	flags.Usage = func() {
156
+		flags.ShortUsage()
157
+		flags.PrintDefaults()
158
+	}
159
+
160
+	flags.ShortUsage = func() {
161
+		options := ""
162
+		if flags.FlagCountUndeprecated() > 0 {
163
+			options = " [OPTIONS]"
164
+		}
165
+
166
+		if len(synopses) == 0 {
167
+			synopses = []string{""}
168
+		}
169
+
170
+		// Allow for multiple command usage synopses.
171
+		for i, synopsis := range synopses {
172
+			lead := "\t"
173
+			if i == 0 {
174
+				// First line needs the word 'Usage'.
175
+				lead = "Usage:\t"
176
+			}
177
+
178
+			if synopsis != "" {
179
+				synopsis = " " + synopsis
180
+			}
181
+
182
+			fmt.Fprintf(flags.Out(), "\n%sdocker %s%s%s", lead, name, options, synopsis)
183
+		}
184
+
185
+		fmt.Fprintf(flags.Out(), "\n\n%s\n", description)
186
+	}
187
+
188
+	return flags
189
+}
190
+
191
+// An StatusError reports an unsuccessful exit by a command.
192
+type StatusError struct {
193
+	Status     string
194
+	StatusCode int
195
+}
196
+
197
+func (e StatusError) Error() string {
198
+	return fmt.Sprintf("Status: %s, Code: %d", e.Status, e.StatusCode)
199
+}
0 200
new file mode 100644
... ...
@@ -0,0 +1,12 @@
0
+package cli
1
+
2
+import flag "github.com/docker/docker/pkg/mflag"
3
+
4
+// ClientFlags represents flags for the docker client.
5
+type ClientFlags struct {
6
+	FlagSet   *flag.FlagSet
7
+	Common    *CommonFlags
8
+	PostParse func()
9
+
10
+	ConfigDir string
11
+}
0 12
new file mode 100644
... ...
@@ -0,0 +1,20 @@
0
+package cli
1
+
2
+import (
3
+	flag "github.com/docker/docker/pkg/mflag"
4
+	"github.com/docker/docker/pkg/tlsconfig"
5
+)
6
+
7
+// CommonFlags represents flags that are common to both the client and the daemon.
8
+type CommonFlags struct {
9
+	FlagSet   *flag.FlagSet
10
+	PostParse func()
11
+
12
+	Debug      bool
13
+	Hosts      []string
14
+	LogLevel   string
15
+	TLS        bool
16
+	TLSVerify  bool
17
+	TLSOptions *tlsconfig.Options
18
+	TrustKey   string
19
+}
... ...
@@ -41,22 +41,22 @@ type CommonConfig struct {
41 41
 // the current process.
42 42
 // Subsequent calls to `flag.Parse` will populate config with values parsed
43 43
 // from the command-line.
44
-func (config *Config) InstallCommonFlags() {
45
-	opts.ListVar(&config.GraphOptions, []string{"-storage-opt"}, "Set storage driver options")
46
-	opts.ListVar(&config.ExecOptions, []string{"-exec-opt"}, "Set exec driver options")
47
-	flag.StringVar(&config.Pidfile, []string{"p", "-pidfile"}, defaultPidFile, "Path to use for daemon PID file")
48
-	flag.StringVar(&config.Root, []string{"g", "-graph"}, defaultGraph, "Root of the Docker runtime")
49
-	flag.StringVar(&config.ExecRoot, []string{"-exec-root"}, "/var/run/docker", "Root of the Docker execdriver")
50
-	flag.BoolVar(&config.AutoRestart, []string{"#r", "#-restart"}, true, "--restart on the daemon has been deprecated in favor of --restart policies on docker run")
51
-	flag.StringVar(&config.GraphDriver, []string{"s", "-storage-driver"}, "", "Storage driver to use")
52
-	flag.StringVar(&config.ExecDriver, []string{"e", "-exec-driver"}, defaultExec, "Exec driver to use")
53
-	flag.IntVar(&config.Mtu, []string{"#mtu", "-mtu"}, 0, "Set the containers network MTU")
54
-	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")
55
-	flag.StringVar(&config.CorsHeaders, []string{"-api-cors-header"}, "", "Set CORS headers in the remote API")
44
+func (config *Config) InstallCommonFlags(cmd *flag.FlagSet, usageFn func(string) string) {
45
+	cmd.Var(opts.NewListOptsRef(&config.GraphOptions, nil), []string{"-storage-opt"}, usageFn("Set storage driver options"))
46
+	cmd.Var(opts.NewListOptsRef(&config.ExecOptions, nil), []string{"-exec-opt"}, usageFn("Set exec driver options"))
47
+	cmd.StringVar(&config.Pidfile, []string{"p", "-pidfile"}, defaultPidFile, usageFn("Path to use for daemon PID file"))
48
+	cmd.StringVar(&config.Root, []string{"g", "-graph"}, defaultGraph, usageFn("Root of the Docker runtime"))
49
+	cmd.StringVar(&config.ExecRoot, []string{"-exec-root"}, "/var/run/docker", usageFn("Root of the Docker execdriver"))
50
+	cmd.BoolVar(&config.AutoRestart, []string{"#r", "#-restart"}, true, usageFn("--restart on the daemon has been deprecated in favor of --restart policies on docker run"))
51
+	cmd.StringVar(&config.GraphDriver, []string{"s", "-storage-driver"}, "", usageFn("Storage driver to use"))
52
+	cmd.StringVar(&config.ExecDriver, []string{"e", "-exec-driver"}, defaultExec, usageFn("Exec driver to use"))
53
+	cmd.IntVar(&config.Mtu, []string{"#mtu", "-mtu"}, 0, usageFn("Set the containers network MTU"))
54
+	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"))
55
+	cmd.StringVar(&config.CorsHeaders, []string{"-api-cors-header"}, "", usageFn("Set CORS headers in the remote API"))
56 56
 	// FIXME: why the inconsistency between "hosts" and "sockets"?
57
-	opts.IPListVar(&config.Dns, []string{"#dns", "-dns"}, "DNS server to use")
58
-	opts.DNSSearchListVar(&config.DnsSearch, []string{"-dns-search"}, "DNS search domains to use")
59
-	opts.LabelListVar(&config.Labels, []string{"-label"}, "Set key=value labels to the daemon")
60
-	flag.StringVar(&config.LogConfig.Type, []string{"-log-driver"}, "json-file", "Default driver for container logs")
61
-	opts.LogOptsVar(config.LogConfig.Config, []string{"-log-opt"}, "Set log driver options")
57
+	cmd.Var(opts.NewListOptsRef(&config.Dns, opts.ValidateIPAddress), []string{"#dns", "-dns"}, usageFn("DNS server to use"))
58
+	cmd.Var(opts.NewListOptsRef(&config.DnsSearch, opts.ValidateDNSSearch), []string{"-dns-search"}, usageFn("DNS search domains to use"))
59
+	cmd.Var(opts.NewListOptsRef(&config.Labels, opts.ValidateLabel), []string{"-label"}, usageFn("Set key=value labels to the daemon"))
60
+	cmd.StringVar(&config.LogConfig.Type, []string{"-log-driver"}, "json-file", usageFn("Default driver for container logs"))
61
+	cmd.Var(opts.NewMapOpts(config.LogConfig.Config, nil), []string{"-log-opt"}, usageFn("Set log driver options"))
62 62
 }
... ...
@@ -4,7 +4,7 @@ package daemon
4 4
 
5 5
 import flag "github.com/docker/docker/pkg/mflag"
6 6
 
7
-func (config *Config) attachExperimentalFlags() {
8
-	flag.StringVar(&config.DefaultNetwork, []string{"-default-network"}, "", "Set default network")
9
-	flag.StringVar(&config.NetworkKVStore, []string{"-kv-store"}, "", "Set KV Store configuration")
7
+func (config *Config) attachExperimentalFlags(cmd *flag.FlagSet, usageFn func(string) string) {
8
+	cmd.StringVar(&config.DefaultNetwork, []string{"-default-network"}, "", usageFn("Set default network"))
9
+	cmd.StringVar(&config.NetworkKVStore, []string{"-kv-store"}, "", usageFn("Set KV Store configuration"))
10 10
 }
... ...
@@ -49,27 +49,28 @@ type bridgeConfig struct {
49 49
 // the current process.
50 50
 // Subsequent calls to `flag.Parse` will populate config with values parsed
51 51
 // from the command-line.
52
-func (config *Config) InstallFlags() {
52
+func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) string) {
53 53
 	// First handle install flags which are consistent cross-platform
54
-	config.InstallCommonFlags()
54
+	config.InstallCommonFlags(cmd, usageFn)
55 55
 
56 56
 	// Then platform-specific install flags
57
-	flag.BoolVar(&config.EnableSelinuxSupport, []string{"-selinux-enabled"}, false, "Enable selinux support")
58
-	flag.StringVar(&config.SocketGroup, []string{"G", "-group"}, "docker", "Group for the unix socket")
57
+	cmd.BoolVar(&config.EnableSelinuxSupport, []string{"-selinux-enabled"}, false, usageFn("Enable selinux support"))
58
+	cmd.StringVar(&config.SocketGroup, []string{"G", "-group"}, "docker", usageFn("Group for the unix socket"))
59 59
 	config.Ulimits = make(map[string]*ulimit.Ulimit)
60
-	opts.UlimitMapVar(config.Ulimits, []string{"-default-ulimit"}, "Set default ulimits for containers")
61
-	flag.BoolVar(&config.Bridge.EnableIPTables, []string{"#iptables", "-iptables"}, true, "Enable addition of iptables rules")
62
-	flag.BoolVar(&config.Bridge.EnableIPForward, []string{"#ip-forward", "-ip-forward"}, true, "Enable net.ipv4.ip_forward")
63
-	flag.BoolVar(&config.Bridge.EnableIPMasq, []string{"-ip-masq"}, true, "Enable IP masquerading")
64
-	flag.BoolVar(&config.Bridge.EnableIPv6, []string{"-ipv6"}, false, "Enable IPv6 networking")
65
-	flag.StringVar(&config.Bridge.IP, []string{"#bip", "-bip"}, "", "Specify network bridge IP")
66
-	flag.StringVar(&config.Bridge.Iface, []string{"b", "-bridge"}, "", "Attach containers to a network bridge")
67
-	flag.StringVar(&config.Bridge.FixedCIDR, []string{"-fixed-cidr"}, "", "IPv4 subnet for fixed IPs")
68
-	flag.StringVar(&config.Bridge.FixedCIDRv6, []string{"-fixed-cidr-v6"}, "", "IPv6 subnet for fixed IPs")
69
-	opts.IPVar(&config.Bridge.DefaultGatewayIPv4, []string{"-default-gateway"}, "", "Container default gateway IPv4 address")
70
-	opts.IPVar(&config.Bridge.DefaultGatewayIPv6, []string{"-default-gateway-v6"}, "", "Container default gateway IPv6 address")
71
-	flag.BoolVar(&config.Bridge.InterContainerCommunication, []string{"#icc", "-icc"}, true, "Enable inter-container communication")
72
-	opts.IPVar(&config.Bridge.DefaultIP, []string{"#ip", "-ip"}, "0.0.0.0", "Default IP when binding container ports")
73
-	flag.BoolVar(&config.Bridge.EnableUserlandProxy, []string{"-userland-proxy"}, true, "Use userland proxy for loopback traffic")
74
-	config.attachExperimentalFlags()
60
+	cmd.Var(opts.NewUlimitOpt(&config.Ulimits), []string{"-default-ulimit"}, usageFn("Set default ulimits for containers"))
61
+	cmd.BoolVar(&config.Bridge.EnableIPTables, []string{"#iptables", "-iptables"}, true, usageFn("Enable addition of iptables rules"))
62
+	cmd.BoolVar(&config.Bridge.EnableIPForward, []string{"#ip-forward", "-ip-forward"}, true, usageFn("Enable net.ipv4.ip_forward"))
63
+	cmd.BoolVar(&config.Bridge.EnableIPMasq, []string{"-ip-masq"}, true, usageFn("Enable IP masquerading"))
64
+	cmd.BoolVar(&config.Bridge.EnableIPv6, []string{"-ipv6"}, false, usageFn("Enable IPv6 networking"))
65
+	cmd.StringVar(&config.Bridge.IP, []string{"#bip", "-bip"}, "", usageFn("Specify network bridge IP"))
66
+	cmd.StringVar(&config.Bridge.Iface, []string{"b", "-bridge"}, "", usageFn("Attach containers to a network bridge"))
67
+	cmd.StringVar(&config.Bridge.FixedCIDR, []string{"-fixed-cidr"}, "", usageFn("IPv4 subnet for fixed IPs"))
68
+	cmd.StringVar(&config.Bridge.FixedCIDRv6, []string{"-fixed-cidr-v6"}, "", usageFn("IPv6 subnet for fixed IPs"))
69
+	cmd.Var(opts.NewIpOpt(&config.Bridge.DefaultGatewayIPv4, ""), []string{"-default-gateway"}, usageFn("Container default gateway IPv4 address"))
70
+	cmd.Var(opts.NewIpOpt(&config.Bridge.DefaultGatewayIPv6, ""), []string{"-default-gateway-v6"}, usageFn("Container default gateway IPv6 address"))
71
+	cmd.BoolVar(&config.Bridge.InterContainerCommunication, []string{"#icc", "-icc"}, true, usageFn("Enable inter-container communication"))
72
+	cmd.Var(opts.NewIpOpt(&config.Bridge.DefaultIP, "0.0.0.0"), []string{"#ip", "-ip"}, usageFn("Default IP when binding container ports"))
73
+	cmd.BoolVar(&config.Bridge.EnableUserlandProxy, []string{"-userland-proxy"}, true, usageFn("Use userland proxy for loopback traffic"))
74
+
75
+	config.attachExperimentalFlags(cmd, usageFn)
75 76
 }
... ...
@@ -2,5 +2,7 @@
2 2
 
3 3
 package daemon
4 4
 
5
-func (config *Config) attachExperimentalFlags() {
5
+import flag "github.com/docker/docker/pkg/mflag"
6
+
7
+func (config *Config) attachExperimentalFlags(cmd *flag.FlagSet, usageFn func(string) string) {
6 8
 }
... ...
@@ -32,9 +32,9 @@ type Config struct {
32 32
 // the current process.
33 33
 // Subsequent calls to `flag.Parse` will populate config with values parsed
34 34
 // from the command-line.
35
-func (config *Config) InstallFlags() {
35
+func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) string) {
36 36
 	// First handle install flags which are consistent cross-platform
37
-	config.InstallCommonFlags()
37
+	config.InstallCommonFlags(cmd, usageFn)
38 38
 
39 39
 	// Then platform-specific install flags.
40 40
 	flag.StringVar(&config.Bridge.VirtualSwitchName, []string{"b", "-bridge"}, "", "Attach containers to a virtual switch")
... ...
@@ -1,11 +1,28 @@
1
-// +build !daemon
2
-
3 1
 package main
4 2
 
5 3
 import (
6
-	"log" // see gh#8745, client needs to use go log pkg
4
+	"path/filepath"
5
+
6
+	"github.com/docker/docker/cli"
7
+	"github.com/docker/docker/cliconfig"
8
+	flag "github.com/docker/docker/pkg/mflag"
7 9
 )
8 10
 
9
-func mainDaemon() {
10
-	log.Fatal("This is a client-only binary - running the Docker daemon is not supported.")
11
+var clientFlags = &cli.ClientFlags{FlagSet: new(flag.FlagSet), Common: commonFlags}
12
+
13
+func init() {
14
+	client := clientFlags.FlagSet
15
+	client.StringVar(&clientFlags.ConfigDir, []string{"-config"}, cliconfig.ConfigDir(), "Location of client config files")
16
+
17
+	clientFlags.PostParse = func() {
18
+		clientFlags.Common.PostParse()
19
+
20
+		if clientFlags.ConfigDir != "" {
21
+			cliconfig.SetConfigDir(clientFlags.ConfigDir)
22
+		}
23
+
24
+		if clientFlags.Common.TrustKey == "" {
25
+			clientFlags.Common.TrustKey = filepath.Join(cliconfig.ConfigDir(), defaultTrustKeyFile)
26
+		}
27
+	}
11 28
 }
12 29
new file mode 100644
... ...
@@ -0,0 +1,101 @@
0
+package main
1
+
2
+import (
3
+	"fmt"
4
+	"os"
5
+	"path/filepath"
6
+
7
+	"github.com/Sirupsen/logrus"
8
+	"github.com/docker/docker/cli"
9
+	"github.com/docker/docker/cliconfig"
10
+	"github.com/docker/docker/opts"
11
+	flag "github.com/docker/docker/pkg/mflag"
12
+	"github.com/docker/docker/pkg/tlsconfig"
13
+)
14
+
15
+const (
16
+	defaultTrustKeyFile = "key.json"
17
+	defaultCaFile       = "ca.pem"
18
+	defaultKeyFile      = "key.pem"
19
+	defaultCertFile     = "cert.pem"
20
+)
21
+
22
+var (
23
+	daemonFlags *flag.FlagSet
24
+	commonFlags = &cli.CommonFlags{FlagSet: new(flag.FlagSet)}
25
+
26
+	dockerCertPath  = os.Getenv("DOCKER_CERT_PATH")
27
+	dockerTLSVerify = os.Getenv("DOCKER_TLS_VERIFY") != ""
28
+)
29
+
30
+func init() {
31
+	if dockerCertPath == "" {
32
+		dockerCertPath = cliconfig.ConfigDir()
33
+	}
34
+
35
+	commonFlags.PostParse = postParseCommon
36
+
37
+	cmd := commonFlags.FlagSet
38
+
39
+	cmd.BoolVar(&commonFlags.Debug, []string{"D", "-debug"}, false, "Enable debug mode")
40
+	cmd.StringVar(&commonFlags.LogLevel, []string{"l", "-log-level"}, "info", "Set the logging level")
41
+	cmd.BoolVar(&commonFlags.TLS, []string{"-tls"}, false, "Use TLS; implied by --tlsverify")
42
+	cmd.BoolVar(&commonFlags.TLSVerify, []string{"-tlsverify"}, dockerTLSVerify, "Use TLS and verify the remote")
43
+
44
+	// TODO use flag flag.String([]string{"i", "-identity"}, "", "Path to libtrust key file")
45
+
46
+	var tlsOptions tlsconfig.Options
47
+	commonFlags.TLSOptions = &tlsOptions
48
+	cmd.StringVar(&tlsOptions.CAFile, []string{"-tlscacert"}, filepath.Join(dockerCertPath, defaultCaFile), "Trust certs signed only by this CA")
49
+	cmd.StringVar(&tlsOptions.CertFile, []string{"-tlscert"}, filepath.Join(dockerCertPath, defaultCertFile), "Path to TLS certificate file")
50
+	cmd.StringVar(&tlsOptions.KeyFile, []string{"-tlskey"}, filepath.Join(dockerCertPath, defaultKeyFile), "Path to TLS key file")
51
+
52
+	cmd.Var(opts.NewListOptsRef(&commonFlags.Hosts, opts.ValidateHost), []string{"H", "-host"}, "Daemon socket(s) to connect to")
53
+}
54
+
55
+func postParseCommon() {
56
+	cmd := commonFlags.FlagSet
57
+
58
+	if commonFlags.LogLevel != "" {
59
+		lvl, err := logrus.ParseLevel(commonFlags.LogLevel)
60
+		if err != nil {
61
+			fmt.Fprintf(os.Stderr, "Unable to parse logging level: %s\n", commonFlags.LogLevel)
62
+			os.Exit(1)
63
+		}
64
+		logrus.SetLevel(lvl)
65
+	} else {
66
+		logrus.SetLevel(logrus.InfoLevel)
67
+	}
68
+
69
+	if commonFlags.Debug {
70
+		os.Setenv("DEBUG", "1")
71
+		logrus.SetLevel(logrus.DebugLevel)
72
+	}
73
+
74
+	// Regardless of whether the user sets it to true or false, if they
75
+	// specify --tlsverify at all then we need to turn on tls
76
+	// TLSVerify can be true even if not set due to DOCKER_TLS_VERIFY env var, so we need to check that here as well
77
+	if cmd.IsSet("-tlsverify") || commonFlags.TLSVerify {
78
+		commonFlags.TLS = true
79
+	}
80
+
81
+	if !commonFlags.TLS {
82
+		commonFlags.TLSOptions = nil
83
+	} else {
84
+		tlsOptions := commonFlags.TLSOptions
85
+		tlsOptions.InsecureSkipVerify = !commonFlags.TLSVerify
86
+
87
+		// Reset CertFile and KeyFile to empty string if the user did not specify
88
+		// the respective flags and the respective default files were not found.
89
+		if !cmd.IsSet("-tlscert") {
90
+			if _, err := os.Stat(tlsOptions.CertFile); os.IsNotExist(err) {
91
+				tlsOptions.CertFile = ""
92
+			}
93
+		}
94
+		if !cmd.IsSet("-tlskey") {
95
+			if _, err := os.Stat(tlsOptions.KeyFile); os.IsNotExist(err) {
96
+				tlsOptions.KeyFile = ""
97
+			}
98
+		}
99
+	}
100
+}
... ...
@@ -8,14 +8,18 @@ import (
8 8
 	"io"
9 9
 	"os"
10 10
 	"path/filepath"
11
+	"runtime"
12
+	"strings"
11 13
 	"time"
12 14
 
13 15
 	"github.com/Sirupsen/logrus"
14 16
 	apiserver "github.com/docker/docker/api/server"
15 17
 	"github.com/docker/docker/autogen/dockerversion"
18
+	"github.com/docker/docker/cli"
16 19
 	"github.com/docker/docker/cliconfig"
17 20
 	"github.com/docker/docker/daemon"
18 21
 	"github.com/docker/docker/daemon/logger"
22
+	"github.com/docker/docker/opts"
19 23
 	flag "github.com/docker/docker/pkg/mflag"
20 24
 	"github.com/docker/docker/pkg/pidfile"
21 25
 	"github.com/docker/docker/pkg/signal"
... ...
@@ -26,17 +30,63 @@ import (
26 26
 	"github.com/docker/docker/utils"
27 27
 )
28 28
 
29
+const daemonUsage = "       docker daemon [ --help | ... ]\n"
30
+
29 31
 var (
30
-	daemonCfg   = &daemon.Config{}
31
-	registryCfg = &registry.Options{}
32
+	flDaemon              = flag.Bool([]string{"#d", "#-daemon"}, false, "Enable daemon mode (deprecated; use docker daemon)")
33
+	daemonCli cli.Handler = NewDaemonCli()
32 34
 )
33 35
 
34
-func init() {
35
-	if daemonCfg.LogConfig.Config == nil {
36
-		daemonCfg.LogConfig.Config = make(map[string]string)
36
+// TODO: remove once `-d` is retired
37
+func handleGlobalDaemonFlag() {
38
+	// This block makes sure that if the deprecated daemon flag `--daemon` is absent,
39
+	// then all daemon-specific flags are absent as well.
40
+	if !*flDaemon && daemonFlags != nil {
41
+		flag.CommandLine.Visit(func(fl *flag.Flag) {
42
+			for _, name := range fl.Names {
43
+				name := strings.TrimPrefix(name, "#")
44
+				if daemonFlags.Lookup(name) != nil {
45
+					// daemon flag was NOT specified, but daemon-specific flags were
46
+					// so let's error out
47
+					fmt.Fprintf(os.Stderr, "docker: the daemon flag '-%s' must follow the 'docker daemon' command.\n", name)
48
+					os.Exit(1)
49
+				}
50
+			}
51
+		})
52
+	}
53
+
54
+	if *flDaemon {
55
+		if *flHelp {
56
+			// We do not show the help output here, instead, we tell the user about the new daemon command,
57
+			// because the help output is so long they would not see the warning anyway.
58
+			fmt.Fprintln(os.Stderr, "Please use 'docker daemon --help' instead.")
59
+			os.Exit(0)
60
+		}
61
+		daemonCli.(*DaemonCli).CmdDaemon(flag.Args()...)
62
+		os.Exit(0)
63
+	}
64
+}
65
+
66
+func presentInHelp(usage string) string { return usage }
67
+func absentFromHelp(string) string      { return "" }
68
+
69
+// NewDaemonCli returns a pre-configured daemon CLI
70
+func NewDaemonCli() *DaemonCli {
71
+	daemonFlags = cli.Subcmd("daemon", nil, "Enable daemon mode", true)
72
+
73
+	// TODO(tiborvass): remove InstallFlags?
74
+	daemonConfig := new(daemon.Config)
75
+	daemonConfig.InstallFlags(daemonFlags, presentInHelp)
76
+	daemonConfig.InstallFlags(flag.CommandLine, absentFromHelp)
77
+	registryOptions := new(registry.Options)
78
+	registryOptions.InstallFlags(daemonFlags, presentInHelp)
79
+	registryOptions.InstallFlags(flag.CommandLine, absentFromHelp)
80
+	daemonFlags.Require(flag.Exact, 0)
81
+
82
+	return &DaemonCli{
83
+		Config:          daemonConfig,
84
+		registryOptions: registryOptions,
37 85
 	}
38
-	daemonCfg.InstallFlags()
39
-	registryCfg.InstallFlags()
40 86
 }
41 87
 
42 88
 func migrateKey() (err error) {
... ...
@@ -79,14 +129,56 @@ func migrateKey() (err error) {
79 79
 	return nil
80 80
 }
81 81
 
82
-func mainDaemon() {
83
-	if utils.ExperimentalBuild() {
84
-		logrus.Warn("Running experimental build")
82
+// DaemonCli represents the daemon CLI.
83
+type DaemonCli struct {
84
+	*daemon.Config
85
+	registryOptions *registry.Options
86
+}
87
+
88
+func getGlobalFlag() (globalFlag *flag.Flag) {
89
+	defer func() {
90
+		if x := recover(); x != nil {
91
+			switch f := x.(type) {
92
+			case *flag.Flag:
93
+				globalFlag = f
94
+			default:
95
+				panic(x)
96
+			}
97
+		}
98
+	}()
99
+	visitor := func(f *flag.Flag) { panic(f) }
100
+	commonFlags.FlagSet.Visit(visitor)
101
+	clientFlags.FlagSet.Visit(visitor)
102
+	return
103
+}
104
+
105
+// CmdDaemon is the daemon command, called the raw arguments after `docker daemon`.
106
+func (cli *DaemonCli) CmdDaemon(args ...string) error {
107
+	if *flDaemon {
108
+		// allow legacy forms `docker -D -d` and `docker -d -D`
109
+		logrus.Warn("please use 'docker daemon' instead.")
110
+	} else if !commonFlags.FlagSet.IsEmpty() || !clientFlags.FlagSet.IsEmpty() {
111
+		// deny `docker -D daemon`
112
+		illegalFlag := getGlobalFlag()
113
+		fmt.Fprintf(os.Stderr, "invalid flag '-%s'.\nSee 'docker daemon --help'.\n", illegalFlag.Names[0])
114
+		os.Exit(1)
115
+	} else {
116
+		// allow new form `docker daemon -D`
117
+		flag.Merge(daemonFlags, commonFlags.FlagSet)
118
+	}
119
+
120
+	daemonFlags.ParseFlags(args, true)
121
+	commonFlags.PostParse()
122
+
123
+	if len(commonFlags.Hosts) == 0 {
124
+		commonFlags.Hosts = []string{opts.DefaultHost}
125
+	}
126
+	if commonFlags.TrustKey == "" {
127
+		commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), defaultTrustKeyFile)
85 128
 	}
86 129
 
87
-	if flag.NArg() != 0 {
88
-		flag.Usage()
89
-		return
130
+	if utils.ExperimentalBuild() {
131
+		logrus.Warn("Running experimental build")
90 132
 	}
91 133
 
92 134
 	logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: timeutils.RFC3339NanoFixed})
... ...
@@ -95,15 +187,15 @@ func mainDaemon() {
95 95
 		logrus.Fatalf("Failed to set umask: %v", err)
96 96
 	}
97 97
 
98
-	if len(daemonCfg.LogConfig.Config) > 0 {
99
-		if err := logger.ValidateLogOpts(daemonCfg.LogConfig.Type, daemonCfg.LogConfig.Config); err != nil {
98
+	if len(cli.LogConfig.Config) > 0 {
99
+		if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil {
100 100
 			logrus.Fatalf("Failed to set log opts: %v", err)
101 101
 		}
102 102
 	}
103 103
 
104 104
 	var pfile *pidfile.PidFile
105
-	if daemonCfg.Pidfile != "" {
106
-		pf, err := pidfile.New(daemonCfg.Pidfile)
105
+	if cli.Pidfile != "" {
106
+		pf, err := pidfile.New(cli.Pidfile)
107 107
 		if err != nil {
108 108
 			logrus.Fatalf("Error starting daemon: %v", err)
109 109
 		}
... ...
@@ -115,21 +207,26 @@ func mainDaemon() {
115 115
 		}()
116 116
 	}
117 117
 
118
+	if cli.LogConfig.Config == nil {
119
+		cli.LogConfig.Config = make(map[string]string)
120
+	}
121
+
118 122
 	serverConfig := &apiserver.ServerConfig{
119 123
 		Logging:     true,
120
-		EnableCors:  daemonCfg.EnableCors,
121
-		CorsHeaders: daemonCfg.CorsHeaders,
124
+		EnableCors:  cli.EnableCors,
125
+		CorsHeaders: cli.CorsHeaders,
122 126
 		Version:     dockerversion.VERSION,
123 127
 	}
124
-	serverConfig = setPlatformServerConfig(serverConfig, daemonCfg)
128
+	serverConfig = setPlatformServerConfig(serverConfig, cli.Config)
125 129
 
126
-	if *flTLS {
127
-		if *flTLSVerify {
128
-			tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert
130
+	if commonFlags.TLSOptions != nil {
131
+		if !commonFlags.TLSOptions.InsecureSkipVerify {
132
+			// server requires and verifies client's certificate
133
+			commonFlags.TLSOptions.ClientAuth = tls.RequireAndVerifyClientCert
129 134
 		}
130
-		tlsConfig, err := tlsconfig.Server(tlsOptions)
135
+		tlsConfig, err := tlsconfig.Server(*commonFlags.TLSOptions)
131 136
 		if err != nil {
132
-			logrus.Fatal(err)
137
+			logrus.Fatalf("foobar: %v", err)
133 138
 		}
134 139
 		serverConfig.TLSConfig = tlsConfig
135 140
 	}
... ...
@@ -141,7 +238,7 @@ func mainDaemon() {
141 141
 	// daemon doesn't exit
142 142
 	serveAPIWait := make(chan error)
143 143
 	go func() {
144
-		if err := api.ServeApi(flHosts); err != nil {
144
+		if err := api.ServeApi(commonFlags.Hosts); err != nil {
145 145
 			logrus.Errorf("ServeAPI error: %v", err)
146 146
 			serveAPIWait <- err
147 147
 			return
... ...
@@ -152,10 +249,10 @@ func mainDaemon() {
152 152
 	if err := migrateKey(); err != nil {
153 153
 		logrus.Fatal(err)
154 154
 	}
155
-	daemonCfg.TrustKeyPath = *flTrustKey
155
+	cli.TrustKeyPath = commonFlags.TrustKey
156 156
 
157
-	registryService := registry.NewService(registryCfg)
158
-	d, err := daemon.NewDaemon(daemonCfg, registryService)
157
+	registryService := registry.NewService(cli.registryOptions)
158
+	d, err := daemon.NewDaemon(cli.Config, registryService)
159 159
 	if err != nil {
160 160
 		if pfile != nil {
161 161
 			if err := pfile.Remove(); err != nil {
... ...
@@ -201,6 +298,7 @@ func mainDaemon() {
201 201
 		}
202 202
 		logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI)
203 203
 	}
204
+	return nil
204 205
 }
205 206
 
206 207
 // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case
... ...
@@ -219,3 +317,11 @@ func shutdownDaemon(d *daemon.Daemon, timeout time.Duration) {
219 219
 		logrus.Error("Force shutdown daemon")
220 220
 	}
221 221
 }
222
+
223
+func getDaemonConfDir() string {
224
+	// TODO: update for Windows daemon
225
+	if runtime.GOOS == "windows" {
226
+		return cliconfig.ConfigDir()
227
+	}
228
+	return "/etc/docker"
229
+}
222 230
new file mode 100644
... ...
@@ -0,0 +1,12 @@
0
+// +build !daemon
1
+
2
+package main
3
+
4
+import "github.com/docker/docker/cli"
5
+
6
+const daemonUsage = ""
7
+
8
+var daemonCli cli.Handler
9
+
10
+// TODO: remove once `-d` is retired
11
+func handleGlobalDaemonFlag() {}
... ...
@@ -1,31 +1,20 @@
1 1
 package main
2 2
 
3 3
 import (
4
-	"crypto/tls"
5 4
 	"fmt"
6 5
 	"os"
7
-	"runtime"
8
-	"strings"
6
+	"sort"
9 7
 
10 8
 	"github.com/Sirupsen/logrus"
11 9
 	"github.com/docker/docker/api/client"
12 10
 	"github.com/docker/docker/autogen/dockerversion"
13
-	"github.com/docker/docker/cliconfig"
14
-	"github.com/docker/docker/opts"
11
+	"github.com/docker/docker/cli"
15 12
 	flag "github.com/docker/docker/pkg/mflag"
16 13
 	"github.com/docker/docker/pkg/reexec"
17 14
 	"github.com/docker/docker/pkg/term"
18
-	"github.com/docker/docker/pkg/tlsconfig"
19 15
 	"github.com/docker/docker/utils"
20 16
 )
21 17
 
22
-const (
23
-	defaultTrustKeyFile = "key.json"
24
-	defaultCaFile       = "ca.pem"
25
-	defaultKeyFile      = "key.pem"
26
-	defaultCertFile     = "cert.pem"
27
-)
28
-
29 18
 func main() {
30 19
 	if reexec.Init() {
31 20
 		return
... ...
@@ -34,116 +23,58 @@ func main() {
34 34
 	// Set terminal emulation based on platform as required.
35 35
 	stdin, stdout, stderr := term.StdStreams()
36 36
 
37
-	initLogging(stderr)
37
+	logrus.SetOutput(stderr)
38 38
 
39
-	flag.Parse()
40
-	// FIXME: validate daemon flags here
39
+	flag.Merge(flag.CommandLine, clientFlags.FlagSet, commonFlags.FlagSet)
41 40
 
42
-	if *flVersion {
43
-		showVersion()
44
-		return
45
-	}
41
+	flag.Usage = func() {
42
+		fmt.Fprint(os.Stdout, "Usage: docker [OPTIONS] COMMAND [arg...]\n"+daemonUsage+"       docker [ -h | --help | -v | --version ]\n\n")
43
+		fmt.Fprint(os.Stdout, "A self-sufficient runtime for containers.\n\nOptions:\n")
46 44
 
47
-	if *flConfigDir != "" {
48
-		cliconfig.SetConfigDir(*flConfigDir)
49
-	}
45
+		flag.CommandLine.SetOutput(os.Stdout)
46
+		flag.PrintDefaults()
50 47
 
51
-	if *flLogLevel != "" {
52
-		lvl, err := logrus.ParseLevel(*flLogLevel)
53
-		if err != nil {
54
-			fmt.Fprintf(os.Stderr, "Unable to parse logging level: %s\n", *flLogLevel)
55
-			os.Exit(1)
56
-		}
57
-		setLogLevel(lvl)
58
-	} else {
59
-		setLogLevel(logrus.InfoLevel)
60
-	}
48
+		help := "\nCommands:\n"
61 49
 
62
-	if *flDebug {
63
-		os.Setenv("DEBUG", "1")
64
-		setLogLevel(logrus.DebugLevel)
65
-	}
50
+		// TODO(tiborvass): no need to sort if we ensure dockerCommands is sorted
51
+		sort.Sort(byName(dockerCommands))
66 52
 
67
-	if len(flHosts) == 0 {
68
-		defaultHost := os.Getenv("DOCKER_HOST")
69
-		if defaultHost == "" || *flDaemon {
70
-			if runtime.GOOS != "windows" {
71
-				// If we do not have a host, default to unix socket
72
-				defaultHost = fmt.Sprintf("unix://%s", opts.DefaultUnixSocket)
73
-			} else {
74
-				// If we do not have a host, default to TCP socket on Windows
75
-				defaultHost = fmt.Sprintf("tcp://%s:%d", opts.DefaultHTTPHost, opts.DefaultHTTPPort)
76
-			}
77
-		}
78
-		defaultHost, err := opts.ValidateHost(defaultHost)
79
-		if err != nil {
80
-			if *flDaemon {
81
-				logrus.Fatal(err)
82
-			} else {
83
-				fmt.Fprint(os.Stderr, err)
84
-			}
85
-			os.Exit(1)
53
+		for _, cmd := range dockerCommands {
54
+			help += fmt.Sprintf("    %-10.10s%s\n", cmd.name, cmd.description)
86 55
 		}
87
-		flHosts = append(flHosts, defaultHost)
88
-	}
89
-
90
-	setDefaultConfFlag(flTrustKey, defaultTrustKeyFile)
91 56
 
92
-	// Regardless of whether the user sets it to true or false, if they
93
-	// specify --tlsverify at all then we need to turn on tls
94
-	// *flTlsVerify can be true even if not set due to DOCKER_TLS_VERIFY env var, so we need to check that here as well
95
-	if flag.IsSet("-tlsverify") || *flTLSVerify {
96
-		*flTLS = true
57
+		help += "\nRun 'docker COMMAND --help' for more information on a command."
58
+		fmt.Fprintf(os.Stdout, "%s\n", help)
97 59
 	}
98 60
 
99
-	if *flDaemon {
100
-		if *flHelp {
101
-			flag.Usage()
102
-			return
103
-		}
104
-		mainDaemon()
61
+	flag.Parse()
62
+
63
+	if *flVersion {
64
+		showVersion()
105 65
 		return
106 66
 	}
107 67
 
108
-	// From here on, we assume we're a client, not a server.
68
+	clientCli := client.NewDockerCli(stdin, stdout, stderr, clientFlags)
69
+	// TODO: remove once `-d` is retired
70
+	handleGlobalDaemonFlag()
109 71
 
110
-	if len(flHosts) > 1 {
111
-		fmt.Fprintf(os.Stderr, "Please specify only one -H")
112
-		os.Exit(0)
113
-	}
114
-	protoAddrParts := strings.SplitN(flHosts[0], "://", 2)
115
-
116
-	var tlsConfig *tls.Config
117
-	if *flTLS {
118
-		tlsOptions.InsecureSkipVerify = !*flTLSVerify
119
-		if !flag.IsSet("-tlscert") {
120
-			if _, err := os.Stat(tlsOptions.CertFile); os.IsNotExist(err) {
121
-				tlsOptions.CertFile = ""
122
-			}
123
-		}
124
-		if !flag.IsSet("-tlskey") {
125
-			if _, err := os.Stat(tlsOptions.KeyFile); os.IsNotExist(err) {
126
-				tlsOptions.KeyFile = ""
127
-			}
128
-		}
129
-		var err error
130
-		tlsConfig, err = tlsconfig.Client(tlsOptions)
131
-		if err != nil {
132
-			fmt.Fprintln(stderr, err)
133
-			os.Exit(1)
134
-		}
72
+	if *flHelp {
73
+		// if global flag --help is present, regardless of what other options and commands there are,
74
+		// just print the usage.
75
+		flag.Usage()
76
+		return
135 77
 	}
136
-	cli := client.NewDockerCli(stdin, stdout, stderr, *flTrustKey, protoAddrParts[0], protoAddrParts[1], tlsConfig)
137 78
 
138
-	if err := cli.Cmd(flag.Args()...); err != nil {
139
-		if sterr, ok := err.(client.StatusError); ok {
79
+	c := cli.New(clientCli, daemonCli)
80
+	if err := c.Run(flag.Args()...); err != nil {
81
+		if sterr, ok := err.(cli.StatusError); ok {
140 82
 			if sterr.Status != "" {
141
-				fmt.Fprintln(cli.Err(), sterr.Status)
83
+				fmt.Fprintln(os.Stderr, sterr.Status)
142 84
 				os.Exit(1)
143 85
 			}
144 86
 			os.Exit(sterr.StatusCode)
145 87
 		}
146
-		fmt.Fprintln(cli.Err(), err)
88
+		fmt.Fprintln(os.Stderr, err)
147 89
 		os.Exit(1)
148 90
 	}
149 91
 }
... ...
@@ -1,16 +1,10 @@
1 1
 package main
2 2
 
3
-import (
4
-	"fmt"
5
-	"os"
6
-	"path/filepath"
7
-	"runtime"
8
-	"sort"
3
+import flag "github.com/docker/docker/pkg/mflag"
9 4
 
10
-	"github.com/docker/docker/cliconfig"
11
-	"github.com/docker/docker/opts"
12
-	flag "github.com/docker/docker/pkg/mflag"
13
-	"github.com/docker/docker/pkg/tlsconfig"
5
+var (
6
+	flHelp    = flag.Bool([]string{"h", "-help"}, false, "Print usage")
7
+	flVersion = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit")
14 8
 )
15 9
 
16 10
 type command struct {
... ...
@@ -24,118 +18,46 @@ func (a byName) Len() int           { return len(a) }
24 24
 func (a byName) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
25 25
 func (a byName) Less(i, j int) bool { return a[i].name < a[j].name }
26 26
 
27
-var (
28
-	dockerCertPath  = os.Getenv("DOCKER_CERT_PATH")
29
-	dockerTlSVerify = os.Getenv("DOCKER_TLS_VERIFY") != ""
30
-
31
-	dockerCommands = []command{
32
-		{"attach", "Attach to a running container"},
33
-		{"build", "Build an image from a Dockerfile"},
34
-		{"commit", "Create a new image from a container's changes"},
35
-		{"cp", "Copy files/folders from a container to a HOSTDIR or to STDOUT"},
36
-		{"create", "Create a new container"},
37
-		{"diff", "Inspect changes on a container's filesystem"},
38
-		{"events", "Get real time events from the server"},
39
-		{"exec", "Run a command in a running container"},
40
-		{"export", "Export a container's filesystem as a tar archive"},
41
-		{"history", "Show the history of an image"},
42
-		{"images", "List images"},
43
-		{"import", "Import the contents from a tarball to create a filesystem image"},
44
-		{"info", "Display system-wide information"},
45
-		{"inspect", "Return low-level information on a container or image"},
46
-		{"kill", "Kill a running container"},
47
-		{"load", "Load an image from a tar archive or STDIN"},
48
-		{"login", "Register or log in to a Docker registry"},
49
-		{"logout", "Log out from a Docker registry"},
50
-		{"logs", "Fetch the logs of a container"},
51
-		{"port", "List port mappings or a specific mapping for the CONTAINER"},
52
-		{"pause", "Pause all processes within a container"},
53
-		{"ps", "List containers"},
54
-		{"pull", "Pull an image or a repository from a registry"},
55
-		{"push", "Push an image or a repository to a registry"},
56
-		{"rename", "Rename a container"},
57
-		{"restart", "Restart a running container"},
58
-		{"rm", "Remove one or more containers"},
59
-		{"rmi", "Remove one or more images"},
60
-		{"run", "Run a command in a new container"},
61
-		{"save", "Save an image(s) to a tar archive"},
62
-		{"search", "Search the Docker Hub for images"},
63
-		{"start", "Start one or more stopped containers"},
64
-		{"stats", "Display a live stream of container(s) resource usage statistics"},
65
-		{"stop", "Stop a running container"},
66
-		{"tag", "Tag an image into a repository"},
67
-		{"top", "Display the running processes of a container"},
68
-		{"unpause", "Unpause all processes within a container"},
69
-		{"version", "Show the Docker version information"},
70
-		{"wait", "Block until a container stops, then print its exit code"},
71
-	}
72
-)
73
-
74
-func init() {
75
-	if dockerCertPath == "" {
76
-		dockerCertPath = cliconfig.ConfigDir()
77
-	}
78
-}
79
-
80
-func getDaemonConfDir() string {
81
-	// TODO: update for Windows daemon
82
-	if runtime.GOOS == "windows" {
83
-		return cliconfig.ConfigDir()
84
-	}
85
-	return "/etc/docker"
86
-}
87
-
88
-var (
89
-	flConfigDir = flag.String([]string{"-config"}, cliconfig.ConfigDir(), "Location of client config files")
90
-	flVersion   = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit")
91
-	flDaemon    = flag.Bool([]string{"d", "-daemon"}, false, "Enable daemon mode")
92
-	flDebug     = flag.Bool([]string{"D", "-debug"}, false, "Enable debug mode")
93
-	flLogLevel  = flag.String([]string{"l", "-log-level"}, "info", "Set the logging level")
94
-	flTLS       = flag.Bool([]string{"-tls"}, false, "Use TLS; implied by --tlsverify")
95
-	flHelp      = flag.Bool([]string{"h", "-help"}, false, "Print usage")
96
-	flTLSVerify = flag.Bool([]string{"-tlsverify"}, dockerTlSVerify, "Use TLS and verify the remote")
97
-
98
-	// these are initialized in init() below since their default values depend on dockerCertPath which isn't fully initialized until init() runs
99
-	tlsOptions tlsconfig.Options
100
-	flTrustKey *string
101
-	flHosts    []string
102
-)
103
-
104
-func setDefaultConfFlag(flag *string, def string) {
105
-	if *flag == "" {
106
-		if *flDaemon {
107
-			*flag = filepath.Join(getDaemonConfDir(), def)
108
-		} else {
109
-			*flag = filepath.Join(cliconfig.ConfigDir(), def)
110
-		}
111
-	}
112
-}
113
-
114
-func init() {
115
-	var placeholderTrustKey string
116
-	// TODO use flag flag.String([]string{"i", "-identity"}, "", "Path to libtrust key file")
117
-	flTrustKey = &placeholderTrustKey
118
-
119
-	flag.StringVar(&tlsOptions.CAFile, []string{"-tlscacert"}, filepath.Join(dockerCertPath, defaultCaFile), "Trust certs signed only by this CA")
120
-	flag.StringVar(&tlsOptions.CertFile, []string{"-tlscert"}, filepath.Join(dockerCertPath, defaultCertFile), "Path to TLS certificate file")
121
-	flag.StringVar(&tlsOptions.KeyFile, []string{"-tlskey"}, filepath.Join(dockerCertPath, defaultKeyFile), "Path to TLS key file")
122
-	opts.HostListVar(&flHosts, []string{"H", "-host"}, "Daemon socket(s) to connect to")
123
-
124
-	flag.Usage = func() {
125
-		fmt.Fprint(os.Stdout, "Usage: docker [OPTIONS] COMMAND [arg...]\n\nA self-sufficient runtime for containers.\n\nOptions:\n")
126
-
127
-		flag.CommandLine.SetOutput(os.Stdout)
128
-		flag.PrintDefaults()
129
-
130
-		help := "\nCommands:\n"
131
-
132
-		sort.Sort(byName(dockerCommands))
133
-
134
-		for _, cmd := range dockerCommands {
135
-			help += fmt.Sprintf("    %-10.10s%s\n", cmd.name, cmd.description)
136
-		}
137
-
138
-		help += "\nRun 'docker COMMAND --help' for more information on a command."
139
-		fmt.Fprintf(os.Stdout, "%s\n", help)
140
-	}
27
+// TODO(tiborvass): do not show 'daemon' on client-only binaries
28
+// and deduplicate description in dockerCommands and cli subcommands
29
+var dockerCommands = []command{
30
+	{"attach", "Attach to a running container"},
31
+	{"build", "Build an image from a Dockerfile"},
32
+	{"commit", "Create a new image from a container's changes"},
33
+	{"cp", "Copy files/folders from a container to a HOSTDIR or to STDOUT"},
34
+	{"create", "Create a new container"},
35
+	{"diff", "Inspect changes on a container's filesystem"},
36
+	{"events", "Get real time events from the server"},
37
+	{"exec", "Run a command in a running container"},
38
+	{"export", "Export a container's filesystem as a tar archive"},
39
+	{"history", "Show the history of an image"},
40
+	{"images", "List images"},
41
+	{"import", "Import the contents from a tarball to create a filesystem image"},
42
+	{"info", "Display system-wide information"},
43
+	{"inspect", "Return low-level information on a container or image"},
44
+	{"kill", "Kill a running container"},
45
+	{"load", "Load an image from a tar archive or STDIN"},
46
+	{"login", "Register or log in to a Docker registry"},
47
+	{"logout", "Log out from a Docker registry"},
48
+	{"logs", "Fetch the logs of a container"},
49
+	{"port", "List port mappings or a specific mapping for the CONTAINER"},
50
+	{"pause", "Pause all processes within a container"},
51
+	{"ps", "List containers"},
52
+	{"pull", "Pull an image or a repository from a registry"},
53
+	{"push", "Push an image or a repository to a registry"},
54
+	{"rename", "Rename a container"},
55
+	{"restart", "Restart a running container"},
56
+	{"rm", "Remove one or more containers"},
57
+	{"rmi", "Remove one or more images"},
58
+	{"run", "Run a command in a new container"},
59
+	{"save", "Save an image(s) to a tar archive"},
60
+	{"search", "Search the Docker Hub for images"},
61
+	{"start", "Start one or more stopped containers"},
62
+	{"stats", "Display a live stream of container(s) resource usage statistics"},
63
+	{"stop", "Stop a running container"},
64
+	{"tag", "Tag an image into a repository"},
65
+	{"top", "Display the running processes of a container"},
66
+	{"unpause", "Unpause all processes within a container"},
67
+	{"version", "Show the Docker version information"},
68
+	{"wait", "Block until a container stops, then print its exit code"},
141 69
 }
142 70
deleted file mode 100644
... ...
@@ -1,14 +0,0 @@
1
-package main
2
-
3
-import (
4
-	"github.com/Sirupsen/logrus"
5
-	"io"
6
-)
7
-
8
-func setLogLevel(lvl logrus.Level) {
9
-	logrus.SetLevel(lvl)
10
-}
11
-
12
-func initLogging(stderr io.Writer) {
13
-	logrus.SetOutput(stderr)
14
-}
... ...
@@ -43,7 +43,8 @@ func (s *DockerDaemonSuite) TestCliProxyProxyTCPSock(c *check.C) {
43 43
 		c.Fatal("could not find ip to connect to")
44 44
 	}
45 45
 
46
-	if err := s.d.Start("-H", "tcp://"+ip+":2375"); err != nil {
46
+	s.d.GlobalFlags = []string{"-H", "tcp://" + ip + ":2375"}
47
+	if err := s.d.Start(); err != nil {
47 48
 		c.Fatal(err)
48 49
 	}
49 50
 
50 51
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+// +build !windows
1
+
2
+package opts
3
+
4
+import "fmt"
5
+
6
+var DefaultHost = fmt.Sprintf("unix://%s", DefaultUnixSocket)
0 7
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+// +build windows
1
+
2
+package opts
3
+
4
+import "fmt"
5
+
6
+var DefaultHost = fmt.Sprintf("tcp://%s:%d", DefaultHTTPHost, DefaultHTTPPort)
... ...
@@ -32,37 +32,37 @@ var (
32 32
 // ListVar Defines a flag with the specified names and usage, and put the value
33 33
 // list into ListOpts that will hold the values.
34 34
 func ListVar(values *[]string, names []string, usage string) {
35
-	flag.Var(newListOptsRef(values, nil), names, usage)
35
+	flag.Var(NewListOptsRef(values, nil), names, usage)
36 36
 }
37 37
 
38 38
 // MapVar Defines a flag with the specified names and usage, and put the value
39 39
 // map into MapOpt that will hold the values (key,value).
40 40
 func MapVar(values map[string]string, names []string, usage string) {
41
-	flag.Var(newMapOpt(values, nil), names, usage)
41
+	flag.Var(NewMapOpts(values, nil), names, usage)
42 42
 }
43 43
 
44 44
 // LogOptsVar Defines a flag with the specified names and usage for --log-opts,
45 45
 // and put the value map into MapOpt that will hold the values (key,value).
46 46
 func LogOptsVar(values map[string]string, names []string, usage string) {
47
-	flag.Var(newMapOpt(values, nil), names, usage)
47
+	flag.Var(NewMapOpts(values, nil), names, usage)
48 48
 }
49 49
 
50 50
 // HostListVar Defines a flag with the specified names and usage and put the
51 51
 // value into a ListOpts that will hold the values, validating the Host format.
52 52
 func HostListVar(values *[]string, names []string, usage string) {
53
-	flag.Var(newListOptsRef(values, ValidateHost), names, usage)
53
+	flag.Var(NewListOptsRef(values, ValidateHost), names, usage)
54 54
 }
55 55
 
56 56
 // IPListVar Defines a flag with the specified names and usage and put the
57 57
 // value into a ListOpts that will hold the values, validating the IP format.
58 58
 func IPListVar(values *[]string, names []string, usage string) {
59
-	flag.Var(newListOptsRef(values, ValidateIPAddress), names, usage)
59
+	flag.Var(NewListOptsRef(values, ValidateIPAddress), names, usage)
60 60
 }
61 61
 
62 62
 // DNSSearchListVar Defines a flag with the specified names and usage and put the
63 63
 // value into a ListOpts that will hold the values, validating the DNS search format.
64 64
 func DNSSearchListVar(values *[]string, names []string, usage string) {
65
-	flag.Var(newListOptsRef(values, ValidateDNSSearch), names, usage)
65
+	flag.Var(NewListOptsRef(values, ValidateDNSSearch), names, usage)
66 66
 }
67 67
 
68 68
 // 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) {
74 74
 // LabelListVar Defines a flag with the specified names and usage and put the
75 75
 // value into a ListOpts that will hold the values, validating the label format.
76 76
 func LabelListVar(values *[]string, names []string, usage string) {
77
-	flag.Var(newListOptsRef(values, ValidateLabel), names, usage)
77
+	flag.Var(NewListOptsRef(values, ValidateLabel), names, usage)
78 78
 }
79 79
 
80 80
 // UlimitMapVar Defines a flag with the specified names and usage for --ulimit,
81 81
 // and put the value map into a UlimitOpt that will hold the values.
82
-func UlimitMapVar(values map[string]*ulimit.Ulimit, names []string, usage string) {
82
+func UlimitMapVar(values *map[string]*ulimit.Ulimit, names []string, usage string) {
83 83
 	flag.Var(NewUlimitOpt(values), names, usage)
84 84
 }
85 85
 
... ...
@@ -92,10 +92,10 @@ type ListOpts struct {
92 92
 // NewListOpts Create a new ListOpts with the specified validator.
93 93
 func NewListOpts(validator ValidatorFctType) ListOpts {
94 94
 	var values []string
95
-	return *newListOptsRef(&values, validator)
95
+	return *NewListOptsRef(&values, validator)
96 96
 }
97 97
 
98
-func newListOptsRef(values *[]string, validator ValidatorFctType) *ListOpts {
98
+func NewListOptsRef(values *[]string, validator ValidatorFctType) *ListOpts {
99 99
 	return &ListOpts{
100 100
 		values:    values,
101 101
 		validator: validator,
... ...
@@ -191,7 +191,10 @@ func (opts *MapOpts) String() string {
191 191
 	return fmt.Sprintf("%v", map[string]string((opts.values)))
192 192
 }
193 193
 
194
-func newMapOpt(values map[string]string, validator ValidatorFctType) *MapOpts {
194
+func NewMapOpts(values map[string]string, validator ValidatorFctType) *MapOpts {
195
+	if values == nil {
196
+		values = make(map[string]string)
197
+	}
195 198
 	return &MapOpts{
196 199
 		values:    values,
197 200
 		validator: validator,
... ...
@@ -32,7 +32,7 @@ func TestValidateIPAddress(t *testing.T) {
32 32
 
33 33
 func TestMapOpts(t *testing.T) {
34 34
 	tmpMap := make(map[string]string)
35
-	o := newMapOpt(tmpMap, logOptsValidator)
35
+	o := NewMapOpts(tmpMap, logOptsValidator)
36 36
 	o.Set("max-size=1")
37 37
 	if o.String() != "map[max-size:1]" {
38 38
 		t.Errorf("%s != [map[max-size:1]", o.String())
... ...
@@ -7,10 +7,13 @@ import (
7 7
 )
8 8
 
9 9
 type UlimitOpt struct {
10
-	values map[string]*ulimit.Ulimit
10
+	values *map[string]*ulimit.Ulimit
11 11
 }
12 12
 
13
-func NewUlimitOpt(ref map[string]*ulimit.Ulimit) *UlimitOpt {
13
+func NewUlimitOpt(ref *map[string]*ulimit.Ulimit) *UlimitOpt {
14
+	if ref == nil {
15
+		ref = &map[string]*ulimit.Ulimit{}
16
+	}
14 17
 	return &UlimitOpt{ref}
15 18
 }
16 19
 
... ...
@@ -20,14 +23,14 @@ func (o *UlimitOpt) Set(val string) error {
20 20
 		return err
21 21
 	}
22 22
 
23
-	o.values[l.Name] = l
23
+	(*o.values)[l.Name] = l
24 24
 
25 25
 	return nil
26 26
 }
27 27
 
28 28
 func (o *UlimitOpt) String() string {
29 29
 	var out []string
30
-	for _, v := range o.values {
30
+	for _, v := range *o.values {
31 31
 		out = append(out, v.String())
32 32
 	}
33 33
 
... ...
@@ -36,7 +39,7 @@ func (o *UlimitOpt) String() string {
36 36
 
37 37
 func (o *UlimitOpt) GetList() []*ulimit.Ulimit {
38 38
 	var ulimits []*ulimit.Ulimit
39
-	for _, v := range o.values {
39
+	for _, v := range *o.values {
40 40
 		ulimits = append(ulimits, v)
41 41
 	}
42 42
 
... ...
@@ -526,7 +526,7 @@ func (f *FlagSet) PrintDefaults() {
526 526
 				names = append(names, name)
527 527
 			}
528 528
 		}
529
-		if len(names) > 0 {
529
+		if len(names) > 0 && len(flag.Usage) > 0 {
530 530
 			val := flag.DefValue
531 531
 
532 532
 			if home != "" && strings.HasPrefix(val, home) {
... ...
@@ -1143,3 +1143,53 @@ func (f *FlagSet) Init(name string, errorHandling ErrorHandling) {
1143 1143
 	f.name = name
1144 1144
 	f.errorHandling = errorHandling
1145 1145
 }
1146
+
1147
+type mergeVal struct {
1148
+	Value
1149
+	key  string
1150
+	fset *FlagSet
1151
+}
1152
+
1153
+func (v mergeVal) Set(s string) error {
1154
+	return v.fset.Set(v.key, s)
1155
+}
1156
+
1157
+func (v mergeVal) IsBoolFlag() bool {
1158
+	if b, ok := v.Value.(boolFlag); ok {
1159
+		return b.IsBoolFlag()
1160
+	}
1161
+	return false
1162
+}
1163
+
1164
+func Merge(dest *FlagSet, flagsets ...*FlagSet) error {
1165
+	for _, fset := range flagsets {
1166
+		for k, f := range fset.formal {
1167
+			if _, ok := dest.formal[k]; ok {
1168
+				var err error
1169
+				if fset.name == "" {
1170
+					err = fmt.Errorf("flag redefined: %s", k)
1171
+				} else {
1172
+					err = fmt.Errorf("%s flag redefined: %s", fset.name, k)
1173
+				}
1174
+				fmt.Fprintln(fset.Out(), err.Error())
1175
+				// Happens only if flags are declared with identical names
1176
+				switch dest.errorHandling {
1177
+				case ContinueOnError:
1178
+					return err
1179
+				case ExitOnError:
1180
+					os.Exit(2)
1181
+				case PanicOnError:
1182
+					panic(err)
1183
+				}
1184
+			}
1185
+			newF := *f
1186
+			newF.Value = mergeVal{f.Value, k, fset}
1187
+			dest.formal[k] = &newF
1188
+		}
1189
+	}
1190
+	return nil
1191
+}
1192
+
1193
+func (f *FlagSet) IsEmpty() bool {
1194
+	return len(f.actual) == 0
1195
+}
... ...
@@ -17,11 +17,18 @@ import (
17 17
 
18 18
 // Options represents the information needed to create client and server TLS configurations.
19 19
 type Options struct {
20
+	CAFile string
21
+
22
+	// If either CertFile or KeyFile is empty, Client() will not load them
23
+	// preventing the client from authenticating to the server.
24
+	// However, Server() requires them and will error out if they are empty.
25
+	CertFile string
26
+	KeyFile  string
27
+
28
+	// client-only option
20 29
 	InsecureSkipVerify bool
21
-	ClientAuth         tls.ClientAuthType
22
-	CAFile             string
23
-	CertFile           string
24
-	KeyFile            string
30
+	// server-only option
31
+	ClientAuth tls.ClientAuthType
25 32
 }
26 33
 
27 34
 // Extra (server-side) accepted CBC cipher suites - will phase out in the future
... ...
@@ -43,11 +43,11 @@ var (
43 43
 
44 44
 // InstallFlags adds command-line options to the top-level flag parser for
45 45
 // the current process.
46
-func (options *Options) InstallFlags() {
46
+func (options *Options) InstallFlags(cmd *flag.FlagSet, usageFn func(string) string) {
47 47
 	options.Mirrors = opts.NewListOpts(ValidateMirror)
48
-	flag.Var(&options.Mirrors, []string{"-registry-mirror"}, "Preferred Docker registry mirror")
48
+	cmd.Var(&options.Mirrors, []string{"-registry-mirror"}, usageFn("Preferred Docker registry mirror"))
49 49
 	options.InsecureRegistries = opts.NewListOpts(ValidateIndexName)
50
-	flag.Var(&options.InsecureRegistries, []string{"-insecure-registry"}, "Enable insecure registry communication")
50
+	cmd.Var(&options.InsecureRegistries, []string{"-insecure-registry"}, usageFn("Enable insecure registry communication"))
51 51
 }
52 52
 
53 53
 type netIPNet net.IPNet
... ...
@@ -9,7 +9,6 @@ import (
9 9
 	flag "github.com/docker/docker/pkg/mflag"
10 10
 	"github.com/docker/docker/pkg/nat"
11 11
 	"github.com/docker/docker/pkg/parsers"
12
-	"github.com/docker/docker/pkg/ulimit"
13 12
 	"github.com/docker/docker/pkg/units"
14 13
 )
15 14
 
... ...
@@ -48,8 +47,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
48 48
 		flLabels  = opts.NewListOpts(opts.ValidateEnv)
49 49
 		flDevices = opts.NewListOpts(opts.ValidateDevice)
50 50
 
51
-		ulimits   = make(map[string]*ulimit.Ulimit)
52
-		flUlimits = opts.NewUlimitOpt(ulimits)
51
+		flUlimits = opts.NewUlimitOpt(nil)
53 52
 
54 53
 		flPublish     = opts.NewListOpts(nil)
55 54
 		flExpose      = opts.NewListOpts(nil)