From 248bb61b14a3f0d4e1d244eff85b30f48554a6a8 Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Wed, 16 Oct 2019 12:01:30 -0400 Subject: images: distinguish between tags and digests Generate an image's RepoDigests list using all applicable digests, and refrain from outputting a digest in the tag column of the "images" output. Signed-off-by: Nalin Dahyabhai --- libpod/image/image.go | 55 ++++++++++++++++++++++++++++++++++++---------- libpod/image/image_test.go | 13 +++++++++++ libpod/image/utils.go | 14 ++++++------ 3 files changed, 63 insertions(+), 19 deletions(-) (limited to 'libpod') diff --git a/libpod/image/image.go b/libpod/image/image.go index faa3b648a..c912ac2ca 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -9,6 +9,7 @@ import ( "io/ioutil" "os" "path/filepath" + "sort" "strings" "syscall" "time" @@ -324,29 +325,54 @@ func (i *Image) Manifest(ctx context.Context) ([]byte, string, error) { return imgRef.Manifest(ctx) } -// Names returns a string array of names associated with the image +// Names returns a string array of names associated with the image, which may be a mixture of tags and digests func (i *Image) Names() []string { return i.image.Names } -// RepoDigests returns a string array of repodigests associated with the image -func (i *Image) RepoDigests() ([]string, error) { - var repoDigests []string - imageDigest := i.Digest() - +// RepoTags returns a string array of repotags associated with the image +func (i *Image) RepoTags() ([]string, error) { + var repoTags []string for _, name := range i.Names() { named, err := reference.ParseNormalizedNamed(name) if err != nil { return nil, err } - - canonical, err := reference.WithDigest(reference.TrimNamed(named), imageDigest) - if err != nil { - return nil, err + if tagged, isTagged := named.(reference.NamedTagged); isTagged { + repoTags = append(repoTags, tagged.String()) } + } + return repoTags, nil +} + +// RepoDigests returns a string array of repodigests associated with the image +func (i *Image) RepoDigests() ([]string, error) { + var repoDigests []string + added := make(map[string]struct{}) - repoDigests = append(repoDigests, canonical.String()) + for _, name := range i.Names() { + for _, imageDigest := range append(i.Digests(), i.Digest()) { + if imageDigest == "" { + continue + } + + named, err := reference.ParseNormalizedNamed(name) + if err != nil { + return nil, err + } + + canonical, err := reference.WithDigest(reference.TrimNamed(named), imageDigest) + if err != nil { + return nil, err + } + + if _, alreadyInList := added[canonical.String()]; !alreadyInList { + repoDigests = append(repoDigests, canonical.String()) + added[canonical.String()] = struct{}{} + } + } } + sort.Strings(repoDigests) return repoDigests, nil } @@ -944,6 +970,11 @@ func (i *Image) Inspect(ctx context.Context) (*inspect.ImageData, error) { size = int64(*usize) } + repoTags, err := i.RepoTags() + if err != nil { + return nil, err + } + repoDigests, err := i.RepoDigests() if err != nil { return nil, err @@ -965,7 +996,7 @@ func (i *Image) Inspect(ctx context.Context) (*inspect.ImageData, error) { data := &inspect.ImageData{ ID: i.ID(), - RepoTags: i.Names(), + RepoTags: repoTags, RepoDigests: repoDigests, Comment: comment, Created: ociv1Img.Created, diff --git a/libpod/image/image_test.go b/libpod/image/image_test.go index ef39d09c3..5aff7d860 100644 --- a/libpod/image/image_test.go +++ b/libpod/image/image_test.go @@ -247,6 +247,19 @@ func TestImage_RepoDigests(t *testing.T) { } assert.Equal(t, test.expected, actual) + + image = &Image{ + image: &storage.Image{ + Names: test.names, + Digests: []digest.Digest{dgst}, + }, + } + actual, err = image.RepoDigests() + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, test.expected, actual) }) } } diff --git a/libpod/image/utils.go b/libpod/image/utils.go index 5c971cb2b..b7ea63c66 100644 --- a/libpod/image/utils.go +++ b/libpod/image/utils.go @@ -87,18 +87,18 @@ func hasTransport(image string) bool { } // ReposToMap parses the specified repotags and returns a map with repositories -// as keys and the corresponding arrays of tags as values. -func ReposToMap(repotags []string) (map[string][]string, error) { - // map format is repo -> tag +// as keys and the corresponding arrays of tags or digests-as-strings as values. +func ReposToMap(names []string) (map[string][]string, error) { + // map format is repo -> []tag-or-digest repos := make(map[string][]string) - for _, repo := range repotags { + for _, name := range names { var repository, tag string - if len(repo) > 0 { - named, err := reference.ParseNormalizedNamed(repo) - repository = named.Name() + if len(name) > 0 { + named, err := reference.ParseNormalizedNamed(name) if err != nil { return nil, err } + repository = named.Name() if ref, ok := named.(reference.NamedTagged); ok { tag = ref.Tag() } else if ref, ok := named.(reference.Canonical); ok { -- cgit v1.2.3-54-g00ecf