diff --git a/api/client/inspect.go b/api/client/inspect.go index 1f72b9174b..fb22335dce 100644 --- a/api/client/inspect.go +++ b/api/client/inspect.go @@ -18,32 +18,39 @@ import ( func (cli *DockerCli) CmdInspect(args ...string) error { cmd := cli.Subcmd("inspect", []string{"CONTAINER|IMAGE [CONTAINER|IMAGE...]"}, "Return low-level information on a container or image", true) tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template") + inspectType := cmd.String([]string{"-type"}, "", "Return JSON for specified type, (e.g image or container)") + cmd.Require(flag.Min, 1) cmd.ParseFlags(args, true) var tmpl *template.Template + var err error + var obj []byte + if *tmplStr != "" { - var err error if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil { return StatusError{StatusCode: 64, Status: "Template parsing error: " + err.Error()} } } + if *inspectType != "" && *inspectType != "container" && *inspectType != "image" { + return fmt.Errorf("%q is not a valid value for --type", *inspectType) + } + indented := new(bytes.Buffer) indented.WriteString("[\n") status := 0 isImage := false for _, name := range cmd.Args() { - obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, nil)) - if err != nil { - obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, nil)) - isImage = true - if err != nil { + + if *inspectType == "" || *inspectType == "container" { + obj, _, err = readBody(cli.call("GET", "/containers/"+name+"/json", nil, nil)) + if err != nil && *inspectType == "container" { if strings.Contains(err.Error(), "No such") { - fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name) + fmt.Fprintf(cli.err, "Error: No such container: %s\n", name) } else { fmt.Fprintf(cli.err, "%s", err) } @@ -52,8 +59,27 @@ func (cli *DockerCli) CmdInspect(args ...string) error { } } + if obj == nil && (*inspectType == "" || *inspectType == "image") { + obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, nil)) + isImage = true + if err != nil { + if strings.Contains(err.Error(), "No such") { + if *inspectType == "" { + fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name) + } else { + fmt.Fprintf(cli.err, "Error: No such image: %s\n", name) + } + } else { + fmt.Fprintf(cli.err, "%s", err) + } + status = 1 + continue + } + + } + if tmpl == nil { - if err = json.Indent(indented, obj, "", " "); err != nil { + if err := json.Indent(indented, obj, "", " "); err != nil { fmt.Fprintf(cli.err, "%s\n", err) status = 1 continue diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index 72d5c3e341..27c84cf82d 100755 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -527,11 +527,16 @@ _docker_inspect() { --format|-f) return ;; + --type) + COMPREPLY=( $( compgen -W "image container" -- "$cur" ) ) + return + ;; + esac case "$cur" in -*) - COMPREPLY=( $( compgen -W "--format -f --help" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "--format -f --type --help" -- "$cur" ) ) ;; *) __docker_containers_and_images diff --git a/docs/reference/commandline/inspect.md b/docs/reference/commandline/inspect.md index 704bc70058..c6c4721c68 100644 --- a/docs/reference/commandline/inspect.md +++ b/docs/reference/commandline/inspect.md @@ -17,6 +17,9 @@ weight=1 -f, --format="" Format the output using the given go template + --type=container|image Return JSON for specified type, permissible + values are "image" or "container" + By default, this will render all results in a JSON array. If a format is specified, the given template will be executed for each result. diff --git a/integration-cli/docker_cli_inspect_test.go b/integration-cli/docker_cli_inspect_test.go index d40a57a47b..64a660d39e 100644 --- a/integration-cli/docker_cli_inspect_test.go +++ b/integration-cli/docker_cli_inspect_test.go @@ -38,6 +38,115 @@ func (s *DockerSuite) TestInspectInt64(c *check.C) { } } +func (s *DockerSuite) TestInspectDefault(c *check.C) { + + //Both the container and image are named busybox. docker inspect will fetch the container JSON. + //If the container JSON is not available, it will go for the image JSON. + + runCmd := exec.Command(dockerBinary, "run", "--name=busybox", "-d", "busybox", "true") + out, _, _, err := runCommandWithStdoutStderr(runCmd) + if err != nil { + c.Fatalf("failed to run container: %v, output: %q", err, out) + } + + inspectCmd := exec.Command(dockerBinary, "inspect", "busybox") + + _, exitCode, err := runCommandWithOutput(inspectCmd) + if exitCode != 0 || err != nil { + c.Fatalf("failed to inspect container: %s, %v", out, err) + } +} + +func (s *DockerSuite) TestInspectTypeFlagContainer(c *check.C) { + + //Both the container and image are named busybox. docker inspect will fetch container + //JSON State.Running field. If the field is true, it's a container. + + runCmd := exec.Command(dockerBinary, "run", "--name=busybox", "-d", "busybox", "top") + out, _, _, err := runCommandWithStdoutStderr(runCmd) + if err != nil { + c.Fatalf("failed to run container: %v, output: %q", err, out) + } + + formatStr := fmt.Sprintf("--format='{{.State.Running}}'") + inspectCmd := exec.Command(dockerBinary, "inspect", "--type=container", formatStr, "busybox") + + out, exitCode, err := runCommandWithOutput(inspectCmd) + if exitCode != 0 || err != nil { + c.Fatalf("failed to inspect container: %s, %v", out, err) + } + + if out != "true\n" { + c.Fatal("not a container JSON") + } +} + +func (s *DockerSuite) TestInspectTypeFlagWithNoContainer(c *check.C) { + + //Run this test on an image named busybox. docker inspect will try to fetch container + //JSON. Since there is no container named busybox and --type=container, docker inspect will + //not try to get the image JSON. It will throw an error. + + runCmd := exec.Command(dockerBinary, "run", "-d", "busybox", "true") + out, _, _, err := runCommandWithStdoutStderr(runCmd) + if err != nil { + c.Fatalf("failed to run container: %v, output: %q", err, out) + } + + inspectCmd := exec.Command(dockerBinary, "inspect", "--type=container", "busybox") + + _, exitCode, err := runCommandWithOutput(inspectCmd) + if exitCode == 0 || err == nil { + c.Fatalf("docker inspect should have failed, as there is no container named busybox") + } +} + +func (s *DockerSuite) TestInspectTypeFlagWithImage(c *check.C) { + + //Both the container and image are named busybox. docker inspect will fetch image + //JSON as --type=image. if there is no image with name busybox, docker inspect + //will throw an error. + + runCmd := exec.Command(dockerBinary, "run", "--name=busybox", "-d", "busybox", "true") + out, _, _, err := runCommandWithStdoutStderr(runCmd) + if err != nil { + c.Fatalf("failed to run container: %v, output: %q", err, out) + } + + inspectCmd := exec.Command(dockerBinary, "inspect", "--type=image", "busybox") + + out, exitCode, err := runCommandWithOutput(inspectCmd) + if exitCode != 0 || err != nil { + c.Fatalf("failed to inspect image: %s, %v", out, err) + } + + if strings.Contains(out, "State") { + c.Fatal("not an image JSON") + } + +} + +func (s *DockerSuite) TestInspectTypeFlagWithInvalidValue(c *check.C) { + + //Both the container and image are named busybox. docker inspect will fail + //as --type=foobar is not a valid value for the flag. + + runCmd := exec.Command(dockerBinary, "run", "--name=busybox", "-d", "busybox", "true") + out, _, _, err := runCommandWithStdoutStderr(runCmd) + if err != nil { + c.Fatalf("failed to run container: %v, output: %q", err, out) + } + + inspectCmd := exec.Command(dockerBinary, "inspect", "--type=foobar", "busybox") + + out, exitCode, err := runCommandWithOutput(inspectCmd) + if exitCode != 0 || err != nil { + if !strings.Contains(out, "not a valid value for --type") { + c.Fatalf("failed to inspect image: %s, %v", out, err) + } + } +} + func (s *DockerSuite) TestInspectImageFilterInt(c *check.C) { imageTest := "emptyfs" out, err := inspectField(imageTest, "Size") diff --git a/man/docker-inspect.1.md b/man/docker-inspect.1.md index 1b87ef088e..1860480ea8 100644 --- a/man/docker-inspect.1.md +++ b/man/docker-inspect.1.md @@ -8,6 +8,7 @@ docker-inspect - Return low-level information on a container or image **docker inspect** [**--help**] [**-f**|**--format**[=*FORMAT*]] +[**--type**=*container*|*image*] CONTAINER|IMAGE [CONTAINER|IMAGE...] # DESCRIPTION @@ -24,14 +25,29 @@ each result. **-f**, **--format**="" Format the output using the given go template. +**--type**=*container*|*image* + Return JSON for specified type, permissible values are "image" or "container" + # EXAMPLES +Getting information on an image where image name conflict with the container name, +e,g both image and container are named rhel7. + + $ docker inspect --type=image rhel7 + [ + { + "Id": "fe01a428b9d9de35d29531e9994157978e8c48fa693e1bf1d221dffbbb67b170", + "Parent": "10acc31def5d6f249b548e01e8ffbaccfd61af0240c17315a7ad393d022c5ca2", + .... + } + ] + ## Getting information on a container To get information on a container use its ID or instance name: $ docker inspect d2cc496561d6 -[{ + [{ "Id": "d2cc496561d6d520cbc0236b4ba88c362c446a7619992123f11c809cded25b47", "Created": "2015-06-08T16:18:02.505155285Z", "Path": "bash", @@ -161,8 +177,8 @@ To get information on a container use its ID or instance name: "CpuShares": 0, "Cpuset": "" } -} -] + } + ] ## Getting the IP address of a container instance To get the IP address of a container use: @@ -188,7 +204,7 @@ Use an image's ID or name (e.g., repository/name[:tag]) to get information on it. $ docker inspect ded7cd95e059 -[{ + [{ "Id": "ded7cd95e059788f2586a51c275a4f151653779d6a7f4dad77c2bd34601d94e4", "Parent": "48ecf305d2cf7046c1f5f8fcbcd4994403173441d4a7f125b1bb0ceead9de731", "Comment": "", @@ -258,8 +274,8 @@ on it. "DeviceSize": "171798691840" } } -} -] + } + ] # HISTORY April 2014, originally compiled by William Henry (whenry at redhat dot com)