Skip to content

Commit 12992f7

Browse files
authored
Merge pull request #6073 from thaJeztah/format_cleanups_and_fixes
cli/command/formatter: fix .Labels format being randomized
2 parents cacd86c + 5ee17ee commit 12992f7

File tree

5 files changed

+108
-69
lines changed

5 files changed

+108
-69
lines changed

cli/command/formatter/container.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,14 @@ ports: {{- pad .Ports 1 0}}
6868

6969
// ContainerWrite renders the context for a list of containers
7070
func ContainerWrite(ctx Context, containers []container.Summary) error {
71-
render := func(format func(subContext SubContext) error) error {
71+
return ctx.Write(NewContainerContext(), func(format func(subContext SubContext) error) error {
7272
for _, ctr := range containers {
73-
err := format(&ContainerContext{trunc: ctx.Trunc, c: ctr})
74-
if err != nil {
73+
if err := format(&ContainerContext{trunc: ctx.Trunc, c: ctr}); err != nil {
7574
return err
7675
}
7776
}
7877
return nil
79-
}
80-
return ctx.Write(NewContainerContext(), render)
78+
})
8179
}
8280

8381
// ContainerContext is a struct used for rendering a list of containers in a Go template.
@@ -256,6 +254,7 @@ func (c *ContainerContext) Labels() string {
256254
for k, v := range c.c.Labels {
257255
joinLabels = append(joinLabels, k+"="+v)
258256
}
257+
sort.Strings(joinLabels)
259258
return strings.Join(joinLabels, ",")
260259
}
261260

cli/command/formatter/container_test.go

Lines changed: 55 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -371,62 +371,53 @@ size: 0B
371371
}
372372

373373
func TestContainerContextWriteWithNoContainers(t *testing.T) {
374-
out := bytes.NewBufferString("")
375-
containers := []container.Summary{}
376-
377374
cases := []struct {
378375
context Context
379376
expected string
380377
}{
381378
{
382379
context: Context{
383380
Format: "{{.Image}}",
384-
Output: out,
385381
},
386382
},
387383
{
388384
context: Context{
389385
Format: "table {{.Image}}",
390-
Output: out,
391386
},
392387
expected: "IMAGE\n",
393388
},
394389
{
395390
context: Context{
396391
Format: NewContainerFormat("{{.Image}}", false, true),
397-
Output: out,
398392
},
399393
},
400394
{
401395
context: Context{
402396
Format: NewContainerFormat("table {{.Image}}", false, true),
403-
Output: out,
404397
},
405398
expected: "IMAGE\n",
406399
},
407400
{
408401
context: Context{
409402
Format: "table {{.Image}}\t{{.Size}}",
410-
Output: out,
411403
},
412404
expected: "IMAGE SIZE\n",
413405
},
414406
{
415407
context: Context{
416408
Format: NewContainerFormat("table {{.Image}}\t{{.Size}}", false, true),
417-
Output: out,
418409
},
419410
expected: "IMAGE SIZE\n",
420411
},
421412
}
422413

423414
for _, tc := range cases {
424415
t.Run(string(tc.context.Format), func(t *testing.T) {
425-
err := ContainerWrite(tc.context, containers)
416+
out := new(bytes.Buffer)
417+
tc.context.Output = out
418+
err := ContainerWrite(tc.context, nil)
426419
assert.NilError(t, err)
427420
assert.Equal(t, out.String(), tc.expected)
428-
// Clean buffer
429-
out.Reset()
430421
})
431422
}
432423
}
@@ -506,28 +497,59 @@ func TestContainerContextWriteJSONField(t *testing.T) {
506497
}
507498

