diff options
author | OpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com> | 2020-04-09 15:56:26 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-09 15:56:26 +0200 |
commit | 1662310e516cb3c21842f59b8f607bb0af408ec5 (patch) | |
tree | 884889920008afc6f688e4f3d5ceec7f9025139b /pkg | |
parent | 555b30e9ebdf82dfc2e0c7ddd9f0f8b51ccb4a39 (diff) | |
parent | 8ce23775a62b816c599e6d65c92985f62acc85c5 (diff) | |
download | podman-1662310e516cb3c21842f59b8f607bb0af408ec5.tar.gz podman-1662310e516cb3c21842f59b8f607bb0af408ec5.tar.bz2 podman-1662310e516cb3c21842f59b8f607bb0af408ec5.zip |
Merge pull request #5754 from baude/v2psformat
v2podman ps alter formats
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/domain/entities/container_ps.go | 171 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/containers.go | 4 | ||||
-rw-r--r-- | pkg/domain/infra/tunnel/helpers.go | 2 | ||||
-rw-r--r-- | pkg/ps/ps.go | 34 |
4 files changed, 181 insertions, 30 deletions
diff --git a/pkg/domain/entities/container_ps.go b/pkg/domain/entities/container_ps.go index ceafecebc..8a8666ff9 100644 --- a/pkg/domain/entities/container_ps.go +++ b/pkg/domain/entities/container_ps.go @@ -1,19 +1,23 @@ package entities import ( + "fmt" "sort" + "strconv" "strings" + "time" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" "github.com/cri-o/ocicni/pkg/ocicni" + "github.com/docker/go-units" "github.com/pkg/errors" ) // Listcontainer describes a container suitable for listing type ListContainer struct { // Container command - Command []string + Cmd []string // Container creation time Created int64 // If container has exited/stopped @@ -33,7 +37,7 @@ type ListContainer struct { // User volume mounts Mounts []string // The names assigned to the container - Names []string + ContainerNames []string // Namespaces the container belongs to. Requires the // namespace boolean to be true Namespaces ListContainerNamespaces @@ -46,13 +50,69 @@ type ListContainer struct { // boolean to be set PodName string // Port mappings - Ports []ocicni.PortMapping + PortMappings []ocicni.PortMapping // Size of the container rootfs. Requires the size boolean to be true - Size *shared.ContainerSize + ContainerSize *shared.ContainerSize // Time when container started StartedAt int64 // State of container - State string + ContainerState string +} + +// State returns the container state in human duration +func (l ListContainer) State() string { + var state string + switch l.ContainerState { + case "running": + t := units.HumanDuration(time.Since(time.Unix(l.StartedAt, 0))) + state = "Up " + t + " ago" + case "configured": + state = "Created" + case "exited": + t := units.HumanDuration(time.Since(time.Unix(l.ExitedAt, 0))) + state = fmt.Sprintf("Exited (%d) %s ago", l.ExitCode, t) + default: + state = l.ContainerState + } + return state +} + +// Command returns the container command in string format +func (l ListContainer) Command() string { + return strings.Join(l.Cmd, " ") +} + +// Size returns the rootfs and virtual sizes in human duration in +// and output form (string) suitable for ps +func (l ListContainer) Size() string { + virt := units.HumanSizeWithPrecision(float64(l.ContainerSize.RootFsSize), 3) + s := units.HumanSizeWithPrecision(float64(l.ContainerSize.RwSize), 3) + return fmt.Sprintf("%s (virtual %s)", s, virt) +} + +// Names returns the container name in string format +func (l ListContainer) Names() string { + return l.ContainerNames[0] +} + +// Ports converts from Portmappings to the string form +// required by ps +func (l ListContainer) Ports() string { + if len(l.PortMappings) < 1 { + return "" + } + return portsToString(l.PortMappings) +} + +// CreatedAt returns the container creation time in string format. podman +// and docker both return a timestamped value for createdat +func (l ListContainer) CreatedAt() string { + return time.Unix(l.Created, 0).String() +} + +// CreateHuman allows us to output the created time in human readable format +func (l ListContainer) CreatedHuman() string { + return units.HumanDuration(time.Since(time.Unix(l.Created, 0))) + " ago" } // ListContainer Namespaces contains the identifiers of the container's Linux namespaces @@ -93,7 +153,7 @@ func (a SortListContainers) Swap(i, j int) { a[i], a[j] = a[j], a[i] } type psSortedCommand struct{ SortListContainers } func (a psSortedCommand) Less(i, j int) bool { - return strings.Join(a.SortListContainers[i].Command, " ") < strings.Join(a.SortListContainers[j].Command, " ") + return strings.Join(a.SortListContainers[i].Cmd, " ") < strings.Join(a.SortListContainers[j].Cmd, " ") } type psSortedId struct{ SortListContainers } @@ -111,7 +171,7 @@ func (a psSortedImage) Less(i, j int) bool { type psSortedNames struct{ SortListContainers } func (a psSortedNames) Less(i, j int) bool { - return a.SortListContainers[i].Names[0] < a.SortListContainers[j].Names[0] + return a.SortListContainers[i].ContainerNames[0] < a.SortListContainers[j].ContainerNames[0] } type psSortedPod struct{ SortListContainers } @@ -129,16 +189,16 @@ func (a psSortedRunningFor) Less(i, j int) bool { type psSortedStatus struct{ SortListContainers } func (a psSortedStatus) Less(i, j int) bool { - return a.SortListContainers[i].State < a.SortListContainers[j].State + return a.SortListContainers[i].ContainerState < a.SortListContainers[j].ContainerState } type psSortedSize struct{ SortListContainers } func (a psSortedSize) Less(i, j int) bool { - if a.SortListContainers[i].Size == nil || a.SortListContainers[j].Size == nil { + if a.SortListContainers[i].ContainerSize == nil || a.SortListContainers[j].ContainerSize == nil { return false } - return a.SortListContainers[i].Size.RootFsSize < a.SortListContainers[j].Size.RootFsSize + return a.SortListContainers[i].ContainerSize.RootFsSize < a.SortListContainers[j].ContainerSize.RootFsSize } type PsSortedCreateTime struct{ SortListContainers } @@ -172,3 +232,94 @@ func SortPsOutput(sortBy string, psOutput SortListContainers) (SortListContainer } return psOutput, nil } + +// portsToString converts the ports used to a string of the from "port1, port2" +// and also groups a continuous list of ports into a readable format. +func portsToString(ports []ocicni.PortMapping) string { + type portGroup struct { + first int32 + last int32 + } + var portDisplay []string + if len(ports) == 0 { + return "" + } + //Sort the ports, so grouping continuous ports become easy. + sort.Slice(ports, func(i, j int) bool { + return comparePorts(ports[i], ports[j]) + }) + + // portGroupMap is used for grouping continuous ports. + portGroupMap := make(map[string]*portGroup) + var groupKeyList []string + + for _, v := range ports { + + hostIP := v.HostIP + if hostIP == "" { + hostIP = "0.0.0.0" + } + // If hostPort and containerPort are not same, consider as individual port. + if v.ContainerPort != v.HostPort { + portDisplay = append(portDisplay, fmt.Sprintf("%s:%d->%d/%s", hostIP, v.HostPort, v.ContainerPort, v.Protocol)) + continue + } + + portMapKey := fmt.Sprintf("%s/%s", hostIP, v.Protocol) + + portgroup, ok := portGroupMap[portMapKey] + if !ok { + portGroupMap[portMapKey] = &portGroup{first: v.ContainerPort, last: v.ContainerPort} + // This list is required to traverse portGroupMap. + groupKeyList = append(groupKeyList, portMapKey) + continue + } + + if portgroup.last == (v.ContainerPort - 1) { + portgroup.last = v.ContainerPort + continue + } + } + // For each portMapKey, format group list and appned to output string. + for _, portKey := range groupKeyList { + group := portGroupMap[portKey] + portDisplay = append(portDisplay, formatGroup(portKey, group.first, group.last)) + } + return strings.Join(portDisplay, ", ") +} + +func comparePorts(i, j ocicni.PortMapping) bool { + if i.ContainerPort != j.ContainerPort { + return i.ContainerPort < j.ContainerPort + } + + if i.HostIP != j.HostIP { + return i.HostIP < j.HostIP + } + + if i.HostPort != j.HostPort { + return i.HostPort < j.HostPort + } + + return i.Protocol < j.Protocol +} + +// formatGroup returns the group as <IP:startPort:lastPort->startPort:lastPort/Proto> +// e.g 0.0.0.0:1000-1006->1000-1006/tcp. +func formatGroup(key string, start, last int32) string { + parts := strings.Split(key, "/") + groupType := parts[0] + var ip string + if len(parts) > 1 { + ip = parts[0] + groupType = parts[1] + } + group := strconv.Itoa(int(start)) + if start != last { + group = fmt.Sprintf("%s-%d", group, last) + } + if ip != "" { + group = fmt.Sprintf("%s:%s->%s", ip, group, group) + } + return fmt.Sprintf("%s/%s", group, groupType) +} diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index d40f0da36..f091e1527 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -242,7 +242,7 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ } // narrow the list to running only for _, c := range allCtrs { - if c.State == define.ContainerStateRunning.String() { + if c.ContainerState == define.ContainerStateRunning.String() { ctrs = append(ctrs, c) } } @@ -276,7 +276,7 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st } // narrow the list to exited only for _, c := range allCtrs { - if c.State == define.ContainerStateExited.String() { + if c.ContainerState == define.ContainerStateExited.String() { ctrs = append(ctrs, c) } } diff --git a/pkg/domain/infra/tunnel/helpers.go b/pkg/domain/infra/tunnel/helpers.go index 682d60d6a..4d7e45897 100644 --- a/pkg/domain/infra/tunnel/helpers.go +++ b/pkg/domain/infra/tunnel/helpers.go @@ -30,7 +30,7 @@ func getContainersByContext(contextWithConnection context.Context, all bool, nam for _, id := range namesOrIds { var found bool for _, con := range c { - if id == con.ID || strings.HasPrefix(con.ID, id) || util.StringInSlice(id, con.Names) { + if id == con.ID || strings.HasPrefix(con.ID, id) || util.StringInSlice(id, con.ContainerNames) { cons = append(cons, con) found = true break diff --git a/pkg/ps/ps.go b/pkg/ps/ps.go index 58fcc2c21..9217fa595 100644 --- a/pkg/ps/ps.go +++ b/pkg/ps/ps.go @@ -148,23 +148,23 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities } ps := entities.ListContainer{ - Command: conConfig.Command, - Created: conConfig.CreatedTime.Unix(), - Exited: exited, - ExitCode: exitCode, - ExitedAt: exitedTime.Unix(), - ID: conConfig.ID, - Image: conConfig.RootfsImageName, - IsInfra: conConfig.IsInfra, - Labels: conConfig.Labels, - Mounts: ctr.UserVolumes(), - Names: []string{conConfig.Name}, - Pid: pid, - Pod: conConfig.Pod, - Ports: conConfig.PortMappings, - Size: size, - StartedAt: startedTime.Unix(), - State: conState.String(), + Cmd: conConfig.Command, + Created: conConfig.CreatedTime.Unix(), + Exited: exited, + ExitCode: exitCode, + ExitedAt: exitedTime.Unix(), + ID: conConfig.ID, + Image: conConfig.RootfsImageName, + IsInfra: conConfig.IsInfra, + Labels: conConfig.Labels, + Mounts: ctr.UserVolumes(), + ContainerNames: []string{conConfig.Name}, + Pid: pid, + Pod: conConfig.Pod, + PortMappings: conConfig.PortMappings, + ContainerSize: size, + StartedAt: startedTime.Unix(), + ContainerState: conState.String(), } if opts.Pod && len(conConfig.Pod) > 0 { pod, err := rt.GetPod(conConfig.Pod) |