Browse Source

Update usage and help to (almost) match the existing docker behaviour

Signed-off-by: Daniel Nephin <dnephin@docker.com>
Daniel Nephin 9 years ago
parent
commit
667dcb0e8e

+ 11 - 0
api/client/volume/cmd.go

@@ -1,9 +1,12 @@
 package volume
 package volume
 
 
 import (
 import (
+	"fmt"
+
 	"github.com/spf13/cobra"
 	"github.com/spf13/cobra"
 
 
 	"github.com/docker/docker/api/client"
 	"github.com/docker/docker/api/client"
+	"github.com/docker/docker/cli"
 )
 )
 
 
 // NewVolumeCommand returns a cobra command for `volume` subcommands
 // NewVolumeCommand returns a cobra command for `volume` subcommands
@@ -11,6 +14,14 @@ func NewVolumeCommand(dockerCli *client.DockerCli) *cobra.Command {
 	cmd := &cobra.Command{
 	cmd := &cobra.Command{
 		Use:   "volume",
 		Use:   "volume",
 		Short: "Manage Docker volumes",
 		Short: "Manage Docker volumes",
+		// TODO: remove once cobra is patched to handle this
+		RunE: func(cmd *cobra.Command, args []string) error {
+			fmt.Fprintf(dockerCli.Err(), "\n%s", cmd.UsageString())
+			if len(args) > 0 {
+				return cli.StatusError{StatusCode: 1}
+			}
+			return nil
+		},
 	}
 	}
 	cmd.AddCommand(
 	cmd.AddCommand(
 		newCreateCommand(dockerCli),
 		newCreateCommand(dockerCli),

+ 8 - 1
api/client/volume/create.go

@@ -6,6 +6,7 @@ import (
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 
 
 	"github.com/docker/docker/api/client"
 	"github.com/docker/docker/api/client"
+	"github.com/docker/docker/cli"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/opts"
 	runconfigopts "github.com/docker/docker/runconfig/opts"
 	runconfigopts "github.com/docker/docker/runconfig/opts"
 	"github.com/docker/engine-api/types"
 	"github.com/docker/engine-api/types"
@@ -20,12 +21,18 @@ type createOptions struct {
 }
 }
 
 
 func newCreateCommand(dockerCli *client.DockerCli) *cobra.Command {
 func newCreateCommand(dockerCli *client.DockerCli) *cobra.Command {
-	var opts createOptions
+	opts := createOptions{
+		driverOpts: *opts.NewMapOpts(nil, nil),
+	}
 
 
 	cmd := &cobra.Command{
 	cmd := &cobra.Command{
 		Use:   "create",
 		Use:   "create",
 		Short: "Create a volume",
 		Short: "Create a volume",
 		RunE: func(cmd *cobra.Command, args []string) error {
 		RunE: func(cmd *cobra.Command, args []string) error {
+			// TODO: remove once cobra is patched to handle this
+			if err := cli.AcceptsNoArgs(args, cmd); err != nil {
+				return err
+			}
 			return runCreate(dockerCli, opts)
 			return runCreate(dockerCli, opts)
 		},
 		},
 	}
 	}

+ 5 - 0
api/client/volume/list.go

@@ -8,6 +8,7 @@ import (
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 
 
 	"github.com/docker/docker/api/client"
 	"github.com/docker/docker/api/client"
+	"github.com/docker/docker/cli"
 	"github.com/docker/engine-api/types"
 	"github.com/docker/engine-api/types"
 	"github.com/docker/engine-api/types/filters"
 	"github.com/docker/engine-api/types/filters"
 	"github.com/spf13/cobra"
 	"github.com/spf13/cobra"
@@ -34,6 +35,10 @@ func newListCommand(dockerCli *client.DockerCli) *cobra.Command {
 		Aliases: []string{"list"},
 		Aliases: []string{"list"},
 		Short:   "List volumes",
 		Short:   "List volumes",
 		RunE: func(cmd *cobra.Command, args []string) error {
 		RunE: func(cmd *cobra.Command, args []string) error {
+			// TODO: remove once cobra is patched to handle this
+			if err := cli.AcceptsNoArgs(args, cmd); err != nil {
+				return err
+			}
 			return runList(dockerCli, opts)
 			return runList(dockerCli, opts)
 		},
 		},
 	}
 	}

+ 18 - 10
cli/cobraadaptor/adaptor.go

@@ -18,17 +18,21 @@ type CobraAdaptor struct {
 
 
 // NewCobraAdaptor returns a new handler
 // NewCobraAdaptor returns a new handler
 func NewCobraAdaptor(clientFlags *cliflags.ClientFlags) CobraAdaptor {
 func NewCobraAdaptor(clientFlags *cliflags.ClientFlags) CobraAdaptor {
-	var rootCmd = &cobra.Command{
-		Use: "docker",
-	}
-	rootCmd.SetUsageTemplate(usageTemplate)
-
 	stdin, stdout, stderr := term.StdStreams()
 	stdin, stdout, stderr := term.StdStreams()
 	dockerCli := client.NewDockerCli(stdin, stdout, stderr, clientFlags)
 	dockerCli := client.NewDockerCli(stdin, stdout, stderr, clientFlags)
 
 
+	var rootCmd = &cobra.Command{
+		Use:           "docker",
+		SilenceUsage:  true,
+		SilenceErrors: true,
+	}
+	rootCmd.SetUsageTemplate(usageTemplate)
+	rootCmd.SetHelpTemplate(helpTemplate)
+	rootCmd.SetOutput(stdout)
 	rootCmd.AddCommand(
 	rootCmd.AddCommand(
 		volume.NewVolumeCommand(dockerCli),
 		volume.NewVolumeCommand(dockerCli),
 	)
 	)
+
 	return CobraAdaptor{
 	return CobraAdaptor{
 		rootCmd:   rootCmd,
 		rootCmd:   rootCmd,
 		dockerCli: dockerCli,
 		dockerCli: dockerCli,
@@ -64,20 +68,24 @@ func (c CobraAdaptor) Command(name string) func(...string) error {
 	return nil
 	return nil
 }
 }
 
 
-var usageTemplate = `Usage:  {{if .Runnable}}{{if .HasFlags}}{{appendIfNotPresent .UseLine "[OPTIONS]"}}{{else}}{{.UseLine}}{{end}}{{end}}{{if .HasSubCommands}}{{ .CommandPath}} COMMAND {{end}}{{if gt .Aliases 0}}
+var usageTemplate = `Usage:	{{if not .HasSubCommands}}{{if .HasLocalFlags}}{{appendIfNotPresent .UseLine "[OPTIONS]"}}{{else}}{{.UseLine}}{{end}}{{end}}{{if .HasSubCommands}}{{ .CommandPath}} COMMAND{{end}}
+
+{{with or .Long .Short }}{{. | trim}}{{end}}{{if gt .Aliases 0}}
 
 
 Aliases:
 Aliases:
-  {{.NameAndAliases}}
-{{end}}{{if .HasExample}}
+  {{.NameAndAliases}}{{end}}{{if .HasExample}}
 
 
 Examples:
 Examples:
-{{ .Example }}{{end}}{{ if .HasLocalFlags}}
+{{ .Example }}{{end}}{{if .HasFlags}}
 
 
 Options:
 Options:
-{{.LocalFlags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasAvailableSubCommands}}
+{{.Flags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasAvailableSubCommands}}
 
 
 Commands:{{range .Commands}}{{if .IsAvailableCommand}}
 Commands:{{range .Commands}}{{if .IsAvailableCommand}}
   {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasSubCommands }}
   {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasSubCommands }}
 
 
 Run '{{.CommandPath}} COMMAND --help' for more information on a command.{{end}}
 Run '{{.CommandPath}} COMMAND --help' for more information on a command.{{end}}
 `
 `
+
+var helpTemplate = `
+{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`

+ 14 - 0
cli/required.go

@@ -21,3 +21,17 @@ func MinRequiredArgs(args []string, min int, cmd *cobra.Command) error {
 		cmd.Short,
 		cmd.Short,
 	)
 	)
 }
 }
+
+// AcceptsNoArgs returns an error message if there are args
+func AcceptsNoArgs(args []string, cmd *cobra.Command) error {
+	if len(args) == 0 {
+		return nil
+	}
+
+	return fmt.Errorf(
+		"\"%s\" accepts no argument(s).\n\nUsage:  %s\n\n%s",
+		cmd.CommandPath(),
+		cmd.UseLine(),
+		cmd.Short,
+	)
+}

+ 0 - 1
cli/usage.go

@@ -48,7 +48,6 @@ var DockerCommandUsage = []Command{
 	{"unpause", "Unpause all processes within a container"},
 	{"unpause", "Unpause all processes within a container"},
 	{"update", "Update configuration of one or more containers"},
 	{"update", "Update configuration of one or more containers"},
 	{"version", "Show the Docker version information"},
 	{"version", "Show the Docker version information"},
-	{"volume", "Manage Docker volumes"},
 	{"wait", "Block until a container stops, then print its exit code"},
 	{"wait", "Block until a container stops, then print its exit code"},
 }
 }
 
 

+ 5 - 4
integration-cli/docker_cli_help_test.go

@@ -249,7 +249,8 @@ func testCommand(cmd string, newEnvs []string, scanForHome bool, home string) er
 		// If a line starts with 4 spaces then assume someone
 		// If a line starts with 4 spaces then assume someone
 		// added a multi-line description for an option and we need
 		// added a multi-line description for an option and we need
 		// to flag it
 		// to flag it
-		if strings.HasPrefix(line, "    ") {
+		if strings.HasPrefix(line, "    ") &&
+			!strings.HasPrefix(strings.TrimLeft(line, " "), "--") {
 			return fmt.Errorf("Help for %q should not have a multi-line option", cmd)
 			return fmt.Errorf("Help for %q should not have a multi-line option", cmd)
 		}
 		}
 
 
@@ -260,7 +261,7 @@ func testCommand(cmd string, newEnvs []string, scanForHome bool, home string) er
 
 
 		// Options should NOT end with a space
 		// Options should NOT end with a space
 		if strings.HasSuffix(line, " ") {
 		if strings.HasSuffix(line, " ") {
-			return fmt.Errorf("Help for %q should not end with a space", cmd)
+			return fmt.Errorf("Help for %q should not end with a space: %s", cmd, line)
 		}
 		}
 
 
 	}
 	}
@@ -326,8 +327,8 @@ func testCommand(cmd string, newEnvs []string, scanForHome bool, home string) er
 			return fmt.Errorf("Bad output from %q\nstdout:%q\nstderr:%q\nec:%d\nerr:%q\n", args, out, stderr, ec, err)
 			return fmt.Errorf("Bad output from %q\nstdout:%q\nstderr:%q\nec:%d\nerr:%q\n", args, out, stderr, ec, err)
 		}
 		}
 		// Should have just short usage
 		// Should have just short usage
