1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Do not show empty tags for digest references in output

When a repository has a tag and digests, show tag for each digest value.
Do not duplicate rows for the same image name with both tag and digest.

Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
This commit is contained in:
Derek McGowan 2016-06-14 22:44:49 -07:00
parent 81a85cf448
commit 79eada3814
3 changed files with 103 additions and 64 deletions

View file

@ -155,6 +155,10 @@ func (ctx ContainerContext) Write() {
ctx.postformat(tmpl, &containerContext{}) ctx.postformat(tmpl, &containerContext{})
} }
func isDangling(image types.Image) bool {
return len(image.RepoTags) == 1 && image.RepoTags[0] == "<none>:<none>" && len(image.RepoDigests) == 1 && image.RepoDigests[0] == "<none>@<none>"
}
func (ctx ImageContext) Write() { func (ctx ImageContext) Write() {
switch ctx.Format { switch ctx.Format {
case tableFormatKey: case tableFormatKey:
@ -200,42 +204,98 @@ virtual_size: {{.Size}}
} }
for _, image := range ctx.Images { for _, image := range ctx.Images {
images := []*imageContext{}
if isDangling(image) {
images = append(images, &imageContext{
trunc: ctx.Trunc,
i: image,
repo: "<none>",
tag: "<none>",
digest: "<none>",
})
} else {
repoTags := map[string][]string{}
repoDigests := map[string][]string{}
repoTags := image.RepoTags for _, refString := range append(image.RepoTags) {
repoDigests := image.RepoDigests ref, err := reference.ParseNamed(refString)
if len(repoTags) == 1 && repoTags[0] == "<none>:<none>" && len(repoDigests) == 1 && repoDigests[0] == "<none>@<none>" {
// dangling image - clear out either repoTags or repoDigests so we only show it once below
repoDigests = []string{}
}
// combine the tags and digests lists
tagsAndDigests := append(repoTags, repoDigests...)
for _, repoAndRef := range tagsAndDigests {
repo := "<none>"
tag := "<none>"
digest := "<none>"
if !strings.HasPrefix(repoAndRef, "<none>") {
ref, err := reference.ParseNamed(repoAndRef)
if err != nil { if err != nil {
continue continue
} }
repo = ref.Name() if nt, ok := ref.(reference.NamedTagged); ok {
repoTags[ref.Name()] = append(repoTags[ref.Name()], nt.Tag())
switch x := ref.(type) {
case reference.Canonical:
digest = x.Digest().String()
case reference.NamedTagged:
tag = x.Tag()
} }
} }
imageCtx := &imageContext{ for _, refString := range append(image.RepoDigests) {
trunc: ctx.Trunc, ref, err := reference.ParseNamed(refString)
i: image, if err != nil {
repo: repo, continue
tag: tag, }
digest: digest, if c, ok := ref.(reference.Canonical); ok {
repoDigests[ref.Name()] = append(repoDigests[ref.Name()], c.Digest().String())
}
} }
for repo, tags := range repoTags {
digests := repoDigests[repo]
// Do not display digests as their own row
delete(repoDigests, repo)
if !ctx.Digest {
// Ignore digest references, just show tag once
digests = nil
}
for _, tag := range tags {
if len(digests) == 0 {
images = append(images, &imageContext{
trunc: ctx.Trunc,
i: image,
repo: repo,
tag: tag,
digest: "<none>",
})
continue
}
// Display the digests for each tag
for _, dgst := range digests {
images = append(images, &imageContext{
trunc: ctx.Trunc,
i: image,
repo: repo,
tag: tag,
digest: dgst,
})
}
}
}
// Show rows for remaining digest only references
for repo, digests := range repoDigests {
// If digests are displayed, show row per digest
if ctx.Digest {
for _, dgst := range digests {
images = append(images, &imageContext{
trunc: ctx.Trunc,
i: image,
repo: repo,
tag: "<none>",
digest: dgst,
})
}
} else {
images = append(images, &imageContext{
trunc: ctx.Trunc,
i: image,
repo: repo,
tag: "<none>",
})
}
}
}
for _, imageCtx := range images {
err = ctx.contextFormat(tmpl, imageCtx) err = ctx.contextFormat(tmpl, imageCtx)
if err != nil { if err != nil {
return return

View file

@ -301,7 +301,6 @@ func TestImageContextWrite(t *testing.T) {
}, },
`REPOSITORY TAG IMAGE ID CREATED SIZE `REPOSITORY TAG IMAGE ID CREATED SIZE
image tag1 imageID1 24 hours ago 0 B image tag1 imageID1 24 hours ago 0 B
image <none> imageID1 24 hours ago 0 B
image tag2 imageID2 24 hours ago 0 B image tag2 imageID2 24 hours ago 0 B
<none> <none> imageID3 24 hours ago 0 B <none> <none> imageID3 24 hours ago 0 B
`, `,
@ -312,7 +311,7 @@ image tag2 imageID2 24 hours ago
Format: "table {{.Repository}}", Format: "table {{.Repository}}",
}, },
}, },
"REPOSITORY\nimage\nimage\nimage\n<none>\n", "REPOSITORY\nimage\nimage\n<none>\n",
}, },
{ {
ImageContext{ ImageContext{
@ -322,7 +321,6 @@ image tag2 imageID2 24 hours ago
Digest: true, Digest: true,
}, },
`REPOSITORY DIGEST `REPOSITORY DIGEST
image <none>
image sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf image sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf
image <none> image <none>
<none> <none> <none> <none>
@ -335,7 +333,7 @@ image <none>
Quiet: true, Quiet: true,
}, },
}, },
"REPOSITORY\nimage\nimage\nimage\n<none>\n", "REPOSITORY\nimage\nimage\n<none>\n",
}, },
{ {
ImageContext{ ImageContext{
@ -344,7 +342,7 @@ image <none>
Quiet: true, Quiet: true,
}, },
}, },
"imageID1\nimageID1\nimageID2\nimageID3\n", "imageID1\nimageID2\nimageID3\n",
}, },
{ {
ImageContext{ ImageContext{
@ -355,8 +353,7 @@ image <none>
Digest: true, Digest: true,
}, },
`REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE `REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE
image tag1 <none> imageID1 24 hours ago 0 B image tag1 sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf imageID1 24 hours ago 0 B
image <none> sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf imageID1 24 hours ago 0 B
image tag2 <none> imageID2 24 hours ago 0 B image tag2 <none> imageID2 24 hours ago 0 B
<none> <none> <none> imageID3 24 hours ago 0 B <none> <none> <none> imageID3 24 hours ago 0 B
`, `,
@ -369,7 +366,7 @@ image tag2 <none>
}, },
Digest: true, Digest: true,
}, },
"imageID1\nimageID1\nimageID2\nimageID3\n", "imageID1\nimageID2\nimageID3\n",
}, },
// Raw Format // Raw Format
{ {
@ -384,12 +381,6 @@ image_id: imageID1
created_at: %s created_at: %s
virtual_size: 0 B virtual_size: 0 B
repository: image
tag: <none>
image_id: imageID1
created_at: %s
virtual_size: 0 B
repository: image repository: image
tag: tag2 tag: tag2
image_id: imageID2 image_id: imageID2
@ -402,7 +393,7 @@ image_id: imageID3
created_at: %s created_at: %s
virtual_size: 0 B virtual_size: 0 B
`, expectedTime, expectedTime, expectedTime, expectedTime), `, expectedTime, expectedTime, expectedTime),
}, },
{ {
ImageContext{ ImageContext{
@ -413,13 +404,6 @@ virtual_size: 0 B
}, },
fmt.Sprintf(`repository: image fmt.Sprintf(`repository: image
tag: tag1 tag: tag1
digest: <none>
image_id: imageID1
created_at: %s
virtual_size: 0 B
repository: image
tag: <none>
digest: sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf digest: sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf
image_id: imageID1 image_id: imageID1
created_at: %s created_at: %s
@ -439,7 +423,7 @@ image_id: imageID3
created_at: %s created_at: %s
virtual_size: 0 B virtual_size: 0 B
`, expectedTime, expectedTime, expectedTime, expectedTime), `, expectedTime, expectedTime, expectedTime),
}, },
{ {
ImageContext{ ImageContext{
@ -449,7 +433,6 @@ virtual_size: 0 B
}, },
}, },
`image_id: imageID1 `image_id: imageID1
image_id: imageID1
image_id: imageID2 image_id: imageID2
image_id: imageID3 image_id: imageID3
`, `,
@ -461,7 +444,7 @@ image_id: imageID3
Format: "{{.Repository}}", Format: "{{.Repository}}",
}, },
}, },
"image\nimage\nimage\n<none>\n", "image\nimage\n<none>\n",
}, },
{ {
ImageContext{ ImageContext{
@ -470,7 +453,7 @@ image_id: imageID3
}, },
Digest: true, Digest: true,
}, },
"image\nimage\nimage\n<none>\n", "image\nimage\n<none>\n",
}, },
} }

