formatter: reduce minimum width for columns in table-view

The tabwriter was configured to have a min-width for columns of 20 positions.
This seemed quite wide, and caused smaller columns to be printed with a large
gap between.

Before:

    docker container stats

    CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
    29184b3ae391        amazing_shirley     0.00%               800KiB / 1.944GiB     0.04%               1.44kB / 0B         0B / 0B             1
    403c101bad56        agitated_swartz     0.15%               34.31MiB / 1.944GiB   1.72%               10.2MB / 206kB      0B / 0B             51
    0dc4b7f6c6be        container2          0.00%               1.012MiB / 1.944GiB   0.05%               12.9kB / 0B         0B / 0B             5
    2d99abcc6f62        container99         0.00%               972KiB / 1.944GiB     0.05%               13kB / 0B           0B / 0B             5
    9f9aa90173ac        foo                 0.00%               820KiB / 1.944GiB     0.04%               13kB / 0B           0B / 0B             5

    docker container ls

    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
    29184b3ae391        docker-cli-dev      "ash"                    4 hours ago         Up 4 hours                              amazing_shirley
    403c101bad56        docker-dev:master   "hack/dind bash"         3 days ago          Up 3 days                               agitated_swartz
    0dc4b7f6c6be        nginx:alpine        "/docker-entrypoint.…"   4 days ago          Up 4 days           80/tcp              container2
    2d99abcc6f62        nginx:alpine        "/docker-entrypoint.…"   4 days ago          Up 4 days           80/tcp              container99
    9f9aa90173ac        nginx:alpine        "/docker-entrypoint.…"   4 days ago          Up 4 days           80/tcp              foo

    docker image ls

    REPOSITORY          TAG                    IMAGE ID            CREATED             SIZE
    docker-cli-dev      latest                 5f603caa04aa        4 hours ago         610MB
    docker-cli-native   latest                 9dd29f8d387b        4 hours ago         519MB
    docker-dev          master                 8132bf7a199e        3 days ago          2.02GB
    docker-dev          improve-build-errors   69e208994b3f        11 days ago         2.01GB
    docker-dev          refactor-idtools       69e208994b3f        11 days ago         2.01GB

After:

    docker container stats

    CONTAINER ID   NAME              CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O   PIDS
    29184b3ae391   amazing_shirley   0.14%     5.703MiB / 1.944GiB   0.29%     1.44kB / 0B      0B / 0B     10
    403c101bad56   agitated_swartz   0.15%     56.97MiB / 1.944GiB   2.86%     10.2MB / 206kB   0B / 0B     51
    0dc4b7f6c6be   container2        0.00%     1016KiB / 1.944GiB    0.05%     12.9kB / 0B      0B / 0B     5
    2d99abcc6f62   container99       0.00%     956KiB / 1.944GiB     0.05%     13kB / 0B        0B / 0B     5
    9f9aa90173ac   foo               0.00%     980KiB / 1.944GiB     0.05%     13kB / 0B        0B / 0B     5

    docker container ls

    CONTAINER ID   IMAGE               COMMAND                  CREATED          STATUS          PORTS     NAMES
    29184b3ae391   docker-cli-dev      "ash"                    12 minutes ago   Up 12 minutes             amazing_shirley
    403c101bad56   docker-dev:master   "hack/dind bash"         3 days ago       Up 3 days                 agitated_swartz
    0dc4b7f6c6be   nginx:alpine        "/docker-entrypoint.…"   4 days ago       Up 4 days       80/tcp    container2
    2d99abcc6f62   nginx:alpine        "/docker-entrypoint.…"   4 days ago       Up 4 days       80/tcp    container99
    9f9aa90173ac   nginx:alpine        "/docker-entrypoint.…"   4 days ago       Up 4 days       80/tcp    foo

    docker image ls

    REPOSITORY          TAG                    IMAGE ID       CREATED         SIZE
    docker-cli-dev      latest                 5f603caa04aa   4 hours ago     610MB
    docker-cli-native   latest                 9dd29f8d387b   4 hours ago     519MB
    docker-dev          master                 8132bf7a199e   3 days ago      2.02GB
    docker-dev          improve-build-errors   69e208994b3f   11 days ago     2.01GB
    docker-dev          refactor-idtools       69e208994b3f   11 days ago     2.01GB

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2020-08-28 23:00:21 +02:00
parent f784262d07
commit dace8fdc75
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
51 changed files with 683 additions and 607 deletions

View File

@ -8,7 +8,6 @@ import (
"github.com/docker/cli/cli/command/formatter"
"github.com/docker/docker/api/types/swarm"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestConfigContextFormatWrite(t *testing.T) {
@ -30,9 +29,9 @@ func TestConfigContextFormatWrite(t *testing.T) {
},
// Table format
{formatter.Context{Format: NewFormat("table", false)},
`ID NAME CREATED UPDATED
1 passwords Less than a second ago Less than a second ago
2 id_rsa Less than a second ago Less than a second ago
`ID NAME CREATED UPDATED
1 passwords Less than a second ago Less than a second ago
2 id_rsa Less than a second ago Less than a second ago
`},
{formatter.Context{Format: NewFormat("table {{.Name}}", true)},
`NAME
@ -53,13 +52,16 @@ id_rsa
Meta: swarm.Meta{CreatedAt: time.Now(), UpdatedAt: time.Now()},
Spec: swarm.ConfigSpec{Annotations: swarm.Annotations{Name: "id_rsa"}}},
}
for _, testcase := range cases {
out := bytes.NewBufferString("")
testcase.context.Output = out
if err := FormatWrite(testcase.context, configs); err != nil {
assert.ErrorContains(t, err, testcase.expected)
} else {
assert.Check(t, is.Equal(out.String(), testcase.expected))
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
if err := FormatWrite(tc.context, configs); err != nil {
assert.ErrorContains(t, err, tc.expected)
} else {
assert.Equal(t, out.String(), tc.expected)
}
})
}
}

View File

@ -1,4 +1,4 @@
ID NAME CREATED UPDATED
ID-1-foo 1-foo 2 hours ago About an hour ago
ID-2-foo 2-foo 2 hours ago About an hour ago
ID-10-foo 10-foo 2 hours ago About an hour ago
ID NAME CREATED UPDATED
ID-1-foo 1-foo 2 hours ago About an hour ago
ID-2-foo 2-foo 2 hours ago About an hour ago
ID-10-foo 10-foo 2 hours ago About an hour ago

View File

@ -1,3 +1,3 @@
ID NAME CREATED UPDATED
ID-bar bar 2 hours ago About an hour ago
ID-foo foo 2 hours ago About an hour ago
ID NAME CREATED UPDATED
ID-bar bar 2 hours ago About an hour ago
ID-foo foo 2 hours ago About an hour ago

View File

