From 028374b99e5c9dd23236a10495ea0aebe819b51f Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Fri, 13 Jul 2018 10:24:17 -0400 Subject: Record whether the container has exited Use this to supplement exit codes returned from containers, to make sure we know when exit codes are invalid (as the container has not yet exited) Signed-off-by: Matthew Heon --- cmd/podman/batchcontainer/container.go | 2 +- cmd/podman/ps.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'cmd') diff --git a/cmd/podman/batchcontainer/container.go b/cmd/podman/batchcontainer/container.go index 09db4f8a2..76b10b633 100644 --- a/cmd/podman/batchcontainer/container.go +++ b/cmd/podman/batchcontainer/container.go @@ -75,7 +75,7 @@ func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStru return errors.Wrapf(err, "unable to obtain container state") } - exitCode, err = c.ExitCode() + exitCode, _, err = c.ExitCode() if err != nil { return errors.Wrapf(err, "unable to obtain container exit code") } diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go index 49e43ffac..22055b274 100644 --- a/cmd/podman/ps.go +++ b/cmd/podman/ps.go @@ -324,7 +324,7 @@ 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() + ec, _, err := c.ExitCode() if ec == int32(exitCode) && err == nil { return true } -- cgit v1.2.3-54-g00ecf From d25ebcc35bc59ac536eacf59af978181e6adabd3 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Fri, 13 Jul 2018 10:34:25 -0400 Subject: Ignore running containers in ps exit-code filters Signed-off-by: Matthew Heon --- cmd/podman/ps.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'cmd') diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go index 22055b274..a5f417449 100644 --- a/cmd/podman/ps.go +++ b/cmd/podman/ps.go @@ -324,8 +324,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 -- cgit v1.2.3-54-g00ecf From 4c44185199299f44425ebd689bf3ac42e4606788 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Fri, 13 Jul 2018 11:21:37 -0400 Subject: Major fixes to podman ps --format=json output A number of fields were never being populated. Populate them as best we can. Add a new field, exited, to indicate whether the exit code has meaning (IE, the container has exited). Fix handling of running time - it stops ticking when the container stops. There is further work needed here, I suspect. Signed-off-by: Matthew Heon --- cmd/podman/batchcontainer/container.go | 5 ++++- cmd/podman/ps.go | 33 +++++++++++++++++++-------------- 2 files changed, 23 insertions(+), 15 deletions(-) (limited to 'cmd') diff --git a/cmd/podman/batchcontainer/container.go b/cmd/podman/batchcontainer/container.go index 76b10b633..6f002f58a 100644 --- a/cmd/podman/batchcontainer/container.go +++ b/cmd/podman/batchcontainer/container.go @@ -38,6 +38,7 @@ type BatchContainerStruct struct { ConConfig *libpod.ContainerConfig ConState libpod.ContainerStatus ExitCode int32 + Exited bool Pid int RootFsSize, RwSize int64 StartedTime time.Time @@ -63,6 +64,7 @@ func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStru conState libpod.ContainerStatus err error exitCode int32 + exited bool pid int rootFsSize, rwSize int64 startedTime time.Time @@ -75,7 +77,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") } @@ -115,6 +117,7 @@ func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStru ConConfig: conConfig, ConState: conState, ExitCode: exitCode, + Exited: exited, Pid: pid, RootFsSize: rootFsSize, RwSize: rwSize, diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go index a5f417449..b659a91a0 100644 --- a/cmd/podman/ps.go +++ b/cmd/podman/ps.go @@ -58,6 +58,7 @@ type psJSONParams struct { 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"` @@ -576,22 +577,26 @@ 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, + RootFsSize: batchInfo.RootFsSize, + RWSize: batchInfo.RwSize, + 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) } -- cgit v1.2.3-54-g00ecf From f08fffa3061fd12763674138719f69290e143e3d Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Fri, 13 Jul 2018 11:25:05 -0400 Subject: Don't print rootfs and rw sizes if they're empty We're printing them unconditionally now, even if --size is not passed, which is confusing (you see a pair of 0s for container size, when it is clearly not 0). This may introduce bugs related to containers with an rwsize of 0 (freshly created from an image) and --size specified, but is definitely better than what we have now. Signed-off-by: Matthew Heon --- cmd/podman/ps.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'cmd') diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go index b659a91a0..f2ea3939b 100644 --- a/cmd/podman/ps.go +++ b/cmd/podman/ps.go @@ -63,8 +63,8 @@ type psJSONParams struct { Status string `json:"status"` PID int `json:"PID"` Ports []ocicni.PortMapping `json:"ports"` - RootFsSize int64 `json:"rootFsSize"` - RWSize int64 `json:"rwSize"` + RootFsSize int64 `json:"rootFsSize,omitempty"` + RWSize int64 `json:"rwSize,omitempty"` Names string `json:"names"` Labels fields.Set `json:"labels"` Mounts []string `json:"mounts"` -- cgit v1.2.3-54-g00ecf From 5b43a6a7ee6638b6d6cdb129321e42ce0dce6975 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Fri, 13 Jul 2018 11:51:06 -0400 Subject: Only print container size JSON if --size was requested To do this, move it into a separate struct, and embed that in the JSON we return. Signed-off-by: Matthew Heon --- cmd/podman/batchcontainer/container.go | 48 ++++++++++++++++++++-------------- cmd/podman/ps.go | 48 ++++++++++++++++++---------------- pkg/varlinkapi/util.go | 6 +++-- 3 files changed, 59 insertions(+), 43 deletions(-) (limited to 'cmd') diff --git a/cmd/podman/batchcontainer/container.go b/cmd/podman/batchcontainer/container.go index 6f002f58a..017837a96 100644 --- a/cmd/podman/batchcontainer/container.go +++ b/cmd/podman/batchcontainer/container.go @@ -35,13 +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 - Exited bool - 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 @@ -56,18 +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 - exited bool - 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 { @@ -97,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 }) @@ -119,9 +130,8 @@ func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStru 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 f2ea3939b..921490ba9 100644 --- a/cmd/podman/ps.go +++ b/cmd/podman/ps.go @@ -52,24 +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"` - Exited bool `json:"exited"` - RunningFor time.Duration `json:"runningFor"` - Status string `json:"status"` - PID int `json:"PID"` - Ports []ocicni.PortMapping `json:"ports"` - RootFsSize int64 `json:"rootFsSize,omitempty"` - RWSize int64 `json:"rwSize,omitempty"` - 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 @@ -115,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 ( @@ -497,7 +499,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) @@ -587,8 +592,7 @@ func getAndSortJSONParams(containers []*libpod.Container, opts batchcontainer.Ps Status: batchInfo.ConState.String(), PID: batchInfo.Pid, Ports: batchInfo.ConConfig.PortMappings, - RootFsSize: batchInfo.RootFsSize, - RWSize: batchInfo.RwSize, + Size: batchInfo.Size, Names: batchInfo.ConConfig.Name, Labels: batchInfo.ConConfig.Labels, Mounts: batchInfo.ConConfig.UserVolumes, 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 } -- cgit v1.2.3-54-g00ecf From 259136c36c89cb32e28edfe8b5d7a3c1082fad5b Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Fri, 13 Jul 2018 16:00:23 -0400 Subject: Change logic for detecting conflicting flags in ps There's no reason --size cannot be used together with a Go template - in fact, using a Go template for {{.Size}} without --size being passed will not work. Allow use of --namespace and --size with Go templates, but not with --quiet. Do not allow --namespace and --size at the same time. Signed-off-by: Matthew Heon --- cmd/podman/ps.go | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) (limited to 'cmd') diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go index 921490ba9..8cec73b3c 100644 --- a/cmd/podman/ps.go +++ b/cmd/podman/ps.go @@ -282,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 } -- cgit v1.2.3-54-g00ecf