From e97ca5bd38045a818e94cb11571fa4120dffcdae Mon Sep 17 00:00:00 2001 From: baude Date: Thu, 22 Feb 2018 10:08:59 -0600 Subject: podman ps json output use batched ops The podman ps command for non-json targets has the ability to use a "batched" function which limits the use of locks needed. The collection of output for JSON should use this too. Signed-off-by: baude Closes: #380 Approved by: rhatdan --- cmd/podman/ps.go | 258 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 135 insertions(+), 123 deletions(-) (limited to 'cmd/podman') diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go index d5bbe6f67..b635ea392 100644 --- a/cmd/podman/ps.go +++ b/cmd/podman/ps.go @@ -64,21 +64,21 @@ 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"` - RunningFor time.Duration `json:"runningFor"` - Status string `json:"status"` - Ports map[string]struct{} `json:"ports"` - RootFsSize int64 `json:"rootFsSize"` - RWSize int64 `json:"rwSize"` - Names string `json:"names"` - Labels fields.Set `json:"labels"` - Mounts []specs.Mount `json:"mounts"` - ContainerRunning bool `json:"ctrRunning"` - Namespaces *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"` + RunningFor time.Duration `json:"runningFor"` + Status string `json:"status"` + Ports []ocicni.PortMapping `json:"ports"` + RootFsSize int64 `json:"rootFsSize"` + RWSize int64 `json:"rwSize"` + Names string `json:"names"` + Labels fields.Set `json:"labels"` + Mounts []specs.Mount `json:"mounts"` + ContainerRunning bool `json:"ctrRunning"` + Namespaces *namespace `json:"namespace,omitempty"` } type namespace struct { @@ -397,72 +397,25 @@ func (p *psTemplateParams) headerMap() map[string]string { // getTemplateOutput returns the modified container information func getTemplateOutput(containers []*libpod.Container, opts psOptions) ([]psTemplateParams, error) { var ( - psOutput []psTemplateParams - status, ctrID, size string - conConfig *libpod.ContainerConfig - conState libpod.ContainerStatus - err error - exitCode int32 - pid int - rootFsSize, rwSize int64 - ns *namespace - startedTime time.Time + psOutput []psTemplateParams + status, size string + ns *namespace ) for _, ctr := range containers { - batchErr := ctr.Batch(func(c *libpod.Container) error { - ctrID = c.ID() - conConfig = c.Config() - conState, err = c.State() - if err != nil { - return errors.Wrapf(err, "unable to obtain container state") - } - exitCode, err = c.ExitCode() - if err != nil { - return errors.Wrapf(err, "unable to obtain container exit code") - } - startedTime, err = c.StartedTime() - if err != nil { - logrus.Errorf("error getting started time for %q: %v", c.ID(), err) - } - - if !opts.size && !opts.namespace { - return nil - } - - if opts.namespace { - pid, err = c.PID() - if err != nil { - return errors.Wrapf(err, "unable to obtain container pid") - } - } - if opts.size { - rootFsSize, err = c.RootFsSize() - if err != nil { - logrus.Errorf("error getting root fs size for %q: %v", c.ID(), err) - } - - rwSize, err = c.RWSize() - if err != nil { - logrus.Errorf("error getting rw size for %q: %v", c.ID(), err) - } - - size = units.HumanSizeWithPrecision(float64(rwSize), 3) + " (virtual " + units.HumanSizeWithPrecision(float64(rootFsSize), 3) + ")" - } - - return nil - }) - if batchErr != nil { + batchInfo, err := batchContainerOp(ctr, opts) + if err != nil { return nil, err } + ctrID := ctr.ID() runningFor := "" // If the container has not be started, the "zero" value of time is 0001-01-01 00:00:00 +0000 UTC // which would make the time elapsed about a few hundred of years. So checking for the "zero" value of time.Time - if startedTime != (time.Time{}) { - runningFor = units.HumanDuration(time.Since(startedTime)) + if batchInfo.startedTime != (time.Time{}) { + runningFor = units.HumanDuration(time.Since(batchInfo.startedTime)) } - createdAt := conConfig.CreatedTime.Format("2006-01-02 15:04:05 -0700 MST") - imageName := conConfig.RootfsImageName + createdAt := batchInfo.conConfig.CreatedTime.Format("2006-01-02 15:04:05 -0700 MST") + imageName := batchInfo.conConfig.RootfsImageName var createArtifact createConfig artifact, err := ctr.GetArtifact("create-config") @@ -474,17 +427,19 @@ func getTemplateOutput(containers []*libpod.Container, opts psOptions) ([]psTemp logrus.Errorf("couldn't get some ps information, error getting artifact %q: %v", ctr.ID(), err) } if opts.namespace { - ns = getNamespaces(pid) + ns = getNamespaces(batchInfo.pid) } + if opts.size { - // TODO We currently dont have the ability to get many of - // these data items. Uncomment as progress is made - command := strings.Join(conConfig.Spec.Process.Args, " ") - ports := getPorts(conConfig.PortMappings) + size = units.HumanSizeWithPrecision(float64(batchInfo.rwSize), 3) + " (virtual " + units.HumanSizeWithPrecision(float64(batchInfo.rootFsSize), 3) + ")" + } + + command := strings.Join(batchInfo.conConfig.Spec.Process.Args, " ") + ports := portsToString(batchInfo.conConfig.PortMappings) mounts := getMounts(createArtifact.Volumes, opts.noTrunc) labels := formatLabels(ctr.Labels()) - switch conState { + switch batchInfo.conState { case libpod.ContainerStateStopped: status = fmt.Sprintf("Exited (%d) %s ago", exitCode, runningFor) case libpod.ContainerStateRunning: @@ -499,12 +454,9 @@ func getTemplateOutput(containers []*libpod.Container, opts psOptions) ([]psTemp if !opts.noTrunc { ctrID = shortID(ctr.ID()) - imageName = conConfig.RootfsImageName + imageName = batchInfo.conConfig.RootfsImageName } - // TODO We currently dont have the ability to get many of - // these data items. Uncomment as progress is made - params := psTemplateParams{ ID: ctrID, Image: imageName, @@ -517,7 +469,7 @@ func getTemplateOutput(containers []*libpod.Container, opts psOptions) ([]psTemp Names: ctr.Name(), Labels: labels, Mounts: mounts, - PID: pid, + PID: batchInfo.pid, } if opts.namespace { params.Cgroup = ns.Cgroup @@ -564,48 +516,34 @@ func getNamespaceInfo(path string) (string, error) { } // getJSONOutput returns the container info in its raw form -func getJSONOutput(containers []*libpod.Container, nSpace bool) ([]psJSONParams, error) { - var psOutput []psJSONParams - var ns *namespace +func getJSONOutput(containers []*libpod.Container, opts psOptions) ([]psJSONParams, error) { + var ( + psOutput []psJSONParams + ns *namespace + ) for _, ctr := range containers { - pid, err := ctr.PID() - if err != nil { - return psOutput, errors.Wrapf(err, "unable to obtain container pid") - } - if nSpace { - ns = getNamespaces(pid) - } - cc := ctr.Config() - conState, err := ctr.State() + batchInfo, err := batchContainerOp(ctr, opts) if err != nil { - return psOutput, errors.Wrapf(err, "unable to obtain container state for JSON output") - } - rootFsSize, err := ctr.RootFsSize() - if err != nil { - logrus.Errorf("error getting root fs size for %q: %v", ctr.ID(), err) + return nil, err } - rwSize, err := ctr.RWSize() - if err != nil { - logrus.Errorf("error getting rw size for %q: %v", ctr.ID(), err) + if opts.namespace { + ns = getNamespaces(batchInfo.pid) } - params := psJSONParams{ - // TODO When we have ability to obtain the commented out data, we need - // TODO to add it - ID: ctr.ID(), - Image: cc.RootfsImageName, - ImageID: cc.RootfsImageID, - Command: ctr.Spec().Process.Args, - CreatedAt: cc.CreatedTime, - RunningFor: time.Since(cc.CreatedTime), - Status: conState.String(), - //Ports: cc.Spec.Linux.Resources.Network. - RootFsSize: rootFsSize, - RWSize: rwSize, - Names: cc.Name, - Labels: cc.Labels, - Mounts: cc.Spec.Mounts, - ContainerRunning: conState == libpod.ContainerStateRunning, + ID: ctr.ID(), + Image: batchInfo.conConfig.RootfsImageName, + ImageID: batchInfo.conConfig.RootfsImageID, + Command: batchInfo.conConfig.Spec.Process.Args, + CreatedAt: batchInfo.conConfig.CreatedTime, + RunningFor: time.Since(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.Spec.Mounts, + ContainerRunning: batchInfo.conState == libpod.ContainerStateRunning, Namespaces: ns, } psOutput = append(psOutput, params) @@ -621,7 +559,7 @@ func generatePsOutput(containers []*libpod.Container, opts psOptions) error { switch opts.format { case formats.JSONString: - psOutput, err := getJSONOutput(containers, opts.namespace) + psOutput, err := getJSONOutput(containers, opts) if err != nil { return errors.Wrapf(err, "unable to create JSON for output") } @@ -678,8 +616,8 @@ func getMounts(mounts []string, noTrunc bool) string { return strings.Join(arr, ",") } -// getPorts converts the ports used to a string of the from "port1, port2" -func getPorts(ports []ocicni.PortMapping) string { +// portsToString converts the ports used to a string of the from "port1, port2" +func portsToString(ports []ocicni.PortMapping) string { var portDisplay []string if len(ports) == 0 { return "" @@ -693,3 +631,77 @@ func getPorts(ports []ocicni.PortMapping) string { } return strings.Join(portDisplay, ", ") } + +type batchContainerStruct struct { + conConfig *libpod.ContainerConfig + conState libpod.ContainerStatus + exitCode int32 + pid int + rootFsSize, rwSize int64 + startedTime time.Time +} + +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 + ) + + batchErr := ctr.Batch(func(c *libpod.Container) error { + conConfig = c.Config() + conState, err = c.State() + if err != nil { + return errors.Wrapf(err, "unable to obtain container state") + } + + exitCode, err = c.ExitCode() + if err != nil { + return errors.Wrapf(err, "unable to obtain container exit code") + } + startedTime, err = c.StartedTime() + if err != nil { + logrus.Errorf("error getting started time for %q: %v", c.ID(), err) + } + + if !opts.size && !opts.namespace { + return nil + } + + if opts.namespace { + pid, err = c.PID() + if err != nil { + return errors.Wrapf(err, "unable to obtain container pid") + } + } + if opts.size { + rootFsSize, err = c.RootFsSize() + if err != nil { + logrus.Errorf("error getting root fs size for %q: %v", c.ID(), err) + } + + rwSize, err = c.RWSize() + if err != nil { + logrus.Errorf("error getting rw size for %q: %v", c.ID(), err) + } + + } + return nil + }) + if batchErr != nil { + return batchContainerStruct{}, batchErr + } + return batchContainerStruct{ + conConfig: conConfig, + conState: conState, + exitCode: exitCode, + pid: pid, + rootFsSize: rootFsSize, + rwSize: rwSize, + startedTime: startedTime, + }, nil +} -- cgit v1.2.3-54-g00ecf