Exit with non-zero code on first argument parsing error

Ignoring return value of ParseFlags leads to exit code 0 if bad
arguments are supplied. This patch makes sure that subcommands exit
with non-zero code in such a case.

Signed-off-by: Michal Minar <miminar@redhat.com>
This commit is contained in:
Michal Minar 2014-11-24 17:34:13 +01:00
parent 08f0f1ee1d
commit 8a785792cd
3 changed files with 85 additions and 142 deletions

View File

@ -83,8 +83,14 @@ func (cli *DockerCli) Cmd(args ...string) error {
return cli.CmdHelp() return cli.CmdHelp()
} }
func (cli *DockerCli) Subcmd(name, signature, description string) *flag.FlagSet { func (cli *DockerCli) Subcmd(name, signature, description string, exitOnError bool) *flag.FlagSet {
flags := flag.NewFlagSet(name, flag.ContinueOnError) var errorHandling flag.ErrorHandling
if exitOnError {
errorHandling = flag.ExitOnError
} else {
errorHandling = flag.ContinueOnError
}
flags := flag.NewFlagSet(name, errorHandling)
flags.Usage = func() { flags.Usage = func() {
options := "" options := ""
if flags.FlagCountUndeprecated() > 0 { if flags.FlagCountUndeprecated() > 0 {

View File

@ -77,7 +77,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
} }
func (cli *DockerCli) CmdBuild(args ...string) error { func (cli *DockerCli) CmdBuild(args ...string) error {
cmd := cli.Subcmd("build", "PATH | URL | -", "Build a new image from the source code at PATH") cmd := cli.Subcmd("build", "PATH | URL | -", "Build a new image from the source code at PATH", true)
tag := cmd.String([]string{"t", "-tag"}, "", "Repository name (and optionally a tag) to be applied to the resulting image in case of success") 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 the verbose output generated by the containers") suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the verbose output generated by the containers")
noCache := cmd.Bool([]string{"#no-cache", "-no-cache"}, false, "Do not use cache when building the image") noCache := cmd.Bool([]string{"#no-cache", "-no-cache"}, false, "Do not use cache when building the image")
@ -86,9 +86,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
pull := cmd.Bool([]string{"-pull"}, false, "Always attempt to pull a newer version of the image") pull := cmd.Bool([]string{"-pull"}, false, "Always attempt to pull a newer version of the image")
cmd.Require(flag.Exact, 1) cmd.Require(flag.Exact, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
var ( var (
context archive.Archive context archive.Archive
@ -246,7 +244,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
// 'docker login': login / register a user to registry service. // 'docker login': login / register a user to registry service.
func (cli *DockerCli) CmdLogin(args ...string) error { func (cli *DockerCli) CmdLogin(args ...string) error {
cmd := cli.Subcmd("login", "[SERVER]", "Register or log in to a Docker registry server, if no server is specified \""+registry.IndexServerAddress()+"\" is the default.") cmd := cli.Subcmd("login", "[SERVER]", "Register or log in to a Docker registry server, if no server is specified \""+registry.IndexServerAddress()+"\" is the default.", true)
cmd.Require(flag.Max, 1) cmd.Require(flag.Max, 1)
var username, password, email string var username, password, email string
@ -255,9 +253,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
cmd.StringVar(&password, []string{"p", "-password"}, "", "Password") cmd.StringVar(&password, []string{"p", "-password"}, "", "Password")
cmd.StringVar(&email, []string{"e", "-email"}, "", "Email") cmd.StringVar(&email, []string{"e", "-email"}, "", "Email")
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
serverAddress := registry.IndexServerAddress() serverAddress := registry.IndexServerAddress()
if len(cmd.Args()) > 0 { if len(cmd.Args()) > 0 {
@ -364,12 +360,10 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
// log out from a Docker registry // log out from a Docker registry
func (cli *DockerCli) CmdLogout(args ...string) error { func (cli *DockerCli) CmdLogout(args ...string) error {
cmd := cli.Subcmd("logout", "[SERVER]", "Log out from a Docker registry, if no server is specified \""+registry.IndexServerAddress()+"\" is the default.") cmd := cli.Subcmd("logout", "[SERVER]", "Log out from a Docker registry, if no server is specified \""+registry.IndexServerAddress()+"\" is the default.", true)
cmd.Require(flag.Max, 1) cmd.Require(flag.Max, 1)
if err := utils.ParseFlags(cmd, args, false); err != nil { utils.ParseFlags(cmd, args, false)
return nil
}
serverAddress := registry.IndexServerAddress() serverAddress := registry.IndexServerAddress()
if len(cmd.Args()) > 0 { if len(cmd.Args()) > 0 {
serverAddress = cmd.Arg(0) serverAddress = cmd.Arg(0)
@ -391,12 +385,10 @@ func (cli *DockerCli) CmdLogout(args ...string) error {
// 'docker wait': block until a container stops // 'docker wait': block until a container stops
func (cli *DockerCli) CmdWait(args ...string) error { func (cli *DockerCli) CmdWait(args ...string) error {
cmd := cli.Subcmd("wait", "CONTAINER [CONTAINER...]", "Block until a container stops, then print its exit code.") cmd := cli.Subcmd("wait", "CONTAINER [CONTAINER...]", "Block until a container stops, then print its exit code.", true)
cmd.Require(flag.Min, 1) cmd.Require(flag.Min, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
var encounteredError error var encounteredError error
for _, name := range cmd.Args() { for _, name := range cmd.Args() {
@ -413,12 +405,10 @@ func (cli *DockerCli) CmdWait(args ...string) error {
// 'docker version': show version information // 'docker version': show version information
func (cli *DockerCli) CmdVersion(args ...string) error { func (cli *DockerCli) CmdVersion(args ...string) error {
cmd := cli.Subcmd("version", "", "Show the Docker version information.") cmd := cli.Subcmd("version", "", "Show the Docker version information.", true)
cmd.Require(flag.Exact, 0) cmd.Require(flag.Exact, 0)
if err := utils.ParseFlags(cmd, args, false); err != nil { utils.ParseFlags(cmd, args, false)
return nil
}
if dockerversion.VERSION != "" { if dockerversion.VERSION != "" {
fmt.Fprintf(cli.out, "Client version: %s\n", dockerversion.VERSION) fmt.Fprintf(cli.out, "Client version: %s\n", dockerversion.VERSION)
@ -457,11 +447,9 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
// 'docker info': display system-wide information. // 'docker info': display system-wide information.
func (cli *DockerCli) CmdInfo(args ...string) error { func (cli *DockerCli) CmdInfo(args ...string) error {
cmd := cli.Subcmd("info", "", "Display system-wide information") cmd := cli.Subcmd("info", "", "Display system-wide information", true)
cmd.Require(flag.Exact, 0) cmd.Require(flag.Exact, 0)
if err := utils.ParseFlags(cmd, args, false); err != nil { utils.ParseFlags(cmd, args, false)
return nil
}
body, _, err := readBody(cli.call("GET", "/info", nil, false)) body, _, err := readBody(cli.call("GET", "/info", nil, false))
if err != nil { if err != nil {
@ -573,13 +561,11 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
} }
func (cli *DockerCli) CmdStop(args ...string) error { func (cli *DockerCli) CmdStop(args ...string) error {
cmd := cli.Subcmd("stop", "CONTAINER [CONTAINER...]", "Stop a running container by sending SIGTERM and then SIGKILL after a grace period") cmd := cli.Subcmd("stop", "CONTAINER [CONTAINER...]", "Stop a running container by sending SIGTERM and then SIGKILL after a grace period", true)
nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Number of seconds to wait for the container to stop before killing it. Default is 10 seconds.") nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Number of seconds to wait for the container to stop before killing it. Default is 10 seconds.")
cmd.Require(flag.Min, 1) cmd.Require(flag.Min, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
v := url.Values{} v := url.Values{}
v.Set("t", strconv.Itoa(*nSeconds)) v.Set("t", strconv.Itoa(*nSeconds))
@ -598,13 +584,11 @@ func (cli *DockerCli) CmdStop(args ...string) error {
} }
func (cli *DockerCli) CmdRestart(args ...string) error { func (cli *DockerCli) CmdRestart(args ...string) error {
cmd := cli.Subcmd("restart", "CONTAINER [CONTAINER...]", "Restart a running container") cmd := cli.Subcmd("restart", "CONTAINER [CONTAINER...]", "Restart a running container", true)
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 is 10 seconds.") 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 is 10 seconds.")
cmd.Require(flag.Min, 1) cmd.Require(flag.Min, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
v := url.Values{} v := url.Values{}
v.Set("t", strconv.Itoa(*nSeconds)) v.Set("t", strconv.Itoa(*nSeconds))
@ -653,15 +637,13 @@ func (cli *DockerCli) CmdStart(args ...string) error {
cErr chan error cErr chan error
tty bool tty bool
cmd = cli.Subcmd("start", "CONTAINER [CONTAINER...]", "Restart a stopped container") cmd = cli.Subcmd("start", "CONTAINER [CONTAINER...]", "Restart a stopped container", true)
attach = cmd.Bool([]string{"a", "-attach"}, false, "Attach container's STDOUT and STDERR and forward all signals to the process") attach = cmd.Bool([]string{"a", "-attach"}, false, "Attach container's STDOUT and STDERR and forward all signals to the process")
openStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Attach container's STDIN") openStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Attach container's STDIN")
) )
cmd.Require(flag.Min, 1) cmd.Require(flag.Min, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
hijacked := make(chan io.Closer) hijacked := make(chan io.Closer)
@ -763,11 +745,9 @@ func (cli *DockerCli) CmdStart(args ...string) error {
} }
func (cli *DockerCli) CmdUnpause(args ...string) error { func (cli *DockerCli) CmdUnpause(args ...string) error {
cmd := cli.Subcmd("unpause", "CONTAINER", "Unpause all processes within a container") cmd := cli.Subcmd("unpause", "CONTAINER", "Unpause all processes within a container", true)
cmd.Require(flag.Exact, 1) cmd.Require(flag.Exact, 1)
if err := utils.ParseFlags(cmd, args, false); err != nil { utils.ParseFlags(cmd, args, false)
return nil
}
var encounteredError error var encounteredError error
for _, name := range cmd.Args() { for _, name := range cmd.Args() {
@ -782,11 +762,9 @@ func (cli *DockerCli) CmdUnpause(args ...string) error {
} }
func (cli *DockerCli) CmdPause(args ...string) error { func (cli *DockerCli) CmdPause(args ...string) error {
cmd := cli.Subcmd("pause", "CONTAINER", "Pause all processes within a container") cmd := cli.Subcmd("pause", "CONTAINER", "Pause all processes within a container", true)
cmd.Require(flag.Exact, 1) cmd.Require(flag.Exact, 1)
if err := utils.ParseFlags(cmd, args, false); err != nil { utils.ParseFlags(cmd, args, false)
return nil
}
var encounteredError error var encounteredError error
for _, name := range cmd.Args() { for _, name := range cmd.Args() {
@ -801,13 +779,11 @@ func (cli *DockerCli) CmdPause(args ...string) error {
} }
func (cli *DockerCli) CmdInspect(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 or image") cmd := cli.Subcmd("inspect", "CONTAINER|IMAGE [CONTAINER|IMAGE...]", "Return low-level information on a container or image", true)
tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template.") tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template.")
cmd.Require(flag.Min, 1) cmd.Require(flag.Min, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
var tmpl *template.Template var tmpl *template.Template
if *tmplStr != "" { if *tmplStr != "" {
@ -879,12 +855,10 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
} }
func (cli *DockerCli) CmdTop(args ...string) error { func (cli *DockerCli) CmdTop(args ...string) error {
cmd := cli.Subcmd("top", "CONTAINER [ps OPTIONS]", "Display the running processes of a container") cmd := cli.Subcmd("top", "CONTAINER [ps OPTIONS]", "Display the running processes of a container", true)
cmd.Require(flag.Min, 1) cmd.Require(flag.Min, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
val := url.Values{} val := url.Values{}
if cmd.NArg() > 1 { if cmd.NArg() > 1 {
@ -913,11 +887,9 @@ func (cli *DockerCli) CmdTop(args ...string) error {
} }
func (cli *DockerCli) CmdPort(args ...string) error { func (cli *DockerCli) CmdPort(args ...string) error {
cmd := cli.Subcmd("port", "CONTAINER [PRIVATE_PORT[/PROTO]]", "List port mappings for the CONTAINER, or lookup the public-facing port that is NAT-ed to the PRIVATE_PORT") cmd := cli.Subcmd("port", "CONTAINER [PRIVATE_PORT[/PROTO]]", "List port mappings for the CONTAINER, or lookup the public-facing port that is NAT-ed to the PRIVATE_PORT", true)
cmd.Require(flag.Min, 1) cmd.Require(flag.Min, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false) stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false)
if err != nil { if err != nil {
@ -966,15 +938,13 @@ func (cli *DockerCli) CmdPort(args ...string) error {
// 'docker rmi IMAGE' removes all images with the name IMAGE // 'docker rmi IMAGE' removes all images with the name IMAGE
func (cli *DockerCli) CmdRmi(args ...string) error { func (cli *DockerCli) CmdRmi(args ...string) error {
var ( var (
cmd = cli.Subcmd("rmi", "IMAGE [IMAGE...]", "Remove one or more images") cmd = cli.Subcmd("rmi", "IMAGE [IMAGE...]", "Remove one or more images", true)
force = cmd.Bool([]string{"f", "-force"}, false, "Force removal of the image") force = cmd.Bool([]string{"f", "-force"}, false, "Force removal of the image")
noprune = cmd.Bool([]string{"-no-prune"}, false, "Do not delete untagged parents") noprune = cmd.Bool([]string{"-no-prune"}, false, "Do not delete untagged parents")
) )
cmd.Require(flag.Min, 1) cmd.Require(flag.Min, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
v := url.Values{} v := url.Values{}
if *force { if *force {
@ -1010,14 +980,12 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
} }
func (cli *DockerCli) CmdHistory(args ...string) error { func (cli *DockerCli) CmdHistory(args ...string) error {
cmd := cli.Subcmd("history", "IMAGE", "Show the history of an image") cmd := cli.Subcmd("history", "IMAGE", "Show the history of an image", true)
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs") quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
cmd.Require(flag.Exact, 1) cmd.Require(flag.Exact, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
body, _, err := readBody(cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, false)) body, _, err := readBody(cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, false))
if err != nil { if err != nil {
@ -1064,15 +1032,13 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
} }
func (cli *DockerCli) CmdRm(args ...string) error { func (cli *DockerCli) CmdRm(args ...string) error {
cmd := cli.Subcmd("rm", "CONTAINER [CONTAINER...]", "Remove one or more containers") cmd := cli.Subcmd("rm", "CONTAINER [CONTAINER...]", "Remove one or more containers", true)
v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated with the container") v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated with the container")
link := cmd.Bool([]string{"l", "#link", "-link"}, false, "Remove the specified link and not the underlying container") link := cmd.Bool([]string{"l", "#link", "-link"}, false, "Remove the specified link and not the underlying container")
force := cmd.Bool([]string{"f", "-force"}, false, "Force the removal of a running container (uses SIGKILL)") force := cmd.Bool([]string{"f", "-force"}, false, "Force the removal of a running container (uses SIGKILL)")
cmd.Require(flag.Min, 1) cmd.Require(flag.Min, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
val := url.Values{} val := url.Values{}
if *v { if *v {
@ -1101,13 +1067,11 @@ func (cli *DockerCli) CmdRm(args ...string) error {
// 'docker kill NAME' kills a running container // 'docker kill NAME' kills a running container
func (cli *DockerCli) CmdKill(args ...string) error { func (cli *DockerCli) CmdKill(args ...string) error {
cmd := cli.Subcmd("kill", "CONTAINER [CONTAINER...]", "Kill a running container using SIGKILL or a specified signal") cmd := cli.Subcmd("kill", "CONTAINER [CONTAINER...]", "Kill a running container using SIGKILL or a specified signal", true)
signal := cmd.String([]string{"s", "-signal"}, "KILL", "Signal to send to the container") signal := cmd.String([]string{"s", "-signal"}, "KILL", "Signal to send to the container")
cmd.Require(flag.Min, 1) cmd.Require(flag.Min, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
var encounteredError error var encounteredError error
for _, name := range cmd.Args() { for _, name := range cmd.Args() {
@ -1122,12 +1086,10 @@ func (cli *DockerCli) CmdKill(args ...string) error {
} }
func (cli *DockerCli) CmdImport(args ...string) error { func (cli *DockerCli) CmdImport(args ...string) error {
cmd := cli.Subcmd("import", "URL|- [REPOSITORY[:TAG]]", "Create an empty filesystem image and import the contents of the tarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then optionally tag it.") cmd := cli.Subcmd("import", "URL|- [REPOSITORY[:TAG]]", "Create an empty filesystem image and import the contents of the tarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then optionally tag it.", true)
cmd.Require(flag.Min, 1) cmd.Require(flag.Min, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
var ( var (
v = url.Values{} v = url.Values{}
@ -1161,12 +1123,10 @@ func (cli *DockerCli) CmdImport(args ...string) error {
} }
func (cli *DockerCli) CmdPush(args ...string) error { func (cli *DockerCli) CmdPush(args ...string) error {
cmd := cli.Subcmd("push", "NAME[:TAG]", "Push an image or a repository to the registry") cmd := cli.Subcmd("push", "NAME[:TAG]", "Push an image or a repository to the registry", true)
cmd.Require(flag.Exact, 1) cmd.Require(flag.Exact, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
name := cmd.Arg(0) name := cmd.Arg(0)
@ -1224,13 +1184,11 @@ func (cli *DockerCli) CmdPush(args ...string) error {
} }
func (cli *DockerCli) CmdPull(args ...string) error { func (cli *DockerCli) CmdPull(args ...string) error {
cmd := cli.Subcmd("pull", "NAME[:TAG]", "Pull an image or a repository from the registry") cmd := cli.Subcmd("pull", "NAME[:TAG]", "Pull an image or a repository from the registry", true)
allTags := cmd.Bool([]string{"a", "-all-tags"}, false, "Download all tagged images in the repository") allTags := cmd.Bool([]string{"a", "-all-tags"}, false, "Download all tagged images in the repository")
cmd.Require(flag.Exact, 1) cmd.Require(flag.Exact, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
var ( var (
v = url.Values{} v = url.Values{}
@ -1288,7 +1246,7 @@ func (cli *DockerCli) CmdPull(args ...string) error {
} }
func (cli *DockerCli) CmdImages(args ...string) error { func (cli *DockerCli) CmdImages(args ...string) error {
cmd := cli.Subcmd("images", "[REPOSITORY]", "List images") cmd := cli.Subcmd("images", "[REPOSITORY]", "List images", true)
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs") 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 image layers)") all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (by default filter out the intermediate image layers)")
noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
@ -1300,9 +1258,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values (i.e. 'dangling=true')") cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values (i.e. 'dangling=true')")
cmd.Require(flag.Max, 1) cmd.Require(flag.Max, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
// Consolidate all filter flags, and sanity check them early. // Consolidate all filter flags, and sanity check them early.
// They'll get process in the daemon/server. // They'll get process in the daemon/server.
@ -1528,7 +1484,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
psFilterArgs = filters.Args{} psFilterArgs = filters.Args{}
v = url.Values{} v = url.Values{}
cmd = cli.Subcmd("ps", "", "List containers") cmd = cli.Subcmd("ps", "", "List containers", true)
quiet = cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs") quiet = cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
size = cmd.Bool([]string{"s", "-size"}, false, "Display total file sizes") size = cmd.Bool([]string{"s", "-size"}, false, "Display total file sizes")
all = cmd.Bool([]string{"a", "-all"}, false, "Show all containers. Only running containers are shown by default.") all = cmd.Bool([]string{"a", "-all"}, false, "Show all containers. Only running containers are shown by default.")
@ -1543,9 +1499,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values. Valid filters:\nexited=<int> - containers with exit code of <int>\nstatus=(restarting|running|paused|exited)") cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values. Valid filters:\nexited=<int> - containers with exit code of <int>\nstatus=(restarting|running|paused|exited)")
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
if *last == -1 && *nLatest { if *last == -1 && *nLatest {
*last = 1 *last = 1
} }
@ -1680,7 +1634,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
} }
func (cli *DockerCli) CmdCommit(args ...string) error { func (cli *DockerCli) CmdCommit(args ...string) error {
cmd := cli.Subcmd("commit", "CONTAINER [REPOSITORY[:TAG]]", "Create a new image from a container's changes") cmd := cli.Subcmd("commit", "CONTAINER [REPOSITORY[:TAG]]", "Create a new image from a container's changes", true)
flPause := cmd.Bool([]string{"p", "-pause"}, true, "Pause container during commit") flPause := cmd.Bool([]string{"p", "-pause"}, true, "Pause container during commit")
flComment := cmd.String([]string{"m", "-message"}, "", "Commit message") flComment := cmd.String([]string{"m", "-message"}, "", "Commit message")
flAuthor := cmd.String([]string{"a", "#author", "-author"}, "", "Author (e.g., \"John Hannibal Smith <hannibal@a-team.com>\")") flAuthor := cmd.String([]string{"a", "#author", "-author"}, "", "Author (e.g., \"John Hannibal Smith <hannibal@a-team.com>\")")
@ -1688,9 +1642,7 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
flConfig := cmd.String([]string{"#run", "#-run"}, "", "This option is deprecated and will be removed in a future version in favor of inline Dockerfile-compatible commands") flConfig := cmd.String([]string{"#run", "#-run"}, "", "This option is deprecated and will be removed in a future version in favor of inline Dockerfile-compatible commands")
cmd.Require(flag.Max, 2) cmd.Require(flag.Max, 2)
cmd.Require(flag.Min, 1) cmd.Require(flag.Min, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
var ( var (
name = cmd.Arg(0) name = cmd.Arg(0)
@ -1738,16 +1690,14 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
} }
func (cli *DockerCli) CmdEvents(args ...string) error { func (cli *DockerCli) CmdEvents(args ...string) error {
cmd := cli.Subcmd("events", "", "Get real time events from the server") cmd := cli.Subcmd("events", "", "Get real time events from the server", true)
since := cmd.String([]string{"#since", "-since"}, "", "Show all events created since timestamp") since := cmd.String([]string{"#since", "-since"}, "", "Show all events created since timestamp")
until := cmd.String([]string{"-until"}, "", "Stream events until this timestamp") until := cmd.String([]string{"-until"}, "", "Stream events until this timestamp")
flFilter := opts.NewListOpts(nil) flFilter := opts.NewListOpts(nil)
cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values (i.e. 'event=stop')") cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values (i.e. 'event=stop')")
cmd.Require(flag.Exact, 0) cmd.Require(flag.Exact, 0)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
var ( var (
v = url.Values{} v = url.Values{}
@ -1795,12 +1745,10 @@ func (cli *DockerCli) CmdEvents(args ...string) error {
} }
func (cli *DockerCli) CmdExport(args ...string) error { func (cli *DockerCli) CmdExport(args ...string) error {
cmd := cli.Subcmd("export", "CONTAINER", "Export the contents of a filesystem as a tar archive to STDOUT") cmd := cli.Subcmd("export", "CONTAINER", "Export the contents of a filesystem as a tar archive to STDOUT", true)
cmd.Require(flag.Exact, 1) cmd.Require(flag.Exact, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
if err := cli.stream("GET", "/containers/"+cmd.Arg(0)+"/export", nil, cli.out, nil); err != nil { if err := cli.stream("GET", "/containers/"+cmd.Arg(0)+"/export", nil, cli.out, nil); err != nil {
return err return err
@ -1809,12 +1757,10 @@ func (cli *DockerCli) CmdExport(args ...string) error {
} }
func (cli *DockerCli) CmdDiff(args ...string) error { func (cli *DockerCli) CmdDiff(args ...string) error {
cmd := cli.Subcmd("diff", "CONTAINER", "Inspect changes on a container's filesystem") cmd := cli.Subcmd("diff", "CONTAINER", "Inspect changes on a container's filesystem", true)
cmd.Require(flag.Exact, 1) cmd.Require(flag.Exact, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil, false)) body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil, false))
@ -1843,16 +1789,14 @@ func (cli *DockerCli) CmdDiff(args ...string) error {
func (cli *DockerCli) CmdLogs(args ...string) error { func (cli *DockerCli) CmdLogs(args ...string) error {
var ( var (
cmd = cli.Subcmd("logs", "CONTAINER", "Fetch the logs of a container") cmd = cli.Subcmd("logs", "CONTAINER", "Fetch the logs of a container", true)
follow = cmd.Bool([]string{"f", "-follow"}, false, "Follow log output") follow = cmd.Bool([]string{"f", "-follow"}, false, "Follow log output")
times = cmd.Bool([]string{"t", "-timestamps"}, false, "Show timestamps") times = cmd.Bool([]string{"t", "-timestamps"}, false, "Show timestamps")
tail = cmd.String([]string{"-tail"}, "all", "Output the specified number of lines at the end of logs (defaults to all logs)") tail = cmd.String([]string{"-tail"}, "all", "Output the specified number of lines at the end of logs (defaults to all logs)")
) )
cmd.Require(flag.Exact, 1) cmd.Require(flag.Exact, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
name := cmd.Arg(0) name := cmd.Arg(0)
@ -1884,15 +1828,13 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
func (cli *DockerCli) CmdAttach(args ...string) error { func (cli *DockerCli) CmdAttach(args ...string) error {
var ( var (
cmd = cli.Subcmd("attach", "CONTAINER", "Attach to a running container") cmd = cli.Subcmd("attach", "CONTAINER", "Attach to a running container", true)
noStdin = cmd.Bool([]string{"#nostdin", "-no-stdin"}, false, "Do not attach STDIN") noStdin = cmd.Bool([]string{"#nostdin", "-no-stdin"}, false, "Do not attach STDIN")
proxy = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy all received signals to the process (non-TTY mode only). SIGCHLD, SIGKILL, and SIGSTOP are not proxied.") proxy = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy all received signals to the process (non-TTY mode only). SIGCHLD, SIGKILL, and SIGSTOP are not proxied.")
) )
cmd.Require(flag.Exact, 1) cmd.Require(flag.Exact, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
name := cmd.Arg(0) name := cmd.Arg(0)
stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false) stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false)
@ -1957,16 +1899,14 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
} }
func (cli *DockerCli) CmdSearch(args ...string) error { func (cli *DockerCli) CmdSearch(args ...string) error {
cmd := cli.Subcmd("search", "TERM", "Search the Docker Hub for images") cmd := cli.Subcmd("search", "TERM", "Search the Docker Hub for images", true)
noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
trusted := cmd.Bool([]string{"#t", "#trusted", "#-trusted"}, false, "Only show trusted builds") trusted := cmd.Bool([]string{"#t", "#trusted", "#-trusted"}, false, "Only show trusted builds")
automated := cmd.Bool([]string{"-automated"}, false, "Only show automated builds") automated := cmd.Bool([]string{"-automated"}, false, "Only show automated builds")
stars := cmd.Int([]string{"s", "#stars", "-stars"}, 0, "Only displays with at least x stars") stars := cmd.Int([]string{"s", "#stars", "-stars"}, 0, "Only displays with at least x stars")
cmd.Require(flag.Exact, 1) cmd.Require(flag.Exact, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
v := url.Values{} v := url.Values{}
v.Set("term", cmd.Arg(0)) v.Set("term", cmd.Arg(0))
@ -2010,13 +1950,11 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
type ports []int type ports []int
func (cli *DockerCli) CmdTag(args ...string) error { func (cli *DockerCli) CmdTag(args ...string) error {
cmd := cli.Subcmd("tag", "IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]", "Tag an image into a repository") cmd := cli.Subcmd("tag", "IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]", "Tag an image into a repository", true)
force := cmd.Bool([]string{"f", "#force", "-force"}, false, "Force") force := cmd.Bool([]string{"f", "#force", "-force"}, false, "Force")
cmd.Require(flag.Exact, 2) cmd.Require(flag.Exact, 2)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
var ( var (
repository, tag = parsers.ParseRepositoryTag(cmd.Arg(1)) repository, tag = parsers.ParseRepositoryTag(cmd.Arg(1))
@ -2177,7 +2115,7 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc
} }
func (cli *DockerCli) CmdCreate(args ...string) error { func (cli *DockerCli) CmdCreate(args ...string) error {
cmd := cli.Subcmd("create", "IMAGE [COMMAND] [ARG...]", "Create a new container") cmd := cli.Subcmd("create", "IMAGE [COMMAND] [ARG...]", "Create a new container", true)
// These are flags not stored in Config/HostConfig // These are flags not stored in Config/HostConfig
var ( var (
@ -2205,7 +2143,7 @@ func (cli *DockerCli) CmdCreate(args ...string) error {
func (cli *DockerCli) CmdRun(args ...string) error { func (cli *DockerCli) CmdRun(args ...string) error {
// FIXME: just use runconfig.Parse already // FIXME: just use runconfig.Parse already
cmd := cli.Subcmd("run", "IMAGE [COMMAND] [ARG...]", "Run a command in a new container") cmd := cli.Subcmd("run", "IMAGE [COMMAND] [ARG...]", "Run a command in a new container", true)
// These are flags not stored in Config/HostConfig // These are flags not stored in Config/HostConfig
var ( var (
@ -2221,6 +2159,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
) )
config, hostConfig, cmd, err := runconfig.Parse(cmd, args) config, hostConfig, cmd, err := runconfig.Parse(cmd, args)
// just in case the Parse does not exit
if err != nil { if err != nil {
return &utils.StatusError{StatusCode: 1} return &utils.StatusError{StatusCode: 1}
} }
@ -2405,12 +2344,10 @@ func (cli *DockerCli) CmdRun(args ...string) error {
} }
func (cli *DockerCli) CmdCp(args ...string) error { func (cli *DockerCli) CmdCp(args ...string) error {
cmd := cli.Subcmd("cp", "CONTAINER:PATH HOSTPATH", "Copy files/folders from the PATH to the HOSTPATH") cmd := cli.Subcmd("cp", "CONTAINER:PATH HOSTPATH", "Copy files/folders from the PATH to the HOSTPATH", true)
cmd.Require(flag.Exact, 2) cmd.Require(flag.Exact, 2)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return nil
}
var copyData engine.Env var copyData engine.Env
info := strings.Split(cmd.Arg(0), ":") info := strings.Split(cmd.Arg(0), ":")
@ -2442,13 +2379,11 @@ func (cli *DockerCli) CmdCp(args ...string) error {
} }
func (cli *DockerCli) CmdSave(args ...string) error { func (cli *DockerCli) CmdSave(args ...string) error {
cmd := cli.Subcmd("save", "IMAGE [IMAGE...]", "Save an image(s) to a tar archive (streamed to STDOUT by default)") cmd := cli.Subcmd("save", "IMAGE [IMAGE...]", "Save an image(s) to a tar archive (streamed to STDOUT by default)", true)
outfile := cmd.String([]string{"o", "-output"}, "", "Write to an file, instead of STDOUT") outfile := cmd.String([]string{"o", "-output"}, "", "Write to an file, instead of STDOUT")
cmd.Require(flag.Min, 1) cmd.Require(flag.Min, 1)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return err
}
var ( var (
output io.Writer = cli.out output io.Writer = cli.out
@ -2481,13 +2416,11 @@ func (cli *DockerCli) CmdSave(args ...string) error {
} }
func (cli *DockerCli) CmdLoad(args ...string) error { func (cli *DockerCli) CmdLoad(args ...string) error {
cmd := cli.Subcmd("load", "", "Load an image from a tar archive on STDIN") cmd := cli.Subcmd("load", "", "Load an image from a tar archive on STDIN", true)
infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN") infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN")
cmd.Require(flag.Exact, 0) cmd.Require(flag.Exact, 0)
if err := utils.ParseFlags(cmd, args, true); err != nil { utils.ParseFlags(cmd, args, true)
return err
}
var ( var (
input io.Reader = cli.in input io.Reader = cli.in
@ -2506,9 +2439,10 @@ func (cli *DockerCli) CmdLoad(args ...string) error {
} }
func (cli *DockerCli) CmdExec(args ...string) error { func (cli *DockerCli) CmdExec(args ...string) error {
cmd := cli.Subcmd("exec", "CONTAINER COMMAND [ARG...]", "Run a command in a running container") cmd := cli.Subcmd("exec", "CONTAINER COMMAND [ARG...]", "Run a command in a running container", true)
execConfig, err := runconfig.ParseExec(cmd, args) execConfig, err := runconfig.ParseExec(cmd, args)
// just in case the ParseExec does not exit
if execConfig.Container == "" || err != nil { if execConfig.Container == "" || err != nil {
return &utils.StatusError{StatusCode: 1} return &utils.StatusError{StatusCode: 1}
} }

View File

@ -8,7 +8,10 @@ import (
) )
// ParseFlags is a utility function that adds a help flag if withHelp is true, // ParseFlags is a utility function that adds a help flag if withHelp is true,
// calls cmd.Parse(args) and prints a relevant error message if there are incorrect number of arguments. // calls cmd.Parse(args) and prints a relevant error message if there are
// incorrect number of arguments. It returns error only if error handling is
// set to ContinueOnError and parsing fails. If error handling is set to
// ExitOnError, it's safe to ignore the return value.
// TODO: move this to a better package than utils // TODO: move this to a better package than utils
func ParseFlags(cmd *flag.FlagSet, args []string, withHelp bool) error { func ParseFlags(cmd *flag.FlagSet, args []string, withHelp bool) error {
var help *bool var help *bool