mirror of https://github.com/containers/podman.git
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>
This commit is contained in:
parent
07195ff09f
commit
248bb61b14
|
|
@ -206,9 +206,9 @@ func (i imagesOptions) setOutputFormat() string {
|
||||||
if i.quiet {
|
if i.quiet {
|
||||||
return formats.IDString
|
return formats.IDString
|
||||||
}
|
}
|
||||||
format := "table {{.Repository}}\t{{.Tag}}\t"
|
format := "table {{.Repository}}\t{{if .Tag}}{{.Tag}}{{else}}<none>{{end}}\t"
|
||||||
if i.noHeading {
|
if i.noHeading {
|
||||||
format = "{{.Repository}}\t{{.Tag}}\t"
|
format = "{{.Repository}}\t{{if .Tag}}{{.Tag}}{{else}}<none>{{end}}\t"
|
||||||
}
|
}
|
||||||
if i.digests {
|
if i.digests {
|
||||||
format += "{{.Digest}}\t"
|
format += "{{.Digest}}\t"
|
||||||
|
|
@ -270,7 +270,7 @@ func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerIma
|
||||||
imageID = shortID(img.ID())
|
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())
|
repopairs, err := image.ReposToMap(img.Names())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("error finding tag/digest for %s", img.ID())
|
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)
|
lastNumIdx := strings.LastIndexFunc(sizeStr, unicode.IsNumber)
|
||||||
sizeStr = sizeStr[:lastNumIdx+1] + " " + sizeStr[lastNumIdx+1:]
|
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{
|
params := imagesTemplateParams{
|
||||||
Repository: repo,
|
Repository: repo,
|
||||||
Tag: tag,
|
Tag: tag,
|
||||||
ID: imageID,
|
ID: imageID,
|
||||||
Digest: img.Digest(),
|
Digest: imageDigest,
|
||||||
Digests: img.Digests(),
|
Digests: img.Digests(),
|
||||||
CreatedTime: createdTime,
|
CreatedTime: createdTime,
|
||||||
Created: units.HumanDuration(time.Since(createdTime)) + " ago",
|
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
|
if opts.quiet { // Show only one image ID when quiet
|
||||||
break outer
|
break outer
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -324,29 +325,54 @@ func (i *Image) Manifest(ctx context.Context) ([]byte, string, error) {
|
||||||
return imgRef.Manifest(ctx)
|
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 {
|
func (i *Image) Names() []string {
|
||||||
return i.image.Names
|
return i.image.Names
|
||||||
}
|
}
|
||||||
|
|
||||||
// RepoDigests returns a string array of repodigests associated with the image
|
// RepoTags returns a string array of repotags associated with the image
|
||||||
func (i *Image) RepoDigests() ([]string, error) {
|
func (i *Image) RepoTags() ([]string, error) {
|
||||||
var repoDigests []string
|
var repoTags []string
|
||||||
imageDigest := i.Digest()
|
|
||||||
|
|
||||||
for _, name := range i.Names() {
|
for _, name := range i.Names() {
|
||||||
named, err := reference.ParseNormalizedNamed(name)
|
named, err := reference.ParseNormalizedNamed(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if tagged, isTagged := named.(reference.NamedTagged); isTagged {
|
||||||
canonical, err := reference.WithDigest(reference.TrimNamed(named), imageDigest)
|
repoTags = append(repoTags, tagged.String())
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
repoDigests = append(repoDigests, canonical.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{})
|
||||||
|
|
||||||
|
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
|
return repoDigests, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -944,6 +970,11 @@ func (i *Image) Inspect(ctx context.Context) (*inspect.ImageData, error) {
|
||||||
size = int64(*usize)
|
size = int64(*usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
repoTags, err := i.RepoTags()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
repoDigests, err := i.RepoDigests()
|
repoDigests, err := i.RepoDigests()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -965,7 +996,7 @@ func (i *Image) Inspect(ctx context.Context) (*inspect.ImageData, error) {
|
||||||
|
|
||||||
data := &inspect.ImageData{
|
data := &inspect.ImageData{
|
||||||
ID: i.ID(),
|
ID: i.ID(),
|
||||||
RepoTags: i.Names(),
|
RepoTags: repoTags,
|
||||||
RepoDigests: repoDigests,
|
RepoDigests: repoDigests,
|
||||||
Comment: comment,
|
Comment: comment,
|
||||||
Created: ociv1Img.Created,
|
Created: ociv1Img.Created,
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,19 @@ func TestImage_RepoDigests(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, test.expected, actual)
|
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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,18 +87,18 @@ func hasTransport(image string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReposToMap parses the specified repotags and returns a map with repositories
|
// ReposToMap parses the specified repotags and returns a map with repositories
|
||||||
// as keys and the corresponding arrays of tags as values.
|
// as keys and the corresponding arrays of tags or digests-as-strings as values.
|
||||||
func ReposToMap(repotags []string) (map[string][]string, error) {
|
func ReposToMap(names []string) (map[string][]string, error) {
|
||||||
// map format is repo -> tag
|
// map format is repo -> []tag-or-digest
|
||||||
repos := make(map[string][]string)
|
repos := make(map[string][]string)
|
||||||
for _, repo := range repotags {
|
for _, name := range names {
|
||||||
var repository, tag string
|
var repository, tag string
|
||||||
if len(repo) > 0 {
|
if len(name) > 0 {
|
||||||
named, err := reference.ParseNormalizedNamed(repo)
|
named, err := reference.ParseNormalizedNamed(name)
|
||||||
repository = named.Name()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
repository = named.Name()
|
||||||
if ref, ok := named.(reference.NamedTagged); ok {
|
if ref, ok := named.(reference.NamedTagged); ok {
|
||||||
tag = ref.Tag()
|
tag = ref.Tag()
|
||||||
} else if ref, ok := named.(reference.Canonical); ok {
|
} else if ref, ok := named.(reference.Canonical); ok {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue