1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #13771 from tiborvass/daemon-cli

New `docker daemon` command
This commit is contained in:
Jessie Frazelle 2015-07-23 19:30:39 -07:00
commit 7674f21686
86 changed files with 1126 additions and 709 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

@ -103,7 +103,7 @@ when no `-H` was passed in.
Run Docker in daemon mode:
$ sudo <path to>/docker -H 0.0.0.0:5555 -d &
$ sudo <path to>/docker daemon -H 0.0.0.0:5555 &
Download an `ubuntu` image:
@ -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 <path to>/docker -H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock -d &
$ sudo <path to>/docker daemon -H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock &
# Download an ubuntu image, use default Unix socket
$ docker pull ubuntu
# OR use the TCP port

View file

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

View file

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

View file

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

View file

@ -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://<my-docker-mirror-host> -d
docker daemon --registry-mirror=http://<my-docker-mirror-host>
For example, if your mirror is serving on `http://10.0.0.2:5000`, you would run:
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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

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

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)