Skip to content

Commit

Permalink
libimage: LookupImage: stop matching *any* tags
Browse files Browse the repository at this point in the history
For reasons buried in the history of Podman, looking up an untagged
image would match *any* tag of matching image.  For instance,
looking up `centos` would match a local image `centos:foobar`.
Docker only looks for `centos:latest`.

Context: github.com/containers/podman/issues/11964
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
  • Loading branch information
vrothberg committed Oct 18, 2021
1 parent 4077ae5 commit 1039100
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 54 deletions.
18 changes: 18 additions & 0 deletions libimage/image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,24 @@ func TestTag(t *testing.T) {
require.True(t, errors.Cause(err) == errTagDigest, "check for specific digest error")
}

func TestLookupWithoutTag(t *testing.T) {
// Regression test for github.com/containers/podman/issues/11964.
runtime, cleanup := testNewRuntime(t)
defer cleanup()
ctx := context.Background()

pullOptions := &PullOptions{}
pullOptions.Writer = os.Stdout
pulledImages, err := runtime.Pull(ctx, "docker.io/library/busybox:unstable", config.PullPolicyMissing, pullOptions)
require.NoError(t, err)
require.Len(t, pulledImages, 1)

_, _, err = runtime.LookupImage("busybox", nil)
require.Error(t, err, "should not resolve to non-latest tag")
_, _, err = runtime.LookupImage("busybox:unstable", nil)
require.NoError(t, err, "should find local image")
}

func TestUntag(t *testing.T) {
// Note: this will resolve pull from the GCR registry (see
// testdata/registries.conf).
Expand Down
72 changes: 18 additions & 54 deletions libimage/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ func (r *Runtime) LookupImage(name string, options *LookupImageOptions) (*Image,
}
}

return r.lookupImageInDigestsAndRepoTags(originalName, options)
return r.lookupImageInDigests(originalName, options)
}

// lookupImageInLocalStorage looks up the specified candidate for name in the
Expand Down Expand Up @@ -365,67 +365,31 @@ func (r *Runtime) lookupImageInLocalStorage(name, candidate string, options *Loo
return image, nil
}

// lookupImageInDigestsAndRepoTags attempts to match name against any image in
// the local containers storage. If name is digested, it will be compared
// against image digests. Otherwise, it will be looked up in the repo tags.
func (r *Runtime) lookupImageInDigestsAndRepoTags(name string, options *LookupImageOptions) (*Image, string, error) {
// Until now, we've tried very hard to find an image but now it is time
// for limbo. If the image includes a digest that we couldn't detect
// verbatim in the storage, we must have a look at all digests of all
// images. Those may change over time (e.g., via manifest lists).
// Both Podman and Buildah want us to do that dance.
allImages, err := r.ListImages(context.Background(), nil, nil)
// If name is a digested image, look at all images in the local storage and
// check whether a digest matches.
func (r *Runtime) lookupImageInDigests(name string, options *LookupImageOptions) (*Image, string, error) {
named, err := reference.ParseNormalizedNamed(name)
if err != nil {
return nil, "", err
}

if !shortnames.IsShortName(name) {
named, err := reference.ParseNormalizedNamed(name)
if err != nil {
return nil, "", err
}
digested, hasDigest := named.(reference.Digested)
if !hasDigest {
return nil, "", errors.Wrap(storage.ErrImageUnknown, name)
}

logrus.Debug("Looking for image with matching recorded digests")
digest := digested.Digest()
for _, image := range allImages {
for _, d := range image.Digests() {
if d == digest {
return image, name, nil
}
}
}

digested, hasDigest := named.(reference.Digested)
if !hasDigest {
return nil, "", errors.Wrap(storage.ErrImageUnknown, name)
}

// Podman compat: if we're looking for a short name but couldn't
// resolve it via the registries.conf dance, we need to look at *all*
// images and check if the name we're looking for matches a repo tag.
// Split the name into a repo/tag pair
split := strings.SplitN(name, ":", 2)
repo := split[0]
tag := ""
if len(split) == 2 {
tag = split[1]
logrus.Debug("Looking for image with matching recorded digests")

allImages, err := r.ListImages(context.Background(), nil, nil)
if err != nil {
return nil, "", err
}

digest := digested.Digest()
for _, image := range allImages {
named, err := image.inRepoTags(repo, tag)
if err != nil {
return nil, "", err
}
if named == nil {
continue
}
img, err := r.lookupImageInLocalStorage(name, named.String(), options)
if err != nil {
return nil, "", err
}
if img != nil {
return img, named.String(), err
for _, d := range image.Digests() {
if d == digest {
return image, name, nil
}
}
}

Expand Down

0 comments on commit 1039100

Please sign in to comment.