From 1aad3fd96b61705243e8f6ae35f65946916aa8a5 Mon Sep 17 00:00:00 2001 From: haircommander Date: Mon, 9 Jul 2018 13:04:29 -0400 Subject: Podman pod create/rm commands with man page and tests. Includes a very stripped down version of podman pod ps, just for testing Signed-off-by: haircommander --- test/e2e/libpod_suite_test.go | 46 +++++++++++++ test/e2e/pod_create_test.go | 84 ++++++++++++++++++++++++ test/e2e/pod_rm_test.go | 148 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 278 insertions(+) create mode 100644 test/e2e/pod_create_test.go create mode 100644 test/e2e/pod_rm_test.go (limited to 'test') diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go index 448dbfc04..6ec995bfc 100644 --- a/test/e2e/libpod_suite_test.go +++ b/test/e2e/libpod_suite_test.go @@ -224,6 +224,17 @@ func (p *PodmanTest) Cleanup() { } } +// CleanupPod cleans up the temporary store +func (p *PodmanTest) CleanupPod() { + // Remove all containers + session := p.Podman([]string{"pod", "rm", "-fa"}) + session.Wait(90) + // Nuke tempdir + if err := os.RemoveAll(p.TempDir); err != nil { + fmt.Printf("%q\n", err) + } +} + // GrepString takes session output and behaves like grep. it returns a bool // if successful and an array of strings on positive matches func (s *PodmanSession) GrepString(term string) (bool, []string) { @@ -459,6 +470,15 @@ func (p *PodmanTest) RunTopContainer(name string) *PodmanSession { return p.Podman(podmanArgs) } +func (p *PodmanTest) RunTopContainerInPod(name, pod string) *PodmanSession { + var podmanArgs = []string{"run", "--pod", pod} + if name != "" { + podmanArgs = append(podmanArgs, "--name", name) + } + podmanArgs = append(podmanArgs, "-d", ALPINE, "top") + return p.Podman(podmanArgs) +} + //RunLsContainer runs a simple container in the background that // simply runs ls. If the name passed != "", it will have a name func (p *PodmanTest) RunLsContainer(name string) (*PodmanSession, int, string) { @@ -472,6 +492,17 @@ func (p *PodmanTest) RunLsContainer(name string) (*PodmanSession, int, string) { return session, session.ExitCode(), session.OutputToString() } +func (p *PodmanTest) RunLsContainerInPod(name, pod string) (*PodmanSession, int, string) { + var podmanArgs = []string{"run", "--pod", pod} + if name != "" { + podmanArgs = append(podmanArgs, "--name", name) + } + podmanArgs = append(podmanArgs, "-d", ALPINE, "ls") + session := p.Podman(podmanArgs) + session.WaitWithDefaultTimeout() + return session, session.ExitCode(), session.OutputToString() +} + //NumberOfContainersRunning returns an int of how many // containers are currently running. func (p *PodmanTest) NumberOfContainersRunning() int { @@ -502,6 +533,21 @@ func (p *PodmanTest) NumberOfContainers() int { return len(containers) } +// NumberOfPods returns an int of how many +// pods are currently defined. +func (p *PodmanTest) NumberOfPods() int { + var pods []string + ps := p.Podman([]string{"pod", "ps", "-q"}) + ps.WaitWithDefaultTimeout() + Expect(ps.ExitCode()).To(Equal(0)) + for _, i := range ps.OutputToStringArray() { + if i != "" { + pods = append(pods, i) + } + } + return len(pods) +} + // NumberOfRunningContainers returns an int of how many containers are currently // running func (p *PodmanTest) NumberOfRunningContainers() int { diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go new file mode 100644 index 000000000..fa420675f --- /dev/null +++ b/test/e2e/pod_create_test.go @@ -0,0 +1,84 @@ +package integration + +import ( + "os" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Podman pod create", 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() + }) + + It("podman create pod", func() { + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + cid := session.OutputToString() + Expect(session.ExitCode()).To(Equal(0)) + + check := podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc"}) + check.WaitWithDefaultTimeout() + match, _ := check.GrepString(cid) + Expect(match).To(BeTrue()) + Expect(len(check.OutputToStringArray())).To(Equal(1)) + }) + + It("podman create pod with name", func() { + name := "test" + session := podmanTest.Podman([]string{"pod", "create", "--name", name}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + check := podmanTest.Podman([]string{"pod", "ps", "--no-trunc"}) + check.WaitWithDefaultTimeout() + match, _ := check.GrepString(name) + Expect(match).To(BeTrue()) + }) + + It("podman create pod with doubled name", func() { + name := "test" + session := podmanTest.Podman([]string{"pod", "create", "--name", name}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"pod", "create", "--name", name}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Not(Equal(1))) + + check := podmanTest.Podman([]string{"pod", "ps", "-q"}) + check.WaitWithDefaultTimeout() + Expect(len(check.OutputToStringArray())).To(Equal(1)) + }) + + It("podman create pod with same name as ctr", func() { + name := "test" + session := podmanTest.Podman([]string{"create", "--name", name, ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"pod", "create", "--name", name}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Not(Equal(1))) + + check := podmanTest.Podman([]string{"pod", "ps", "-q"}) + check.WaitWithDefaultTimeout() + Expect(len(check.OutputToStringArray())).To(Equal(1)) + }) +}) diff --git a/test/e2e/pod_rm_test.go b/test/e2e/pod_rm_test.go new file mode 100644 index 000000000..ca6b4fcbe --- /dev/null +++ b/test/e2e/pod_rm_test.go @@ -0,0 +1,148 @@ +package integration + +import ( + "os" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Podman pod rm", 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() + + }) + + It("podman pod rm empty pod", func() { + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + cid := session.OutputToString() + + result := podmanTest.Podman([]string{"pod", "rm", cid}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + }) + + It("podman pod rm doesn't remove a pod with a container", func() { + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + + cid := session.OutputToString() + + session = podmanTest.Podman([]string{"create", "--pod", cid, ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + result := podmanTest.Podman([]string{"pod", "rm", cid}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(125)) + + result = podmanTest.Podman([]string{"ps", "-qa"}) + result.WaitWithDefaultTimeout() + Expect(len(result.OutputToStringArray())).To(Equal(1)) + }) + + It("podman pod rm -f does remove a running container", func() { + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + + cid := session.OutputToString() + + session = podmanTest.Podman([]string{"run", "-d", "--pod", cid, ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + result := podmanTest.Podman([]string{"pod", "rm", "-f", cid}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + + result = podmanTest.Podman([]string{"ps", "-q"}) + result.WaitWithDefaultTimeout() + Expect(result.OutputToString()).To(BeEmpty()) + }) + + It("podman pod rm -a doesn't remove a running container", func() { + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + + cid1 := session.OutputToString() + + session = podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + + session = podmanTest.Podman([]string{"run", "-d", "--pod", cid1, ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + result := podmanTest.Podman([]string{"pod", "rm", "-a"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Not(Equal(0))) + + result = podmanTest.Podman([]string{"ps", "-q"}) + result.WaitWithDefaultTimeout() + Expect(len(result.OutputToStringArray())).To(Equal(1)) + + // one pod should have been deleted + result = podmanTest.Podman([]string{"pod", "ps", "-q"}) + result.WaitWithDefaultTimeout() + Expect(len(result.OutputToStringArray())).To(Equal(1)) + }) + + It("podman pod rm -fa removes everything", func() { + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + + cid1 := session.OutputToString() + + session = podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + + cid2 := session.OutputToString() + + session = podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + + session = podmanTest.Podman([]string{"run", "-d", "--pod", cid1, ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"create", "-d", "--pod", cid1, ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"run", "-d", "--pod", cid2, ALPINE, "ls"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"run", "-d", "--pod", cid2, nginx}) + session.WaitWithDefaultTimeout() + + result := podmanTest.Podman([]string{"pod", "rm", "-fa"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + + result = podmanTest.Podman([]string{"ps", "-q"}) + result.WaitWithDefaultTimeout() + Expect(result.OutputToString()).To(BeEmpty()) + + // one pod should have been deleted + result = podmanTest.Podman([]string{"pod", "ps", "-q"}) + result.WaitWithDefaultTimeout() + Expect(result.OutputToString()).To(BeEmpty()) + }) +}) -- cgit v1.2.3-54-g00ecf From a04a8d1dd4d375ebe5084bac760dc82f88cfc77f Mon Sep 17 00:00:00 2001 From: haircommander Date: Mon, 9 Jul 2018 17:48:20 -0400 Subject: Added full podman pod ps, with tests and man page Signed-off-by: haircommander --- cmd/podman/pod.go | 3 +- cmd/podman/pod_create.go | 18 +- cmd/podman/pod_ps.go | 431 +++++++++++++++++++++++++++++++++++++++++--- cmd/podman/pod_rm.go | 26 ++- completions/bash/podman | 87 ++------- docs/podman-pod-create.1.md | 17 +- docs/podman-pod-ps.1.md | 165 +++++++++++++++++ docs/podman-pod-rm.1.md | 19 +- docs/podman-pod.1.md | 13 +- libpod/runtime_pod.go | 45 ----- pkg/spec/createconfig.go | 4 +- test/e2e/pod_ps_test.go | 227 +++++++++++++++++++++++ test/e2e/pod_rm_test.go | 52 ++++-- 13 files changed, 886 insertions(+), 221 deletions(-) create mode 100644 docs/podman-pod-ps.1.md create mode 100644 test/e2e/pod_ps_test.go (limited to 'test') diff --git a/cmd/podman/pod.go b/cmd/podman/pod.go index f32ae4626..6cf2920a5 100644 --- a/cmd/podman/pod.go +++ b/cmd/podman/pod.go @@ -8,7 +8,8 @@ var ( podDescription = ` podman pod - manage pods + Manage container pods. + Pods are a group of one or more containers sharing the same network, pid and ipc namespaces. ` podCommand = cli.Command{ Name: "pod", diff --git a/cmd/podman/pod_create.go b/cmd/podman/pod_create.go index 9bbc60d4d..f86faa409 100644 --- a/cmd/podman/pod_create.go +++ b/cmd/podman/pod_create.go @@ -17,13 +17,9 @@ var podCreateDescription = "Creates a new empty pod. The pod ID is then" + " initial state 'created'." var podCreateFlags = []cli.Flag{ - cli.BoolTFlag{ - Name: "cgroup-to-ctr", - Usage: "Tells containers in this pod to use the cgroup created for the pod", - }, cli.StringFlag{ Name: "cgroup-parent", - Usage: "Optional parent cgroup for the pod", + Usage: "Set parent cgroup for the pod", }, cli.StringSliceFlag{ Name: "label-file", @@ -45,7 +41,7 @@ var podCreateFlags = []cli.Flag{ var podCreateCommand = cli.Command{ Name: "create", - Usage: "create but do not start a pod", + Usage: "create a new empty pod", Description: podCreateDescription, Flags: podCreateFlags, Action: podCreateCmd, @@ -75,18 +71,11 @@ func podCreateCmd(c *cli.Context) error { return errors.Wrapf(err, "unable to write pod id file %s", c.String("pod-id-file")) } } - // BEGIN GetPodCreateOptions - // TODO make sure this is correct usage if c.IsSet("cgroup-parent") { options = append(options, libpod.WithPodCgroupParent(c.String("cgroup-parent"))) } - if c.Bool("cgroup-to-ctr") { - options = append(options, libpod.WithPodCgroups()) - } - // LABEL VARIABLES - // TODO make sure this works as expected labels, err := getAllLabels(c.StringSlice("label-file"), c.StringSlice("label")) if err != nil { return errors.Wrapf(err, "unable to process labels") @@ -99,6 +88,9 @@ func podCreateCmd(c *cli.Context) error { options = append(options, libpod.WithPodName(c.String("name"))) } + // always have containers use pod cgroups + options = append(options, libpod.WithPodCgroups()) + pod, err := runtime.NewPod(options...) if err != nil { return err diff --git a/cmd/podman/pod_ps.go b/cmd/podman/pod_ps.go index f4e2d0ae5..470810901 100644 --- a/cmd/podman/pod_ps.go +++ b/cmd/podman/pod_ps.go @@ -2,31 +2,62 @@ package main import ( "reflect" + "sort" + "strconv" "strings" + "time" + "github.com/docker/go-units" "github.com/pkg/errors" "github.com/projectatomic/libpod/cmd/podman/batchcontainer" "github.com/projectatomic/libpod/cmd/podman/formats" "github.com/projectatomic/libpod/cmd/podman/libpodruntime" "github.com/projectatomic/libpod/libpod" + "github.com/projectatomic/libpod/pkg/util" "github.com/urfave/cli" ) +const ( + STOPPED = "Stopped" + RUNNING = "Running" + PAUSED = "Paused" + EXITED = "Exited" + ERROR = "Error" + CREATED = "Created" + NUM_CTR_INFO = 10 +) + var ( - opts batchcontainer.PsOptions + bc_opts batchcontainer.PsOptions ) +type podPsCtrInfo struct { + Name string `"json:name,omitempty"` + Id string `"json:id,omitempty"` + Status string `"json:status,omitempty"` +} + type podPsOptions struct { NoTrunc bool Format string + Sort string Quiet bool NumberOfContainers bool + Cgroup bool + NamesOfContainers bool + IdsOfContainers bool + StatusOfContainers bool } type podPsTemplateParams struct { + Created string ID string Name string NumberOfContainers int + Status string + Cgroup string + UsePodCgroup bool + ContainerInfo string } // podPsJSONParams is used as a base structure for the psParams @@ -35,23 +66,93 @@ type podPsTemplateParams struct { // podPsJSONParams will be populated by data from libpod.Container, // the members of the struct are the sama data types as their sources. type podPsJSONParams struct { - ID string `json:"id"` - Name string `json:"name"` - NumberOfContainers int `json:"numberofcontainers"` + CreatedAt time.Time `json:"createdAt"` + ID string `json:"id"` + Name string `json:"name"` + NumberOfContainers int `json:"numberofcontainers"` + Status string `json:"status"` + CtrsInfo []podPsCtrInfo `json:"containerinfo,omitempty"` + Cgroup string `json:"cgroup,omitempty"` + UsePodCgroup bool `json:"podcgroup,omitempty"` +} + +// Type declaration and functions for sorting the pod PS output +type podPsSorted []podPsJSONParams + +func (a podPsSorted) Len() int { return len(a) } +func (a podPsSorted) Swap(i, j int) { a[i], a[j] = a[j], a[i] } + +type podPsSortedCreated struct{ podPsSorted } + +func (a podPsSortedCreated) Less(i, j int) bool { + return a.podPsSorted[i].CreatedAt.After(a.podPsSorted[j].CreatedAt) +} + +type podPsSortedId struct{ podPsSorted } + +func (a podPsSortedId) Less(i, j int) bool { return a.podPsSorted[i].ID < a.podPsSorted[j].ID } + +type podPsSortedNumber struct{ podPsSorted } + +func (a podPsSortedNumber) Less(i, j int) bool { + return len(a.podPsSorted[i].CtrsInfo) < len(a.podPsSorted[j].CtrsInfo) +} + +type podPsSortedName struct{ podPsSorted } + +func (a podPsSortedName) Less(i, j int) bool { return a.podPsSorted[i].Name < a.podPsSorted[j].Name } + +type podPsSortedStatus struct{ podPsSorted } + +func (a podPsSortedStatus) Less(i, j int) bool { + return a.podPsSorted[i].Status < a.podPsSorted[j].Status } var ( podPsFlags = []cli.Flag{ + cli.BoolFlag{ + Name: "cgroup", + Usage: "Print the Cgroup information of the pod", + }, + cli.BoolFlag{ + Name: "ctr-names", + Usage: "Display the container names", + }, + cli.BoolFlag{ + Name: "ctr-ids", + Usage: "Display the container UUIDs. If no-trunc is not set they will be truncated", + }, + cli.BoolFlag{ + Name: "ctr-status", + Usage: "Display the container status", + }, + cli.StringFlag{ + Name: "filter, f", + Usage: "Filter output based on conditions given", + }, + cli.StringFlag{ + Name: "format", + Usage: "Pretty-print pods to JSON or using a Go template", + }, + cli.BoolFlag{ + Name: "latest, l", + Usage: "Show the latest pod created", + }, cli.BoolFlag{ Name: "no-trunc", - Usage: "Display the extended information", + Usage: "Do not truncate pod and container IDs", }, cli.BoolFlag{ Name: "quiet, q", Usage: "Print the numeric IDs of the pods only", }, + cli.StringFlag{ + Name: "sort", + Usage: "Sort output by created, id, name, or number", + Value: "created", + }, } - podPsDescription = "Prints out information about pods" + podPsDescription = "List all pods on system including their names, ids and current state." podPsCommand = cli.Command{ Name: "ps", Aliases: []string{"ls", "list"}, @@ -68,7 +169,7 @@ func podPsCmd(c *cli.Context) error { return err } - if err := podCheckFlagsPassed(c); err != nil { + if err := podPsCheckFlagsPassed(c); err != nil { return errors.Wrapf(err, "error with flags passed") } @@ -76,50 +177,194 @@ func podPsCmd(c *cli.Context) error { if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } - defer runtime.Shutdown(false) if len(c.Args()) > 0 { return errors.Errorf("too many arguments, ps takes no arguments") } - format := genPodPsFormat(c.Bool("quiet")) - opts := podPsOptions{ - Format: format, - NoTrunc: c.Bool("no-trunc"), - Quiet: c.Bool("quiet"), + NoTrunc: c.Bool("no-trunc"), + Quiet: c.Bool("quiet"), + Sort: c.String("sort"), + IdsOfContainers: c.Bool("ctr-ids"), + NamesOfContainers: c.Bool("ctr-names"), + StatusOfContainers: c.Bool("ctr-status"), } + opts.Format = genPodPsFormat(c) + var filterFuncs []libpod.PodFilter + if c.String("filter") != "" { + filters := strings.Split(c.String("filter"), ",") + for _, f := range filters { + filterSplit := strings.Split(f, "=") + if len(filterSplit) < 2 { + return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) + } + generatedFunc, err := generatePodFilterFuncs(filterSplit[0], filterSplit[1], runtime) + if err != nil { + return errors.Wrapf(err, "invalid filter") + } + filterFuncs = append(filterFuncs, generatedFunc) + } + } - pods, err := runtime.Pods(filterFuncs...) - if err != nil { - return err + var pods []*libpod.Pod + if c.IsSet("latest") { + pod, err := runtime.GetLatestPod() + if err != nil { + return err + } + pods = append(pods, pod) + } else { + pods, err = runtime.GetAllPods() + if err != nil { + return err + } + } + + podsFiltered := make([]*libpod.Pod, 0, len(pods)) + for _, pod := range pods { + include := true + for _, filter := range filterFuncs { + include = include && filter(pod) + } + + if include { + podsFiltered = append(podsFiltered, pod) + } } - return generatePodPsOutput(pods, opts, runtime) + return generatePodPsOutput(podsFiltered, opts, runtime) } -// podCheckFlagsPassed checks if mutually exclusive flags are passed together -func podCheckFlagsPassed(c *cli.Context) error { +// podPsCheckFlagsPassed checks if mutually exclusive flags are passed together +func podPsCheckFlagsPassed(c *cli.Context) error { // quiet, and format with Go template are mutually exclusive flags := 0 if c.Bool("quiet") { flags++ } + if c.IsSet("format") && c.String("format") != formats.JSONString { + flags++ + } if flags > 1 { - return errors.Errorf("quiet, and format with Go template are mutually exclusive") + return errors.Errorf("quiet and format with Go template are mutually exclusive") } return nil } +func generatePodFilterFuncs(filter, filterValue string, runtime *libpod.Runtime) (func(pod *libpod.Pod) bool, error) { + switch filter { + case "ctr-ids": + return func(p *libpod.Pod) bool { + ctrIds, err := p.AllContainersByID() + if err != nil { + return false + } + return util.StringInSlice(filterValue, ctrIds) + }, nil + case "ctr-names": + return func(p *libpod.Pod) bool { + ctrs, err := p.AllContainers() + if err != nil { + return false + } + for _, ctr := range ctrs { + if filterValue == ctr.Name() { + return true + } + } + return false + }, nil + case "ctr-number": + return func(p *libpod.Pod) bool { + ctrIds, err := p.AllContainersByID() + if err != nil { + return false + } + + fVint, err2 := strconv.Atoi(filterValue) + if err2 != nil { + return false + } + return len(ctrIds) == fVint + }, nil + case "ctr-status": + if !util.StringInSlice(filterValue, []string{"created", "restarting", "running", "paused", "exited", "unknown"}) { + return nil, errors.Errorf("%s is not a valid status", filterValue) + } + return func(p *libpod.Pod) bool { + ctrs, err := p.AllContainers() + if err != nil { + return false + } + for _, ctr := range ctrs { + status, err := ctr.State() + if err != nil { + return false + } + state := status.String() + if status == libpod.ContainerStateConfigured { + state = "created" + } + if state == filterValue { + return true + } + } + return false + }, nil + case "id": + return func(p *libpod.Pod) bool { + return strings.Contains(p.ID(), filterValue) + }, nil + case "name": + return func(p *libpod.Pod) bool { + return strings.Contains(p.Name(), filterValue) + }, nil + case "status": + if !util.StringInSlice(filterValue, []string{"stopped", "running", "paused", "exited", "dead", "created"}) { + return nil, errors.Errorf("%s is not a valid pod status", filterValue) + } + return func(p *libpod.Pod) bool { + ctrs, err := p.AllContainers() + if err != nil { + return false + } + status, err := getPodStatus(ctrs) + if err != nil { + return false + } + if strings.ToLower(status) == filterValue { + return true + } + return false + }, nil + } + return nil, errors.Errorf("%s is an invalid filter", filter) +} + // generate the template based on conditions given -func genPodPsFormat(quiet bool) string { - if quiet { - return formats.IDString +func genPodPsFormat(c *cli.Context) string { + format := "" + if c.String("format") != "" { + // "\t" from the command line is not being recognized as a tab + // replacing the string "\t" to a tab character if the user passes in "\t" + format = strings.Replace(c.String("format"), `\t`, "\t", -1) + } else if c.Bool("quiet") { + format = formats.IDString + } else { + format = "table {{.ID}}\t{{.Name}}\t{{.Status}}\t{{.Created}}" + if c.Bool("cgroup") { + format += "\t{{.Cgroup}}\t{{.UsePodCgroup}}" + } + if c.Bool("ctr-names") || c.Bool("ctr-ids") || c.Bool("ctr-status") { + format += "\t{{.ContainerInfo}}" + } else { + format += "\t{{.NumberOfContainers}}" + } } - format := "table {{.ID}}\t{{.Name}}" return format } @@ -152,6 +397,24 @@ func (p *podPsTemplateParams) podHeaderMap() map[string]string { return values } +func sortPodPsOutput(sortBy string, psOutput podPsSorted) (podPsSorted, error) { + switch sortBy { + case "created": + sort.Sort(podPsSortedCreated{psOutput}) + case "id": + sort.Sort(podPsSortedId{psOutput}) + case "name": + sort.Sort(podPsSortedName{psOutput}) + case "number": + sort.Sort(podPsSortedNumber{psOutput}) + case "status": + sort.Sort(podPsSortedStatus{psOutput}) + default: + return nil, errors.Errorf("invalid option for --sort, options are: id, names, or number") + } + return psOutput, nil +} + // getPodTemplateOutput returns the modified container information func getPodTemplateOutput(psParams []podPsJSONParams, opts podPsOptions) ([]podPsTemplateParams, error) { var ( @@ -160,13 +423,43 @@ func getPodTemplateOutput(psParams []podPsJSONParams, opts podPsOptions) ([]podP for _, psParam := range psParams { podID := psParam.ID + var ctrStr string + truncated := "" if !opts.NoTrunc { - podID = shortID(psParam.ID) + podID = shortID(podID) + if len(psParam.CtrsInfo) > NUM_CTR_INFO { + psParam.CtrsInfo = psParam.CtrsInfo[:NUM_CTR_INFO] + truncated = "..." + } } + for _, ctrInfo := range psParam.CtrsInfo { + ctrStr += "[ " + if opts.IdsOfContainers { + if opts.NoTrunc { + ctrStr += ctrInfo.Id + } else { + ctrStr += shortID(ctrInfo.Id) + } + } + if opts.NamesOfContainers { + ctrStr += ctrInfo.Name + " " + } + if opts.StatusOfContainers { + ctrStr += ctrInfo.Status + " " + } + ctrStr += "] " + } + ctrStr += truncated params := podPsTemplateParams{ - ID: podID, - Name: psParam.Name, + Created: units.HumanDuration(time.Since(psParam.CreatedAt)) + " ago", + ID: podID, + Name: psParam.Name, + Status: psParam.Status, + NumberOfContainers: psParam.NumberOfContainers, + UsePodCgroup: psParam.UsePodCgroup, + Cgroup: psParam.Cgroup, + ContainerInfo: ctrStr, } psOutput = append(psOutput, params) @@ -175,6 +468,52 @@ func getPodTemplateOutput(psParams []podPsJSONParams, opts podPsOptions) ([]podP return psOutput, nil } +func getPodStatus(ctrs []*libpod.Container) (string, error) { + ctrNum := len(ctrs) + if ctrNum == 0 { + return CREATED, nil + } + statuses := map[string]int{ + STOPPED: 0, + RUNNING: 0, + PAUSED: 0, + CREATED: 0, + ERROR: 0, + } + for _, ctr := range ctrs { + state, err := ctr.State() + if err != nil { + return "", err + } + switch state { + case libpod.ContainerStateStopped: + statuses[STOPPED]++ + case libpod.ContainerStateRunning: + statuses[RUNNING]++ + case libpod.ContainerStatePaused: + statuses[PAUSED]++ + case libpod.ContainerStateCreated, libpod.ContainerStateConfigured: + statuses[CREATED]++ + default: + statuses[ERROR]++ + } + } + + if statuses[RUNNING] > 0 { + return RUNNING, nil + } else if statuses[PAUSED] == ctrNum { + return PAUSED, nil + } else if statuses[STOPPED] == ctrNum { + return EXITED, nil + } else if statuses[STOPPED] > 0 { + return STOPPED, nil + } else if statuses[ERROR] > 0 { + return ERROR, nil + } else { + return CREATED, nil + } +} + // getAndSortPodJSONOutput returns the container info in its raw, sorted form func getAndSortPodJSONParams(pods []*libpod.Pod, opts podPsOptions, runtime *libpod.Runtime) ([]podPsJSONParams, error) { var ( @@ -182,21 +521,55 @@ func getAndSortPodJSONParams(pods []*libpod.Pod, opts podPsOptions, runtime *lib ) for _, pod := range pods { - ctrs, err := runtime.ContainersInPod(pod) + ctrs, err := pod.AllContainers() + ctrsInfo := make([]podPsCtrInfo, 0) if err != nil { return nil, err } ctrNum := len(ctrs) + status, err := getPodStatus(ctrs) + if err != nil { + return nil, err + } + for _, ctr := range ctrs { + batchInfo, err := batchcontainer.BatchContainerOp(ctr, bc_opts) + if err != nil { + return nil, err + } + var status string + switch batchInfo.ConState { + case libpod.ContainerStateStopped: + status = EXITED + case libpod.ContainerStateRunning: + status = RUNNING + case libpod.ContainerStatePaused: + status = PAUSED + case libpod.ContainerStateCreated, libpod.ContainerStateConfigured: + status = CREATED + default: + status = ERROR + } + ctrsInfo = append(ctrsInfo, podPsCtrInfo{ + Name: batchInfo.ConConfig.Name, + Id: ctr.ID(), + Status: status, + }) + } params := podPsJSONParams{ + CreatedAt: pod.CreatedTime(), ID: pod.ID(), Name: pod.Name(), + Status: status, + Cgroup: pod.CgroupParent(), + UsePodCgroup: pod.UsePodCgroup(), NumberOfContainers: ctrNum, + CtrsInfo: ctrsInfo, } psOutput = append(psOutput, params) } - return psOutput, nil + return sortPodPsOutput(opts.Sort, psOutput) } func generatePodPsOutput(pods []*libpod.Pod, opts podPsOptions, runtime *libpod.Runtime) error { diff --git a/cmd/podman/pod_rm.go b/cmd/podman/pod_rm.go index db2b1ee51..8cc46761e 100644 --- a/cmd/podman/pod_rm.go +++ b/cmd/podman/pod_rm.go @@ -20,17 +20,18 @@ var ( Name: "all, a", Usage: "Remove all pods", }, + LatestFlag, } podRmDescription = "Remove one or more pods" podRmCommand = cli.Command{ Name: "rm", Usage: fmt.Sprintf(`podman rm will remove one or more pods from the host. The pod name or ID can be used. - A pod with running or attached containrs will not be removed. - If --force, -f is specified, all containers will be stopped, then removed.`), + A pod with containers will not be removed without --force. + If --force is specified, all containers will be stopped, then removed.`), Description: podRmDescription, Flags: podRmFlags, Action: podRmCmd, - ArgsUsage: "", + ArgsUsage: "[POD ...]", UseShortOptionHandling: true, } ) @@ -42,6 +43,10 @@ func podRmCmd(c *cli.Context) error { return err } + if c.Bool("latest") && c.Bool("all") { + return errors.Errorf("--all and --latest cannot be used together") + } + runtime, err := libpodruntime.GetRuntime(c) if err != nil { return errors.Wrapf(err, "could not get runtime") @@ -50,22 +55,31 @@ func podRmCmd(c *cli.Context) error { args := c.Args() - if len(args) == 0 && !c.Bool("all") { + if len(args) == 0 && !c.Bool("all") && !c.Bool("latest") { return errors.Errorf("specify one or more pods to remove") } var delPods []*libpod.Pod var lastError error - if c.Bool("all") || c.Bool("a") { - delPods, err = runtime.Pods() + if c.IsSet("all") { + delPods, err = runtime.GetAllPods() if err != nil { return errors.Wrapf(err, "unable to get pod list") } + } else if c.IsSet("latest") { + delPod, err := runtime.GetLatestPod() + if err != nil { + return errors.Wrapf(err, "unable to get latest pod") + } + delPods = append(delPods, delPod) } else { for _, i := range args { pod, err := runtime.LookupPod(i) if err != nil { fmt.Fprintln(os.Stderr, err) + if lastError != nil { + fmt.Fprintln(os.Stderr, lastError) + } lastError = errors.Wrapf(err, "unable to find pods %s", i) continue } diff --git a/completions/bash/podman b/completions/bash/podman index 079636c24..b01a30aaa 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -2055,11 +2055,9 @@ _podman_logout() { _complete_ "$options_with_args" "$boolean_options" } - _podman_pod_create() { local options_with_args=" --cgroup-parent - --cgroup-to-ctr --podidfile --label-file --label @@ -2072,14 +2070,6 @@ _podman_pod_create() { _complete_ "$options_with_args" "$boolean_options" } -# _podman_pod_inspect() { -# _podman_container_run -# } -# -# _podman_pod_kill() { -# _podman_container_run -# } - __podman_pod_ps() { local options_with_args=" -f @@ -2089,11 +2079,16 @@ __podman_pod_ps() { " local boolean_options=" - --no-trunc + --cgroup + --ctr-ids + --ctr-names + --ctr-status -q --quiet - --cgroup + --no-trunc --labels + -l + --latest " _complete_ "$options_with_args" "$boolean_options" } @@ -2110,14 +2105,6 @@ _podman_pod_ps() { __podman_pod_ps } -# _podman_pod_pause() { -# _podman_container_run -# } -# -# _podman_pod_restart() { -# _podman_container_run -# } - _podman_pod_rm() { local options_with_args=" " @@ -2127,24 +2114,8 @@ _podman_pod_rm() { --all -f --force - -rmctr - " - _complete_ "$options_with_args" "$boolean_options" - case "$cur" in - -*) - COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur")) - ;; - *) - __podman_complete_pod_names - ;; - esac -} - -_podman_pod_start() { - local options_with_args=" - " - - local boolean_options=" + --latest + -l " _complete_ "$options_with_args" "$boolean_options" case "$cur" in @@ -2157,27 +2128,6 @@ _podman_pod_start() { esac } -# _podman_pod_stats() { -# _podman_container_run -# } - -# _podman_pod_stop() { -# _podman_container_run -# } - -# _podman_pod_top() { -# _podman_container_run -# } - -# _podman_pod_unpause() { -# _podman_container_run -# } -# -# _podman_pod_wait() { -# _podman_container_run -# } - - _podman_pod() { local boolean_options=" --help @@ -2185,22 +2135,12 @@ _podman_pod() { " subcommands=" create - inspect - kill - ls - pause - restart + ps rm - start - stats - stop - top - unpause - wait " local aliases=" list - ps + ls " __podman_subcommands "$subcommands $aliases" && return @@ -2214,9 +2154,6 @@ _podman_pod() { esac } - - - _podman_podman() { local options_with_args=" --config -c @@ -2253,8 +2190,8 @@ _podman_podman() { logs mount pause - port pod + port ps pull push diff --git a/docs/podman-pod-create.1.md b/docs/podman-pod-create.1.md index 66d74a774..495c6934a 100644 --- a/docs/podman-pod-create.1.md +++ b/docs/podman-pod-create.1.md @@ -15,13 +15,6 @@ containers added to it. The pod id is printed to STDOUT. You can then use ## OPTIONS -**-a**, **--attach**=[] - -Not yet implemented. - -**--cgroup-to-ctr**="" -Tells containers in this pod to use the cgroup created for the pod - **--cgroup-parent**=*true*|*false* Path to cgroups under which the cgroup for the pod will be created. If the path is not absolute, the path is considered to be relative to the cgroups path of the init process. Cgroups will be created if they do not already exist. @@ -52,8 +45,8 @@ UUID short identifier (“f78375b1c487”) Name (“jonah”) podman generates a UUID for each pod, and if a name is not assigned -to the container with **--name** then the daemon will also generate a random -string name. The name is useful any place you need to identify a pod. +to the container with **--name** then a random string name will be generated +for it. The name is useful any place you need to identify a pod. ## EXAMPLES @@ -64,8 +57,4 @@ string name. The name is useful any place you need to identify a pod. podman-pod(1) ## HISTORY -August 2014, updated by Sven Dowideit -September 2014, updated by Sven Dowideit -November 2014, updated by Sven Dowideit -October 2017, converted from Docker documentation to podman by Dan Walsh for podman -July 2018, adapted from create man page by Peter Hunt +July 2018, Originally compiled by Peter Hunt diff --git a/docs/podman-pod-ps.1.md b/docs/podman-pod-ps.1.md new file mode 100644 index 000000000..1fdbe2935 --- /dev/null +++ b/docs/podman-pod-ps.1.md @@ -0,0 +1,165 @@ +% podman-pod-ps "1" + +## NAME +podman\-pod\-ps - Prints out information about pods + +## SYNOPSIS +**podman pod ps** [*options*] + +## DESCRIPTION +**podman pod ps** lists the pods on the system. +By default it lists: + + * pod id + * pod name + * number of containers attached to pod + * status of pod as defined by the following table + +| **Status** | **Description** | +| ------------ | ------------------------------------------------| +| Created | No containers running nor stopped | +| Running | at least one container is running | +| Stopped | At least one container stopped and none running | +| Exited | All containers stopped in pod | +| Dead | Error retrieving state | + + +## OPTIONS + +**--ctr-names** + +Includes the container names in the container info field + +**--ctr-ids** + +Includes the container IDs in the container info field + +**--ctr-status** + +Includes the container statuses in the container info field + +**--latest, -l** + +Show the latest pod created (all states) + +**--no-trunc** + +Display the extended information + +**--quiet, -q** + +Print the numeric IDs of the pods only + +**--format** + +Pretty-print containers to JSON or using a Go template + +Valid placeholders for the Go template are listed below: + +| **Placeholder** | **Description** | +| ------------------- | ----------------------------------------------------------------------------------------------- | +| .ID | Container ID | +| .Name | Name of pod | +| .Status | Status of pod | +| .Labels | All the labels assigned to the pod | +| .ContainerInfo | Show the names, ids and/or statuses of containers (only shows 9 unless no-trunc is specified) | +| .NumberOfContainers | Show the number of containers attached to pod | +| .Cgroup | Cgroup path of pod | +| .UsePodCgroup | Whether containers use the Cgroup of the pod | + +**--sort** + +Sort by created, ID, name, status, or number of containers + +Default: created + +**--filter, -f** + +Filter output based on conditions given + +Valid filters are listed below: + +| **Filter** | **Description** | +| --------------- | ------------------------------------------------------------------- | +| id | [ID] Pod's ID | +| name | [Name] Pod's name | +| label | [Key] or [Key=Value] Label assigned to a container | +| ctr-names | Container name within the pod | +| ctr-ids | Container ID within the pod | +| ctr-status | Container status within the pod | +| ctr-number | Number of containers in the pod | + +**--help**, **-h** + +Print usage statement + +## EXAMPLES + +``` +sudo podman pod ps +POD ID NAME STATUS NUMBER OF CONTAINERS +00dfd6fa02c0 jolly_goldstine Running 1 +f4df8692e116 nifty_torvalds Created 2 +``` + +``` +sudo podman pod ps --ctr-names +POD ID NAME STATUS CONTAINER INFO +00dfd6fa02c0 jolly_goldstine Running [ loving_archimedes ] +f4df8692e116 nifty_torvalds Created [ thirsty_hawking ] [ wizardly_golick ] +``` + +``` +podman pod ps --ctr-status --ctr-names --ctr-ids +POD ID NAME STATUS CONTAINER INFO +00dfd6fa02c0 jolly_goldstine Running [ ba465ab0a3a4 loving_archimedes Running ] +f4df8692e116 nifty_torvalds Created [ 331693bff40a thirsty_hawking Created ] [ 8e428daeb89e wizardly_golick Created ] +``` + +``` +sudo podman pod ps --format "{{.ID}} {{.ContainerInfo}} {{.Cgroup}}" --ctr-names +00dfd6fa02c0 [ loving_archimedes ] /libpod_parent +f4df8692e116 [ thirsty_hawking ] [ wizardly_golick ] /libpod_parent +``` + +``` +sudo podman pod ps --cgroup +POD ID NAME STATUS NUMBER OF CONTAINERS CGROUP USE POD CGROUP +00dfd6fa02c0 jolly_goldstine Running 1 /libpod_parent true +f4df8692e116 nifty_torvalds Created 2 /libpod_parent true +``` + +``` +podman pod ps --sort id --filter ctr-number=2 +POD ID NAME STATUS NUMBER OF CONTAINERS +f4df8692e116 nifty_torvalds Created 2 +``` + +``` +sudo podman pod ps --ctr-ids +POD ID NAME STATUS CONTAINER INFO +00dfd6fa02c0 jolly_goldstine Running [ ba465ab0a3a4 ] +f4df8692e116 nifty_torvalds Created [ 331693bff40a ] [ 8e428daeb89e ] +``` + +``` +sudo podman pod ps --no-trunc --ctr-ids +POD ID NAME STATUS CONTAINER INFO +00dfd6fa02c0a2daaedfdf8fcecd06f22ad114d46d167d71777224735f701866 jolly_goldstine Running [ ba465ab0a3a4e15e3539a1e79c32d1213a02b0989371e274f98e0f1ae9de7050 ] +f4df8692e116a3e6d1d62572644ed36ca475d933808cc3c93435c45aa139314b nifty_torvalds Created [ 331693bff40a0ef2f05a3aba73ce49e3243108911927fff04d1f7fc44dda8022 ] [ 8e428daeb89e69b71e7916a13accfb87d122889442b5c05c2d99cf94a3230e9d ] +``` + +``` +podman pod ps --ctr-names +POD ID NAME STATUS CONTAINER INFO +314f4da82d74 hi Created [ jovial_jackson ] [ hopeful_archimedes ] [ vibrant_ptolemy ] [ heuristic_jennings ] [ keen_raman ] [ hopeful_newton ] [ mystifying_bose ] [ silly_lalande ] [ serene_lichterman ] ... +``` + +## pod ps +Print a list of pods + +## SEE ALSO +podman-pod(1) + +## HISTORY +July 2018, Originally compiled by Peter Hunt diff --git a/docs/podman-pod-rm.1.md b/docs/podman-pod-rm.1.md index 725702714..3b571ee9a 100644 --- a/docs/podman-pod-rm.1.md +++ b/docs/podman-pod-rm.1.md @@ -4,20 +4,24 @@ podman\-pod\-rm - Remove one or more pods ## SYNOPSIS -**podman rm** [*options*] *container* +**podman pod rm** [*options*] *pod* ## DESCRIPTION -**podman pod rm** will remove one or more pods from the host. The pod name or ID can be used. The -f option stops all containers then removes them before removing the pod. Without the -f option, a pod cannot be removed if it has attached containers. +**podman pod rm** will remove one or more pods from the host. The pod name or ID can be used. The \-f option stops all containers and then removes them before removing the pod. Without the \-f option, a pod cannot be removed if it has associated containers. ## OPTIONS -**--force, f** +**--all, a** -Stop running containers and delete all stopped containers before removal of pod. +Remove all pods. Can be used in conjunction with \-f as well. -**--all, a** +**--latest, -l** -Remove all pods. Can be used in conjunction with -f and -r as well. +Instead of providing the pod name or ID, use the last created pod. + +**--force, f** + +Stop running containers and delete all stopped containers before removal of pod. ## EXAMPLE @@ -35,5 +39,4 @@ podman pod rm -fa podman-pod(1) ## HISTORY -August 2017, Originally compiled by Ryan Cole -July 2018, Adapted from podman rm man page by Peter Hunt +July 2018, Originally compiled by Peter Hunt diff --git a/docs/podman-pod.1.md b/docs/podman-pod.1.md index 9b8afaeb0..511a5841f 100644 --- a/docs/podman-pod.1.md +++ b/docs/podman-pod.1.md @@ -14,19 +14,8 @@ podman pod is a set of subcommands that manage pods, or groups of containers. | Subcommand | Description | | ------------------------------------------------- | ------------------------------------------------------------------------------ | | [podman-pod-create(1)](podman-pod-create.1.md) | Create a new pod. | -| [podman-pod-inspect(1)](podman-pod-inspect.1.md) | Display a pod's configuration. | -| [podman-pod-kill(1)](podman-pod-kill.1.md) | Kill the main process in one or more pods. | -| [podman-pod-pause(1)](podman-pod-pause.1.md) | Pause one or more pods. | | [podman-pod-ps(1)](podman-pod-ps.1.md) | Prints out information about pods. | -| [podman-pod-restart(1)](podman-pod-restart.1.md) | Restart one or more pods. | | [podman-pod-rm(1)](podman-pod-rm.1.md) | Remove one or more pods. | -| [podman-pod-start(1)](podman-pod-start.1.md) | Starts one or more pods. | -| [podman-pod-stats(1)](podman-pod-stats.1.md) | Display a live stream of one or more pod's resource usage statistics. | -| [podman-pod-stop(1)](podman-pod-stop.1.md) | Stop one or more running pods. | -| [podman-pod-top(1)](podman-pod-top.1.md) | Display the running processes of a pod. | -| [podman-pod-unpause(1)](podman-pod-unpause.1.md) | Unpause one or more pods. | -| [podman-pod-wait(1)](podman-pod-wait.1.md) | Wait on one or more pods to stop and print their exit codes. | ## HISTORY -Dec 2016, Originally compiled by Dan Walsh -July 2018, Adapted from podman man page by Peter Hunt +July 2018, Originally compiled by Peter Hunt diff --git a/libpod/runtime_pod.go b/libpod/runtime_pod.go index 3ad8454b4..f5a2b017b 100644 --- a/libpod/runtime_pod.go +++ b/libpod/runtime_pod.go @@ -52,51 +52,6 @@ func (r *Runtime) HasPod(id string) (bool, error) { return r.state.HasPod(id) } -// ContainerIDsInPod returns the IDs of containers in the pod -func (r *Runtime) ContainerIDsInPod(pod *Pod) ([]string, error) { - r.lock.RLock() - defer r.lock.RUnlock() - - if !r.valid { - return nil, ErrRuntimeStopped - } - - return r.state.PodContainersByID(pod) -} - -// ContainersInPod returns the containers in the pod -func (r *Runtime) ContainersInPod(pod *Pod) ([]*Container, error) { - r.lock.RLock() - defer r.lock.RUnlock() - - if !r.valid { - return nil, ErrRuntimeStopped - } - - return r.state.PodContainers(pod) -} - -// ContainerNamesInPod returns the names of containers in the pod -func (r *Runtime) ContainerNamesInPod(pod *Pod) ([]string, error) { - r.lock.RLock() - defer r.lock.RUnlock() - - if !r.valid { - return nil, ErrRuntimeStopped - } - - var ctrNames []string - ctrs, err := r.ContainersInPod(pod) - if err != nil { - return nil, err - } - for _, ctr := range ctrs { - ctrNames = append(ctrNames, ctr.Name()) - } - - return ctrNames, nil -} - // LookupPod retrieves a pod by its name or a partial ID // If a partial ID is not unique, an error will be returned func (r *Runtime) LookupPod(idOrName string) (*Pod, error) { diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go index dbbf99325..57416732d 100644 --- a/pkg/spec/createconfig.go +++ b/pkg/spec/createconfig.go @@ -319,10 +319,10 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime) ([]lib options = append(options, libpod.WithName(c.Name)) } if c.Pod != "" { - logrus.Debugf("appending to pod %s", c.Pod) + logrus.Debugf("adding container to pod %s", c.Pod) pod, err := runtime.LookupPod(c.Pod) if err != nil { - return nil, errors.Wrapf(err, "unable to add container to pod") + return nil, errors.Wrapf(err, "unable to add container to pod %s", c.Pod) } options = append(options, runtime.WithPod(pod)) } diff --git a/test/e2e/pod_ps_test.go b/test/e2e/pod_ps_test.go new file mode 100644 index 000000000..4ab13f119 --- /dev/null +++ b/test/e2e/pod_ps_test.go @@ -0,0 +1,227 @@ +package integration + +import ( + "fmt" + "os" + "sort" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Podman ps", 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() + + }) + + It("podman pod ps no pods", func() { + session := podmanTest.Podman([]string{"pod", "ps"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + + It("podman pod ps default", 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)) + + result := podmanTest.Podman([]string{"pod", "ps"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(len(result.OutputToStringArray())).Should(BeNumerically(">", 0)) + }) + + It("podman pod ps quiet flag", func() { + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + podid := session.OutputToString() + _, ec, _ := podmanTest.RunLsContainerInPod("", podid) + Expect(ec).To(Equal(0)) + + result := podmanTest.Podman([]string{"pod", "ps", "-q"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(len(result.OutputToStringArray())).Should(BeNumerically(">", 0)) + Expect(podid).To(ContainSubstring(result.OutputToStringArray()[0])) + }) + + It("podman pod ps no-trunc", func() { + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + podid := session.OutputToString() + _, ec, _ := podmanTest.RunLsContainerInPod("", podid) + Expect(ec).To(Equal(0)) + + result := podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(len(result.OutputToStringArray())).Should(BeNumerically(">", 0)) + Expect(podid).To(Equal(result.OutputToStringArray()[0])) + }) + + It("podman pod ps latest", func() { + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + podid1 := session.OutputToString() + + session = podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + podid2 := session.OutputToString() + + result := podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--latest"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(ContainSubstring(podid2)) + Expect(result.OutputToString()).To(Not(ContainSubstring(podid1))) + }) + It("podman pod ps id filter flag", func() { + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + result := podmanTest.Podman([]string{"pod", "ps", "--filter", fmt.Sprintf("id=%s", session.OutputToString())}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + }) + + It("podman pod ps mutually exclusive flags", func() { + session := podmanTest.Podman([]string{"pod", "ps", "-q", "--format", "{{.ID}}"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Not(Equal(0))) + + }) + + It("podman pod ps --sort by name", func() { + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"pod", "ps", "--sort=name", "--format", "{{.Name}}"}) + + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + sortedArr := session.OutputToStringArray() + + Expect(sort.SliceIsSorted(sortedArr, func(i, j int) bool { return sortedArr[i] < sortedArr[j] })).To(BeTrue()) + }) + + It("podman pod ps --ctr-names", func() { + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + podid := session.OutputToString() + + session = podmanTest.RunTopContainerInPod("test1", podid) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + _, ec, _ := podmanTest.RunLsContainerInPod("test2", podid) + Expect(ec).To(Equal(0)) + + session = podmanTest.Podman([]string{"pod", "ps", "--format={{.ContainerInfo}}", "--ctr-names"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("test1")) + Expect(session.OutputToString()).To(ContainSubstring("test2")) + }) + + It("podman pod ps filter ctr attributes", func() { + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + podid1 := session.OutputToString() + + session = podmanTest.RunTopContainerInPod("test1", podid1) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + podid2 := session.OutputToString() + + _, ec, cid := podmanTest.RunLsContainerInPod("test2", podid2) + Expect(ec).To(Equal(0)) + + session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-names=test1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(podid1)) + Expect(session.OutputToString()).To(Not(ContainSubstring(podid2))) + + session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", fmt.Sprintf("ctr-ids=%s", cid)}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(podid2)) + Expect(session.OutputToString()).To(Not(ContainSubstring(podid1))) + + session = podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + podid3 := session.OutputToString() + + session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-number=1"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(podid1)) + Expect(session.OutputToString()).To(ContainSubstring(podid2)) + Expect(session.OutputToString()).To(Not(ContainSubstring(podid3))) + + session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-status=running"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(podid1)) + Expect(session.OutputToString()).To(Not(ContainSubstring(podid2))) + Expect(session.OutputToString()).To(Not(ContainSubstring(podid3))) + + session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-status=exited"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(podid2)) + Expect(session.OutputToString()).To(Not(ContainSubstring(podid1))) + Expect(session.OutputToString()).To(Not(ContainSubstring(podid3))) + + session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-status=created"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(BeEmpty()) + }) +}) diff --git a/test/e2e/pod_rm_test.go b/test/e2e/pod_rm_test.go index ca6b4fcbe..4ebf2f340 100644 --- a/test/e2e/pod_rm_test.go +++ b/test/e2e/pod_rm_test.go @@ -31,24 +31,44 @@ var _ = Describe("Podman pod rm", func() { It("podman pod rm empty pod", func() { session := podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - cid := session.OutputToString() + podid := session.OutputToString() - result := podmanTest.Podman([]string{"pod", "rm", cid}) + result := podmanTest.Podman([]string{"pod", "rm", podid}) result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) }) + It("podman pod rm latest pod", func() { + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + podid := session.OutputToString() + + session = podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + podid2 := session.OutputToString() + + result := podmanTest.Podman([]string{"pod", "rm", "--latest"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + + result = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(ContainSubstring(podid)) + Expect(result.OutputToString()).To(Not(ContainSubstring(podid2))) + }) + It("podman pod rm doesn't remove a pod with a container", func() { session := podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - cid := session.OutputToString() + podid := session.OutputToString() - session = podmanTest.Podman([]string{"create", "--pod", cid, ALPINE, "ls"}) + session = podmanTest.Podman([]string{"create", "--pod", podid, ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - result := podmanTest.Podman([]string{"pod", "rm", cid}) + result := podmanTest.Podman([]string{"pod", "rm", podid}) result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(125)) @@ -61,13 +81,13 @@ var _ = Describe("Podman pod rm", func() { session := podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - cid := session.OutputToString() + podid := session.OutputToString() - session = podmanTest.Podman([]string{"run", "-d", "--pod", cid, ALPINE, "top"}) + session = podmanTest.Podman([]string{"run", "-d", "--pod", podid, ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - result := podmanTest.Podman([]string{"pod", "rm", "-f", cid}) + result := podmanTest.Podman([]string{"pod", "rm", "-f", podid}) result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) @@ -80,12 +100,12 @@ var _ = Describe("Podman pod rm", func() { session := podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - cid1 := session.OutputToString() + podid1 := session.OutputToString() session = podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - session = podmanTest.Podman([]string{"run", "-d", "--pod", cid1, ALPINE, "top"}) + session = podmanTest.Podman([]string{"run", "-d", "--pod", podid1, ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -107,29 +127,29 @@ var _ = Describe("Podman pod rm", func() { session := podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - cid1 := session.OutputToString() + podid1 := session.OutputToString() session = podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - cid2 := session.OutputToString() + podid2 := session.OutputToString() session = podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - session = podmanTest.Podman([]string{"run", "-d", "--pod", cid1, ALPINE, "top"}) + session = podmanTest.Podman([]string{"run", "-d", "--pod", podid1, ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"create", "-d", "--pod", cid1, ALPINE, "ls"}) + session = podmanTest.Podman([]string{"create", "-d", "--pod", podid1, ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"run", "-d", "--pod", cid2, ALPINE, "ls"}) + session = podmanTest.Podman([]string{"run", "-d", "--pod", podid2, ALPINE, "ls"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"run", "-d", "--pod", cid2, nginx}) + session = podmanTest.Podman([]string{"run", "-d", "--pod", podid2, nginx}) session.WaitWithDefaultTimeout() result := podmanTest.Podman([]string{"pod", "rm", "-fa"}) -- cgit v1.2.3-54-g00ecf