-		if !strings.Contains(stderr, "\nUsage:\t") {
-			return fmt.Errorf("Missing short usage on %q\n", args)
+		if !strings.Contains(stderr, "\nUsage:") {
+			return fmt.Errorf("Missing short usage on %q\n:%#v", args, stderr)
 		}
 		}
 		// But shouldn't have full usage
 		// But shouldn't have full usage
 		if strings.Contains(stderr, "--help=false") {
 		if strings.Contains(stderr, "--help=false") {

+ 7 - 7
integration-cli/docker_cli_volume_test.go

@@ -25,7 +25,7 @@ func (s *DockerSuite) TestVolumeCliCreateOptionConflict(c *check.C) {
 	c.Assert(err, check.NotNil, check.Commentf("volume create exception name already in use with another driver"))
 	c.Assert(err, check.NotNil, check.Commentf("volume create exception name already in use with another driver"))
 	c.Assert(out, checker.Contains, "A volume named test already exists")
 	c.Assert(out, checker.Contains, "A volume named test already exists")
 
 
-	out, _ = dockerCmd(c, "volume", "inspect", "--format='{{ .Driver }}'", "test")
+	out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Driver }}", "test")
 	_, _, err = dockerCmdWithError("volume", "create", "--name", "test", "--driver", strings.TrimSpace(out))
 	_, _, err = dockerCmdWithError("volume", "create", "--name", "test", "--driver", strings.TrimSpace(out))
 	c.Assert(err, check.IsNil)
 	c.Assert(err, check.IsNil)
 }
 }
