From 6ce70a33c5825c0f4d2afae7643db95e8b7d46f1 Mon Sep 17 00:00:00 2001 From: baude Date: Tue, 20 Feb 2018 10:57:37 -0600 Subject: Inspect output should be in array form Inspect should be able to inspect one or more containers depending on the user input. Therefore, inspect output should be in array format so the consumer could potentially iterate it. This PR allows users to specify one more or containers|images|or a mix for inspection. The output, as stated, is therefore in array form. This holds true even for a singular image. In the case that the user enters an invalid container|image "name", we handle that gracefully. Podman will output json for the valid names until it reaches the invalid one. For example: In this case, podman will out the json for alpine and then print an error about 123 being invalid. It will not continute onto busybox. This behavior imatates docker. This addresses issue #360 Signed-off-by: baude Closes: #371 Approved by: baude --- cmd/podman/inspect.go | 148 ++++++++++++++++++++++++------------------ docs/podman-inspect.1.md | 6 +- test/e2e/commit_test.go | 8 +-- test/e2e/create_test.go | 2 +- test/e2e/import_test.go | 2 +- test/e2e/inspect_test.go | 23 ++++++- test/e2e/libpod_suite_test.go | 8 +-- test/e2e/tag_test.go | 12 ++-- 8 files changed, 125 insertions(+), 84 deletions(-) diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go index ce5a3b823..7820842e8 100644 --- a/cmd/podman/inspect.go +++ b/cmd/podman/inspect.go @@ -43,7 +43,7 @@ var ( Description: inspectDescription, Flags: inspectFlags, Action: inspectCmd, - ArgsUsage: "CONTAINER-OR-IMAGE", + ArgsUsage: "CONTAINER-OR-IMAGE [CONTAINER-OR-IMAGE]...", } ) @@ -51,12 +51,12 @@ func inspectCmd(c *cli.Context) error { args := c.Args() inspectType := c.String("type") latestContainer := c.Bool("latest") - var name string if len(args) == 0 && !latestContainer { return errors.Errorf("container or image name must be specified: podman inspect [options [...]] name") } - if len(args) > 1 { - return errors.Errorf("too many arguments specified") + + if len(args) > 0 && latestContainer { + return errors.Errorf("you cannot provide additional arguements with --latest") } if err := validateFlags(c, inspectFlags); err != nil { return err @@ -71,81 +71,103 @@ func inspectCmd(c *cli.Context) error { if !libpod.StringInSlice(inspectType, []string{inspectTypeContainer, inspectTypeImage, inspectAll}) { return errors.Errorf("the only recognized types are %q, %q, and %q", inspectTypeContainer, inspectTypeImage, inspectAll) } - if !latestContainer { - name = args[0] - } - if latestContainer { - inspectType = inspectTypeContainer - } + outputFormat := c.String("format") if strings.Contains(outputFormat, "{{.Id}}") { outputFormat = strings.Replace(outputFormat, "{{.Id}}", formats.IDString, -1) } - - var data interface{} - switch inspectType { - case inspectTypeContainer: - var ctr *libpod.Container - var err error - if latestContainer { - ctr, err = runtime.GetLatestContainer() - } else { - ctr, err = runtime.LookupContainer(name) - } - if err != nil { - return errors.Wrapf(err, "error looking up container %q", name) - } - libpodInspectData, err := ctr.Inspect(c.Bool("size")) - if err != nil { - return errors.Wrapf(err, "error getting libpod container inspect data %q", ctr.ID) - } - data, err = getCtrInspectInfo(ctr, libpodInspectData) - if err != nil { - return errors.Wrapf(err, "error parsing container data %q", ctr.ID()) - } - case inspectTypeImage: - image, err := runtime.GetImage(name) - if err != nil { - return errors.Wrapf(err, "error getting image %q", name) - } - data, err = runtime.GetImageInspectInfo(*image) + if latestContainer { + lc, err := runtime.GetLatestContainer() if err != nil { - return errors.Wrapf(err, "error parsing image data %q", image.ID) - } - case inspectAll: - ctr, err := runtime.LookupContainer(name) - if err != nil { - image, err := runtime.GetImage(name) - if err != nil { - return errors.Wrapf(err, "error getting image %q", name) - } - data, err = runtime.GetImageInspectInfo(*image) - if err != nil { - return errors.Wrapf(err, "error parsing image data %q", image.ID) - } - } else { - libpodInspectData, err := ctr.Inspect(c.Bool("size")) - if err != nil { - return errors.Wrapf(err, "error getting libpod container inspect data %q", ctr.ID) - } - data, err = getCtrInspectInfo(ctr, libpodInspectData) - if err != nil { - return errors.Wrapf(err, "error parsing container data %q", ctr.ID) - } + return err } + args = append(args, lc.ID()) + inspectType = inspectTypeContainer } + inspectedObjects, iterateErr := iterateInput(c, args, runtime, inspectType) + var out formats.Writer if outputFormat != "" && outputFormat != formats.JSONString { //template - out = formats.StdoutTemplate{Output: data, Template: outputFormat} + out = formats.StdoutTemplateArray{Output: inspectedObjects, Template: outputFormat} } else { // default is json output - out = formats.JSONStruct{Output: data} + out = formats.JSONStructArray{Output: inspectedObjects} } formats.Writer(out).Out() - return nil + return iterateErr +} + +// func iterateInput iterates the images|containers the user has requested and returns the inspect data and error +func iterateInput(c *cli.Context, args []string, runtime *libpod.Runtime, inspectType string) ([]interface{}, error) { + var ( + data interface{} + inspectedItems []interface{} + inspectError error + ) + + for _, input := range args { + switch inspectType { + case inspectTypeContainer: + ctr, err := runtime.LookupContainer(input) + if err != nil { + inspectError = errors.Wrapf(err, "error looking up container %q", input) + break + } + libpodInspectData, err := ctr.Inspect(c.Bool("size")) + if err != nil { + inspectError = errors.Wrapf(err, "error getting libpod container inspect data %q", ctr.ID) + break + } + data, err = getCtrInspectInfo(ctr, libpodInspectData) + if err != nil { + inspectError = errors.Wrapf(err, "error parsing container data %q", ctr.ID()) + break + } + case inspectTypeImage: + image, err := runtime.GetImage(input) + if err != nil { + inspectError = errors.Wrapf(err, "error getting image %q", input) + break + } + data, err = runtime.GetImageInspectInfo(*image) + if err != nil { + inspectError = errors.Wrapf(err, "error parsing image data %q", image.ID) + break + } + case inspectAll: + ctr, err := runtime.LookupContainer(input) + if err != nil { + image, err := runtime.GetImage(input) + if err != nil { + inspectError = errors.Wrapf(err, "error getting image %q", input) + break + } + data, err = runtime.GetImageInspectInfo(*image) + if err != nil { + inspectError = errors.Wrapf(err, "error parsing image data %q", image.ID) + break + } + } else { + libpodInspectData, err := ctr.Inspect(c.Bool("size")) + if err != nil { + inspectError = errors.Wrapf(err, "error getting libpod container inspect data %q", ctr.ID) + break + } + data, err = getCtrInspectInfo(ctr, libpodInspectData) + if err != nil { + inspectError = errors.Wrapf(err, "error parsing container data %q", ctr.ID) + break + } + } + } + if inspectError == nil { + inspectedItems = append(inspectedItems, data) + } + } + return inspectedItems, inspectError } func getCtrInspectInfo(ctr *libpod.Container, ctrInspectData *inspect.ContainerInspectData) (*inspect.ContainerData, error) { diff --git a/docs/podman-inspect.1.md b/docs/podman-inspect.1.md index 9f39deceb..d5996cdf7 100644 --- a/docs/podman-inspect.1.md +++ b/docs/podman-inspect.1.md @@ -6,10 +6,12 @@ podman inspect - Display a container or image's configuration ## SYNOPSIS -**podman** **inspect** [*options* [...]] name +**podman** **inspect** [*options* [...]] name [...] ## DESCRIPTION -This displays the low-level information on containers and images identified by name or ID. 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 the low-level information on containers and images identified by name or ID. 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 diff --git a/test/e2e/commit_test.go b/test/e2e/commit_test.go index c807b46c5..154a83bd2 100644 --- a/test/e2e/commit_test.go +++ b/test/e2e/commit_test.go @@ -40,7 +40,7 @@ var _ = Describe("Podman commit", func() { check := podmanTest.Podman([]string{"inspect", "foobar.com/test1-image:latest"}) check.WaitWithDefaultTimeout() data := check.InspectImageJSON() - Expect(StringInSlice("foobar.com/test1-image:latest", data.RepoTags)).To(BeTrue()) + Expect(StringInSlice("foobar.com/test1-image:latest", data[0].RepoTags)).To(BeTrue()) }) It("podman commit container with message", func() { @@ -55,7 +55,7 @@ var _ = Describe("Podman commit", func() { check := podmanTest.Podman([]string{"inspect", "foobar.com/test1-image:latest"}) check.WaitWithDefaultTimeout() data := check.InspectImageJSON() - Expect(data.Comment).To(Equal("testing-commit")) + Expect(data[0].Comment).To(Equal("testing-commit")) }) It("podman commit container with author", func() { @@ -70,7 +70,7 @@ var _ = Describe("Podman commit", func() { check := podmanTest.Podman([]string{"inspect", "foobar.com/test1-image:latest"}) check.WaitWithDefaultTimeout() data := check.InspectImageJSON() - Expect(data.Author).To(Equal("snoopy")) + Expect(data[0].Author).To(Equal("snoopy")) }) It("podman commit container with change flag", func() { @@ -88,7 +88,7 @@ var _ = Describe("Podman commit", func() { check.WaitWithDefaultTimeout() data := check.InspectImageJSON() foundBlue := false - for _, i := range data.Labels { + for _, i := range data[0].Labels { if i == "blue" { foundBlue = true break diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go index e54e35761..ebe4ed924 100644 --- a/test/e2e/create_test.go +++ b/test/e2e/create_test.go @@ -38,7 +38,7 @@ var _ = Describe("Podman create", func() { check := podmanTest.Podman([]string{"inspect", "-l"}) check.WaitWithDefaultTimeout() data := check.InspectContainerToJSON() - Expect(data.ID).To(ContainSubstring(cid)) + Expect(data[0].ID).To(ContainSubstring(cid)) }) It("podman create container based on a remote image", func() { diff --git a/test/e2e/import_test.go b/test/e2e/import_test.go index ed90ede0f..9ad650948 100644 --- a/test/e2e/import_test.go +++ b/test/e2e/import_test.go @@ -101,7 +101,7 @@ var _ = Describe("Podman import", func() { results.WaitWithDefaultTimeout() Expect(results.ExitCode()).To(Equal(0)) imageData := results.InspectImageJSON() - Expect(imageData.ContainerConfig.Cmd[0]).To(Equal("/bin/bash")) + Expect(imageData[0].ContainerConfig.Cmd[0]).To(Equal("/bin/bash")) }) }) diff --git a/test/e2e/inspect_test.go b/test/e2e/inspect_test.go index b6020f53b..e04465eb5 100644 --- a/test/e2e/inspect_test.go +++ b/test/e2e/inspect_test.go @@ -2,7 +2,6 @@ package integration import ( "os" - "strings" . "github.com/onsi/ginkgo" @@ -36,7 +35,7 @@ var _ = Describe("Podman inspect", func() { Expect(session.ExitCode()).To(Equal(0)) Expect(session.IsJSONOutputValid()).To(BeTrue()) imageData := session.InspectImageJSON() - Expect(imageData.RepoTags[0]).To(Equal("docker.io/library/alpine:latest")) + Expect(imageData[0].RepoTags[0]).To(Equal("docker.io/library/alpine:latest")) }) It("podman inspect bogus container", func() { @@ -70,6 +69,24 @@ var _ = Describe("Podman inspect", func() { result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) conData := result.InspectContainerToJSON() - Expect(conData.SizeRootFs).To(BeNumerically(">", 0)) + Expect(conData[0].SizeRootFs).To(BeNumerically(">", 0)) }) + + It("podman inspect container and image", func() { + ls, ec, _ := podmanTest.RunLsContainer("") + Expect(ec).To(Equal(0)) + cid := ls.OutputToString() + + result := podmanTest.Podman([]string{"inspect", "--format={{.ID}}", cid, ALPINE}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(len(result.OutputToStringArray())).To(Equal(2)) + }) + + It("podman inspect -l with additional input should fail", func() { + result := podmanTest.Podman([]string{"inspect", "-l", "1234foobar"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(125)) + }) + }) diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go index c479a6cef..bd117d5f4 100644 --- a/test/e2e/libpod_suite_test.go +++ b/test/e2e/libpod_suite_test.go @@ -228,8 +228,8 @@ func (s *PodmanSession) IsJSONOutputValid() bool { // InspectContainerToJSON takes the session output of an inspect // container and returns json -func (s *PodmanSession) InspectContainerToJSON() inspect.ContainerData { - var i inspect.ContainerData +func (s *PodmanSession) InspectContainerToJSON() []inspect.ContainerData { + var i []inspect.ContainerData err := json.Unmarshal(s.Out.Contents(), &i) Expect(err).To(BeNil()) return i @@ -237,8 +237,8 @@ func (s *PodmanSession) InspectContainerToJSON() inspect.ContainerData { // InspectImageJSON takes the session output of an inspect // image and returns json -func (s *PodmanSession) InspectImageJSON() inspect.ImageData { - var i inspect.ImageData +func (s *PodmanSession) InspectImageJSON() []inspect.ImageData { + var i []inspect.ImageData err := json.Unmarshal(s.Out.Contents(), &i) Expect(err).To(BeNil()) return i diff --git a/test/e2e/tag_test.go b/test/e2e/tag_test.go index c5ec5710d..7f14c7eb4 100644 --- a/test/e2e/tag_test.go +++ b/test/e2e/tag_test.go @@ -37,8 +37,8 @@ var _ = Describe("Podman tag", func() { results.WaitWithDefaultTimeout() Expect(results.ExitCode()).To(Equal(0)) inspectData := results.InspectImageJSON() - Expect(StringInSlice("docker.io/library/alpine:latest", inspectData.RepoTags)).To(BeTrue()) - Expect(StringInSlice("foobar:latest", inspectData.RepoTags)).To(BeTrue()) + Expect(StringInSlice("docker.io/library/alpine:latest", inspectData[0].RepoTags)).To(BeTrue()) + Expect(StringInSlice("foobar:latest", inspectData[0].RepoTags)).To(BeTrue()) }) It("podman tag shortname", func() { @@ -50,8 +50,8 @@ var _ = Describe("Podman tag", func() { results.WaitWithDefaultTimeout() Expect(results.ExitCode()).To(Equal(0)) inspectData := results.InspectImageJSON() - Expect(StringInSlice("docker.io/library/alpine:latest", inspectData.RepoTags)).To(BeTrue()) - Expect(StringInSlice("foobar:latest", inspectData.RepoTags)).To(BeTrue()) + Expect(StringInSlice("docker.io/library/alpine:latest", inspectData[0].RepoTags)).To(BeTrue()) + Expect(StringInSlice("foobar:latest", inspectData[0].RepoTags)).To(BeTrue()) }) It("podman tag shortname:tag", func() { @@ -63,7 +63,7 @@ var _ = Describe("Podman tag", func() { results.WaitWithDefaultTimeout() Expect(results.ExitCode()).To(Equal(0)) inspectData := results.InspectImageJSON() - Expect(StringInSlice("docker.io/library/alpine:latest", inspectData.RepoTags)).To(BeTrue()) - Expect(StringInSlice("foobar:new", inspectData.RepoTags)).To(BeTrue()) + Expect(StringInSlice("docker.io/library/alpine:latest", inspectData[0].RepoTags)).To(BeTrue()) + Expect(StringInSlice("foobar:new", inspectData[0].RepoTags)).To(BeTrue()) }) }) -- cgit v1.2.3-54-g00ecf