diff options
author | Nalin Dahyabhai <nalin@redhat.com> | 2019-10-16 12:01:30 -0400 |
---|---|---|
committer | Nalin Dahyabhai <nalin@redhat.com> | 2019-10-29 13:35:19 -0400 |
commit | 248bb61b14a3f0d4e1d244eff85b30f48554a6a8 (patch) | |
tree | bbaa162a20177f96a0fc03a48faaa49dcfc89215 | |
parent | 07195ff09fdcb0d2d3a044c92665b082d6e742b1 (diff) | |
download | podman-248bb61b14a3f0d4e1d244eff85b30f48554a6a8.tar.gz podman-248bb61b14a3f0d4e1d244eff85b30f48554a6a8.tar.bz2 podman-248bb61b14a3f0d4e1d244eff85b30f48554a6a8.zip |
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 <nalin@redhat.com>
-rw-r--r-- | cmd/podman/images.go | 14 | ||||
-rw-r--r-- | libpod/image/image.go | 55 | ||||
-rw-r--r-- | libpod/image/image_test.go | 13 | ||||
-rw-r--r-- | libpod/image/utils.go | 14 |
4 files changed, 72 insertions, 24 deletions
diff --git a/cmd/podman/images.go b/cmd/podman/images.go index 6bb08e195..6157fda2a 100644 --- a/cmd/podman/images.go +++ b/cmd/podman/images.go @@ -206,9 +206,9 @@ func (i imagesOptions) setOutputFormat() string { if i.quiet { return formats.IDString } - format := "table {{.Repository}}\t{{.Tag}}\t" + format := "table {{.Repository}}\t{{if .Tag}}{{.Tag}}{{else}}<none>{{end}}\t" if i.noHeading { - format = "{{.Repository}}\t{{.Tag}}\t" + format = "{{.Repository}}\t{{if .Tag}}{{.Tag}}{{else}}<none>{{end}}\t" } if i.digests { format += "{{.Digest}}\t" @@ -270,7 +270,7 @@ func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerIma imageID = shortID(img.ID()) } - // get all specified repo:tag pairs and print them separately + // get all specified repo:tag and repo@digest pairs and print them separately repopairs, err := image.ReposToMap(img.Names()) if err != nil { logrus.Errorf("error finding tag/digest for %s", img.ID()) @@ -287,11 +287,16 @@ func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerIma lastNumIdx := strings.LastIndexFunc(sizeStr, unicode.IsNumber) sizeStr = sizeStr[:lastNumIdx+1] + " " + sizeStr[lastNumIdx+1:] } + var imageDigest digest.Digest + if len(tag) == 71 && strings.HasPrefix(tag, "sha256:") { + imageDigest = digest.Digest(tag) + tag = "" + } params := imagesTemplateParams{ Repository: repo, Tag: tag, ID: imageID, - Digest: img.Digest(), + Digest: imageDigest, Digests: img.Digests(), CreatedTime: createdTime, Created: units.HumanDuration(time.Since(createdTime)) + " ago", @@ -302,7 +307,6 @@ func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerIma if opts.quiet { // Show only one image ID when quiet break outer } - } } } 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 { |