diff --git a/api/client/commands.go b/api/client/commands.go index d746466316..745180e19c 100644 --- a/api/client/commands.go +++ b/api/client/commands.go @@ -2,7 +2,5 @@ package client // Command returns a cli command handler if one exists func (cli *DockerCli) Command(name string) func(...string) error { - return map[string]func(...string) error{ - "inspect": cli.CmdInspect, - }[name] + return map[string]func(...string) error{}[name] } diff --git a/api/client/inspect.go b/api/client/inspect.go deleted file mode 100644 index 2c22d5dc65..0000000000 --- a/api/client/inspect.go +++ /dev/null @@ -1,95 +0,0 @@ -package client - -import ( - "fmt" - - "golang.org/x/net/context" - - "github.com/docker/docker/api/client/inspect" - Cli "github.com/docker/docker/cli" - flag "github.com/docker/docker/pkg/mflag" - "github.com/docker/engine-api/client" -) - -// CmdInspect displays low-level information on one or more containers, images or tasks. -// -// Usage: docker inspect [OPTIONS] CONTAINER|IMAGE|TASK [CONTAINER|IMAGE|TASK...] -func (cli *DockerCli) CmdInspect(args ...string) error { - cmd := Cli.Subcmd("inspect", []string{"[OPTIONS] CONTAINER|IMAGE|TASK [CONTAINER|IMAGE|TASK...]"}, Cli.DockerCommands["inspect"].Description, true) - tmplStr := cmd.String([]string{"f", "-format"}, "", "Format the output using the given go template") - inspectType := cmd.String([]string{"-type"}, "", "Return JSON for specified type, (e.g image, container or task)") - size := cmd.Bool([]string{"s", "-size"}, false, "Display total file sizes if the type is container") - cmd.Require(flag.Min, 1) - - cmd.ParseFlags(args, true) - - if *inspectType != "" && *inspectType != "container" && *inspectType != "image" && *inspectType != "task" { - return fmt.Errorf("%q is not a valid value for --type", *inspectType) - } - - ctx := context.Background() - - var elementSearcher inspect.GetRefFunc - switch *inspectType { - case "container": - elementSearcher = cli.inspectContainers(ctx, *size) - case "image": - elementSearcher = cli.inspectImages(ctx, *size) - case "task": - if *size { - fmt.Fprintln(cli.err, "WARNING: --size ignored for tasks") - } - elementSearcher = cli.inspectTasks(ctx) - default: - elementSearcher = cli.inspectAll(ctx, *size) - } - - return inspect.Inspect(cli.out, cmd.Args(), *tmplStr, elementSearcher) -} - -func (cli *DockerCli) inspectContainers(ctx context.Context, getSize bool) inspect.GetRefFunc { - return func(ref string) (interface{}, []byte, error) { - return cli.client.ContainerInspectWithRaw(ctx, ref, getSize) - } -} - -func (cli *DockerCli) inspectImages(ctx context.Context, getSize bool) inspect.GetRefFunc { - return func(ref string) (interface{}, []byte, error) { - return cli.client.ImageInspectWithRaw(ctx, ref, getSize) - } -} - -func (cli *DockerCli) inspectTasks(ctx context.Context) inspect.GetRefFunc { - return func(ref string) (interface{}, []byte, error) { - return cli.client.TaskInspectWithRaw(ctx, ref) - } -} - -func (cli *DockerCli) inspectAll(ctx context.Context, getSize bool) inspect.GetRefFunc { - return func(ref string) (interface{}, []byte, error) { - c, rawContainer, err := cli.client.ContainerInspectWithRaw(ctx, ref, getSize) - if err != nil { - // Search for image with that id if a container doesn't exist. - if client.IsErrContainerNotFound(err) { - i, rawImage, err := cli.client.ImageInspectWithRaw(ctx, ref, getSize) - if err != nil { - if client.IsErrImageNotFound(err) { - // Search for task with that id if an image doesn't exists. - t, rawTask, err := cli.client.TaskInspectWithRaw(ctx, ref) - if err != nil { - return nil, nil, fmt.Errorf("Error: No such image, container or task: %s", ref) - } - if getSize { - fmt.Fprintln(cli.err, "WARNING: --size ignored for tasks") - } - return t, rawTask, nil - } - return nil, nil, err - } - return i, rawImage, nil - } - return nil, nil, err - } - return c, rawContainer, nil - } -} diff --git a/api/client/system/inspect.go b/api/client/system/inspect.go new file mode 100644 index 0000000000..1866a90ea8 --- /dev/null +++ b/api/client/system/inspect.go @@ -0,0 +1,103 @@ +package system + +import ( + "fmt" + "strings" + + "golang.org/x/net/context" + + "github.com/docker/docker/api/client" + "github.com/docker/docker/api/client/inspect" + "github.com/docker/docker/cli" + apiclient "github.com/docker/engine-api/client" + "github.com/spf13/cobra" +) + +type inspectOptions struct { + format string + inspectType string + size bool + ids []string +} + +// NewInspectCommand creates a new cobra.Command for `docker inspect` +func NewInspectCommand(dockerCli *client.DockerCli) *cobra.Command { + var opts inspectOptions + + cmd := &cobra.Command{ + Use: "inspect [OPTIONS] CONTAINER|IMAGE|TASK [CONTAINER|IMAGE|TASK...]", + Short: "Return low-level information on a container, image or task", + Args: cli.RequiresMinArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + opts.ids = args + return runInspect(dockerCli, opts) + }, + } + + flags := cmd.Flags() + flags.StringVarP(&opts.format, "format", "f", "", "Format the output using the given go template") + flags.StringVar(&opts.inspectType, "type", "", "Return JSON for specified type, (e.g image, container or task)") + flags.BoolVarP(&opts.size, "size", "s", false, "Display total file sizes if the type is container") + + return cmd +} + +func runInspect(dockerCli *client.DockerCli, opts inspectOptions) error { + ctx := context.Background() + client := dockerCli.Client() + + var getRefFunc inspect.GetRefFunc + switch opts.inspectType { + case "container": + getRefFunc = func(ref string) (interface{}, []byte, error) { + return client.ContainerInspectWithRaw(ctx, ref, opts.size) + } + case "image": + getRefFunc = func(ref string) (interface{}, []byte, error) { + return client.ImageInspectWithRaw(ctx, ref, opts.size) + } + case "task": + if opts.size { + fmt.Fprintln(dockerCli.Err(), "WARNING: --size ignored for tasks") + } + getRefFunc = func(ref string) (interface{}, []byte, error) { + return client.TaskInspectWithRaw(ctx, ref) + } + case "": + getRefFunc = inspectAll(ctx, dockerCli, opts.size) + default: + return fmt.Errorf("%q is not a valid value for --type", opts.inspectType) + } + + return inspect.Inspect(dockerCli.Out(), opts.ids, opts.format, getRefFunc) +} + +func inspectAll(ctx context.Context, dockerCli *client.DockerCli, getSize bool) inspect.GetRefFunc { + client := dockerCli.Client() + + return func(ref string) (interface{}, []byte, error) { + c, rawContainer, err := client.ContainerInspectWithRaw(ctx, ref, getSize) + if err == nil || !apiclient.IsErrNotFound(err) { + return c, rawContainer, err + } + // Search for image with that id if a container doesn't exist. + i, rawImage, err := client.ImageInspectWithRaw(ctx, ref, getSize) + if err == nil || !apiclient.IsErrNotFound(err) { + return i, rawImage, err + } + + // Search for task with that id if an image doesn't exist. + t, rawTask, err := client.TaskInspectWithRaw(ctx, ref) + if err == nil || !(apiclient.IsErrNotFound(err) || isErrorNoSwarmMode(err)) { + if getSize { + fmt.Fprintln(dockerCli.Err(), "WARNING: --size ignored for tasks") + } + return t, rawTask, err + } + return nil, nil, fmt.Errorf("Error: No such container, image or task: %s", ref) + } +} + +func isErrorNoSwarmMode(err error) bool { + return strings.Contains(err.Error(), "This node is not a swarm manager") +} diff --git a/cli/cobraadaptor/adaptor.go b/cli/cobraadaptor/adaptor.go index 6614f1fa69..2df553bea1 100644 --- a/cli/cobraadaptor/adaptor.go +++ b/cli/cobraadaptor/adaptor.go @@ -83,6 +83,7 @@ func NewCobraAdaptor(clientFlags *cliflags.ClientFlags) CobraAdaptor { image.NewTagCommand(dockerCli), network.NewNetworkCommand(dockerCli), system.NewEventsCommand(dockerCli), + system.NewInspectCommand(dockerCli), registry.NewLoginCommand(dockerCli), registry.NewLogoutCommand(dockerCli), system.NewVersionCommand(dockerCli), diff --git a/cli/usage.go b/cli/usage.go index 0ad053d734..a8a0328643 100644 --- a/cli/usage.go +++ b/cli/usage.go @@ -7,9 +7,7 @@ type Command struct { } // DockerCommandUsage lists the top level docker commands and their short usage -var DockerCommandUsage = []Command{ - {"inspect", "Return low-level information on a container, image or task"}, -} +var DockerCommandUsage = []Command{} // DockerCommands stores all the docker command var DockerCommands = make(map[string]Command) diff --git a/integration-cli/docker_cli_by_digest_test.go b/integration-cli/docker_cli_by_digest_test.go index a62b251364..a007d99021 100644 --- a/integration-cli/docker_cli_by_digest_test.go +++ b/integration-cli/docker_cli_by_digest_test.go @@ -176,7 +176,7 @@ func (s *DockerRegistrySuite) TestRemoveImageByDigest(c *check.C) { _, err = inspectFieldWithError(imageReference, "Id") //unexpected nil err trying to inspect what should be a non-existent image c.Assert(err, checker.NotNil) - c.Assert(err.Error(), checker.Contains, "No such image") + c.Assert(err.Error(), checker.Contains, "No such container, image") } func (s *DockerRegistrySuite) TestBuildByDigest(c *check.C) { diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index 36353cead5..a35bfcd41d 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -326,7 +326,7 @@ func (s *DockerDaemonSuite) TestDaemonIptablesCreate(c *check.C) { } // make sure the container is not running - runningOut, err := s.d.Cmd("inspect", "--format='{{.State.Running}}'", "top") + runningOut, err := s.d.Cmd("inspect", "--format={{.State.Running}}", "top") if err != nil { c.Fatalf("Could not inspect on container: %s, %v", out, err) } @@ -2196,7 +2196,7 @@ func (s *DockerDaemonSuite) TestCleanupMountsAfterDaemonCrash(c *check.C) { } // container should be running. - out, err = s.d.Cmd("inspect", "--format='{{.State.Running}}'", id) + out, err = s.d.Cmd("inspect", "--format={{.State.Running}}", id) c.Assert(err, check.IsNil, check.Commentf("Output: %s", out)) out = strings.TrimSpace(out) if out != "true" { diff --git a/integration-cli/docker_cli_external_graphdriver_unix_test.go b/integration-cli/docker_cli_external_graphdriver_unix_test.go index 771996bb11..c4a7281cc1 100644 --- a/integration-cli/docker_cli_external_graphdriver_unix_test.go +++ b/integration-cli/docker_cli_external_graphdriver_unix_test.go @@ -353,7 +353,7 @@ func (s *DockerExternalGraphdriverSuite) testExternalGraphDriver(name string, ex err = s.d.Restart("-s", name) - out, err = s.d.Cmd("inspect", "--format='{{.GraphDriver.Name}}'", "graphtest") + out, err = s.d.Cmd("inspect", "--format={{.GraphDriver.Name}}", "graphtest") c.Assert(err, check.IsNil, check.Commentf(out)) c.Assert(strings.TrimSpace(out), check.Equals, name) diff --git a/integration-cli/docker_cli_health_test.go b/integration-cli/docker_cli_health_test.go index d231965332..669edb330c 100644 --- a/integration-cli/docker_cli_health_test.go +++ b/integration-cli/docker_cli_health_test.go @@ -73,10 +73,10 @@ func (s *DockerSuite) TestHealth(c *check.C) { // Inspect the options out, _ = dockerCmd(c, "inspect", - "--format='timeout={{.Config.Healthcheck.Timeout}} "+ + "--format=timeout={{.Config.Healthcheck.Timeout}} "+ "interval={{.Config.Healthcheck.Interval}} "+ "retries={{.Config.Healthcheck.Retries}} "+ - "test={{.Config.Healthcheck.Test}}'", name) + "test={{.Config.Healthcheck.Test}}", name) c.Check(out, checker.Equals, "timeout=30s interval=1s retries=0 test=[CMD-SHELL cat /status]\n") // Start diff --git a/integration-cli/docker_cli_inspect_test.go b/integration-cli/docker_cli_inspect_test.go index f1f8247cdc..61bab8de9a 100644 --- a/integration-cli/docker_cli_inspect_test.go +++ b/integration-cli/docker_cli_inspect_test.go @@ -85,7 +85,7 @@ func (s *DockerSuite) TestInspectTypeFlagContainer(c *check.C) { dockerCmd(c, "run", "--name=busybox", "-d", "busybox", "top") - formatStr := "--format='{{.State.Running}}'" + formatStr := "--format={{.State.Running}}" out, _ := dockerCmd(c, "inspect", "--type=container", formatStr, "busybox") c.Assert(out, checker.Equals, "true\n") // not a container JSON } @@ -137,7 +137,7 @@ func (s *DockerSuite) TestInspectImageFilterInt(c *check.C) { c.Assert(err, checker.IsNil, check.Commentf("failed to inspect size of the image: %s, %v", out, err)) //now see if the size turns out to be the same - formatStr := fmt.Sprintf("--format='{{eq .Size %d}}'", size) + formatStr := fmt.Sprintf("--format={{eq .Size %d}}", size) out, _ = dockerCmd(c, "inspect", formatStr, imageTest) result, err := strconv.ParseBool(strings.TrimSuffix(out, "\n")) c.Assert(err, checker.IsNil) @@ -159,7 +159,7 @@ func (s *DockerSuite) TestInspectContainerFilterInt(c *check.C) { c.Assert(err, checker.IsNil, check.Commentf("failed to inspect exitcode of the container: %s, %v", out, err)) //now get the exit code to verify - formatStr := fmt.Sprintf("--format='{{eq .State.ExitCode %d}}'", exitCode) + formatStr := fmt.Sprintf("--format={{eq .State.ExitCode %d}}", exitCode) out, _ = dockerCmd(c, "inspect", formatStr, id) result, err := strconv.ParseBool(strings.TrimSuffix(out, "\n")) c.Assert(err, checker.IsNil) @@ -312,7 +312,7 @@ func (s *DockerSuite) TestInspectNoSizeFlagContainer(c *check.C) { runSleepingContainer(c, "--name=busybox", "-d") - formatStr := "--format='{{.SizeRw}},{{.SizeRootFs}}'" + formatStr := "--format={{.SizeRw}},{{.SizeRootFs}}" out, _ := dockerCmd(c, "inspect", "--type=container", formatStr, "busybox") c.Assert(strings.TrimSpace(out), check.Equals, ",", check.Commentf("Exepcted not to display size info: %s", out)) } @@ -356,7 +356,7 @@ func (s *DockerSuite) TestInspectTemplateError(c *check.C) { func (s *DockerSuite) TestInspectJSONFields(c *check.C) { runSleepingContainer(c, "--name=busybox", "-d") - out, _, err := dockerCmdWithError("inspect", "--type=container", "--format='{{.HostConfig.Dns}}'", "busybox") + out, _, err := dockerCmdWithError("inspect", "--type=container", "--format={{.HostConfig.Dns}}", "busybox") c.Assert(err, check.IsNil) c.Assert(out, checker.Equals, "[]\n") diff --git a/integration-cli/docker_cli_rename_test.go b/integration-cli/docker_cli_rename_test.go index 1ba620a5e1..f1a16fae52 100644 --- a/integration-cli/docker_cli_rename_test.go +++ b/integration-cli/docker_cli_rename_test.go @@ -63,7 +63,7 @@ func (s *DockerSuite) TestRenameCheckNames(c *check.C) { name, err := inspectFieldWithError("first_name", "Name") c.Assert(err, checker.NotNil, check.Commentf(name)) - c.Assert(err.Error(), checker.Contains, "No such image, container or task: first_name") + c.Assert(err.Error(), checker.Contains, "No such container, image or task: first_name") } func (s *DockerSuite) TestRenameInvalidName(c *check.C) { @@ -129,7 +129,7 @@ func (s *DockerSuite) TestRenameContainerWithLinkedContainer(c *check.C) { db1, _ := dockerCmd(c, "run", "--name", "db1", "-d", "busybox", "top") dockerCmd(c, "run", "--name", "app1", "-d", "--link", "db1:/mysql", "busybox", "top") dockerCmd(c, "rename", "app1", "app2") - out, _, err := dockerCmdWithError("inspect", "--format='{{ .Id }}'", "app2/mysql") + out, _, err := dockerCmdWithError("inspect", "--format={{ .Id }}", "app2/mysql") c.Assert(err, checker.IsNil) c.Assert(strings.TrimSpace(out), checker.Equals, strings.TrimSpace(db1)) } diff --git a/integration-cli/docker_cli_userns_test.go b/integration-cli/docker_cli_userns_test.go index f8b3f77b61..1be80f64dd 100644 --- a/integration-cli/docker_cli_userns_test.go +++ b/integration-cli/docker_cli_userns_test.go @@ -45,7 +45,7 @@ func (s *DockerDaemonSuite) TestDaemonUserNamespaceRootSetting(c *check.C) { user := s.findUser(c, "userns") c.Assert(uidgid[0], checker.Equals, user) - pid, err := s.d.Cmd("inspect", "--format='{{.State.Pid}}'", "userns") + pid, err := s.d.Cmd("inspect", "--format={{.State.Pid}}", "userns") c.Assert(err, checker.IsNil, check.Commentf("Could not inspect running container: out: %q", pid)) // check the uid and gid maps for the PID to ensure root is remapped // (cmd = cat /proc//uid_map | grep -E '0\s+9999\s+1')