summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Heon <matthew.heon@gmail.com>2018-07-13 16:35:05 -0400
committerGitHub <noreply@github.com>2018-07-13 16:35:05 -0400
commit4729fd425588e17eff4f575d433f1075d4e27bb4 (patch)
treee7727035759b5c7aa08a745a2f87ae0fd4e546af
parenta689639a6502bab3f49b853bc2983c1b44363b2f (diff)
parent259136c36c89cb32e28edfe8b5d7a3c1082fad5b (diff)
downloadpodman-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.go49
-rw-r--r--cmd/podman/ps.go101
-rw-r--r--libpod/container.go12
-rw-r--r--libpod/container_internal.go2
-rw-r--r--libpod/oci.go1
-rw-r--r--pkg/varlinkapi/util.go6
-rw-r--r--test/e2e/ps_test.go5
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])