@ -8,7 +8,6 @@ import (
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/pkg/archive"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestDiffContextFormatWrite(t *testing.T) {
@ -19,10 +18,10 @@ func TestDiffContextFormatWrite(t *testing.T) {
}{
{
formatter.Context{Format: NewDiffFormat("table")},
`CHANGE TYPE PATH
C /var/log/app.log
A /usr/app/app.js
D /usr/app/old_app.js
`CHANGE TYPE PATH
C /var/log/app.log
A /usr/app/app.js
D /usr/app/old_app.js
`,
},
{
@ -48,14 +47,17 @@ D: /usr/app/old_app.js
{Kind: archive.ChangeDelete, Path: "/usr/app/old_app.js"},
}
for _, testcase := range cases {
out := bytes.NewBufferString("")
testcase.context.Output = out
err := DiffFormatWrite(testcase.context, diffs)
if err != nil {
assert.Error(t, err, testcase.expected)
} else {
assert.Check(t, is.Equal(testcase.expected, out.String()))
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
out := bytes.NewBufferString("")
tc.context.Output = out
err := DiffFormatWrite(tc.context, diffs)
if err != nil {
assert.Error(t, err, tc.expected)
} else {
assert.Equal(t, out.String(), tc.expected)
}
})
}
}

View File

@ -126,7 +126,7 @@ container2 --
}
func TestContainerStatsContextWriteWindows(t *testing.T) {
tt := []struct {
cases := []struct {
context formatter.Context
expected string
}{
@ -150,51 +150,54 @@ container2 -- --
`,
},
}
stats := []StatsEntry{
{
Container: "container1",
CPUPercentage: 20,
Memory: 20,
MemoryLimit: 20,
MemoryPercentage: 20,
NetworkRx: 20,
NetworkTx: 20,
BlockRead: 20,
BlockWrite: 20,
PidsCurrent: 2,
IsInvalid: false,
},
{
Container: "container2",
CPUPercentage: 30,
Memory: 30,
MemoryLimit: 30,
MemoryPercentage: 30,
NetworkRx: 30,
NetworkTx: 30,
BlockRead: 30,
BlockWrite: 30,
PidsCurrent: 3,
IsInvalid: true,
},
}
for _, te := range tt {
stats := []StatsEntry{
{
Container: "container1",
CPUPercentage: 20,
Memory: 20,
MemoryLimit: 20,
MemoryPercentage: 20,
NetworkRx: 20,
NetworkTx: 20,
BlockRead: 20,
BlockWrite: 20,
PidsCurrent: 2,
IsInvalid: false,
},
{
Container: "container2",
CPUPercentage: 30,
Memory: 30,
MemoryLimit: 30,
MemoryPercentage: 30,
NetworkRx: 30,
NetworkTx: 30,
BlockRead: 30,
BlockWrite: 30,
PidsCurrent: 3,
IsInvalid: true,
},
}
var out bytes.Buffer
te.context.Output = &out
err := statsFormatWrite(te.context, stats, "windows", false)
if err != nil {
assert.Error(t, err, te.expected)
} else {
assert.Check(t, is.Equal(te.expected, out.String()))
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
err := statsFormatWrite(tc.context, stats, "windows", false)
if err != nil {
assert.Error(t, err, tc.expected)
} else {
assert.Equal(t, out.String(), tc.expected)
}
})
}
}
func TestContainerStatsContextWriteWithNoStats(t *testing.T) {
var out bytes.Buffer
contexts := []struct {
cases := []struct {
context formatter.Context
expected string
}{
@ -217,22 +220,26 @@ func TestContainerStatsContextWriteWithNoStats(t *testing.T) {
Format: "table {{.Container}}\t{{.CPUPerc}}",
Output: &out,
},
"CONTAINER CPU %\n",
"CONTAINER CPU %\n",
},
}
for _, context := range contexts {
statsFormatWrite(context.context, []StatsEntry{}, "linux", false)
assert.Check(t, is.Equal(context.expected, out.String()))
// Clean buffer
out.Reset()
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
err := statsFormatWrite(tc.context, []StatsEntry{}, "linux", false)
assert.NilError(t, err)
assert.Equal(t, out.String(), tc.expected)
// Clean buffer
out.Reset()
})
}
}
func TestContainerStatsContextWriteWithNoStatsWindows(t *testing.T) {
var out bytes.Buffer
contexts := []struct {
cases := []struct {
context formatter.Context
expected string
}{
@ -248,22 +255,25 @@ func TestContainerStatsContextWriteWithNoStatsWindows(t *testing.T) {
Format: "table {{.Container}}\t{{.MemUsage}}",
Output: &out,
},
"CONTAINER PRIV WORKING SET\n",
"CONTAINER PRIV WORKING SET\n",
},
{
formatter.Context{
Format: "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}",
Output: &out,
},
"CONTAINER CPU % PRIV WORKING SET\n",
"CONTAINER CPU % PRIV WORKING SET\n",
},
}
for _, context := range contexts {
statsFormatWrite(context.context, []StatsEntry{}, "windows", false)
assert.Check(t, is.Equal(context.expected, out.String()))
// Clean buffer
out.Reset()
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
err := statsFormatWrite(tc.context, []StatsEntry{}, "windows", false)
assert.NilError(t, err)
assert.Equal(t, out.String(), tc.expected)
out.Reset()
})
}
}

View File

@ -1,3 +1,3 @@
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
container_id busybox:latest "top" Less than a second ago Up 1 second c1
container_id busybox:latest "top" Less than a second ago Up 1 second c2,foo/bar
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
container_id busybox:latest "top" Less than a second ago Up 1 second c1
container_id busybox:latest "top" Less than a second ago Up 1 second c2,foo/bar

View File

@ -1,6 +1,6 @@
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
container_id busybox:latest "top" Less than a second ago Up 1 second c1
container_id busybox:latest "top" Less than a second ago Up 1 second c2
container_id busybox:latest "top" Less than a second ago Up 1 second 80-82/tcp c3
container_id busybox:latest "top" Less than a second ago Up 1 second 81/udp c4
container_id busybox:latest "top" Less than a second ago Up 1 second 8.8.8.8:82->82/tcp c5
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
container_id busybox:latest "top" Less than a second ago Up 1 second c1
container_id busybox:latest "top" Less than a second ago Up 1 second c2
container_id busybox:latest "top" Less than a second ago Up 1 second 80-82/tcp c3
container_id busybox:latest "top" Less than a second ago Up 1 second 81/udp c4
container_id busybox:latest "top" Less than a second ago Up 1 second 8.8.8.8:82->82/tcp c5

View File

@ -1,5 +1,5 @@
NAME DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
current * description of current https://someswarmserver https://someserver (default) all
default Current DOCKER_HOST based configuration unix:///var/run/docker.sock swarm
other description of other https://someswarmserver https://someserver (default) all
unset description of unset https://someswarmserver https://someserver (default)
NAME DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
current * description of current https://someswarmserver https://someserver (default) all
default Current DOCKER_HOST based configuration unix:///var/run/docker.sock swarm
other description of other https://someswarmserver https://someserver (default) all
unset description of unset https://someswarmserver https://someserver (default)

View File

@ -141,16 +141,16 @@ func TestContainerContextWrite(t *testing.T) {
// Table Format
{
Context{Format: NewContainerFormat("table", false, true)},
`CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE
containerID1 ubuntu "" 24 hours ago foobar_baz 0B
containerID2 ubuntu "" 24 hours ago foobar_bar 0B
`CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE
containerID1 ubuntu "" 24 hours ago foobar_baz 0B
containerID2 ubuntu "" 24 hours ago foobar_bar 0B
`,
},
{
Context{Format: NewContainerFormat("table", false, false)},
`CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
containerID1 ubuntu "" 24 hours ago foobar_baz
containerID2 ubuntu "" 24 hours ago foobar_bar
`CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
containerID1 ubuntu "" 24 hours ago foobar_baz
containerID2 ubuntu "" 24 hours ago foobar_bar
`,
},
{
@ -248,19 +248,24 @@ size: 0B
},
}
for _, testcase := range cases {
containers := []types.Container{
{ID: "containerID1", Names: []string{"/foobar_baz"}, Image: "ubuntu", Created: unixTime, State: "running"},
{ID: "containerID2", Names: []string{"/foobar_bar"}, Image: "ubuntu", Created: unixTime, State: "running"},
}
out := bytes.NewBufferString("")
testcase.context.Output = out
err := ContainerWrite(testcase.context, containers)
if err != nil {
assert.Error(t, err, testcase.expected)
} else {
assert.Check(t, is.Equal(testcase.expected, out.String()))
}
containers := []types.Container{
{ID: "containerID1", Names: []string{"/foobar_baz"}, Image: "ubuntu", Created: unixTime, State: "running"},
{ID: "containerID2", Names: []string{"/foobar_bar"}, Image: "ubuntu", Created: unixTime, State: "running"},
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
err := ContainerWrite(tc.context, containers)
if err != nil {
assert.Error(t, err, tc.expected)
} else {
assert.Equal(t, out.String(), tc.expected)
}
})
}
}
@ -268,7 +273,7 @@ func TestContainerContextWriteWithNoContainers(t *testing.T) {
out := bytes.NewBufferString("")
containers := []types.Container{}
contexts := []struct {
cases := []struct {
context Context
expected string
}{
@ -305,23 +310,26 @@ func TestContainerContextWriteWithNoContainers(t *testing.T) {
Format: "table {{.Image}}\t{{.Size}}",
Output: out,
},
"IMAGE SIZE\n",
"IMAGE SIZE\n",
},
{
Context{
Format: NewContainerFormat("table {{.Image}}\t{{.Size}}", false, true),
Output: out,
},
"IMAGE SIZE\n",
"IMAGE SIZE\n",
},
}
for _, context := range contexts {
err := ContainerWrite(context.context, containers)
assert.NilError(t, err)
assert.Check(t, is.Equal(context.expected, out.String()))
// Clean buffer
out.Reset()
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
err := ContainerWrite(tc.context, containers)
assert.NilError(t, err)
assert.Equal(t, out.String(), tc.expected)
// Clean buffer
out.Reset()
})
}
}

