cli: new daemon command and new cli package

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

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

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

This patch does not break any old but correct usages.

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

Signed-off-by: Tibor Vass <tibor@docker.com>
This commit is contained in:
Tibor Vass 2015-05-05 00:18:28 -04:00
parent 490e78a642
commit 96ce3a194a
67 changed files with 913 additions and 614 deletions

View file

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

View file

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

View file

@ -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 <subcmd-name> [OPTIONS] <synopsis 0>
// docker <subcmd-name> [OPTIONS] <synopsis 1>
// ...
//
// If no undeprecated flags are added to the returned FlagSet, "[OPTIONS]" will
// not be included on the usage synopsis lines. If no synopses are given, only
// one usage synopsis line will be printed with nothing following the
// "[OPTIONS]" section
//
// To see all available subcommands, run "docker --help".
func (cli *DockerCli) Subcmd(name string, synopses []string, description string, exitOnError bool) *flag.FlagSet {
var errorHandling flag.ErrorHandling
if exitOnError {
errorHandling = flag.ExitOnError
} else {
errorHandling = flag.ContinueOnError
}
flags := flag.NewFlagSet(name, errorHandling)
flags.Usage = func() {
flags.ShortUsage()
flags.PrintDefaults()
}
flags.ShortUsage = func() {
options := ""
if flags.FlagCountUndeprecated() > 0 {
options = " [OPTIONS]"
}
if len(synopses) == 0 {
synopses = []string{""}
}
// Allow for multiple command usage synopses.
for i, synopsis := range synopses {
lead := "\t"
if i == 0 {
// First line needs the word 'Usage'.
lead = "Usage:\t"
}
if synopsis != "" {
synopsis = " " + synopsis
}
fmt.Fprintf(flags.Out(), "\n%sdocker %s%s%s", lead, name, options, synopsis)
}
fmt.Fprintf(flags.Out(), "\n\n%s\n", description)
}
return flags
return cli.init()
}
// CheckTtyInput checks if we are trying to attach to a container tty
@ -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
}

View file

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

View file

@ -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 <hannibal@a-team.com>\")")

View file

@ -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{

View file

@ -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 (

View file

@ -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)

View file

@ -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)

View file

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

View file

@ -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)

View file

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

View file

@ -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")

View file

@ -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")

View file

@ -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)

View file

@ -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)

View file

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

View file

@ -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)

View file

@ -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)

View file

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

View file

@ -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)

View file

@ -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")

View file

@ -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)

View file

@ -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)

View file

@ -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)")

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)")

View file

@ -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)

View file

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

View file

@ -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)

View file

@ -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")

View file

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

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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()}
}

View file

@ -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)

200
cli/cli.go Normal file
View file

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

12
cli/client.go Normal file
View file

@ -0,0 +1,12 @@
package cli
import flag "github.com/docker/docker/pkg/mflag"
// ClientFlags represents flags for the docker client.
type ClientFlags struct {
FlagSet *flag.FlagSet
Common *CommonFlags
PostParse func()
ConfigDir string
}

20
cli/common.go Normal file
View file

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

View file

@ -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"))
}

View file

@ -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"))
}

View file

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

View file

@ -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) {
}

View file

@ -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")

View file

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

101
docker/common.go Normal file
View file

@ -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 = ""
}
}
}
}

View file

@ -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 = &registry.Options{}
flDaemon = flag.Bool([]string{"#d", "#-daemon"}, false, "Enable daemon mode (deprecated; use docker daemon)")
daemonCli cli.Handler = NewDaemonCli()
)
func init() {
if daemonCfg.LogConfig.Config == nil {
daemonCfg.LogConfig.Config = make(map[string]string)
// TODO: remove once `-d` is retired
func handleGlobalDaemonFlag() {
// This block makes sure that if the deprecated daemon flag `--daemon` is absent,
// then all daemon-specific flags are absent as well.
if !*flDaemon && daemonFlags != nil {
flag.CommandLine.Visit(func(fl *flag.Flag) {
for _, name := range fl.Names {
name := strings.TrimPrefix(name, "#")
if daemonFlags.Lookup(name) != nil {
// daemon flag was NOT specified, but daemon-specific flags were
// so let's error out
fmt.Fprintf(os.Stderr, "docker: the daemon flag '-%s' must follow the 'docker daemon' command.\n", name)
os.Exit(1)
}
}
})
}
if *flDaemon {
if *flHelp {
// We do not show the help output here, instead, we tell the user about the new daemon command,
// because the help output is so long they would not see the warning anyway.
fmt.Fprintln(os.Stderr, "Please use 'docker daemon --help' instead.")
os.Exit(0)
}
daemonCli.(*DaemonCli).CmdDaemon(flag.Args()...)
os.Exit(0)
}
}
func presentInHelp(usage string) string { return usage }
func absentFromHelp(string) string { return "" }
// NewDaemonCli returns a pre-configured daemon CLI
func NewDaemonCli() *DaemonCli {
daemonFlags = cli.Subcmd("daemon", nil, "Enable daemon mode", true)
// TODO(tiborvass): remove InstallFlags?
daemonConfig := new(daemon.Config)
daemonConfig.InstallFlags(daemonFlags, presentInHelp)
daemonConfig.InstallFlags(flag.CommandLine, absentFromHelp)
registryOptions := new(registry.Options)
registryOptions.InstallFlags(daemonFlags, presentInHelp)
registryOptions.InstallFlags(flag.CommandLine, absentFromHelp)
daemonFlags.Require(flag.Exact, 0)
return &DaemonCli{
Config: daemonConfig,
registryOptions: registryOptions,
}
daemonCfg.InstallFlags()
registryCfg.InstallFlags()
}
func migrateKey() (err error) {
@ -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"
}

12
docker/daemon_none.go Normal file
View file

@ -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() {}

View file

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

View file

@ -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"},
}

View file

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

View file

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

7
opts/hosts_unix.go Normal file
View file

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

7
opts/hosts_windows.go Normal file
View file

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

View file

@ -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,

View file

@ -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())

View file

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

View file

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

View file

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

View file

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

View file

@ -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)