From 3db9f7afce03b1926fd6ba22483ebf0d76d2a1da Mon Sep 17 00:00:00 2001 From: "Arnaud Porterie (icecrime)" Date: Wed, 15 Jun 2016 21:41:54 -0700 Subject: [PATCH] Refactor `docker inspect` to work on all types Signed-off-by: Arnaud Porterie (icecrime) --- api/client/system/inspect.go | 129 ++++++++++++------- docs/reference/commandline/inspect.md | 7 +- integration-cli/docker_cli_by_digest_test.go | 2 +- integration-cli/docker_cli_rename_test.go | 2 +- man/docker-inspect.1.md | 21 +-- 5 files changed, 98 insertions(+), 63 deletions(-) diff --git a/api/client/system/inspect.go b/api/client/system/inspect.go index 196966bc14..7d8065d57d 100644 --- a/api/client/system/inspect.go +++ b/api/client/system/inspect.go @@ -36,68 +36,101 @@ func NewInspectCommand(dockerCli *client.DockerCli) *cobra.Command { 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.StringVar(&opts.inspectType, "type", "", "Return JSON for specified type") 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 + var elementSearcher 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) - } - 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) + case "", "container", "image", "node", "network", "service", "volume", "task": + elementSearcher = inspectAll(context.Background(), dockerCli, opts.size, opts.inspectType) default: return fmt.Errorf("%q is not a valid value for --type", opts.inspectType) } - - return inspect.Inspect(dockerCli.Out(), opts.ids, opts.format, getRefFunc) + return inspect.Inspect(dockerCli.Out(), opts.ids, opts.format, elementSearcher) } -func inspectAll(ctx context.Context, dockerCli *client.DockerCli, getSize bool) inspect.GetRefFunc { - client := dockerCli.Client() - +func inspectContainers(ctx context.Context, dockerCli *client.DockerCli, getSize bool) inspect.GetRefFunc { 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) - 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) + return dockerCli.Client().ContainerInspectWithRaw(ctx, ref, getSize) } } -func isErrorNoSwarmMode(err error) bool { - return strings.Contains(err.Error(), "This node is not a swarm manager") +func inspectImages(ctx context.Context, dockerCli *client.DockerCli) inspect.GetRefFunc { + return func(ref string) (interface{}, []byte, error) { + return dockerCli.Client().ImageInspectWithRaw(ctx, ref) + } +} + +func inspectNetwork(ctx context.Context, dockerCli *client.DockerCli) inspect.GetRefFunc { + return func(ref string) (interface{}, []byte, error) { + return dockerCli.Client().NetworkInspectWithRaw(ctx, ref) + } +} + +func inspectNode(ctx context.Context, dockerCli *client.DockerCli) inspect.GetRefFunc { + return func(ref string) (interface{}, []byte, error) { + return dockerCli.Client().NodeInspectWithRaw(ctx, ref) + } +} + +func inspectService(ctx context.Context, dockerCli *client.DockerCli) inspect.GetRefFunc { + return func(ref string) (interface{}, []byte, error) { + return dockerCli.Client().ServiceInspectWithRaw(ctx, ref) + } +} + +func inspectTasks(ctx context.Context, dockerCli *client.DockerCli) inspect.GetRefFunc { + return func(ref string) (interface{}, []byte, error) { + return dockerCli.Client().TaskInspectWithRaw(ctx, ref) + } +} + +func inspectVolume(ctx context.Context, dockerCli *client.DockerCli) inspect.GetRefFunc { + return func(ref string) (interface{}, []byte, error) { + return dockerCli.Client().VolumeInspectWithRaw(ctx, ref) + } +} + +func inspectAll(ctx context.Context, dockerCli *client.DockerCli, getSize bool, typeConstraint string) inspect.GetRefFunc { + var inspectAutodetect = []struct { + ObjectType string + IsSizeSupported bool + ObjectInspector func(string) (interface{}, []byte, error) + }{ + {"container", true, inspectContainers(ctx, dockerCli, getSize)}, + {"image", true, inspectImages(ctx, dockerCli)}, + {"network", false, inspectNetwork(ctx, dockerCli)}, + {"volume", false, inspectVolume(ctx, dockerCli)}, + {"service", false, inspectService(ctx, dockerCli)}, + {"task", false, inspectTasks(ctx, dockerCli)}, + {"node", false, inspectNode(ctx, dockerCli)}, + } + + isErrNotSwarmManager := func(err error) bool { + return strings.Contains(err.Error(), "This node is not a swarm manager") + } + + return func(ref string) (interface{}, []byte, error) { + for _, inspectData := range inspectAutodetect { + if typeConstraint != "" && inspectData.ObjectType != typeConstraint { + continue + } + v, raw, err := inspectData.ObjectInspector(ref) + if err != nil { + if typeConstraint == "" && (apiclient.IsErrNotFound(err) || isErrNotSwarmManager(err)) { + continue + } + return v, raw, err + } + if !inspectData.IsSizeSupported { + fmt.Fprintf(dockerCli.Err(), "WARNING: --size ignored for %s\n", inspectData.ObjectType) + } + return v, raw, err + } + return nil, nil, fmt.Errorf("Error: No such object: %s", ref) + } } diff --git a/docs/reference/commandline/inspect.md b/docs/reference/commandline/inspect.md index 7ccc7f7392..6232ec12b6 100644 --- a/docs/reference/commandline/inspect.md +++ b/docs/reference/commandline/inspect.md @@ -11,15 +11,16 @@ parent = "smn_cli" # inspect ```markdown -Usage: docker inspect [OPTIONS] CONTAINER|IMAGE|TASK [CONTAINER|IMAGE|TASK...] +Usage: docker inspect [OPTIONS] NAME|ID [NAME|ID...] -Return low-level information on a container, image or task +Return low-level information on one or multiple containers, images, volumes, +networks, nodes, services, or tasks identified by name or ID. -f, --format Format the output using the given go template --help Print usage -s, --size Display total file sizes if the type is container values are "image" or "container" or "task - --type Return JSON for specified type, (e.g image, container or task) + --type Return JSON for specified type ``` By default, this will render all results in a JSON array. If the container and diff --git a/integration-cli/docker_cli_by_digest_test.go b/integration-cli/docker_cli_by_digest_test.go index a007d99021..edfed8e04d 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 container, image") + c.Assert(err.Error(), checker.Contains, "No such object") } func (s *DockerRegistrySuite) TestBuildByDigest(c *check.C) { diff --git a/integration-cli/docker_cli_rename_test.go b/integration-cli/docker_cli_rename_test.go index d32daa8f3e..9094e7d691 100644 --- a/integration-cli/docker_cli_rename_test.go +++ b/integration-cli/docker_cli_rename_test.go @@ -65,7 +65,7 @@ func (s *DockerSuite) TestRenameCheckNames(c *check.C) { result := dockerCmdWithResult("inspect", "-f={{.Name}}", "first_name") c.Assert(result, icmd.Matches, icmd.Expected{ ExitCode: 1, - Err: "No such container, image or task: first_name", + Err: "No such object: first_name", }) } diff --git a/man/docker-inspect.1.md b/man/docker-inspect.1.md index 6d7a54ad3b..3dbf600fde 100644 --- a/man/docker-inspect.1.md +++ b/man/docker-inspect.1.md @@ -2,23 +2,23 @@ % Docker Community % JUNE 2014 # NAME -docker-inspect - Return low-level information on a container or image +docker-inspect - Return low-level information on docker objects # SYNOPSIS **docker inspect** [**--help**] [**-f**|**--format**[=*FORMAT*]] [**-s**|**--size**] -[**--type**=*container*|*image*] -CONTAINER|IMAGE [CONTAINER|IMAGE...] +[**--type**=*container*|*image*|*network*|*node*|*service*|*task*|*volume*] +NAME|ID [NAME|ID...] # DESCRIPTION -This displays all the information available in Docker for a given -container or image. By default, this will render all results in a JSON -array. If the container and image have the same name, this will return -container JSON for unspecified type. If a format is specified, the given -template will be executed for each result. +This displays all the information available in Docker for one or multiple given +containers, images, volumes, networks, nodes, services, or tasks. By default, +this will render all results in a JSON array. If the container and image have +the same name, this will return container JSON for unspecified type. If a format +is specified, the given template will be executed for each result. # OPTIONS **--help** @@ -30,8 +30,9 @@ template will be executed for each result. **-s**, **--size** Display total file sizes if the type is container. -**--type**="*container*|*image*" - Return JSON for specified type, permissible values are "image" or "container" +**--type**=*container*|*image*|*network*|*node*|*service*|*task*|*volume* + Return JSON for specified type, permissible values are "image", "container", + "network", "node", "service", "task", and "volume". # EXAMPLES