View File

@ -5,7 +5,6 @@ import (
"testing"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/golden"
)
@ -21,30 +20,30 @@ func TestDiskUsageContextFormatWrite(t *testing.T) {
Format: NewDiskUsageFormat("table", false),
},
Verbose: false},
`TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 0 0 0B 0B
Containers 0 0 0B 0B
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B
`TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 0 0 0B 0B
Containers 0 0 0B 0B
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B
`,
},
{
DiskUsageContext{Verbose: true, Context: Context{Format: NewDiskUsageFormat("table", true)}},
`Images space usage:
REPOSITORY TAG IMAGE ID CREATED SIZE SHARED SIZE UNIQUE SIZE CONTAINERS
REPOSITORY TAG IMAGE ID CREATED SIZE SHARED SIZE UNIQUE SIZE CONTAINERS
Containers space usage:
CONTAINER ID IMAGE COMMAND LOCAL VOLUMES SIZE CREATED STATUS NAMES
CONTAINER ID IMAGE COMMAND LOCAL VOLUMES SIZE CREATED STATUS NAMES
Local Volumes space usage:
VOLUME NAME LINKS SIZE
VOLUME NAME LINKS SIZE
Build cache usage: 0B
CACHE ID CACHE TYPE SIZE CREATED LAST USED USAGE SHARED
CACHE ID CACHE TYPE SIZE CREATED LAST USED USAGE SHARED
`,
},
{
@ -81,11 +80,11 @@ CACHE ID CACHE TYPE SIZE CREATED
Format: NewDiskUsageFormat("table", false),
},
},
`TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 0 0 0B 0B
Containers 0 0 0B 0B
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B
`TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 0 0 0B 0B
Containers 0 0 0B 0B
Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B
`,
},
{
@ -107,13 +106,16 @@ Build Cache 0 0 0B
},
}
for _, testcase := range cases {
out := bytes.NewBufferString("")
testcase.context.Output = out
if err := testcase.context.Write(); err != nil {
assert.Check(t, is.Equal(testcase.expected, err.Error()))
} else {
assert.Check(t, is.Equal(testcase.expected, out.String()))
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
if err := tc.context.Write(); err != nil {
assert.Error(t, err, tc.expected)
} else {
assert.Equal(t, out.String(), tc.expected)
}
})
}
}

View File

@ -71,7 +71,7 @@ func (c *Context) parseFormat() (*template.Template, error) {
func (c *Context) postFormat(tmpl *template.Template, subContext SubContext) {
if c.Format.IsTable() {
t := tabwriter.NewWriter(c.Output, 20, 1, 3, ' ', 0)
t := tabwriter.NewWriter(c.Output, 10, 1, 3, ' ', 0)
buffer := bytes.NewBufferString("")
tmpl.Funcs(templates.HeaderFunctions).Execute(buffer, subContext.FullHeader())
buffer.WriteTo(t)

View File

@ -138,10 +138,10 @@ func TestImageContextWrite(t *testing.T) {
Format: NewImageFormat("table", false, false),
},
},
`REPOSITORY TAG IMAGE ID CREATED SIZE
image tag1 imageID1 24 hours ago 0B
image tag2 imageID2 N/A 0B
<none> <none> imageID3 24 hours ago 0B
`REPOSITORY TAG IMAGE ID CREATED SIZE
image tag1 imageID1 24 hours ago 0B
image tag2 imageID2 N/A 0B
<none> <none> imageID3 24 hours ago 0B
`,
},
{
@ -159,10 +159,10 @@ image tag2 imageID2 N/A
},
Digest: true,
},
`REPOSITORY DIGEST
image sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf
image <none>
<none> <none>
`REPOSITORY DIGEST
image sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf
image <none>
<none> <none>
`,
},
{
@ -196,10 +196,10 @@ image <none>
},
Digest: true,
},
`REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE
image tag1 sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf imageID1 24 hours ago 0B
image tag2 <none> imageID2 N/A 0B
<none> <none> <none> imageID3 24 hours ago 0B
`REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE
image tag1 sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf imageID1 24 hours ago 0B
image tag2 <none> imageID2 N/A 0B
<none> <none> <none> imageID3 24 hours ago 0B
`,
},
{
@ -299,20 +299,24 @@ image_id: imageID3
},
}
for _, testcase := range cases {
images := []types.ImageSummary{
{ID: "imageID1", RepoTags: []string{"image:tag1"}, RepoDigests: []string{"image@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf"}, Created: unixTime},
{ID: "imageID2", RepoTags: []string{"image:tag2"}, Created: zeroTime},
{ID: "imageID3", RepoTags: []string{"<none>:<none>"}, RepoDigests: []string{"<none>@<none>"}, Created: unixTime},
}
out := bytes.NewBufferString("")
testcase.context.Output = out
err := ImageWrite(testcase.context, images)
if err != nil {
assert.Error(t, err, testcase.expected)
} else {
assert.Check(t, is.Equal(testcase.expected, out.String()))
}
images := []types.ImageSummary{
{ID: "imageID1", RepoTags: []string{"image:tag1"}, RepoDigests: []string{"image@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf"}, Created: unixTime},
{ID: "imageID2", RepoTags: []string{"image:tag2"}, Created: zeroTime},
{ID: "imageID3", RepoTags: []string{"<none>:<none>"}, RepoDigests: []string{"<none>@<none>"}, Created: unixTime},
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
err := ImageWrite(tc.context, images)
if err != nil {
assert.Error(t, err, tc.expected)
} else {
assert.Equal(t, out.String(), tc.expected)
}
})
}
}
@ -320,7 +324,7 @@ func TestImageContextWriteWithNoImage(t *testing.T) {
out := bytes.NewBufferString("")
images := []types.ImageSummary{}
contexts := []struct {
cases := []struct {
context ImageContext
expected string
}{
@ -358,15 +362,18 @@ func TestImageContextWriteWithNoImage(t *testing.T) {
Output: out,
},
},
"REPOSITORY DIGEST\n",
"REPOSITORY DIGEST\n",
},
}
for _, context := range contexts {
err := ImageWrite(context.context, images)
assert.NilError(t, err)
assert.Check(t, is.Equal(context.expected, out.String()))
// Clean buffer
out.Reset()
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
err := ImageWrite(tc.context, images)
assert.NilError(t, err)
assert.Equal(t, out.String(), tc.expected)
// Clean buffer
out.Reset()
})
}
}

View File

@ -1,3 +1,3 @@
CONTAINER ID IMAGE CREATED/STATUS/ PORTS .NAMES STATUS
conta "ubuntu" 24 hours ago//.FOOBAR_BAZ
conta "ubuntu" 24 hours ago//.FOOBAR_BAR
CONTAINER ID IMAGE CREATED/STATUS/ PORTS .NAMES STATUS
conta "ubuntu" 24 hours ago//.FOOBAR_BAZ
conta "ubuntu" 24 hours ago//.FOOBAR_BAR

View File

@ -1,5 +1,5 @@
TYPE ACTIVE
Images 0
Containers 0
Local Volumes 0
Build Cache 0
TYPE ACTIVE
Images 0
Containers 0
Local Volumes 0
Build Cache 0

View File

@ -59,7 +59,6 @@ func TestVolumeContextWrite(t *testing.T) {
context Context
expected string
}{
// Errors
{
Context{Format: "{{InvalidFunction}}"},
@ -74,9 +73,9 @@ func TestVolumeContextWrite(t *testing.T) {
// Table format
{
Context{Format: NewVolumeFormat("table", false)},
`DRIVER VOLUME NAME
foo foobar_baz
bar foobar_bar
`DRIVER VOLUME NAME
foo foobar_baz
bar foobar_bar
`,
},
{
@ -125,19 +124,23 @@ foobar_bar
},
}
for _, testcase := range cases {
volumes := []*types.Volume{
{Name: "foobar_baz", Driver: "foo"},
{Name: "foobar_bar", Driver: "bar"},
}
out := bytes.NewBufferString("")
testcase.context.Output = out
err := VolumeWrite(testcase.context, volumes)
if err != nil {
assert.Error(t, err, testcase.expected)
} else {
assert.Check(t, is.Equal(testcase.expected, out.String()))
}
volumes := []*types.Volume{
{Name: "foobar_baz", Driver: "foo"},
{Name: "foobar_bar", Driver: "bar"},
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
err := VolumeWrite(tc.context, volumes)
if err != nil {
assert.Error(t, err, tc.expected)
} else {
assert.Equal(t, out.String(), tc.expected)
}
})
}
}

