diff --git a/cmd/podman/top.go b/cmd/podman/top.go index 6aff25a710..5ff3b6643c 100644 --- a/cmd/podman/top.go +++ b/cmd/podman/top.go @@ -70,11 +70,11 @@ func topCmd(c *cli.Context) error { psArgs = append(psArgs, psOpts...) - results, err := container.GetContainerPidInformation(psArgs) + psOutput, err := container.GetContainerPidInformation(psArgs) if err != nil { return err } - for _, line := range results { + for _, line := range psOutput { fmt.Println(line) } return nil diff --git a/libpod/container_top.go b/libpod/container_top.go index 3eb484acd5..241e3a3e72 100644 --- a/libpod/container_top.go +++ b/libpod/container_top.go @@ -38,7 +38,7 @@ func (c *Container) getContainerPids() ([]string, error) { } // GetContainerPidInformation calls ps with the appropriate options and returns -// the results as a string +// the results as a string and the container's PIDs as a []string func (c *Container) GetContainerPidInformation(args []string) ([]string, error) { if !c.locked { c.lock.Lock() @@ -57,5 +57,55 @@ func (c *Container) GetContainerPidInformation(args []string) ([]string, error) if err != nil { return []string{}, errors.Wrapf(err, "unable to obtain information about pids") } - return strings.Split(results, "\n"), nil + + filteredOutput, err := filterPids(results, pids) + if err != nil { + return []string{}, err + } + return filteredOutput, nil +} + +func filterPids(psOutput string, pids []string) ([]string, error) { + var output []string + results := strings.Split(psOutput, "\n") + // The headers are in the first line of the results + headers := fieldsASCII(results[0]) + // We need to make sure PID in headers, so that we can filter + // Pids that don't belong. + + // append the headers back in + output = append(output, results[0]) + + pidIndex := -1 + for i, header := range headers { + if header == "PID" { + pidIndex = i + } + } + if pidIndex == -1 { + return []string{}, errors.Errorf("unable to find PID field in ps output. try a different set of ps arguments") + } + for _, l := range results[1:] { + if l == "" { + continue + } + cols := fieldsASCII(l) + pid := cols[pidIndex] + if StringInSlice(pid, pids) { + output = append(output, l) + } + } + return output, nil +} + +// Detects ascii whitespaces +func fieldsASCII(s string) []string { + fn := func(r rune) bool { + switch r { + case '\t', '\n', '\f', '\r', ' ': + return true + } + return false + } + return strings.FieldsFunc(s, fn) } diff --git a/test/e2e/top_test.go b/test/e2e/top_test.go index d4438293bb..410803353c 100644 --- a/test/e2e/top_test.go +++ b/test/e2e/top_test.go @@ -59,14 +59,26 @@ var _ = Describe("Podman top", func() { Expect(len(result.OutputToStringArray())).To(BeNumerically(">", 1)) }) - It("podman top on non-running container", func() { + It("podman top with options", func() { session := podmanTest.Podman([]string{"run", "-d", ALPINE, "top", "-d", "2"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - result := podmanTest.Podman([]string{"top", session.OutputToString(), "-o", "fuser,f,comm,label"}) + result := podmanTest.Podman([]string{"top", session.OutputToString(), "-o", "pid,fuser,f,comm,label"}) result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(0)) Expect(len(result.OutputToStringArray())).To(BeNumerically(">", 1)) }) + + It("podman top on container invalid options", func() { + sleep := podmanTest.RunSleepContainer("") + sleep.WaitWithDefaultTimeout() + Expect(sleep.ExitCode()).To(Equal(0)) + cid := sleep.OutputToString() + + result := podmanTest.Podman([]string{"top", cid, "-o time"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(125)) + }) + })