Skip to content

Commit 4d6fc33

Browse files
authored
Merge pull request #6704 from vvoland/list-fix
image: Fix dangling image detection with graphdrivers
2 parents d96b786 + 09a4664 commit 4d6fc33

7 files changed

+233
-4
lines changed

cli/command/image/list.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ func shouldUseTree(options imagesOptions) (bool, error) {
177177

178178
// isDangling is a copy of [formatter.isDangling].
179179
func isDangling(img image.Summary) bool {
180-
if len(img.RepoTags) == 0 && len(img.RepoDigests) == 0 {
180+
if len(img.RepoTags) == 0 {
181181
return true
182182
}
183183
return len(img.RepoTags) == 1 && img.RepoTags[0] == "<none>:<none>" && len(img.RepoDigests) == 1 && img.RepoDigests[0] == "<none>@<none>"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
2+
multiplatform:latest aaaaaaaaaaaa 25.5 MB 20.2 MB U
3+
├─ linux/amd64 bbbbbbbbbbbb 12.1 MB 10.0 MB
4+
└─ linux/arm64 cccccccccccc 13.4 MB 10.2 MB U
5+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
2+
app:v1
3+
app:latest 101010101010 30.5 MB 25.2 MB U
4+
└─ linux/amd64 202020202020 15.2 MB 12.6 MB U
5+
6+
<untagged> 303030303030 12.3 MB 10.1 MB
7+
└─ linux/arm/v7 404040404040 6.1 MB 5.0 MB
8+
9+
base:alpine 505050505050 5.5 MB 5.5 MB
10+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
2+
<untagged> dddddddddddd 18.5 MB 15.2 MB
3+
├─ linux/amd64 eeeeeeeeeeee 9.2 MB 7.6 MB
4+
└─ linux/arm64 ffffffffffff 9.3 MB 7.6 MB
5+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
2+
a:1 111111111111 5.5 MB 2.5 MB
3+
<untagged> 222222222222 3.2 MB 1.6 MB
4+
short:v1 333333333333 7.1 MB 3.5 MB U

cli/command/image/tree.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import (
2222
"github.com/opencontainers/go-digest"
2323
)
2424

25+
const untaggedName = "<untagged>"
26+
2527
type treeOptions struct {
2628
images []imagetypes.Summary
2729
all bool
@@ -111,7 +113,7 @@ func runTree(ctx context.Context, dockerCLI command.Cli, opts treeOptions) (int,
111113
continue
112114
}
113115

114-
if opts.all && len(sortedTags) == 0 {
116+
if len(sortedTags) == 0 {
115117
view.images = append(view.images, topImage{
116118
Details: topDetails,
117119
Children: children,
@@ -433,7 +435,7 @@ func printChildren(out tui.Output, headers []imgColumn, img topImage, normalColo
433435

434436
func printNames(out tui.Output, headers []imgColumn, img topImage, color, untaggedColor aec.ANSI) {
435437
if len(img.Names) == 0 {
436-
_, _ = fmt.Fprint(out, headers[0].Print(untaggedColor, "<untagged>"))
438+
_, _ = fmt.Fprint(out, headers[0].Print(untaggedColor, untaggedName))
437439
}
438440

439441
for nameIdx, name := range img.Names {
@@ -545,7 +547,11 @@ func (h imgColumn) PrintR(clr aec.ANSI, s string) string {
545547
func widestFirstColumnValue(headers []imgColumn, images []topImage) int {
546548
width := len(headers[0].Title)
547549
for _, img := range images {
548-
for _, name := range img.Names {
550+
names := img.Names
551+
if len(names) == 0 {
552+
names = []string{untaggedName}
553+
}
554+
for _, name := range names {
549555
if len(name) > width {
550556
width = len(name)
551557
}

cli/command/image/tree_test.go

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package image
22

33
import (
4+
"fmt"
45
"strings"
56
"testing"
67

78
"github.com/docker/cli/internal/test"
89
"gotest.tools/v3/assert"
10+
"gotest.tools/v3/golden"
911
)
1012

1113
func TestPrintImageTreeAnsiTty(t *testing.T) {
@@ -154,3 +156,200 @@ func TestPrintImageTreeAnsiTty(t *testing.T) {
154156
})
155157
}
156158
}
159+
160+
func TestPrintImageTreeGolden(t *testing.T) {
161+
testCases := []struct {
162+
name string
163+
view treeView
164+
expanded bool
165+
}{
166+
{
167+
name: "width-calculation-untagged",
168+
expanded: false,
169+
view: treeView{
170+
images: []topImage{
171+
{
172+
Names: []string{"a:1"},
173+
Details: imageDetails{
174+
ID: "sha256:1111111111111111111111111111111111111111111111111111111111111111",
175+
DiskUsage: "5.5 MB",
176+
InUse: false,
177+
ContentSize: "2.5 MB",
178+
},
179+
},
180+
{
181+
// Untagged image name is longer than "a:1"
182+
Names: []string{},
183+
Details: imageDetails{
184+
ID: "sha256:2222222222222222222222222222222222222222222222222222222222222222",
185+
DiskUsage: "3.2 MB",
186+
InUse: false,
187+
ContentSize: "1.6 MB",
188+
},
189+
},
190+
{
191+
Names: []string{"short:v1"},
192+
Details: imageDetails{
193+
ID: "sha256:3333333333333333333333333333333333333333333333333333333333333333",
194+
DiskUsage: "7.1 MB",
195+
InUse: true,
196+
ContentSize: "3.5 MB",
197+
},
198+
},
199+
},
200+
imageSpacing: false,
201+
},
202+
},
203+
{
204+
name: "expanded-view-with-platforms",
205+
expanded: false,
206+
view: treeView{
207+
images: []topImage{
208+
{
209+
Names: []string{"multiplatform:latest"},
210+
Details: imageDetails{
211+
ID: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
212+
DiskUsage: "25.5 MB",
213+
InUse: true,
214+
ContentSize: "20.2 MB",
215+
},
216+
Children: []subImage{
217+
{
218+
Platform: "linux/amd64",
219+
Available: true,
220+
Details: imageDetails{
221+
ID: "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
222+
DiskUsage: "12.1 MB",
223+
InUse: false,
224+
ContentSize: "10.0 MB",
225+
},
226+
},
227+
{
228+
Platform: "linux/arm64",
229+
Available: true,
230+
Details: imageDetails{
231+
ID: "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
232+
DiskUsage: "13.4 MB",
233+
InUse: true,
234+
ContentSize: "10.2 MB",
235+
},
236+
},
237+
},
238+
},
239+
},
240+
imageSpacing: true,
241+
},
242+
},
243+
{
244+
name: "untagged-with-platforms",
245+
expanded: false,
246+
view: treeView{
247+
images: []topImage{
248+
{
249+
Names: []string{},
250+
Details: imageDetails{
251+
ID: "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
252+
DiskUsage: "18.5 MB",
253+
InUse: false,
254+
ContentSize: "15.2 MB",
255+
},
256+
Children: []subImage{
257+
{
258+
Platform: "linux/amd64",
259+
Available: true,
260+
Details: imageDetails{
261+
ID: "sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
262+
DiskUsage: "9.2 MB",
263+
InUse: false,
264+
ContentSize: "7.6 MB",
265+
},
266+
},
267+
{
268+
Platform: "linux/arm64",
269+
Available: false,
270+
Details: imageDetails{
271+
ID: "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
272+
DiskUsage: "9.3 MB",
273+
InUse: false,
274+
ContentSize: "7.6 MB",
275+
},
276+
},
277+
},
278+
},
279+
},
280+
imageSpacing: true,
281+
},
282+
},
283+
{
284+
name: "mixed-tagged-untagged-with-children",
285+
expanded: false,
286+
view: treeView{
287+
images: []topImage{
288+
{
289+
Names: []string{"app:v1", "app:latest"},
290+
Details: imageDetails{
291+
ID: "sha256:1010101010101010101010101010101010101010101010101010101010101010",
292+
DiskUsage: "30.5 MB",
293+
InUse: true,
294+
ContentSize: "25.2 MB",
295+
},
296+
Children: []subImage{
297+
{
298+
Platform: "linux/amd64",
299+
Available: true,
300+
Details: imageDetails{
301+
ID: "sha256:2020202020202020202020202020202020202020202020202020202020202020",
302+
DiskUsage: "15.2 MB",
303+
InUse: true,
304+
ContentSize: "12.6 MB",
305+
},
306+
},
307+
},
308+
},
309+
{
310+
Names: []string{},
311+
Details: imageDetails{
312+
ID: "sha256:3030303030303030303030303030303030303030303030303030303030303030",
313+
DiskUsage: "12.3 MB",
314+
InUse: false,
315+
ContentSize: "10.1 MB",
316+
},
317+
Children: []subImage{
318+
{
319+
Platform: "linux/arm/v7",
320+
Available: true,
321+
Details: imageDetails{
322+
ID: "sha256:4040404040404040404040404040404040404040404040404040404040404040",
323+
DiskUsage: "6.1 MB",
324+
InUse: false,
325+
ContentSize: "5.0 MB",
326+
},
327+
},
328+
},
329+
},
330+
{
331+
Names: []string{"base:alpine"},
332+
Details: imageDetails{
333+
ID: "sha256:5050505050505050505050505050505050505050505050505050505050505050",
334+
DiskUsage: "5.5 MB",
335+
InUse: false,
336+
ContentSize: "5.5 MB",
337+
},
338+
},
339+
},
340+
imageSpacing: true,
341+
},
342+
},
343+
}
344+
345+
for _, tc := range testCases {
346+
t.Run(tc.name, func(t *testing.T) {
347+
cli := test.NewFakeCli(nil)
348+
cli.Out().SetIsTerminal(false)
349+
350+
printImageTree(cli, tc.view)
351+
352+
golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("tree-command-success.%s.golden", tc.name))
353+
})
354+
}
355+
}

0 commit comments

Comments
 (0)