View File

@ -12,7 +12,6 @@ import (
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/pkg/stringid"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/skip"
)
@ -188,20 +187,20 @@ func TestHistoryContext_Table(t *testing.T) {
{ID: "imageID4", Created: unixTime, CreatedBy: "/bin/bash grep", Size: int64(182964289), Comment: "Hi", Tags: []string{"image:tag2"}},
}
// nolint: lll
expectedNoTrunc := `IMAGE CREATED CREATED BY SIZE COMMENT
imageID1 24 hours ago /bin/bash ls && npm i && npm run test && karma -c karma.conf.js start && npm start && more commands here && the list goes on 183MB Hi
imageID2 24 hours ago /bin/bash echo 183MB Hi
imageID3 24 hours ago /bin/bash ls 183MB Hi
imageID4 24 hours ago /bin/bash grep 183MB Hi
expectedNoTrunc := `IMAGE CREATED CREATED BY SIZE COMMENT
imageID1 24 hours ago /bin/bash ls && npm i && npm run test && karma -c karma.conf.js start && npm start && more commands here && the list goes on 183MB Hi
imageID2 24 hours ago /bin/bash echo 183MB Hi
imageID3 24 hours ago /bin/bash ls 183MB Hi
imageID4 24 hours ago /bin/bash grep 183MB Hi
`
expectedTrunc := `IMAGE CREATED CREATED BY SIZE COMMENT
imageID1 24 hours ago /bin/bash ls && npm i && npm run test && kar 183MB Hi
imageID2 24 hours ago /bin/bash echo 183MB Hi
imageID3 24 hours ago /bin/bash ls 183MB Hi
imageID4 24 hours ago /bin/bash grep 183MB Hi
expectedTrunc := `IMAGE CREATED CREATED BY SIZE COMMENT
imageID1 24 hours ago /bin/bash ls && npm i && npm run test && kar 183MB Hi
imageID2 24 hours ago /bin/bash echo 183MB Hi
imageID3 24 hours ago /bin/bash ls 183MB Hi
imageID4 24 hours ago /bin/bash grep 183MB Hi
`
contexts := []struct {
cases := []struct {
context formatter.Context
expected string
}{
@ -221,10 +220,14 @@ imageID4 24 hours ago /bin/bash grep
},
}
for _, context := range contexts {
HistoryWrite(context.context, true, histories)
assert.Check(t, is.Equal(context.expected, out.String()))
// Clean buffer
out.Reset()
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
err := HistoryWrite(tc.context, true, histories)
assert.NilError(t, err)
assert.Equal(t, out.String(), tc.expected)
// Clean buffer
out.Reset()
})
}
}

View File

@ -1 +1 @@
REPOSITORY TAG IMAGE ID CREATED SIZE
REPOSITORY TAG IMAGE ID CREATED SIZE

View File

@ -1 +1 @@
REPOSITORY TAG IMAGE ID CREATED SIZE
REPOSITORY TAG IMAGE ID CREATED SIZE

View File

@ -1 +1 @@
REPOSITORY TAG IMAGE ID CREATED SIZE
REPOSITORY TAG IMAGE ID CREATED SIZE

View File