@@ -39,11 +39,11 @@ func (s *DockerSuite) TestVolumeCliInspect(c *check.C) {
 
 
 	out, _ := dockerCmd(c, "volume", "create")
 	out, _ := dockerCmd(c, "volume", "create")
 	name := strings.TrimSpace(out)
 	name := strings.TrimSpace(out)
-	out, _ = dockerCmd(c, "volume", "inspect", "--format='{{ .Name }}'", name)
+	out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Name }}", name)
 	c.Assert(strings.TrimSpace(out), check.Equals, name)
 	c.Assert(strings.TrimSpace(out), check.Equals, name)
 
 
 	dockerCmd(c, "volume", "create", "--name", "test")
 	dockerCmd(c, "volume", "create", "--name", "test")
-	out, _ = dockerCmd(c, "volume", "inspect", "--format='{{ .Name }}'", "test")
+	out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Name }}", "test")
 	c.Assert(strings.TrimSpace(out), check.Equals, "test")
 	c.Assert(strings.TrimSpace(out), check.Equals, "test")
 }
 }
 
 
@@ -212,7 +212,7 @@ func (s *DockerSuite) TestVolumeCliRm(c *check.C) {
 func (s *DockerSuite) TestVolumeCliNoArgs(c *check.C) {
 func (s *DockerSuite) TestVolumeCliNoArgs(c *check.C) {
 	out, _ := dockerCmd(c, "volume")
 	out, _ := dockerCmd(c, "volume")
 	// no args should produce the cmd usage output
 	// no args should produce the cmd usage output
-	usage := "Usage:	docker volume [OPTIONS] [COMMAND]"
+	usage := "Usage:	docker volume COMMAND"
 	c.Assert(out, checker.Contains, usage)
 	c.Assert(out, checker.Contains, usage)
 
 
 	// invalid arg should error and show the command usage on stderr
 	// invalid arg should error and show the command usage on stderr
@@ -224,7 +224,7 @@ func (s *DockerSuite) TestVolumeCliNoArgs(c *check.C) {
 	_, stderr, _, err = runCommandWithStdoutStderr(exec.Command(dockerBinary, "volume", "--no-such-flag"))
 	_, stderr, _, err = runCommandWithStdoutStderr(exec.Command(dockerBinary, "volume", "--no-such-flag"))
 	c.Assert(err, check.NotNil, check.Commentf(stderr))
 	c.Assert(err, check.NotNil, check.Commentf(stderr))
 	c.Assert(stderr, checker.Contains, usage)
 	c.Assert(stderr, checker.Contains, usage)
-	c.Assert(stderr, checker.Contains, "flag provided but not defined: --no-such-flag")
+	c.Assert(stderr, checker.Contains, "unknown flag: --no-such-flag")
 }
 }
 
 
 func (s *DockerSuite) TestVolumeCliInspectTmplError(c *check.C) {
 func (s *DockerSuite) TestVolumeCliInspectTmplError(c *check.C) {
@@ -268,7 +268,7 @@ func (s *DockerSuite) TestVolumeCliCreateLabel(c *check.C) {
 	out, _, err := dockerCmdWithError("volume", "create", "--label", testLabel+"="+testValue, "--name", testVol)
 	out, _, err := dockerCmdWithError("volume", "create", "--label", testLabel+"="+testValue, "--name", testVol)
 	c.Assert(err, check.IsNil)
 	c.Assert(err, check.IsNil)
 
 
-	out, _ = dockerCmd(c, "volume", "inspect", "--format='{{ .Labels."+testLabel+" }}'", testVol)
+	out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Labels."+testLabel+" }}", testVol)
 	c.Assert(strings.TrimSpace(out), check.Equals, testValue)
 	c.Assert(strings.TrimSpace(out), check.Equals, testValue)
 }
 }
 
 
@@ -295,7 +295,7 @@ func (s *DockerSuite) TestVolumeCliCreateLabelMultiple(c *check.C) {
 	c.Assert(err, check.IsNil)
 	c.Assert(err, check.IsNil)
 
 
 	for k, v := range testLabels {
 	for k, v := range testLabels {
-		out, _ = dockerCmd(c, "volume", "inspect", "--format='{{ .Labels."+k+" }}'", testVol)
+		out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Labels."+k+" }}", testVol)
 		c.Assert(strings.TrimSpace(out), check.Equals, v)
 		c.Assert(strings.TrimSpace(out), check.Equals, v)
 	}
 	}
 }
 }

+ 5 - 0
opts/opts.go

@@ -100,6 +100,11 @@ func (opts *ListOpts) Len() int {
 	return len((*opts.values))
 	return len((*opts.values))
 }
 }
 
 
+// Type returns a string name for this Option type
+func (opts *ListOpts) Type() string {
+	return "list"
+}
+
 // NamedOption is an interface that list and map options
 // NamedOption is an interface that list and map options
 // with names implement.
 // with names implement.
 type NamedOption interface {
 type NamedOption interface {