From e71dbf4ee5add5736f595948fc20bd01af56a744 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Mon, 23 Dec 2013 11:43:54 -0800 Subject: [PATCH] update commands.go update docker.go move to pkg update docs update name and copyright change --sinceId to --since-id, update completion and docs Docker-DCO-1.1-Signed-off-by: Victor Vieux (github: vieux) --- commands.go | 136 +-- contrib/completion/bash/docker | 32 +- contrib/completion/zsh/_docker | 8 +- docker/docker.go | 36 +- .../sources/examples/couchdb_data_volumes.rst | 2 +- docs/sources/examples/python_web_app.rst | 2 +- .../examples/running_redis_service.rst | 4 +- docs/sources/reference/commandline/cli.rst | 193 ++-- pkg/mflag/LICENSE | 27 + pkg/mflag/README.md | 40 + pkg/mflag/example/example.go | 27 + pkg/mflag/export_test.go | 17 + pkg/mflag/flag.go | 863 ++++++++++++++++++ pkg/mflag/flag_test.go | 377 ++++++++ 14 files changed, 1558 insertions(+), 206 deletions(-) create mode 100644 pkg/mflag/LICENSE create mode 100644 pkg/mflag/README.md create mode 100644 pkg/mflag/example/example.go create mode 100644 pkg/mflag/export_test.go create mode 100644 pkg/mflag/flag.go create mode 100644 pkg/mflag/flag_test.go diff --git a/commands.go b/commands.go index 6553c0af4b..09b65ad163 100644 --- a/commands.go +++ b/commands.go @@ -7,11 +7,11 @@ import ( "encoding/base64" "encoding/json" "errors" - "flag" "fmt" "github.com/dotcloud/docker/archive" "github.com/dotcloud/docker/auth" "github.com/dotcloud/docker/engine" + flag "github.com/dotcloud/docker/pkg/mflag" "github.com/dotcloud/docker/pkg/term" "github.com/dotcloud/docker/registry" "github.com/dotcloud/docker/utils" @@ -164,10 +164,10 @@ func MkBuildContext(dockerfile string, files [][2]string) (archive.Archive, erro func (cli *DockerCli) CmdBuild(args ...string) error { cmd := cli.Subcmd("build", "[OPTIONS] PATH | URL | -", "Build a new container image from the source code at PATH") - tag := cmd.String("t", "", "Repository name (and optionally a tag) to be applied to the resulting image in case of success") - suppressOutput := cmd.Bool("q", false, "Suppress verbose build output") - noCache := cmd.Bool("no-cache", false, "Do not use cache when building the image") - rm := cmd.Bool("rm", false, "Remove intermediate containers after a successful build") + tag := cmd.String([]string{"t", "-tag"}, "", "Repository name (and optionally a tag) to be applied to the resulting image in case of success") + suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress verbose build output") + noCache := cmd.Bool([]string{"#no-cache", "-no-cache"}, false, "Do not use cache when building the image") + rm := cmd.Bool([]string{"#rm", "-rm"}, false, "Remove intermediate containers after a successful build") if err := cmd.Parse(args); err != nil { return nil } @@ -253,9 +253,9 @@ func (cli *DockerCli) CmdLogin(args ...string) error { var username, password, email string - cmd.StringVar(&username, "u", "", "username") - cmd.StringVar(&password, "p", "", "password") - cmd.StringVar(&email, "e", "", "email") + cmd.StringVar(&username, []string{"u", "-username"}, "", "username") + cmd.StringVar(&password, []string{"p", "-password"}, "", "password") + cmd.StringVar(&email, []string{"e", "-email"}, "", "email") err := cmd.Parse(args) if err != nil { return nil @@ -504,7 +504,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error { func (cli *DockerCli) CmdStop(args ...string) error { cmd := cli.Subcmd("stop", "[OPTIONS] CONTAINER [CONTAINER...]", "Stop a running container (Send SIGTERM, and then SIGKILL after grace period)") - nSeconds := cmd.Int("t", 10, "Number of seconds to wait for the container to stop before killing it.") + nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Number of seconds to wait for the container to stop before killing it.") if err := cmd.Parse(args); err != nil { return nil } @@ -531,7 +531,7 @@ func (cli *DockerCli) CmdStop(args ...string) error { func (cli *DockerCli) CmdRestart(args ...string) error { cmd := cli.Subcmd("restart", "[OPTIONS] CONTAINER [CONTAINER...]", "Restart a running container") - nSeconds := cmd.Int("t", 10, "Number of seconds to try to stop for before killing the container. Once killed it will then be restarted. Default=10") + nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Number of seconds to try to stop for before killing the container. Once killed it will then be restarted. Default=10") if err := cmd.Parse(args); err != nil { return nil } @@ -574,8 +574,8 @@ func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal { func (cli *DockerCli) CmdStart(args ...string) error { cmd := cli.Subcmd("start", "CONTAINER [CONTAINER...]", "Restart a stopped container") - attach := cmd.Bool("a", false, "Attach container's stdout/stderr and forward all signals to the process") - openStdin := cmd.Bool("i", false, "Attach container's stdin") + attach := cmd.Bool([]string{"a", "-attach"}, false, "Attach container's stdout/stderr and forward all signals to the process") + openStdin := cmd.Bool([]string{"i", "-interactive"}, false, "Attach container's stdin") if err := cmd.Parse(args); err != nil { return nil } @@ -660,7 +660,7 @@ func (cli *DockerCli) CmdStart(args ...string) error { func (cli *DockerCli) CmdInspect(args ...string) error { cmd := cli.Subcmd("inspect", "CONTAINER|IMAGE [CONTAINER|IMAGE...]", "Return low-level information on a container/image") - tmplStr := cmd.String("format", "", "Format the output using the given go template.") + tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template.") if err := cmd.Parse(args); err != nil { return nil } @@ -846,8 +846,8 @@ func (cli *DockerCli) CmdRmi(args ...string) error { func (cli *DockerCli) CmdHistory(args ...string) error { cmd := cli.Subcmd("history", "[OPTIONS] IMAGE", "Show the history of an image") - quiet := cmd.Bool("q", false, "only show numeric IDs") - noTrunc := cmd.Bool("notrunc", false, "Don't truncate output") + quiet := cmd.Bool([]string{"q", "-quiet"}, false, "only show numeric IDs") + noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") if err := cmd.Parse(args); err != nil { return nil @@ -906,8 +906,8 @@ func (cli *DockerCli) CmdHistory(args ...string) error { func (cli *DockerCli) CmdRm(args ...string) error { cmd := cli.Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove one or more containers") - v := cmd.Bool("v", false, "Remove the volumes associated to the container") - link := cmd.Bool("link", false, "Remove the specified link and not the underlying container") + v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated to the container") + link := cmd.Bool([]string{"l", "#link", "-link"}, false, "Remove the specified link and not the underlying container") if err := cmd.Parse(args); err != nil { return nil @@ -1058,7 +1058,7 @@ func (cli *DockerCli) CmdPush(args ...string) error { func (cli *DockerCli) CmdPull(args ...string) error { cmd := cli.Subcmd("pull", "NAME", "Pull an image or a repository from the registry") - tag := cmd.String("t", "", "Download tagged image in repository") + tag := cmd.String([]string{"t", "-tag"}, "", "Download tagged image in repository") if err := cmd.Parse(args); err != nil { return nil } @@ -1118,11 +1118,11 @@ func (cli *DockerCli) CmdPull(args ...string) error { func (cli *DockerCli) CmdImages(args ...string) error { cmd := cli.Subcmd("images", "[OPTIONS] [NAME]", "List images") - quiet := cmd.Bool("q", false, "only show numeric IDs") - all := cmd.Bool("a", false, "show all images (by default filter out the intermediate images used to build)") - noTrunc := cmd.Bool("notrunc", false, "Don't truncate output") - flViz := cmd.Bool("viz", false, "output graph in graphviz format") - flTree := cmd.Bool("tree", false, "output graph in tree format") + quiet := cmd.Bool([]string{"q", "-quiet"}, false, "only show numeric IDs") + all := cmd.Bool([]string{"a", "-all"}, false, "show all images (by default filter out the intermediate images used to build)") + noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") + flViz := cmd.Bool([]string{"v", "#viz", "-viz"}, false, "output graph in graphviz format") + flTree := cmd.Bool([]string{"t", "#tree", "-tree"}, false, "output graph in tree format") if err := cmd.Parse(args); err != nil { return nil @@ -1329,14 +1329,14 @@ func displayablePorts(ports []APIPort) string { func (cli *DockerCli) CmdPs(args ...string) error { cmd := cli.Subcmd("ps", "[OPTIONS]", "List containers") - quiet := cmd.Bool("q", false, "Only display numeric IDs") - size := cmd.Bool("s", false, "Display sizes") - all := cmd.Bool("a", false, "Show all containers. Only running containers are shown by default.") - noTrunc := cmd.Bool("notrunc", false, "Don't truncate output") - nLatest := cmd.Bool("l", false, "Show only the latest created container, include non-running ones.") - since := cmd.String("sinceId", "", "Show only containers created since Id, include non-running ones.") - before := cmd.String("beforeId", "", "Show only container created before Id, include non-running ones.") - last := cmd.Int("n", -1, "Show n last created containers, include non-running ones.") + quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs") + size := cmd.Bool([]string{"s", "-size"}, false, "Display sizes") + all := cmd.Bool([]string{"a", "-all"}, false, "Show all containers. Only running containers are shown by default.") + noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") + nLatest := cmd.Bool([]string{"l", "-latest"}, false, "Show only the latest created container, include non-running ones.") + since := cmd.String([]string{"#sinceId", "-since-id"}, "", "Show only containers created since Id, include non-running ones.") + before := cmd.String([]string{"#beforeId", "-before-id"}, "", "Show only container created before Id, include non-running ones.") + last := cmd.Int([]string{"n"}, -1, "Show n last created containers, include non-running ones.") if err := cmd.Parse(args); err != nil { return nil @@ -1418,9 +1418,9 @@ func (cli *DockerCli) CmdPs(args ...string) error { func (cli *DockerCli) CmdCommit(args ...string) error { cmd := cli.Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY[:TAG]]", "Create a new image from a container's changes") - flComment := cmd.String("m", "", "Commit message") - flAuthor := cmd.String("author", "", "Author (eg. \"John Hannibal Smith \"") - flConfig := cmd.String("run", "", "Config automatically applied when the image is run. "+`(ex: -run='{"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}')`) + flComment := cmd.String([]string{"m", "-message"}, "", "Commit message") + flAuthor := cmd.String([]string{"a", "#author", "-author"}, "", "Author (eg. \"John Hannibal Smith \"") + flConfig := cmd.String([]string{"#run", "-run"}, "", "Config automatically applied when the image is run. "+`(ex: -run='{"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}')`) if err := cmd.Parse(args); err != nil { return nil } @@ -1470,7 +1470,7 @@ func (cli *DockerCli) CmdCommit(args ...string) error { func (cli *DockerCli) CmdEvents(args ...string) error { cmd := cli.Subcmd("events", "[OPTIONS]", "Get real time events from the server") - since := cmd.String("since", "", "Show previously created events and then stream.") + since := cmd.String([]string{"#since", "-since"}, "", "Show previously created events and then stream.") if err := cmd.Parse(args); err != nil { return nil } @@ -1557,7 +1557,7 @@ func (cli *DockerCli) CmdDiff(args ...string) error { func (cli *DockerCli) CmdLogs(args ...string) error { cmd := cli.Subcmd("logs", "CONTAINER", "Fetch the logs of a container") - follow := cmd.Bool("f", false, "Follow log output") + follow := cmd.Bool([]string{"f", "-follow"}, false, "Follow log output") if err := cmd.Parse(args); err != nil { return nil } @@ -1593,8 +1593,8 @@ func (cli *DockerCli) CmdLogs(args ...string) error { func (cli *DockerCli) CmdAttach(args ...string) error { cmd := cli.Subcmd("attach", "[OPTIONS] CONTAINER", "Attach to a running container") - noStdin := cmd.Bool("nostdin", false, "Do not attach stdin") - proxy := cmd.Bool("sig-proxy", true, "Proxify all received signal to the process (even in non-tty mode)") + noStdin := cmd.Bool([]string{"#nostdin", "-no-stdin"}, false, "Do not attach stdin") + proxy := cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signal to the process (even in non-tty mode)") if err := cmd.Parse(args); err != nil { return nil } @@ -1657,9 +1657,9 @@ func (cli *DockerCli) CmdAttach(args ...string) error { func (cli *DockerCli) CmdSearch(args ...string) error { cmd := cli.Subcmd("search", "TERM", "Search the docker index for images") - noTrunc := cmd.Bool("notrunc", false, "Don't truncate output") - trusted := cmd.Bool("trusted", false, "Only show trusted builds") - stars := cmd.Int("stars", 0, "Only displays with at least xxx stars") + noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") + trusted := cmd.Bool([]string{"t", "#trusted", "-trusted"}, false, "Only show trusted builds") + stars := cmd.Int([]string{"s", "#stars", "-stars"}, 0, "Only displays with at least xxx stars") if err := cmd.Parse(args); err != nil { return nil } @@ -1712,7 +1712,7 @@ type ports []int func (cli *DockerCli) CmdTag(args ...string) error { cmd := cli.Subcmd("tag", "[OPTIONS] IMAGE REPOSITORY[:TAG]", "Tag an image into a repository") - force := cmd.Bool("f", false, "Force") + force := cmd.Bool([]string{"f", "#force", "-force"}, false, "Force") if err := cmd.Parse(args); err != nil { return nil } @@ -1766,36 +1766,36 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co flVolumesFrom ListOpts flLxcOpts ListOpts - flAutoRemove = cmd.Bool("rm", false, "Automatically remove the container when it exits (incompatible with -d)") - flDetach = cmd.Bool("d", false, "Detached mode: Run container in the background, print new container id") - flNetwork = cmd.Bool("n", true, "Enable networking for this container") - flPrivileged = cmd.Bool("privileged", false, "Give extended privileges to this container") - flPublishAll = cmd.Bool("P", false, "Publish all exposed ports to the host interfaces") - flStdin = cmd.Bool("i", false, "Keep stdin open even if not attached") - flTty = cmd.Bool("t", false, "Allocate a pseudo-tty") - flContainerIDFile = cmd.String("cidfile", "", "Write the container ID to the file") - flEntrypoint = cmd.String("entrypoint", "", "Overwrite the default entrypoint of the image") - flHostname = cmd.String("h", "", "Container host name") - flMemoryString = cmd.String("m", "", "Memory limit (format: , where unit = b, k, m or g)") - flUser = cmd.String("u", "", "Username or UID") - flWorkingDir = cmd.String("w", "", "Working directory inside the container") - flCpuShares = cmd.Int64("c", 0, "CPU shares (relative weight)") + flAutoRemove = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)") + flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: Run container in the background, print new container id") + flNetwork = cmd.Bool([]string{"n", "-networking"}, true, "Enable networking for this container") + flPrivileged = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container") + flPublishAll = cmd.Bool([]string{"P", "-publish-all"}, false, "Publish all exposed ports to the host interfaces") + flStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Keep stdin open even if not attached") + flTty = cmd.Bool([]string{"t", "-tty"}, false, "Allocate a pseudo-tty") + flContainerIDFile = cmd.String([]string{"#cidfile", "-cidfile"}, "", "Write the container ID to the file") + flEntrypoint = cmd.String([]string{"#entrypoint", "-entrypoint"}, "", "Overwrite the default entrypoint of the image") + flHostname = cmd.String([]string{"h", "-hostname"}, "", "Container host name") + flMemoryString = cmd.String([]string{"m", "-memory"}, "", "Memory limit (format: , where unit = b, k, m or g)") + flUser = cmd.String([]string{"u", "-username"}, "", "Username or UID") + flWorkingDir = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container") + flCpuShares = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)") // For documentation purpose - _ = cmd.Bool("sig-proxy", true, "Proxify all received signal to the process (even in non-tty mode)") - _ = cmd.String("name", "", "Assign a name to the container") + _ = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signal to the process (even in non-tty mode)") + _ = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container") ) - cmd.Var(&flAttach, "a", "Attach to stdin, stdout or stderr.") - cmd.Var(&flVolumes, "v", "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)") - cmd.Var(&flLinks, "link", "Add link to another container (name:alias)") - cmd.Var(&flEnv, "e", "Set environment variables") + cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to stdin, stdout or stderr.") + cmd.Var(&flVolumes, []string{"v", "-volume"}, "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)") + cmd.Var(&flLinks, []string{"#link", "-link"}, "Add link to another container (name:alias)") + cmd.Var(&flEnv, []string{"e", "-env"}, "Set environment variables") - cmd.Var(&flPublish, "p", fmt.Sprintf("Publish a container's port to the host (format: %s) (use 'docker port' to see the actual mapping)", PortSpecTemplateFormat)) - cmd.Var(&flExpose, "expose", "Expose a port from the container without publishing it to your host") - cmd.Var(&flDns, "dns", "Set custom dns servers") - cmd.Var(&flVolumesFrom, "volumes-from", "Mount volumes from the specified container(s)") - cmd.Var(&flLxcOpts, "lxc-conf", "Add custom lxc options -lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"") + cmd.Var(&flPublish, []string{"p", "-publish"}, fmt.Sprintf("Publish a container's port to the host (format: %s) (use 'docker port' to see the actual mapping)", PortSpecTemplateFormat)) + cmd.Var(&flExpose, []string{"#expose", "-expose"}, "Expose a port from the container without publishing it to your host") + cmd.Var(&flDns, []string{"#dns", "-dns"}, "Set custom dns servers") + cmd.Var(&flVolumesFrom, []string{"#volumes-from", "-volumes-from"}, "Mount volumes from the specified container(s)") + cmd.Var(&flLxcOpts, []string{"#lxc-conf", "-lxc-conf"}, "Add custom lxc options -lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"") if err := cmd.Parse(args); err != nil { return nil, nil, cmd, err @@ -1892,7 +1892,7 @@ func parseRun(cmd *flag.FlagSet, args []string, capabilities *Capabilities) (*Co // Merge in exposed ports to the map of published ports for _, e := range flExpose.GetAll() { if strings.Contains(e, ":") { - return nil, nil, cmd, fmt.Errorf("Invalid port format for -expose: %s", e) + return nil, nil, cmd, fmt.Errorf("Invalid port format for --expose: %s", e) } p := NewPort(splitProtoPort(e)) if _, exists := ports[p]; !exists { diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index f1a515d00a..4ebe45e083 100755 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -25,7 +25,7 @@ __docker_containers_all() { local containers containers="$( docker ps -a -q )" - names="$( docker inspect -format '{{.Name}}' $containers | sed 's,^/,,' )" + names="$( docker inspect --format '{{.Name}}' $containers | sed 's,^/,,' )" COMPREPLY=( $( compgen -W "$names $containers" -- "$cur" ) ) } @@ -33,7 +33,7 @@ __docker_containers_running() { local containers containers="$( docker ps -q )" - names="$( docker inspect -format '{{.Name}}' $containers | sed 's,^/,,' )" + names="$( docker inspect --format '{{.Name}}' $containers | sed 's,^/,,' )" COMPREPLY=( $( compgen -W "$names $containers" -- "$cur" ) ) } @@ -41,7 +41,7 @@ __docker_containers_stopped() { local containers containers="$( comm -13 <(docker ps -q | sort -u) <(docker ps -a -q | sort -u) )" - names="$( docker inspect -format '{{.Name}}' $containers | sed 's,^/,,' )" + names="$( docker inspect --format '{{.Name}}' $containers | sed 's,^/,,' )" COMPREPLY=( $( compgen -W "$names $containers" -- "$cur" ) ) } @@ -73,7 +73,7 @@ __docker_containers_and_images() { local containers images containers="$( docker ps -a -q )" - names="$( docker inspect -format '{{.Name}}' $containers | sed 's,^/,,' )" + names="$( docker inspect --format '{{.Name}}' $containers | sed 's,^/,,' )" images="$( docker images | awk 'NR>1{print $1":"$2}' )" COMPREPLY=( $( compgen -W "$images $names $containers" -- "$cur" ) ) __ltrim_colon_completions "$cur" @@ -118,7 +118,7 @@ _docker_build() case "$cur" in -*) - COMPREPLY=( $( compgen -W "-no-cache -t -q -rm" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--no-cache -t -q --rm" -- "$cur" ) ) ;; *) _filedir @@ -138,7 +138,7 @@ _docker_commit() case "$cur" in -*) - COMPREPLY=( $( compgen -W "-author -m -run" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--author -m --run" -- "$cur" ) ) ;; *) local counter=$cpos @@ -191,7 +191,7 @@ _docker_events() case "$cur" in -*) - COMPREPLY=( $( compgen -W "-since" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--since" -- "$cur" ) ) ;; *) ;; @@ -223,7 +223,7 @@ _docker_images() { case "$cur" in -*) - COMPREPLY=( $( compgen -W "-a -notrunc -q -viz" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "-a --no-trunc -q --viz" -- "$cur" ) ) ;; *) local counter=$cpos @@ -308,7 +308,7 @@ _docker_port() _docker_ps() { case "$prev" in - -beforeId|-n|-sinceId) + -before-id|-n|-since-id) return ;; *) @@ -317,7 +317,7 @@ _docker_ps() case "$cur" in -*) - COMPREPLY=( $( compgen -W "-a -beforeId -l -n -notrunc -q -s -sinceId" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "-a --before-id -l -n --no-trunc -q -s --since-id" -- "$cur" ) ) ;; *) ;; @@ -388,13 +388,13 @@ _docker_rmi() _docker_run() { case "$prev" in - -cidfile) + --cidfile) _filedir ;; - -volumes-from) + --volumes-from) __docker_containers_all ;; - -a|-c|-dns|-e|-entrypoint|-h|-lxc-conf|-m|-p|-u|-v|-w) + -a|-c|--dns|-e|--entrypoint|-h|--lxc-conf|-m|-p|-u|-v|-w) return ;; *) @@ -403,13 +403,13 @@ _docker_run() case "$cur" in -*) - COMPREPLY=( $( compgen -W "-a -c -cidfile -d -dns -e -entrypoint -h -i -lxc-conf -m -n -p -privileged -t -u -v -volumes-from -w" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "-a -c --cidfile -d --dns -e --entrypoint -h -i --lxc-conf -m -n -p --privileged -t -u -v --volumes-from -w" -- "$cur" ) ) ;; *) local counter=$cpos while [ $counter -le $cword ]; do case "${words[$counter]}" in - -a|-c|-cidfile|-dns|-e|-entrypoint|-h|-lxc-conf|-m|-p|-u|-v|-volumes-from|-w) + -a|-c|--cidfile|--dns|-e|--entrypoint|-h|--lxc-conf|-m|-p|-u|-v|--volumes-from|-w) (( counter++ )) ;; -*) @@ -430,7 +430,7 @@ _docker_run() _docker_search() { - COMPREPLY=( $( compgen -W "-notrunc" "-stars" "-trusted" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--no-trunc" "--stars" "--trusted" -- "$cur" ) ) } _docker_start() diff --git a/contrib/completion/zsh/_docker b/contrib/completion/zsh/_docker index 92acdb13dd..8b50bac01b 100755 --- a/contrib/completion/zsh/_docker +++ b/contrib/completion/zsh/_docker @@ -174,7 +174,7 @@ __docker_subcommand () { (ps) _arguments '-a[Show all containers. Only running containers are shown by default]' \ '-h[Show help]' \ - '-beforeId=-[Show only container created before Id, include non-running one]:containers:__docker_containers' \ + '-before-id=-[Show only container created before Id, include non-running one]:containers:__docker_containers' \ '-n=-[Show n last created containers, include non-running one]:n:(1 5 10 25 50)' ;; (tag) @@ -189,9 +189,9 @@ __docker_subcommand () { '-a=-[Attach to stdin, stdout or stderr]:toggle:(true false)' \ '-c=-[CPU shares (relative weight)]:CPU shares: ' \ '-d[Detached mode: leave the container running in the background]' \ - '*-dns=[Set custom dns servers]:dns server: ' \ + '*--dns=[Set custom dns servers]:dns server: ' \ '*-e=[Set environment variables]:environment variable: ' \ - '-entrypoint=-[Overwrite the default entrypoint of the image]:entry point: ' \ + '--entrypoint=-[Overwrite the default entrypoint of the image]:entry point: ' \ '-h=-[Container host name]:hostname:_hosts' \ '-i[Keep stdin open even if not attached]' \ '-m=-[Memory limit (in bytes)]:limit: ' \ @@ -199,7 +199,7 @@ __docker_subcommand () { '-t=-[Allocate a pseudo-tty]:toggle:(true false)' \ '-u=-[Username or UID]:user:_users' \ '*-v=-[Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)]:volume: '\ - '-volumes-from=-[Mount volumes from the specified container]:volume: ' \ + '--volumes-from=-[Mount volumes from the specified container]:volume: ' \ '(-):images:__docker_images' \ '(-):command: _command_names -e' \ '*::arguments: _normal' diff --git a/docker/docker.go b/docker/docker.go index 2d7e04ce92..5f5b3c17ce 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -1,10 +1,10 @@ package main import ( - "flag" "fmt" "github.com/dotcloud/docker" "github.com/dotcloud/docker/engine" + flag "github.com/dotcloud/docker/pkg/mflag" "github.com/dotcloud/docker/sysinit" "github.com/dotcloud/docker/utils" "log" @@ -25,25 +25,25 @@ func main() { } var ( - flVersion = flag.Bool("v", false, "Print version information and quit") - flDaemon = flag.Bool("d", false, "Enable daemon mode") - flDebug = flag.Bool("D", false, "Enable debug mode") - flAutoRestart = flag.Bool("r", true, "Restart previously running containers") - bridgeName = flag.String("b", "", "Attach containers to a pre-existing network bridge; use 'none' to disable container networking") - bridgeIp = flag.String("bip", "", "Use this CIDR notation address for the network bridge's IP, not compatible with -b") - pidfile = flag.String("p", "/var/run/docker.pid", "Path to use for daemon PID file") - flRoot = flag.String("g", "/var/lib/docker", "Path to use as the root of the docker runtime") - flEnableCors = flag.Bool("api-enable-cors", false, "Enable CORS headers in the remote API") + 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") + flAutoRestart = flag.Bool([]string{"r", "-restart"}, true, "Restart previously running containers") + bridgeName = flag.String([]string{"b", "-bridge"}, "", "Attach containers to a pre-existing network bridge; use 'none' to disable container networking") + bridgeIp = flag.String([]string{"#bip", "-bip"}, "", "Use this CIDR notation address for the network bridge's IP, not compatible with -b") + pidfile = flag.String([]string{"p", "-pidfile"}, "/var/run/docker.pid", "Path to use for daemon PID file") + flRoot = flag.String([]string{"g", "-graph"}, "/var/lib/docker", "Path to use as the root of the docker runtime") + flEnableCors = flag.Bool([]string{"#api-enable-cors", "-api-enable-cors"}, false, "Enable CORS headers in the remote API") flDns = docker.NewListOpts(docker.ValidateIp4Address) - flEnableIptables = flag.Bool("iptables", true, "Disable docker's addition of iptables rules") - flDefaultIp = flag.String("ip", "0.0.0.0", "Default IP address to use when binding container ports") - flInterContainerComm = flag.Bool("icc", true, "Enable inter-container communication") - flGraphDriver = flag.String("s", "", "Force the docker runtime to use a specific storage driver") + flEnableIptables = flag.Bool([]string{"#iptables", "-iptables"}, true, "Disable docker's addition of iptables rules") + flDefaultIp = flag.String([]string{"#ip", "-ip"}, "0.0.0.0", "Default IP address to use when binding container ports") + flInterContainerComm = flag.Bool([]string{"#icc", "-icc"}, true, "Enable inter-container communication") + flGraphDriver = flag.String([]string{"s", "-storage-driver"}, "", "Force the docker runtime to use a specific storage driver") flHosts = docker.NewListOpts(docker.ValidateHost) - flMtu = flag.Int("mtu", docker.DefaultNetworkMtu, "Set the containers network mtu") + flMtu = flag.Int([]string{"#mtu", "-mtu"}, docker.DefaultNetworkMtu, "Set the containers network mtu") ) - flag.Var(&flDns, "dns", "Force docker to use specific DNS servers") - flag.Var(&flHosts, "H", "Multiple tcp://host:port or unix://path/to/socket to bind in daemon mode, single connection otherwise") + flag.Var(&flDns, []string{"#dns", "-dns"}, "Force docker to use specific DNS servers") + flag.Var(&flHosts, []string{"H", "-host"}, "Multiple tcp://host:port or unix://path/to/socket to bind in daemon mode, single connection otherwise") flag.Parse() @@ -62,7 +62,7 @@ func main() { } if *bridgeName != "" && *bridgeIp != "" { - log.Fatal("You specified -b & -bip, mutually exclusive options. Please specify only one.") + log.Fatal("You specified -b & --bip, mutually exclusive options. Please specify only one.") } if *flDebug { diff --git a/docs/sources/examples/couchdb_data_volumes.rst b/docs/sources/examples/couchdb_data_volumes.rst index 1f6b4b7910..6cf3fab68c 100644 --- a/docs/sources/examples/couchdb_data_volumes.rst +++ b/docs/sources/examples/couchdb_data_volumes.rst @@ -41,7 +41,7 @@ This time, we're requesting shared access to ``$COUCH1``'s volumes. .. code-block:: bash - COUCH2=$(sudo docker run -d -p 5984 -volumes-from $COUCH1 shykes/couchdb:2013-05-03) + COUCH2=$(sudo docker run -d -p 5984 --volumes-from $COUCH1 shykes/couchdb:2013-05-03) Browse data on the second database ---------------------------------- diff --git a/docs/sources/examples/python_web_app.rst b/docs/sources/examples/python_web_app.rst index 3034bf980a..02160d0753 100644 --- a/docs/sources/examples/python_web_app.rst +++ b/docs/sources/examples/python_web_app.rst @@ -43,7 +43,7 @@ container. The ``BUILD_JOB`` environment variable will be set with the new conta [...] While this container is running, we can attach to the new container to -see what is going on. The flag ``-sig-proxy`` set as ``false`` allows you to connect and +see what is going on. The flag ``--sig-proxy`` set as ``false`` allows you to connect and disconnect (Ctrl-C) to it without stopping the container. .. code-block:: bash diff --git a/docs/sources/examples/running_redis_service.rst b/docs/sources/examples/running_redis_service.rst index 886f473ef2..9687f0cfa8 100644 --- a/docs/sources/examples/running_redis_service.rst +++ b/docs/sources/examples/running_redis_service.rst @@ -44,7 +44,7 @@ use a container link to provide access to our Redis database. .. code-block:: bash - sudo docker run -name redis -d /redis + sudo docker run --name redis -d /redis Create your web application container ------------------------------------- @@ -56,7 +56,7 @@ Redis instance running inside that container to only this container. .. code-block:: bash - sudo docker run -link redis:db -i -t ubuntu:12.10 /bin/bash + sudo docker run --link redis:db -i -t ubuntu:12.10 /bin/bash Once inside our freshly created container we need to install Redis to get the ``redis-cli`` binary to test our connection. diff --git a/docs/sources/reference/commandline/cli.rst b/docs/sources/reference/commandline/cli.rst index 67c8b06189..a6ae75076b 100644 --- a/docs/sources/reference/commandline/cli.rst +++ b/docs/sources/reference/commandline/cli.rst @@ -26,22 +26,21 @@ To list available commands, either run ``docker`` with no parameters or execute :: Usage of docker: - -D=false: Enable debug mode - -H=[unix:///var/run/docker.sock]: tcp://[host[:port]] to bind or unix://[/path/to/socket] to use. When host=[0.0.0.0], port=[4243] or path=[/var/run/docker.sock] is omitted, default values are used. - -api-enable-cors=false: Enable CORS headers in the remote API - -b="": Attach containers to a pre-existing network bridge; use 'none' to disable container networking - -bip="": Use the provided CIDR notation address for the dynamically created bridge (docker0); Mutually exclusive of -b - -d=false: Enable daemon mode - -dns="": Force docker to use specific DNS servers - -g="/var/lib/docker": Path to use as the root of the docker runtime - -icc=true: Enable inter-container communication - -ip="0.0.0.0": Default IP address to use when binding container ports - -iptables=true: Disable docker's addition of iptables rules - -mtu=1500: Set the containers network mtu - -p="/var/run/docker.pid": Path to use for daemon PID file - -r=true: Restart previously running containers - -s="": Force the docker runtime to use a specific storage driver - -v=false: Print version information and quit + -D, --debug=false: Enable debug mode + -H, --host=[]: Multiple tcp://host:port or unix://path/to/socket to bind in daemon mode, single connection otherwise + --api-enable-cors=false: Enable CORS headers in the remote API + -b, --bridge="": Attach containers to a pre-existing network bridge; use 'none' to disable container networking + --bip="": Use this CIDR notation address for the network bridge's IP, not compatible with -b + -d, --daemon=false: Enable daemon mode + --dns=[]: Force docker to use specific DNS servers + -g, --graph="/var/lib/docker": Path to use as the root of the docker runtime + --icc=true: Enable inter-container communication + --ip="0.0.0.0": Default IP address to use when binding container ports + --iptables=true: Disable docker's addition of iptables rules + -p, --pidfile="/var/run/docker.pid": Path to use for daemon PID file + -r, --restart=true: Restart previously running containers + -s, --storage-driver="": Force the docker runtime to use a specific storage driver + -v, --version=false: Print version information and quit 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. @@ -75,8 +74,8 @@ the ``-H`` flag for the client. Attach to a running container. - -nostdin=false: Do not attach stdin - -sig-proxy=true: Proxify all received signal to the process (even in non-tty mode) + --no-stdin=false: Do not attach stdin + --sig-proxy=true: Proxify all received signal to the process (even in non-tty mode) You can detach from the container again (and leave it running) with ``CTRL-c`` (for a quiet exit) or ``CTRL-\`` to get a stacktrace of @@ -135,11 +134,11 @@ Examples: Usage: docker build [OPTIONS] PATH | URL | - Build a new container image from the source code at PATH - -t="": Repository name (and optionally a tag) to be applied + -t, --time="": Repository name (and optionally a tag) to be applied to the resulting image in case of success. - -q=false: Suppress verbose build output. - -no-cache: Do not use the cache when building the image. - -rm: Remove intermediate containers after a successful build + -q, --quiet=false: Suppress verbose build output. + --no-cache: Do not use the cache when building the image. + --rm: Remove intermediate containers after a successful build The files at ``PATH`` or ``URL`` are called the "context" of the build. The build process may refer to any of the files in the context, for example when @@ -233,9 +232,9 @@ by using the ``git://`` schema. Create a new image from a container's changes - -m="": Commit message - -author="": Author (eg. "John Hannibal Smith " - -run="": Configuration to be applied when the image is launched with `docker run`. + -m, --message="": Commit message + -a, --author="": Author (eg. "John Hannibal Smith " + --run="": Configuration to be applied when the image is launched with `docker run`. (ex: -run='{"Cmd": ["cat", "/world"], "PortSpecs": ["22"]}') .. _cli_commit_examples: @@ -279,7 +278,7 @@ run ``ls /etc``. Full -run example ................. -The ``-run`` JSON hash changes the ``Config`` section when running ``docker inspect CONTAINERID`` +The ``--run`` JSON hash changes the ``Config`` section when running ``docker inspect CONTAINERID`` or ``config`` when running ``docker inspect IMAGEID``. (Multiline is okay within a single quote ``'``) @@ -379,7 +378,7 @@ For example: Get real time events from the server - -since="": Show previously created events and then stream. + --since="": Show previously created events and then stream. (either seconds since epoch, or date string as below) .. _cli_events_example: @@ -459,8 +458,8 @@ For example: Show the history of an image - -notrunc=false: Don't truncate output - -q=false: only show numeric IDs + --no-trunc=false: Don't truncate output + -q, --quiet=false: only show numeric IDs To see how the ``docker:latest`` image was built: @@ -507,11 +506,11 @@ To see how the ``docker:latest`` image was built: List images - -a=false: show all images (by default filter out the intermediate images used to build) - -notrunc=false: Don't truncate output - -q=false: only show numeric IDs - -tree=false: output graph in tree format - -viz=false: output graph in graphviz format + -a, --all=false: show all images (by default filter out the intermediate images used to build) + --no-trunc=false: Don't truncate output + -q, --quiet=false: only show numeric IDs + --tree=false: output graph in tree format + --viz=false: output graph in graphviz format Listing the most recently created images ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -535,7 +534,7 @@ Listing the full length image IDs .. code-block:: bash - $ sudo docker images -notrunc | head + $ sudo docker images --no-trunc | head REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE 77af4d6b9913e693e8d0b4b294fa62ade6054e6b2f1ffb617ac955dd63fb0182 19 hours ago 1.089 GB committest latest b6fa739cedf5ea12a620a439402b6004d057da800f91c7524b5086a5e4749c9f 19 hours ago 1.089 GB @@ -552,7 +551,7 @@ Displaying images visually .. code-block:: bash - $ sudo docker images -viz | dot -Tpng -o docker.png + $ sudo docker images --viz | dot -Tpng -o docker.png .. image:: docker_images.gif :alt: Example inheritance graph of Docker images. @@ -563,7 +562,7 @@ Displaying image hierarchy .. code-block:: bash - $ sudo docker images -tree + $ sudo docker images --tree ├─8dbd9e392a96 Size: 131.5 MB (virtual 131.5 MB) Tags: ubuntu:12.04,ubuntu:latest,ubuntu:precise └─27cf78414709 Size: 180.1 MB (virtual 180.1 MB) @@ -702,7 +701,7 @@ Insert file from GitHub Return low-level information on a container/image - -format="": Format the output using the given go template. + -f, --format="": Format the output using the given go template. By default, this will render all results in a JSON array. If a format is specified, the given template will be executed for each result. @@ -721,7 +720,7 @@ fairly straightforward manner. .. code-block:: bash - $ sudo docker inspect -format='{{.NetworkSettings.IPAddress}}' $INSTANCE_ID + $ sudo docker inspect --format='{{.NetworkSettings.IPAddress}}' $INSTANCE_ID List All Port Bindings ...................... @@ -790,9 +789,9 @@ Known Issues (kill) Register or Login to the docker registry server - -e="": email - -p="": password - -u="": username + -e, --email="": email + -p, --password="": password + -u, --username="": username If you want to login to a private registry you can specify this by adding the server name. @@ -812,12 +811,14 @@ Known Issues (kill) Fetch the logs of a container + -f, --follow=false: Follow log output + The ``docker logs`` command is a convenience which batch-retrieves whatever logs are present at the time of execution. This does not guarantee execution order when combined with a ``docker run`` (i.e. your run may not have generated any logs at the time you execute ``docker logs``). -The ``docker logs -f`` command combines ``docker logs`` and ``docker attach``: +The ``docker logs --follow`` command combines ``docker logs`` and ``docker attach``: it will first return all logs from the beginning and then continue streaming new output from the container's stdout and stderr. @@ -845,9 +846,9 @@ new output from the container's stdout and stderr. List containers - -a=false: Show all containers. Only running containers are shown by default. - -notrunc=false: Don't truncate output - -q=false: Only display numeric IDs + -a, --all=false: Show all containers. Only running containers are shown by default. + --no-trunc=false: Don't truncate output + -q, --quiet=false: Only display numeric IDs Running ``docker ps`` showing 2 linked containers. @@ -903,7 +904,7 @@ Running ``docker ps`` showing 2 linked containers. Usage: docker rm [OPTIONS] CONTAINER Remove one or more containers - -link="": Remove the link instead of the actual container + --link="": Remove the link instead of the actual container Known Issues (rm) ~~~~~~~~~~~~~~~~~ @@ -926,7 +927,7 @@ This will remove the container referenced under the link ``/redis``. .. code-block:: bash - $ sudo docker rm -link /webapp/redis + $ sudo docker rm --link /webapp/redis /webapp/redis @@ -996,31 +997,31 @@ image is removed. Run a command in a new container - -a=map[]: Attach to stdin, stdout or stderr - -c=0: CPU shares (relative weight) - -cidfile="": Write the container ID to the file - -d=false: Detached mode: Run container in the background, print new container id - -e=[]: Set environment variables - -h="": Container host name - -i=false: Keep stdin open even if not attached - -privileged=false: Give extended privileges to this container - -m="": Memory limit (format: , where unit = b, k, m or g) - -n=true: Enable networking for this container - -p=[]: Map a network port to the container - -rm=false: Automatically remove the container when it exits (incompatible with -d) - -t=false: Allocate a pseudo-tty - -u="": Username or UID - -dns=[]: Set custom dns servers for the container - -v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]. If "container-dir" is missing, then docker creates a new volume. - -volumes-from="": Mount all volumes from the given container(s) - -entrypoint="": Overwrite the default entrypoint set by the image - -w="": Working directory inside the container - -lxc-conf=[]: Add custom lxc options -lxc-conf="lxc.cgroup.cpuset.cpus = 0,1" - -sig-proxy=true: Proxify all received signal to the process (even in non-tty mode) - -expose=[]: Expose a port from the container without publishing it to your host - -link="": Add link to another container (name:alias) - -name="": Assign the specified name to the container. If no name is specific docker will generate a random name - -P=false: Publish all exposed ports to the host interfaces + -a, --attach=map[]: Attach to stdin, stdout or stderr + -c, --cpu-shares=0: CPU shares (relative weight) + --cidfile="": Write the container ID to the file + -d, --detach=false: Detached mode: Run container in the background, print new container id + -e, --env=[]: Set environment variables + -h, --host="": Container host name + -i, --interactive=false: Keep stdin open even if not attached + --privileged=false: Give extended privileges to this container + -m, --memory="": Memory limit (format: , where unit = b, k, m or g) + -n, --networking=true: Enable networking for this container + -p, --publish=[]: Map a network port to the container + --rm=false: Automatically remove the container when it exits (incompatible with -d) + -t, --tty=false: Allocate a pseudo-tty + -u, --username="": Username or UID + --dns=[]: Set custom dns servers for the container + -v, --volume=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]. If "container-dir" is missing, then docker creates a new volume. + --volumes-from="": Mount all volumes from the given container(s) + --entrypoint="": Overwrite the default entrypoint set by the image + -w, --workdir="": Working directory inside the container + --lxc-conf=[]: Add custom lxc options -lxc-conf="lxc.cgroup.cpuset.cpus = 0,1" + --sig-proxy=true: Proxify all received signal to the process (even in non-tty mode) + --expose=[]: Expose a port from the container without publishing it to your host + --link="": Add link to another container (name:alias) + --name="": Assign the specified name to the container. If no name is specific docker will generate a random name + -P, --publish-all=false: Publish all exposed ports to the host interfaces The ``docker run`` command first ``creates`` a writeable container layer over the specified image, and then ``starts`` it using the specified command. That @@ -1042,7 +1043,7 @@ Examples: .. code-block:: bash - $ sudo docker run -cidfile /tmp/docker_test.cid ubuntu echo "test" + $ sudo docker run --cidfile /tmp/docker_test.cid ubuntu echo "test" This will create a container and print ``test`` to the console. The ``cidfile`` flag makes Docker attempt to create a new file and write the @@ -1051,7 +1052,7 @@ error. Docker will close this file when ``docker run`` exits. .. code-block:: bash - $ sudo docker run -t -i -rm ubuntu bash + $ sudo docker run -t -i --rm ubuntu bash root@bc338942ef20:/# mount -t tmpfs none /mnt mount: permission denied @@ -1063,7 +1064,7 @@ allow it to run: .. code-block:: bash - $ sudo docker run -privileged ubuntu bash + $ sudo docker run --privileged ubuntu bash root@50e3f57e16e6:/# mount -t tmpfs none /mnt root@50e3f57e16e6:/# df -h Filesystem Size Used Avail Use% Mounted on @@ -1104,7 +1105,7 @@ in Docker. .. code-block:: bash - $ sudo docker run -expose 80 ubuntu bash + $ sudo docker run --expose 80 ubuntu bash This exposes port ``80`` of the container for use within a link without publishing the port to the host system's interfaces. :ref:`port_redirection` @@ -1112,28 +1113,28 @@ explains in detail how to manipulate ports in Docker. .. code-block:: bash - $ sudo docker run -name console -t -i ubuntu bash + $ sudo docker run --name console -t -i ubuntu bash This will create and run a new container with the container name being ``console``. .. code-block:: bash - $ sudo docker run -link /redis:redis -name console ubuntu bash + $ sudo docker run --link /redis:redis --name console ubuntu bash -The ``-link`` flag will link the container named ``/redis`` into the +The ``--link`` flag will link the container named ``/redis`` into the newly created container with the alias ``redis``. The new container can access the network and environment of the redis container via -environment variables. The ``-name`` flag will assign the name ``console`` +environment variables. The ``--name`` flag will assign the name ``console`` to the newly created container. .. code-block:: bash - $ sudo docker run -volumes-from 777f7dc92da7,ba8c0c54f0f2:ro -i -t ubuntu pwd + $ sudo docker run --volumes-from 777f7dc92da7,ba8c0c54f0f2:ro -i -t ubuntu pwd -The ``-volumes-from`` flag mounts all the defined volumes from the +The ``--volumes-from`` flag mounts all the defined volumes from the referenced containers. Containers can be specified by a comma seperated -list or by repetitions of the ``-volumes-from`` argument. The container +list or by repetitions of the ``--volumes-from`` argument. The container ID may be optionally suffixed with ``:ro`` or ``:rw`` to mount the volumes in read-only or read-write mode, respectively. By default, the volumes are mounted in the same mode (read write or read only) as the reference container. @@ -1143,11 +1144,11 @@ A complete example .. code-block:: bash - $ sudo docker run -d -name static static-web-files sh - $ sudo docker run -d -expose=8098 -name riak riakserver - $ sudo docker run -d -m 100m -e DEVELOPMENT=1 -e BRANCH=example-code -v $(pwd):/app/bin:ro -name app appserver - $ sudo docker run -d -p 1443:443 -dns=dns.dev.org -v /var/log/httpd -volumes-from static -link riak -link app -h www.sven.dev.org -name web webserver - $ sudo docker run -t -i -rm -volumes-from web -w /var/log/httpd busybox tail -f access.log + $ sudo docker run -d --name static static-web-files sh + $ sudo docker run -d --expose=8098 --name riak riakserver + $ sudo docker run -d -m 100m -e DEVELOPMENT=1 -e BRANCH=example-code -v $(pwd):/app/bin:ro --name app appserver + $ sudo docker run -d -p 1443:443 --dns=dns.dev.org -v /var/log/httpd --volumes-from static --link riak --link app -h www.sven.dev.org --name web webserver + $ sudo docker run -t -i --rm --volumes-from web -w /var/log/httpd busybox tail -f access.log This example shows 5 containers that might be set up to test a web application change: @@ -1181,9 +1182,9 @@ This example shows 5 containers that might be set up to test a web application c Search the docker index for images - -notrunc=false: Don't truncate output - -stars=0: Only displays with at least xxx stars - -trusted=false: Only show trusted builds + --no-trunc=false: Don't truncate output + -s, --stars=0: Only displays with at least xxx stars + -t, --trusted=false: Only show trusted builds .. _cli_start: @@ -1196,8 +1197,8 @@ This example shows 5 containers that might be set up to test a web application c Start a stopped container - -a=false: Attach container's stdout/stderr and forward all signals to the process - -i=false: Attach container's stdin + -a, --attach=false: Attach container's stdout/stderr and forward all signals to the process + -i, --interactive=false: Attach container's stdin .. _cli_stop: @@ -1210,7 +1211,7 @@ This example shows 5 containers that might be set up to test a web application c Stop a running container (Send SIGTERM, and then SIGKILL after grace period) - -t=10: Number of seconds to wait for the container to stop before killing it. + -t, --time=10: Number of seconds to wait for the container to stop before killing it. The main process inside the container will receive SIGTERM, and after a grace period, SIGKILL @@ -1225,7 +1226,7 @@ The main process inside the container will receive SIGTERM, and after a grace pe Tag an image into a repository - -f=false: Force + -f, --force=false: Force .. _cli_top: diff --git a/pkg/mflag/LICENSE b/pkg/mflag/LICENSE new file mode 100644 index 0000000000..ebcfbcc779 --- /dev/null +++ b/pkg/mflag/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2014 The Docker & Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/pkg/mflag/README.md b/pkg/mflag/README.md new file mode 100644 index 0000000000..da00efa336 --- /dev/null +++ b/pkg/mflag/README.md @@ -0,0 +1,40 @@ +Package mflag (aka multiple-flag) implements command-line flag parsing. +It's an **hacky** fork of the [official golang package](http://golang.org/pkg/flag/) + +It adds: + +* both short and long flag version +`./example -s red` `./example --string blue` + +* multiple names for the same option +``` +$>./example -h +Usage of example: + -s, --string="": a simple string +``` + +___ +It is very flexible on purpose, so you can do things like: +``` +$>./example -h +Usage of example: + -s, -string, --string="": a simple string +``` + +Or: +``` +$>./example -h +Usage of example: + -oldflag, --newflag="": a simple string +``` + +You can also hide some flags from the usage, so if we want only `--newflag`: +``` +$>./example -h +Usage of example: + --newflag="": a simple string +$>./example -oldflag str +str +``` + +See [example.go](example/example.go) for more details. diff --git a/pkg/mflag/example/example.go b/pkg/mflag/example/example.go new file mode 100644 index 0000000000..dcff4277e2 --- /dev/null +++ b/pkg/mflag/example/example.go @@ -0,0 +1,27 @@ +package main + +import ( + "fmt" + "github.com/dotcloud/docker/pkg/flag" +) + +var ( + i int + str string + b, h bool +) + +func init() { + flag.BoolVar(&b, []string{"b"}, false, "a simple bool") + flag.IntVar(&i, []string{"#integer", "-integer"}, -1, "a simple integer") + flag.StringVar(&str, []string{"s", "#hidden", "-string"}, "", "a simple string") //-s -hidden and --string will work, but -hidden won't be in the usage + flag.BoolVar(&h, []string{"h", "#help", "-help"}, false, "display the help") + flag.Parse() +} +func main() { + if h { + flag.PrintDefaults() + } + fmt.Printf("%s\n", str) + fmt.Printf("%s\n", flag.Lookup("s").Value.String()) +} diff --git a/pkg/mflag/export_test.go b/pkg/mflag/export_test.go new file mode 100644 index 0000000000..7c1cea0bd7 --- /dev/null +++ b/pkg/mflag/export_test.go @@ -0,0 +1,17 @@ +// Copyright 2014 The Docker & Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mflag + +import "os" + +// Additional routines compiled into the package only during testing. + +// ResetForTesting clears all flag state and sets the usage function as directed. +// After calling ResetForTesting, parse errors in flag handling will not +// exit the program. +func ResetForTesting(usage func()) { + CommandLine = NewFlagSet(os.Args[0], ContinueOnError) + Usage = usage +} diff --git a/pkg/mflag/flag.go b/pkg/mflag/flag.go new file mode 100644 index 0000000000..8e22c0e959 --- /dev/null +++ b/pkg/mflag/flag.go @@ -0,0 +1,863 @@ +// Copyright 2014 The Docker & Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + Package flag implements command-line flag parsing. + + Usage: + + Define flags using flag.String(), Bool(), Int(), etc. + + This declares an integer flag, -f or --flagname, stored in the pointer ip, with type *int. + import "flag" + var ip = flag.Int([]string{"f", "-flagname"}, 1234, "help message for flagname") + If you like, you can bind the flag to a variable using the Var() functions. + var flagvar int + func init() { + // -flaghidden will work, but will be hidden from the usage + flag.IntVar(&flagvar, []string{"f", "#flaghidden", "-flagname"}, 1234, "help message for flagname") + } + Or you can create custom flags that satisfy the Value interface (with + pointer receivers) and couple them to flag parsing by + flag.Var(&flagVal, []string{"name"}, "help message for flagname") + For such flags, the default value is just the initial value of the variable. + + After all flags are defined, call + flag.Parse() + to parse the command line into the defined flags. + + Flags may then be used directly. If you're using the flags themselves, + they are all pointers; if you bind to variables, they're values. + fmt.Println("ip has value ", *ip) + fmt.Println("flagvar has value ", flagvar) + + After parsing, the arguments after the flag are available as the + slice flag.Args() or individually as flag.Arg(i). + The arguments are indexed from 0 through flag.NArg()-1. + + Command line flag syntax: + -flag + -flag=x + -flag x // non-boolean flags only + One or two minus signs may be used; they are equivalent. + The last form is not permitted for boolean flags because the + meaning of the command + cmd -x * + will change if there is a file called 0, false, etc. You must + use the -flag=false form to turn off a boolean flag. + + Flag parsing stops just before the first non-flag argument + ("-" is a non-flag argument) or after the terminator "--". + + Integer flags accept 1234, 0664, 0x1234 and may be negative. + Boolean flags may be 1, 0, t, f, true, false, TRUE, FALSE, True, False. + Duration flags accept any input valid for time.ParseDuration. + + The default set of command-line flags is controlled by + top-level functions. The FlagSet type allows one to define + independent sets of flags, such as to implement subcommands + in a command-line interface. The methods of FlagSet are + analogous to the top-level functions for the command-line + flag set. +*/ +package mflag + +import ( + "errors" + "fmt" + "io" + "os" + "sort" + "strconv" + "strings" + "time" +) + +// ErrHelp is the error returned if the flag -help is invoked but no such flag is defined. +var ErrHelp = errors.New("flag: help requested") + +// -- bool Value +type boolValue bool + +func newBoolValue(val bool, p *bool) *boolValue { + *p = val + return (*boolValue)(p) +} + +func (b *boolValue) Set(s string) error { + v, err := strconv.ParseBool(s) + *b = boolValue(v) + return err +} + +func (b *boolValue) Get() interface{} { return bool(*b) } + +func (b *boolValue) String() string { return fmt.Sprintf("%v", *b) } + +func (b *boolValue) IsBoolFlag() bool { return true } + +// optional interface to indicate boolean flags that can be +// supplied without "=value" text +type boolFlag interface { + Value + IsBoolFlag() bool +} + +// -- int Value +type intValue int + +func newIntValue(val int, p *int) *intValue { + *p = val + return (*intValue)(p) +} + +func (i *intValue) Set(s string) error { + v, err := strconv.ParseInt(s, 0, 64) + *i = intValue(v) + return err +} + +func (i *intValue) Get() interface{} { return int(*i) } + +func (i *intValue) String() string { return fmt.Sprintf("%v", *i) } + +// -- int64 Value +type int64Value int64 + +func newInt64Value(val int64, p *int64) *int64Value { + *p = val + return (*int64Value)(p) +} + +func (i *int64Value) Set(s string) error { + v, err := strconv.ParseInt(s, 0, 64) + *i = int64Value(v) + return err +} + +func (i *int64Value) Get() interface{} { return int64(*i) } + +func (i *int64Value) String() string { return fmt.Sprintf("%v", *i) } + +// -- uint Value +type uintValue uint + +func newUintValue(val uint, p *uint) *uintValue { + *p = val + return (*uintValue)(p) +} + +func (i *uintValue) Set(s string) error { + v, err := strconv.ParseUint(s, 0, 64) + *i = uintValue(v) + return err +} + +func (i *uintValue) Get() interface{} { return uint(*i) } + +func (i *uintValue) String() string { return fmt.Sprintf("%v", *i) } + +// -- uint64 Value +type uint64Value uint64 + +func newUint64Value(val uint64, p *uint64) *uint64Value { + *p = val + return (*uint64Value)(p) +} + +func (i *uint64Value) Set(s string) error { + v, err := strconv.ParseUint(s, 0, 64) + *i = uint64Value(v) + return err +} + +func (i *uint64Value) Get() interface{} { return uint64(*i) } + +func (i *uint64Value) String() string { return fmt.Sprintf("%v", *i) } + +// -- string Value +type stringValue string + +func newStringValue(val string, p *string) *stringValue { + *p = val + return (*stringValue)(p) +} + +func (s *stringValue) Set(val string) error { + *s = stringValue(val) + return nil +} + +func (s *stringValue) Get() interface{} { return string(*s) } + +func (s *stringValue) String() string { return fmt.Sprintf("%s", *s) } + +// -- float64 Value +type float64Value float64 + +func newFloat64Value(val float64, p *float64) *float64Value { + *p = val + return (*float64Value)(p) +} + +func (f *float64Value) Set(s string) error { + v, err := strconv.ParseFloat(s, 64) + *f = float64Value(v) + return err +} + +func (f *float64Value) Get() interface{} { return float64(*f) } + +func (f *float64Value) String() string { return fmt.Sprintf("%v", *f) } + +// -- time.Duration Value +type durationValue time.Duration + +func newDurationValue(val time.Duration, p *time.Duration) *durationValue { + *p = val + return (*durationValue)(p) +} + +func (d *durationValue) Set(s string) error { + v, err := time.ParseDuration(s) + *d = durationValue(v) + return err +} + +func (d *durationValue) Get() interface{} { return time.Duration(*d) } + +func (d *durationValue) String() string { return (*time.Duration)(d).String() } + +// Value is the interface to the dynamic value stored in a flag. +// (The default value is represented as a string.) +// +// If a Value has an IsBoolFlag() bool method returning true, +// the command-line parser makes -name equivalent to -name=true +// rather than using the next command-line argument. +type Value interface { + String() string + Set(string) error +} + +// Getter is an interface that allows the contents of a Value to be retrieved. +// It wraps the Value interface, rather than being part of it, because it +// appeared after Go 1 and its compatibility rules. All Value types provided +// by this package satisfy the Getter interface. +type Getter interface { + Value + Get() interface{} +} + +// ErrorHandling defines how to handle flag parsing errors. +type ErrorHandling int + +const ( + ContinueOnError ErrorHandling = iota + ExitOnError + PanicOnError +) + +// A FlagSet represents a set of defined flags. The zero value of a FlagSet +// has no name and has ContinueOnError error handling. +type FlagSet struct { + // Usage is the function called when an error occurs while parsing flags. + // The field is a function (not a method) that may be changed to point to + // a custom error handler. + Usage func() + + name string + parsed bool + actual map[string]*Flag + formal map[string]*Flag + args []string // arguments after flags + errorHandling ErrorHandling + output io.Writer // nil means stderr; use out() accessor +} + +// A Flag represents the state of a flag. +type Flag struct { + Names []string // name as it appears on command line + Usage string // help message + Value Value // value as set + DefValue string // default value (as text); for usage message +} + +// sortFlags returns the flags as a slice in lexicographical sorted order. +func sortFlags(flags map[string]*Flag) []*Flag { + var list sort.StringSlice + for _, f := range flags { + found := false + fName := strings.TrimPrefix(strings.TrimPrefix(f.Names[0], "#"), "-") + for _, name := range list { + if name == fName { + found = true + break + } + } + if !found { + list = append(list, fName) + } + } + list.Sort() + result := make([]*Flag, len(list)) + for i, name := range list { + result[i] = flags[name] + } + return result +} + +func (f *FlagSet) out() io.Writer { + if f.output == nil { + return os.Stderr + } + return f.output +} + +// SetOutput sets the destination for usage and error messages. +// If output is nil, os.Stderr is used. +func (f *FlagSet) SetOutput(output io.Writer) { + f.output = output +} + +// VisitAll visits the flags in lexicographical order, calling fn for each. +// It visits all flags, even those not set. +func (f *FlagSet) VisitAll(fn func(*Flag)) { + for _, flag := range sortFlags(f.formal) { + fn(flag) + } +} + +// VisitAll visits the command-line flags in lexicographical order, calling +// fn for each. It visits all flags, even those not set. +func VisitAll(fn func(*Flag)) { + CommandLine.VisitAll(fn) +} + +// Visit visits the flags in lexicographical order, calling fn for each. +// It visits only those flags that have been set. +func (f *FlagSet) Visit(fn func(*Flag)) { + for _, flag := range sortFlags(f.actual) { + fn(flag) + } +} + +// Visit visits the command-line flags in lexicographical order, calling fn +// for each. It visits only those flags that have been set. +func Visit(fn func(*Flag)) { + CommandLine.Visit(fn) +} + +// Lookup returns the Flag structure of the named flag, returning nil if none exists. +func (f *FlagSet) Lookup(name string) *Flag { + return f.formal[name] +} + +// Lookup returns the Flag structure of the named command-line flag, +// returning nil if none exists. +func Lookup(name string) *Flag { + return CommandLine.formal[name] +} + +// Set sets the value of the named flag. +func (f *FlagSet) Set(name, value string) error { + flag, ok := f.formal[name] + if !ok { + return fmt.Errorf("no such flag -%v", name) + } + err := flag.Value.Set(value) + if err != nil { + return err + } + if f.actual == nil { + f.actual = make(map[string]*Flag) + } + f.actual[name] = flag + return nil +} + +// Set sets the value of the named command-line flag. +func Set(name, value string) error { + return CommandLine.Set(name, value) +} + +// PrintDefaults prints, to standard error unless configured +// otherwise, the default values of all defined flags in the set. +func (f *FlagSet) PrintDefaults() { + f.VisitAll(func(flag *Flag) { + format := " -%s=%s: %s\n" + if _, ok := flag.Value.(*stringValue); ok { + // put quotes on the value + format = " -%s=%q: %s\n" + } + names := []string{} + for _, name := range flag.Names { + if name[0] != '#' { + names = append(names, name) + } + } + fmt.Fprintf(f.out(), format, strings.Join(names, ", -"), flag.DefValue, flag.Usage) + }) +} + +// PrintDefaults prints to standard error the default values of all defined command-line flags. +func PrintDefaults() { + CommandLine.PrintDefaults() +} + +// defaultUsage is the default function to print a usage message. +func defaultUsage(f *FlagSet) { + if f.name == "" { + fmt.Fprintf(f.out(), "Usage:\n") + } else { + fmt.Fprintf(f.out(), "Usage of %s:\n", f.name) + } + f.PrintDefaults() +} + +// NOTE: Usage is not just defaultUsage(CommandLine) +// because it serves (via godoc flag Usage) as the example +// for how to write your own usage function. + +// Usage prints to standard error a usage message documenting all defined command-line flags. +// The function is a variable that may be changed to point to a custom function. +var Usage = func() { + fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) + PrintDefaults() +} + +// NFlag returns the number of flags that have been set. +func (f *FlagSet) NFlag() int { return len(f.actual) } + +// NFlag returns the number of command-line flags that have been set. +func NFlag() int { return len(CommandLine.actual) } + +// Arg returns the i'th argument. Arg(0) is the first remaining argument +// after flags have been processed. +func (f *FlagSet) Arg(i int) string { + if i < 0 || i >= len(f.args) { + return "" + } + return f.args[i] +} + +// Arg returns the i'th command-line argument. Arg(0) is the first remaining argument +// after flags have been processed. +func Arg(i int) string { + return CommandLine.Arg(i) +} + +// NArg is the number of arguments remaining after flags have been processed. +func (f *FlagSet) NArg() int { return len(f.args) } + +// NArg is the number of arguments remaining after flags have been processed. +func NArg() int { return len(CommandLine.args) } + +// Args returns the non-flag arguments. +func (f *FlagSet) Args() []string { return f.args } + +// Args returns the non-flag command-line arguments. +func Args() []string { return CommandLine.args } + +// BoolVar defines a bool flag with specified name, default value, and usage string. +// The argument p points to a bool variable in which to store the value of the flag. +func (f *FlagSet) BoolVar(p *bool, names []string, value bool, usage string) { + f.Var(newBoolValue(value, p), names, usage) +} + +// BoolVar defines a bool flag with specified name, default value, and usage string. +// The argument p points to a bool variable in which to store the value of the flag. +func BoolVar(p *bool, names []string, value bool, usage string) { + CommandLine.Var(newBoolValue(value, p), names, usage) +} + +// Bool defines a bool flag with specified name, default value, and usage string. +// The return value is the address of a bool variable that stores the value of the flag. +func (f *FlagSet) Bool(names []string, value bool, usage string) *bool { + p := new(bool) + f.BoolVar(p, names, value, usage) + return p +} + +// Bool defines a bool flag with specified name, default value, and usage string. +// The return value is the address of a bool variable that stores the value of the flag. +func Bool(names []string, value bool, usage string) *bool { + return CommandLine.Bool(names, value, usage) +} + +// IntVar defines an int flag with specified name, default value, and usage string. +// The argument p points to an int variable in which to store the value of the flag. +func (f *FlagSet) IntVar(p *int, names []string, value int, usage string) { + f.Var(newIntValue(value, p), names, usage) +} + +// IntVar defines an int flag with specified name, default value, and usage string. +// The argument p points to an int variable in which to store the value of the flag. +func IntVar(p *int, names []string, value int, usage string) { + CommandLine.Var(newIntValue(value, p), names, usage) +} + +// Int defines an int flag with specified name, default value, and usage string. +// The return value is the address of an int variable that stores the value of the flag. +func (f *FlagSet) Int(names []string, value int, usage string) *int { + p := new(int) + f.IntVar(p, names, value, usage) + return p +} + +// Int defines an int flag with specified name, default value, and usage string. +// The return value is the address of an int variable that stores the value of the flag. +func Int(names []string, value int, usage string) *int { + return CommandLine.Int(names, value, usage) +} + +// Int64Var defines an int64 flag with specified name, default value, and usage string. +// The argument p points to an int64 variable in which to store the value of the flag. +func (f *FlagSet) Int64Var(p *int64, names []string, value int64, usage string) { + f.Var(newInt64Value(value, p), names, usage) +} + +// Int64Var defines an int64 flag with specified name, default value, and usage string. +// The argument p points to an int64 variable in which to store the value of the flag. +func Int64Var(p *int64, names []string, value int64, usage string) { + CommandLine.Var(newInt64Value(value, p), names, usage) +} + +// Int64 defines an int64 flag with specified name, default value, and usage string. +// The return value is the address of an int64 variable that stores the value of the flag. +func (f *FlagSet) Int64(names []string, value int64, usage string) *int64 { + p := new(int64) + f.Int64Var(p, names, value, usage) + return p +} + +// Int64 defines an int64 flag with specified name, default value, and usage string. +// The return value is the address of an int64 variable that stores the value of the flag. +func Int64(names []string, value int64, usage string) *int64 { + return CommandLine.Int64(names, value, usage) +} + +// UintVar defines a uint flag with specified name, default value, and usage string. +// The argument p points to a uint variable in which to store the value of the flag. +func (f *FlagSet) UintVar(p *uint, names []string, value uint, usage string) { + f.Var(newUintValue(value, p), names, usage) +} + +// UintVar defines a uint flag with specified name, default value, and usage string. +// The argument p points to a uint variable in which to store the value of the flag. +func UintVar(p *uint, names []string, value uint, usage string) { + CommandLine.Var(newUintValue(value, p), names, usage) +} + +// Uint defines a uint flag with specified name, default value, and usage string. +// The return value is the address of a uint variable that stores the value of the flag. +func (f *FlagSet) Uint(names []string, value uint, usage string) *uint { + p := new(uint) + f.UintVar(p, names, value, usage) + return p +} + +// Uint defines a uint flag with specified name, default value, and usage string. +// The return value is the address of a uint variable that stores the value of the flag. +func Uint(names []string, value uint, usage string) *uint { + return CommandLine.Uint(names, value, usage) +} + +// Uint64Var defines a uint64 flag with specified name, default value, and usage string. +// The argument p points to a uint64 variable in which to store the value of the flag. +func (f *FlagSet) Uint64Var(p *uint64, names []string, value uint64, usage string) { + f.Var(newUint64Value(value, p), names, usage) +} + +// Uint64Var defines a uint64 flag with specified name, default value, and usage string. +// The argument p points to a uint64 variable in which to store the value of the flag. +func Uint64Var(p *uint64, names []string, value uint64, usage string) { + CommandLine.Var(newUint64Value(value, p), names, usage) +} + +// Uint64 defines a uint64 flag with specified name, default value, and usage string. +// The return value is the address of a uint64 variable that stores the value of the flag. +func (f *FlagSet) Uint64(names []string, value uint64, usage string) *uint64 { + p := new(uint64) + f.Uint64Var(p, names, value, usage) + return p +} + +// Uint64 defines a uint64 flag with specified name, default value, and usage string. +// The return value is the address of a uint64 variable that stores the value of the flag. +func Uint64(names []string, value uint64, usage string) *uint64 { + return CommandLine.Uint64(names, value, usage) +} + +// StringVar defines a string flag with specified name, default value, and usage string. +// The argument p points to a string variable in which to store the value of the flag. +func (f *FlagSet) StringVar(p *string, names []string, value string, usage string) { + f.Var(newStringValue(value, p), names, usage) +} + +// StringVar defines a string flag with specified name, default value, and usage string. +// The argument p points to a string variable in which to store the value of the flag. +func StringVar(p *string, names []string, value string, usage string) { + CommandLine.Var(newStringValue(value, p), names, usage) +} + +// String defines a string flag with specified name, default value, and usage string. +// The return value is the address of a string variable that stores the value of the flag. +func (f *FlagSet) String(names []string, value string, usage string) *string { + p := new(string) + f.StringVar(p, names, value, usage) + return p +} + +// String defines a string flag with specified name, default value, and usage string. +// The return value is the address of a string variable that stores the value of the flag. +func String(names []string, value string, usage string) *string { + return CommandLine.String(names, value, usage) +} + +// Float64Var defines a float64 flag with specified name, default value, and usage string. +// The argument p points to a float64 variable in which to store the value of the flag. +func (f *FlagSet) Float64Var(p *float64, names []string, value float64, usage string) { + f.Var(newFloat64Value(value, p), names, usage) +} + +// Float64Var defines a float64 flag with specified name, default value, and usage string. +// The argument p points to a float64 variable in which to store the value of the flag. +func Float64Var(p *float64, names []string, value float64, usage string) { + CommandLine.Var(newFloat64Value(value, p), names, usage) +} + +// Float64 defines a float64 flag with specified name, default value, and usage string. +// The return value is the address of a float64 variable that stores the value of the flag. +func (f *FlagSet) Float64(names []string, value float64, usage string) *float64 { + p := new(float64) + f.Float64Var(p, names, value, usage) + return p +} + +// Float64 defines a float64 flag with specified name, default value, and usage string. +// The return value is the address of a float64 variable that stores the value of the flag. +func Float64(names []string, value float64, usage string) *float64 { + return CommandLine.Float64(names, value, usage) +} + +// DurationVar defines a time.Duration flag with specified name, default value, and usage string. +// The argument p points to a time.Duration variable in which to store the value of the flag. +func (f *FlagSet) DurationVar(p *time.Duration, names []string, value time.Duration, usage string) { + f.Var(newDurationValue(value, p), names, usage) +} + +// DurationVar defines a time.Duration flag with specified name, default value, and usage string. +// The argument p points to a time.Duration variable in which to store the value of the flag. +func DurationVar(p *time.Duration, names []string, value time.Duration, usage string) { + CommandLine.Var(newDurationValue(value, p), names, usage) +} + +// Duration defines a time.Duration flag with specified name, default value, and usage string. +// The return value is the address of a time.Duration variable that stores the value of the flag. +func (f *FlagSet) Duration(names []string, value time.Duration, usage string) *time.Duration { + p := new(time.Duration) + f.DurationVar(p, names, value, usage) + return p +} + +// Duration defines a time.Duration flag with specified name, default value, and usage string. +// The return value is the address of a time.Duration variable that stores the value of the flag. +func Duration(names []string, value time.Duration, usage string) *time.Duration { + return CommandLine.Duration(names, value, usage) +} + +// Var defines a flag with the specified name and usage string. The type and +// value of the flag are represented by the first argument, of type Value, which +// typically holds a user-defined implementation of Value. For instance, the +// caller could create a flag that turns a comma-separated string into a slice +// of strings by giving the slice the methods of Value; in particular, Set would +// decompose the comma-separated string into the slice. +func (f *FlagSet) Var(value Value, names []string, usage string) { + // Remember the default value as a string; it won't change. + flag := &Flag{names, usage, value, value.String()} + for _, name := range names { + name = strings.TrimPrefix(name, "#") + _, alreadythere := f.formal[name] + if alreadythere { + var msg string + if f.name == "" { + msg = fmt.Sprintf("flag redefined: %s", name) + } else { + msg = fmt.Sprintf("%s flag redefined: %s", f.name, name) + } + fmt.Fprintln(f.out(), msg) + panic(msg) // Happens only if flags are declared with identical names + } + if f.formal == nil { + f.formal = make(map[string]*Flag) + } + f.formal[name] = flag + } +} + +// Var defines a flag with the specified name and usage string. The type and +// value of the flag are represented by the first argument, of type Value, which +// typically holds a user-defined implementation of Value. For instance, the +// caller could create a flag that turns a comma-separated string into a slice +// of strings by giving the slice the methods of Value; in particular, Set would +// decompose the comma-separated string into the slice. +func Var(value Value, names []string, usage string) { + CommandLine.Var(value, names, usage) +} + +// failf prints to standard error a formatted error and usage message and +// returns the error. +func (f *FlagSet) failf(format string, a ...interface{}) error { + err := fmt.Errorf(format, a...) + fmt.Fprintln(f.out(), err) + f.usage() + return err +} + +// usage calls the Usage method for the flag set, or the usage function if +// the flag set is CommandLine. +func (f *FlagSet) usage() { + if f == CommandLine { + Usage() + } else if f.Usage == nil { + defaultUsage(f) + } else { + f.Usage() + } +} + +// parseOne parses one flag. It reports whether a flag was seen. +func (f *FlagSet) parseOne() (bool, error) { + if len(f.args) == 0 { + return false, nil + } + s := f.args[0] + if len(s) == 0 || s[0] != '-' || len(s) == 1 { + return false, nil + } + if s[1] == '-' && len(s) == 2 { // "--" terminates the flags + f.args = f.args[1:] + return false, nil + } + name := s[1:] + if len(name) == 0 || name[0] == '=' { + return false, f.failf("bad flag syntax: %s", s) + } + + // it's a flag. does it have an argument? + f.args = f.args[1:] + has_value := false + value := "" + for i := 1; i < len(name); i++ { // equals cannot be first + if name[i] == '=' { + value = name[i+1:] + has_value = true + name = name[0:i] + break + } + } + m := f.formal + flag, alreadythere := m[name] // BUG + if !alreadythere { + if name == "-help" || name == "help" || name == "h" { // special case for nice help message. + f.usage() + return false, ErrHelp + } + return false, f.failf("flag provided but not defined: -%s", name) + } + if fv, ok := flag.Value.(boolFlag); ok && fv.IsBoolFlag() { // special case: doesn't need an arg + if has_value { + if err := fv.Set(value); err != nil { + return false, f.failf("invalid boolean value %q for -%s: %v", value, name, err) + } + } else { + fv.Set("true") + } + } else { + // It must have a value, which might be the next argument. + if !has_value && len(f.args) > 0 { + // value is the next arg + has_value = true + value, f.args = f.args[0], f.args[1:] + } + if !has_value { + return false, f.failf("flag needs an argument: -%s", name) + } + if err := flag.Value.Set(value); err != nil { + return false, f.failf("invalid value %q for flag -%s: %v", value, name, err) + } + } + if f.actual == nil { + f.actual = make(map[string]*Flag) + } + f.actual[name] = flag + return true, nil +} + +// Parse parses flag definitions from the argument list, which should not +// include the command name. Must be called after all flags in the FlagSet +// are defined and before flags are accessed by the program. +// The return value will be ErrHelp if -help was set but not defined. +func (f *FlagSet) Parse(arguments []string) error { + f.parsed = true + f.args = arguments + for { + seen, err := f.parseOne() + if seen { + continue + } + if err == nil { + break + } + switch f.errorHandling { + case ContinueOnError: + return err + case ExitOnError: + os.Exit(2) + case PanicOnError: + panic(err) + } + } + return nil +} + +// Parsed reports whether f.Parse has been called. +func (f *FlagSet) Parsed() bool { + return f.parsed +} + +// Parse parses the command-line flags from os.Args[1:]. Must be called +// after all flags are defined and before flags are accessed by the program. +func Parse() { + // Ignore errors; CommandLine is set for ExitOnError. + CommandLine.Parse(os.Args[1:]) +} + +// Parsed returns true if the command-line flags have been parsed. +func Parsed() bool { + return CommandLine.Parsed() +} + +// CommandLine is the default set of command-line flags, parsed from os.Args. +// The top-level functions such as BoolVar, Arg, and on are wrappers for the +// methods of CommandLine. +var CommandLine = NewFlagSet(os.Args[0], ExitOnError) + +// NewFlagSet returns a new, empty flag set with the specified name and +// error handling property. +func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet { + f := &FlagSet{ + name: name, + errorHandling: errorHandling, + } + return f +} + +// Init sets the name and error handling property for a flag set. +// By default, the zero FlagSet uses an empty name and the +// ContinueOnError error handling policy. +func (f *FlagSet) Init(name string, errorHandling ErrorHandling) { + f.name = name + f.errorHandling = errorHandling +} diff --git a/pkg/mflag/flag_test.go b/pkg/mflag/flag_test.go new file mode 100644 index 0000000000..aecbbd0fbb --- /dev/null +++ b/pkg/mflag/flag_test.go @@ -0,0 +1,377 @@ +// Copyright 2014 The Docker & Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mflag_test + +import ( + "bytes" + "fmt" + . "github.com/dotcloud/docker/pkg/mflag" + "os" + "sort" + "strings" + "testing" + "time" +) + +func boolString(s string) string { + if s == "0" { + return "false" + } + return "true" +} + +func TestEverything(t *testing.T) { + ResetForTesting(nil) + Bool([]string{"test_bool"}, false, "bool value") + Int([]string{"test_int"}, 0, "int value") + Int64([]string{"test_int64"}, 0, "int64 value") + Uint([]string{"test_uint"}, 0, "uint value") + Uint64([]string{"test_uint64"}, 0, "uint64 value") + String([]string{"test_string"}, "0", "string value") + Float64([]string{"test_float64"}, 0, "float64 value") + Duration([]string{"test_duration"}, 0, "time.Duration value") + + m := make(map[string]*Flag) + desired := "0" + visitor := func(f *Flag) { + for _, name := range f.Names { + if len(name) > 5 && name[0:5] == "test_" { + m[name] = f + ok := false + switch { + case f.Value.String() == desired: + ok = true + case name == "test_bool" && f.Value.String() == boolString(desired): + ok = true + case name == "test_duration" && f.Value.String() == desired+"s": + ok = true + } + if !ok { + t.Error("Visit: bad value", f.Value.String(), "for", name) + } + } + } + } + VisitAll(visitor) + if len(m) != 8 { + t.Error("VisitAll misses some flags") + for k, v := range m { + t.Log(k, *v) + } + } + m = make(map[string]*Flag) + Visit(visitor) + if len(m) != 0 { + t.Errorf("Visit sees unset flags") + for k, v := range m { + t.Log(k, *v) + } + } + // Now set all flags + Set("test_bool", "true") + Set("test_int", "1") + Set("test_int64", "1") + Set("test_uint", "1") + Set("test_uint64", "1") + Set("test_string", "1") + Set("test_float64", "1") + Set("test_duration", "1s") + desired = "1" + Visit(visitor) + if len(m) != 8 { + t.Error("Visit fails after set") + for k, v := range m { + t.Log(k, *v) + } + } + // Now test they're visited in sort order. + var flagNames []string + Visit(func(f *Flag) { + for _, name := range f.Names { + flagNames = append(flagNames, name) + } + }) + if !sort.StringsAreSorted(flagNames) { + t.Errorf("flag names not sorted: %v", flagNames) + } +} + +func TestGet(t *testing.T) { + ResetForTesting(nil) + Bool([]string{"test_bool"}, true, "bool value") + Int([]string{"test_int"}, 1, "int value") + Int64([]string{"test_int64"}, 2, "int64 value") + Uint([]string{"test_uint"}, 3, "uint value") + Uint64([]string{"test_uint64"}, 4, "uint64 value") + String([]string{"test_string"}, "5", "string value") + Float64([]string{"test_float64"}, 6, "float64 value") + Duration([]string{"test_duration"}, 7, "time.Duration value") + + visitor := func(f *Flag) { + for _, name := range f.Names { + if len(name) > 5 && name[0:5] == "test_" { + g, ok := f.Value.(Getter) + if !ok { + t.Errorf("Visit: value does not satisfy Getter: %T", f.Value) + return + } + switch name { + case "test_bool": + ok = g.Get() == true + case "test_int": + ok = g.Get() == int(1) + case "test_int64": + ok = g.Get() == int64(2) + case "test_uint": + ok = g.Get() == uint(3) + case "test_uint64": + ok = g.Get() == uint64(4) + case "test_string": + ok = g.Get() == "5" + case "test_float64": + ok = g.Get() == float64(6) + case "test_duration": + ok = g.Get() == time.Duration(7) + } + if !ok { + t.Errorf("Visit: bad value %T(%v) for %s", g.Get(), g.Get(), name) + } + } + } + } + VisitAll(visitor) +} + +func TestUsage(t *testing.T) { + called := false + ResetForTesting(func() { called = true }) + if CommandLine.Parse([]string{"-x"}) == nil { + t.Error("parse did not fail for unknown flag") + } + if !called { + t.Error("did not call Usage for unknown flag") + } +} + +func testParse(f *FlagSet, t *testing.T) { + if f.Parsed() { + t.Error("f.Parse() = true before Parse") + } + boolFlag := f.Bool([]string{"bool"}, false, "bool value") + bool2Flag := f.Bool([]string{"bool2"}, false, "bool2 value") + intFlag := f.Int([]string{"-int"}, 0, "int value") + int64Flag := f.Int64([]string{"-int64"}, 0, "int64 value") + uintFlag := f.Uint([]string{"uint"}, 0, "uint value") + uint64Flag := f.Uint64([]string{"-uint64"}, 0, "uint64 value") + stringFlag := f.String([]string{"string"}, "0", "string value") + float64Flag := f.Float64([]string{"float64"}, 0, "float64 value") + durationFlag := f.Duration([]string{"duration"}, 5*time.Second, "time.Duration value") + extra := "one-extra-argument" + args := []string{ + "-bool", + "-bool2=true", + "--int", "22", + "--int64", "0x23", + "-uint", "24", + "--uint64", "25", + "-string", "hello", + "-float64", "2718e28", + "-duration", "2m", + extra, + } + if err := f.Parse(args); err != nil { + t.Fatal(err) + } + if !f.Parsed() { + t.Error("f.Parse() = false after Parse") + } + if *boolFlag != true { + t.Error("bool flag should be true, is ", *boolFlag) + } + if *bool2Flag != true { + t.Error("bool2 flag should be true, is ", *bool2Flag) + } + if *intFlag != 22 { + t.Error("int flag should be 22, is ", *intFlag) + } + if *int64Flag != 0x23 { + t.Error("int64 flag should be 0x23, is ", *int64Flag) + } + if *uintFlag != 24 { + t.Error("uint flag should be 24, is ", *uintFlag) + } + if *uint64Flag != 25 { + t.Error("uint64 flag should be 25, is ", *uint64Flag) + } + if *stringFlag != "hello" { + t.Error("string flag should be `hello`, is ", *stringFlag) + } + if *float64Flag != 2718e28 { + t.Error("float64 flag should be 2718e28, is ", *float64Flag) + } + if *durationFlag != 2*time.Minute { + t.Error("duration flag should be 2m, is ", *durationFlag) + } + if len(f.Args()) != 1 { + t.Error("expected one argument, got", len(f.Args())) + } else if f.Args()[0] != extra { + t.Errorf("expected argument %q got %q", extra, f.Args()[0]) + } +} + +func TestParse(t *testing.T) { + ResetForTesting(func() { t.Error("bad parse") }) + testParse(CommandLine, t) +} + +func TestFlagSetParse(t *testing.T) { + testParse(NewFlagSet("test", ContinueOnError), t) +} + +// Declare a user-defined flag type. +type flagVar []string + +func (f *flagVar) String() string { + return fmt.Sprint([]string(*f)) +} + +func (f *flagVar) Set(value string) error { + *f = append(*f, value) + return nil +} + +func TestUserDefined(t *testing.T) { + var flags FlagSet + flags.Init("test", ContinueOnError) + var v flagVar + flags.Var(&v, []string{"v"}, "usage") + if err := flags.Parse([]string{"-v", "1", "-v", "2", "-v=3"}); err != nil { + t.Error(err) + } + if len(v) != 3 { + t.Fatal("expected 3 args; got ", len(v)) + } + expect := "[1 2 3]" + if v.String() != expect { + t.Errorf("expected value %q got %q", expect, v.String()) + } +} + +// Declare a user-defined boolean flag type. +type boolFlagVar struct { + count int +} + +func (b *boolFlagVar) String() string { + return fmt.Sprintf("%d", b.count) +} + +func (b *boolFlagVar) Set(value string) error { + if value == "true" { + b.count++ + } + return nil +} + +func (b *boolFlagVar) IsBoolFlag() bool { + return b.count < 4 +} + +func TestUserDefinedBool(t *testing.T) { + var flags FlagSet + flags.Init("test", ContinueOnError) + var b boolFlagVar + var err error + flags.Var(&b, []string{"b"}, "usage") + if err = flags.Parse([]string{"-b", "-b", "-b", "-b=true", "-b=false", "-b", "barg", "-b"}); err != nil { + if b.count < 4 { + t.Error(err) + } + } + + if b.count != 4 { + t.Errorf("want: %d; got: %d", 4, b.count) + } + + if err == nil { + t.Error("expected error; got none") + } +} + +func TestSetOutput(t *testing.T) { + var flags FlagSet + var buf bytes.Buffer + flags.SetOutput(&buf) + flags.Init("test", ContinueOnError) + flags.Parse([]string{"-unknown"}) + if out := buf.String(); !strings.Contains(out, "-unknown") { + t.Logf("expected output mentioning unknown; got %q", out) + } +} + +// This tests that one can reset the flags. This still works but not well, and is +// superseded by FlagSet. +func TestChangingArgs(t *testing.T) { + ResetForTesting(func() { t.Fatal("bad parse") }) + oldArgs := os.Args + defer func() { os.Args = oldArgs }() + os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"} + before := Bool([]string{"before"}, false, "") + if err := CommandLine.Parse(os.Args[1:]); err != nil { + t.Fatal(err) + } + cmd := Arg(0) + os.Args = Args() + after := Bool([]string{"after"}, false, "") + Parse() + args := Args() + + if !*before || cmd != "subcmd" || !*after || len(args) != 1 || args[0] != "args" { + t.Fatalf("expected true subcmd true [args] got %v %v %v %v", *before, cmd, *after, args) + } +} + +// Test that -help invokes the usage message and returns ErrHelp. +func TestHelp(t *testing.T) { + var helpCalled = false + fs := NewFlagSet("help test", ContinueOnError) + fs.Usage = func() { helpCalled = true } + var flag bool + fs.BoolVar(&flag, []string{"flag"}, false, "regular flag") + // Regular flag invocation should work + err := fs.Parse([]string{"-flag=true"}) + if err != nil { + t.Fatal("expected no error; got ", err) + } + if !flag { + t.Error("flag was not set by -flag") + } + if helpCalled { + t.Error("help called for regular flag") + helpCalled = false // reset for next test + } + // Help flag should work as expected. + err = fs.Parse([]string{"-help"}) + if err == nil { + t.Fatal("error expected") + } + if err != ErrHelp { + t.Fatal("expected ErrHelp; got ", err) + } + if !helpCalled { + t.Fatal("help was not called") + } + // If we define a help flag, that should override. + var help bool + fs.BoolVar(&help, []string{"help"}, false, "help flag") + helpCalled = false + err = fs.Parse([]string{"-help"}) + if err != nil { + t.Fatal("expected no error for defined -help; got ", err) + } + if helpCalled { + t.Fatal("help was called; should not have been for defined help flag") + } +}