508499
func TestContainerBackCompat(t *testing.T) {
509-
containers := []container.Summary{{ID: "brewhaha"}}
510-
cases := []string{
511-
"ID",
512-
"Names",
513-
"Image",
514-
"Command",
515-
"CreatedAt",
516-
"RunningFor",
517-
"Ports",
518-
"Status",
519-
"Size",
520-
"Labels",
521-
"Mounts",
500+
createdAtTime := time.Now().AddDate(-1, 0, 0) // 1 year ago
501+
502+
ctrContext := container.Summary{
503+
ID: "aabbccddeeff",
504+
Names: []string{"/foobar_baz"},
505+
Image: "docker.io/library/ubuntu", // should this have canonical format or not?
506+
ImageID: "sha256:a5a665ff33eced1e0803148700880edab4269067ed77e27737a708d0d293fbf5", // should this have algo-prefix or not?
507+
ImageManifestDescriptor: nil,
508+
Command: "/bin/sh",
509+
Created: createdAtTime.UTC().Unix(),
510+
Ports: []container.Port{{PrivatePort: 8080, PublicPort: 8080, Type: "tcp"}},
511+
SizeRw: 123,
512+
SizeRootFs: 12345,
513+
Labels: map[string]string{"label1": "value1", "label2": "value2"},
514+
State: "running",
515+
Status: "running",
516+
HostConfig: struct {
517+
NetworkMode string `json:",omitempty"`
518+
Annotations map[string]string `json:",omitempty"`
519+
}{
520+
NetworkMode: "bridge",
521+
Annotations: map[string]string{
522+
"com.example.annotation": "hello",
523+
},
524+
},
525+
NetworkSettings: nil,
526+
Mounts: nil,
522527
}
523-
buf := bytes.NewBuffer(nil)
524-
for _, c := range cases {
525-
ctx := Context{Format: Format(fmt.Sprintf("{{ .%s }}", c)), Output: buf}
526-
if err := ContainerWrite(ctx, containers); err != nil {
527-
t.Logf("could not render template for field '%s': %v", c, err)
528-
t.Fail()
529-
}
530-
buf.Reset()
528+
529+
tests := []struct {
530+
field string
531+
expected string
532+
}{
533+
{field: "ID", expected: "aabbccddeeff"},
534+
{field: "Names", expected: "foobar_baz"},
535+
{field: "Image", expected: "docker.io/library/ubuntu"},
536+
{field: "Command", expected: `"/bin/sh"`},
537+
{field: "CreatedAt", expected: time.Unix(createdAtTime.Unix(), 0).String()},
538+
{field: "RunningFor", expected: "12 months ago"},
539+
{field: "Ports", expected: "8080/tcp"},
540+
{field: "Status", expected: "running"},
541+
{field: "Size", expected: "123B (virtual 12.3kB)"},
542+
{field: "Labels", expected: "label1=value1,label2=value2"},
543+
{field: "Mounts", expected: ""},
544+
}
545+
546+
for _, tc := range tests {
547+
t.Run(tc.field, func(t *testing.T) {
548+
buf := new(bytes.Buffer)
549+
ctx := Context{Format: Format(fmt.Sprintf("{{ .%s }}", tc.field)), Output: buf}
550+
assert.NilError(t, ContainerWrite(ctx, []container.Summary{ctrContext}))
551+
assert.Check(t, is.Equal(strings.TrimSpace(buf.String()), tc.expected))
552+
})
531553
}
532554
}
533555

cli/command/formatter/disk_usage.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ type DiskUsageContext struct {
4343
}
4444

4545
func (ctx *DiskUsageContext) startSubsection(format string) (*template.Template, error) {
46-
ctx.buffer = bytes.NewBufferString("")
46+
ctx.buffer = &bytes.Buffer{}
4747
ctx.header = ""
4848
ctx.Format = Format(format)
4949
ctx.preFormat()
@@ -87,7 +87,7 @@ func (ctx *DiskUsageContext) Write() (err error) {
8787
if ctx.Verbose {
8888
return ctx.verboseWrite()
8989
}
90-
ctx.buffer = bytes.NewBufferString("")
90+
ctx.buffer = &bytes.Buffer{}
9191
ctx.preFormat()
9292

9393
tmpl, err := ctx.parseFormat()

cli/command/formatter/formatter.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ func (c *Context) parseFormat() (*template.Template, error) {
8282
}
8383

8484
func (c *Context) postFormat(tmpl *template.Template, subContext SubContext) {
85+
if c.Output == nil {
86+
c.Output = io.Discard
87+
}
8588
if c.Format.IsTable() {
8689
t := tabwriter.NewWriter(c.Output, 10, 1, 3, ' ', 0)
8790
buffer := bytes.NewBufferString("")
@@ -111,7 +114,7 @@ type SubFormat func(func(SubContext) error) error
111114

112115
// Write the template to the buffer using this Context
113116
func (c *Context) Write(sub SubContext, f SubFormat) error {
114-
c.buffer = bytes.NewBufferString("")
117+
c.buffer = &bytes.Buffer{}
115118
c.preFormat()
116119

117120
tmpl, err := c.parseFormat()

cli/command/inspect/inspector.go

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,29 @@ type Inspector interface {
2626

2727
// TemplateInspector uses a text template to inspect elements.
2828
type TemplateInspector struct {
29-
outputStream io.Writer
30-
buffer *bytes.Buffer
31-
tmpl *template.Template
29+
out io.Writer
30+
buffer *bytes.Buffer
31+
tmpl *template.Template
3232
}
3333

3434
// NewTemplateInspector creates a new inspector with a template.
35-
func NewTemplateInspector(outputStream io.Writer, tmpl *template.Template) Inspector {
35+
func NewTemplateInspector(out io.Writer, tmpl *template.Template) *TemplateInspector {
36+
if out == nil {
37+
out = io.Discard
38+
}
3639
return &TemplateInspector{
37-
outputStream: outputStream,
38-
buffer: new(bytes.Buffer),
39-
tmpl: tmpl,
40+
out: out,
41+
buffer: new(bytes.Buffer),
42+
tmpl: tmpl,
4043
}
4144
}
4245

4346
// NewTemplateInspectorFromString creates a new TemplateInspector from a string
4447
// which is compiled into a template.
4548
func NewTemplateInspectorFromString(out io.Writer, tmplStr string) (Inspector, error) {
49+
if out == nil {
50+
return nil, errors.New("no output stream")
51+
}
4652
if tmplStr == "" {
4753
return NewIndentedInspector(out), nil
4854
}
@@ -65,6 +71,9 @@ type GetRefFunc func(ref string) (any, []byte, error)
6571
// Inspect fetches objects by reference using GetRefFunc and writes the json
6672
// representation to the output writer.
6773
func Inspect(out io.Writer, references []string, tmplStr string, getRef GetRefFunc) error {
74+
if out == nil {
75+
return errors.New("no output stream")
76+
}
6877
inspector, err := NewTemplateInspectorFromString(out, tmplStr)
6978
if err != nil {
7079
return cli.StatusError{StatusCode: 64, Status: err.Error()}
@@ -138,18 +147,21 @@ func (i *TemplateInspector) tryRawInspectFallback(rawElement []byte) error {
138147
// Flush writes the result of inspecting all elements into the output stream.
139148
func (i *TemplateInspector) Flush() error {
140149
if i.buffer.Len() == 0 {
141-
_, err := io.WriteString(i.outputStream, "\n")
150+
_, err := io.WriteString(i.out, "\n")
142151
return err
143152
}
144-
_, err := io.Copy(i.outputStream, i.buffer)
153+
_, err := io.Copy(i.out, i.buffer)
145154
return err
146155
}
147156

148157
// NewIndentedInspector generates a new inspector with an indented representation
149158
// of elements.
150-
func NewIndentedInspector(outputStream io.Writer) Inspector {
151-
return &elementsInspector{
152-
outputStream: outputStream,
159+
func NewIndentedInspector(out io.Writer) Inspector {
160+
if out == nil {
161+
out = io.Discard
162+
}
163+
return &jsonInspector{
164+
out: out,
153165
raw: func(dst *bytes.Buffer, src []byte) error {
154166
return json.Indent(dst, src, "", " ")
155167
},
@@ -161,23 +173,26 @@ func NewIndentedInspector(outputStream io.Writer) Inspector {
161173

162174
// NewJSONInspector generates a new inspector with a compact representation
163175
// of elements.
164-
func NewJSONInspector(outputStream io.Writer) Inspector {
165-
return &elementsInspector{
166-
outputStream: outputStream,
167-
raw: json.Compact,
168-
el: json.Marshal,
176+
func NewJSONInspector(out io.Writer) Inspector {
177+
if out == nil {
178+
out = io.Discard
179+
}
180+
return &jsonInspector{
181+
out: out,
182+
raw: json.Compact,
183+
el: json.Marshal,
169184
}
170185
}
171186

172-
type elementsInspector struct {
173-
outputStream io.Writer
174-
elements []any
175-
rawElements [][]byte
176-
raw func(dst *bytes.Buffer, src []byte) error
177-
el func(v any) ([]byte, error)
187+
type jsonInspector struct {
188+
out io.Writer
189+
elements []any
190+
rawElements [][]byte
191+
raw func(dst *bytes.Buffer, src []byte) error
192+
el func(v any) ([]byte, error)
178193
}
179194

180-
func (e *elementsInspector) Inspect(typedElement any, rawElement []byte) error {
195+
func (e *jsonInspector) Inspect(typedElement any, rawElement []byte) error {
181196
if rawElement != nil {
182197
e.rawElements = append(e.rawElements, rawElement)
183198
} else {
@@ -186,9 +201,9 @@ func (e *elementsInspector) Inspect(typedElement any, rawElement []byte) error {
186201
return nil
187202
}
188203

189-
func (e *elementsInspector) Flush() error {
204+
func (e *jsonInspector) Flush() error {
190205
if len(e.elements) == 0 && len(e.rawElements) == 0 {
191-
_, err := io.WriteString(e.outputStream, "[]\n")
206+
_, err := io.WriteString(e.out, "[]\n")
192207
return err
193208
}
194209

@@ -216,9 +231,9 @@ func (e *elementsInspector) Flush() error {
216231
buffer = bytes.NewReader(b)
217232
}
218233

219-
if _, err := io.Copy(e.outputStream, buffer); err != nil {
234+
if _, err := io.Copy(e.out, buffer); err != nil {
220235
return err
221236
}
222-
_, err := io.WriteString(e.outputStream, "\n")
237+
_, err := io.WriteString(e.out, "\n")
223238
return err
224239
}

0 commit comments

Comments
 (0)