@ -90,9 +90,9 @@ func TestNetworkContextWrite(t *testing.T) {
// Table format
{
formatter.Context{Format: NewFormat("table", false)},
`NETWORK ID NAME DRIVER SCOPE
networkID1 foobar_baz foo local
networkID2 foobar_bar bar local
`NETWORK ID NAME DRIVER SCOPE
networkID1 foobar_baz foo local
networkID2 foobar_bar bar local
`,
},
{
@ -155,19 +155,23 @@ foobar_bar 2017-01-01 00:00:00 +0000 UTC
timestamp1, _ := time.Parse("2006-01-02", "2016-01-01")
timestamp2, _ := time.Parse("2006-01-02", "2017-01-01")
for _, testcase := range cases {
networks := []types.NetworkResource{
{ID: "networkID1", Name: "foobar_baz", Driver: "foo", Scope: "local", Created: timestamp1},
{ID: "networkID2", Name: "foobar_bar", Driver: "bar", Scope: "local", Created: timestamp2},
}
out := bytes.NewBufferString("")
testcase.context.Output = out
err := FormatWrite(testcase.context, networks)
if err != nil {
assert.Error(t, err, testcase.expected)
} else {
assert.Check(t, is.Equal(testcase.expected, out.String()))
}
networks := []types.NetworkResource{
{ID: "networkID1", Name: "foobar_baz", Driver: "foo", Scope: "local", Created: timestamp1},
{ID: "networkID2", Name: "foobar_bar", Driver: "bar", Scope: "local", Created: timestamp2},
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
err := FormatWrite(tc.context, networks)
if err != nil {
assert.Error(t, err, tc.expected)
} else {
assert.Equal(t, out.String(), tc.expected)
}
})
}
}

View File

@ -1,2 +1,2 @@
NETWORK ID NAME DRIVER SCOPE
123454321 network_1 09.7.01 global
NETWORK ID NAME DRIVER SCOPE
123454321 network_1 09.7.01 global

View File

@ -76,10 +76,10 @@ func TestNodeContextWrite(t *testing.T) {
// Table format
{
context: formatter.Context{Format: NewFormat("table", false)},
expected: `ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
nodeID1 foobar_baz Foo Drain Leader 18.03.0-ce
nodeID2 foobar_bar Bar Active Reachable 1.2.3
nodeID3 foobar_boo Boo Active ` + "\n", // (to preserve whitespace)
expected: `ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
nodeID1 foobar_baz Foo Drain Leader 18.03.0-ce
nodeID2 foobar_bar Bar Active Reachable 1.2.3
nodeID3 foobar_boo Boo Active ` + "\n", // (to preserve whitespace)
clusterInfo: swarm.ClusterInfo{TLSInfo: swarm.TLSInfo{TrustRoot: "hi"}},
},
{
@ -110,19 +110,19 @@ foobar_boo
},
{
context: formatter.Context{Format: NewFormat("table {{.ID}}\t{{.Hostname}}\t{{.TLSStatus}}", false)},
expected: `ID HOSTNAME TLS STATUS
nodeID1 foobar_baz Needs Rotation
nodeID2 foobar_bar Ready
nodeID3 foobar_boo Unknown
expected: `ID HOSTNAME TLS STATUS
nodeID1 foobar_baz Needs Rotation
nodeID2 foobar_bar Ready
nodeID3 foobar_boo Unknown
`,
clusterInfo: swarm.ClusterInfo{TLSInfo: swarm.TLSInfo{TrustRoot: "hi"}},
},
{ // no cluster TLS status info, TLS status for all nodes is unknown
context: formatter.Context{Format: NewFormat("table {{.ID}}\t{{.Hostname}}\t{{.TLSStatus}}", false)},
expected: `ID HOSTNAME TLS STATUS
nodeID1 foobar_baz Unknown
nodeID2 foobar_bar Unknown
nodeID3 foobar_boo Unknown
expected: `ID HOSTNAME TLS STATUS
nodeID1 foobar_baz Unknown
nodeID2 foobar_bar Unknown
nodeID3 foobar_boo Unknown
`,
clusterInfo: swarm.ClusterInfo{},
},
@ -167,48 +167,53 @@ foobar_boo Unknown
},
}
for _, testcase := range cases {
nodes := []swarm.Node{
{
ID: "nodeID1",
Description: swarm.NodeDescription{
Hostname: "foobar_baz",
TLSInfo: swarm.TLSInfo{TrustRoot: "no"},
Engine: swarm.EngineDescription{EngineVersion: "18.03.0-ce"},
},
Status: swarm.NodeStatus{State: swarm.NodeState("foo")},
Spec: swarm.NodeSpec{Availability: swarm.NodeAvailability("drain")},
ManagerStatus: &swarm.ManagerStatus{Leader: true},
nodes := []swarm.Node{
{
ID: "nodeID1",
Description: swarm.NodeDescription{
Hostname: "foobar_baz",
TLSInfo: swarm.TLSInfo{TrustRoot: "no"},
Engine: swarm.EngineDescription{EngineVersion: "18.03.0-ce"},
},
{
ID: "nodeID2",
Description: swarm.NodeDescription{
Hostname: "foobar_bar",
TLSInfo: swarm.TLSInfo{TrustRoot: "hi"},
Engine: swarm.EngineDescription{EngineVersion: "1.2.3"},
},
Status: swarm.NodeStatus{State: swarm.NodeState("bar")},
Spec: swarm.NodeSpec{Availability: swarm.NodeAvailability("active")},
ManagerStatus: &swarm.ManagerStatus{
Leader: false,
Reachability: swarm.Reachability("Reachable"),
},
Status: swarm.NodeStatus{State: swarm.NodeState("foo")},
Spec: swarm.NodeSpec{Availability: swarm.NodeAvailability("drain")},
ManagerStatus: &swarm.ManagerStatus{Leader: true},
},
{
ID: "nodeID2",
Description: swarm.NodeDescription{
Hostname: "foobar_bar",
TLSInfo: swarm.TLSInfo{TrustRoot: "hi"},
Engine: swarm.EngineDescription{EngineVersion: "1.2.3"},
},
{
ID: "nodeID3",
Description: swarm.NodeDescription{Hostname: "foobar_boo"},
Status: swarm.NodeStatus{State: swarm.NodeState("boo")},
Spec: swarm.NodeSpec{Availability: swarm.NodeAvailability("active")},
Status: swarm.NodeStatus{State: swarm.NodeState("bar")},
Spec: swarm.NodeSpec{Availability: swarm.NodeAvailability("active")},
ManagerStatus: &swarm.ManagerStatus{
Leader: false,
Reachability: swarm.Reachability("Reachable"),
},
}
out := bytes.NewBufferString("")
testcase.context.Output = out
err := FormatWrite(testcase.context, nodes, types.Info{Swarm: swarm.Info{Cluster: &testcase.clusterInfo}})
if err != nil {
assert.Error(t, err, testcase.expected)
} else {
assert.Check(t, is.Equal(testcase.expected, out.String()))
}
},
{
ID: "nodeID3",
Description: swarm.NodeDescription{Hostname: "foobar_boo"},
Status: swarm.NodeStatus{State: swarm.NodeState("boo")},
Spec: swarm.NodeSpec{Availability: swarm.NodeAvailability("active")},
},
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
err := FormatWrite(tc.context, nodes, types.Info{Swarm: swarm.Info{Cluster: &tc.clusterInfo}})
if err != nil {
assert.Error(t, err, tc.expected)
} else {
assert.Equal(t, out.String(), tc.expected)
}
})
}
}

View File

@ -1,4 +1,4 @@
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
nodeID3 node-1-foo Ready Active 1.13.0
nodeID1 * node-2-foo Ready Active Leader .
nodeID2 node-10-foo Ready Active Reachable 18.03.0-ce
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
nodeID3 node-1-foo Ready Active 1.13.0
nodeID1 * node-2-foo Ready Active Leader .
nodeID2 node-10-foo Ready Active Reachable 18.03.0-ce

View File

@ -1,2 +1,2 @@
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
taskID rl02d5gwz6chzu7il5fhtb8be.1 myimage:mytag defaultNodeName Ready Ready 2 hours ago *:80->80/tcp
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
taskID rl02d5gwz6chzu7il5fhtb8be.1 myimage:mytag defaultNodeName Ready Ready 2 hours ago *:80->80/tcp

View File

@ -1,4 +1,4 @@
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
taskID1 failure.1 myimage:mytag defaultNodeName Ready Ready 2 hours ago "a task error"
taskID2 \_ failure.1 myimage:mytag defaultNodeName Ready Ready 3 hours ago "a task error"
taskID3 \_ failure.1 myimage:mytag defaultNodeName Ready Ready 4 hours ago "a task error"
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
taskID1 failure.1 myimage:mytag defaultNodeName Ready Ready 2 hours ago "a task error"
taskID2 \_ failure.1 myimage:mytag defaultNodeName Ready Ready 3 hours ago "a task error"
taskID3 \_ failure.1 myimage:mytag defaultNodeName Ready Ready 4 hours ago "a task error"

View File

@ -70,9 +70,9 @@ func TestPluginContextWrite(t *testing.T) {
// Table format
{
formatter.Context{Format: NewFormat("table", false)},
`ID NAME DESCRIPTION ENABLED
pluginID1 foobar_baz description 1 true
pluginID2 foobar_bar description 2 false
`ID NAME DESCRIPTION ENABLED
pluginID1 foobar_baz description 1 true
pluginID2 foobar_bar description 2 false
`,
},
{
@ -125,19 +125,24 @@ foobar_bar
},
}
for _, testcase := range cases {
plugins := []*types.Plugin{
{ID: "pluginID1", Name: "foobar_baz", Config: types.PluginConfig{Description: "description 1"}, Enabled: true},
{ID: "pluginID2", Name: "foobar_bar", Config: types.PluginConfig{Description: "description 2"}, Enabled: false},
}
out := bytes.NewBufferString("")
testcase.context.Output = out
err := FormatWrite(testcase.context, plugins)
if err != nil {
assert.Error(t, err, testcase.expected)
} else {
assert.Check(t, is.Equal(testcase.expected, out.String()))
}
plugins := []*types.Plugin{
{ID: "pluginID1", Name: "foobar_baz", Config: types.PluginConfig{Description: "description 1"}, Enabled: true},
{ID: "pluginID2", Name: "foobar_bar", Config: types.PluginConfig{Description: "description 2"}, Enabled: false},
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
err := FormatWrite(tc.context, plugins)
if err != nil {
assert.Error(t, err, tc.expected)
} else {
assert.Equal(t, out.String(), tc.expected)
}
})
}
}

View File

@ -1,2 +1,2 @@
ID NAME DESCRIPTION ENABLED
id-foo name-foo desc-bar true
ID NAME DESCRIPTION ENABLED
id-foo name-foo desc-bar true

