From 596bdc68831fd26183c87db1784ce034deee5bb8 Mon Sep 17 00:00:00 2001 From: Jeff Mickey Date: Wed, 21 Jan 2015 15:52:22 -0800 Subject: [PATCH] docker ps: add fields for ordering and column selection * api/client/ps.go: Refactor CmdPs to use a fields list of characters to determine which columns to print on `docker ps` invocation. This adds an ability for the docker command to print the columns of output in arbitrary order. Signed-off-by: Jeff Mickey Docker-DCO-1.1-Signed-off-by: Jeff Mickey Upstream-commit: de0e883331c85cc7617184900369e1ec9f723b6c Component: engine --- components/engine/api/client/ps.go | 140 +++++++++++++++++++---------- 1 file changed, 95 insertions(+), 45 deletions(-) diff --git a/components/engine/api/client/ps.go b/components/engine/api/client/ps.go index 1a96c52d3d..c95d473b85 100644 --- a/components/engine/api/client/ps.go +++ b/components/engine/api/client/ps.go @@ -38,6 +38,7 @@ func (cli *DockerCli) CmdPs(args ...string) error { since = cmd.String([]string{"#sinceId", "#-since-id", "-since"}, "", "Show created since Id or Name, include non-running") before = cmd.String([]string{"#beforeId", "#-before-id", "-before"}, "", "Show only container created before Id or Name") last = cmd.Int([]string{"n"}, -1, "Show n last created containers, include non-running") + fields = cmd.String([]string{"-fields"}, "cimtspn", "Choose fields to print, and order (c,i,m,t,s,p,n,z)") flFilter = opts.NewListOpts(nil) ) cmd.Require(flag.Exact, 0) @@ -99,13 +100,35 @@ func (cli *DockerCli) CmdPs(args ...string) error { } w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) - if !*quiet { - fmt.Fprint(w, "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES") + if *quiet { + *fields = "c" + } - if *size { - fmt.Fprintln(w, "\tSIZE") - } else { - fmt.Fprint(w, "\n") + if *size { + *fields = *fields + "z" + } + + if !*quiet { + headermap := map[rune]string{ + 'c': "CONTAINER ID", + 'i': "IMAGE", + 'm': "COMMAND", + 's': "STATUS", + 't': "CREATED", + 'p': "PORTS", + 'n': "NAMES", + 'z': "SIZE", + } + + headers := make([]string, 0) + for _, v := range *fields { + if title, ok := headermap[v]; ok { + headers = append(headers, title) + } + } + + if len(headers) > 0 { + fmt.Fprint(w, strings.Join(headers, "\t")+"\n") } } @@ -117,63 +140,90 @@ func (cli *DockerCli) CmdPs(args ...string) error { return ss } + type containerMeta struct { + c string + i string + m string + t string + s string + p string + n string + z string + } + + var displayPort string + if container.HostConfig.NetworkMode == "host" { + displayPort = "*/tcp, */udp" + } else { + displayPort = api.DisplayablePorts(container.Ports) + } + + outp := make([]containerMeta, 0) for _, container := range containers { - ID := container.ID - - if !*noTrunc { - ID = stringid.TruncateID(ID) + next := containerMeta{ + c: container.ID, + n: "", + m: strconv.Quote(container.Command), + i: container.Image, + t: units.HumanDuration(time.Now().UTC().Sub(time.Unix(int64(container.Created), 0))) + " ago", + s: container.Status, + p: displayPort, + z: fmt.Sprintf("%s", units.HumanSize(float64(container.SizeRw))), } - if *quiet { - fmt.Fprintln(w, ID) - - continue - } - - var ( - names = stripNamePrefix(container.Names) - command = strconv.Quote(container.Command) - displayPort string - ) - + // handle truncation + outNames := stripNamePrefix(container.Names) if !*noTrunc { - command = stringutils.Truncate(command, 20) - + next.c = stringid.TruncateID(next.c) + next.m = stringutils.Truncate(next.m, 20) // only display the default name for the container with notrunc is passed - for _, name := range names { + for _, name := range outNames { if len(strings.Split(name, "/")) == 1 { - names = []string{name} + outNames = []string{name} break } } } + next.n = strings.Join(outNames, ",") - image := container.Image - if image == "" { - image = "" + if next.i == "" { + next.i = "" } - if container.HostConfig.NetworkMode == "host" { - displayPort = "*/tcp, */udp" - } else { - displayPort = api.DisplayablePorts(container.Ports) + // handle rootfs sizing + if container.SizeRootFs > 0 { + next.z = next.z + fmt.Sprintf(" (virtual %s)", units.HumanSize(float64(container.SizeRootFs))) } - fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", ID, image, command, - units.HumanDuration(time.Now().UTC().Sub(time.Unix(int64(container.Created), 0))), - container.Status, displayPort, strings.Join(names, ",")) + outp = append(outp, next) + } + + for _, out := range outp { + of := make([]string, 0) + for _, v := range *fields { + switch v { + case 'c': + of = append(of, out.c) + case 'i': + of = append(of, out.i) + case 'm': + of = append(of, out.m) + case 't': + of = append(of, out.t) + case 's': + of = append(of, out.s) + case 'p': + of = append(of, out.p) + case 'n': + of = append(of, out.n) + case 'z': + of = append(of, out.z) - if *size { - if container.SizeRootFs > 0 { - fmt.Fprintf(w, "%s (virtual %s)\n", units.HumanSize(float64(container.SizeRw)), units.HumanSize(float64(container.SizeRootFs))) - } else { - fmt.Fprintf(w, "%s\n", units.HumanSize(float64(container.SizeRw))) } - - continue } - - fmt.Fprint(w, "\n") + if len(of) > 0 { + fmt.Fprintf(w, "%s\n", strings.Join(of, "\t")) + } } if !*quiet {