Merge pull request #2699 from thaJeztah/reduce_table_width

formatter: reduce minimum width for columns in table-view
This commit is contained in:
Silvin Lubecki 2020-09-01 10:11:18 +02:00 committed by GitHub
commit ed8ce81d58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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/cli/cli/command/formatter"
"github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/swarm"
"gotest.tools/v3/assert" "gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
) )
func TestConfigContextFormatWrite(t *testing.T) { func TestConfigContextFormatWrite(t *testing.T) {
@ -30,9 +29,9 @@ func TestConfigContextFormatWrite(t *testing.T) {
}, },
// Table format // Table format
{formatter.Context{Format: NewFormat("table", false)}, {formatter.Context{Format: NewFormat("table", false)},
`ID NAME CREATED UPDATED `ID NAME CREATED UPDATED
1 passwords Less than a second ago Less than a second ago 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 2 id_rsa Less than a second ago Less than a second ago
`}, `},
{formatter.Context{Format: NewFormat("table {{.Name}}", true)}, {formatter.Context{Format: NewFormat("table {{.Name}}", true)},
`NAME `NAME
@ -53,13 +52,16 @@ id_rsa
Meta: swarm.Meta{CreatedAt: time.Now(), UpdatedAt: time.Now()}, Meta: swarm.Meta{CreatedAt: time.Now(), UpdatedAt: time.Now()},
Spec: swarm.ConfigSpec{Annotations: swarm.Annotations{Name: "id_rsa"}}}, Spec: swarm.ConfigSpec{Annotations: swarm.Annotations{Name: "id_rsa"}}},
} }
for _, testcase := range cases { for _, tc := range cases {
out := bytes.NewBufferString("") tc := tc
testcase.context.Output = out t.Run(string(tc.context.Format), func(t *testing.T) {
if err := FormatWrite(testcase.context, configs); err != nil { var out bytes.Buffer
assert.ErrorContains(t, err, testcase.expected) tc.context.Output = &out
} else { if err := FormatWrite(tc.context, configs); err != nil {
assert.Check(t, is.Equal(out.String(), testcase.expected)) 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 NAME CREATED UPDATED
ID-1-foo 1-foo 2 hours ago About an hour ago 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-2-foo 2-foo 2 hours ago About an hour ago
ID-10-foo 10-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 NAME CREATED UPDATED
ID-bar bar 2 hours ago About an hour ago ID-bar bar 2 hours ago About an hour ago
ID-foo foo 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/api/types/container"
"github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/archive"
"gotest.tools/v3/assert" "gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
) )
func TestDiffContextFormatWrite(t *testing.T) { func TestDiffContextFormatWrite(t *testing.T) {
@ -19,10 +18,10 @@ func TestDiffContextFormatWrite(t *testing.T) {
}{ }{
{ {
formatter.Context{Format: NewDiffFormat("table")}, formatter.Context{Format: NewDiffFormat("table")},
`CHANGE TYPE PATH `CHANGE TYPE PATH
C /var/log/app.log C /var/log/app.log
A /usr/app/app.js A /usr/app/app.js
D /usr/app/old_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"}, {Kind: archive.ChangeDelete, Path: "/usr/app/old_app.js"},
} }
for _, testcase := range cases { for _, tc := range cases {
out := bytes.NewBufferString("") tc := tc
testcase.context.Output = out t.Run(string(tc.context.Format), func(t *testing.T) {
err := DiffFormatWrite(testcase.context, diffs) out := bytes.NewBufferString("")
if err != nil { tc.context.Output = out
assert.Error(t, err, testcase.expected) err := DiffFormatWrite(tc.context, diffs)
} else { if err != nil {
assert.Check(t, is.Equal(testcase.expected, out.String())) 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) { func TestContainerStatsContextWriteWindows(t *testing.T) {
tt := []struct { cases := []struct {
context formatter.Context context formatter.Context
expected string 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 { for _, tc := range cases {
stats := []StatsEntry{ tc := tc
{ t.Run(string(tc.context.Format), func(t *testing.T) {
Container: "container1", var out bytes.Buffer
CPUPercentage: 20, tc.context.Output = &out
Memory: 20, err := statsFormatWrite(tc.context, stats, "windows", false)
MemoryLimit: 20, if err != nil {
MemoryPercentage: 20, assert.Error(t, err, tc.expected)
NetworkRx: 20, } else {
NetworkTx: 20, assert.Equal(t, out.String(), tc.expected)
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()))
}
} }
} }
func TestContainerStatsContextWriteWithNoStats(t *testing.T) { func TestContainerStatsContextWriteWithNoStats(t *testing.T) {
var out bytes.Buffer var out bytes.Buffer
contexts := []struct { cases := []struct {
context formatter.Context context formatter.Context
expected string expected string
}{ }{
@ -217,22 +220,26 @@ func TestContainerStatsContextWriteWithNoStats(t *testing.T) {
Format: "table {{.Container}}\t{{.CPUPerc}}", Format: "table {{.Container}}\t{{.CPUPerc}}",
Output: &out, Output: &out,
}, },
"CONTAINER CPU %\n", "CONTAINER CPU %\n",
}, },
} }
for _, context := range contexts { for _, tc := range cases {
statsFormatWrite(context.context, []StatsEntry{}, "linux", false) tc := tc
assert.Check(t, is.Equal(context.expected, out.String())) t.Run(string(tc.context.Format), func(t *testing.T) {
// Clean buffer err := statsFormatWrite(tc.context, []StatsEntry{}, "linux", false)
out.Reset() assert.NilError(t, err)
assert.Equal(t, out.String(), tc.expected)
// Clean buffer
out.Reset()
})
} }
} }
func TestContainerStatsContextWriteWithNoStatsWindows(t *testing.T) { func TestContainerStatsContextWriteWithNoStatsWindows(t *testing.T) {
var out bytes.Buffer var out bytes.Buffer
contexts := []struct { cases := []struct {
context formatter.Context context formatter.Context
expected string expected string
}{ }{
@ -248,22 +255,25 @@ func TestContainerStatsContextWriteWithNoStatsWindows(t *testing.T) {
Format: "table {{.Container}}\t{{.MemUsage}}", Format: "table {{.Container}}\t{{.MemUsage}}",
Output: &out, Output: &out,
}, },
"CONTAINER PRIV WORKING SET\n", "CONTAINER PRIV WORKING SET\n",
}, },
{ {
formatter.Context{ formatter.Context{
Format: "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}", Format: "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}",
Output: &out, Output: &out,
}, },
"CONTAINER CPU % PRIV WORKING SET\n", "CONTAINER CPU % PRIV WORKING SET\n",
}, },
} }
for _, context := range contexts { for _, tc := range cases {
statsFormatWrite(context.context, []StatsEntry{}, "windows", false) tc := tc
assert.Check(t, is.Equal(context.expected, out.String())) t.Run(string(tc.context.Format), func(t *testing.T) {
// Clean buffer err := statsFormatWrite(tc.context, []StatsEntry{}, "windows", false)
out.Reset() 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 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 c1
container_id busybox:latest "top" Less than a second ago Up 1 second c2,foo/bar 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 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 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 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 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 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 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 NAME DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR
current * description of current https://someswarmserver https://someserver (default) all current * description of current https://someswarmserver https://someserver (default) all
default Current DOCKER_HOST based configuration unix:///var/run/docker.sock swarm default Current DOCKER_HOST based configuration unix:///var/run/docker.sock swarm
other description of other https://someswarmserver https://someserver (default) all other description of other https://someswarmserver https://someserver (default) all
unset description of unset https://someswarmserver https://someserver (default) unset description of unset https://someswarmserver https://someserver (default)

View File

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

View File

@ -5,7 +5,6 @@ import (
"testing" "testing"
"gotest.tools/v3/assert" "gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/golden" "gotest.tools/v3/golden"
) )
@ -21,30 +20,30 @@ func TestDiskUsageContextFormatWrite(t *testing.T) {
Format: NewDiskUsageFormat("table", false), Format: NewDiskUsageFormat("table", false),
}, },
Verbose: false}, Verbose: false},
`TYPE TOTAL ACTIVE SIZE RECLAIMABLE `TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 0 0 0B 0B Images 0 0 0B 0B
Containers 0 0 0B 0B Containers 0 0 0B 0B
Local Volumes 0 0 0B 0B Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B Build Cache 0 0 0B 0B
`, `,
}, },
{ {
DiskUsageContext{Verbose: true, Context: Context{Format: NewDiskUsageFormat("table", true)}}, DiskUsageContext{Verbose: true, Context: Context{Format: NewDiskUsageFormat("table", true)}},
`Images space usage: `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: 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: Local Volumes space usage:
VOLUME NAME LINKS SIZE VOLUME NAME LINKS SIZE
Build cache usage: 0B 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), Format: NewDiskUsageFormat("table", false),
}, },
}, },
`TYPE TOTAL ACTIVE SIZE RECLAIMABLE `TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 0 0 0B 0B Images 0 0 0B 0B
Containers 0 0 0B 0B Containers 0 0 0B 0B
Local Volumes 0 0 0B 0B Local Volumes 0 0 0B 0B
Build Cache 0 0 0B 0B Build Cache 0 0 0B 0B
`, `,
}, },
{ {
@ -107,13 +106,16 @@ Build Cache 0 0 0B
}, },
} }
for _, testcase := range cases { for _, tc := range cases {
out := bytes.NewBufferString("") tc := tc
testcase.context.Output = out t.Run(string(tc.context.Format), func(t *testing.T) {
if err := testcase.context.Write(); err != nil { var out bytes.Buffer
assert.Check(t, is.Equal(testcase.expected, err.Error())) tc.context.Output = &out
} else { if err := tc.context.Write(); err != nil {
assert.Check(t, is.Equal(testcase.expected, out.String())) 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) { func (c *Context) postFormat(tmpl *template.Template, subContext SubContext) {
if c.Format.IsTable() { 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("") buffer := bytes.NewBufferString("")
tmpl.Funcs(templates.HeaderFunctions).Execute(buffer, subContext.FullHeader()) tmpl.Funcs(templates.HeaderFunctions).Execute(buffer, subContext.FullHeader())
buffer.WriteTo(t) buffer.WriteTo(t)

View File

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

View File

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

View File

@ -59,7 +59,6 @@ func TestVolumeContextWrite(t *testing.T) {
context Context context Context
expected string expected string
}{ }{
// Errors // Errors
{ {
Context{Format: "{{InvalidFunction}}"}, Context{Format: "{{InvalidFunction}}"},
@ -74,9 +73,9 @@ func TestVolumeContextWrite(t *testing.T) {
// Table format // Table format
{ {
Context{Format: NewVolumeFormat("table", false)}, Context{Format: NewVolumeFormat("table", false)},
`DRIVER VOLUME NAME `DRIVER VOLUME NAME
foo foobar_baz foo foobar_baz
bar foobar_bar bar foobar_bar
`, `,
}, },
{ {
@ -125,19 +124,23 @@ foobar_bar
}, },
} }
for _, testcase := range cases { volumes := []*types.Volume{
volumes := []*types.Volume{ {Name: "foobar_baz", Driver: "foo"},
{Name: "foobar_baz", Driver: "foo"}, {Name: "foobar_bar", Driver: "bar"},
{Name: "foobar_bar", Driver: "bar"}, }
}
out := bytes.NewBufferString("") for _, tc := range cases {
testcase.context.Output = out tc := tc
err := VolumeWrite(testcase.context, volumes) t.Run(string(tc.context.Format), func(t *testing.T) {
if err != nil { var out bytes.Buffer
assert.Error(t, err, testcase.expected) tc.context.Output = &out
} else { err := VolumeWrite(tc.context, volumes)
assert.Check(t, is.Equal(testcase.expected, out.String())) 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/api/types/image"
"github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/stringid"
"gotest.tools/v3/assert" "gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/skip" "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"}}, {ID: "imageID4", Created: unixTime, CreatedBy: "/bin/bash grep", Size: int64(182964289), Comment: "Hi", Tags: []string{"image:tag2"}},
} }
// nolint: lll // nolint: lll
expectedNoTrunc := `IMAGE CREATED CREATED BY SIZE COMMENT 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 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 imageID2 24 hours ago /bin/bash echo 183MB Hi
imageID3 24 hours ago /bin/bash ls 183MB Hi imageID3 24 hours ago /bin/bash ls 183MB Hi
imageID4 24 hours ago /bin/bash grep 183MB Hi imageID4 24 hours ago /bin/bash grep 183MB Hi
` `
expectedTrunc := `IMAGE CREATED CREATED BY SIZE COMMENT expectedTrunc := `IMAGE CREATED CREATED BY SIZE COMMENT
imageID1 24 hours ago /bin/bash ls && npm i && npm run test && kar 183MB Hi imageID1 24 hours ago /bin/bash ls && npm i && npm run test && kar 183MB Hi
imageID2 24 hours ago /bin/bash echo 183MB Hi imageID2 24 hours ago /bin/bash echo 183MB Hi
imageID3 24 hours ago /bin/bash ls 183MB Hi imageID3 24 hours ago /bin/bash ls 183MB Hi
imageID4 24 hours ago /bin/bash grep 183MB Hi imageID4 24 hours ago /bin/bash grep 183MB Hi
` `
contexts := []struct { cases := []struct {
context formatter.Context context formatter.Context
expected string expected string
}{ }{
@ -221,10 +220,14 @@ imageID4 24 hours ago /bin/bash grep
}, },
} }
for _, context := range contexts { for _, tc := range cases {
HistoryWrite(context.context, true, histories) tc := tc
assert.Check(t, is.Equal(context.expected, out.String())) t.Run(string(tc.context.Format), func(t *testing.T) {
// Clean buffer err := HistoryWrite(tc.context, true, histories)
out.Reset() 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 // Table format
{ {
formatter.Context{Format: NewFormat("table", false)}, formatter.Context{Format: NewFormat("table", false)},
`NETWORK ID NAME DRIVER SCOPE `NETWORK ID NAME DRIVER SCOPE
networkID1 foobar_baz foo local networkID1 foobar_baz foo local
networkID2 foobar_bar bar 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") timestamp1, _ := time.Parse("2006-01-02", "2016-01-01")
timestamp2, _ := time.Parse("2006-01-02", "2017-01-01") timestamp2, _ := time.Parse("2006-01-02", "2017-01-01")
for _, testcase := range cases { networks := []types.NetworkResource{
networks := []types.NetworkResource{ {ID: "networkID1", Name: "foobar_baz", Driver: "foo", Scope: "local", Created: timestamp1},
{ID: "networkID1", Name: "foobar_baz", Driver: "foo", Scope: "local", Created: timestamp1}, {ID: "networkID2", Name: "foobar_bar", Driver: "bar", Scope: "local", Created: timestamp2},
{ID: "networkID2", Name: "foobar_bar", Driver: "bar", Scope: "local", Created: timestamp2}, }
}
out := bytes.NewBufferString("") for _, tc := range cases {
testcase.context.Output = out tc := tc
err := FormatWrite(testcase.context, networks) t.Run(string(tc.context.Format), func(t *testing.T) {
if err != nil { var out bytes.Buffer
assert.Error(t, err, testcase.expected) tc.context.Output = &out
} else { err := FormatWrite(tc.context, networks)
assert.Check(t, is.Equal(testcase.expected, out.String())) 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 NETWORK ID NAME DRIVER SCOPE
123454321 network_1 09.7.01 global 123454321 network_1 09.7.01 global

View File

@ -76,10 +76,10 @@ func TestNodeContextWrite(t *testing.T) {
// Table format // Table format
{ {
context: formatter.Context{Format: NewFormat("table", false)}, context: formatter.Context{Format: NewFormat("table", false)},
expected: `ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION expected: `ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
nodeID1 foobar_baz Foo Drain Leader 18.03.0-ce nodeID1 foobar_baz Foo Drain Leader 18.03.0-ce
nodeID2 foobar_bar Bar Active Reachable 1.2.3 nodeID2 foobar_bar Bar Active Reachable 1.2.3
nodeID3 foobar_boo Boo Active ` + "\n", // (to preserve whitespace) nodeID3 foobar_boo Boo Active ` + "\n", // (to preserve whitespace)
clusterInfo: swarm.ClusterInfo{TLSInfo: swarm.TLSInfo{TrustRoot: "hi"}}, 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)}, context: formatter.Context{Format: NewFormat("table {{.ID}}\t{{.Hostname}}\t{{.TLSStatus}}", false)},
expected: `ID HOSTNAME TLS STATUS expected: `ID HOSTNAME TLS STATUS
nodeID1 foobar_baz Needs Rotation nodeID1 foobar_baz Needs Rotation
nodeID2 foobar_bar Ready nodeID2 foobar_bar Ready
nodeID3 foobar_boo Unknown nodeID3 foobar_boo Unknown
`, `,
clusterInfo: swarm.ClusterInfo{TLSInfo: swarm.TLSInfo{TrustRoot: "hi"}}, clusterInfo: swarm.ClusterInfo{TLSInfo: swarm.TLSInfo{TrustRoot: "hi"}},
}, },
{ // no cluster TLS status info, TLS status for all nodes is unknown { // no cluster TLS status info, TLS status for all nodes is unknown
context: formatter.Context{Format: NewFormat("table {{.ID}}\t{{.Hostname}}\t{{.TLSStatus}}", false)}, context: formatter.Context{Format: NewFormat("table {{.ID}}\t{{.Hostname}}\t{{.TLSStatus}}", false)},
expected: `ID HOSTNAME TLS STATUS expected: `ID HOSTNAME TLS STATUS
nodeID1 foobar_baz Unknown nodeID1 foobar_baz Unknown
nodeID2 foobar_bar Unknown nodeID2 foobar_bar Unknown
nodeID3 foobar_boo Unknown nodeID3 foobar_boo Unknown
`, `,
clusterInfo: swarm.ClusterInfo{}, clusterInfo: swarm.ClusterInfo{},
}, },
@ -167,48 +167,53 @@ foobar_boo Unknown
}, },
} }
for _, testcase := range cases { nodes := []swarm.Node{
nodes := []swarm.Node{ {
{ ID: "nodeID1",
ID: "nodeID1", Description: swarm.NodeDescription{
Description: swarm.NodeDescription{ Hostname: "foobar_baz",
Hostname: "foobar_baz", TLSInfo: swarm.TLSInfo{TrustRoot: "no"},
TLSInfo: swarm.TLSInfo{TrustRoot: "no"}, Engine: swarm.EngineDescription{EngineVersion: "18.03.0-ce"},
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},
}, },
{ Status: swarm.NodeStatus{State: swarm.NodeState("foo")},
ID: "nodeID2", Spec: swarm.NodeSpec{Availability: swarm.NodeAvailability("drain")},
Description: swarm.NodeDescription{ ManagerStatus: &swarm.ManagerStatus{Leader: true},
Hostname: "foobar_bar", },
TLSInfo: swarm.TLSInfo{TrustRoot: "hi"}, {
Engine: swarm.EngineDescription{EngineVersion: "1.2.3"}, ID: "nodeID2",
}, Description: swarm.NodeDescription{
Status: swarm.NodeStatus{State: swarm.NodeState("bar")}, Hostname: "foobar_bar",
Spec: swarm.NodeSpec{Availability: swarm.NodeAvailability("active")}, TLSInfo: swarm.TLSInfo{TrustRoot: "hi"},
ManagerStatus: &swarm.ManagerStatus{ Engine: swarm.EngineDescription{EngineVersion: "1.2.3"},
Leader: false,
Reachability: swarm.Reachability("Reachable"),
},
}, },
{ Status: swarm.NodeStatus{State: swarm.NodeState("bar")},
ID: "nodeID3", Spec: swarm.NodeSpec{Availability: swarm.NodeAvailability("active")},
Description: swarm.NodeDescription{Hostname: "foobar_boo"}, ManagerStatus: &swarm.ManagerStatus{
Status: swarm.NodeStatus{State: swarm.NodeState("boo")}, Leader: false,
Spec: swarm.NodeSpec{Availability: swarm.NodeAvailability("active")}, Reachability: swarm.Reachability("Reachable"),
}, },
} },
out := bytes.NewBufferString("") {
testcase.context.Output = out ID: "nodeID3",
err := FormatWrite(testcase.context, nodes, types.Info{Swarm: swarm.Info{Cluster: &testcase.clusterInfo}}) Description: swarm.NodeDescription{Hostname: "foobar_boo"},
if err != nil { Status: swarm.NodeStatus{State: swarm.NodeState("boo")},
assert.Error(t, err, testcase.expected) Spec: swarm.NodeSpec{Availability: swarm.NodeAvailability("active")},
} 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
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 ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
nodeID3 node-1-foo Ready Active 1.13.0 nodeID3 node-1-foo Ready Active 1.13.0
nodeID1 * node-2-foo Ready Active Leader . nodeID1 * node-2-foo Ready Active Leader .
nodeID2 node-10-foo Ready Active Reachable 18.03.0-ce 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 ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
taskID rl02d5gwz6chzu7il5fhtb8be.1 myimage:mytag defaultNodeName Ready Ready 2 hours ago *:80->80/tcp 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 ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
taskID1 failure.1 myimage:mytag defaultNodeName Ready Ready 2 hours ago "a task error" 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" 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" 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 // Table format
{ {
formatter.Context{Format: NewFormat("table", false)}, formatter.Context{Format: NewFormat("table", false)},
`ID NAME DESCRIPTION ENABLED `ID NAME DESCRIPTION ENABLED
pluginID1 foobar_baz description 1 true pluginID1 foobar_baz description 1 true
pluginID2 foobar_bar description 2 false pluginID2 foobar_bar description 2 false
`, `,
}, },
{ {
@ -125,19 +125,24 @@ foobar_bar
}, },
} }
for _, testcase := range cases { plugins := []*types.Plugin{
plugins := []*types.Plugin{ {ID: "pluginID1", Name: "foobar_baz", Config: types.PluginConfig{Description: "description 1"}, Enabled: true},
{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},
{ID: "pluginID2", Name: "foobar_bar", Config: types.PluginConfig{Description: "description 2"}, Enabled: false}, }
}
out := bytes.NewBufferString("") for _, tc := range cases {
testcase.context.Output = out tc := tc
err := FormatWrite(testcase.context, plugins) t.Run(string(tc.context.Format), func(t *testing.T) {
if err != nil { var out bytes.Buffer
assert.Error(t, err, testcase.expected) tc.context.Output = &out
} else {
assert.Check(t, is.Equal(testcase.expected, out.String())) 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 NAME DESCRIPTION ENABLED
id-foo name-foo desc-bar true id-foo name-foo desc-bar true

View File

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

View File

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

View File

@ -1,3 +1,3 @@
ID NAME DRIVER CREATED UPDATED ID NAME DRIVER CREATED UPDATED
ID-bar bar 2 hours ago About an hour ago ID-bar bar 2 hours ago About an hour ago
ID-foo foo 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 // Table format
{ {
formatter.Context{Format: NewListFormat("table", false)}, formatter.Context{Format: NewListFormat("table", false)},
`ID NAME MODE REPLICAS IMAGE PORTS `ID NAME MODE REPLICAS IMAGE PORTS
02_bar bar replicated 2/4 *:80->8090/udp 02_bar bar replicated 2/4 *:80->8090/udp
01_baz baz global 1/3 *:80->8080/tcp 01_baz baz global 1/3 *:80->8080/tcp
04_qux2 qux2 replicated 3/3 (max 2 per node) 04_qux2 qux2 replicated 3/3 (max 2 per node)
03_qux10 qux10 replicated 2/3 (max 1 per node) 03_qux10 qux10 replicated 2/3 (max 1 per node)
05_job1 zarp1 replicated job 2/3 (5/10 completed) 05_job1 zarp1 replicated job 2/3 (5/10 completed)
06_job2 zarp2 global job 1/1 (3/4 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)}, formatter.Context{Format: NewListFormat("table {{.Name}}\t{{.Mode}}", false)},
`NAME MODE `NAME MODE
bar replicated bar replicated
baz global baz global
qux2 replicated qux2 replicated
qux10 replicated qux10 replicated
zarp1 replicated job zarp1 replicated job
zarp2 global job zarp2 global job
`, `,
}, },
{ {
@ -109,126 +109,130 @@ zarp2
}, },
} }
for _, testcase := range cases { services := []swarm.Service{
services := []swarm.Service{ {
{ ID: "01_baz",
ID: "01_baz", Spec: swarm.ServiceSpec{
Spec: swarm.ServiceSpec{ Annotations: swarm.Annotations{Name: "baz"},
Annotations: swarm.Annotations{Name: "baz"}, Mode: swarm.ServiceMode{
Mode: swarm.ServiceMode{ Global: &swarm.GlobalService{},
Global: &swarm.GlobalService{},
},
},
Endpoint: swarm.Endpoint{
Ports: []swarm.PortConfig{
{
PublishMode: "ingress",
PublishedPort: 80,
TargetPort: 8080,
Protocol: "tcp",
},
},
},
ServiceStatus: &swarm.ServiceStatus{
RunningTasks: 1,
DesiredTasks: 3,
}, },
}, },
{ Endpoint: swarm.Endpoint{
ID: "02_bar", Ports: []swarm.PortConfig{
Spec: swarm.ServiceSpec{ {
Annotations: swarm.Annotations{Name: "bar"}, PublishMode: "ingress",
Mode: swarm.ServiceMode{ PublishedPort: 80,
Replicated: &swarm.ReplicatedService{}, 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,
},
}, },
{ ServiceStatus: &swarm.ServiceStatus{
ID: "03_qux10", RunningTasks: 1,
Spec: swarm.ServiceSpec{ DesiredTasks: 3,
Annotations: swarm.Annotations{Name: "qux10"}, },
Mode: swarm.ServiceMode{ },
Replicated: &swarm.ReplicatedService{}, {
}, ID: "02_bar",
TaskTemplate: swarm.TaskSpec{ Spec: swarm.ServiceSpec{
Placement: &swarm.Placement{MaxReplicas: 1}, Annotations: swarm.Annotations{Name: "bar"},
}, Mode: swarm.ServiceMode{
}, Replicated: &swarm.ReplicatedService{},
ServiceStatus: &swarm.ServiceStatus{
RunningTasks: 2,
DesiredTasks: 3,
}, },
}, },
{ Endpoint: swarm.Endpoint{
ID: "04_qux2", Ports: []swarm.PortConfig{
Spec: swarm.ServiceSpec{ {
Annotations: swarm.Annotations{Name: "qux2"}, PublishMode: "ingress",
Mode: swarm.ServiceMode{ PublishedPort: 80,
Replicated: &swarm.ReplicatedService{}, TargetPort: 8090,
Protocol: "udp",
}, },
TaskTemplate: swarm.TaskSpec{
Placement: &swarm.Placement{MaxReplicas: 2},
},
},
ServiceStatus: &swarm.ServiceStatus{
RunningTasks: 3,
DesiredTasks: 3,
}, },
}, },
{ ServiceStatus: &swarm.ServiceStatus{
ID: "05_job1", RunningTasks: 2,
Spec: swarm.ServiceSpec{ DesiredTasks: 4,
Annotations: swarm.Annotations{Name: "zarp1"}, },
Mode: swarm.ServiceMode{ },
ReplicatedJob: &swarm.ReplicatedJob{ {
MaxConcurrent: &varThree, ID: "03_qux10",
TotalCompletions: &varTen, Spec: swarm.ServiceSpec{
}, Annotations: swarm.Annotations{Name: "qux10"},
}, Mode: swarm.ServiceMode{
Replicated: &swarm.ReplicatedService{},
}, },
ServiceStatus: &swarm.ServiceStatus{ TaskTemplate: swarm.TaskSpec{
RunningTasks: 2, Placement: &swarm.Placement{MaxReplicas: 1},
DesiredTasks: 3,
CompletedTasks: 5,
}, },
}, },
{ ServiceStatus: &swarm.ServiceStatus{
ID: "06_job2", RunningTasks: 2,
Spec: swarm.ServiceSpec{ DesiredTasks: 3,
Annotations: swarm.Annotations{Name: "zarp2"}, },
Mode: swarm.ServiceMode{ },
GlobalJob: &swarm.GlobalJob{}, {
}, ID: "04_qux2",
Spec: swarm.ServiceSpec{
Annotations: swarm.Annotations{Name: "qux2"},
Mode: swarm.ServiceMode{
Replicated: &swarm.ReplicatedService{},
}, },
ServiceStatus: &swarm.ServiceStatus{ TaskTemplate: swarm.TaskSpec{
RunningTasks: 1, Placement: &swarm.Placement{MaxReplicas: 2},
DesiredTasks: 1,
CompletedTasks: 3,
}, },
}, },
} ServiceStatus: &swarm.ServiceStatus{
out := bytes.NewBufferString("") RunningTasks: 3,
testcase.context.Output = out DesiredTasks: 3,
err := ListFormatWrite(testcase.context, services) },
if err != nil { },
assert.Error(t, err, testcase.expected) {
} else { ID: "05_job1",
assert.Check(t, is.Equal(testcase.expected, out.String())) 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" "github.com/docker/cli/cli/command/formatter"
"gotest.tools/v3/assert" "gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
) )
func TestStackContextWrite(t *testing.T) { func TestStackContextWrite(t *testing.T) {
@ -28,17 +27,17 @@ func TestStackContextWrite(t *testing.T) {
// Table format // Table format
{ {
formatter.Context{Format: SwarmStackTableFormat}, formatter.Context{Format: SwarmStackTableFormat},
`NAME SERVICES ORCHESTRATOR `NAME SERVICES ORCHESTRATOR
baz 2 orchestrator1 baz 2 orchestrator1
bar 1 orchestrator2 bar 1 orchestrator2
`, `,
}, },
// Kubernetes table format adds Namespace column // Kubernetes table format adds Namespace column
{ {
formatter.Context{Format: KubernetesStackTableFormat}, formatter.Context{Format: KubernetesStackTableFormat},
`NAME SERVICES ORCHESTRATOR NAMESPACE `NAME SERVICES ORCHESTRATOR NAMESPACE
baz 2 orchestrator1 namespace1 baz 2 orchestrator1 namespace1
bar 1 orchestrator2 namespace2 bar 1 orchestrator2 namespace2
`, `,
}, },
{ {
@ -61,14 +60,17 @@ bar
{Name: "baz", Services: 2, Orchestrator: "orchestrator1", Namespace: "namespace1"}, {Name: "baz", Services: 2, Orchestrator: "orchestrator1", Namespace: "namespace1"},
{Name: "bar", Services: 1, Orchestrator: "orchestrator2", Namespace: "namespace2"}, {Name: "bar", Services: 1, Orchestrator: "orchestrator2", Namespace: "namespace2"},
} }
for _, testcase := range cases { for _, tc := range cases {
out := bytes.NewBufferString("") tc := tc
testcase.context.Output = out t.Run(string(tc.context.Format), func(t *testing.T) {
err := StackWrite(testcase.context, stacks) var out bytes.Buffer
if err != nil { tc.context.Output = &out
assert.Check(t, is.ErrorContains(err, testcase.expected))
} else { if err := StackWrite(tc.context, stacks); err != nil {
assert.Check(t, is.Equal(out.String(), testcase.expected)) assert.Error(t, err, tc.expected)
} } else {
assert.Equal(t, out.String(), tc.expected)
}
})
} }
} }

View File

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

View File

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

View File

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

View File

@ -1,2 +1,2 @@
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS 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-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 NAME MODE REPLICAS IMAGE PORTS
id-foo name-foo replicated 0/2 busybox:latest *:30000->3232/tcp 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{
tasks := []swarm.Task{ {ID: "taskID1"},
{ID: "taskID1"}, {ID: "taskID2"},
{ID: "taskID2"}, }
} names := map[string]string{
names := map[string]string{ "taskID1": "foobar_baz",
"taskID1": "foobar_baz", "taskID2": "foobar_bar",
"taskID2": "foobar_bar", }
} nodes := map[string]string{
nodes := map[string]string{ "taskID1": "foo1",
"taskID1": "foo1", "taskID2": "foo2",
"taskID2": "foo2", }
}
out := bytes.NewBufferString("") for _, tc := range cases {
testcase.context.Output = out tc := tc
err := FormatWrite(testcase.context, tasks, names, nodes) t.Run(string(tc.context.Format), func(t *testing.T) {
if err != nil { var out bytes.Buffer
assert.Error(t, err, testcase.expected) tc.context.Output = &out
} else {
assert.Check(t, is.Equal(testcase.expected, out.String())) 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 NAME NODE PORTS
foobar_baz foo1 foobar_baz foo1
foobar_bar foo2 foobar_bar foo2

View File

@ -1,3 +1,3 @@
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS 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-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-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 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-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-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{ formatter.Context{
Format: NewTrustTagFormat(), Format: NewTrustTagFormat(),
}, },
`SIGNED TAG DIGEST SIGNERS `SIGNED TAG DIGEST SIGNERS
tag1 deadbeef alice tag1 deadbeef alice
tag2 aaaaaaaa alice, bob tag2 aaaaaaaa alice, bob
tag3 bbbbbbbb tag3 bbbbbbbb
`, `,
}, },
} }
for _, testcase := range cases { signedTags := []SignedTagInfo{
signedTags := []SignedTagInfo{ {Name: "tag1", Digest: "deadbeef", Signers: []string{"alice"}},
{Name: "tag1", Digest: "deadbeef", Signers: []string{"alice"}}, {Name: "tag2", Digest: "aaaaaaaa", Signers: []string{"alice", "bob"}},
{Name: "tag2", Digest: "aaaaaaaa", Signers: []string{"alice", "bob"}}, {Name: "tag3", Digest: "bbbbbbbb", Signers: []string{}},
{Name: "tag3", Digest: "bbbbbbbb", Signers: []string{}}, }
}
out := bytes.NewBufferString("") for _, tc := range cases {
testcase.context.Output = out tc := tc
err := TagWrite(testcase.context, signedTags) t.Run(string(tc.context.Format), func(t *testing.T) {
if err != nil { var out bytes.Buffer
assert.Error(t, err, testcase.expected) tc.context.Output = &out
} else {
assert.Check(t, is.Equal(testcase.expected, out.String())) 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{ formatter.Context{
Format: NewTrustTagFormat(), Format: NewTrustTagFormat(),
}, },
`SIGNED TAG DIGEST SIGNERS `SIGNED TAG DIGEST SIGNERS
`, `,
} }
@ -166,7 +170,7 @@ func TestSignerInfoContextEmptyWrite(t *testing.T) {
formatter.Context{ formatter.Context{
Format: NewSignerInfoFormat(), Format: NewSignerInfoFormat(),
}, },
`SIGNER KEYS `SIGNER KEYS
`, `,
} }
emptySignerInfo := []SignerInfo{} emptySignerInfo := []SignerInfo{}
@ -203,10 +207,10 @@ func TestSignerInfoContextWrite(t *testing.T) {
Format: NewSignerInfoFormat(), Format: NewSignerInfoFormat(),
Trunc: true, Trunc: true,
}, },
`SIGNER KEYS `SIGNER KEYS
alice key11, key12 alice key11, key12
bob key21 bob key21
eve foobarbazqux, key31, key32 eve foobarbazqux, key31, key32
`, `,
}, },
// No truncation // No truncation
@ -214,27 +218,30 @@ eve foobarbazqux, key31, key32
formatter.Context{ formatter.Context{
Format: NewSignerInfoFormat(), Format: NewSignerInfoFormat(),
}, },
`SIGNER KEYS `SIGNER KEYS
alice key11, key12 alice key11, key12
bob key21 bob key21
eve foobarbazquxquux, key31, key32 eve foobarbazquxquux, key31, key32
`, `,
}, },
} }
for _, testcase := range cases { signerInfo := []SignerInfo{
signerInfo := []SignerInfo{ {Name: "alice", Keys: []string{"key11", "key12"}},
{Name: "alice", Keys: []string{"key11", "key12"}}, {Name: "bob", Keys: []string{"key21"}},
{Name: "bob", Keys: []string{"key21"}}, {Name: "eve", Keys: []string{"key31", "key32", "foobarbazquxquux"}},
{Name: "eve", Keys: []string{"key31", "key32", "foobarbazquxquux"}}, }
} for _, tc := range cases {
out := bytes.NewBufferString("") tc := tc
testcase.context.Output = out t.Run(string(tc.context.Format), func(t *testing.T) {
err := SignerInfoWrite(testcase.context, signerInfo) var out bytes.Buffer
if err != nil { tc.context.Output = &out
assert.Error(t, err, testcase.expected)
} else { if err := SignerInfoWrite(tc.context, signerInfo); err != nil {
assert.Check(t, is.Equal(testcase.expected, out.String())) 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"}, "signer1-foo": {"A"},
} }
expected := `SIGNER KEYS expected := `SIGNER KEYS
signer1-foo A signer1-foo A
signer2-foo B signer2-foo B
signer10-foo C signer10-foo C
` `
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
assert.NilError(t, printSignerInfo(buf, roleToKeyIDs)) assert.NilError(t, printSignerInfo(buf, roleToKeyIDs))

View File

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

View File

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

View File

@ -1,8 +1,8 @@
Signatures for signed-repo:green Signatures for signed-repo:green
SIGNED TAG DIGEST SIGNERS SIGNED TAG DIGEST SIGNERS
green 677265656e2d646967657374 (Repo Admin) green 677265656e2d646967657374 (Repo Admin)
Administrative keys for signed-repo:green 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 List of signers and their keys for signed-repo:unsigned
SIGNER KEYS SIGNER KEYS
alice A alice A
bob B bob B
Administrative keys for signed-repo:unsigned Administrative keys for signed-repo:unsigned

View File

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

View File

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