From 06fafe4cd04f29ee19078d6e78fdd792f2c1a37e Mon Sep 17 00:00:00 2001 From: baude Date: Wed, 8 Aug 2018 13:08:22 -0500 Subject: add podman pod inspect first pass of podman pod inspect Signed-off-by: baude Closes: #1236 Approved by: rhatdan --- cmd/podman/pod.go | 1 + cmd/podman/pod_inspect.go | 65 +++++++++++++++++++++++++++++++++++++++++++ commands.md | 1 + docs/podman-pod-inspect.1.md | 48 ++++++++++++++++++++++++++++++++ libpod/pod.go | 50 +++++++++++++++++++++++++++++++++ test/e2e/libpod_suite_test.go | 9 ++++++ test/e2e/pod_inspect_test.go | 61 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 235 insertions(+) create mode 100644 cmd/podman/pod_inspect.go create mode 100644 docs/podman-pod-inspect.1.md create mode 100644 test/e2e/pod_inspect_test.go diff --git a/cmd/podman/pod.go b/cmd/podman/pod.go index ed79f0711..a298ca32b 100644 --- a/cmd/podman/pod.go +++ b/cmd/podman/pod.go @@ -11,6 +11,7 @@ Pods are a group of one or more containers sharing the same network, pid and ipc ` podSubCommands = []cli.Command{ podCreateCommand, + podInspectCommand, podKillCommand, podPauseCommand, podPsCommand, diff --git a/cmd/podman/pod_inspect.go b/cmd/podman/pod_inspect.go new file mode 100644 index 000000000..6935335a6 --- /dev/null +++ b/cmd/podman/pod_inspect.go @@ -0,0 +1,65 @@ +package main + +import ( + "encoding/json" + + "fmt" + "github.com/pkg/errors" + "github.com/projectatomic/libpod/cmd/podman/libpodruntime" + "github.com/projectatomic/libpod/libpod" + "github.com/urfave/cli" +) + +var ( + podInspectFlags = []cli.Flag{ + LatestPodFlag, + } + podInspectDescription = "display the configuration for a pod by name or id" + podInspectCommand = cli.Command{ + Name: "inspect", + Usage: "displays a pod configuration", + Description: podInspectDescription, + Flags: podInspectFlags, + Action: podInspectCmd, + UseShortOptionHandling: true, + ArgsUsage: "[POD_NAME_OR_ID]", + } +) + +func podInspectCmd(c *cli.Context) error { + var ( + pod *libpod.Pod + ) + if err := checkMutuallyExclusiveFlags(c); err != nil { + return err + } + args := c.Args() + runtime, err := libpodruntime.GetRuntime(c) + if err != nil { + return errors.Wrapf(err, "could not get runtime") + } + defer runtime.Shutdown(false) + + if c.Bool("latest") { + pod, err = runtime.GetLatestPod() + if err != nil { + return errors.Wrapf(err, "unable to get latest pod") + } + } else { + pod, err = runtime.LookupPod(args[0]) + if err != nil { + return err + } + } + + podInspectData, err := pod.Inspect() + if err != nil { + return err + } + b, err := json.MarshalIndent(&podInspectData, "", " ") + if err != nil { + return err + } + fmt.Println(string(b)) + return nil +} diff --git a/commands.md b/commands.md index 3ac195476..d3e8d93cc 100644 --- a/commands.md +++ b/commands.md @@ -31,6 +31,7 @@ | [podman-pause(1)](/docs/podman-pause.1.md) | Pause one or more running containers |[![...](/docs/play.png)](https://asciinema.org/a/141292)| | [podman-pod(1)](/docs/podman-pod.1.md) | Simple management tool for groups of containers, called pods || | [podman-pod-create(1)](/docs/podman-pod-create.1.md) | Create a new pod || +| [podman-pod-inspect(1)](/docs/podman-pod-inspect.1.md) | Inspect a pod || | [podman-pod-kill(1)](podman-pod-kill.1.md) | Kill the main process of each container in pod. || | [podman-pod-ps(1)](/docs/podman-pod-ps.1.md) | List the pods on the system || | [podman-pod-pause(1)](podman-pod-pause.1.md) | Pause one or more pods. || diff --git a/docs/podman-pod-inspect.1.md b/docs/podman-pod-inspect.1.md new file mode 100644 index 000000000..edcd5dcef --- /dev/null +++ b/docs/podman-pod-inspect.1.md @@ -0,0 +1,48 @@ +% podman-pod-inspect "1" + +## NAME +podman\-pod\-inspect - Displays information describing a pod + +## SYNOPSIS +**podman pod inspect** [*options*] *pod* ... + +## DESCRIPTION +Displays configuration and state information about a given pod. It also displays information about containers +that belong to the pod. + +## OPTIONS +**--latest, -l** + +Instead of providing the pod name or ID, use the last created pod. If you use methods other than Podman +to run pods such as CRI-O, the last started pod could be from either of those methods. + + +## EXAMPLE +``` +# podman pod inspect foobar +{ + "Config": { + "id": "3513ca70583dd7ef2bac83331350f6b6c47d7b4e526c908e49d89ebf720e4693", + "name": "foobar", + "labels": {}, + "cgroupParent": "/libpod_parent", + "UsePodCgroup": true, + "created": "2018-08-08T11:15:18.823115347-05:00" + }, + "State": { + "CgroupPath": "" + }, + "Containers": [ + { + "id": "d53f8bf1e9730281264aac6e6586e327429f62c704abea4b6afb5d8a2b2c9f2c", + "state": "configured" + } + ] +} +``` + +## SEE ALSO +podman-pod(1), podman-pod-ps(1) + +## HISTORY +August 2018, Originally compiled by Brent Baude diff --git a/libpod/pod.go b/libpod/pod.go index 2ad4f7d0e..46182680a 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -10,6 +10,7 @@ import ( "github.com/docker/docker/pkg/stringid" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "github.com/ulule/deepcopier" ) // Pod represents a group of containers that are managed together. @@ -59,6 +60,20 @@ type podState struct { CgroupPath string } +// PodInspect represents the data we want to display for +// podman pod inspect +type PodInspect struct { + Config *PodConfig + State *podState + Containers []PodContainerInfo +} + +// PodContainerInfo keeps information on a container in a pod +type PodContainerInfo struct { + ID string `json:"id"` + State string `json:"state"` +} + // ID retrieves the pod's ID func (p *Pod) ID() string { return p.config.ID @@ -696,3 +711,38 @@ func (p *Pod) Status() (map[string]ContainerStatus, error) { // TODO add pod batching // Lock pod to avoid lock contention // Store and lock all containers (no RemoveContainer in batch guarantees cache will not become stale) + +// Inspect returns a PodInspect struct to describe the pod +func (p *Pod) Inspect() (*PodInspect, error) { + var ( + podContainers []PodContainerInfo + ) + + containers, err := p.AllContainers() + if err != nil { + return &PodInspect{}, err + } + for _, c := range containers { + containerStatus := "unknown" + // Ignoring possible errors here because we dont want this to be + // catastrophic in nature + containerState, err := c.State() + if err == nil { + containerStatus = containerState.String() + } + pc := PodContainerInfo{ + ID: c.ID(), + State: containerStatus, + } + podContainers = append(podContainers, pc) + } + + config := new(PodConfig) + deepcopier.Copy(p.config).To(config) + inspectData := PodInspect{ + Config: config, + State: p.state, + Containers: podContainers, + } + return &inspectData, nil +} diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go index e95b03cb9..3b7953672 100644 --- a/test/e2e/libpod_suite_test.go +++ b/test/e2e/libpod_suite_test.go @@ -19,6 +19,7 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/gexec" + "github.com/projectatomic/libpod/libpod" "github.com/projectatomic/libpod/pkg/inspect" ) @@ -339,6 +340,14 @@ func (s *PodmanSession) InspectContainerToJSON() []inspect.ContainerData { return i } +// InspectPodToJSON takes the sessions output from a pod inspect and returns json +func (s *PodmanSession) InspectPodToJSON() libpod.PodInspect { + var i libpod.PodInspect + err := json.Unmarshal(s.Out.Contents(), &i) + Expect(err).To(BeNil()) + return i +} + // InspectImageJSON takes the session output of an inspect // image and returns json func (s *PodmanSession) InspectImageJSON() []inspect.ImageData { diff --git a/test/e2e/pod_inspect_test.go b/test/e2e/pod_inspect_test.go new file mode 100644 index 000000000..b5c2bd1be --- /dev/null +++ b/test/e2e/pod_inspect_test.go @@ -0,0 +1,61 @@ +package integration + +import ( + "fmt" + "os" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Podman pod inspect", func() { + var ( + tempdir string + err error + podmanTest PodmanTest + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanCreate(tempdir) + podmanTest.RestoreAllArtifacts() + }) + + AfterEach(func() { + podmanTest.CleanupPod() + f := CurrentGinkgoTestDescription() + timedResult := fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds()) + GinkgoWriter.Write([]byte(timedResult)) + }) + + It("podman inspect bogus pod", func() { + session := podmanTest.Podman([]string{"pod", "inspect", "foobar"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Not(Equal(0))) + }) + + It("podman inspect a pod", func() { + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + podid := session.OutputToString() + + session = podmanTest.RunTopContainerInPod("", podid) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.RunTopContainerInPod("", podid) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"pod", "inspect", podid}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect.IsJSONOutputValid()).To(BeTrue()) + podData := inspect.InspectPodToJSON() + Expect(podData.Config.ID).To(Equal(podid)) + }) +}) -- cgit v1.2.3-54-g00ecf