View File

@ -148,19 +148,24 @@ result2 5
},
}
for _, testcase := range cases {
results := []registrytypes.SearchResult{
{Name: "result1", Description: "Official build", StarCount: 5000, IsOfficial: true, IsAutomated: false},
{Name: "result2", Description: "Not official", StarCount: 5, IsOfficial: false, IsAutomated: true},
}
out := bytes.NewBufferString("")
testcase.context.Output = out
err := SearchWrite(testcase.context, results)
if err != nil {
assert.Check(t, is.ErrorContains(err, testcase.expected))
} else {
assert.Check(t, is.Equal(out.String(), testcase.expected))
}
results := []registrytypes.SearchResult{
{Name: "result1", Description: "Official build", StarCount: 5000, IsOfficial: true, IsAutomated: false},
{Name: "result2", Description: "Not official", StarCount: 5, IsOfficial: false, IsAutomated: true},
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
err := SearchWrite(tc.context, results)
if err != nil {
assert.Error(t, err, tc.expected)
} else {
assert.Equal(t, out.String(), tc.expected)
}
})
}
}

View File

@ -1,3 +1,3 @@
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
result1 Official build 5000 [OK]
result2 Not official 5 [OK]
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
result1 Official build 5000 [OK]
result2 Not official 5 [OK]

View File

@ -8,7 +8,6 @@ import (
"github.com/docker/cli/cli/command/formatter"
"github.com/docker/docker/api/types/swarm"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestSecretContextFormatWrite(t *testing.T) {
@ -30,9 +29,9 @@ func TestSecretContextFormatWrite(t *testing.T) {
},
// Table format
{formatter.Context{Format: NewFormat("table", false)},
`ID NAME DRIVER CREATED UPDATED
1 passwords Less than a second ago Less than a second ago
2 id_rsa Less than a second ago Less than a second ago
`ID NAME DRIVER CREATED UPDATED
1 passwords Less than a second ago Less than a second ago
2 id_rsa Less than a second ago Less than a second ago
`},
{formatter.Context{Format: NewFormat("table {{.Name}}", true)},
`NAME
@ -53,13 +52,17 @@ id_rsa
Meta: swarm.Meta{CreatedAt: time.Now(), UpdatedAt: time.Now()},
Spec: swarm.SecretSpec{Annotations: swarm.Annotations{Name: "id_rsa"}}},
}
for _, testcase := range cases {
out := bytes.NewBufferString("")
testcase.context.Output = out
if err := FormatWrite(testcase.context, secrets); err != nil {
assert.Error(t, err, testcase.expected)
} else {
assert.Check(t, is.Equal(testcase.expected, out.String()))
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
if err := FormatWrite(tc.context, secrets); err != nil {
assert.Error(t, err, tc.expected)
} else {
assert.Equal(t, out.String(), tc.expected)
}
})
}
}

View File

@ -1,4 +1,4 @@
ID NAME DRIVER CREATED UPDATED
ID-1-foo 1-foo 2 hours ago About an hour ago
ID-2-foo 2-foo driver 2 hours ago About an hour ago
ID-10-foo 10-foo driver 2 hours ago About an hour ago
ID NAME DRIVER CREATED UPDATED
ID-1-foo 1-foo 2 hours ago About an hour ago
ID-2-foo 2-foo driver 2 hours ago About an hour ago
ID-10-foo 10-foo driver 2 hours ago About an hour ago

View File

@ -1,3 +1,3 @@
ID NAME DRIVER CREATED UPDATED
ID-bar bar 2 hours ago About an hour ago
ID-foo foo 2 hours ago About an hour ago
ID NAME DRIVER CREATED UPDATED
ID-bar bar 2 hours ago About an hour ago
ID-foo foo 2 hours ago About an hour ago

View File

