top: make output tabular

Make the output of top tabular to be compatible with Docker.  Please
note, that any user-input for `GetContainerPidInformation(...)` will be
ignored until we have found a way to generically and reliably parse ps-1
output or until there is a go-lib to extract all the data from /proc in
a ps-1 compatible fashion.

Fixes: #458
Signed-off-by: Valentin Rothberg <vrothberg@suse.com>

Closes: #939
Approved by: rhatdan
This commit is contained in:
Valentin Rothberg
2018-06-13 08:59:59 +02:00
committed by Atomic Bot
parent 9e134576e8
commit b1e709806d
3 changed files with 38 additions and 13 deletions

View File

@ -2,6 +2,8 @@ package main
import (
"fmt"
"os"
"text/tabwriter"
"github.com/pkg/errors"
"github.com/projectatomic/libpod/cmd/podman/libpodruntime"
@ -34,8 +36,7 @@ func topCmd(c *cli.Context) error {
var container *libpod.Container
var err error
args := c.Args()
var psArgs []string
psOpts := []string{"-o", "uid,pid,ppid,c,stime,tname,time,cmd"}
if len(args) < 1 && !c.Bool("latest") {
return errors.Errorf("you must provide the name or id of a running container")
}
@ -48,9 +49,6 @@ func topCmd(c *cli.Context) error {
return errors.Wrapf(err, "error creating libpod runtime")
}
defer runtime.Shutdown(false)
if len(args) > 1 {
psOpts = args[1:]
}
if c.Bool("latest") {
container, err = runtime.GetLatestContainer()
@ -69,14 +67,15 @@ func topCmd(c *cli.Context) error {
return errors.Errorf("top can only be used on running containers")
}
psArgs = append(psArgs, psOpts...)
psOutput, err := container.GetContainerPidInformation(psArgs)
psOutput, err := container.GetContainerPidInformation([]string{})
if err != nil {
return err
}
for _, line := range psOutput {
fmt.Println(line)
w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
for _, proc := range psOutput {
fmt.Fprintln(w, proc)
}
w.Flush()
return nil
}

View File

@ -44,8 +44,20 @@ func (c *Container) getContainerPids() ([]string, error) {
}
// GetContainerPidInformation calls ps with the appropriate options and returns
// the results as a string and the container's PIDs as a []string
// the results as a string and the container's PIDs as a []string. Note that
// the ps output columns of each string are separated by a '\t\'. Currently,
// the args argument is overwriten with []string{"-ef"} until there is a
// portable library for ps-1 or to parse the procFS to extract all data.
func (c *Container) GetContainerPidInformation(args []string) ([]string, error) {
// XXX(ps-issue): args is overwriten with []{"-ef"} as the ps-1 tool
// doesn't support a generic way of splitting columns, rendering its
// output hard to parse. Docker first deterimes the number of columns
// and merges all exceeding ones into the last one. We believe that
// writing a go library which parses procFS in a ps-compatible way may
// be more beneficial in the long run. Until then, args will be
// ignored.
args = []string{"-ef"}
if !c.batched {
c.lock.Lock()
defer c.lock.Unlock()
@ -80,7 +92,7 @@ func filterPids(psOutput string, pids []string) ([]string, error) {
// Pids that don't belong.
// append the headers back in
output = append(output, results[0])
output = append(output, strings.Join(headers, "\t"))
pidIndex := -1
for i, header := range headers {
@ -98,7 +110,13 @@ func filterPids(psOutput string, pids []string) ([]string, error) {
cols := fieldsASCII(l)
pid := cols[pidIndex]
if util.StringInSlice(pid, pids) {
output = append(output, l)
// XXX(ps-issue): Strip cols down to the header's size
// and merge exceeding fields. This is required to
// "merge" the overhanging CMD entries which can
// contain white spaces.
out := cols[:len(headers)-1]
out = append(out, strings.Join(cols[len(headers)-1:], " "))
output = append(output, strings.Join(out, "\t"))
}
}
return output, nil

View File

@ -59,7 +59,14 @@ var _ = Describe("Podman top", func() {
Expect(len(result.OutputToStringArray())).To(BeNumerically(">", 1))
})
// XXX(ps-issue): for the time being, podman-top and the libpod API
// GetContainerPidInformation(...) will ignore any arguments passed to ps,
// so we have to disable the tests below. Please refer to
// https://github.com/projectatomic/libpod/pull/939 for more background
// information.
It("podman top with options", func() {
Skip("podman-top with options: options are temporarily ignored")
session := podmanTest.Podman([]string{"run", "-d", ALPINE, "top", "-d", "2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@ -71,6 +78,7 @@ var _ = Describe("Podman top", func() {
})
It("podman top on container invalid options", func() {
Skip("podman-top with invalid options: options are temporarily ignored")
top := podmanTest.RunTopContainer("")
top.WaitWithDefaultTimeout()
Expect(top.ExitCode()).To(Equal(0))