create: support images with invalid platform

Much to my regret, there is a number of images in the wild with invalid
platforms breaking the platform checks in libimage that want to make
sure that a local image is matching the expected platform.

Imagine a `podman run --arch=arm64 fedora` with a local amd64 fedora
image.  We really shouldn't use the local one in this case and pull down
the arm64 one.

The strict platform checks in libimage in combination with invalid
platforms in images surfaced in Podman being able to pull an image but
failing to look it up in subsequent presence checks.  A `podman run`
would hence pull such an image but fail to create the container.

Support images with invalid platforms by vendoring the latest HEAD from
containers/common.  Also remove the partially implemented pull-policy
logic from Podman and let libimage handle that entirely.  However,
whenever --arch, --os or --platform are specified, the pull policy will
be forced to "newer".  This way, we pessimistically assume that the
local image has an invalid platform and we reach out to the registry.
If there's a newer image (i.e., one with a different digest), we'll pull
it down.

Please note that most of the logic has either already been implemented
in libimage or been moved down which allows for removing some clutter
from Podman.

[NO TESTS NEEDED] since c/common has new tests.  Podman can rely on the
existing tests.

Fixes: #10648
Fixes: #10682
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
Valentin Rothberg
2021-06-21 10:51:51 +02:00
parent d3afc6b3b6
commit 5fc622f945
21 changed files with 197 additions and 166 deletions

View File

@@ -144,9 +144,8 @@ func (r *Runtime) Exists(name string) (bool, error) {
if image == nil {
return false, nil
}
// Inspect the image to make sure if it's corrupted or not.
if _, err := image.Inspect(context.Background(), false); err != nil {
logrus.Errorf("Image %s exists in local storage but may be corrupted: %v", name, err)
if err := image.isCorrupted(name); err != nil {
logrus.Error(err)
return false, nil
}
return true, nil
@@ -159,6 +158,13 @@ type LookupImageOptions struct {
// the platform does not matter, for instance, for image removal.
IgnorePlatform bool
// Lookup an image matching the specified architecture.
Architecture string
// Lookup an image matching the specified OS.
OS string
// Lookup an image matching the specified variant.
Variant string
// If set, do not look for items/instances in the manifest list that
// match the current platform but return the manifest list as is.
lookupManifest bool
@@ -210,6 +216,25 @@ func (r *Runtime) LookupImage(name string, options *LookupImageOptions) (*Image,
name = strings.TrimPrefix(name, "sha256:")
}
// Set the platform for matching local images.
if !options.IgnorePlatform {
if options.Architecture == "" {
options.Architecture = r.systemContext.ArchitectureChoice
}
if options.Architecture == "" {
options.Architecture = runtime.GOARCH
}
if options.OS == "" {
options.OS = r.systemContext.OSChoice
}
if options.OS == "" {
options.OS = runtime.GOOS
}
if options.Variant == "" {
options.Variant = r.systemContext.VariantChoice
}
}
// First, check if we have an exact match in the storage. Maybe an ID
// or a fully-qualified image name.
img, err := r.lookupImageInLocalStorage(name, name, options)
@@ -295,7 +320,7 @@ func (r *Runtime) lookupImageInLocalStorage(name, candidate string, options *Loo
if err != nil {
return nil, err
}
instance, err := manifestList.LookupInstance(context.Background(), "", "", "")
instance, err := manifestList.LookupInstance(context.Background(), options.Architecture, options.OS, options.Variant)
if err != nil {
// NOTE: If we are not looking for a specific platform
// and already found the manifest list, then return it
@@ -316,7 +341,7 @@ func (r *Runtime) lookupImageInLocalStorage(name, candidate string, options *Loo
return image, nil
}
matches, err := imageReferenceMatchesContext(context.Background(), ref, &r.systemContext)
matches, err := r.imageReferenceMatchesContext(ref, options)
if err != nil {
return nil, err
}
@@ -428,12 +453,13 @@ func (r *Runtime) ResolveName(name string) (string, error) {
}
// imageReferenceMatchesContext return true if the specified reference matches
// the platform (os, arch, variant) as specified by the system context.
func imageReferenceMatchesContext(ctx context.Context, ref types.ImageReference, sys *types.SystemContext) (bool, error) {
if sys == nil {
// the platform (os, arch, variant) as specified by the lookup options.
func (r *Runtime) imageReferenceMatchesContext(ref types.ImageReference, options *LookupImageOptions) (bool, error) {
if options.IgnorePlatform {
return true, nil
}
img, err := ref.NewImage(ctx, sys)
ctx := context.Background()
img, err := ref.NewImage(ctx, &r.systemContext)
if err != nil {
return false, err
}
@@ -442,16 +468,8 @@ func imageReferenceMatchesContext(ctx context.Context, ref types.ImageReference,
if err != nil {
return false, err
}
osChoice := sys.OSChoice
if osChoice == "" {
osChoice = runtime.GOOS
}
arch := sys.ArchitectureChoice
if arch == "" {
arch = runtime.GOARCH
}
if osChoice == data.Os && arch == data.Architecture {
if sys.VariantChoice == "" || sys.VariantChoice == data.Variant {
if options.OS == data.Os && options.Architecture == data.Architecture {
if options.Variant == "" || options.Variant == data.Variant {
return true, nil
}
}