From 96ce3a194aab2807fdd638825b9ea7cb9ba55c36 Mon Sep 17 00:00:00 2001 From: Tibor Vass Date: Tue, 5 May 2015 00:18:28 -0400 Subject: [PATCH 1/3] 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 --- api/client/attach.go | 6 +- api/client/build.go | 7 +- api/client/cli.go | 250 ++++++++--------------- api/client/client.go | 12 -- api/client/commit.go | 3 +- api/client/cp.go | 3 +- api/client/create.go | 3 +- api/client/diff.go | 3 +- api/client/events.go | 3 +- api/client/exec.go | 7 +- api/client/export.go | 3 +- api/client/help.go | 34 --- api/client/history.go | 3 +- api/client/images.go | 3 +- api/client/import.go | 3 +- api/client/info.go | 3 +- api/client/inspect.go | 14 +- api/client/kill.go | 3 +- api/client/load.go | 3 +- api/client/login.go | 3 +- api/client/logout.go | 3 +- api/client/logs.go | 3 +- api/client/pause.go | 3 +- api/client/port.go | 3 +- api/client/ps.go | 3 +- api/client/pull.go | 3 +- api/client/push.go | 3 +- api/client/rename.go | 3 +- api/client/restart.go | 3 +- api/client/rm.go | 3 +- api/client/rmi.go | 3 +- api/client/run.go | 5 +- api/client/save.go | 3 +- api/client/search.go | 3 +- api/client/start.go | 5 +- api/client/stats.go | 3 +- api/client/stop.go | 3 +- api/client/tag.go | 3 +- api/client/top.go | 3 +- api/client/unpause.go | 3 +- api/client/version.go | 7 +- api/client/wait.go | 3 +- cli/cli.go | 200 ++++++++++++++++++ cli/client.go | 12 ++ cli/common.go | 20 ++ daemon/config.go | 34 +-- daemon/config_experimental.go | 6 +- daemon/config_linux.go | 39 ++-- daemon/config_stub.go | 4 +- daemon/config_windows.go | 4 +- docker/client.go | 27 ++- docker/common.go | 101 +++++++++ docker/daemon.go | 164 ++++++++++++--- docker/daemon_none.go | 12 ++ docker/docker.go | 143 ++++--------- docker/flags.go | 170 +++++---------- docker/log.go | 14 -- integration-cli/docker_cli_proxy_test.go | 3 +- opts/hosts_unix.go | 7 + opts/hosts_windows.go | 7 + opts/opts.go | 25 ++- opts/opts_test.go | 2 +- opts/ulimit.go | 13 +- pkg/mflag/flag.go | 52 ++++- pkg/tlsconfig/config.go | 15 +- registry/config.go | 6 +- runconfig/parse.go | 4 +- 67 files changed, 913 insertions(+), 614 deletions(-) delete mode 100644 api/client/help.go create mode 100644 cli/cli.go create mode 100644 cli/client.go create mode 100644 cli/common.go create mode 100644 docker/common.go create mode 100644 docker/daemon_none.go delete mode 100644 docker/log.go create mode 100644 opts/hosts_unix.go create mode 100644 opts/hosts_windows.go diff --git a/api/client/attach.go b/api/client/attach.go index d851bfda34..584c53ea06 100644 --- a/api/client/attach.go +++ b/api/client/attach.go @@ -8,6 +8,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/docker/api/types" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/signal" ) @@ -16,9 +17,10 @@ import ( // // Usage: docker attach [OPTIONS] CONTAINER func (cli *DockerCli) CmdAttach(args ...string) error { - cmd := cli.Subcmd("attach", []string{"CONTAINER"}, "Attach to a running container", true) + cmd := Cli.Subcmd("attach", []string{"CONTAINER"}, "Attach to a running container", true) noStdin := cmd.Bool([]string{"#nostdin", "-no-stdin"}, false, "Do not attach STDIN") proxy := cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy all received signals to the process") + cmd.Require(flag.Exact, 1) cmd.ParseFlags(args, true) @@ -75,7 +77,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error { return err } if status != 0 { - return StatusError{StatusCode: status} + return Cli.StatusError{StatusCode: status} } return nil diff --git a/api/client/build.go b/api/client/build.go index d2799ce900..a6eee12b04 100644 --- a/api/client/build.go +++ b/api/client/build.go @@ -18,6 +18,7 @@ import ( "strings" "github.com/docker/docker/api" + Cli "github.com/docker/docker/cli" "github.com/docker/docker/graph/tags" "github.com/docker/docker/opts" "github.com/docker/docker/pkg/archive" @@ -46,7 +47,7 @@ const ( // // Usage: docker build [OPTIONS] PATH | URL | - func (cli *DockerCli) CmdBuild(args ...string) error { - cmd := cli.Subcmd("build", []string{"PATH | URL | -"}, "Build a new image from the source code at PATH", true) + cmd := Cli.Subcmd("build", []string{"PATH | URL | -"}, "Build a new image from the source code at PATH", true) tag := cmd.String([]string{"t", "-tag"}, "", "Repository name (and optionally a tag) for the image") suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the verbose output generated by the containers") noCache := cmd.Bool([]string{"#no-cache", "-no-cache"}, false, "Do not use cache when building the image") @@ -64,7 +65,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error { flCgroupParent := cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container") ulimits := make(map[string]*ulimit.Ulimit) - flUlimits := opts.NewUlimitOpt(ulimits) + flUlimits := opts.NewUlimitOpt(&ulimits) cmd.Var(flUlimits, []string{"-ulimit"}, "Ulimit options") cmd.Require(flag.Exact, 1) @@ -325,7 +326,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error { if jerr.Code == 0 { jerr.Code = 1 } - return StatusError{Status: jerr.Message, StatusCode: jerr.Code} + return Cli.StatusError{Status: jerr.Message, StatusCode: jerr.Code} } return err } diff --git a/api/client/cli.go b/api/client/cli.go index a8dd276cee..fc4e236285 100644 --- a/api/client/cli.go +++ b/api/client/cli.go @@ -2,25 +2,28 @@ package client import ( "crypto/tls" - "encoding/json" "errors" "fmt" "io" "net/http" "net/url" - "reflect" + "os" "strings" - "text/template" + "github.com/docker/docker/cli" "github.com/docker/docker/cliconfig" - flag "github.com/docker/docker/pkg/mflag" + "github.com/docker/docker/opts" "github.com/docker/docker/pkg/sockets" "github.com/docker/docker/pkg/term" + "github.com/docker/docker/pkg/tlsconfig" ) // DockerCli represents the docker command line client. // Instances of the client can be returned from NewDockerCli. type DockerCli struct { + // initializing closure + init func() error + // proto holds the client protocol i.e. unix. proto string // addr holds the client address. @@ -55,116 +58,11 @@ type DockerCli struct { transport *http.Transport } -var funcMap = template.FuncMap{ - "json": func(v interface{}) string { - a, _ := json.Marshal(v) - return string(a) - }, -} - -func (cli *DockerCli) Out() io.Writer { - return cli.out -} - -func (cli *DockerCli) Err() io.Writer { - return cli.err -} - -func (cli *DockerCli) getMethod(args ...string) (func(...string) error, bool) { - camelArgs := make([]string, len(args)) - for i, s := range args { - if len(s) == 0 { - return nil, false - } - camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:]) +func (cli *DockerCli) Initialize() error { + if cli.init == nil { + return nil } - methodName := "Cmd" + strings.Join(camelArgs, "") - method := reflect.ValueOf(cli).MethodByName(methodName) - if !method.IsValid() { - return nil, false - } - return method.Interface().(func(...string) error), true -} - -// Cmd executes the specified command. -func (cli *DockerCli) Cmd(args ...string) error { - if len(args) > 1 { - method, exists := cli.getMethod(args[:2]...) - if exists { - return method(args[2:]...) - } - } - if len(args) > 0 { - method, exists := cli.getMethod(args[0]) - if !exists { - return fmt.Errorf("docker: '%s' is not a docker command.\nSee 'docker --help'.", args[0]) - } - return method(args[1:]...) - } - return cli.CmdHelp() -} - -// Subcmd is a subcommand of the main "docker" command. -// A subcommand represents an action that can be performed -// from the Docker command line client. -// -// Multiple subcommand synopses may be provided with one 'Usage' line being -// printed for each in the following way: -// -// Usage: docker [OPTIONS] -// docker [OPTIONS] -// ... -// -// If no undeprecated flags are added to the returned FlagSet, "[OPTIONS]" will -// not be included on the usage synopsis lines. If no synopses are given, only -// one usage synopsis line will be printed with nothing following the -// "[OPTIONS]" section -// -// To see all available subcommands, run "docker --help". -func (cli *DockerCli) Subcmd(name string, synopses []string, description string, exitOnError bool) *flag.FlagSet { - var errorHandling flag.ErrorHandling - if exitOnError { - errorHandling = flag.ExitOnError - } else { - errorHandling = flag.ContinueOnError - } - - flags := flag.NewFlagSet(name, errorHandling) - - flags.Usage = func() { - flags.ShortUsage() - flags.PrintDefaults() - } - - flags.ShortUsage = func() { - options := "" - if flags.FlagCountUndeprecated() > 0 { - options = " [OPTIONS]" - } - - if len(synopses) == 0 { - synopses = []string{""} - } - - // Allow for multiple command usage synopses. - for i, synopsis := range synopses { - lead := "\t" - if i == 0 { - // First line needs the word 'Usage'. - lead = "Usage:\t" - } - - if synopsis != "" { - synopsis = " " + synopsis - } - - fmt.Fprintf(flags.Out(), "\n%sdocker %s%s%s", lead, name, options, synopsis) - } - - fmt.Fprintf(flags.Out(), "\n\n%s\n", description) - } - - return flags + return cli.init() } // CheckTtyInput checks if we are trying to attach to a container tty @@ -187,64 +85,78 @@ func (cli *DockerCli) PsFormat() string { // The key file, protocol (i.e. unix) and address are passed in as strings, along with the tls.Config. If the tls.Config // is set the client scheme will be set to https. // The client will be given a 32-second timeout (see https://github.com/docker/docker/pull/8035). -func NewDockerCli(in io.ReadCloser, out, err io.Writer, keyFile string, proto, addr string, tlsConfig *tls.Config) *DockerCli { - var ( - inFd uintptr - outFd uintptr - isTerminalIn = false - isTerminalOut = false - scheme = "http" - basePath = "" - ) - - if tlsConfig != nil { - scheme = "https" - } - if in != nil { - inFd, isTerminalIn = term.GetFdInfo(in) +func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientFlags) *DockerCli { + cli := &DockerCli{ + in: in, + out: out, + err: err, + keyFile: clientFlags.Common.TrustKey, } - if out != nil { - outFd, isTerminalOut = term.GetFdInfo(out) + cli.init = func() error { + clientFlags.PostParse() + + hosts := clientFlags.Common.Hosts + + switch len(hosts) { + case 0: + defaultHost := os.Getenv("DOCKER_HOST") + if defaultHost == "" { + defaultHost = opts.DefaultHost + } + defaultHost, err := opts.ValidateHost(defaultHost) + if err != nil { + return err + } + hosts = []string{defaultHost} + case 1: + // only accept one host to talk to + default: + return errors.New("Please specify only one -H") + } + + protoAddrParts := strings.SplitN(hosts[0], "://", 2) + cli.proto, cli.addr = protoAddrParts[0], protoAddrParts[1] + + if cli.proto == "tcp" { + // error is checked in pkg/parsers already + parsed, _ := url.Parse("tcp://" + cli.addr) + cli.addr = parsed.Host + cli.basePath = parsed.Path + } + + if clientFlags.Common.TLSOptions != nil { + cli.scheme = "https" + var e error + cli.tlsConfig, e = tlsconfig.Client(*clientFlags.Common.TLSOptions) + if e != nil { + return e + } + } else { + cli.scheme = "http" + } + + if cli.in != nil { + cli.inFd, cli.isTerminalIn = term.GetFdInfo(cli.in) + } + if cli.out != nil { + cli.outFd, cli.isTerminalOut = term.GetFdInfo(cli.out) + } + + // The transport is created here for reuse during the client session. + cli.transport = &http.Transport{ + TLSClientConfig: cli.tlsConfig, + } + sockets.ConfigureTCPTransport(cli.transport, cli.proto, cli.addr) + + configFile, e := cliconfig.Load(cliconfig.ConfigDir()) + if e != nil { + fmt.Fprintf(cli.err, "WARNING: Error loading config file:%v\n", e) + } + cli.configFile = configFile + + return nil } - if err == nil { - err = out - } - - // The transport is created here for reuse during the client session. - tr := &http.Transport{ - TLSClientConfig: tlsConfig, - } - sockets.ConfigureTCPTransport(tr, proto, addr) - - configFile, e := cliconfig.Load(cliconfig.ConfigDir()) - if e != nil { - fmt.Fprintf(err, "WARNING: Error loading config file:%v\n", e) - } - - if proto == "tcp" { - // error is checked in pkg/parsers already - parsed, _ := url.Parse("tcp://" + addr) - addr = parsed.Host - basePath = parsed.Path - } - - return &DockerCli{ - proto: proto, - addr: addr, - basePath: basePath, - configFile: configFile, - in: in, - out: out, - err: err, - keyFile: keyFile, - inFd: inFd, - outFd: outFd, - isTerminalIn: isTerminalIn, - isTerminalOut: isTerminalOut, - tlsConfig: tlsConfig, - scheme: scheme, - transport: tr, - } + return cli } diff --git a/api/client/client.go b/api/client/client.go index 3170881744..4cfce5f684 100644 --- a/api/client/client.go +++ b/api/client/client.go @@ -3,15 +3,3 @@ // Run "docker help SUBCOMMAND" or "docker SUBCOMMAND --help" to see more information on any Docker subcommand, including the full list of options supported for the subcommand. // See https://docs.docker.com/installation/ for instructions on installing Docker. package client - -import "fmt" - -// An StatusError reports an unsuccessful exit by a command. -type StatusError struct { - Status string - StatusCode int -} - -func (e StatusError) Error() string { - return fmt.Sprintf("Status: %s, Code: %d", e.Status, e.StatusCode) -} diff --git a/api/client/commit.go b/api/client/commit.go index 2a14c084b8..fe4acd481b 100644 --- a/api/client/commit.go +++ b/api/client/commit.go @@ -6,6 +6,7 @@ import ( "net/url" "github.com/docker/docker/api/types" + Cli "github.com/docker/docker/cli" "github.com/docker/docker/opts" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/parsers" @@ -17,7 +18,7 @@ import ( // // Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]] func (cli *DockerCli) CmdCommit(args ...string) error { - cmd := cli.Subcmd("commit", []string{"CONTAINER [REPOSITORY[:TAG]]"}, "Create a new image from a container's changes", true) + cmd := Cli.Subcmd("commit", []string{"CONTAINER [REPOSITORY[:TAG]]"}, "Create a new image from a container's changes", true) flPause := cmd.Bool([]string{"p", "-pause"}, true, "Pause container during commit") flComment := cmd.String([]string{"m", "-message"}, "", "Commit message") flAuthor := cmd.String([]string{"a", "#author", "-author"}, "", "Author (e.g., \"John Hannibal Smith \")") diff --git a/api/client/cp.go b/api/client/cp.go index 0463a994b7..99278adfc1 100644 --- a/api/client/cp.go +++ b/api/client/cp.go @@ -12,6 +12,7 @@ import ( "strings" "github.com/docker/docker/api/types" + Cli "github.com/docker/docker/cli" "github.com/docker/docker/pkg/archive" flag "github.com/docker/docker/pkg/mflag" ) @@ -37,7 +38,7 @@ const ( // docker cp CONTAINER:PATH LOCALPATH|- // docker cp LOCALPATH|- CONTAINER:PATH func (cli *DockerCli) CmdCp(args ...string) error { - cmd := cli.Subcmd( + cmd := Cli.Subcmd( "cp", []string{"CONTAINER:PATH LOCALPATH|-", "LOCALPATH|- CONTAINER:PATH"}, strings.Join([]string{ diff --git a/api/client/create.go b/api/client/create.go index d9df039785..8d573c2218 100644 --- a/api/client/create.go +++ b/api/client/create.go @@ -10,6 +10,7 @@ import ( "strings" "github.com/docker/docker/api/types" + Cli "github.com/docker/docker/cli" "github.com/docker/docker/graph/tags" "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/registry" @@ -137,7 +138,7 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc // // Usage: docker create [OPTIONS] IMAGE [COMMAND] [ARG...] func (cli *DockerCli) CmdCreate(args ...string) error { - cmd := cli.Subcmd("create", []string{"IMAGE [COMMAND] [ARG...]"}, "Create a new container", true) + cmd := Cli.Subcmd("create", []string{"IMAGE [COMMAND] [ARG...]"}, "Create a new container", true) // These are flags not stored in Config/HostConfig var ( diff --git a/api/client/diff.go b/api/client/diff.go index 3dfbb00b98..b955774ccd 100644 --- a/api/client/diff.go +++ b/api/client/diff.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/docker/docker/api/types" + Cli "github.com/docker/docker/cli" "github.com/docker/docker/pkg/archive" flag "github.com/docker/docker/pkg/mflag" ) @@ -17,7 +18,7 @@ import ( // // Usage: docker diff CONTAINER func (cli *DockerCli) CmdDiff(args ...string) error { - cmd := cli.Subcmd("diff", []string{"CONTAINER"}, "Inspect changes on a container's filesystem", true) + cmd := Cli.Subcmd("diff", []string{"CONTAINER"}, "Inspect changes on a container's filesystem", true) cmd.Require(flag.Exact, 1) cmd.ParseFlags(args, true) diff --git a/api/client/events.go b/api/client/events.go index d4829edfd4..c0168bdb15 100644 --- a/api/client/events.go +++ b/api/client/events.go @@ -4,6 +4,7 @@ import ( "net/url" "time" + Cli "github.com/docker/docker/cli" "github.com/docker/docker/opts" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/parsers/filters" @@ -14,7 +15,7 @@ import ( // // Usage: docker events [OPTIONS] func (cli *DockerCli) CmdEvents(args ...string) error { - cmd := cli.Subcmd("events", nil, "Get real time events from the server", true) + cmd := Cli.Subcmd("events", nil, "Get real time events from the server", true) since := cmd.String([]string{"#since", "-since"}, "", "Show all events created since timestamp") until := cmd.String([]string{"-until"}, "", "Stream events until this timestamp") flFilter := opts.NewListOpts(nil) diff --git a/api/client/exec.go b/api/client/exec.go index ec4d124c01..d02c019bca 100644 --- a/api/client/exec.go +++ b/api/client/exec.go @@ -7,6 +7,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/docker/api/types" + Cli "github.com/docker/docker/cli" "github.com/docker/docker/pkg/promise" "github.com/docker/docker/runconfig" ) @@ -15,12 +16,12 @@ import ( // // Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...] func (cli *DockerCli) CmdExec(args ...string) error { - cmd := cli.Subcmd("exec", []string{"CONTAINER COMMAND [ARG...]"}, "Run a command in a running container", true) + cmd := Cli.Subcmd("exec", []string{"CONTAINER COMMAND [ARG...]"}, "Run a command in a running container", true) execConfig, err := runconfig.ParseExec(cmd, args) // just in case the ParseExec does not exit if execConfig.Container == "" || err != nil { - return StatusError{StatusCode: 1} + return Cli.StatusError{StatusCode: 1} } serverResp, err := cli.call("POST", "/containers/"+execConfig.Container+"/exec", execConfig, nil) @@ -126,7 +127,7 @@ func (cli *DockerCli) CmdExec(args ...string) error { } if status != 0 { - return StatusError{StatusCode: status} + return Cli.StatusError{StatusCode: status} } return nil diff --git a/api/client/export.go b/api/client/export.go index 381e555b63..78594997be 100644 --- a/api/client/export.go +++ b/api/client/export.go @@ -5,6 +5,7 @@ import ( "io" "os" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" ) @@ -14,7 +15,7 @@ import ( // // Usage: docker export [OPTIONS] CONTAINER func (cli *DockerCli) CmdExport(args ...string) error { - cmd := cli.Subcmd("export", []string{"CONTAINER"}, "Export the contents of a container's filesystem as a tar archive", true) + cmd := Cli.Subcmd("export", []string{"CONTAINER"}, "Export the contents of a container's filesystem as a tar archive", true) outfile := cmd.String([]string{"o", "-output"}, "", "Write to a file, instead of STDOUT") cmd.Require(flag.Exact, 1) diff --git a/api/client/help.go b/api/client/help.go deleted file mode 100644 index 8e1dc852b7..0000000000 --- a/api/client/help.go +++ /dev/null @@ -1,34 +0,0 @@ -package client - -import ( - "fmt" - - flag "github.com/docker/docker/pkg/mflag" -) - -// CmdHelp displays information on a Docker command. -// -// If more than one command is specified, information is only shown for the first command. -// -// Usage: docker help COMMAND or docker COMMAND --help -func (cli *DockerCli) CmdHelp(args ...string) error { - if len(args) > 1 { - method, exists := cli.getMethod(args[:2]...) - if exists { - method("--help") - return nil - } - } - if len(args) > 0 { - method, exists := cli.getMethod(args[0]) - if !exists { - return fmt.Errorf("docker: '%s' is not a docker command. See 'docker --help'.", args[0]) - } - method("--help") - return nil - } - - flag.Usage() - - return nil -} diff --git a/api/client/history.go b/api/client/history.go index ca66524286..925add6640 100644 --- a/api/client/history.go +++ b/api/client/history.go @@ -7,6 +7,7 @@ import ( "time" "github.com/docker/docker/api/types" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/stringutils" @@ -17,7 +18,7 @@ import ( // // Usage: docker history [OPTIONS] IMAGE func (cli *DockerCli) CmdHistory(args ...string) error { - cmd := cli.Subcmd("history", []string{"IMAGE"}, "Show the history of an image", true) + cmd := Cli.Subcmd("history", []string{"IMAGE"}, "Show the history of an image", true) human := cmd.Bool([]string{"H", "-human"}, true, "Print sizes and dates in human readable format") quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs") noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") diff --git a/api/client/images.go b/api/client/images.go index 93dc35bbca..92adeed00e 100644 --- a/api/client/images.go +++ b/api/client/images.go @@ -8,6 +8,7 @@ import ( "time" "github.com/docker/docker/api/types" + Cli "github.com/docker/docker/cli" "github.com/docker/docker/opts" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/parsers" @@ -21,7 +22,7 @@ import ( // // Usage: docker images [OPTIONS] [REPOSITORY] func (cli *DockerCli) CmdImages(args ...string) error { - cmd := cli.Subcmd("images", []string{"[REPOSITORY]"}, "List images", true) + cmd := Cli.Subcmd("images", []string{"[REPOSITORY]"}, "List images", true) quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs") all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (default hides intermediate images)") noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") diff --git a/api/client/import.go b/api/client/import.go index 43edce3def..ec3d028f90 100644 --- a/api/client/import.go +++ b/api/client/import.go @@ -6,6 +6,7 @@ import ( "net/url" "os" + Cli "github.com/docker/docker/cli" "github.com/docker/docker/opts" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/parsers" @@ -19,7 +20,7 @@ import ( // // Usage: docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]] func (cli *DockerCli) CmdImport(args ...string) error { - cmd := cli.Subcmd("import", []string{"file|URL|- [REPOSITORY[:TAG]]"}, "Create an empty filesystem image and import the contents of the\ntarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then\noptionally tag it.", true) + cmd := Cli.Subcmd("import", []string{"file|URL|- [REPOSITORY[:TAG]]"}, "Create an empty filesystem image and import the contents of the\ntarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then\noptionally tag it.", true) flChanges := opts.NewListOpts(nil) cmd.Var(&flChanges, []string{"c", "-change"}, "Apply Dockerfile instruction to the created image") cmd.Require(flag.Min, 1) diff --git a/api/client/info.go b/api/client/info.go index 985548ae70..c7b19ccb5c 100644 --- a/api/client/info.go +++ b/api/client/info.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/docker/docker/api/types" + Cli "github.com/docker/docker/cli" "github.com/docker/docker/pkg/httputils" "github.com/docker/docker/pkg/ioutils" flag "github.com/docker/docker/pkg/mflag" @@ -15,7 +16,7 @@ import ( // // Usage: docker info func (cli *DockerCli) CmdInfo(args ...string) error { - cmd := cli.Subcmd("info", nil, "Display system-wide information", true) + cmd := Cli.Subcmd("info", nil, "Display system-wide information", true) cmd.Require(flag.Exact, 0) cmd.ParseFlags(args, true) diff --git a/api/client/inspect.go b/api/client/inspect.go index b09646afa9..6e728bdf6b 100644 --- a/api/client/inspect.go +++ b/api/client/inspect.go @@ -9,14 +9,22 @@ import ( "text/template" "github.com/docker/docker/api/types" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" ) +var funcMap = template.FuncMap{ + "json": func(v interface{}) string { + a, _ := json.Marshal(v) + return string(a) + }, +} + // CmdInspect displays low-level information on one or more containers or images. // // Usage: docker inspect [OPTIONS] CONTAINER|IMAGE [CONTAINER|IMAGE...] func (cli *DockerCli) CmdInspect(args ...string) error { - cmd := cli.Subcmd("inspect", []string{"CONTAINER|IMAGE [CONTAINER|IMAGE...]"}, "Return low-level information on a container or image", true) + cmd := Cli.Subcmd("inspect", []string{"CONTAINER|IMAGE [CONTAINER|IMAGE...]"}, "Return low-level information on a container or image", true) tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template") inspectType := cmd.String([]string{"-type"}, "", "Return JSON for specified type, (e.g image or container)") cmd.Require(flag.Min, 1) @@ -29,7 +37,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error { if *tmplStr != "" { if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil { - return StatusError{StatusCode: 64, + return Cli.StatusError{StatusCode: 64, Status: "Template parsing error: " + err.Error()} } } @@ -143,7 +151,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error { } if status != 0 { - return StatusError{StatusCode: status} + return Cli.StatusError{StatusCode: status} } return nil } diff --git a/api/client/kill.go b/api/client/kill.go index 4f3d7ad0bd..63abed31f6 100644 --- a/api/client/kill.go +++ b/api/client/kill.go @@ -3,6 +3,7 @@ package client import ( "fmt" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" ) @@ -10,7 +11,7 @@ import ( // // Usage: docker kill [OPTIONS] CONTAINER [CONTAINER...] func (cli *DockerCli) CmdKill(args ...string) error { - cmd := cli.Subcmd("kill", []string{"CONTAINER [CONTAINER...]"}, "Kill a running container using SIGKILL or a specified signal", true) + cmd := Cli.Subcmd("kill", []string{"CONTAINER [CONTAINER...]"}, "Kill a running container using SIGKILL or a specified signal", true) signal := cmd.String([]string{"s", "-signal"}, "KILL", "Signal to send to the container") cmd.Require(flag.Min, 1) diff --git a/api/client/load.go b/api/client/load.go index a512ffb06a..9501db4f82 100644 --- a/api/client/load.go +++ b/api/client/load.go @@ -4,6 +4,7 @@ import ( "io" "os" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" ) @@ -13,7 +14,7 @@ import ( // // Usage: docker load [OPTIONS] func (cli *DockerCli) CmdLoad(args ...string) error { - cmd := cli.Subcmd("load", nil, "Load an image from a tar archive or STDIN", true) + cmd := Cli.Subcmd("load", nil, "Load an image from a tar archive or STDIN", true) infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN") cmd.Require(flag.Exact, 0) diff --git a/api/client/login.go b/api/client/login.go index 145fe9c525..8c012737ff 100644 --- a/api/client/login.go +++ b/api/client/login.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/docker/docker/api/types" + Cli "github.com/docker/docker/cli" "github.com/docker/docker/cliconfig" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/term" @@ -21,7 +22,7 @@ import ( // // Usage: docker login SERVER func (cli *DockerCli) CmdLogin(args ...string) error { - cmd := cli.Subcmd("login", []string{"[SERVER]"}, "Register or log in to a Docker registry server, if no server is\nspecified \""+registry.INDEXSERVER+"\" is the default.", true) + cmd := Cli.Subcmd("login", []string{"[SERVER]"}, "Register or log in to a Docker registry server, if no server is\nspecified \""+registry.INDEXSERVER+"\" is the default.", true) cmd.Require(flag.Max, 1) var username, password, email string diff --git a/api/client/logout.go b/api/client/logout.go index 32108d3429..7eb721ec7d 100644 --- a/api/client/logout.go +++ b/api/client/logout.go @@ -3,6 +3,7 @@ package client import ( "fmt" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/registry" ) @@ -13,7 +14,7 @@ import ( // // Usage: docker logout [SERVER] func (cli *DockerCli) CmdLogout(args ...string) error { - cmd := cli.Subcmd("logout", []string{"[SERVER]"}, "Log out from a Docker registry, if no server is\nspecified \""+registry.INDEXSERVER+"\" is the default.", true) + cmd := Cli.Subcmd("logout", []string{"[SERVER]"}, "Log out from a Docker registry, if no server is\nspecified \""+registry.INDEXSERVER+"\" is the default.", true) cmd.Require(flag.Max, 1) cmd.ParseFlags(args, true) diff --git a/api/client/logs.go b/api/client/logs.go index 0f4be3dac6..f1d647f3af 100644 --- a/api/client/logs.go +++ b/api/client/logs.go @@ -7,6 +7,7 @@ import ( "time" "github.com/docker/docker/api/types" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/timeutils" ) @@ -15,7 +16,7 @@ import ( // // docker logs [OPTIONS] CONTAINER func (cli *DockerCli) CmdLogs(args ...string) error { - cmd := cli.Subcmd("logs", []string{"CONTAINER"}, "Fetch the logs of a container", true) + cmd := Cli.Subcmd("logs", []string{"CONTAINER"}, "Fetch the logs of a container", true) follow := cmd.Bool([]string{"f", "-follow"}, false, "Follow log output") since := cmd.String([]string{"-since"}, "", "Show logs since timestamp") times := cmd.Bool([]string{"t", "-timestamps"}, false, "Show timestamps") diff --git a/api/client/pause.go b/api/client/pause.go index 46285755f6..94dd59d7e1 100644 --- a/api/client/pause.go +++ b/api/client/pause.go @@ -3,6 +3,7 @@ package client import ( "fmt" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" ) @@ -10,7 +11,7 @@ import ( // // Usage: docker pause CONTAINER [CONTAINER...] func (cli *DockerCli) CmdPause(args ...string) error { - cmd := cli.Subcmd("pause", []string{"CONTAINER [CONTAINER...]"}, "Pause all processes within a container", true) + cmd := Cli.Subcmd("pause", []string{"CONTAINER [CONTAINER...]"}, "Pause all processes within a container", true) cmd.Require(flag.Min, 1) cmd.ParseFlags(args, true) diff --git a/api/client/port.go b/api/client/port.go index 9f83aa47a1..d8bcbf6eff 100644 --- a/api/client/port.go +++ b/api/client/port.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/nat" ) @@ -14,7 +15,7 @@ import ( // // Usage: docker port CONTAINER [PRIVATE_PORT[/PROTO]] func (cli *DockerCli) CmdPort(args ...string) error { - cmd := cli.Subcmd("port", []string{"CONTAINER [PRIVATE_PORT[/PROTO]]"}, "List port mappings for the CONTAINER, or lookup the public-facing port that\nis NAT-ed to the PRIVATE_PORT", true) + cmd := Cli.Subcmd("port", []string{"CONTAINER [PRIVATE_PORT[/PROTO]]"}, "List port mappings for the CONTAINER, or lookup the public-facing port that\nis NAT-ed to the PRIVATE_PORT", true) cmd.Require(flag.Min, 1) cmd.ParseFlags(args, true) diff --git a/api/client/ps.go b/api/client/ps.go index 28b9615639..e7fb97c315 100644 --- a/api/client/ps.go +++ b/api/client/ps.go @@ -7,6 +7,7 @@ import ( "github.com/docker/docker/api/client/ps" "github.com/docker/docker/api/types" + Cli "github.com/docker/docker/cli" "github.com/docker/docker/opts" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/parsers/filters" @@ -22,7 +23,7 @@ func (cli *DockerCli) CmdPs(args ...string) error { psFilterArgs = filters.Args{} v = url.Values{} - cmd = cli.Subcmd("ps", nil, "List containers", true) + cmd = Cli.Subcmd("ps", nil, "List containers", true) quiet = cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs") size = cmd.Bool([]string{"s", "-size"}, false, "Display total file sizes") all = cmd.Bool([]string{"a", "-all"}, false, "Show all containers (default shows just running)") diff --git a/api/client/pull.go b/api/client/pull.go index 8607eac192..7773b975ea 100644 --- a/api/client/pull.go +++ b/api/client/pull.go @@ -4,6 +4,7 @@ import ( "fmt" "net/url" + Cli "github.com/docker/docker/cli" "github.com/docker/docker/graph/tags" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/parsers" @@ -15,7 +16,7 @@ import ( // // Usage: docker pull [OPTIONS] IMAGENAME[:TAG|@DIGEST] func (cli *DockerCli) CmdPull(args ...string) error { - cmd := cli.Subcmd("pull", []string{"NAME[:TAG|@DIGEST]"}, "Pull an image or a repository from a registry", true) + cmd := Cli.Subcmd("pull", []string{"NAME[:TAG|@DIGEST]"}, "Pull an image or a repository from a registry", true) allTags := cmd.Bool([]string{"a", "-all-tags"}, false, "Download all tagged images in the repository") cmd.Require(flag.Exact, 1) diff --git a/api/client/push.go b/api/client/push.go index 2d99a51298..4a31f9ba61 100644 --- a/api/client/push.go +++ b/api/client/push.go @@ -4,6 +4,7 @@ import ( "fmt" "net/url" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/registry" @@ -13,7 +14,7 @@ import ( // // Usage: docker push NAME[:TAG] func (cli *DockerCli) CmdPush(args ...string) error { - cmd := cli.Subcmd("push", []string{"NAME[:TAG]"}, "Push an image or a repository to a registry", true) + cmd := Cli.Subcmd("push", []string{"NAME[:TAG]"}, "Push an image or a repository to a registry", true) cmd.Require(flag.Exact, 1) cmd.ParseFlags(args, true) diff --git a/api/client/rename.go b/api/client/rename.go index caad597d00..ae09a46221 100644 --- a/api/client/rename.go +++ b/api/client/rename.go @@ -3,6 +3,7 @@ package client import ( "fmt" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" ) @@ -10,7 +11,7 @@ import ( // // Usage: docker rename OLD_NAME NEW_NAME func (cli *DockerCli) CmdRename(args ...string) error { - cmd := cli.Subcmd("rename", []string{"OLD_NAME NEW_NAME"}, "Rename a container", true) + cmd := Cli.Subcmd("rename", []string{"OLD_NAME NEW_NAME"}, "Rename a container", true) cmd.Require(flag.Exact, 2) cmd.ParseFlags(args, true) diff --git a/api/client/restart.go b/api/client/restart.go index 3dc6a650c3..88de4f758a 100644 --- a/api/client/restart.go +++ b/api/client/restart.go @@ -5,6 +5,7 @@ import ( "net/url" "strconv" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" ) @@ -12,7 +13,7 @@ import ( // // Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...] func (cli *DockerCli) CmdRestart(args ...string) error { - cmd := cli.Subcmd("restart", []string{"CONTAINER [CONTAINER...]"}, "Restart a running container", true) + cmd := Cli.Subcmd("restart", []string{"CONTAINER [CONTAINER...]"}, "Restart a running container", true) nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Seconds to wait for stop before killing the container") cmd.Require(flag.Min, 1) diff --git a/api/client/rm.go b/api/client/rm.go index fe5d241210..5766727a94 100644 --- a/api/client/rm.go +++ b/api/client/rm.go @@ -5,6 +5,7 @@ import ( "net/url" "strings" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" ) @@ -12,7 +13,7 @@ import ( // // Usage: docker rm [OPTIONS] CONTAINER [CONTAINER...] func (cli *DockerCli) CmdRm(args ...string) error { - cmd := cli.Subcmd("rm", []string{"CONTAINER [CONTAINER...]"}, "Remove one or more containers", true) + cmd := Cli.Subcmd("rm", []string{"CONTAINER [CONTAINER...]"}, "Remove one or more containers", true) v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated with the container") link := cmd.Bool([]string{"l", "#link", "-link"}, false, "Remove the specified link") force := cmd.Bool([]string{"f", "-force"}, false, "Force the removal of a running container (uses SIGKILL)") diff --git a/api/client/rmi.go b/api/client/rmi.go index 4bfbecc6e3..25d5646ebe 100644 --- a/api/client/rmi.go +++ b/api/client/rmi.go @@ -6,6 +6,7 @@ import ( "net/url" "github.com/docker/docker/api/types" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" ) @@ -13,7 +14,7 @@ import ( // // Usage: docker rmi [OPTIONS] IMAGE [IMAGE...] func (cli *DockerCli) CmdRmi(args ...string) error { - cmd := cli.Subcmd("rmi", []string{"IMAGE [IMAGE...]"}, "Remove one or more images", true) + cmd := Cli.Subcmd("rmi", []string{"IMAGE [IMAGE...]"}, "Remove one or more images", true) force := cmd.Bool([]string{"f", "-force"}, false, "Force removal of the image") noprune := cmd.Bool([]string{"-no-prune"}, false, "Do not delete untagged parents") cmd.Require(flag.Min, 1) diff --git a/api/client/run.go b/api/client/run.go index e80b9a8c47..7e28cb2219 100644 --- a/api/client/run.go +++ b/api/client/run.go @@ -8,6 +8,7 @@ import ( "runtime" "github.com/Sirupsen/logrus" + Cli "github.com/docker/docker/cli" "github.com/docker/docker/opts" "github.com/docker/docker/pkg/promise" "github.com/docker/docker/pkg/signal" @@ -39,7 +40,7 @@ func (cid *cidFile) Write(id string) error { // // Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...] func (cli *DockerCli) CmdRun(args ...string) error { - cmd := cli.Subcmd("run", []string{"IMAGE [COMMAND] [ARG...]"}, "Run a command in a new container", true) + cmd := Cli.Subcmd("run", []string{"IMAGE [COMMAND] [ARG...]"}, "Run a command in a new container", true) // These are flags not stored in Config/HostConfig var ( @@ -249,7 +250,7 @@ func (cli *DockerCli) CmdRun(args ...string) error { } } if status != 0 { - return StatusError{StatusCode: status} + return Cli.StatusError{StatusCode: status} } return nil } diff --git a/api/client/save.go b/api/client/save.go index 8101837cbf..5155319c80 100644 --- a/api/client/save.go +++ b/api/client/save.go @@ -6,6 +6,7 @@ import ( "net/url" "os" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" ) @@ -15,7 +16,7 @@ import ( // // Usage: docker save [OPTIONS] IMAGE [IMAGE...] func (cli *DockerCli) CmdSave(args ...string) error { - cmd := cli.Subcmd("save", []string{"IMAGE [IMAGE...]"}, "Save an image(s) to a tar archive (streamed to STDOUT by default)", true) + cmd := Cli.Subcmd("save", []string{"IMAGE [IMAGE...]"}, "Save an image(s) to a tar archive (streamed to STDOUT by default)", true) outfile := cmd.String([]string{"o", "-output"}, "", "Write to an file, instead of STDOUT") cmd.Require(flag.Min, 1) diff --git a/api/client/search.go b/api/client/search.go index 1fdff73f2f..2305d08343 100644 --- a/api/client/search.go +++ b/api/client/search.go @@ -8,6 +8,7 @@ import ( "strings" "text/tabwriter" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/stringutils" @@ -25,7 +26,7 @@ func (r ByStars) Less(i, j int) bool { return r[i].StarCount < r[j].StarCount } // // Usage: docker search [OPTIONS] TERM func (cli *DockerCli) CmdSearch(args ...string) error { - cmd := cli.Subcmd("search", []string{"TERM"}, "Search the Docker Hub for images", true) + cmd := Cli.Subcmd("search", []string{"TERM"}, "Search the Docker Hub for images", true) noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") trusted := cmd.Bool([]string{"#t", "#trusted", "#-trusted"}, false, "Only show trusted builds") automated := cmd.Bool([]string{"-automated"}, false, "Only show automated builds") diff --git a/api/client/start.go b/api/client/start.go index e78b45d58c..e039df022d 100644 --- a/api/client/start.go +++ b/api/client/start.go @@ -9,6 +9,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/docker/api/types" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/promise" "github.com/docker/docker/pkg/signal" @@ -44,7 +45,7 @@ func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal { // // Usage: docker start [OPTIONS] CONTAINER [CONTAINER...] func (cli *DockerCli) CmdStart(args ...string) error { - cmd := cli.Subcmd("start", []string{"CONTAINER [CONTAINER...]"}, "Start one or more stopped containers", true) + cmd := Cli.Subcmd("start", []string{"CONTAINER [CONTAINER...]"}, "Start one or more stopped containers", true) attach := cmd.Bool([]string{"a", "-attach"}, false, "Attach STDOUT/STDERR and forward signals") openStdin := cmd.Bool([]string{"i", "-interactive"}, false, "Attach container's STDIN") cmd.Require(flag.Min, 1) @@ -162,7 +163,7 @@ func (cli *DockerCli) CmdStart(args ...string) error { return err } if status != 0 { - return StatusError{StatusCode: status} + return Cli.StatusError{StatusCode: status} } } return nil diff --git a/api/client/stats.go b/api/client/stats.go index a1fa37f51d..1feb1e11b7 100644 --- a/api/client/stats.go +++ b/api/client/stats.go @@ -12,6 +12,7 @@ import ( "time" "github.com/docker/docker/api/types" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/units" ) @@ -124,7 +125,7 @@ func (s *containerStats) Display(w io.Writer) error { // // Usage: docker stats CONTAINER [CONTAINER...] func (cli *DockerCli) CmdStats(args ...string) error { - cmd := cli.Subcmd("stats", []string{"CONTAINER [CONTAINER...]"}, "Display a live stream of one or more containers' resource usage statistics", true) + cmd := Cli.Subcmd("stats", []string{"CONTAINER [CONTAINER...]"}, "Display a live stream of one or more containers' resource usage statistics", true) noStream := cmd.Bool([]string{"-no-stream"}, false, "Disable streaming stats and only pull the first result") cmd.Require(flag.Min, 1) diff --git a/api/client/stop.go b/api/client/stop.go index ee9ce3bffe..b7348a7bbb 100644 --- a/api/client/stop.go +++ b/api/client/stop.go @@ -5,6 +5,7 @@ import ( "net/url" "strconv" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" ) @@ -14,7 +15,7 @@ import ( // // Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...] func (cli *DockerCli) CmdStop(args ...string) error { - cmd := cli.Subcmd("stop", []string{"CONTAINER [CONTAINER...]"}, "Stop a running container by sending SIGTERM and then SIGKILL after a\ngrace period", true) + cmd := Cli.Subcmd("stop", []string{"CONTAINER [CONTAINER...]"}, "Stop a running container by sending SIGTERM and then SIGKILL after a\ngrace period", true) nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Seconds to wait for stop before killing it") cmd.Require(flag.Min, 1) diff --git a/api/client/tag.go b/api/client/tag.go index 287d3e7cb6..454c7ec5b4 100644 --- a/api/client/tag.go +++ b/api/client/tag.go @@ -3,6 +3,7 @@ package client import ( "net/url" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/registry" @@ -12,7 +13,7 @@ import ( // // Usage: docker tag [OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG] func (cli *DockerCli) CmdTag(args ...string) error { - cmd := cli.Subcmd("tag", []string{"IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]"}, "Tag an image into a repository", true) + cmd := Cli.Subcmd("tag", []string{"IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]"}, "Tag an image into a repository", true) force := cmd.Bool([]string{"f", "#force", "-force"}, false, "Force") cmd.Require(flag.Exact, 2) diff --git a/api/client/top.go b/api/client/top.go index ac69a34c75..c9934fe08d 100644 --- a/api/client/top.go +++ b/api/client/top.go @@ -8,6 +8,7 @@ import ( "text/tabwriter" "github.com/docker/docker/api/types" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" ) @@ -15,7 +16,7 @@ import ( // // Usage: docker top CONTAINER func (cli *DockerCli) CmdTop(args ...string) error { - cmd := cli.Subcmd("top", []string{"CONTAINER [ps OPTIONS]"}, "Display the running processes of a container", true) + cmd := Cli.Subcmd("top", []string{"CONTAINER [ps OPTIONS]"}, "Display the running processes of a container", true) cmd.Require(flag.Min, 1) cmd.ParseFlags(args, true) diff --git a/api/client/unpause.go b/api/client/unpause.go index 0c9dc882db..cd1e676673 100644 --- a/api/client/unpause.go +++ b/api/client/unpause.go @@ -3,6 +3,7 @@ package client import ( "fmt" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" ) @@ -10,7 +11,7 @@ import ( // // Usage: docker unpause CONTAINER [CONTAINER...] func (cli *DockerCli) CmdUnpause(args ...string) error { - cmd := cli.Subcmd("unpause", []string{"CONTAINER [CONTAINER...]"}, "Unpause all processes within a container", true) + cmd := Cli.Subcmd("unpause", []string{"CONTAINER [CONTAINER...]"}, "Unpause all processes within a container", true) cmd.Require(flag.Min, 1) cmd.ParseFlags(args, true) diff --git a/api/client/version.go b/api/client/version.go index c741baa134..4c42a32de8 100644 --- a/api/client/version.go +++ b/api/client/version.go @@ -8,6 +8,7 @@ import ( "github.com/docker/docker/api" "github.com/docker/docker/api/types" "github.com/docker/docker/autogen/dockerversion" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/utils" ) @@ -42,7 +43,7 @@ type VersionData struct { // // Usage: docker version func (cli *DockerCli) CmdVersion(args ...string) (err error) { - cmd := cli.Subcmd("version", nil, "Show the Docker version information.", true) + cmd := Cli.Subcmd("version", nil, "Show the Docker version information.", true) tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template") cmd.Require(flag.Exact, 0) @@ -53,7 +54,7 @@ func (cli *DockerCli) CmdVersion(args ...string) (err error) { var tmpl *template.Template if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil { - return StatusError{StatusCode: 64, + return Cli.StatusError{StatusCode: 64, Status: "Template parsing error: " + err.Error()} } @@ -85,7 +86,7 @@ func (cli *DockerCli) CmdVersion(args ...string) (err error) { defer serverResp.body.Close() if err = json.NewDecoder(serverResp.body).Decode(&vd.Server); err != nil { - return StatusError{StatusCode: 1, + return Cli.StatusError{StatusCode: 1, Status: "Error reading remote version: " + err.Error()} } diff --git a/api/client/wait.go b/api/client/wait.go index 068f0f1262..829a320cfe 100644 --- a/api/client/wait.go +++ b/api/client/wait.go @@ -3,6 +3,7 @@ package client import ( "fmt" + Cli "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" ) @@ -12,7 +13,7 @@ import ( // // Usage: docker wait CONTAINER [CONTAINER...] func (cli *DockerCli) CmdWait(args ...string) error { - cmd := cli.Subcmd("wait", []string{"CONTAINER [CONTAINER...]"}, "Block until a container stops, then print its exit code.", true) + cmd := Cli.Subcmd("wait", []string{"CONTAINER [CONTAINER...]"}, "Block until a container stops, then print its exit code.", true) cmd.Require(flag.Min, 1) cmd.ParseFlags(args, true) diff --git a/cli/cli.go b/cli/cli.go new file mode 100644 index 0000000000..8e559fc3fd --- /dev/null +++ b/cli/cli.go @@ -0,0 +1,200 @@ +package cli + +import ( + "errors" + "fmt" + "io" + "os" + "reflect" + "strings" + + flag "github.com/docker/docker/pkg/mflag" +) + +// Cli represents a command line interface. +type Cli struct { + Stderr io.Writer + handlers []Handler + Usage func() +} + +// Handler holds the different commands Cli will call +// It should have methods with names starting with `Cmd` like: +// func (h myHandler) CmdFoo(args ...string) error +type Handler interface{} + +// Initializer can be optionally implemented by a Handler to +// initialize before each call to one of its commands. +type Initializer interface { + Initialize() error +} + +// New instantiates a ready-to-use Cli. +func New(handlers ...Handler) *Cli { + // make the generic Cli object the first cli handler + // in order to handle `docker help` appropriately + cli := new(Cli) + cli.handlers = append([]Handler{cli}, handlers...) + return cli +} + +// initErr is an error returned upon initialization of a handler implementing Initializer. +type initErr struct{ error } + +func (err initErr) Error() string { + return err.Error() +} + +func (cli *Cli) command(args ...string) (func(...string) error, error) { + for _, c := range cli.handlers { + if c == nil { + continue + } + camelArgs := make([]string, len(args)) + for i, s := range args { + if len(s) == 0 { + return nil, errors.New("empty command") + } + camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:]) + } + methodName := "Cmd" + strings.Join(camelArgs, "") + method := reflect.ValueOf(c).MethodByName(methodName) + if method.IsValid() { + if c, ok := c.(Initializer); ok { + if err := c.Initialize(); err != nil { + return nil, initErr{err} + } + } + return method.Interface().(func(...string) error), nil + } + } + return nil, errors.New("command not found") +} + +// Run executes the specified command. +func (cli *Cli) Run(args ...string) error { + if len(args) > 1 { + command, err := cli.command(args[:2]...) + switch err := err.(type) { + case nil: + return command(args[2:]...) + case initErr: + return err.error + } + } + if len(args) > 0 { + command, err := cli.command(args[0]) + switch err := err.(type) { + case nil: + return command(args[1:]...) + case initErr: + return err.error + } + cli.noSuchCommand(args[0]) + } + return cli.CmdHelp() +} + +func (cli *Cli) noSuchCommand(command string) { + if cli.Stderr == nil { + cli.Stderr = os.Stderr + } + fmt.Fprintf(cli.Stderr, "docker: '%s' is not a docker command.\nSee 'docker --help'.\n", command) + os.Exit(1) +} + +// CmdHelp displays information on a Docker command. +// +// If more than one command is specified, information is only shown for the first command. +// +// Usage: docker help COMMAND or docker COMMAND --help +func (cli *Cli) CmdHelp(args ...string) error { + if len(args) > 1 { + command, err := cli.command(args[:2]...) + switch err := err.(type) { + case nil: + command("--help") + return nil + case initErr: + return err.error + } + } + if len(args) > 0 { + command, err := cli.command(args[0]) + switch err := err.(type) { + case nil: + command("--help") + return nil + case initErr: + return err.error + } + cli.noSuchCommand(args[0]) + } + + if cli.Usage == nil { + flag.Usage() + } else { + cli.Usage() + } + + return nil +} + +// Subcmd is a subcommand of the main "docker" command. +// A subcommand represents an action that can be performed +// from the Docker command line client. +// +// To see all available subcommands, run "docker --help". +func Subcmd(name string, synopses []string, description string, exitOnError bool) *flag.FlagSet { + var errorHandling flag.ErrorHandling + if exitOnError { + errorHandling = flag.ExitOnError + } else { + errorHandling = flag.ContinueOnError + } + flags := flag.NewFlagSet(name, errorHandling) + flags.Usage = func() { + flags.ShortUsage() + flags.PrintDefaults() + } + + flags.ShortUsage = func() { + options := "" + if flags.FlagCountUndeprecated() > 0 { + options = " [OPTIONS]" + } + + if len(synopses) == 0 { + synopses = []string{""} + } + + // Allow for multiple command usage synopses. + for i, synopsis := range synopses { + lead := "\t" + if i == 0 { + // First line needs the word 'Usage'. + lead = "Usage:\t" + } + + if synopsis != "" { + synopsis = " " + synopsis + } + + fmt.Fprintf(flags.Out(), "\n%sdocker %s%s%s", lead, name, options, synopsis) + } + + fmt.Fprintf(flags.Out(), "\n\n%s\n", description) + } + + return flags +} + +// An StatusError reports an unsuccessful exit by a command. +type StatusError struct { + Status string + StatusCode int +} + +func (e StatusError) Error() string { + return fmt.Sprintf("Status: %s, Code: %d", e.Status, e.StatusCode) +} diff --git a/cli/client.go b/cli/client.go new file mode 100644 index 0000000000..6a82eb52a5 --- /dev/null +++ b/cli/client.go @@ -0,0 +1,12 @@ +package cli + +import flag "github.com/docker/docker/pkg/mflag" + +// ClientFlags represents flags for the docker client. +type ClientFlags struct { + FlagSet *flag.FlagSet + Common *CommonFlags + PostParse func() + + ConfigDir string +} diff --git a/cli/common.go b/cli/common.go new file mode 100644 index 0000000000..85a02ac43b --- /dev/null +++ b/cli/common.go @@ -0,0 +1,20 @@ +package cli + +import ( + flag "github.com/docker/docker/pkg/mflag" + "github.com/docker/docker/pkg/tlsconfig" +) + +// CommonFlags represents flags that are common to both the client and the daemon. +type CommonFlags struct { + FlagSet *flag.FlagSet + PostParse func() + + Debug bool + Hosts []string + LogLevel string + TLS bool + TLSVerify bool + TLSOptions *tlsconfig.Options + TrustKey string +} diff --git a/daemon/config.go b/daemon/config.go index 23d9ebcf70..874ea4912a 100644 --- a/daemon/config.go +++ b/daemon/config.go @@ -41,22 +41,22 @@ type CommonConfig struct { // the current process. // Subsequent calls to `flag.Parse` will populate config with values parsed // from the command-line. -func (config *Config) InstallCommonFlags() { - opts.ListVar(&config.GraphOptions, []string{"-storage-opt"}, "Set storage driver options") - opts.ListVar(&config.ExecOptions, []string{"-exec-opt"}, "Set exec driver options") - flag.StringVar(&config.Pidfile, []string{"p", "-pidfile"}, defaultPidFile, "Path to use for daemon PID file") - flag.StringVar(&config.Root, []string{"g", "-graph"}, defaultGraph, "Root of the Docker runtime") - flag.StringVar(&config.ExecRoot, []string{"-exec-root"}, "/var/run/docker", "Root of the Docker execdriver") - flag.BoolVar(&config.AutoRestart, []string{"#r", "#-restart"}, true, "--restart on the daemon has been deprecated in favor of --restart policies on docker run") - flag.StringVar(&config.GraphDriver, []string{"s", "-storage-driver"}, "", "Storage driver to use") - flag.StringVar(&config.ExecDriver, []string{"e", "-exec-driver"}, defaultExec, "Exec driver to use") - flag.IntVar(&config.Mtu, []string{"#mtu", "-mtu"}, 0, "Set the containers network MTU") - flag.BoolVar(&config.EnableCors, []string{"#api-enable-cors", "#-api-enable-cors"}, false, "Enable CORS headers in the remote API, this is deprecated by --api-cors-header") - flag.StringVar(&config.CorsHeaders, []string{"-api-cors-header"}, "", "Set CORS headers in the remote API") +func (config *Config) InstallCommonFlags(cmd *flag.FlagSet, usageFn func(string) string) { + cmd.Var(opts.NewListOptsRef(&config.GraphOptions, nil), []string{"-storage-opt"}, usageFn("Set storage driver options")) + cmd.Var(opts.NewListOptsRef(&config.ExecOptions, nil), []string{"-exec-opt"}, usageFn("Set exec driver options")) + cmd.StringVar(&config.Pidfile, []string{"p", "-pidfile"}, defaultPidFile, usageFn("Path to use for daemon PID file")) + cmd.StringVar(&config.Root, []string{"g", "-graph"}, defaultGraph, usageFn("Root of the Docker runtime")) + cmd.StringVar(&config.ExecRoot, []string{"-exec-root"}, "/var/run/docker", usageFn("Root of the Docker execdriver")) + cmd.BoolVar(&config.AutoRestart, []string{"#r", "#-restart"}, true, usageFn("--restart on the daemon has been deprecated in favor of --restart policies on docker run")) + cmd.StringVar(&config.GraphDriver, []string{"s", "-storage-driver"}, "", usageFn("Storage driver to use")) + cmd.StringVar(&config.ExecDriver, []string{"e", "-exec-driver"}, defaultExec, usageFn("Exec driver to use")) + cmd.IntVar(&config.Mtu, []string{"#mtu", "-mtu"}, 0, usageFn("Set the containers network MTU")) + cmd.BoolVar(&config.EnableCors, []string{"#api-enable-cors", "#-api-enable-cors"}, false, usageFn("Enable CORS headers in the remote API, this is deprecated by --api-cors-header")) + cmd.StringVar(&config.CorsHeaders, []string{"-api-cors-header"}, "", usageFn("Set CORS headers in the remote API")) // FIXME: why the inconsistency between "hosts" and "sockets"? - opts.IPListVar(&config.Dns, []string{"#dns", "-dns"}, "DNS server to use") - opts.DNSSearchListVar(&config.DnsSearch, []string{"-dns-search"}, "DNS search domains to use") - opts.LabelListVar(&config.Labels, []string{"-label"}, "Set key=value labels to the daemon") - flag.StringVar(&config.LogConfig.Type, []string{"-log-driver"}, "json-file", "Default driver for container logs") - opts.LogOptsVar(config.LogConfig.Config, []string{"-log-opt"}, "Set log driver options") + cmd.Var(opts.NewListOptsRef(&config.Dns, opts.ValidateIPAddress), []string{"#dns", "-dns"}, usageFn("DNS server to use")) + cmd.Var(opts.NewListOptsRef(&config.DnsSearch, opts.ValidateDNSSearch), []string{"-dns-search"}, usageFn("DNS search domains to use")) + cmd.Var(opts.NewListOptsRef(&config.Labels, opts.ValidateLabel), []string{"-label"}, usageFn("Set key=value labels to the daemon")) + cmd.StringVar(&config.LogConfig.Type, []string{"-log-driver"}, "json-file", usageFn("Default driver for container logs")) + cmd.Var(opts.NewMapOpts(config.LogConfig.Config, nil), []string{"-log-opt"}, usageFn("Set log driver options")) } diff --git a/daemon/config_experimental.go b/daemon/config_experimental.go index 55704d00dc..b3a1d3fbc7 100644 --- a/daemon/config_experimental.go +++ b/daemon/config_experimental.go @@ -4,7 +4,7 @@ package daemon import flag "github.com/docker/docker/pkg/mflag" -func (config *Config) attachExperimentalFlags() { - flag.StringVar(&config.DefaultNetwork, []string{"-default-network"}, "", "Set default network") - flag.StringVar(&config.NetworkKVStore, []string{"-kv-store"}, "", "Set KV Store configuration") +func (config *Config) attachExperimentalFlags(cmd *flag.FlagSet, usageFn func(string) string) { + cmd.StringVar(&config.DefaultNetwork, []string{"-default-network"}, "", usageFn("Set default network")) + cmd.StringVar(&config.NetworkKVStore, []string{"-kv-store"}, "", usageFn("Set KV Store configuration")) } diff --git a/daemon/config_linux.go b/daemon/config_linux.go index e1095e078c..783637ea6d 100644 --- a/daemon/config_linux.go +++ b/daemon/config_linux.go @@ -49,27 +49,28 @@ type bridgeConfig struct { // the current process. // Subsequent calls to `flag.Parse` will populate config with values parsed // from the command-line. -func (config *Config) InstallFlags() { +func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) string) { // First handle install flags which are consistent cross-platform - config.InstallCommonFlags() + config.InstallCommonFlags(cmd, usageFn) // Then platform-specific install flags - flag.BoolVar(&config.EnableSelinuxSupport, []string{"-selinux-enabled"}, false, "Enable selinux support") - flag.StringVar(&config.SocketGroup, []string{"G", "-group"}, "docker", "Group for the unix socket") + cmd.BoolVar(&config.EnableSelinuxSupport, []string{"-selinux-enabled"}, false, usageFn("Enable selinux support")) + cmd.StringVar(&config.SocketGroup, []string{"G", "-group"}, "docker", usageFn("Group for the unix socket")) config.Ulimits = make(map[string]*ulimit.Ulimit) - opts.UlimitMapVar(config.Ulimits, []string{"-default-ulimit"}, "Set default ulimits for containers") - flag.BoolVar(&config.Bridge.EnableIPTables, []string{"#iptables", "-iptables"}, true, "Enable addition of iptables rules") - flag.BoolVar(&config.Bridge.EnableIPForward, []string{"#ip-forward", "-ip-forward"}, true, "Enable net.ipv4.ip_forward") - flag.BoolVar(&config.Bridge.EnableIPMasq, []string{"-ip-masq"}, true, "Enable IP masquerading") - flag.BoolVar(&config.Bridge.EnableIPv6, []string{"-ipv6"}, false, "Enable IPv6 networking") - flag.StringVar(&config.Bridge.IP, []string{"#bip", "-bip"}, "", "Specify network bridge IP") - flag.StringVar(&config.Bridge.Iface, []string{"b", "-bridge"}, "", "Attach containers to a network bridge") - flag.StringVar(&config.Bridge.FixedCIDR, []string{"-fixed-cidr"}, "", "IPv4 subnet for fixed IPs") - flag.StringVar(&config.Bridge.FixedCIDRv6, []string{"-fixed-cidr-v6"}, "", "IPv6 subnet for fixed IPs") - opts.IPVar(&config.Bridge.DefaultGatewayIPv4, []string{"-default-gateway"}, "", "Container default gateway IPv4 address") - opts.IPVar(&config.Bridge.DefaultGatewayIPv6, []string{"-default-gateway-v6"}, "", "Container default gateway IPv6 address") - flag.BoolVar(&config.Bridge.InterContainerCommunication, []string{"#icc", "-icc"}, true, "Enable inter-container communication") - opts.IPVar(&config.Bridge.DefaultIP, []string{"#ip", "-ip"}, "0.0.0.0", "Default IP when binding container ports") - flag.BoolVar(&config.Bridge.EnableUserlandProxy, []string{"-userland-proxy"}, true, "Use userland proxy for loopback traffic") - config.attachExperimentalFlags() + cmd.Var(opts.NewUlimitOpt(&config.Ulimits), []string{"-default-ulimit"}, usageFn("Set default ulimits for containers")) + cmd.BoolVar(&config.Bridge.EnableIPTables, []string{"#iptables", "-iptables"}, true, usageFn("Enable addition of iptables rules")) + cmd.BoolVar(&config.Bridge.EnableIPForward, []string{"#ip-forward", "-ip-forward"}, true, usageFn("Enable net.ipv4.ip_forward")) + cmd.BoolVar(&config.Bridge.EnableIPMasq, []string{"-ip-masq"}, true, usageFn("Enable IP masquerading")) + cmd.BoolVar(&config.Bridge.EnableIPv6, []string{"-ipv6"}, false, usageFn("Enable IPv6 networking")) + cmd.StringVar(&config.Bridge.IP, []string{"#bip", "-bip"}, "", usageFn("Specify network bridge IP")) + cmd.StringVar(&config.Bridge.Iface, []string{"b", "-bridge"}, "", usageFn("Attach containers to a network bridge")) + cmd.StringVar(&config.Bridge.FixedCIDR, []string{"-fixed-cidr"}, "", usageFn("IPv4 subnet for fixed IPs")) + cmd.StringVar(&config.Bridge.FixedCIDRv6, []string{"-fixed-cidr-v6"}, "", usageFn("IPv6 subnet for fixed IPs")) + cmd.Var(opts.NewIpOpt(&config.Bridge.DefaultGatewayIPv4, ""), []string{"-default-gateway"}, usageFn("Container default gateway IPv4 address")) + cmd.Var(opts.NewIpOpt(&config.Bridge.DefaultGatewayIPv6, ""), []string{"-default-gateway-v6"}, usageFn("Container default gateway IPv6 address")) + cmd.BoolVar(&config.Bridge.InterContainerCommunication, []string{"#icc", "-icc"}, true, usageFn("Enable inter-container communication")) + cmd.Var(opts.NewIpOpt(&config.Bridge.DefaultIP, "0.0.0.0"), []string{"#ip", "-ip"}, usageFn("Default IP when binding container ports")) + cmd.BoolVar(&config.Bridge.EnableUserlandProxy, []string{"-userland-proxy"}, true, usageFn("Use userland proxy for loopback traffic")) + + config.attachExperimentalFlags(cmd, usageFn) } diff --git a/daemon/config_stub.go b/daemon/config_stub.go index 32d551408c..796e6b6e4e 100644 --- a/daemon/config_stub.go +++ b/daemon/config_stub.go @@ -2,5 +2,7 @@ package daemon -func (config *Config) attachExperimentalFlags() { +import flag "github.com/docker/docker/pkg/mflag" + +func (config *Config) attachExperimentalFlags(cmd *flag.FlagSet, usageFn func(string) string) { } diff --git a/daemon/config_windows.go b/daemon/config_windows.go index 26bb8a7775..d693e285de 100644 --- a/daemon/config_windows.go +++ b/daemon/config_windows.go @@ -32,9 +32,9 @@ type Config struct { // the current process. // Subsequent calls to `flag.Parse` will populate config with values parsed // from the command-line. -func (config *Config) InstallFlags() { +func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) string) { // First handle install flags which are consistent cross-platform - config.InstallCommonFlags() + config.InstallCommonFlags(cmd, usageFn) // Then platform-specific install flags. flag.StringVar(&config.Bridge.VirtualSwitchName, []string{"b", "-bridge"}, "", "Attach containers to a virtual switch") diff --git a/docker/client.go b/docker/client.go index 0e05f7b8ce..8395805b10 100644 --- a/docker/client.go +++ b/docker/client.go @@ -1,11 +1,28 @@ -// +build !daemon - package main import ( - "log" // see gh#8745, client needs to use go log pkg + "path/filepath" + + "github.com/docker/docker/cli" + "github.com/docker/docker/cliconfig" + flag "github.com/docker/docker/pkg/mflag" ) -func mainDaemon() { - log.Fatal("This is a client-only binary - running the Docker daemon is not supported.") +var clientFlags = &cli.ClientFlags{FlagSet: new(flag.FlagSet), Common: commonFlags} + +func init() { + client := clientFlags.FlagSet + client.StringVar(&clientFlags.ConfigDir, []string{"-config"}, cliconfig.ConfigDir(), "Location of client config files") + + clientFlags.PostParse = func() { + clientFlags.Common.PostParse() + + if clientFlags.ConfigDir != "" { + cliconfig.SetConfigDir(clientFlags.ConfigDir) + } + + if clientFlags.Common.TrustKey == "" { + clientFlags.Common.TrustKey = filepath.Join(cliconfig.ConfigDir(), defaultTrustKeyFile) + } + } } diff --git a/docker/common.go b/docker/common.go new file mode 100644 index 0000000000..351257e779 --- /dev/null +++ b/docker/common.go @@ -0,0 +1,101 @@ +package main + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/Sirupsen/logrus" + "github.com/docker/docker/cli" + "github.com/docker/docker/cliconfig" + "github.com/docker/docker/opts" + flag "github.com/docker/docker/pkg/mflag" + "github.com/docker/docker/pkg/tlsconfig" +) + +const ( + defaultTrustKeyFile = "key.json" + defaultCaFile = "ca.pem" + defaultKeyFile = "key.pem" + defaultCertFile = "cert.pem" +) + +var ( + daemonFlags *flag.FlagSet + commonFlags = &cli.CommonFlags{FlagSet: new(flag.FlagSet)} + + dockerCertPath = os.Getenv("DOCKER_CERT_PATH") + dockerTLSVerify = os.Getenv("DOCKER_TLS_VERIFY") != "" +) + +func init() { + if dockerCertPath == "" { + dockerCertPath = cliconfig.ConfigDir() + } + + commonFlags.PostParse = postParseCommon + + cmd := commonFlags.FlagSet + + cmd.BoolVar(&commonFlags.Debug, []string{"D", "-debug"}, false, "Enable debug mode") + cmd.StringVar(&commonFlags.LogLevel, []string{"l", "-log-level"}, "info", "Set the logging level") + cmd.BoolVar(&commonFlags.TLS, []string{"-tls"}, false, "Use TLS; implied by --tlsverify") + cmd.BoolVar(&commonFlags.TLSVerify, []string{"-tlsverify"}, dockerTLSVerify, "Use TLS and verify the remote") + + // TODO use flag flag.String([]string{"i", "-identity"}, "", "Path to libtrust key file") + + var tlsOptions tlsconfig.Options + commonFlags.TLSOptions = &tlsOptions + cmd.StringVar(&tlsOptions.CAFile, []string{"-tlscacert"}, filepath.Join(dockerCertPath, defaultCaFile), "Trust certs signed only by this CA") + cmd.StringVar(&tlsOptions.CertFile, []string{"-tlscert"}, filepath.Join(dockerCertPath, defaultCertFile), "Path to TLS certificate file") + cmd.StringVar(&tlsOptions.KeyFile, []string{"-tlskey"}, filepath.Join(dockerCertPath, defaultKeyFile), "Path to TLS key file") + + cmd.Var(opts.NewListOptsRef(&commonFlags.Hosts, opts.ValidateHost), []string{"H", "-host"}, "Daemon socket(s) to connect to") +} + +func postParseCommon() { + cmd := commonFlags.FlagSet + + if commonFlags.LogLevel != "" { + lvl, err := logrus.ParseLevel(commonFlags.LogLevel) + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to parse logging level: %s\n", commonFlags.LogLevel) + os.Exit(1) + } + logrus.SetLevel(lvl) + } else { + logrus.SetLevel(logrus.InfoLevel) + } + + if commonFlags.Debug { + os.Setenv("DEBUG", "1") + logrus.SetLevel(logrus.DebugLevel) + } + + // Regardless of whether the user sets it to true or false, if they + // specify --tlsverify at all then we need to turn on tls + // TLSVerify can be true even if not set due to DOCKER_TLS_VERIFY env var, so we need to check that here as well + if cmd.IsSet("-tlsverify") || commonFlags.TLSVerify { + commonFlags.TLS = true + } + + if !commonFlags.TLS { + commonFlags.TLSOptions = nil + } else { + tlsOptions := commonFlags.TLSOptions + tlsOptions.InsecureSkipVerify = !commonFlags.TLSVerify + + // Reset CertFile and KeyFile to empty string if the user did not specify + // the respective flags and the respective default files were not found. + if !cmd.IsSet("-tlscert") { + if _, err := os.Stat(tlsOptions.CertFile); os.IsNotExist(err) { + tlsOptions.CertFile = "" + } + } + if !cmd.IsSet("-tlskey") { + if _, err := os.Stat(tlsOptions.KeyFile); os.IsNotExist(err) { + tlsOptions.KeyFile = "" + } + } + } +} diff --git a/docker/daemon.go b/docker/daemon.go index e7e0dfe3f9..e11b98d8fb 100644 --- a/docker/daemon.go +++ b/docker/daemon.go @@ -8,14 +8,18 @@ import ( "io" "os" "path/filepath" + "runtime" + "strings" "time" "github.com/Sirupsen/logrus" apiserver "github.com/docker/docker/api/server" "github.com/docker/docker/autogen/dockerversion" + "github.com/docker/docker/cli" "github.com/docker/docker/cliconfig" "github.com/docker/docker/daemon" "github.com/docker/docker/daemon/logger" + "github.com/docker/docker/opts" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/pidfile" "github.com/docker/docker/pkg/signal" @@ -26,17 +30,63 @@ import ( "github.com/docker/docker/utils" ) +const daemonUsage = " docker daemon [ --help | ... ]\n" + var ( - daemonCfg = &daemon.Config{} - registryCfg = ®istry.Options{} + flDaemon = flag.Bool([]string{"#d", "#-daemon"}, false, "Enable daemon mode (deprecated; use docker daemon)") + daemonCli cli.Handler = NewDaemonCli() ) -func init() { - if daemonCfg.LogConfig.Config == nil { - daemonCfg.LogConfig.Config = make(map[string]string) +// TODO: remove once `-d` is retired +func handleGlobalDaemonFlag() { + // This block makes sure that if the deprecated daemon flag `--daemon` is absent, + // then all daemon-specific flags are absent as well. + if !*flDaemon && daemonFlags != nil { + flag.CommandLine.Visit(func(fl *flag.Flag) { + for _, name := range fl.Names { + name := strings.TrimPrefix(name, "#") + if daemonFlags.Lookup(name) != nil { + // daemon flag was NOT specified, but daemon-specific flags were + // so let's error out + fmt.Fprintf(os.Stderr, "docker: the daemon flag '-%s' must follow the 'docker daemon' command.\n", name) + os.Exit(1) + } + } + }) + } + + if *flDaemon { + if *flHelp { + // We do not show the help output here, instead, we tell the user about the new daemon command, + // because the help output is so long they would not see the warning anyway. + fmt.Fprintln(os.Stderr, "Please use 'docker daemon --help' instead.") + os.Exit(0) + } + daemonCli.(*DaemonCli).CmdDaemon(flag.Args()...) + os.Exit(0) + } +} + +func presentInHelp(usage string) string { return usage } +func absentFromHelp(string) string { return "" } + +// NewDaemonCli returns a pre-configured daemon CLI +func NewDaemonCli() *DaemonCli { + daemonFlags = cli.Subcmd("daemon", nil, "Enable daemon mode", true) + + // TODO(tiborvass): remove InstallFlags? + daemonConfig := new(daemon.Config) + daemonConfig.InstallFlags(daemonFlags, presentInHelp) + daemonConfig.InstallFlags(flag.CommandLine, absentFromHelp) + registryOptions := new(registry.Options) + registryOptions.InstallFlags(daemonFlags, presentInHelp) + registryOptions.InstallFlags(flag.CommandLine, absentFromHelp) + daemonFlags.Require(flag.Exact, 0) + + return &DaemonCli{ + Config: daemonConfig, + registryOptions: registryOptions, } - daemonCfg.InstallFlags() - registryCfg.InstallFlags() } func migrateKey() (err error) { @@ -79,14 +129,56 @@ func migrateKey() (err error) { return nil } -func mainDaemon() { - if utils.ExperimentalBuild() { - logrus.Warn("Running experimental build") +// DaemonCli represents the daemon CLI. +type DaemonCli struct { + *daemon.Config + registryOptions *registry.Options +} + +func getGlobalFlag() (globalFlag *flag.Flag) { + defer func() { + if x := recover(); x != nil { + switch f := x.(type) { + case *flag.Flag: + globalFlag = f + default: + panic(x) + } + } + }() + visitor := func(f *flag.Flag) { panic(f) } + commonFlags.FlagSet.Visit(visitor) + clientFlags.FlagSet.Visit(visitor) + return +} + +// CmdDaemon is the daemon command, called the raw arguments after `docker daemon`. +func (cli *DaemonCli) CmdDaemon(args ...string) error { + if *flDaemon { + // allow legacy forms `docker -D -d` and `docker -d -D` + logrus.Warn("please use 'docker daemon' instead.") + } else if !commonFlags.FlagSet.IsEmpty() || !clientFlags.FlagSet.IsEmpty() { + // deny `docker -D daemon` + illegalFlag := getGlobalFlag() + fmt.Fprintf(os.Stderr, "invalid flag '-%s'.\nSee 'docker daemon --help'.\n", illegalFlag.Names[0]) + os.Exit(1) + } else { + // allow new form `docker daemon -D` + flag.Merge(daemonFlags, commonFlags.FlagSet) } - if flag.NArg() != 0 { - flag.Usage() - return + daemonFlags.ParseFlags(args, true) + commonFlags.PostParse() + + if len(commonFlags.Hosts) == 0 { + commonFlags.Hosts = []string{opts.DefaultHost} + } + if commonFlags.TrustKey == "" { + commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), defaultTrustKeyFile) + } + + if utils.ExperimentalBuild() { + logrus.Warn("Running experimental build") } logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: timeutils.RFC3339NanoFixed}) @@ -95,15 +187,15 @@ func mainDaemon() { logrus.Fatalf("Failed to set umask: %v", err) } - if len(daemonCfg.LogConfig.Config) > 0 { - if err := logger.ValidateLogOpts(daemonCfg.LogConfig.Type, daemonCfg.LogConfig.Config); err != nil { + if len(cli.LogConfig.Config) > 0 { + if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil { logrus.Fatalf("Failed to set log opts: %v", err) } } var pfile *pidfile.PidFile - if daemonCfg.Pidfile != "" { - pf, err := pidfile.New(daemonCfg.Pidfile) + if cli.Pidfile != "" { + pf, err := pidfile.New(cli.Pidfile) if err != nil { logrus.Fatalf("Error starting daemon: %v", err) } @@ -115,21 +207,26 @@ func mainDaemon() { }() } + if cli.LogConfig.Config == nil { + cli.LogConfig.Config = make(map[string]string) + } + serverConfig := &apiserver.ServerConfig{ Logging: true, - EnableCors: daemonCfg.EnableCors, - CorsHeaders: daemonCfg.CorsHeaders, + EnableCors: cli.EnableCors, + CorsHeaders: cli.CorsHeaders, Version: dockerversion.VERSION, } - serverConfig = setPlatformServerConfig(serverConfig, daemonCfg) + serverConfig = setPlatformServerConfig(serverConfig, cli.Config) - if *flTLS { - if *flTLSVerify { - tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert + if commonFlags.TLSOptions != nil { + if !commonFlags.TLSOptions.InsecureSkipVerify { + // server requires and verifies client's certificate + commonFlags.TLSOptions.ClientAuth = tls.RequireAndVerifyClientCert } - tlsConfig, err := tlsconfig.Server(tlsOptions) + tlsConfig, err := tlsconfig.Server(*commonFlags.TLSOptions) if err != nil { - logrus.Fatal(err) + logrus.Fatalf("foobar: %v", err) } serverConfig.TLSConfig = tlsConfig } @@ -141,7 +238,7 @@ func mainDaemon() { // daemon doesn't exit serveAPIWait := make(chan error) go func() { - if err := api.ServeApi(flHosts); err != nil { + if err := api.ServeApi(commonFlags.Hosts); err != nil { logrus.Errorf("ServeAPI error: %v", err) serveAPIWait <- err return @@ -152,10 +249,10 @@ func mainDaemon() { if err := migrateKey(); err != nil { logrus.Fatal(err) } - daemonCfg.TrustKeyPath = *flTrustKey + cli.TrustKeyPath = commonFlags.TrustKey - registryService := registry.NewService(registryCfg) - d, err := daemon.NewDaemon(daemonCfg, registryService) + registryService := registry.NewService(cli.registryOptions) + d, err := daemon.NewDaemon(cli.Config, registryService) if err != nil { if pfile != nil { if err := pfile.Remove(); err != nil { @@ -201,6 +298,7 @@ func mainDaemon() { } logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI) } + return nil } // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case @@ -219,3 +317,11 @@ func shutdownDaemon(d *daemon.Daemon, timeout time.Duration) { logrus.Error("Force shutdown daemon") } } + +func getDaemonConfDir() string { + // TODO: update for Windows daemon + if runtime.GOOS == "windows" { + return cliconfig.ConfigDir() + } + return "/etc/docker" +} diff --git a/docker/daemon_none.go b/docker/daemon_none.go new file mode 100644 index 0000000000..c829cc1ca2 --- /dev/null +++ b/docker/daemon_none.go @@ -0,0 +1,12 @@ +// +build !daemon + +package main + +import "github.com/docker/docker/cli" + +const daemonUsage = "" + +var daemonCli cli.Handler + +// TODO: remove once `-d` is retired +func handleGlobalDaemonFlag() {} diff --git a/docker/docker.go b/docker/docker.go index b589f94440..179b56f633 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -1,31 +1,20 @@ package main import ( - "crypto/tls" "fmt" "os" - "runtime" - "strings" + "sort" "github.com/Sirupsen/logrus" "github.com/docker/docker/api/client" "github.com/docker/docker/autogen/dockerversion" - "github.com/docker/docker/cliconfig" - "github.com/docker/docker/opts" + "github.com/docker/docker/cli" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/reexec" "github.com/docker/docker/pkg/term" - "github.com/docker/docker/pkg/tlsconfig" "github.com/docker/docker/utils" ) -const ( - defaultTrustKeyFile = "key.json" - defaultCaFile = "ca.pem" - defaultKeyFile = "key.pem" - defaultCertFile = "cert.pem" -) - func main() { if reexec.Init() { return @@ -34,116 +23,58 @@ func main() { // Set terminal emulation based on platform as required. stdin, stdout, stderr := term.StdStreams() - initLogging(stderr) + logrus.SetOutput(stderr) + + flag.Merge(flag.CommandLine, clientFlags.FlagSet, commonFlags.FlagSet) + + flag.Usage = func() { + fmt.Fprint(os.Stdout, "Usage: docker [OPTIONS] COMMAND [arg...]\n"+daemonUsage+" docker [ -h | --help | -v | --version ]\n\n") + fmt.Fprint(os.Stdout, "A self-sufficient runtime for containers.\n\nOptions:\n") + + flag.CommandLine.SetOutput(os.Stdout) + flag.PrintDefaults() + + help := "\nCommands:\n" + + // TODO(tiborvass): no need to sort if we ensure dockerCommands is sorted + sort.Sort(byName(dockerCommands)) + + for _, cmd := range dockerCommands { + help += fmt.Sprintf(" %-10.10s%s\n", cmd.name, cmd.description) + } + + help += "\nRun 'docker COMMAND --help' for more information on a command." + fmt.Fprintf(os.Stdout, "%s\n", help) + } flag.Parse() - // FIXME: validate daemon flags here if *flVersion { showVersion() return } - if *flConfigDir != "" { - cliconfig.SetConfigDir(*flConfigDir) - } + clientCli := client.NewDockerCli(stdin, stdout, stderr, clientFlags) + // TODO: remove once `-d` is retired + handleGlobalDaemonFlag() - if *flLogLevel != "" { - lvl, err := logrus.ParseLevel(*flLogLevel) - if err != nil { - fmt.Fprintf(os.Stderr, "Unable to parse logging level: %s\n", *flLogLevel) - os.Exit(1) - } - setLogLevel(lvl) - } else { - setLogLevel(logrus.InfoLevel) - } - - if *flDebug { - os.Setenv("DEBUG", "1") - setLogLevel(logrus.DebugLevel) - } - - if len(flHosts) == 0 { - defaultHost := os.Getenv("DOCKER_HOST") - if defaultHost == "" || *flDaemon { - if runtime.GOOS != "windows" { - // If we do not have a host, default to unix socket - defaultHost = fmt.Sprintf("unix://%s", opts.DefaultUnixSocket) - } else { - // If we do not have a host, default to TCP socket on Windows - defaultHost = fmt.Sprintf("tcp://%s:%d", opts.DefaultHTTPHost, opts.DefaultHTTPPort) - } - } - defaultHost, err := opts.ValidateHost(defaultHost) - if err != nil { - if *flDaemon { - logrus.Fatal(err) - } else { - fmt.Fprint(os.Stderr, err) - } - os.Exit(1) - } - flHosts = append(flHosts, defaultHost) - } - - setDefaultConfFlag(flTrustKey, defaultTrustKeyFile) - - // Regardless of whether the user sets it to true or false, if they - // specify --tlsverify at all then we need to turn on tls - // *flTlsVerify can be true even if not set due to DOCKER_TLS_VERIFY env var, so we need to check that here as well - if flag.IsSet("-tlsverify") || *flTLSVerify { - *flTLS = true - } - - if *flDaemon { - if *flHelp { - flag.Usage() - return - } - mainDaemon() + if *flHelp { + // if global flag --help is present, regardless of what other options and commands there are, + // just print the usage. + flag.Usage() return } - // From here on, we assume we're a client, not a server. - - if len(flHosts) > 1 { - fmt.Fprintf(os.Stderr, "Please specify only one -H") - os.Exit(0) - } - protoAddrParts := strings.SplitN(flHosts[0], "://", 2) - - var tlsConfig *tls.Config - if *flTLS { - tlsOptions.InsecureSkipVerify = !*flTLSVerify - if !flag.IsSet("-tlscert") { - if _, err := os.Stat(tlsOptions.CertFile); os.IsNotExist(err) { - tlsOptions.CertFile = "" - } - } - if !flag.IsSet("-tlskey") { - if _, err := os.Stat(tlsOptions.KeyFile); os.IsNotExist(err) { - tlsOptions.KeyFile = "" - } - } - var err error - tlsConfig, err = tlsconfig.Client(tlsOptions) - if err != nil { - fmt.Fprintln(stderr, err) - os.Exit(1) - } - } - cli := client.NewDockerCli(stdin, stdout, stderr, *flTrustKey, protoAddrParts[0], protoAddrParts[1], tlsConfig) - - if err := cli.Cmd(flag.Args()...); err != nil { - if sterr, ok := err.(client.StatusError); ok { + c := cli.New(clientCli, daemonCli) + if err := c.Run(flag.Args()...); err != nil { + if sterr, ok := err.(cli.StatusError); ok { if sterr.Status != "" { - fmt.Fprintln(cli.Err(), sterr.Status) + fmt.Fprintln(os.Stderr, sterr.Status) os.Exit(1) } os.Exit(sterr.StatusCode) } - fmt.Fprintln(cli.Err(), err) + fmt.Fprintln(os.Stderr, err) os.Exit(1) } } diff --git a/docker/flags.go b/docker/flags.go index 9243b46f4d..34567bb332 100644 --- a/docker/flags.go +++ b/docker/flags.go @@ -1,16 +1,10 @@ package main -import ( - "fmt" - "os" - "path/filepath" - "runtime" - "sort" +import flag "github.com/docker/docker/pkg/mflag" - "github.com/docker/docker/cliconfig" - "github.com/docker/docker/opts" - flag "github.com/docker/docker/pkg/mflag" - "github.com/docker/docker/pkg/tlsconfig" +var ( + flHelp = flag.Bool([]string{"h", "-help"}, false, "Print usage") + flVersion = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit") ) type command struct { @@ -24,118 +18,46 @@ func (a byName) Len() int { return len(a) } func (a byName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a byName) Less(i, j int) bool { return a[i].name < a[j].name } -var ( - dockerCertPath = os.Getenv("DOCKER_CERT_PATH") - dockerTlSVerify = os.Getenv("DOCKER_TLS_VERIFY") != "" - - dockerCommands = []command{ - {"attach", "Attach to a running container"}, - {"build", "Build an image from a Dockerfile"}, - {"commit", "Create a new image from a container's changes"}, - {"cp", "Copy files/folders from a container to a HOSTDIR or to STDOUT"}, - {"create", "Create a new container"}, - {"diff", "Inspect changes on a container's filesystem"}, - {"events", "Get real time events from the server"}, - {"exec", "Run a command in a running container"}, - {"export", "Export a container's filesystem as a tar archive"}, - {"history", "Show the history of an image"}, - {"images", "List images"}, - {"import", "Import the contents from a tarball to create a filesystem image"}, - {"info", "Display system-wide information"}, - {"inspect", "Return low-level information on a container or image"}, - {"kill", "Kill a running container"}, - {"load", "Load an image from a tar archive or STDIN"}, - {"login", "Register or log in to a Docker registry"}, - {"logout", "Log out from a Docker registry"}, - {"logs", "Fetch the logs of a container"}, - {"port", "List port mappings or a specific mapping for the CONTAINER"}, - {"pause", "Pause all processes within a container"}, - {"ps", "List containers"}, - {"pull", "Pull an image or a repository from a registry"}, - {"push", "Push an image or a repository to a registry"}, - {"rename", "Rename a container"}, - {"restart", "Restart a running container"}, - {"rm", "Remove one or more containers"}, - {"rmi", "Remove one or more images"}, - {"run", "Run a command in a new container"}, - {"save", "Save an image(s) to a tar archive"}, - {"search", "Search the Docker Hub for images"}, - {"start", "Start one or more stopped containers"}, - {"stats", "Display a live stream of container(s) resource usage statistics"}, - {"stop", "Stop a running container"}, - {"tag", "Tag an image into a repository"}, - {"top", "Display the running processes of a container"}, - {"unpause", "Unpause all processes within a container"}, - {"version", "Show the Docker version information"}, - {"wait", "Block until a container stops, then print its exit code"}, - } -) - -func init() { - if dockerCertPath == "" { - dockerCertPath = cliconfig.ConfigDir() - } -} - -func getDaemonConfDir() string { - // TODO: update for Windows daemon - if runtime.GOOS == "windows" { - return cliconfig.ConfigDir() - } - return "/etc/docker" -} - -var ( - flConfigDir = flag.String([]string{"-config"}, cliconfig.ConfigDir(), "Location of client config files") - flVersion = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit") - flDaemon = flag.Bool([]string{"d", "-daemon"}, false, "Enable daemon mode") - flDebug = flag.Bool([]string{"D", "-debug"}, false, "Enable debug mode") - flLogLevel = flag.String([]string{"l", "-log-level"}, "info", "Set the logging level") - flTLS = flag.Bool([]string{"-tls"}, false, "Use TLS; implied by --tlsverify") - flHelp = flag.Bool([]string{"h", "-help"}, false, "Print usage") - flTLSVerify = flag.Bool([]string{"-tlsverify"}, dockerTlSVerify, "Use TLS and verify the remote") - - // these are initialized in init() below since their default values depend on dockerCertPath which isn't fully initialized until init() runs - tlsOptions tlsconfig.Options - flTrustKey *string - flHosts []string -) - -func setDefaultConfFlag(flag *string, def string) { - if *flag == "" { - if *flDaemon { - *flag = filepath.Join(getDaemonConfDir(), def) - } else { - *flag = filepath.Join(cliconfig.ConfigDir(), def) - } - } -} - -func init() { - var placeholderTrustKey string - // TODO use flag flag.String([]string{"i", "-identity"}, "", "Path to libtrust key file") - flTrustKey = &placeholderTrustKey - - flag.StringVar(&tlsOptions.CAFile, []string{"-tlscacert"}, filepath.Join(dockerCertPath, defaultCaFile), "Trust certs signed only by this CA") - flag.StringVar(&tlsOptions.CertFile, []string{"-tlscert"}, filepath.Join(dockerCertPath, defaultCertFile), "Path to TLS certificate file") - flag.StringVar(&tlsOptions.KeyFile, []string{"-tlskey"}, filepath.Join(dockerCertPath, defaultKeyFile), "Path to TLS key file") - opts.HostListVar(&flHosts, []string{"H", "-host"}, "Daemon socket(s) to connect to") - - flag.Usage = func() { - fmt.Fprint(os.Stdout, "Usage: docker [OPTIONS] COMMAND [arg...]\n\nA self-sufficient runtime for containers.\n\nOptions:\n") - - flag.CommandLine.SetOutput(os.Stdout) - flag.PrintDefaults() - - help := "\nCommands:\n" - - sort.Sort(byName(dockerCommands)) - - for _, cmd := range dockerCommands { - help += fmt.Sprintf(" %-10.10s%s\n", cmd.name, cmd.description) - } - - help += "\nRun 'docker COMMAND --help' for more information on a command." - fmt.Fprintf(os.Stdout, "%s\n", help) - } +// TODO(tiborvass): do not show 'daemon' on client-only binaries +// and deduplicate description in dockerCommands and cli subcommands +var dockerCommands = []command{ + {"attach", "Attach to a running container"}, + {"build", "Build an image from a Dockerfile"}, + {"commit", "Create a new image from a container's changes"}, + {"cp", "Copy files/folders from a container to a HOSTDIR or to STDOUT"}, + {"create", "Create a new container"}, + {"diff", "Inspect changes on a container's filesystem"}, + {"events", "Get real time events from the server"}, + {"exec", "Run a command in a running container"}, + {"export", "Export a container's filesystem as a tar archive"}, + {"history", "Show the history of an image"}, + {"images", "List images"}, + {"import", "Import the contents from a tarball to create a filesystem image"}, + {"info", "Display system-wide information"}, + {"inspect", "Return low-level information on a container or image"}, + {"kill", "Kill a running container"}, + {"load", "Load an image from a tar archive or STDIN"}, + {"login", "Register or log in to a Docker registry"}, + {"logout", "Log out from a Docker registry"}, + {"logs", "Fetch the logs of a container"}, + {"port", "List port mappings or a specific mapping for the CONTAINER"}, + {"pause", "Pause all processes within a container"}, + {"ps", "List containers"}, + {"pull", "Pull an image or a repository from a registry"}, + {"push", "Push an image or a repository to a registry"}, + {"rename", "Rename a container"}, + {"restart", "Restart a running container"}, + {"rm", "Remove one or more containers"}, + {"rmi", "Remove one or more images"}, + {"run", "Run a command in a new container"}, + {"save", "Save an image(s) to a tar archive"}, + {"search", "Search the Docker Hub for images"}, + {"start", "Start one or more stopped containers"}, + {"stats", "Display a live stream of container(s) resource usage statistics"}, + {"stop", "Stop a running container"}, + {"tag", "Tag an image into a repository"}, + {"top", "Display the running processes of a container"}, + {"unpause", "Unpause all processes within a container"}, + {"version", "Show the Docker version information"}, + {"wait", "Block until a container stops, then print its exit code"}, } diff --git a/docker/log.go b/docker/log.go deleted file mode 100644 index 7b43b56f5f..0000000000 --- a/docker/log.go +++ /dev/null @@ -1,14 +0,0 @@ -package main - -import ( - "github.com/Sirupsen/logrus" - "io" -) - -func setLogLevel(lvl logrus.Level) { - logrus.SetLevel(lvl) -} - -func initLogging(stderr io.Writer) { - logrus.SetOutput(stderr) -} diff --git a/integration-cli/docker_cli_proxy_test.go b/integration-cli/docker_cli_proxy_test.go index 8b55c67d81..e05728d067 100644 --- a/integration-cli/docker_cli_proxy_test.go +++ b/integration-cli/docker_cli_proxy_test.go @@ -43,7 +43,8 @@ func (s *DockerDaemonSuite) TestCliProxyProxyTCPSock(c *check.C) { c.Fatal("could not find ip to connect to") } - if err := s.d.Start("-H", "tcp://"+ip+":2375"); err != nil { + s.d.GlobalFlags = []string{"-H", "tcp://" + ip + ":2375"} + if err := s.d.Start(); err != nil { c.Fatal(err) } diff --git a/opts/hosts_unix.go b/opts/hosts_unix.go new file mode 100644 index 0000000000..a29335e605 --- /dev/null +++ b/opts/hosts_unix.go @@ -0,0 +1,7 @@ +// +build !windows + +package opts + +import "fmt" + +var DefaultHost = fmt.Sprintf("unix://%s", DefaultUnixSocket) diff --git a/opts/hosts_windows.go b/opts/hosts_windows.go new file mode 100644 index 0000000000..55eac2aaca --- /dev/null +++ b/opts/hosts_windows.go @@ -0,0 +1,7 @@ +// +build windows + +package opts + +import "fmt" + +var DefaultHost = fmt.Sprintf("tcp://%s:%d", DefaultHTTPHost, DefaultHTTPPort) diff --git a/opts/opts.go b/opts/opts.go index b739012791..6d1b2f7b99 100644 --- a/opts/opts.go +++ b/opts/opts.go @@ -32,37 +32,37 @@ var ( // ListVar Defines a flag with the specified names and usage, and put the value // list into ListOpts that will hold the values. func ListVar(values *[]string, names []string, usage string) { - flag.Var(newListOptsRef(values, nil), names, usage) + flag.Var(NewListOptsRef(values, nil), names, usage) } // MapVar Defines a flag with the specified names and usage, and put the value // map into MapOpt that will hold the values (key,value). func MapVar(values map[string]string, names []string, usage string) { - flag.Var(newMapOpt(values, nil), names, usage) + flag.Var(NewMapOpts(values, nil), names, usage) } // LogOptsVar Defines a flag with the specified names and usage for --log-opts, // and put the value map into MapOpt that will hold the values (key,value). func LogOptsVar(values map[string]string, names []string, usage string) { - flag.Var(newMapOpt(values, nil), names, usage) + flag.Var(NewMapOpts(values, nil), names, usage) } // HostListVar Defines a flag with the specified names and usage and put the // value into a ListOpts that will hold the values, validating the Host format. func HostListVar(values *[]string, names []string, usage string) { - flag.Var(newListOptsRef(values, ValidateHost), names, usage) + flag.Var(NewListOptsRef(values, ValidateHost), names, usage) } // IPListVar Defines a flag with the specified names and usage and put the // value into a ListOpts that will hold the values, validating the IP format. func IPListVar(values *[]string, names []string, usage string) { - flag.Var(newListOptsRef(values, ValidateIPAddress), names, usage) + flag.Var(NewListOptsRef(values, ValidateIPAddress), names, usage) } // DNSSearchListVar Defines a flag with the specified names and usage and put the // value into a ListOpts that will hold the values, validating the DNS search format. func DNSSearchListVar(values *[]string, names []string, usage string) { - flag.Var(newListOptsRef(values, ValidateDNSSearch), names, usage) + flag.Var(NewListOptsRef(values, ValidateDNSSearch), names, usage) } // IPVar Defines a flag with the specified names and usage for IP and will use @@ -74,12 +74,12 @@ func IPVar(value *net.IP, names []string, defaultValue, usage string) { // LabelListVar Defines a flag with the specified names and usage and put the // value into a ListOpts that will hold the values, validating the label format. func LabelListVar(values *[]string, names []string, usage string) { - flag.Var(newListOptsRef(values, ValidateLabel), names, usage) + flag.Var(NewListOptsRef(values, ValidateLabel), names, usage) } // UlimitMapVar Defines a flag with the specified names and usage for --ulimit, // and put the value map into a UlimitOpt that will hold the values. -func UlimitMapVar(values map[string]*ulimit.Ulimit, names []string, usage string) { +func UlimitMapVar(values *map[string]*ulimit.Ulimit, names []string, usage string) { flag.Var(NewUlimitOpt(values), names, usage) } @@ -92,10 +92,10 @@ type ListOpts struct { // NewListOpts Create a new ListOpts with the specified validator. func NewListOpts(validator ValidatorFctType) ListOpts { var values []string - return *newListOptsRef(&values, validator) + return *NewListOptsRef(&values, validator) } -func newListOptsRef(values *[]string, validator ValidatorFctType) *ListOpts { +func NewListOptsRef(values *[]string, validator ValidatorFctType) *ListOpts { return &ListOpts{ values: values, validator: validator, @@ -191,7 +191,10 @@ func (opts *MapOpts) String() string { return fmt.Sprintf("%v", map[string]string((opts.values))) } -func newMapOpt(values map[string]string, validator ValidatorFctType) *MapOpts { +func NewMapOpts(values map[string]string, validator ValidatorFctType) *MapOpts { + if values == nil { + values = make(map[string]string) + } return &MapOpts{ values: values, validator: validator, diff --git a/opts/opts_test.go b/opts/opts_test.go index 3e639c1fa0..f08df30be6 100644 --- a/opts/opts_test.go +++ b/opts/opts_test.go @@ -32,7 +32,7 @@ func TestValidateIPAddress(t *testing.T) { func TestMapOpts(t *testing.T) { tmpMap := make(map[string]string) - o := newMapOpt(tmpMap, logOptsValidator) + o := NewMapOpts(tmpMap, logOptsValidator) o.Set("max-size=1") if o.String() != "map[max-size:1]" { t.Errorf("%s != [map[max-size:1]", o.String()) diff --git a/opts/ulimit.go b/opts/ulimit.go index 361eadf220..f8d34365f5 100644 --- a/opts/ulimit.go +++ b/opts/ulimit.go @@ -7,10 +7,13 @@ import ( ) type UlimitOpt struct { - values map[string]*ulimit.Ulimit + values *map[string]*ulimit.Ulimit } -func NewUlimitOpt(ref map[string]*ulimit.Ulimit) *UlimitOpt { +func NewUlimitOpt(ref *map[string]*ulimit.Ulimit) *UlimitOpt { + if ref == nil { + ref = &map[string]*ulimit.Ulimit{} + } return &UlimitOpt{ref} } @@ -20,14 +23,14 @@ func (o *UlimitOpt) Set(val string) error { return err } - o.values[l.Name] = l + (*o.values)[l.Name] = l return nil } func (o *UlimitOpt) String() string { var out []string - for _, v := range o.values { + for _, v := range *o.values { out = append(out, v.String()) } @@ -36,7 +39,7 @@ func (o *UlimitOpt) String() string { func (o *UlimitOpt) GetList() []*ulimit.Ulimit { var ulimits []*ulimit.Ulimit - for _, v := range o.values { + for _, v := range *o.values { ulimits = append(ulimits, v) } diff --git a/pkg/mflag/flag.go b/pkg/mflag/flag.go index 9626a2f6b7..1c068df523 100644 --- a/pkg/mflag/flag.go +++ b/pkg/mflag/flag.go @@ -526,7 +526,7 @@ func (f *FlagSet) PrintDefaults() { names = append(names, name) } } - if len(names) > 0 { + if len(names) > 0 && len(flag.Usage) > 0 { val := flag.DefValue if home != "" && strings.HasPrefix(val, home) { @@ -1143,3 +1143,53 @@ func (f *FlagSet) Init(name string, errorHandling ErrorHandling) { f.name = name f.errorHandling = errorHandling } + +type mergeVal struct { + Value + key string + fset *FlagSet +} + +func (v mergeVal) Set(s string) error { + return v.fset.Set(v.key, s) +} + +func (v mergeVal) IsBoolFlag() bool { + if b, ok := v.Value.(boolFlag); ok { + return b.IsBoolFlag() + } + return false +} + +func Merge(dest *FlagSet, flagsets ...*FlagSet) error { + for _, fset := range flagsets { + for k, f := range fset.formal { + if _, ok := dest.formal[k]; ok { + var err error + if fset.name == "" { + err = fmt.Errorf("flag redefined: %s", k) + } else { + err = fmt.Errorf("%s flag redefined: %s", fset.name, k) + } + fmt.Fprintln(fset.Out(), err.Error()) + // Happens only if flags are declared with identical names + switch dest.errorHandling { + case ContinueOnError: + return err + case ExitOnError: + os.Exit(2) + case PanicOnError: + panic(err) + } + } + newF := *f + newF.Value = mergeVal{f.Value, k, fset} + dest.formal[k] = &newF + } + } + return nil +} + +func (f *FlagSet) IsEmpty() bool { + return len(f.actual) == 0 +} diff --git a/pkg/tlsconfig/config.go b/pkg/tlsconfig/config.go index aee2132c2b..88f768ae24 100644 --- a/pkg/tlsconfig/config.go +++ b/pkg/tlsconfig/config.go @@ -17,11 +17,18 @@ import ( // Options represents the information needed to create client and server TLS configurations. type Options struct { + CAFile string + + // If either CertFile or KeyFile is empty, Client() will not load them + // preventing the client from authenticating to the server. + // However, Server() requires them and will error out if they are empty. + CertFile string + KeyFile string + + // client-only option InsecureSkipVerify bool - ClientAuth tls.ClientAuthType - CAFile string - CertFile string - KeyFile string + // server-only option + ClientAuth tls.ClientAuthType } // Extra (server-side) accepted CBC cipher suites - will phase out in the future diff --git a/registry/config.go b/registry/config.go index 333f1c46c2..a1dc3aba7a 100644 --- a/registry/config.go +++ b/registry/config.go @@ -43,11 +43,11 @@ var ( // InstallFlags adds command-line options to the top-level flag parser for // the current process. -func (options *Options) InstallFlags() { +func (options *Options) InstallFlags(cmd *flag.FlagSet, usageFn func(string) string) { options.Mirrors = opts.NewListOpts(ValidateMirror) - flag.Var(&options.Mirrors, []string{"-registry-mirror"}, "Preferred Docker registry mirror") + cmd.Var(&options.Mirrors, []string{"-registry-mirror"}, usageFn("Preferred Docker registry mirror")) options.InsecureRegistries = opts.NewListOpts(ValidateIndexName) - flag.Var(&options.InsecureRegistries, []string{"-insecure-registry"}, "Enable insecure registry communication") + cmd.Var(&options.InsecureRegistries, []string{"-insecure-registry"}, usageFn("Enable insecure registry communication")) } type netIPNet net.IPNet diff --git a/runconfig/parse.go b/runconfig/parse.go index f32936daf3..c83d5bea17 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -9,7 +9,6 @@ import ( flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/nat" "github.com/docker/docker/pkg/parsers" - "github.com/docker/docker/pkg/ulimit" "github.com/docker/docker/pkg/units" ) @@ -48,8 +47,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe flLabels = opts.NewListOpts(opts.ValidateEnv) flDevices = opts.NewListOpts(opts.ValidateDevice) - ulimits = make(map[string]*ulimit.Ulimit) - flUlimits = opts.NewUlimitOpt(ulimits) + flUlimits = opts.NewUlimitOpt(nil) flPublish = opts.NewListOpts(nil) flExpose = opts.NewListOpts(nil) From e7fc632147e40f5a243158e8378082c51a3d952e Mon Sep 17 00:00:00 2001 From: Shishir Mahajan Date: Fri, 1 May 2015 10:00:43 -0400 Subject: [PATCH 2/3] Add and modify tests for legacy and new daemon invokations Signed-off-by: Shishir Mahajan Signed-off-by: Tibor Vass --- integration-cli/docker_cli_build_test.go | 2 +- integration-cli/docker_cli_daemon_test.go | 118 +++++++++++++++++++--- integration-cli/docker_cli_help_test.go | 38 ++++--- integration-cli/docker_cli_proxy_test.go | 3 +- integration-cli/docker_cli_run_test.go | 11 ++ integration-cli/docker_utils.go | 39 ++++--- opts/ulimit_test.go | 2 +- 7 files changed, 166 insertions(+), 47 deletions(-) diff --git a/integration-cli/docker_cli_build_test.go b/integration-cli/docker_cli_build_test.go index 232bf7c0e5..91babea135 100644 --- a/integration-cli/docker_cli_build_test.go +++ b/integration-cli/docker_cli_build_test.go @@ -79,7 +79,7 @@ func (s *DockerSuite) TestBuildShCmdJSONEntrypoint(c *check.C) { out, _ := dockerCmd(c, "run", "--rm", name) if strings.TrimSpace(out) != "/bin/sh -c echo test" { - c.Fatal("CMD did not contain /bin/sh -c") + c.Fatalf("CMD did not contain /bin/sh -c : %s", out) } } diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index 9705899978..3cfabd9c1c 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -324,6 +324,100 @@ func (s *DockerDaemonSuite) TestDaemonLogLevelWrong(c *check.C) { c.Assert(s.d.Start("--log-level=bogus"), check.NotNil, check.Commentf("Daemon shouldn't start with wrong log level")) } +func (s *DockerSuite) TestDaemonStartWithBackwardCompatibility(c *check.C) { + + var validCommandArgs = [][]string{ + {"--selinux-enabled", "-l", "info"}, + {"--insecure-registry", "daemon"}, + } + + var invalidCommandArgs = [][]string{ + {"--selinux-enabled", "--storage-opt"}, + {"-D", "-b"}, + {"--config", "/tmp"}, + } + + for _, args := range validCommandArgs { + d := NewDaemon(c) + d.Command = "--daemon" + if err := d.Start(args...); err != nil { + c.Fatalf("Daemon should have started successfully with --daemon %v: %v", args, err) + } + d.Stop() + } + + for _, args := range invalidCommandArgs { + d := NewDaemon(c) + if err := d.Start(args...); err == nil { + d.Stop() + c.Fatalf("Daemon should have failed to start with %v", args) + } + } +} + +func (s *DockerSuite) TestDaemonStartWithDaemonCommand(c *check.C) { + + type kind int + + const ( + common kind = iota + daemon + ) + + var flags = []map[kind][]string{ + {common: {"-l", "info"}, daemon: {"--selinux-enabled"}}, + {common: {"-D"}, daemon: {"--selinux-enabled", "-r"}}, + {common: {"-D"}, daemon: {"--restart"}}, + {common: {"--debug"}, daemon: {"--log-driver=json-file", "--log-opt=max-size=1k"}}, + } + + var invalidGlobalFlags = [][]string{ + //Invalid because you cannot pass daemon flags as global flags. + {"--selinux-enabled", "-l", "info"}, + {"-D", "-r"}, + {"--config", "/tmp"}, + } + + // `docker daemon -l info --selinux-enabled` + // should NOT error out + for _, f := range flags { + d := NewDaemon(c) + args := append(f[common], f[daemon]...) + if err := d.Start(args...); err != nil { + c.Fatalf("Daemon should have started successfully with %v: %v", args, err) + } + d.Stop() + } + + // `docker -l info daemon --selinux-enabled` + // should error out + for _, f := range flags { + d := NewDaemon(c) + d.GlobalFlags = f[common] + if err := d.Start(f[daemon]...); err == nil { + d.Stop() + c.Fatalf("Daemon should have failed to start with docker %v daemon %v", d.GlobalFlags, f[daemon]) + } + } + + for _, f := range invalidGlobalFlags { + cmd := exec.Command(dockerBinary, append(f, "daemon")...) + errch := make(chan error) + var err error + go func() { + errch <- cmd.Run() + }() + select { + case <-time.After(time.Second): + cmd.Process.Kill() + case err = <-errch: + } + if err == nil { + c.Fatalf("Daemon should have failed to start with docker %v daemon", f) + } + } +} + func (s *DockerDaemonSuite) TestDaemonLogLevelDebug(c *check.C) { if err := s.d.Start("--log-level=debug"); err != nil { c.Fatal(err) @@ -382,7 +476,7 @@ func (s *DockerDaemonSuite) TestDaemonAllocatesListeningPort(c *check.C) { {"localhost", "127.0.0.1", "1235"}, } - cmdArgs := []string{} + cmdArgs := make([]string, 0, len(listeningPorts)*2) for _, hostDirective := range listeningPorts { cmdArgs = append(cmdArgs, "--host", fmt.Sprintf("tcp://%s:%s", hostDirective[0], hostDirective[2])) } @@ -798,8 +892,7 @@ func (s *DockerDaemonSuite) TestDaemonLinksIpTablesRulesWhenLinkAndUnlink(c *che c.Assert(err, check.IsNil, check.Commentf(out)) defer deleteInterface(c, bridgeName) - args := []string{"--bridge", bridgeName, "--icc=false"} - err = s.d.StartWithBusybox(args...) + err = s.d.StartWithBusybox("--bridge", bridgeName, "--icc=false") c.Assert(err, check.IsNil) defer s.d.Restart() @@ -1210,7 +1303,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartKillWait(c *check.C) { // TestHttpsInfo connects via two-way authenticated HTTPS to the info endpoint func (s *DockerDaemonSuite) TestHttpsInfo(c *check.C) { const ( - testDaemonHTTPSAddr = "localhost:4271" + testDaemonHTTPSAddr = "tcp://localhost:4271" ) if err := s.d.Start("--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-cert.pem", @@ -1218,9 +1311,7 @@ func (s *DockerDaemonSuite) TestHttpsInfo(c *check.C) { c.Fatalf("Could not start daemon with busybox: %v", err) } - //force tcp protocol - host := fmt.Sprintf("tcp://%s", testDaemonHTTPSAddr) - daemonArgs := []string{"--host", host, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-cert.pem", "--tlskey", "fixtures/https/client-key.pem"} + daemonArgs := []string{"--host", testDaemonHTTPSAddr, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-cert.pem", "--tlskey", "fixtures/https/client-key.pem"} out, err := s.d.CmdWithArgs(daemonArgs, "info") if err != nil { c.Fatalf("Error Occurred: %s and output: %s", err, out) @@ -1232,16 +1323,15 @@ func (s *DockerDaemonSuite) TestHttpsInfo(c *check.C) { func (s *DockerDaemonSuite) TestHttpsInfoRogueCert(c *check.C) { const ( errBadCertificate = "remote error: bad certificate" - testDaemonHTTPSAddr = "localhost:4271" + testDaemonHTTPSAddr = "tcp://localhost:4271" ) + if err := s.d.Start("--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-cert.pem", "--tlskey", "fixtures/https/server-key.pem", "-H", testDaemonHTTPSAddr); err != nil { c.Fatalf("Could not start daemon with busybox: %v", err) } - //force tcp protocol - host := fmt.Sprintf("tcp://%s", testDaemonHTTPSAddr) - daemonArgs := []string{"--host", host, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-rogue-cert.pem", "--tlskey", "fixtures/https/client-rogue-key.pem"} + daemonArgs := []string{"--host", testDaemonHTTPSAddr, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-rogue-cert.pem", "--tlskey", "fixtures/https/client-rogue-key.pem"} out, err := s.d.CmdWithArgs(daemonArgs, "info") if err == nil || !strings.Contains(out, errBadCertificate) { c.Fatalf("Expected err: %s, got instead: %s and output: %s", errBadCertificate, err, out) @@ -1253,16 +1343,14 @@ func (s *DockerDaemonSuite) TestHttpsInfoRogueCert(c *check.C) { func (s *DockerDaemonSuite) TestHttpsInfoRogueServerCert(c *check.C) { const ( errCaUnknown = "x509: certificate signed by unknown authority" - testDaemonRogueHTTPSAddr = "localhost:4272" + testDaemonRogueHTTPSAddr = "tcp://localhost:4272" ) if err := s.d.Start("--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-rogue-cert.pem", "--tlskey", "fixtures/https/server-rogue-key.pem", "-H", testDaemonRogueHTTPSAddr); err != nil { c.Fatalf("Could not start daemon with busybox: %v", err) } - //force tcp protocol - host := fmt.Sprintf("tcp://%s", testDaemonRogueHTTPSAddr) - daemonArgs := []string{"--host", host, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-rogue-cert.pem", "--tlskey", "fixtures/https/client-rogue-key.pem"} + daemonArgs := []string{"--host", testDaemonRogueHTTPSAddr, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-rogue-cert.pem", "--tlskey", "fixtures/https/client-rogue-key.pem"} out, err := s.d.CmdWithArgs(daemonArgs, "info") if err == nil || !strings.Contains(out, errCaUnknown) { c.Fatalf("Expected err: %s, got instead: %s and output: %s", errCaUnknown, err, out) diff --git a/integration-cli/docker_cli_help_test.go b/integration-cli/docker_cli_help_test.go index ce9f54e69a..789132834d 100644 --- a/integration-cli/docker_cli_help_test.go +++ b/integration-cli/docker_cli_help_test.go @@ -89,10 +89,18 @@ func (s *DockerSuite) TestHelpTextVerify(c *check.C) { c.Fatalf("Missing 'Commands:' in:\n%s", out) } - // Grab all chars starting at "Commands:" - // Skip first line, its "Commands:" cmds := []string{} - for _, cmd := range strings.Split(out[i:], "\n")[1:] { + // Grab all chars starting at "Commands:" + helpOut := strings.Split(out[i:], "\n") + // First line is just "Commands:" + if isLocalDaemon { + // Replace first line with "daemon" command since it's not part of the list of commands. + helpOut[0] = " daemon" + } else { + // Skip first line + helpOut = helpOut[1:] + } + for _, cmd := range helpOut { var stderr string // Stop on blank line or non-idented line @@ -192,9 +200,10 @@ func (s *DockerSuite) TestHelpTextVerify(c *check.C) { // lead to incorrect test result (like false negative). // Whatever the reason, skip trying to run w/o args and // jump to trying with a bogus arg. - skipNoArgs := map[string]string{ - "events": "", - "load": "", + skipNoArgs := map[string]struct{}{ + "daemon": {}, + "events": {}, + "load": {}, } ec = 0 @@ -230,6 +239,9 @@ func (s *DockerSuite) TestHelpTextVerify(c *check.C) { } expected := 39 + if isLocalDaemon { + expected++ // for the daemon command + } if len(cmds) != expected { c.Fatalf("Wrong # of cmds(%d), it should be: %d\nThe list:\n%q", len(cmds), expected, cmds) @@ -246,7 +258,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) { cmd := exec.Command(dockerBinary) stdout, stderr, ec, err := runCommandWithStdoutStderr(cmd) if len(stdout) == 0 || len(stderr) != 0 || ec != 0 || err != nil { - c.Fatalf("Bad results from 'docker'\nec:%d\nstdout:%s\nstderr:%s\nerr:%q", ec, stdout, stderr, err) + c.Fatalf("Bad results from 'docker'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err) } // Be really pick if strings.HasSuffix(stdout, "\n\n") { @@ -257,7 +269,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) { cmd = exec.Command(dockerBinary, "help") stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd) if len(stdout) == 0 || len(stderr) != 0 || ec != 0 || err != nil { - c.Fatalf("Bad results from 'docker help'\nec:%d\nstdout:%s\nstderr:%s\nerr:%q", ec, stdout, stderr, err) + c.Fatalf("Bad results from 'docker help'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err) } // Be really pick if strings.HasSuffix(stdout, "\n\n") { @@ -268,7 +280,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) { cmd = exec.Command(dockerBinary, "--help") stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd) if len(stdout) == 0 || len(stderr) != 0 || ec != 0 || err != nil { - c.Fatalf("Bad results from 'docker --help'\nec:%d\nstdout:%s\nstderr:%s\nerr:%q", ec, stdout, stderr, err) + c.Fatalf("Bad results from 'docker --help'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err) } // Be really pick if strings.HasSuffix(stdout, "\n\n") { @@ -280,7 +292,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) { cmd = exec.Command(dockerBinary, "inspect", "busybox") stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd) if len(stdout) == 0 || len(stderr) != 0 || ec != 0 || err != nil { - c.Fatalf("Bad results from 'docker inspect busybox'\nec:%d\nstdout:%s\nstderr:%s\nerr:%q", ec, stdout, stderr, err) + c.Fatalf("Bad results from 'docker inspect busybox'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err) } // Be really pick if strings.HasSuffix(stdout, "\n\n") { @@ -292,7 +304,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) { cmd = exec.Command(dockerBinary, "rm") stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd) if len(stdout) != 0 || len(stderr) == 0 || ec == 0 || err == nil { - c.Fatalf("Bad results from 'docker rm'\nec:%d\nstdout:%s\nstderr:%s\nerr:%q", ec, stdout, stderr, err) + c.Fatalf("Bad results from 'docker rm'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err) } // Should not contain full help text but should contain info about // # of args and Usage line @@ -305,7 +317,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) { cmd = exec.Command(dockerBinary, "rm", "NoSuchContainer") stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd) if len(stdout) != 0 || len(stderr) == 0 || ec == 0 || err == nil { - c.Fatalf("Bad results from 'docker rm NoSuchContainer'\nec:%d\nstdout:%s\nstderr:%s\nerr:%q", ec, stdout, stderr, err) + c.Fatalf("Bad results from 'docker rm NoSuchContainer'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err) } // Be really picky if strings.HasSuffix(stderr, "\n\n") { @@ -316,7 +328,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) { cmd = exec.Command(dockerBinary, "BadCmd") stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd) if len(stdout) != 0 || len(stderr) == 0 || ec == 0 || err == nil { - c.Fatalf("Bad results from 'docker BadCmd'\nec:%d\nstdout:%s\nstderr:%s\nerr:%q", ec, stdout, stderr, err) + c.Fatalf("Bad results from 'docker BadCmd'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err) } if stderr != "docker: 'BadCmd' is not a docker command.\nSee 'docker --help'.\n" { c.Fatalf("Unexcepted output for 'docker badCmd'\nstderr:%s", stderr) diff --git a/integration-cli/docker_cli_proxy_test.go b/integration-cli/docker_cli_proxy_test.go index e05728d067..8b55c67d81 100644 --- a/integration-cli/docker_cli_proxy_test.go +++ b/integration-cli/docker_cli_proxy_test.go @@ -43,8 +43,7 @@ func (s *DockerDaemonSuite) TestCliProxyProxyTCPSock(c *check.C) { c.Fatal("could not find ip to connect to") } - s.d.GlobalFlags = []string{"-H", "tcp://" + ip + ":2375"} - if err := s.d.Start(); err != nil { + if err := s.d.Start("-H", "tcp://"+ip+":2375"); err != nil { c.Fatal(err) } diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 5c3b3e3a91..a574b2b139 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -217,6 +217,17 @@ func (s *DockerSuite) TestRunLinksContainerWithContainerId(c *check.C) { } } +// Issue 9677. +func (s *DockerSuite) TestRunWithDaemonFlags(c *check.C) { + out, _, err := dockerCmdWithError(c, "--selinux-enabled", "run", "-i", "-t", "busybox", "true") + if err != nil { + if !strings.Contains(out, "must follow the 'docker daemon' command") && // daemon + !strings.Contains(out, "flag provided but not defined: --selinux-enabled") { // no daemon (client-only) + c.Fatal(err, out) + } + } +} + // Regression test for #4979 func (s *DockerSuite) TestRunWithVolumesFromExited(c *check.C) { out, exitCode := dockerCmd(c, "run", "--name", "test-data", "--volume", "/some/dir", "busybox", "touch", "/some/dir/file") diff --git a/integration-cli/docker_utils.go b/integration-cli/docker_utils.go index b3e4e92584..87c6504343 100644 --- a/integration-cli/docker_utils.go +++ b/integration-cli/docker_utils.go @@ -29,6 +29,12 @@ import ( // Daemon represents a Docker daemon for the testing framework. type Daemon struct { + // Defaults to "daemon" + // Useful to set to --daemon or -d for checking backwards compatability + Command string + GlobalFlags []string + + id string c *check.C logFile *os.File folder string @@ -59,7 +65,8 @@ func NewDaemon(c *check.C) *Daemon { c.Fatal("Please set the DEST environment variable") } - dir := filepath.Join(dest, fmt.Sprintf("d%d", time.Now().UnixNano()%100000000)) + id := fmt.Sprintf("d%d", time.Now().UnixNano()%100000000) + dir := filepath.Join(dest, id) daemonFolder, err := filepath.Abs(dir) if err != nil { c.Fatalf("Could not make %q an absolute path: %v", dir, err) @@ -77,6 +84,8 @@ func NewDaemon(c *check.C) *Daemon { } return &Daemon{ + Command: "daemon", + id: id, c: c, folder: daemonFolder, storageDriver: os.Getenv("DOCKER_GRAPHDRIVER"), @@ -90,22 +99,22 @@ func NewDaemon(c *check.C) *Daemon { func (d *Daemon) Start(arg ...string) error { dockerBinary, err := exec.LookPath(dockerBinary) if err != nil { - d.c.Fatalf("could not find docker binary in $PATH: %v", err) + d.c.Fatalf("[%s] could not find docker binary in $PATH: %v", d.id, err) } - args := []string{ + args := append(d.GlobalFlags, + d.Command, "--host", d.sock(), - "--daemon", "--graph", fmt.Sprintf("%s/graph", d.folder), "--pidfile", fmt.Sprintf("%s/docker.pid", d.folder), fmt.Sprintf("--userland-proxy=%t", d.userlandProxy), - } + ) // If we don't explicitly set the log-level or debug flag(-D) then // turn on debug mode foundIt := false for _, a := range arg { - if strings.Contains(a, "--log-level") || strings.Contains(a, "-D") { + if strings.Contains(a, "--log-level") || strings.Contains(a, "-D") || strings.Contains(a, "--debug") { foundIt = true } } @@ -125,21 +134,21 @@ func (d *Daemon) Start(arg ...string) error { d.logFile, err = os.OpenFile(filepath.Join(d.folder, "docker.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600) if err != nil { - d.c.Fatalf("Could not create %s/docker.log: %v", d.folder, err) + d.c.Fatalf("[%s] Could not create %s/docker.log: %v", d.id, d.folder, err) } d.cmd.Stdout = d.logFile d.cmd.Stderr = d.logFile if err := d.cmd.Start(); err != nil { - return fmt.Errorf("could not start daemon container: %v", err) + return fmt.Errorf("[%s] could not start daemon container: %v", d.id, err) } wait := make(chan error) go func() { wait <- d.cmd.Wait() - d.c.Log("exiting daemon") + d.c.Logf("[%s] exiting daemon", d.id) close(wait) }() @@ -149,14 +158,14 @@ func (d *Daemon) Start(arg ...string) error { // make sure daemon is ready to receive requests startTime := time.Now().Unix() for { - d.c.Log("waiting for daemon to start") + d.c.Logf("[%s] waiting for daemon to start", d.id) if time.Now().Unix()-startTime > 5 { // After 5 seconds, give up - return errors.New("Daemon exited and never started") + return fmt.Errorf("[%s] Daemon exited and never started", d.id) } select { case <-time.After(2 * time.Second): - return errors.New("timeout: daemon does not respond") + return fmt.Errorf("[%s] timeout: daemon does not respond", d.id) case <-tick: c, err := net.Dial("unix", filepath.Join(d.folder, "docker.sock")) if err != nil { @@ -168,7 +177,7 @@ func (d *Daemon) Start(arg ...string) error { req, err := http.NewRequest("GET", "/_ping", nil) if err != nil { - d.c.Fatalf("could not create new request: %v", err) + d.c.Fatalf("[%s] could not create new request: %v", d.id, err) } resp, err := client.Do(req) @@ -176,10 +185,10 @@ func (d *Daemon) Start(arg ...string) error { continue } if resp.StatusCode != http.StatusOK { - d.c.Logf("received status != 200 OK: %s", resp.Status) + d.c.Logf("[%s] received status != 200 OK: %s", d.id, resp.Status) } - d.c.Log("daemon started") + d.c.Logf("[%s] daemon started", d.id) return nil } } diff --git a/opts/ulimit_test.go b/opts/ulimit_test.go index e72be7a813..3845d1ec18 100644 --- a/opts/ulimit_test.go +++ b/opts/ulimit_test.go @@ -11,7 +11,7 @@ func TestUlimitOpt(t *testing.T) { "nofile": {"nofile", 1024, 512}, } - ulimitOpt := NewUlimitOpt(ulimitMap) + ulimitOpt := NewUlimitOpt(&ulimitMap) expected := "[nofile=512:1024]" if ulimitOpt.String() != expected { From e246f1e4eec3290f343627e73a926b940b77b121 Mon Sep 17 00:00:00 2001 From: Tibor Vass Date: Wed, 22 Jul 2015 15:37:17 -0400 Subject: [PATCH 3/3] Update docs to use the new `docker daemon` command Signed-off-by: Tibor Vass --- docs/articles/basics.md | 4 +- docs/articles/configuring.md | 9 ++- docs/articles/https.md | 2 +- docs/articles/networking.md | 4 +- docs/articles/registry_mirror.md | 4 +- docs/articles/systemd.md | 2 +- docs/installation/binaries.md | 2 +- docs/installation/ubuntulinux.md | 2 +- docs/project/set-up-dev-env.md | 4 +- docs/project/test-and-docs.md | 2 +- docs/reference/api/docker_remote_api_v1.20.md | 2 +- docs/reference/commandline/cli.md | 3 + docs/reference/commandline/daemon.md | 59 +++++++++---------- docs/userguide/labels-custom-metadata.md | 2 +- 14 files changed, 50 insertions(+), 51 deletions(-) diff --git a/docs/articles/basics.md b/docs/articles/basics.md index 013ac1bc2a..62cff1de13 100644 --- a/docs/articles/basics.md +++ b/docs/articles/basics.md @@ -103,7 +103,7 @@ when no `-H` was passed in. Run Docker in daemon mode: - $ sudo /docker -H 0.0.0.0:5555 -d & + $ sudo /docker daemon -H 0.0.0.0:5555 & Download an `ubuntu` image: @@ -113,7 +113,7 @@ You can use multiple `-H`, for example, if you want to listen on both TCP and a Unix socket # Run docker in daemon mode - $ sudo /docker -H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock -d & + $ sudo /docker daemon -H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock & # Download an ubuntu image, use default Unix socket $ docker pull ubuntu # OR use the TCP port diff --git a/docs/articles/configuring.md b/docs/articles/configuring.md index 3321573d05..d7ae834d06 100644 --- a/docs/articles/configuring.md +++ b/docs/articles/configuring.md @@ -24,7 +24,7 @@ or `systemd` to manage the `docker` daemon's start and stop. The `docker` daemon can be run directly using the `-d` option. By default it listens on the Unix socket `unix:///var/run/docker.sock` - $ docker -d + $ docker daemon INFO[0000] +job init_networkdriver() INFO[0000] +job serveapi(unix:///var/run/docker.sock) @@ -34,10 +34,9 @@ the Unix socket `unix:///var/run/docker.sock` ### Configuring the docker daemon directly -If you're running the `docker` daemon directly by running `docker -d` instead +If you're running the `docker` daemon directly by running `docker daemon` instead of using a process manager, you can append the configuration options to the `docker` run -command directly. Just like the `-d` option, other options can be passed to the `docker` -daemon to configure it. +command directly. Other options can be passed to the `docker` daemon to configure it. Some of the daemon's options are: @@ -50,7 +49,7 @@ Some of the daemon's options are: Here is a an example of running the `docker` daemon with configuration options: - $ docker -d -D --tls=true --tlscert=/var/docker/server.pem --tlskey=/var/docker/serverkey.pem -H tcp://192.168.59.3:2376 + $ docker daemon -D --tls=true --tlscert=/var/docker/server.pem --tlskey=/var/docker/serverkey.pem -H tcp://192.168.59.3:2376 These options : diff --git a/docs/articles/https.md b/docs/articles/https.md index 7fe1d88c1b..2441629883 100644 --- a/docs/articles/https.md +++ b/docs/articles/https.md @@ -136,7 +136,7 @@ prevent accidental damage: Now you can make the Docker daemon only accept connections from clients providing a certificate trusted by our CA: - $ docker -d --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem \ + $ docker daemon --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem \ -H=0.0.0.0:2376 To be able to connect to Docker and validate its certificate, you now diff --git a/docs/articles/networking.md b/docs/articles/networking.md index d454dc1fd9..94b698a191 100644 --- a/docs/articles/networking.md +++ b/docs/articles/networking.md @@ -503,7 +503,7 @@ To assign globally routable IPv6 addresses to your containers you have to specify an IPv6 subnet to pick the addresses from. Set the IPv6 subnet via the `--fixed-cidr-v6` parameter when starting Docker daemon: - docker -d --ipv6 --fixed-cidr-v6="2001:db8:1::/64" + docker daemon --ipv6 --fixed-cidr-v6="2001:db8:1::/64" The subnet for Docker containers should at least have a size of `/80`. This way an IPv6 address can end with the container's MAC address and you prevent NDP @@ -589,7 +589,7 @@ Let's split up the configurable address range into two subnets `2001:db8::c000/125` and `2001:db8::c008/125`. The first one can be used by the host itself, the latter by Docker: - docker -d --ipv6 --fixed-cidr-v6 2001:db8::c008/125 + docker daemon --ipv6 --fixed-cidr-v6 2001:db8::c008/125 You notice the Docker subnet is within the subnet managed by your router that is connected to `eth0`. This means all devices (containers) with the addresses diff --git a/docs/articles/registry_mirror.md b/docs/articles/registry_mirror.md index 13461d5187..6e309824e2 100644 --- a/docs/articles/registry_mirror.md +++ b/docs/articles/registry_mirror.md @@ -36,11 +36,11 @@ There are two steps to set up and use a local registry mirror. You will need to pass the `--registry-mirror` option to your Docker daemon on startup: - docker --registry-mirror=http:// -d + docker daemon --registry-mirror=http:// For example, if your mirror is serving on `http://10.0.0.2:5000`, you would run: - docker --registry-mirror=http://10.0.0.2:5000 -d + docker daemon --registry-mirror=http://10.0.0.2:5000 **NOTE:** Depending on your local host setup, you may be able to add the diff --git a/docs/articles/systemd.md b/docs/articles/systemd.md index 919ad50daa..7082ca273f 100644 --- a/docs/articles/systemd.md +++ b/docs/articles/systemd.md @@ -70,7 +70,7 @@ In this example, we'll assume that your `docker.service` file looks something li [Service] Type=notify EnvironmentFile=-/etc/sysconfig/docker - ExecStart=/usr/bin/docker -d -H fd:// $OPTIONS + ExecStart=/usr/bin/docker daemon -H fd:// $OPTIONS LimitNOFILE=1048576 LimitNPROC=1048576 diff --git a/docs/installation/binaries.md b/docs/installation/binaries.md index 14bf7c2e74..ed02fb8a46 100644 --- a/docs/installation/binaries.md +++ b/docs/installation/binaries.md @@ -174,7 +174,7 @@ For example: ## Run the Docker daemon # start the docker in daemon mode from the directory you unpacked - $ sudo ./docker -d & + $ sudo ./docker daemon & ## Giving non-root access diff --git a/docs/installation/ubuntulinux.md b/docs/installation/ubuntulinux.md index 46cc65bced..7f2e1fa199 100644 --- a/docs/installation/ubuntulinux.md +++ b/docs/installation/ubuntulinux.md @@ -185,7 +185,7 @@ To create the `docker` group and add your user: If this fails with a message similar to this: - Cannot connect to the Docker daemon. Is 'docker -d' running on this host? + Cannot connect to the Docker daemon. Is 'docker daemon' running on this host? Check that the `DOCKER_HOST` environment variable is not set for your shell. If it is, unset it. diff --git a/docs/project/set-up-dev-env.md b/docs/project/set-up-dev-env.md index 06e6c01100..4e711f009e 100644 --- a/docs/project/set-up-dev-env.md +++ b/docs/project/set-up-dev-env.md @@ -285,7 +285,7 @@ with the `make.sh` script. 8. Start a `docker` daemon running inside your container. - root@5f8630b873fe:/go/src/github.com/docker/docker# docker -dD + root@5f8630b873fe:/go/src/github.com/docker/docker# docker daemon -D The `-dD` flag starts the daemon in debug mode. You'll find this useful when debugging your code. @@ -411,7 +411,7 @@ onto the `/go` directory inside the container. * copy the binary inside the development container using `cp bundles/1.5.0-dev/binary/docker /usr/bin` - * start `docker -dD` to launch the Docker daemon inside the container + * start `docker daemon -D` to launch the Docker daemon inside the container * run `docker ps` on local host to get the development container's name * connect to your running container `docker exec -it container_name bash` * use the `docker run hello-world` command to create and run a container diff --git a/docs/project/test-and-docs.md b/docs/project/test-and-docs.md index 7b0a59f322..e58ebd2967 100644 --- a/docs/project/test-and-docs.md +++ b/docs/project/test-and-docs.md @@ -120,7 +120,7 @@ Run the entire test suite on your current repository: PASS coverage: 70.8% of statements ---> Making bundle: test-docker-py (in bundles/1.5.0-dev/test-docker-py) - +++ exec docker --daemon --debug --host unix:///go/src/github.com/docker/docker/bundles/1.5.0-dev/test-docker-py/docker.sock --storage-driver vfs --exec-driver native --pidfile /go/src/github.com/docker/docker/bundles/1.5.0-dev/test-docker-py/docker.pid + +++ exec docker daemon --debug --host unix:///go/src/github.com/docker/docker/bundles/1.5.0-dev/test-docker-py/docker.sock --storage-driver vfs --exec-driver native --pidfile /go/src/github.com/docker/docker/bundles/1.5.0-dev/test-docker-py/docker.pid ................................................................. ---------------------------------------------------------------------- Ran 65 tests in 89.266s diff --git a/docs/reference/api/docker_remote_api_v1.20.md b/docs/reference/api/docker_remote_api_v1.20.md index 0076639c88..8524f31746 100644 --- a/docs/reference/api/docker_remote_api_v1.20.md +++ b/docs/reference/api/docker_remote_api_v1.20.md @@ -2269,4 +2269,4 @@ To set cross origin requests to the remote api please give values to `--api-cors-header` when running Docker in daemon mode. Set * (asterisk) allows all, default or blank means CORS disabled - $ docker -d -H="192.168.1.9:2375" --api-cors-header="http://foo.bar" + $ docker daemon -H="192.168.1.9:2375" --api-cors-header="http://foo.bar" diff --git a/docs/reference/commandline/cli.md b/docs/reference/commandline/cli.md index c6561cd365..0f8bfedaac 100644 --- a/docs/reference/commandline/cli.md +++ b/docs/reference/commandline/cli.md @@ -19,6 +19,9 @@ or execute `docker help`: $ docker Usage: docker [OPTIONS] COMMAND [arg...] + docker daemon [ --help | ... ] + docker [ -h | --help | -v | --version ] + -H, --host=[]: The socket(s) to bind to in daemon mode, specified using one or more tcp://host:port, unix:///path/to/socket, fd://* or fd://socketfd. A self-sufficient runtime for Linux containers. diff --git a/docs/reference/commandline/daemon.md b/docs/reference/commandline/daemon.md index eccc7fad7a..aec2a0776d 100644 --- a/docs/reference/commandline/daemon.md +++ b/docs/reference/commandline/daemon.md @@ -10,7 +10,7 @@ parent = "smn_cli" # daemon - Usage: docker [OPTIONS] COMMAND [arg...] + Usage: docker daemon [OPTIONS] A self-sufficient runtime for linux containers. @@ -18,9 +18,7 @@ parent = "smn_cli" --api-cors-header="" Set CORS headers in the remote API -b, --bridge="" Attach containers to a network bridge --bip="" Specify network bridge IP - --config=~/.docker Location of client config files -D, --debug=false Enable debug mode - -d, --daemon=false Enable daemon mode --default-gateway="" Container default gateway IPv4 address --default-gateway-v6="" Container default gateway IPv6 address --dns=[] DNS server to use @@ -58,15 +56,14 @@ parent = "smn_cli" --tlskey="~/.docker/key.pem" Path to TLS key file --tlsverify=false Use TLS and verify the remote --userland-proxy=true Use userland proxy for loopback traffic - -v, --version=false Print version information and quit Options with [] may be specified multiple times. The Docker daemon is the persistent process that manages containers. Docker uses the same binary for both the daemon and client. To run the daemon you -provide the `-d` flag. +type `docker daemon`. -To run the daemon with debug output, use `docker -d -D`. +To run the daemon with debug output, use `docker daemon -D`. ## Daemon socket option @@ -94,8 +91,8 @@ communication with the daemon. On Systemd based systems, you can communicate with the daemon via [Systemd socket activation](http://0pointer.de/blog/projects/socket-activation.html), -use `docker -d -H fd://`. Using `fd://` will work perfectly for most setups but -you can also specify individual sockets: `docker -d -H fd://3`. If the +use `docker daemon -H fd://`. Using `fd://` will work perfectly for most setups but +you can also specify individual sockets: `docker daemon -H fd://3`. If the specified socket activated files aren't found, then Docker will exit. You can find examples of using Systemd socket activation with Docker and Systemd in the [Docker source tree](https://github.com/docker/docker/tree/master/contrib/init/systemd/). @@ -104,7 +101,7 @@ You can configure the Docker daemon to listen to multiple sockets at the same time using multiple `-H` options: # listen using the default unix socket, and on 2 specific IP addresses on this host. - docker -d -H unix:///var/run/docker.sock -H tcp://192.168.59.106 -H tcp://10.10.10.2 + docker daemon -H unix:///var/run/docker.sock -H tcp://192.168.59.106 -H tcp://10.10.10.2 The Docker client will honor the `DOCKER_HOST` environment variable to set the `-H` flag for the client. @@ -152,16 +149,16 @@ article explains how to tune your existing setup without the use of options. The `btrfs` driver is very fast for `docker build` - but like `devicemapper` does not share executable memory between devices. Use -`docker -d -s btrfs -g /mnt/btrfs_partition`. +`docker daemon -s btrfs -g /mnt/btrfs_partition`. The `zfs` driver is probably not fast as `btrfs` but has a longer track record on stability. Thanks to `Single Copy ARC` shared blocks between clones will be -cached only once. Use `docker -d -s zfs`. To select a different zfs filesystem +cached only once. Use `docker daemon -s zfs`. To select a different zfs filesystem set `zfs.fsname` option as described in [Storage driver options](#storage-driver-options). The `overlay` is a very fast union filesystem. It is now merged in the main Linux kernel as of [3.18.0](https://lkml.org/lkml/2014/10/26/137). Call -`docker -d -s overlay` to use it. +`docker daemon -s overlay` to use it. > **Note:** > As promising as `overlay` is, the feature is still quite young and should not @@ -196,7 +193,7 @@ options for `zfs` start with `zfs`. Example use: - docker -d --storage-opt dm.thinpooldev=/dev/mapper/thin-pool + docker daemon --storage-opt dm.thinpooldev=/dev/mapper/thin-pool * `dm.basesize` @@ -216,7 +213,7 @@ options for `zfs` start with `zfs`. Example use: - $ docker -d --storage-opt dm.basesize=20G + $ docker daemon --storage-opt dm.basesize=20G * `dm.loopdatasize` @@ -229,7 +226,7 @@ options for `zfs` start with `zfs`. Example use: - $ docker -d --storage-opt dm.loopdatasize=200G + $ docker daemon --storage-opt dm.loopdatasize=200G * `dm.loopmetadatasize` @@ -242,7 +239,7 @@ options for `zfs` start with `zfs`. Example use: - $ docker -d --storage-opt dm.loopmetadatasize=4G + $ docker daemon --storage-opt dm.loopmetadatasize=4G * `dm.fs` @@ -251,7 +248,7 @@ options for `zfs` start with `zfs`. Example use: - $ docker -d --storage-opt dm.fs=xfs + $ docker daemon --storage-opt dm.fs=xfs * `dm.mkfsarg` @@ -259,7 +256,7 @@ options for `zfs` start with `zfs`. Example use: - $ docker -d --storage-opt "dm.mkfsarg=-O ^has_journal" + $ docker daemon --storage-opt "dm.mkfsarg=-O ^has_journal" * `dm.mountopt` @@ -267,7 +264,7 @@ options for `zfs` start with `zfs`. Example use: - $ docker -d --storage-opt dm.mountopt=nodiscard + $ docker daemon --storage-opt dm.mountopt=nodiscard * `dm.datadev` @@ -281,7 +278,7 @@ options for `zfs` start with `zfs`. Example use: - $ docker -d --storage-opt dm.datadev=/dev/sdb1 --storage-opt dm.metadatadev=/dev/sdc1 + $ docker daemon --storage-opt dm.datadev=/dev/sdb1 --storage-opt dm.metadatadev=/dev/sdc1 * `dm.metadatadev` @@ -299,7 +296,7 @@ options for `zfs` start with `zfs`. Example use: - $ docker -d --storage-opt dm.datadev=/dev/sdb1 --storage-opt dm.metadatadev=/dev/sdc1 + $ docker daemon --storage-opt dm.datadev=/dev/sdb1 --storage-opt dm.metadatadev=/dev/sdc1 * `dm.blocksize` @@ -308,7 +305,7 @@ options for `zfs` start with `zfs`. Example use: - $ docker -d --storage-opt dm.blocksize=512K + $ docker daemon --storage-opt dm.blocksize=512K * `dm.blkdiscard` @@ -322,7 +319,7 @@ options for `zfs` start with `zfs`. Example use: - $ docker -d --storage-opt dm.blkdiscard=false + $ docker daemon --storage-opt dm.blkdiscard=false * `dm.override_udev_sync_check` @@ -348,7 +345,7 @@ options for `zfs` start with `zfs`. To allow the `docker` daemon to start, regardless of `udev` sync not being supported, set `dm.override_udev_sync_check` to true: - $ docker -d --storage-opt dm.override_udev_sync_check=true + $ docker daemon --storage-opt dm.override_udev_sync_check=true When this value is `true`, the `devicemapper` continues and simply warns you the errors are happening. @@ -373,7 +370,7 @@ Currently supported options of `zfs`: Example use: - $ docker -d -s zfs --storage-opt zfs.fsname=zroot/docker + $ docker daemon -s zfs --storage-opt zfs.fsname=zroot/docker ## Docker execdriver option @@ -397,17 +394,17 @@ it is not available, the system uses `cgroupfs`. By default, if no option is specified, the execdriver first tries `systemd` and falls back to `cgroupfs`. This example sets the execdriver to `cgroupfs`: - $ sudo docker -d --exec-opt native.cgroupdriver=cgroupfs + $ sudo docker daemon --exec-opt native.cgroupdriver=cgroupfs Setting this option applies to all containers the daemon launches. ## Daemon DNS options To set the DNS server for all Docker containers, use -`docker -d --dns 8.8.8.8`. +`docker daemon --dns 8.8.8.8`. To set the DNS search domain for all Docker containers, use -`docker -d --dns-search example.com`. +`docker daemon --dns-search example.com`. ## Insecure registries @@ -456,7 +453,7 @@ need to be added to your Docker host's configuration: 1. Install the `ca-certificates` package for your distribution 2. Ask your network admin for the proxy's CA certificate and append them to `/etc/pki/tls/certs/ca-bundle.crt` -3. Then start your Docker daemon with `HTTPS_PROXY=http://username:password@proxy:port/ docker -d`. +3. Then start your Docker daemon with `HTTPS_PROXY=http://username:password@proxy:port/ docker daemon`. The `username:` and `password@` are optional - and are only needed if your proxy is set up to require authentication. @@ -486,9 +483,9 @@ Docker supports softlinks for the Docker data directory (`/var/lib/docker`) and for `/var/lib/docker/tmp`. The `DOCKER_TMPDIR` and the data directory can be set like this: - DOCKER_TMPDIR=/mnt/disk2/tmp /usr/local/bin/docker -d -D -g /var/lib/docker -H unix:// > /var/lib/boot2docker/docker.log 2>&1 + DOCKER_TMPDIR=/mnt/disk2/tmp /usr/local/bin/docker daemon -D -g /var/lib/docker -H unix:// > /var/lib/boot2docker/docker.log 2>&1 # or export DOCKER_TMPDIR=/mnt/disk2/tmp - /usr/local/bin/docker -d -D -g /var/lib/docker -H unix:// > /var/lib/boot2docker/docker.log 2>&1 + /usr/local/bin/docker daemon -D -g /var/lib/docker -H unix:// > /var/lib/boot2docker/docker.log 2>&1 diff --git a/docs/userguide/labels-custom-metadata.md b/docs/userguide/labels-custom-metadata.md index 693bfd55bd..2be7f8597f 100644 --- a/docs/userguide/labels-custom-metadata.md +++ b/docs/userguide/labels-custom-metadata.md @@ -164,7 +164,7 @@ List all images with `vendor` `ACME`: ## Daemon labels - docker -d \ + docker daemon \ --dns 8.8.8.8 \ --dns 8.8.4.4 \ -H unix:///var/run/docker.sock \