@ -40,13 +40,13 @@ func TestServiceContextWrite(t *testing.T) {
// Table format
{
formatter.Context{Format: NewListFormat("table", false)},
`ID NAME MODE REPLICAS IMAGE PORTS
02_bar bar replicated 2/4 *:80->8090/udp
01_baz baz global 1/3 *:80->8080/tcp
04_qux2 qux2 replicated 3/3 (max 2 per node)
03_qux10 qux10 replicated 2/3 (max 1 per node)
05_job1 zarp1 replicated job 2/3 (5/10 completed)
06_job2 zarp2 global job 1/1 (3/4 completed)
`ID NAME MODE REPLICAS IMAGE PORTS
02_bar bar replicated 2/4 *:80->8090/udp
01_baz baz global 1/3 *:80->8080/tcp
04_qux2 qux2 replicated 3/3 (max 2 per node)
03_qux10 qux10 replicated 2/3 (max 1 per node)
05_job1 zarp1 replicated job 2/3 (5/10 completed)
06_job2 zarp2 global job 1/1 (3/4 completed)
`,
},
{
@ -61,13 +61,13 @@ func TestServiceContextWrite(t *testing.T) {
},
{
formatter.Context{Format: NewListFormat("table {{.Name}}\t{{.Mode}}", false)},
`NAME MODE
bar replicated
baz global
qux2 replicated
qux10 replicated
zarp1 replicated job
zarp2 global job
`NAME MODE
bar replicated
baz global
qux2 replicated
qux10 replicated
zarp1 replicated job
zarp2 global job
`,
},
{
@ -109,126 +109,130 @@ zarp2
},
}
for _, testcase := range cases {
services := []swarm.Service{
{
ID: "01_baz",
Spec: swarm.ServiceSpec{
Annotations: swarm.Annotations{Name: "baz"},
Mode: swarm.ServiceMode{
Global: &swarm.GlobalService{},
},
},
Endpoint: swarm.Endpoint{
Ports: []swarm.PortConfig{
{
PublishMode: "ingress",
PublishedPort: 80,
TargetPort: 8080,
Protocol: "tcp",
},
},
},
ServiceStatus: &swarm.ServiceStatus{
RunningTasks: 1,
DesiredTasks: 3,
services := []swarm.Service{
{
ID: "01_baz",
Spec: swarm.ServiceSpec{
Annotations: swarm.Annotations{Name: "baz"},
Mode: swarm.ServiceMode{
Global: &swarm.GlobalService{},
},
},
{
ID: "02_bar",
Spec: swarm.ServiceSpec{
Annotations: swarm.Annotations{Name: "bar"},
Mode: swarm.ServiceMode{
Replicated: &swarm.ReplicatedService{},
Endpoint: swarm.Endpoint{
Ports: []swarm.PortConfig{
{
PublishMode: "ingress",
PublishedPort: 80,
TargetPort: 8080,
Protocol: "tcp",
},
},
Endpoint: swarm.Endpoint{
Ports: []swarm.PortConfig{
{
PublishMode: "ingress",
PublishedPort: 80,
TargetPort: 8090,
Protocol: "udp",
},
},
},
ServiceStatus: &swarm.ServiceStatus{
RunningTasks: 2,
DesiredTasks: 4,
},
},
{
ID: "03_qux10",
Spec: swarm.ServiceSpec{
Annotations: swarm.Annotations{Name: "qux10"},
Mode: swarm.ServiceMode{
Replicated: &swarm.ReplicatedService{},
},
TaskTemplate: swarm.TaskSpec{
Placement: &swarm.Placement{MaxReplicas: 1},
},
},
ServiceStatus: &swarm.ServiceStatus{
RunningTasks: 2,
DesiredTasks: 3,
ServiceStatus: &swarm.ServiceStatus{
RunningTasks: 1,
DesiredTasks: 3,
},
},
{
ID: "02_bar",
Spec: swarm.ServiceSpec{
Annotations: swarm.Annotations{Name: "bar"},
Mode: swarm.ServiceMode{
Replicated: &swarm.ReplicatedService{},
},
},
{
ID: "04_qux2",
Spec: swarm.ServiceSpec{
Annotations: swarm.Annotations{Name: "qux2"},
Mode: swarm.ServiceMode{
Replicated: &swarm.ReplicatedService{},
Endpoint: swarm.Endpoint{
Ports: []swarm.PortConfig{
{
PublishMode: "ingress",
PublishedPort: 80,
TargetPort: 8090,
Protocol: "udp",
},
TaskTemplate: swarm.TaskSpec{
Placement: &swarm.Placement{MaxReplicas: 2},
},
},
ServiceStatus: &swarm.ServiceStatus{
RunningTasks: 3,
DesiredTasks: 3,
},
},
{
ID: "05_job1",
Spec: swarm.ServiceSpec{
Annotations: swarm.Annotations{Name: "zarp1"},
Mode: swarm.ServiceMode{
ReplicatedJob: &swarm.ReplicatedJob{
MaxConcurrent: &varThree,
TotalCompletions: &varTen,
},
},
ServiceStatus: &swarm.ServiceStatus{
RunningTasks: 2,
DesiredTasks: 4,
},
},
{
ID: "03_qux10",
Spec: swarm.ServiceSpec{
Annotations: swarm.Annotations{Name: "qux10"},
Mode: swarm.ServiceMode{
Replicated: &swarm.ReplicatedService{},
},
ServiceStatus: &swarm.ServiceStatus{
RunningTasks: 2,
DesiredTasks: 3,
CompletedTasks: 5,
TaskTemplate: swarm.TaskSpec{
Placement: &swarm.Placement{MaxReplicas: 1},
},
},
{
ID: "06_job2",
Spec: swarm.ServiceSpec{
Annotations: swarm.Annotations{Name: "zarp2"},
Mode: swarm.ServiceMode{
GlobalJob: &swarm.GlobalJob{},
},
ServiceStatus: &swarm.ServiceStatus{
RunningTasks: 2,
DesiredTasks: 3,
},
},
{
ID: "04_qux2",
Spec: swarm.ServiceSpec{
Annotations: swarm.Annotations{Name: "qux2"},
Mode: swarm.ServiceMode{
Replicated: &swarm.ReplicatedService{},
},
ServiceStatus: &swarm.ServiceStatus{
RunningTasks: 1,
DesiredTasks: 1,
CompletedTasks: 3,
TaskTemplate: swarm.TaskSpec{
Placement: &swarm.Placement{MaxReplicas: 2},
},
},
}
out := bytes.NewBufferString("")
testcase.context.Output = out
err := ListFormatWrite(testcase.context, services)
if err != nil {
assert.Error(t, err, testcase.expected)
} else {
assert.Check(t, is.Equal(testcase.expected, out.String()))
}
ServiceStatus: &swarm.ServiceStatus{
RunningTasks: 3,
DesiredTasks: 3,
},
},
{
ID: "05_job1",
Spec: swarm.ServiceSpec{
Annotations: swarm.Annotations{Name: "zarp1"},
Mode: swarm.ServiceMode{
ReplicatedJob: &swarm.ReplicatedJob{
MaxConcurrent: &varThree,
TotalCompletions: &varTen,
},
},
},
ServiceStatus: &swarm.ServiceStatus{
RunningTasks: 2,
DesiredTasks: 3,
CompletedTasks: 5,
},
},
{
ID: "06_job2",
Spec: swarm.ServiceSpec{
Annotations: swarm.Annotations{Name: "zarp2"},
Mode: swarm.ServiceMode{
GlobalJob: &swarm.GlobalJob{},
},
},
ServiceStatus: &swarm.ServiceStatus{
RunningTasks: 1,
DesiredTasks: 1,
CompletedTasks: 3,
},
},
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
if err := ListFormatWrite(tc.context, services); err != nil {
assert.Error(t, err, tc.expected)
} else {
assert.Equal(t, out.String(), tc.expected)
}
})
}
}

View File

@ -6,7 +6,6 @@ import (
"github.com/docker/cli/cli/command/formatter"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestStackContextWrite(t *testing.T) {
@ -28,17 +27,17 @@ func TestStackContextWrite(t *testing.T) {
// Table format
{
formatter.Context{Format: SwarmStackTableFormat},
`NAME SERVICES ORCHESTRATOR
baz 2 orchestrator1
bar 1 orchestrator2
`NAME SERVICES ORCHESTRATOR
baz 2 orchestrator1
bar 1 orchestrator2
`,
},
// Kubernetes table format adds Namespace column
{
formatter.Context{Format: KubernetesStackTableFormat},
`NAME SERVICES ORCHESTRATOR NAMESPACE
baz 2 orchestrator1 namespace1
bar 1 orchestrator2 namespace2
`NAME SERVICES ORCHESTRATOR NAMESPACE
baz 2 orchestrator1 namespace1
bar 1 orchestrator2 namespace2
`,
},
{
@ -61,14 +60,17 @@ bar
{Name: "baz", Services: 2, Orchestrator: "orchestrator1", Namespace: "namespace1"},
{Name: "bar", Services: 1, Orchestrator: "orchestrator2", Namespace: "namespace2"},
}
for _, testcase := range cases {
out := bytes.NewBufferString("")
testcase.context.Output = out
err := StackWrite(testcase.context, stacks)
if err != nil {
assert.Check(t, is.ErrorContains(err, testcase.expected))
} else {
assert.Check(t, is.Equal(out.String(), testcase.expected))
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
if err := StackWrite(tc.context, stacks); err != nil {
assert.Error(t, err, tc.expected)
} else {
assert.Equal(t, out.String(), tc.expected)
}
})
}
}

View File

@ -1,4 +1,4 @@
NAME SERVICES ORCHESTRATOR
service-name-1-foo 1 Swarm
service-name-2-foo 1 Swarm
service-name-10-foo 1 Swarm
NAME SERVICES ORCHESTRATOR
service-name-1-foo 1 Swarm
service-name-2-foo 1 Swarm
service-name-10-foo 1 Swarm

View File

@ -1,3 +1,3 @@
NAME SERVICES ORCHESTRATOR
service-name-bar 1 Swarm
service-name-foo 1 Swarm
NAME SERVICES ORCHESTRATOR
service-name-bar 1 Swarm
service-name-foo 1 Swarm

View File

@ -1,2 +1,2 @@
NAME SERVICES ORCHESTRATOR
service-name-foo 1 Swarm
NAME SERVICES ORCHESTRATOR
service-name-foo 1 Swarm

View File

@ -1,2 +1,2 @@
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
id-foo service-id-foo.1 myimage:mytag node-name-bar Ready Failed 2 hours ago
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
id-foo service-id-foo.1 myimage:mytag node-name-bar Ready Failed 2 hours ago

View File

@ -1,2 +1,2 @@
ID NAME MODE REPLICAS IMAGE PORTS
id-foo name-foo replicated 0/2 busybox:latest *:30000->3232/tcp
ID NAME MODE REPLICAS IMAGE PORTS
id-foo name-foo replicated 0/2 busybox:latest *:30000->3232/tcp

View File

@ -59,27 +59,31 @@ foobar_bar foo2
},
}
for _, testcase := range cases {
tasks := []swarm.Task{
{ID: "taskID1"},
{ID: "taskID2"},
}
names := map[string]string{
"taskID1": "foobar_baz",
"taskID2": "foobar_bar",
}
nodes := map[string]string{
"taskID1": "foo1",
"taskID2": "foo2",
}
out := bytes.NewBufferString("")
testcase.context.Output = out
err := FormatWrite(testcase.context, tasks, names, nodes)
if err != nil {
assert.Error(t, err, testcase.expected)
} else {
assert.Check(t, is.Equal(testcase.expected, out.String()))
}
tasks := []swarm.Task{
{ID: "taskID1"},
{ID: "taskID2"},
}
names := map[string]string{
"taskID1": "foobar_baz",
"taskID2": "foobar_bar",
}
nodes := map[string]string{
"taskID1": "foo1",
"taskID2": "foo2",
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
if err := FormatWrite(tc.context, tasks, names, nodes); err != nil {
assert.Error(t, err, tc.expected)
} else {
assert.Equal(t, out.String(), tc.expected)
}
})
}
}

View File

@ -1,3 +1,3 @@
NAME NODE PORTS
foobar_baz foo1
foobar_bar foo2
NAME NODE PORTS
foobar_baz foo1
foobar_bar foo2

View File

@ -1,3 +1,3 @@
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
id-bar service-name-1.1 myimage:mytag id-node Ready Failed 2 hours ago
id-foo service-name-10.1 myimage:mytag id-node Ready Failed 2 hours ago
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
id-bar service-name-1.1 myimage:mytag id-node Ready Failed 2 hours ago
id-foo service-name-10.1 myimage:mytag id-node Ready Failed 2 hours ago

View File

@ -1,3 +1,3 @@
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
id-foo service-name-foo.1 myimage:mytag node-name-bar Ready Failed 2 hours ago
id-bar \_ service-name-foo.1 myimage:mytag node-name-bar Ready Failed 2 hours ago
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
id-foo service-name-foo.1 myimage:mytag node-name-bar Ready Failed 2 hours ago
id-bar \_ service-name-foo.1 myimage:mytag node-name-bar Ready Failed 2 hours ago

View File

@ -110,28 +110,32 @@ func TestTrustTagContextWrite(t *testing.T) {
formatter.Context{
Format: NewTrustTagFormat(),
},
`SIGNED TAG DIGEST SIGNERS
tag1 deadbeef alice
tag2 aaaaaaaa alice, bob
tag3 bbbbbbbb
`SIGNED TAG DIGEST SIGNERS
tag1 deadbeef alice
tag2 aaaaaaaa alice, bob
tag3 bbbbbbbb
`,
},
}
for _, testcase := range cases {
signedTags := []SignedTagInfo{
{Name: "tag1", Digest: "deadbeef", Signers: []string{"alice"}},
{Name: "tag2", Digest: "aaaaaaaa", Signers: []string{"alice", "bob"}},
{Name: "tag3", Digest: "bbbbbbbb", Signers: []string{}},
}
out := bytes.NewBufferString("")
testcase.context.Output = out
err := TagWrite(testcase.context, signedTags)
if err != nil {
assert.Error(t, err, testcase.expected)
} else {
assert.Check(t, is.Equal(testcase.expected, out.String()))
}
signedTags := []SignedTagInfo{
{Name: "tag1", Digest: "deadbeef", Signers: []string{"alice"}},
{Name: "tag2", Digest: "aaaaaaaa", Signers: []string{"alice", "bob"}},
{Name: "tag3", Digest: "bbbbbbbb", Signers: []string{}},
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
if err := TagWrite(tc.context, signedTags); err != nil {
assert.Error(t, err, tc.expected)
} else {
assert.Equal(t, out.String(), tc.expected)
}
})
}
}
@ -146,7 +150,7 @@ func TestTrustTagContextEmptyWrite(t *testing.T) {
formatter.Context{
Format: NewTrustTagFormat(),
},
`SIGNED TAG DIGEST SIGNERS
`SIGNED TAG DIGEST SIGNERS
`,
}
@ -166,7 +170,7 @@ func TestSignerInfoContextEmptyWrite(t *testing.T) {
formatter.Context{
Format: NewSignerInfoFormat(),
},
`SIGNER KEYS
`SIGNER KEYS
`,
}
emptySignerInfo := []SignerInfo{}
@ -203,10 +207,10 @@ func TestSignerInfoContextWrite(t *testing.T) {
Format: NewSignerInfoFormat(),
Trunc: true,
},
`SIGNER KEYS
alice key11, key12
bob key21
eve foobarbazqux, key31, key32
`SIGNER KEYS
alice key11, key12
bob key21
eve foobarbazqux, key31, key32
`,
},
// No truncation
@ -214,27 +218,30 @@ eve foobarbazqux, key31, key32
formatter.Context{
Format: NewSignerInfoFormat(),
},
`SIGNER KEYS
alice key11, key12
bob key21
eve foobarbazquxquux, key31, key32
`SIGNER KEYS
alice key11, key12
bob key21
eve foobarbazquxquux, key31, key32
`,
},
}
for _, testcase := range cases {
signerInfo := []SignerInfo{
{Name: "alice", Keys: []string{"key11", "key12"}},
{Name: "bob", Keys: []string{"key21"}},
{Name: "eve", Keys: []string{"key31", "key32", "foobarbazquxquux"}},
}
out := bytes.NewBufferString("")
testcase.context.Output = out
err := SignerInfoWrite(testcase.context, signerInfo)
if err != nil {
assert.Error(t, err, testcase.expected)
} else {
assert.Check(t, is.Equal(testcase.expected, out.String()))
}
signerInfo := []SignerInfo{
{Name: "alice", Keys: []string{"key11", "key12"}},
{Name: "bob", Keys: []string{"key21"}},
{Name: "eve", Keys: []string{"key31", "key32", "foobarbazquxquux"}},
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
if err := SignerInfoWrite(tc.context, signerInfo); err != nil {
assert.Error(t, err, tc.expected)
} else {
assert.Equal(t, out.String(), tc.expected)
}
})
}
}

View File

@ -460,10 +460,10 @@ func TestPrintSignerInfoSortOrder(t *testing.T) {
"signer1-foo": {"A"},
}
expected := `SIGNER KEYS
signer1-foo A
signer2-foo B
signer10-foo C
expected := `SIGNER KEYS
signer1-foo A
signer2-foo B
signer10-foo C
`
buf := new(bytes.Buffer)
assert.NilError(t, printSignerInfo(buf, roleToKeyIDs))

View File

@ -1,8 +1,8 @@
Signatures for signed-repo
SIGNED TAG DIGEST SIGNERS
green 677265656e2d646967657374 (Repo Admin)
SIGNED TAG DIGEST SIGNERS
green 677265656e2d646967657374 (Repo Admin)
Administrative keys for signed-repo

View File

@ -1,16 +1,16 @@
Signatures for signed-repo
SIGNED TAG DIGEST SIGNERS
blue 626c75652d646967657374 alice
green 677265656e2d646967657374 (Repo Admin)
red 7265642d646967657374 alice, bob
SIGNED TAG DIGEST SIGNERS
blue 626c75652d646967657374 alice
green 677265656e2d646967657374 (Repo Admin)
red 7265642d646967657374 alice, bob
List of signers and their keys for signed-repo
SIGNER KEYS
alice A
bob B
SIGNER KEYS
alice A
bob B
Administrative keys for signed-repo

View File

@ -1,8 +1,8 @@
Signatures for signed-repo:green
SIGNED TAG DIGEST SIGNERS
green 677265656e2d646967657374 (Repo Admin)
SIGNED TAG DIGEST SIGNERS
green 677265656e2d646967657374 (Repo Admin)
Administrative keys for signed-repo:green

View File

@ -4,9 +4,9 @@ No signatures for signed-repo:unsigned
List of signers and their keys for signed-repo:unsigned
SIGNER KEYS
alice A
bob B
SIGNER KEYS
alice A
bob B
Administrative keys for signed-repo:unsigned

View File

@ -1,4 +1,4 @@
DRIVER VOLUME NAME
local baz
bar foo
local volume
DRIVER VOLUME NAME
local baz
bar foo
local volume

View File

@ -1,3 +1,3 @@
NAME DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
default * Current DOCKER_HOST based configuration unix:///var/run/docker.sock https://someserver (zoinx) swarm
remote my remote cluster ssh://someserver https://someserver (default) kubernetes
NAME DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
default * Current DOCKER_HOST based configuration unix:///var/run/docker.sock https://someserver (zoinx) swarm
remote my remote cluster ssh://someserver https://someserver (default) kubernetes