summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbaude <bbaude@redhat.com>2019-03-17 08:40:22 -0500
committerbaude <bbaude@redhat.com>2019-03-28 08:34:27 -0500
commit3423c5a8c9fe4853880959a67073c697402648eb (patch)
treeae45a4f797e0f845223913115583d8f72f5e1510
parent850326cc192444d1c5cfd8ba6e1015f653b41e73 (diff)
downloadpodman-3423c5a8c9fe4853880959a67073c697402648eb.tar.gz
podman-3423c5a8c9fe4853880959a67073c697402648eb.tar.bz2
podman-3423c5a8c9fe4853880959a67073c697402648eb.zip
Add watch mode to podman ps
allows users to "watch" the output of podman ps on a set interval in seconds. in watch mode, the screen is cleared between intervals as well. podman -ps -w1 watches on 1 second intervals Signed-off-by: baude <bbaude@redhat.com>
-rw-r--r--cmd/podman/cliconfig/config.go1
-rw-r--r--cmd/podman/ps.go288
-rw-r--r--completions/bash/podman1
-rw-r--r--docs/podman-ps.1.md4
4 files changed, 166 insertions, 128 deletions
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index 884bd7fdb..f7ac0de6c 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -357,6 +357,7 @@ type PsValues struct {
Size bool
Sort string
Sync bool
+ Watch uint
}
type PullValues struct {
diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go
index 27774f95d..fe9efeba4 100644
--- a/cmd/podman/ps.go
+++ b/cmd/podman/ps.go
@@ -12,6 +12,7 @@ import (
"text/tabwriter"
"time"
+ tm "github.com/buger/goterm"
"github.com/containers/buildah/pkg/formats"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/libpodruntime"
@@ -21,7 +22,7 @@ import (
"github.com/containers/libpod/pkg/util"
"github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/go-units"
- opentracing "github.com/opentracing/opentracing-go"
+ "github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@@ -191,6 +192,7 @@ func psInit(command *cliconfig.PsValues) {
flags.BoolVarP(&command.Size, "size", "s", false, "Display the total file sizes")
flags.StringVar(&command.Sort, "sort", "created", "Sort output by command, created, id, image, names, runningfor, size, or status")
flags.BoolVar(&command.Sync, "sync", false, "Sync container state with OCI runtime")
+ flags.UintVarP(&command.Watch, "watch", "w", 0, "Watch the ps output on an interval in seconds")
markFlagHiddenForRemoteClient("latest", flags)
}
@@ -209,10 +211,15 @@ func psCmd(c *cliconfig.PsValues) error {
defer span.Finish()
}
- var (
- filterFuncs []libpod.ContainerFilter
- outputContainers []*libpod.Container
- )
+ var watch bool
+
+ if c.Watch > 0 {
+ watch = true
+ }
+
+ if c.Watch > 0 && c.Latest {
+ return errors.New("the watch and latest flags cannot be used together")
+ }
if err := checkFlagsPassed(c); err != nil {
return errors.Wrapf(err, "error with flags passed")
@@ -225,135 +232,24 @@ func psCmd(c *cliconfig.PsValues) error {
defer runtime.Shutdown(false)
- opts := shared.PsOptions{
- All: c.All,
- Format: c.Format,
- Last: c.Last,
- Latest: c.Latest,
- NoTrunc: c.NoTrunct,
- Pod: c.Pod,
- Quiet: c.Quiet,
- Size: c.Size,
- Namespace: c.Namespace,
- Sort: c.Sort,
- Sync: c.Sync,
- }
-
- filters := c.Filter
- if len(filters) > 0 {
- for _, f := range filters {
- filterSplit := strings.SplitN(f, "=", 2)
- if len(filterSplit) < 2 {
- return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
- }
- generatedFunc, err := generateContainerFilterFuncs(filterSplit[0], filterSplit[1], runtime)
- if err != nil {
- return errors.Wrapf(err, "invalid filter")
- }
- filterFuncs = append(filterFuncs, generatedFunc)
- }
- }
-
- if !opts.Latest {
- // Get all containers
- containers, err := runtime.GetContainers(filterFuncs...)
- if err != nil {
- return err
- }
-
- // We only want the last few containers
- if opts.Last > 0 && opts.Last <= len(containers) {
- return errors.Errorf("--last not yet supported")
- } else {
- outputContainers = containers
- }
- } else {
- // Get just the latest container
- // Ignore filters
- latestCtr, err := runtime.GetLatestContainer()
- if err != nil {
- return err
- }
-
- outputContainers = []*libpod.Container{latestCtr}
- }
-
- maxWorkers := shared.Parallelize("ps")
- if c.GlobalIsSet("max-workers") {
- maxWorkers = c.GlobalFlags.MaxWorks
- }
- logrus.Debugf("Setting maximum workers to %d", maxWorkers)
-
- pss := shared.PBatch(outputContainers, maxWorkers, opts)
- if opts.Sort != "" {
- pss, err = sortPsOutput(opts.Sort, pss)
- if err != nil {
+ if !watch {
+ if err := psDisplay(c, runtime); err != nil {
return err
}
- }
-
- // If quiet, print only cids and return
- if opts.Quiet {
- return printQuiet(pss)
- }
-
- // If the user wants their own GO template format
- if opts.Format != "" {
- if opts.Format == "json" {
- return dumpJSON(pss)
- }
- return printFormat(opts.Format, pss)
- }
-
- // Define a tab writer with stdout as the output
- w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
- defer w.Flush()
-
- // Output standard PS headers
- if !opts.Namespace {
- fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s", hid, himage, hcommand, hcreated, hstatus, hports, hnames)
- // User wants pod info
- if opts.Pod {
- fmt.Fprintf(w, "\t%s", hpod)
- }
- //User wants size info
- if opts.Size {
- fmt.Fprintf(w, "\t%s", hsize)
- }
} else {
- // Output Namespace headers
- fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s", hid, hnames, nspid, nscgroup, nsipc, nsmnt, nsnet, nspidns, nsuserns, nsuts)
- }
-
- // Now iterate each container and output its information
- for _, container := range pss {
-
- // Standard PS output
- if !opts.Namespace {
- fmt.Fprintf(w, "\n%s\t%s\t%s\t%s\t%s\t%s\t%s", container.ID, container.Image, container.Command, container.Created, container.Status, container.Ports, container.Names)
- // User wants pod info
- if opts.Pod {
- fmt.Fprintf(w, "\t%s", container.Pod)
+ for {
+ tm.Clear()
+ tm.MoveCursor(1, 1)
+ tm.Flush()
+ if err := psDisplay(c, runtime); err != nil {
+ return err
}
- //User wants size info
- if opts.Size {
- var size string
- if container.Size == nil {
- size = units.HumanSizeWithPrecision(0, 0)
- } else {
- size = units.HumanSizeWithPrecision(float64(container.Size.RwSize), 3) + " (virtual " + units.HumanSizeWithPrecision(float64(container.Size.RootFsSize), 3) + ")"
- fmt.Fprintf(w, "\t%s", size)
- }
- }
-
- } else {
- // Print namespace information
- ns := shared.GetNamespaces(container.Pid)
- fmt.Fprintf(w, "\n%s\t%s\t%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s", container.ID, container.Names, container.Pid, ns.Cgroup, ns.IPC, ns.MNT, ns.NET, ns.PIDNS, ns.User, ns.UTS)
+ time.Sleep(time.Duration(c.Watch) * time.Second)
+ tm.Clear()
+ tm.MoveCursor(1, 1)
+ tm.Flush()
}
-
}
- fmt.Fprint(w, "\n")
return nil
}
@@ -654,3 +550,139 @@ func dumpJSON(containers []shared.PsContainerOutput) error {
os.Stdout.Write(b)
return nil
}
+
+func psDisplay(c *cliconfig.PsValues, runtime *libpod.Runtime) error {
+ var (
+ filterFuncs []libpod.ContainerFilter
+ outputContainers []*libpod.Container
+ err error
+ )
+ opts := shared.PsOptions{
+ All: c.All,
+ Format: c.Format,
+ Last: c.Last,
+ Latest: c.Latest,
+ NoTrunc: c.NoTrunct,
+ Pod: c.Pod,
+ Quiet: c.Quiet,
+ Size: c.Size,
+ Namespace: c.Namespace,
+ Sort: c.Sort,
+ Sync: c.Sync,
+ }
+
+ maxWorkers := shared.Parallelize("ps")
+ if c.GlobalIsSet("max-workers") {
+ maxWorkers = c.GlobalFlags.MaxWorks
+ }
+ logrus.Debugf("Setting maximum workers to %d", maxWorkers)
+
+ filters := c.Filter
+ if len(filters) > 0 {
+ for _, f := range filters {
+ filterSplit := strings.SplitN(f, "=", 2)
+ if len(filterSplit) < 2 {
+ return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
+ }
+ generatedFunc, err := generateContainerFilterFuncs(filterSplit[0], filterSplit[1], runtime)
+ if err != nil {
+ return errors.Wrapf(err, "invalid filter")
+ }
+ filterFuncs = append(filterFuncs, generatedFunc)
+ }
+ }
+ if !opts.Latest {
+ // Get all containers
+ containers, err := runtime.GetContainers(filterFuncs...)
+ if err != nil {
+ return err
+ }
+
+ // We only want the last few containers
+ if opts.Last > 0 && opts.Last <= len(containers) {
+ return errors.Errorf("--last not yet supported")
+ } else {
+ outputContainers = containers
+ }
+ } else {
+ // Get just the latest container
+ // Ignore filters
+ latestCtr, err := runtime.GetLatestContainer()
+ if err != nil {
+ return err
+ }
+
+ outputContainers = []*libpod.Container{latestCtr}
+ }
+
+ pss := shared.PBatch(outputContainers, maxWorkers, opts)
+ if opts.Sort != "" {
+ pss, err = sortPsOutput(opts.Sort, pss)
+ if err != nil {
+ return err
+ }
+ }
+
+ // If quiet, print only cids and return
+ if opts.Quiet {
+ return printQuiet(pss)
+ }
+
+ // If the user wants their own GO template format
+ if opts.Format != "" {
+ if opts.Format == "json" {
+ return dumpJSON(pss)
+ }
+ return printFormat(opts.Format, pss)
+ }
+
+ // Define a tab writer with stdout as the output
+ w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
+
+ // Output standard PS headers
+ if !opts.Namespace {
+ fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s", hid, himage, hcommand, hcreated, hstatus, hports, hnames)
+ // User wants pod info
+ if opts.Pod {
+ fmt.Fprintf(w, "\t%s", hpod)
+ }
+ //User wants size info
+ if opts.Size {
+ fmt.Fprintf(w, "\t%s", hsize)
+ }
+ } else {
+ // Output Namespace headers
+ fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s", hid, hnames, nspid, nscgroup, nsipc, nsmnt, nsnet, nspidns, nsuserns, nsuts)
+ }
+
+ // Now iterate each container and output its information
+ for _, container := range pss {
+
+ // Standard PS output
+ if !opts.Namespace {
+ fmt.Fprintf(w, "\n%s\t%s\t%s\t%s\t%s\t%s\t%s", container.ID, container.Image, container.Command, container.Created, container.Status, container.Ports, container.Names)
+ // User wants pod info
+ if opts.Pod {
+ fmt.Fprintf(w, "\t%s", container.Pod)
+ }
+ //User wants size info
+ if opts.Size {
+ var size string
+ if container.Size == nil {
+ size = units.HumanSizeWithPrecision(0, 0)
+ } else {
+ size = units.HumanSizeWithPrecision(float64(container.Size.RwSize), 3) + " (virtual " + units.HumanSizeWithPrecision(float64(container.Size.RootFsSize), 3) + ")"
+ fmt.Fprintf(w, "\t%s", size)
+ }
+ }
+
+ } else {
+ // Print namespace information
+ ns := shared.GetNamespaces(container.Pid)
+ fmt.Fprintf(w, "\n%s\t%s\t%d\t%s\t%s\t%s\t%s\t%s\t%s\t%s", container.ID, container.Names, container.Pid, ns.Cgroup, ns.IPC, ns.MNT, ns.NET, ns.PIDNS, ns.User, ns.UTS)
+ }
+
+ }
+ fmt.Fprint(w, "\n")
+ return w.Flush()
+}
diff --git a/completions/bash/podman b/completions/bash/podman
index 798d49ceb..a3f381962 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -2205,6 +2205,7 @@ _podman_ps() {
--format
--last -n
--sort
+ --watch -w
"
local boolean_options="
--all -a
diff --git a/docs/podman-ps.1.md b/docs/podman-ps.1.md
index 685a52bda..a4a521b39 100644
--- a/docs/podman-ps.1.md
+++ b/docs/podman-ps.1.md
@@ -113,6 +113,10 @@ In some cases, a container's state in the runtime can become out of sync with Po
This will update Podman's state based on what the OCI runtime reports.
Forcibly syncing is much slower, but can resolve inconsistent state issues.
+**--watch, -w**
+
+Refresh the output with current containers on an interval in seconds.
+
## EXAMPLES
```