ps --format: Add config.js doc, fix gofmt, add integration tests

Re-add the docs from @calavera's PR to the moved cli cmd reference docs.
Fix gofmt and vet issues from carried commits
Add integration test for using format with --no-trunc and multi-names
Fix custom_test map order dependency on expected value check
Add docs to reference/commandline/ps.md
Remove "-F" flag option from original carried PR content

Docker-DCO-1.1-Signed-off-by: Phil Estes <estesp@linux.vnet.ibm.com> (github: estesp)
This commit is contained in:
Phil Estes 2015-07-17 00:03:16 -04:00
parent 37209190c7
commit 542b58d8f7
7 changed files with 111 additions and 14 deletions

View File

@ -31,7 +31,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") 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") 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") last = cmd.Int([]string{"n"}, -1, "Show n last created containers, include non-running")
format = cmd.String([]string{"F", "-format"}, "", "Pretty-print containers using a Go template") format = cmd.String([]string{"-format"}, "", "Pretty-print containers using a Go template")
flFilter = opts.NewListOpts(nil) flFilter = opts.NewListOpts(nil)
) )
cmd.Require(flag.Exact, 0) cmd.Require(flag.Exact, 0)

View File

@ -1,6 +1,8 @@
package ps package ps
import ( import (
"reflect"
"strings"
"testing" "testing"
"time" "time"
@ -26,7 +28,7 @@ func TestContainerContextID(t *testing.T) {
{types.Container{Image: ""}, true, "<no image>", imageHeader, ctx.Image}, {types.Container{Image: ""}, true, "<no image>", imageHeader, ctx.Image},
{types.Container{Command: "sh -c 'ls -la'"}, true, `"sh -c 'ls -la'"`, commandHeader, ctx.Command}, {types.Container{Command: "sh -c 'ls -la'"}, true, `"sh -c 'ls -la'"`, commandHeader, ctx.Command},
{types.Container{Created: int(unix)}, true, time.Unix(unix, 0).String(), createdAtHeader, ctx.CreatedAt}, {types.Container{Created: int(unix)}, true, time.Unix(unix, 0).String(), createdAtHeader, ctx.CreatedAt},
{types.Container{Ports: []types.Port{types.Port{PrivatePort: 8080, PublicPort: 8080, Type: "tcp"}}}, true, "8080/tcp", portsHeader, ctx.Ports}, {types.Container{Ports: []types.Port{{PrivatePort: 8080, PublicPort: 8080, Type: "tcp"}}}, true, "8080/tcp", portsHeader, ctx.Ports},
{types.Container{Status: "RUNNING"}, true, "RUNNING", statusHeader, ctx.Status}, {types.Container{Status: "RUNNING"}, true, "RUNNING", statusHeader, ctx.Status},
{types.Container{SizeRw: 10}, true, "10 B", sizeHeader, ctx.Size}, {types.Container{SizeRw: 10}, true, "10 B", sizeHeader, ctx.Size},
{types.Container{SizeRw: 10, SizeRootFs: 20}, true, "10 B (virtual 20 B)", sizeHeader, ctx.Size}, {types.Container{SizeRw: 10, SizeRootFs: 20}, true, "10 B (virtual 20 B)", sizeHeader, ctx.Size},
@ -36,7 +38,26 @@ func TestContainerContextID(t *testing.T) {
for _, c := range cases { for _, c := range cases {
ctx = containerContext{c: c.container, trunc: c.trunc} ctx = containerContext{c: c.container, trunc: c.trunc}
v := c.call() v := c.call()
if v != c.expValue { if strings.Contains(v, ",") {
// comma-separated values means probably a map input, which won't
// be guaranteed to have the same order as our expected value
// We'll create maps and use reflect.DeepEquals to check instead:
entriesMap := make(map[string]string)
expMap := make(map[string]string)
entries := strings.Split(v, ",")
expectedEntries := strings.Split(c.expValue, ",")
for _, entry := range entries {
keyval := strings.Split(entry, "=")
entriesMap[keyval[0]] = keyval[1]
}
for _, expected := range expectedEntries {
keyval := strings.Split(expected, "=")
expMap[keyval[0]] = keyval[1]
}
if !reflect.DeepEqual(expMap, entriesMap) {
t.Fatalf("Expected entries: %v, got: %v", c.expValue, v)
}
} else if v != c.expValue {
t.Fatalf("Expected %s, was %s\n", c.expValue, v) t.Fatalf("Expected %s, was %s\n", c.expValue, v)
} }
@ -52,17 +73,16 @@ func TestContainerContextID(t *testing.T) {
sid := ctx.Label("com.docker.swarm.swarm-id") sid := ctx.Label("com.docker.swarm.swarm-id")
node := ctx.Label("com.docker.swarm.node_name") node := ctx.Label("com.docker.swarm.node_name")
if sid != "33" { if sid != "33" {
t.Fatal("Expected 33, was %s\n", sid) t.Fatalf("Expected 33, was %s\n", sid)
} }
if node != "ubuntu" { if node != "ubuntu" {
t.Fatal("Expected ubuntu, was %s\n", node) t.Fatalf("Expected ubuntu, was %s\n", node)
} }
h := ctx.fullHeader() h := ctx.fullHeader()
if h != "SWARM ID\tNODE NAME" { if h != "SWARM ID\tNODE NAME" {
t.Fatal("Expected %s, was %s\n", "SWARM ID\tNODE NAME", h) t.Fatalf("Expected %s, was %s\n", "SWARM ID\tNODE NAME", h)
} }
} }

View File

@ -158,7 +158,7 @@ func TestNewJson(t *testing.T) {
func TestJsonWithPsFormat(t *testing.T) { func TestJsonWithPsFormat(t *testing.T) {
tmpHome, _ := ioutil.TempDir("", "config-test") tmpHome, _ := ioutil.TempDir("", "config-test")
fn := filepath.Join(tmpHome, CONFIGFILE) fn := filepath.Join(tmpHome, ConfigFileName)
js := `{ js := `{
"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } }, "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } },
"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}" "psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
@ -180,7 +180,7 @@ func TestJsonWithPsFormat(t *testing.T) {
t.Fatalf("Failed to save: %q", err) t.Fatalf("Failed to save: %q", err)
} }
buf, err := ioutil.ReadFile(filepath.Join(tmpHome, CONFIGFILE)) buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
if !strings.Contains(string(buf), `"psFormat":`) || if !strings.Contains(string(buf), `"psFormat":`) ||
!strings.Contains(string(buf), "{{.ID}}") { !strings.Contains(string(buf), "{{.ID}}") {
t.Fatalf("Should have save in new form: %s", string(buf)) t.Fatalf("Should have save in new form: %s", string(buf))

View File

@ -85,18 +85,26 @@ mechanisms, you must keep in mind the order of precedence among them. Command
line options override environment variables and environment variables override line options override environment variables and environment variables override
properties you specify in a `config.json` file. properties you specify in a `config.json` file.
The `config.json` file stores a JSON encoding of a single `HttpHeaders` The `config.json` file stores a JSON encoding of several properties:
property. The property specifies a set of headers to include in all messages
The property `HttpHeaders` specifies a set of headers to include in all messages
sent from the Docker client to the daemon. Docker does not try to interpret or sent from the Docker client to the daemon. Docker does not try to interpret or
understand these header; it simply puts them into the messages. Docker does understand these header; it simply puts them into the messages. Docker does
not allow these headers to change any headers it sets for itself. not allow these headers to change any headers it sets for itself.
The property `psFormat` specifies the default format for `docker ps` output.
When the `--format` flag is not provided with the `docker ps` command,
Docker's client uses this property. If this property is not set, the client
falls back to the default table format. For a list of supported formatting
directives, see the [**Formatting** section in the `docker ps` documentation](../ps)
Following is a sample `config.json` file: Following is a sample `config.json` file:
{ {
"HttpHeaders: { "HttpHeaders: {
"MyHeader": "MyValue" "MyHeader": "MyValue"
} },
"psFormat": "table {{.ID}}\\t{{.Image}}\\t{{.Command}}\\t{{.Labels}}"
} }
## Help ## Help

View File

@ -24,6 +24,7 @@ weight=1
-q, --quiet=false Only display numeric IDs -q, --quiet=false Only display numeric IDs
-s, --size=false Display total file sizes -s, --size=false Display total file sizes
--since="" Show created since Id or Name, include non-running --since="" Show created since Id or Name, include non-running
--format=[] Pretty-print containers using a Go template
Running `docker ps --no-trunc` showing 2 linked containers. Running `docker ps --no-trunc` showing 2 linked containers.
@ -60,5 +61,42 @@ The currently supported filters are:
This shows all the containers that have exited with status of '0' This shows all the containers that have exited with status of '0'
## Formatting
The formatting option (`--format`) will pretty-print container output using a Go template.
Valid placeholders for the Go template are listed below:
Placeholder | Description
---- | ----
`.ID` | Container ID
`.Image` | Image ID
`.Command` | Quoted command
`.CreatedAt` | Time when the container was created.
`.RunningFor` | Elapsed time since the container was started.
`.Ports` | Exposed ports.
`.Status` | Container status.
`.Size` | Container disk size.
`.Labels` | All labels asigned to the container.
`.Label` | Value of a specific label for this container. For example `{{.Label "com.docker.swarm.cpu"}}`
When using the `--format` option, the `ps` command will either output the data exactly as the template
declares or, when using the `table` directive, will include column headers as well.
The following example uses a template without headers and outputs the `ID` and `Command`
entries separated by a colon for all running containers:
$ docker ps --format "{{.ID}}: {{.Command}}"
a87ecb4f327c: /bin/sh -c #(nop) MA
01946d9d34d8: /bin/sh -c #(nop) MA
c1d3b0166030: /bin/sh -c yum -y up
41d50ecd2f57: /bin/sh -c #(nop) MA
To list all running containers with their labels in a table format you can use:
$ docker ps --format "table {{.ID}}\t{{.Labels}}"
CONTAINER ID LABELS
a87ecb4f327c com.docker.swarm.node=ubuntu,com.docker.swarm.storage=ssd
01946d9d34d8
c1d3b0166030 com.docker.swarm.node=debian,com.docker.swarm.cpu=6
41d50ecd2f57 com.docker.swarm.node=fedora,com.docker.swarm.cpu=3,com.docker.swarm.storage=ssd

View File

@ -508,3 +508,34 @@ func (s *DockerSuite) TestPsListContainersFilterCreated(c *check.C) {
c.Fatalf("Expected id %s, got %s for filter, out: %s", cID, containerOut, out) c.Fatalf("Expected id %s, got %s for filter, out: %s", cID, containerOut, out)
} }
} }
func (s *DockerSuite) TestPsFormatMultiNames(c *check.C) {
//create 2 containers and link them
dockerCmd(c, "run", "--name=child", "-d", "busybox", "top")
dockerCmd(c, "run", "--name=parent", "--link=child:linkedone", "-d", "busybox", "top")
//use the new format capabilities to only list the names and --no-trunc to get all names
out, _ := dockerCmd(c, "ps", "--format", "{{.Names}}", "--no-trunc")
lines := strings.Split(strings.TrimSpace(string(out)), "\n")
expected := []string{"parent", "child,parent/linkedone"}
var names []string
for _, l := range lines {
names = append(names, l)
}
if !reflect.DeepEqual(expected, names) {
c.Fatalf("Expected array with non-truncated names: %v, got: %v", expected, names)
}
//now list without turning off truncation and make sure we only get the non-link names
out, _ = dockerCmd(c, "ps", "--format", "{{.Names}}")
lines = strings.Split(strings.TrimSpace(string(out)), "\n")
expected = []string{"parent", "child"}
var truncNames []string
for _, l := range lines {
truncNames = append(truncNames, l)
}
if !reflect.DeepEqual(expected, truncNames) {
c.Fatalf("Expected array with truncated names: %v, got: %v", expected, truncNames)
}
}

View File

@ -16,7 +16,7 @@ docker-ps - List containers
[**-q**|**--quiet**[=*false*]] [**-q**|**--quiet**[=*false*]]
[**-s**|**--size**[=*false*]] [**-s**|**--size**[=*false*]]
[**--since**[=*SINCE*]] [**--since**[=*SINCE*]]
[**-F**|**--format**=*"TEMPLATE"*] [**--format**=*"TEMPLATE"*]
# DESCRIPTION # DESCRIPTION
@ -60,7 +60,7 @@ the running containers.
**--since**="" **--since**=""
Show only containers created since Id or Name, include non-running ones. Show only containers created since Id or Name, include non-running ones.
**-F**, **--format**=*"TEMPLATE"* **--format**=*"TEMPLATE"*
Pretty-print containers using a Go template. Pretty-print containers using a Go template.
Valid placeholders: Valid placeholders:
.ID - Container ID .ID - Container ID