diff options
author | Matthew Heon <matthew.heon@gmail.com> | 2018-07-13 16:35:05 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-07-13 16:35:05 -0400 |
commit | 4729fd425588e17eff4f575d433f1075d4e27bb4 (patch) | |
tree | e7727035759b5c7aa08a745a2f87ae0fd4e546af | |
parent | a689639a6502bab3f49b853bc2983c1b44363b2f (diff) | |
parent | 259136c36c89cb32e28edfe8b5d7a3c1082fad5b (diff) | |
download | podman-4729fd425588e17eff4f575d433f1075d4e27bb4.tar.gz podman-4729fd425588e17eff4f575d433f1075d4e27bb4.tar.bz2 podman-4729fd425588e17eff4f575d433f1075d4e27bb4.zip |
Merge pull request #1089 from mheon/add_exited
Record whether the container has exited
-rw-r--r-- | cmd/podman/batchcontainer/container.go | 49 | ||||
-rw-r--r-- | cmd/podman/ps.go | 101 | ||||
-rw-r--r-- | libpod/container.go | 12 | ||||
-rw-r--r-- | libpod/container_internal.go | 2 | ||||
-rw-r--r-- | libpod/oci.go | 1 | ||||
-rw-r--r-- | pkg/varlinkapi/util.go | 6 | ||||
-rw-r--r-- | test/e2e/ps_test.go | 5 |
7 files changed, 102 insertions, 74 deletions
diff --git a/cmd/podman/batchcontainer/container.go b/cmd/podman/batchcontainer/container.go index 09db4f8a2..017837a96 100644 --- a/cmd/podman/batchcontainer/container.go +++ b/cmd/podman/batchcontainer/container.go @@ -35,12 +35,13 @@ type PsOptions struct { // BatchContainerStruct is the return obkect from BatchContainer and contains // container related information type BatchContainerStruct struct { - ConConfig *libpod.ContainerConfig - ConState libpod.ContainerStatus - ExitCode int32 - Pid int - RootFsSize, RwSize int64 - StartedTime time.Time + ConConfig *libpod.ContainerConfig + ConState libpod.ContainerStatus + ExitCode int32 + Exited bool + Pid int + StartedTime time.Time + Size *ContainerSize } // Namespace describes output for ps namespace @@ -55,17 +56,25 @@ type Namespace struct { UTS string `json:"uts,omitempty"` } +// ContainerSize holds the size of the container's root filesystem and top +// read-write layer +type ContainerSize struct { + RootFsSize int64 `json:"rootFsSize"` + RwSize int64 `json:"rwSize"` +} + // BatchContainer is used in ps to reduce performance hits by "batching" // locks. func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStruct, error) { var ( - conConfig *libpod.ContainerConfig - conState libpod.ContainerStatus - err error - exitCode int32 - pid int - rootFsSize, rwSize int64 - startedTime time.Time + conConfig *libpod.ContainerConfig + conState libpod.ContainerStatus + err error + exitCode int32 + exited bool + pid int + size *ContainerSize + startedTime time.Time ) batchErr := ctr.Batch(func(c *libpod.Container) error { @@ -75,7 +84,7 @@ func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStru return errors.Wrapf(err, "unable to obtain container state") } - exitCode, err = c.ExitCode() + exitCode, exited, err = c.ExitCode() if err != nil { return errors.Wrapf(err, "unable to obtain container exit code") } @@ -95,16 +104,20 @@ func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStru } } if opts.Size { - rootFsSize, err = c.RootFsSize() + size = new(ContainerSize) + + rootFsSize, err := c.RootFsSize() if err != nil { logrus.Errorf("error getting root fs size for %q: %v", c.ID(), err) } - rwSize, err = c.RWSize() + rwSize, err := c.RWSize() if err != nil { logrus.Errorf("error getting rw size for %q: %v", c.ID(), err) } + size.RootFsSize = rootFsSize + size.RwSize = rwSize } return nil }) @@ -115,10 +128,10 @@ func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStru ConConfig: conConfig, ConState: conState, ExitCode: exitCode, + Exited: exited, Pid: pid, - RootFsSize: rootFsSize, - RwSize: rwSize, StartedTime: startedTime, + Size: size, }, nil } diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go index 49e43ffac..8cec73b3c 100644 --- a/cmd/podman/ps.go +++ b/cmd/podman/ps.go @@ -52,23 +52,23 @@ type psTemplateParams struct { // psJSONParams will be populated by data from libpod.Container, // the members of the struct are the sama data types as their sources. type psJSONParams struct { - ID string `json:"id"` - Image string `json:"image"` - ImageID string `json:"image_id"` - Command []string `json:"command"` - CreatedAt time.Time `json:"createdAt"` - ExitCode int32 `json:"exitCode"` - RunningFor time.Duration `json:"runningFor"` - Status string `json:"status"` - PID int `json:"PID"` - Ports []ocicni.PortMapping `json:"ports"` - RootFsSize int64 `json:"rootFsSize"` - RWSize int64 `json:"rwSize"` - Names string `json:"names"` - Labels fields.Set `json:"labels"` - Mounts []string `json:"mounts"` - ContainerRunning bool `json:"ctrRunning"` - Namespaces *batchcontainer.Namespace `json:"namespace,omitempty"` + ID string `json:"id"` + Image string `json:"image"` + ImageID string `json:"image_id"` + Command []string `json:"command"` + CreatedAt time.Time `json:"createdAt"` + ExitCode int32 `json:"exitCode"` + Exited bool `json:"exited"` + RunningFor time.Duration `json:"runningFor"` + Status string `json:"status"` + PID int `json:"PID"` + Ports []ocicni.PortMapping `json:"ports"` + Size *batchcontainer.ContainerSize `json:"size,omitempty"` + Names string `json:"names"` + Labels fields.Set `json:"labels"` + Mounts []string `json:"mounts"` + ContainerRunning bool `json:"ctrRunning"` + Namespaces *batchcontainer.Namespace `json:"namespace,omitempty"` } // Type declaration and functions for sorting the PS output @@ -114,7 +114,10 @@ func (a psSortedStatus) Less(i, j int) bool { return a.psSorted[i].Status < a.ps type psSortedSize struct{ psSorted } func (a psSortedSize) Less(i, j int) bool { - return a.psSorted[i].RootFsSize < a.psSorted[j].RootFsSize + if a.psSorted[i].Size == nil || a.psSorted[j].Size == nil { + return false + } + return a.psSorted[i].Size.RootFsSize < a.psSorted[j].Size.RootFsSize } var ( @@ -279,22 +282,16 @@ func checkFlagsPassed(c *cli.Context) error { if c.Int("last") >= 0 && c.Bool("latest") { return errors.Errorf("last and latest are mutually exclusive") } - // quiet, size, namespace, and format with Go template are mutually exclusive - flags := 0 + // Quiet conflicts with size, namespace, and format with a Go template if c.Bool("quiet") { - flags++ - } - if c.Bool("size") { - flags++ - } - if c.Bool("namespace") { - flags++ - } - if c.IsSet("format") && c.String("format") != formats.JSONString { - flags++ + if c.Bool("size") || c.Bool("namespace") || (c.IsSet("format") && + c.String("format") != formats.JSONString) { + return errors.Errorf("quiet conflicts with size, namespace, and format with go template") + } } - if flags > 1 { - return errors.Errorf("quiet, size, namespace, and format with Go template are mutually exclusive") + // Size and namespace conflict with each other + if c.Bool("size") && c.Bool("namespace") { + return errors.Errorf("size and namespace options conflict") } return nil } @@ -324,8 +321,8 @@ func generateContainerFilterFuncs(filter, filterValue string, runtime *libpod.Ru return nil, errors.Wrapf(err, "exited code out of range %q", filterValue) } return func(c *libpod.Container) bool { - ec, err := c.ExitCode() - if ec == int32(exitCode) && err == nil { + ec, exited, err := c.ExitCode() + if ec == int32(exitCode) && err == nil && exited == true { return true } return false @@ -496,7 +493,10 @@ func getTemplateOutput(psParams []psJSONParams, opts batchcontainer.PsOptions) ( ns = psParam.Namespaces } if opts.Size { - size = units.HumanSizeWithPrecision(float64(psParam.RWSize), 3) + " (virtual " + units.HumanSizeWithPrecision(float64(psParam.RootFsSize), 3) + ")" + if psParam.Size == nil { + return nil, errors.Errorf("Container %s does not have a size struct", psParam.ID) + } + size = units.HumanSizeWithPrecision(float64(psParam.Size.RwSize), 3) + " (virtual " + units.HumanSizeWithPrecision(float64(psParam.Size.RootFsSize), 3) + ")" } runningFor := units.HumanDuration(psParam.RunningFor) @@ -576,22 +576,25 @@ func getAndSortJSONParams(containers []*libpod.Container, opts batchcontainer.Ps ns = batchcontainer.GetNamespaces(batchInfo.Pid) } params := psJSONParams{ - ID: ctr.ID(), - Image: batchInfo.ConConfig.RootfsImageName, - ImageID: batchInfo.ConConfig.RootfsImageID, - Command: batchInfo.ConConfig.Spec.Process.Args, - CreatedAt: batchInfo.ConConfig.CreatedTime, - Status: batchInfo.ConState.String(), - Ports: batchInfo.ConConfig.PortMappings, - RootFsSize: batchInfo.RootFsSize, - RWSize: batchInfo.RwSize, - Names: batchInfo.ConConfig.Name, - Labels: batchInfo.ConConfig.Labels, - Mounts: batchInfo.ConConfig.UserVolumes, - Namespaces: ns, + ID: ctr.ID(), + Image: batchInfo.ConConfig.RootfsImageName, + ImageID: batchInfo.ConConfig.RootfsImageID, + Command: batchInfo.ConConfig.Spec.Process.Args, + CreatedAt: batchInfo.ConConfig.CreatedTime, + ExitCode: batchInfo.ExitCode, + Exited: batchInfo.Exited, + Status: batchInfo.ConState.String(), + PID: batchInfo.Pid, + Ports: batchInfo.ConConfig.PortMappings, + Size: batchInfo.Size, + Names: batchInfo.ConConfig.Name, + Labels: batchInfo.ConConfig.Labels, + Mounts: batchInfo.ConConfig.UserVolumes, + ContainerRunning: batchInfo.ConState == libpod.ContainerStateRunning, + Namespaces: ns, } - if !batchInfo.StartedTime.IsZero() { + if !batchInfo.StartedTime.IsZero() && batchInfo.ConState == libpod.ContainerStateRunning { params.RunningFor = time.Since(batchInfo.StartedTime) } diff --git a/libpod/container.go b/libpod/container.go index f882868ed..38f07bd5b 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -135,6 +135,8 @@ type containerState struct { FinishedTime time.Time `json:"finishedTime,omitempty"` // ExitCode is the exit code returned when the container stopped ExitCode int32 `json:"exitCode,omitempty"` + // Exited is whether the container has exited + Exited bool `json:"exited,omitempty"` // OOMKilled indicates that the container was killed as it ran out of // memory OOMKilled bool `json:"oomKilled,omitempty"` @@ -667,16 +669,18 @@ func (c *Container) FinishedTime() (time.Time, error) { } // ExitCode returns the exit code of the container as -// an int32 -func (c *Container) ExitCode() (int32, error) { +// an int32, and whether the container has exited. +// If the container has not exited, exit code will always be 0. +// If the container restarts, the exit code is reset to 0. +func (c *Container) ExitCode() (int32, bool, error) { if !c.batched { c.lock.Lock() defer c.lock.Unlock() if err := c.syncContainer(); err != nil { - return 0, errors.Wrapf(err, "error updating container %s state", c.ID()) + return 0, false, errors.Wrapf(err, "error updating container %s state", c.ID()) } } - return c.state.ExitCode, nil + return c.state.ExitCode, c.state.Exited, nil } // OOMKilled returns whether the container was killed by an OOM condition diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 905402c47..010d01315 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -586,6 +586,8 @@ func (c *Container) reinit(ctx context.Context) error { // Set and save now to make sure that, if the init() below fails // we still have a valid state c.state.State = ContainerStateConfigured + c.state.ExitCode = 0 + c.state.Exited = false if err := c.save(); err != nil { return err } diff --git a/libpod/oci.go b/libpod/oci.go index c0478b3b6..3eaf159e7 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -450,6 +450,7 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container) error { ctr.state.OOMKilled = true } + ctr.state.Exited = true } return nil diff --git a/pkg/varlinkapi/util.go b/pkg/varlinkapi/util.go index 25ab59801..ad5c8c19d 100644 --- a/pkg/varlinkapi/util.go +++ b/pkg/varlinkapi/util.go @@ -65,13 +65,15 @@ func makeListContainer(containerID string, batchInfo batchcontainer.BatchContain Runningfor: time.Since(batchInfo.ConConfig.CreatedTime).String(), Status: batchInfo.ConState.String(), Ports: ports, - Rootfssize: batchInfo.RootFsSize, - Rwsize: batchInfo.RwSize, Names: batchInfo.ConConfig.Name, Labels: batchInfo.ConConfig.Labels, Mounts: mounts, Containerrunning: batchInfo.ConState == libpod.ContainerStateRunning, Namespaces: namespace, } + if batchInfo.Size != nil { + lc.Rootfssize = batchInfo.Size.RootFsSize + lc.Rwsize = batchInfo.Size.RwSize + } return lc } diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index 38b59e1d1..b3d58af77 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -201,12 +201,15 @@ var _ = Describe("Podman ps", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"ps", "-a", "--sort=size", "--format", "{{.Size}}"}) + session = podmanTest.Podman([]string{"ps", "-a", "-s", "--sort=size", "--format", "{{.Size}}"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) sortedArr := session.OutputToStringArray() + // TODO: This may be broken - the test was running without the + // ability to perform any sorting for months and succeeded + // without error. Expect(sort.SliceIsSorted(sortedArr, func(i, j int) bool { r := regexp.MustCompile(`^\S+\s+\(virtual (\S+)\)`) matches1 := r.FindStringSubmatch(sortedArr[i]) |