diff --git a/api/client/commands.go b/api/client/commands.go index 14bcc8ffa7..cfbedc835a 100644 --- a/api/client/commands.go +++ b/api/client/commands.go @@ -85,18 +85,11 @@ func (cli *DockerCli) CmdBuild(args ...string) error { rm := cmd.Bool([]string{"#rm", "-rm"}, true, "Remove intermediate containers after a successful build") forceRm := cmd.Bool([]string{"-force-rm"}, false, "Always remove intermediate containers, even after unsuccessful builds") pull := cmd.Bool([]string{"-pull"}, false, "Always attempt to pull a newer version of the image") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") + cmd.Require(flag.Exact, 1) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Exact, 1) { - os.Exit(1) - } var ( context archive.Archive @@ -255,22 +248,18 @@ func (cli *DockerCli) CmdBuild(args ...string) error { // 'docker login': login / register a user to registry service. 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.Require(flag.Max, 1) var username, password, email string cmd.StringVar(&username, []string{"u", "-username"}, "", "Username") cmd.StringVar(&password, []string{"p", "-password"}, "", "Password") cmd.StringVar(&email, []string{"e", "-email"}, "", "Email") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") - err := cmd.Parse(args) - if err != nil { - return nil - } - if *help { - cmd.Usage() + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } + serverAddress := registry.IndexServerAddress() if len(cmd.Args()) > 0 { serverAddress = cmd.Arg(0) @@ -377,8 +366,9 @@ func (cli *DockerCli) CmdLogin(args ...string) error { // log out from a Docker registry 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.Require(flag.Max, 1) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, false); err != nil { return nil } serverAddress := registry.IndexServerAddress() @@ -403,18 +393,12 @@ func (cli *DockerCli) CmdLogout(args ...string) error { // 'docker wait': block until a container stops func (cli *DockerCli) CmdWait(args ...string) error { cmd := cli.Subcmd("wait", "CONTAINER [CONTAINER...]", "Block until a container stops, then print its exit code.") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") + cmd.Require(flag.Min, 1) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Min, 1) { - os.Exit(1) - } + var encounteredError error for _, name := range cmd.Args() { status, err := waitForExit(cli, name) @@ -431,12 +415,12 @@ func (cli *DockerCli) CmdWait(args ...string) error { // 'docker version': show version information func (cli *DockerCli) CmdVersion(args ...string) error { cmd := cli.Subcmd("version", "", "Show the Docker version information.") - if err := cmd.Parse(args); err != nil { + cmd.Require(flag.Exact, 0) + + if err := utils.ParseFlags(cmd, args, false); err != nil { return nil } - if cmd.BadArgs(flag.Exact, 0) { - os.Exit(1) - } + if dockerversion.VERSION != "" { fmt.Fprintf(cli.out, "Client version: %s\n", dockerversion.VERSION) } @@ -475,12 +459,10 @@ func (cli *DockerCli) CmdVersion(args ...string) error { // 'docker info': display system-wide information. func (cli *DockerCli) CmdInfo(args ...string) error { cmd := cli.Subcmd("info", "", "Display system-wide information") - if err := cmd.Parse(args); err != nil { + cmd.Require(flag.Exact, 0) + if err := utils.ParseFlags(cmd, args, false); err != nil { return nil } - if cmd.BadArgs(flag.Exact, 0) { - os.Exit(1) - } body, _, err := readBody(cli.call("GET", "/info", nil, false)) if err != nil { @@ -594,18 +576,11 @@ func (cli *DockerCli) CmdInfo(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") nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Number of seconds to wait for the container to stop before killing it. Default is 10 seconds.") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") + cmd.Require(flag.Min, 1) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Min, 1) { - os.Exit(1) - } v := url.Values{} v.Set("t", strconv.Itoa(*nSeconds)) @@ -626,18 +601,11 @@ func (cli *DockerCli) CmdStop(args ...string) error { func (cli *DockerCli) CmdRestart(args ...string) error { cmd := cli.Subcmd("restart", "CONTAINER [CONTAINER...]", "Restart a running container") 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.") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") + cmd.Require(flag.Min, 1) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Min, 1) { - os.Exit(1) - } v := url.Values{} v.Set("t", strconv.Itoa(*nSeconds)) @@ -689,19 +657,12 @@ func (cli *DockerCli) CmdStart(args ...string) error { cmd = cli.Subcmd("start", "CONTAINER [CONTAINER...]", "Restart a stopped container") 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") - help = cmd.Bool([]string{"#help", "-help"}, false, "Print usage") ) - if err := cmd.Parse(args); err != nil { + cmd.Require(flag.Min, 1) + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Min, 1) { - os.Exit(1) - } hijacked := make(chan io.Closer) @@ -804,12 +765,10 @@ func (cli *DockerCli) CmdStart(args ...string) error { func (cli *DockerCli) CmdUnpause(args ...string) error { cmd := cli.Subcmd("unpause", "CONTAINER", "Unpause all processes within a container") - if err := cmd.Parse(args); err != nil { + cmd.Require(flag.Exact, 1) + if err := utils.ParseFlags(cmd, args, false); err != nil { return nil } - if cmd.BadArgs(flag.Exact, 1) { - os.Exit(1) - } var encounteredError error for _, name := range cmd.Args() { @@ -825,12 +784,10 @@ func (cli *DockerCli) CmdUnpause(args ...string) error { func (cli *DockerCli) CmdPause(args ...string) error { cmd := cli.Subcmd("pause", "CONTAINER", "Pause all processes within a container") - if err := cmd.Parse(args); err != nil { + cmd.Require(flag.Exact, 1) + if err := utils.ParseFlags(cmd, args, false); err != nil { return nil } - if cmd.BadArgs(flag.Exact, 1) { - os.Exit(1) - } var encounteredError error for _, name := range cmd.Args() { @@ -847,18 +804,11 @@ func (cli *DockerCli) CmdPause(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") tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template.") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") + cmd.Require(flag.Min, 1) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Min, 1) { - os.Exit(1) - } var tmpl *template.Template if *tmplStr != "" { @@ -931,18 +881,12 @@ func (cli *DockerCli) CmdInspect(args ...string) error { func (cli *DockerCli) CmdTop(args ...string) error { cmd := cli.Subcmd("top", "CONTAINER [ps OPTIONS]", "Display the running processes of a container") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") + cmd.Require(flag.Min, 1) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Min, 1) { - os.Exit(1) - } + val := url.Values{} if cmd.NArg() > 1 { val.Set("ps_args", strings.Join(cmd.Args()[1:], " ")) @@ -971,17 +915,10 @@ func (cli *DockerCli) CmdTop(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") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") - if err := cmd.Parse(args); err != nil { + cmd.Require(flag.Min, 1) + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Min, 1) { - os.Exit(1) - } stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false) if err != nil { @@ -1034,18 +971,11 @@ func (cli *DockerCli) CmdRmi(args ...string) error { force = cmd.Bool([]string{"f", "-force"}, false, "Force removal of the image") noprune = cmd.Bool([]string{"-no-prune"}, false, "Do not delete untagged parents") ) - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") + cmd.Require(flag.Min, 1) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Min, 1) { - os.Exit(1) - } v := url.Values{} if *force { @@ -1084,18 +1014,11 @@ func (cli *DockerCli) CmdHistory(args ...string) error { cmd := cli.Subcmd("history", "IMAGE", "Show the history of an image") quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs") noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") + cmd.Require(flag.Exact, 1) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Exact, 1) { - os.Exit(1) - } body, _, err := readBody(cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, false)) if err != nil { @@ -1146,18 +1069,11 @@ func (cli *DockerCli) CmdRm(args ...string) error { 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") force := cmd.Bool([]string{"f", "-force"}, false, "Force the removal of a running container (uses SIGKILL)") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") + cmd.Require(flag.Min, 1) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Min, 1) { - os.Exit(1) - } val := url.Values{} if *v { @@ -1188,18 +1104,11 @@ func (cli *DockerCli) CmdRm(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") signal := cmd.String([]string{"s", "-signal"}, "KILL", "Signal to send to the container") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") + cmd.Require(flag.Min, 1) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Min, 1) { - os.Exit(1) - } var encounteredError error for _, name := range cmd.Args() { @@ -1215,18 +1124,12 @@ func (cli *DockerCli) CmdKill(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.") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") + cmd.Require(flag.Min, 1) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Min, 1) { - os.Exit(1) - } + var ( v = url.Values{} src = cmd.Arg(0) @@ -1260,18 +1163,12 @@ func (cli *DockerCli) CmdImport(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") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") + cmd.Require(flag.Exact, 1) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Exact, 1) { - os.Exit(1) - } + name := cmd.Arg(0) cli.LoadConfigFile() @@ -1330,19 +1227,12 @@ func (cli *DockerCli) CmdPush(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") allTags := cmd.Bool([]string{"a", "-all-tags"}, false, "Download all tagged images in the repository") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") + cmd.Require(flag.Exact, 1) - if err := cmd.Parse(args); err != nil { - return nil - } - if *help { - cmd.Usage() + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if cmd.BadArgs(flag.Exact, 1) { - os.Exit(1) - } var ( v = url.Values{} remote = cmd.Arg(0) @@ -1406,21 +1296,14 @@ func (cli *DockerCli) CmdImages(args ...string) error { // FIXME: --viz and --tree are deprecated. Remove them in a future version. 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") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") flFilter := opts.NewListOpts(nil) cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values (i.e. 'dangling=true')") + cmd.Require(flag.Max, 1) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Max, 1) { - os.Exit(1) - } // Consolidate all filter flags, and sanity check them early. // They'll get process in the daemon/server. @@ -1650,7 +1533,6 @@ func (cli *DockerCli) CmdPs(args ...string) error { quiet = cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs") size = cmd.Bool([]string{"s", "-size"}, false, "Display total file sizes") all = cmd.Bool([]string{"a", "-all"}, false, "Show all containers. Only running containers are shown by default.") - help = cmd.Bool([]string{"#help", "-help"}, false, "Print usage") 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", "-since"}, "", "Show only containers created since Id or Name, include non-running ones.") @@ -1658,14 +1540,11 @@ func (cli *DockerCli) CmdPs(args ...string) error { last = cmd.Int([]string{"n"}, -1, "Show n last created containers, include non-running ones.") flFilter = opts.NewListOpts(nil) ) + cmd.Require(flag.Exact, 0) cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values. Valid filters:\nexited= - containers with exit code of \nstatus=(restarting|running|paused|exited)") - if err := cmd.Parse(args); err != nil { - return nil - } - if *help { - cmd.Usage() + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } if *last == -1 && *nLatest { @@ -1808,22 +1687,11 @@ func (cli *DockerCli) CmdCommit(args ...string) error { flAuthor := cmd.String([]string{"a", "#author", "-author"}, "", "Author (e.g., \"John Hannibal Smith \")") // FIXME: --run is deprecated, it will be replaced with inline Dockerfile 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") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") - if err := cmd.Parse(args); err != nil { + cmd.Require(flag.Max, 2) + cmd.Require(flag.Min, 1) + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - - if cmd.BadArgs(flag.Max, 2) { - os.Exit(1) - } - - if cmd.BadArgs(flag.Min, 1) { - os.Exit(1) - } var ( name = cmd.Arg(0) @@ -1876,18 +1744,11 @@ func (cli *DockerCli) CmdEvents(args ...string) error { until := cmd.String([]string{"-until"}, "", "Stream events until this timestamp") flFilter := opts.NewListOpts(nil) cmd.Var(&flFilter, []string{"f", "-filter"}, "Provide filter values (i.e. 'event=stop')") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") + cmd.Require(flag.Exact, 0) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Exact, 0) { - os.Exit(1) - } var ( v = url.Values{} @@ -1936,18 +1797,11 @@ func (cli *DockerCli) CmdEvents(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") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") + cmd.Require(flag.Exact, 1) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Exact, 1) { - os.Exit(1) - } if err := cli.stream("GET", "/containers/"+cmd.Arg(0)+"/export", nil, cli.out, nil); err != nil { return err @@ -1957,18 +1811,11 @@ func (cli *DockerCli) CmdExport(args ...string) error { func (cli *DockerCli) CmdDiff(args ...string) error { cmd := cli.Subcmd("diff", "CONTAINER", "Inspect changes on a container's filesystem") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") + cmd.Require(flag.Exact, 1) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Exact, 1) { - os.Exit(1) - } body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil, false)) @@ -2001,20 +1848,13 @@ func (cli *DockerCli) CmdLogs(args ...string) error { follow = cmd.Bool([]string{"f", "-follow"}, false, "Follow log output") 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)") - help = cmd.Bool([]string{"#help", "-help"}, false, "Print usage") ) + cmd.Require(flag.Exact, 1) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Exact, 1) { - os.Exit(1) - } name := cmd.Arg(0) stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false) @@ -2048,19 +1888,12 @@ func (cli *DockerCli) CmdAttach(args ...string) error { cmd = cli.Subcmd("attach", "CONTAINER", "Attach to a running container") 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.") - help = cmd.Bool([]string{"#help", "-help"}, false, "Print usage") ) + cmd.Require(flag.Exact, 1) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Exact, 1) { - os.Exit(1) - } name := cmd.Arg(0) stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false) @@ -2130,18 +1963,11 @@ func (cli *DockerCli) CmdSearch(args ...string) error { trusted := cmd.Bool([]string{"#t", "#trusted", "#-trusted"}, false, "Only show trusted 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") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") + cmd.Require(flag.Exact, 1) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Exact, 1) { - os.Exit(1) - } v := url.Values{} v.Set("term", cmd.Arg(0)) @@ -2187,18 +2013,11 @@ type ports []int func (cli *DockerCli) CmdTag(args ...string) error { cmd := cli.Subcmd("tag", "IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]", "Tag an image into a repository") force := cmd.Bool([]string{"f", "#force", "-force"}, false, "Force") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") + cmd.Require(flag.Exact, 2) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Exact, 2) { - os.Exit(1) - } var ( repository, tag = parsers.ParseRepositoryTag(cmd.Arg(1)) @@ -2588,18 +2407,11 @@ func (cli *DockerCli) CmdRun(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") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") + cmd.Require(flag.Exact, 2) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Exact, 2) { - os.Exit(1) - } var copyData engine.Env info := strings.Split(cmd.Arg(0), ":") @@ -2632,21 +2444,13 @@ func (cli *DockerCli) CmdCp(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)") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") outfile := cmd.String([]string{"o", "-output"}, "", "Write to an file, instead of STDOUT") + cmd.Require(flag.Min, 1) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return err } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Min, 1) { - os.Exit(1) - } - var ( output io.Writer = cli.out err error @@ -2680,18 +2484,11 @@ func (cli *DockerCli) CmdSave(args ...string) error { func (cli *DockerCli) CmdLoad(args ...string) error { cmd := cli.Subcmd("load", "", "Load an image from a tar archive on STDIN") infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN") - help := cmd.Bool([]string{"#help", "-help"}, false, "Print usage") + cmd.Require(flag.Exact, 0) - if err := cmd.Parse(args); err != nil { + if err := utils.ParseFlags(cmd, args, true); err != nil { return err } - if *help { - cmd.Usage() - return nil - } - if cmd.BadArgs(flag.Exact, 0) { - os.Exit(1) - } var ( input io.Reader = cli.in diff --git a/pkg/mflag/flag.go b/pkg/mflag/flag.go index ffc509a598..42711b763a 100644 --- a/pkg/mflag/flag.go +++ b/pkg/mflag/flag.go @@ -284,13 +284,14 @@ type FlagSet struct { // 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 + 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 + nArgRequirements []nArgRequirement } // A Flag represents the state of a flag. @@ -348,7 +349,13 @@ func sortFlags(flags map[string]*Flag) []*Flag { return result } -func (f *FlagSet) out() io.Writer { +// Name returns the name of the FlagSet. +func (f *FlagSet) Name() string { + return f.name +} + +// Out returns the destination for usage and error messages. +func (f *FlagSet) Out() io.Writer { if f.output == nil { return os.Stderr } @@ -410,45 +417,60 @@ func IsSet(name string) bool { return CommandLine.IsSet(name) } +type nArgRequirementType int + // Indicator used to pass to BadArgs function const ( - Exact = 1 - Max = 2 - Min = 3 + Exact nArgRequirementType = iota + Max + Min ) -// Bad Args takes two arguments. -// The first one indicates whether the number of arguments should, be -// A Minimal number of arguments, a maximum number of arguments or -// The exact number of arguments required -// If the actuall number of arguments is not valid and error message -// prints and true is returned, otherwise false is returned -func (f *FlagSet) BadArgs(arg_type, nargs int) bool { - if arg_type == Max && f.NArg() > nargs { - if nargs == 1 { - fmt.Fprintf(f.out(), "docker: '%s' requires a maximum of 1 argument. See 'docker %s --help'.\n", f.name, f.name) +type nArgRequirement struct { + Type nArgRequirementType + N int +} + +// Require adds a requirement about the number of arguments for the FlagSet. +// The first parameter can be Exact, Max, or Min to respectively specify the exact, +// the maximum, or the minimal number of arguments required. +// The actual check is done in FlagSet.CheckArgs(). +func (f *FlagSet) Require(nArgRequirementType nArgRequirementType, nArg int) { + f.nArgRequirements = append(f.nArgRequirements, nArgRequirement{nArgRequirementType, nArg}) +} + +// CheckArgs uses the requirements set by FlagSet.Require() to validate +// the number of arguments. If the requirements are not met, +// an error message string is returned. +func (f *FlagSet) CheckArgs() (message string) { + for _, req := range f.nArgRequirements { + var arguments string + if req.N == 1 { + arguments = "1 argument" } else { - fmt.Fprintf(f.out(), "docker: '%s' requires a maximum of %d arguments. See 'docker %s --help'.\n", f.name, nargs, f.name) + arguments = fmt.Sprintf("%d arguments", req.N) } - return true - } - if arg_type == Exact && f.NArg() != nargs { - if nargs == 1 { - fmt.Fprintf(f.out(), "docker: '%s' requires 1 argument. See 'docker %s --help'.\n", f.name, f.name) - } else { - fmt.Fprintf(f.out(), "docker: '%s' requires %d arguments. See 'docker %s --help'.\n", f.name, nargs, f.name) + + str := func(kind string) string { + return fmt.Sprintf("%q requires %s%s", f.name, kind, arguments) } - return true - } - if arg_type == Min && f.NArg() < nargs { - if nargs == 1 { - fmt.Fprintf(f.out(), "docker: '%s' requires a minimum of 1 argument. See 'docker %s --help'.\n", f.name, f.name) - } else { - fmt.Fprintf(f.out(), "docker: '%s' requires a minimum of %d arguments. See 'docker %s --help'.\n", f.name, nargs, f.name) + + switch req.Type { + case Exact: + if f.NArg() != req.N { + return str("") + } + case Max: + if f.NArg() > req.N { + return str("a maximum of ") + } + case Min: + if f.NArg() < req.N { + return str("a minimum of ") + } } - return true } - return false + return "" } // Set sets the value of the named flag. @@ -476,7 +498,7 @@ func Set(name, value string) error { // PrintDefaults prints, to standard error unless configured // otherwise, the default values of all defined flags in the set. func (f *FlagSet) PrintDefaults() { - writer := tabwriter.NewWriter(f.out(), 20, 1, 3, ' ', 0) + writer := tabwriter.NewWriter(f.Out(), 20, 1, 3, ' ', 0) f.VisitAll(func(flag *Flag) { format := " -%s=%s" if _, ok := flag.Value.(*stringValue); ok { @@ -510,9 +532,9 @@ func PrintDefaults() { // defaultUsage is the default function to print a usage message. func defaultUsage(f *FlagSet) { if f.name == "" { - fmt.Fprintf(f.out(), "Usage:\n") + fmt.Fprintf(f.Out(), "Usage:\n") } else { - fmt.Fprintf(f.out(), "Usage of %s:\n", f.name) + fmt.Fprintf(f.Out(), "Usage of %s:\n", f.name) } f.PrintDefaults() } @@ -805,7 +827,7 @@ func (f *FlagSet) Var(value Value, names []string, usage string) { } else { msg = fmt.Sprintf("%s flag redefined: %s", f.name, name) } - fmt.Fprintln(f.out(), msg) + fmt.Fprintln(f.Out(), msg) panic(msg) // Happens only if flags are declared with identical names } if f.formal == nil { @@ -829,8 +851,8 @@ func Var(value Value, names []string, usage string) { // returns the error. func (f *FlagSet) failf(format string, a ...interface{}) error { err := fmt.Errorf(format, a...) - fmt.Fprintln(f.out(), err) - fmt.Fprintf(f.out(), "See 'docker %s --help'.\n", f.name) + fmt.Fprintln(f.Out(), err) + fmt.Fprintf(f.Out(), "See 'docker %s --help'.\n", f.name) return err } @@ -956,9 +978,9 @@ func (f *FlagSet) parseOne() (bool, string, error) { } } if replacement != "" { - fmt.Fprintf(f.out(), "Warning: '-%s' is deprecated, it will be replaced by '-%s' soon. See usage.\n", name, replacement) + fmt.Fprintf(f.Out(), "Warning: '-%s' is deprecated, it will be replaced by '-%s' soon. See usage.\n", name, replacement) } else { - fmt.Fprintf(f.out(), "Warning: '-%s' is deprecated, it will be removed soon. See usage.\n", name) + fmt.Fprintf(f.Out(), "Warning: '-%s' is deprecated, it will be removed soon. See usage.\n", name) } } } diff --git a/runconfig/exec.go b/runconfig/exec.go index 4e23481e5d..9390781a45 100644 --- a/runconfig/exec.go +++ b/runconfig/exec.go @@ -5,7 +5,7 @@ import ( "github.com/docker/docker/engine" flag "github.com/docker/docker/pkg/mflag" - "os" + "github.com/docker/docker/utils" ) type ExecConfig struct { @@ -46,20 +46,13 @@ func ParseExec(cmd *flag.FlagSet, args []string) (*ExecConfig, error) { 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") flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: run command in the background") - help = cmd.Bool([]string{"#help", "-help"}, false, "Print usage") execCmd []string container string ) - if err := cmd.Parse(args); err != nil { + cmd.Require(flag.Min, 2) + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil, err } - if *help { - cmd.Usage() - return nil, nil - } - if cmd.BadArgs(flag.Min, 2) { - os.Exit(1) - } container = cmd.Arg(0) parsedArgs := cmd.Args() execCmd = parsedArgs[1:] diff --git a/runconfig/parse.go b/runconfig/parse.go index d2e4c73849..911ab7e52c 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -2,7 +2,6 @@ package runconfig import ( "fmt" - "os" "path" "strconv" "strings" @@ -62,7 +61,6 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe flMacAddress = cmd.String([]string{"-mac-address"}, "", "Container MAC address (e.g. 92:d0:c6:0a:29:33)") flIpcMode = cmd.String([]string{"-ipc"}, "", "Default is to create a private IPC namespace (POSIX SysV IPC) for the container\n'container:': reuses another container shared memory, semaphores and message queues\n'host': use the host shared memory,semaphores and message queues inside the container. Note: the host mode gives the container full access to local shared memory and is therefore considered insecure.") flRestartPolicy = cmd.String([]string{"-restart"}, "", "Restart policy to apply when a container exits (no, on-failure[:max-retry], always)") - help = cmd.Bool([]string{"#help", "-help"}, false, "Print usage") ) cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to STDIN, STDOUT or STDERR.") @@ -85,16 +83,11 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe cmd.Var(&flCapDrop, []string{"-cap-drop"}, "Drop Linux capabilities") cmd.Var(&flSecurityOpt, []string{"-security-opt"}, "Security Options") - if err := cmd.Parse(args); err != nil { + cmd.Require(flag.Min, 1) + + if err := utils.ParseFlags(cmd, args, true); err != nil { return nil, nil, cmd, err } - if *help { - cmd.Usage() - return nil, nil, cmd, nil - } - if cmd.BadArgs(flag.Min, 1) { - os.Exit(1) - } // Validate input params if *flWorkingDir != "" && !path.IsAbs(*flWorkingDir) { diff --git a/utils/flags.go b/utils/flags.go new file mode 100644 index 0000000000..d88104b635 --- /dev/null +++ b/utils/flags.go @@ -0,0 +1,34 @@ +package utils + +import ( + "fmt" + "os" + + flag "github.com/docker/docker/pkg/mflag" +) + +// 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. +// TODO: move this to a better package than utils +func ParseFlags(cmd *flag.FlagSet, args []string, withHelp bool) error { + var help *bool + if withHelp { + help = cmd.Bool([]string{"#help", "-help"}, false, "Print usage") + } + if err := cmd.Parse(args); err != nil { + return err + } + if help != nil && *help { + cmd.Usage() + // just in case Usage does not exit + os.Exit(0) + } + if str := cmd.CheckArgs(); str != "" { + if withHelp { + str += ". See 'docker " + cmd.Name() + " --help'" + } + fmt.Fprintf(cmd.Out(), "docker: %s.\n", str) + os.Exit(1) + } + return nil +}