Signed-off-by: Daniel Nephin <dnephin@docker.com>
| ... | ... |
@@ -1,9 +1,12 @@ |
| 1 | 1 |
package volume |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "fmt" |
|
| 5 |
+ |
|
| 4 | 6 |
"github.com/spf13/cobra" |
| 5 | 7 |
|
| 6 | 8 |
"github.com/docker/docker/api/client" |
| 9 |
+ "github.com/docker/docker/cli" |
|
| 7 | 10 |
) |
| 8 | 11 |
|
| 9 | 12 |
// NewVolumeCommand returns a cobra command for `volume` subcommands |
| ... | ... |
@@ -11,6 +14,14 @@ func NewVolumeCommand(dockerCli *client.DockerCli) *cobra.Command {
|
| 11 | 11 |
cmd := &cobra.Command{
|
| 12 | 12 |
Use: "volume", |
| 13 | 13 |
Short: "Manage Docker volumes", |
| 14 |
+ // TODO: remove once cobra is patched to handle this |
|
| 15 |
+ RunE: func(cmd *cobra.Command, args []string) error {
|
|
| 16 |
+ fmt.Fprintf(dockerCli.Err(), "\n%s", cmd.UsageString()) |
|
| 17 |
+ if len(args) > 0 {
|
|
| 18 |
+ return cli.StatusError{StatusCode: 1}
|
|
| 19 |
+ } |
|
| 20 |
+ return nil |
|
| 21 |
+ }, |
|
| 14 | 22 |
} |
| 15 | 23 |
cmd.AddCommand( |
| 16 | 24 |
newCreateCommand(dockerCli), |
| ... | ... |
@@ -6,6 +6,7 @@ import ( |
| 6 | 6 |
"golang.org/x/net/context" |
| 7 | 7 |
|
| 8 | 8 |
"github.com/docker/docker/api/client" |
| 9 |
+ "github.com/docker/docker/cli" |
|
| 9 | 10 |
"github.com/docker/docker/opts" |
| 10 | 11 |
runconfigopts "github.com/docker/docker/runconfig/opts" |
| 11 | 12 |
"github.com/docker/engine-api/types" |
| ... | ... |
@@ -20,12 +21,18 @@ type createOptions struct {
|
| 20 | 20 |
} |
| 21 | 21 |
|
| 22 | 22 |
func newCreateCommand(dockerCli *client.DockerCli) *cobra.Command {
|
| 23 |
- var opts createOptions |
|
| 23 |
+ opts := createOptions{
|
|
| 24 |
+ driverOpts: *opts.NewMapOpts(nil, nil), |
|
| 25 |
+ } |
|
| 24 | 26 |
|
| 25 | 27 |
cmd := &cobra.Command{
|
| 26 | 28 |
Use: "create", |
| 27 | 29 |
Short: "Create a volume", |
| 28 | 30 |
RunE: func(cmd *cobra.Command, args []string) error {
|
| 31 |
+ // TODO: remove once cobra is patched to handle this |
|
| 32 |
+ if err := cli.AcceptsNoArgs(args, cmd); err != nil {
|
|
| 33 |
+ return err |
|
| 34 |
+ } |
|
| 29 | 35 |
return runCreate(dockerCli, opts) |
| 30 | 36 |
}, |
| 31 | 37 |
} |
| ... | ... |
@@ -8,6 +8,7 @@ import ( |
| 8 | 8 |
"golang.org/x/net/context" |
| 9 | 9 |
|
| 10 | 10 |
"github.com/docker/docker/api/client" |
| 11 |
+ "github.com/docker/docker/cli" |
|
| 11 | 12 |
"github.com/docker/engine-api/types" |
| 12 | 13 |
"github.com/docker/engine-api/types/filters" |
| 13 | 14 |
"github.com/spf13/cobra" |
| ... | ... |
@@ -34,6 +35,10 @@ func newListCommand(dockerCli *client.DockerCli) *cobra.Command {
|
| 34 | 34 |
Aliases: []string{"list"},
|
| 35 | 35 |
Short: "List volumes", |
| 36 | 36 |
RunE: func(cmd *cobra.Command, args []string) error {
|
| 37 |
+ // TODO: remove once cobra is patched to handle this |
|
| 38 |
+ if err := cli.AcceptsNoArgs(args, cmd); err != nil {
|
|
| 39 |
+ return err |
|
| 40 |
+ } |
|
| 37 | 41 |
return runList(dockerCli, opts) |
| 38 | 42 |
}, |
| 39 | 43 |
} |
| ... | ... |
@@ -18,17 +18,21 @@ type CobraAdaptor struct {
|
| 18 | 18 |
|
| 19 | 19 |
// NewCobraAdaptor returns a new handler |
| 20 | 20 |
func NewCobraAdaptor(clientFlags *cliflags.ClientFlags) CobraAdaptor {
|
| 21 |
- var rootCmd = &cobra.Command{
|
|
| 22 |
- Use: "docker", |
|
| 23 |
- } |
|
| 24 |
- rootCmd.SetUsageTemplate(usageTemplate) |
|
| 25 |
- |
|
| 26 | 21 |
stdin, stdout, stderr := term.StdStreams() |
| 27 | 22 |
dockerCli := client.NewDockerCli(stdin, stdout, stderr, clientFlags) |
| 28 | 23 |
|
| 24 |
+ var rootCmd = &cobra.Command{
|
|
| 25 |
+ Use: "docker", |
|
| 26 |
+ SilenceUsage: true, |
|
| 27 |
+ SilenceErrors: true, |
|
| 28 |
+ } |
|
| 29 |
+ rootCmd.SetUsageTemplate(usageTemplate) |
|
| 30 |
+ rootCmd.SetHelpTemplate(helpTemplate) |
|
| 31 |
+ rootCmd.SetOutput(stdout) |
|
| 29 | 32 |
rootCmd.AddCommand( |
| 30 | 33 |
volume.NewVolumeCommand(dockerCli), |
| 31 | 34 |
) |
| 35 |
+ |
|
| 32 | 36 |
return CobraAdaptor{
|
| 33 | 37 |
rootCmd: rootCmd, |
| 34 | 38 |
dockerCli: dockerCli, |
| ... | ... |
@@ -64,20 +68,24 @@ func (c CobraAdaptor) Command(name string) func(...string) error {
|
| 64 | 64 |
return nil |
| 65 | 65 |
} |
| 66 | 66 |
|
| 67 |
-var usageTemplate = `Usage: {{if .Runnable}}{{if .HasFlags}}{{appendIfNotPresent .UseLine "[OPTIONS]"}}{{else}}{{.UseLine}}{{end}}{{end}}{{if .HasSubCommands}}{{ .CommandPath}} COMMAND {{end}}{{if gt .Aliases 0}}
|
|
| 67 |
+var usageTemplate = `Usage: {{if not .HasSubCommands}}{{if .HasLocalFlags}}{{appendIfNotPresent .UseLine "[OPTIONS]"}}{{else}}{{.UseLine}}{{end}}{{end}}{{if .HasSubCommands}}{{ .CommandPath}} COMMAND{{end}}
|
|
| 68 |
+ |
|
| 69 |
+{{with or .Long .Short }}{{. | trim}}{{end}}{{if gt .Aliases 0}}
|
|
| 68 | 70 |
|
| 69 | 71 |
Aliases: |
| 70 |
- {{.NameAndAliases}}
|
|
| 71 |
-{{end}}{{if .HasExample}}
|
|
| 72 |
+ {{.NameAndAliases}}{{end}}{{if .HasExample}}
|
|
| 72 | 73 |
|
| 73 | 74 |
Examples: |
| 74 |
-{{ .Example }}{{end}}{{ if .HasLocalFlags}}
|
|
| 75 |
+{{ .Example }}{{end}}{{if .HasFlags}}
|
|
| 75 | 76 |
|
| 76 | 77 |
Options: |
| 77 |
-{{.LocalFlags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasAvailableSubCommands}}
|
|
| 78 |
+{{.Flags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasAvailableSubCommands}}
|
|
| 78 | 79 |
|
| 79 | 80 |
Commands:{{range .Commands}}{{if .IsAvailableCommand}}
|
| 80 | 81 |
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasSubCommands }}
|
| 81 | 82 |
|
| 82 | 83 |
Run '{{.CommandPath}} COMMAND --help' for more information on a command.{{end}}
|
| 83 | 84 |
` |
| 85 |
+ |
|
| 86 |
+var helpTemplate = ` |
|
| 87 |
+{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
|
| ... | ... |
@@ -21,3 +21,17 @@ func MinRequiredArgs(args []string, min int, cmd *cobra.Command) error {
|
| 21 | 21 |
cmd.Short, |
| 22 | 22 |
) |
| 23 | 23 |
} |
| 24 |
+ |
|
| 25 |
+// AcceptsNoArgs returns an error message if there are args |
|
| 26 |
+func AcceptsNoArgs(args []string, cmd *cobra.Command) error {
|
|
| 27 |
+ if len(args) == 0 {
|
|
| 28 |
+ return nil |
|
| 29 |
+ } |
|
| 30 |
+ |
|
| 31 |
+ return fmt.Errorf( |
|
| 32 |
+ "\"%s\" accepts no argument(s).\n\nUsage: %s\n\n%s", |
|
| 33 |
+ cmd.CommandPath(), |
|
| 34 |
+ cmd.UseLine(), |
|
| 35 |
+ cmd.Short, |
|
| 36 |
+ ) |
|
| 37 |
+} |
| ... | ... |
@@ -48,7 +48,6 @@ var DockerCommandUsage = []Command{
|
| 48 | 48 |
{"unpause", "Unpause all processes within a container"},
|
| 49 | 49 |
{"update", "Update configuration of one or more containers"},
|
| 50 | 50 |
{"version", "Show the Docker version information"},
|
| 51 |
- {"volume", "Manage Docker volumes"},
|
|
| 52 | 51 |
{"wait", "Block until a container stops, then print its exit code"},
|
| 53 | 52 |
} |
| 54 | 53 |
|
| ... | ... |
@@ -249,7 +249,8 @@ func testCommand(cmd string, newEnvs []string, scanForHome bool, home string) er |
| 249 | 249 |
// If a line starts with 4 spaces then assume someone |
| 250 | 250 |
// added a multi-line description for an option and we need |
| 251 | 251 |
// to flag it |
| 252 |
- if strings.HasPrefix(line, " ") {
|
|
| 252 |
+ if strings.HasPrefix(line, " ") && |
|
| 253 |
+ !strings.HasPrefix(strings.TrimLeft(line, " "), "--") {
|
|
| 253 | 254 |
return fmt.Errorf("Help for %q should not have a multi-line option", cmd)
|
| 254 | 255 |
} |
| 255 | 256 |
|
| ... | ... |
@@ -260,7 +261,7 @@ func testCommand(cmd string, newEnvs []string, scanForHome bool, home string) er |
| 260 | 260 |
|
| 261 | 261 |
// Options should NOT end with a space |
| 262 | 262 |
if strings.HasSuffix(line, " ") {
|
| 263 |
- return fmt.Errorf("Help for %q should not end with a space", cmd)
|
|
| 263 |
+ return fmt.Errorf("Help for %q should not end with a space: %s", cmd, line)
|
|
| 264 | 264 |
} |
| 265 | 265 |
|
| 266 | 266 |
} |
| ... | ... |
@@ -326,8 +327,8 @@ func testCommand(cmd string, newEnvs []string, scanForHome bool, home string) er |
| 326 | 326 |
return fmt.Errorf("Bad output from %q\nstdout:%q\nstderr:%q\nec:%d\nerr:%q\n", args, out, stderr, ec, err)
|
| 327 | 327 |
} |
| 328 | 328 |
// Should have just short usage |
| 329 |
- if !strings.Contains(stderr, "\nUsage:\t") {
|
|
| 330 |
- return fmt.Errorf("Missing short usage on %q\n", args)
|
|
| 329 |
+ if !strings.Contains(stderr, "\nUsage:") {
|
|
| 330 |
+ return fmt.Errorf("Missing short usage on %q\n:%#v", args, stderr)
|
|
| 331 | 331 |
} |
| 332 | 332 |
// But shouldn't have full usage |
| 333 | 333 |
if strings.Contains(stderr, "--help=false") {
|
| ... | ... |
@@ -25,7 +25,7 @@ func (s *DockerSuite) TestVolumeCliCreateOptionConflict(c *check.C) {
|
| 25 | 25 |
c.Assert(err, check.NotNil, check.Commentf("volume create exception name already in use with another driver"))
|
| 26 | 26 |
c.Assert(out, checker.Contains, "A volume named test already exists") |
| 27 | 27 |
|
| 28 |
- out, _ = dockerCmd(c, "volume", "inspect", "--format='{{ .Driver }}'", "test")
|
|
| 28 |
+ out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Driver }}", "test")
|
|
| 29 | 29 |
_, _, err = dockerCmdWithError("volume", "create", "--name", "test", "--driver", strings.TrimSpace(out))
|
| 30 | 30 |
c.Assert(err, check.IsNil) |
| 31 | 31 |
} |
| ... | ... |
@@ -39,11 +39,11 @@ func (s *DockerSuite) TestVolumeCliInspect(c *check.C) {
|
| 39 | 39 |
|
| 40 | 40 |
out, _ := dockerCmd(c, "volume", "create") |
| 41 | 41 |
name := strings.TrimSpace(out) |
| 42 |
- out, _ = dockerCmd(c, "volume", "inspect", "--format='{{ .Name }}'", name)
|
|
| 42 |
+ out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Name }}", name)
|
|
| 43 | 43 |
c.Assert(strings.TrimSpace(out), check.Equals, name) |
| 44 | 44 |
|
| 45 | 45 |
dockerCmd(c, "volume", "create", "--name", "test") |
| 46 |
- out, _ = dockerCmd(c, "volume", "inspect", "--format='{{ .Name }}'", "test")
|
|
| 46 |
+ out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Name }}", "test")
|
|
| 47 | 47 |
c.Assert(strings.TrimSpace(out), check.Equals, "test") |
| 48 | 48 |
} |
| 49 | 49 |
|
| ... | ... |
@@ -212,7 +212,7 @@ func (s *DockerSuite) TestVolumeCliRm(c *check.C) {
|
| 212 | 212 |
func (s *DockerSuite) TestVolumeCliNoArgs(c *check.C) {
|
| 213 | 213 |
out, _ := dockerCmd(c, "volume") |
| 214 | 214 |
// no args should produce the cmd usage output |
| 215 |
- usage := "Usage: docker volume [OPTIONS] [COMMAND]" |
|
| 215 |
+ usage := "Usage: docker volume COMMAND" |
|
| 216 | 216 |
c.Assert(out, checker.Contains, usage) |
| 217 | 217 |
|
| 218 | 218 |
// invalid arg should error and show the command usage on stderr |
| ... | ... |
@@ -224,7 +224,7 @@ func (s *DockerSuite) TestVolumeCliNoArgs(c *check.C) {
|
| 224 | 224 |
_, stderr, _, err = runCommandWithStdoutStderr(exec.Command(dockerBinary, "volume", "--no-such-flag")) |
| 225 | 225 |
c.Assert(err, check.NotNil, check.Commentf(stderr)) |
| 226 | 226 |
c.Assert(stderr, checker.Contains, usage) |
| 227 |
- c.Assert(stderr, checker.Contains, "flag provided but not defined: --no-such-flag") |
|
| 227 |
+ c.Assert(stderr, checker.Contains, "unknown flag: --no-such-flag") |
|
| 228 | 228 |
} |
| 229 | 229 |
|
| 230 | 230 |
func (s *DockerSuite) TestVolumeCliInspectTmplError(c *check.C) {
|
| ... | ... |
@@ -268,7 +268,7 @@ func (s *DockerSuite) TestVolumeCliCreateLabel(c *check.C) {
|
| 268 | 268 |
out, _, err := dockerCmdWithError("volume", "create", "--label", testLabel+"="+testValue, "--name", testVol)
|
| 269 | 269 |
c.Assert(err, check.IsNil) |
| 270 | 270 |
|
| 271 |
- out, _ = dockerCmd(c, "volume", "inspect", "--format='{{ .Labels."+testLabel+" }}'", testVol)
|
|
| 271 |
+ out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Labels."+testLabel+" }}", testVol)
|
|
| 272 | 272 |
c.Assert(strings.TrimSpace(out), check.Equals, testValue) |
| 273 | 273 |
} |
| 274 | 274 |
|
| ... | ... |
@@ -295,7 +295,7 @@ func (s *DockerSuite) TestVolumeCliCreateLabelMultiple(c *check.C) {
|
| 295 | 295 |
c.Assert(err, check.IsNil) |
| 296 | 296 |
|
| 297 | 297 |
for k, v := range testLabels {
|
| 298 |
- out, _ = dockerCmd(c, "volume", "inspect", "--format='{{ .Labels."+k+" }}'", testVol)
|
|
| 298 |
+ out, _ = dockerCmd(c, "volume", "inspect", "--format={{ .Labels."+k+" }}", testVol)
|
|
| 299 | 299 |
c.Assert(strings.TrimSpace(out), check.Equals, v) |
| 300 | 300 |
} |
| 301 | 301 |
} |
| ... | ... |
@@ -100,6 +100,11 @@ func (opts *ListOpts) Len() int {
|
| 100 | 100 |
return len((*opts.values)) |
| 101 | 101 |
} |
| 102 | 102 |
|
| 103 |
+// Type returns a string name for this Option type |
|
| 104 |
+func (opts *ListOpts) Type() string {
|
|
| 105 |
+ return "list" |
|
| 106 |
+} |
|
| 107 |
+ |
|
| 103 | 108 |
// NamedOption is an interface that list and map options |
| 104 | 109 |
// with names implement. |
| 105 | 110 |
type NamedOption interface {
|