View file

@ -284,10 +284,8 @@ func (s *DockerRegistrySuite) TestListImagesWithDigests(c *check.C) {
out, _ = dockerCmd(c, "images", "--digests") out, _ = dockerCmd(c, "images", "--digests")
// make sure image 1 has repo, tag, <none> AND repo, <none>, digest // make sure image 1 has repo, tag, <none> AND repo, <none>, digest
reWithTag1 := regexp.MustCompile(`\s*` + repoName + `\s*tag1\s*<none>\s`) reWithDigest1 := regexp.MustCompile(`\s*` + repoName + `\s*tag1\s*` + digest1.String() + `\s`)
reWithDigest1 := regexp.MustCompile(`\s*` + repoName + `\s*<none>\s*` + digest1.String() + `\s`)
c.Assert(reWithDigest1.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithDigest1.String(), out)) c.Assert(reWithDigest1.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithDigest1.String(), out))
c.Assert(reWithTag1.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithTag1.String(), out))
// make sure image 2 has repo, <none>, digest // make sure image 2 has repo, <none>, digest
c.Assert(re2.MatchString(out), checker.True, check.Commentf("expected %q: %s", re2.String(), out)) c.Assert(re2.MatchString(out), checker.True, check.Commentf("expected %q: %s", re2.String(), out))
@ -298,21 +296,19 @@ func (s *DockerRegistrySuite) TestListImagesWithDigests(c *check.C) {
out, _ = dockerCmd(c, "images", "--digests") out, _ = dockerCmd(c, "images", "--digests")
// make sure image 1 has repo, tag, digest // make sure image 1 has repo, tag, digest
c.Assert(reWithTag1.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithTag1.String(), out)) c.Assert(reWithDigest1.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithDigest1.String(), out))
// make sure image 2 has repo, tag, digest // make sure image 2 has repo, tag, digest
reWithTag2 := regexp.MustCompile(`\s*` + repoName + `\s*tag2\s*<none>\s`) reWithDigest2 := regexp.MustCompile(`\s*` + repoName + `\s*tag2\s*` + digest2.String() + `\s`)
reWithDigest2 := regexp.MustCompile(`\s*` + repoName + `\s*<none>\s*` + digest2.String() + `\s`)
c.Assert(reWithTag2.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithTag2.String(), out))
c.Assert(reWithDigest2.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithDigest2.String(), out)) c.Assert(reWithDigest2.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithDigest2.String(), out))
// list images // list images
out, _ = dockerCmd(c, "images", "--digests") out, _ = dockerCmd(c, "images", "--digests")
// make sure image 1 has repo, tag, digest // make sure image 1 has repo, tag, digest
c.Assert(reWithTag1.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithTag1.String(), out)) c.Assert(reWithDigest1.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithDigest1.String(), out))
// make sure image 2 has repo, tag, digest // make sure image 2 has repo, tag, digest
c.Assert(reWithTag2.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithTag2.String(), out)) c.Assert(reWithDigest2.MatchString(out), checker.True, check.Commentf("expected %q: %s", reWithDigest2.String(), out))
// make sure busybox has tag, but not digest // make sure busybox has tag, but not digest
busyboxRe := regexp.MustCompile(`\s*busybox\s*latest\s*<none>\s`) busyboxRe := regexp.MustCompile(`\s*busybox\s*latest\s*<none>\s`)
c.Assert(busyboxRe.MatchString(out), checker.True, check.Commentf("expected %q: %s", busyboxRe.String(), out)) c.Assert(busyboxRe.MatchString(out), checker.True, check.Commentf("expected %q: %s", busyboxRe.String(), out))