From 19a89db66122e7dcc543fa27307abef28c6a516b Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Wed, 23 Jun 2021 15:56:03 +0200 Subject: [PATCH 1/2] vendor containers/common@v0.38.10 Please refer to the individual commits for further details: * libimage: pull: override even --pull=never with custom platfo * libimage: pull: enforce pull policy for custom platforms * libimage: pull: ignore platform for local image lookup * Allow /etc/containers/containers.conf to be read by non-root * [0.38] libimage: force remove: only untag on multi tag image Signed-off-by: Valentin Rothberg --- go.mod | 2 +- go.sum | 4 +- .../containers/common/libimage/image.go | 34 ++++++++++--- .../containers/common/libimage/pull.go | 48 ++++++++++++++++++- .../containers/common/libimage/runtime.go | 5 +- .../containers/common/pkg/config/config.go | 2 +- .../containers/common/version/version.go | 2 +- vendor/modules.txt | 2 +- 8 files changed, 81 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index f701a495f3..a6fc311644 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/containernetworking/cni v0.8.1 github.com/containernetworking/plugins v0.9.1 github.com/containers/buildah v1.21.0 - github.com/containers/common v0.38.9 + github.com/containers/common v0.38.10 github.com/containers/conmon v2.0.20+incompatible github.com/containers/image/v5 v5.12.0 github.com/containers/ocicrypt v1.1.1 diff --git a/go.sum b/go.sum index 28143c399d..899093c6cf 100644 --- a/go.sum +++ b/go.sum @@ -218,8 +218,8 @@ github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRD github.com/containers/buildah v1.21.0 h1:LuwuqRPjan3X3AIdGwfkEkqMgmrDMNpQznFqNdHgCz8= github.com/containers/buildah v1.21.0/go.mod h1:yPdlpVd93T+i91yGxrJbW1YOWrqN64j5ZhHOZmHUejs= github.com/containers/common v0.38.4/go.mod h1:egfpX/Y3+19Dz4Wa1eRZDdgzoEOeneieF9CQppKzLBg= -github.com/containers/common v0.38.9 h1:73TUUqIIMRU6hNqrgmZy4vlWPgMz4A/oFfi+v2VEdkg= -github.com/containers/common v0.38.9/go.mod h1:egfpX/Y3+19Dz4Wa1eRZDdgzoEOeneieF9CQppKzLBg= +github.com/containers/common v0.38.10 h1:X3spMNjrqKYQ25Lc+Z1wpc0t7KIrwO6mT2S5J1TDiTI= +github.com/containers/common v0.38.10/go.mod h1:egfpX/Y3+19Dz4Wa1eRZDdgzoEOeneieF9CQppKzLBg= github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg= github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= github.com/containers/image/v5 v5.12.0 h1:1hNS2QkzFQ4lH3GYQLyAXB0acRMhS1Ubm6oV++8vw4w= diff --git a/vendor/github.com/containers/common/libimage/image.go b/vendor/github.com/containers/common/libimage/image.go index 1f76d4ae59..4349e9baba 100644 --- a/vendor/github.com/containers/common/libimage/image.go +++ b/vendor/github.com/containers/common/libimage/image.go @@ -61,6 +61,24 @@ func (i *Image) reload() error { return nil } +// isCorrupted returns an error if the image may be corrupted. +func (i *Image) isCorrupted(name string) error { + // If it's a manifest list, we're good for now. + if _, err := i.getManifestList(); err == nil { + return nil + } + + ref, err := i.StorageReference() + if err != nil { + return err + } + + if _, err := ref.NewImage(context.Background(), nil); err != nil { + return errors.Errorf("Image %s exists in local storage but may be corrupted: %v", name, err) + } + return nil +} + // Names returns associated names with the image which may be a mix of tags and // digests. func (i *Image) Names() []string { @@ -329,17 +347,19 @@ func (i *Image) remove(ctx context.Context, rmMap map[string]*RemoveImageReport, // an `rmi foo` will not untag "foo" but instead attempt to remove the // entire image. If there's a container using "foo", we should get an // error. - if options.Force || referencedBy == "" || numNames == 1 { + if referencedBy == "" || numNames == 1 { // DO NOTHING, the image will be removed } else { byID := strings.HasPrefix(i.ID(), referencedBy) byDigest := strings.HasPrefix(referencedBy, "sha256:") - if byID && numNames > 1 { - return errors.Errorf("unable to delete image %q by ID with more than one tag (%s): please force removal", i.ID(), i.Names()) - } else if byDigest && numNames > 1 { - // FIXME - Docker will remove the digest but containers storage - // does not support that yet, so our hands are tied. - return errors.Errorf("unable to delete image %q by digest with more than one tag (%s): please force removal", i.ID(), i.Names()) + if !options.Force { + if byID && numNames > 1 { + return errors.Errorf("unable to delete image %q by ID with more than one tag (%s): please force removal", i.ID(), i.Names()) + } else if byDigest && numNames > 1 { + // FIXME - Docker will remove the digest but containers storage + // does not support that yet, so our hands are tied. + return errors.Errorf("unable to delete image %q by digest with more than one tag (%s): please force removal", i.ID(), i.Names()) + } } // Only try to untag if we know it's not an ID or digest. diff --git a/vendor/github.com/containers/common/libimage/pull.go b/vendor/github.com/containers/common/libimage/pull.go index 3c3bfca104..d97a884d2a 100644 --- a/vendor/github.com/containers/common/libimage/pull.go +++ b/vendor/github.com/containers/common/libimage/pull.go @@ -96,6 +96,20 @@ func (r *Runtime) Pull(ctx context.Context, name string, pullPolicy config.PullP r.writeEvent(&Event{ID: "", Name: name, Time: time.Now(), Type: EventTypeImagePull}) } + // Some callers may set the platform via the system context at creation + // time of the runtime. We need this information to decide whether we + // need to enforce pulling from a registry (see + // containers/podman/issues/10682). + if options.Architecture == "" { + options.Architecture = r.systemContext.ArchitectureChoice + } + if options.OS == "" { + options.OS = r.systemContext.OSChoice + } + if options.Variant == "" { + options.Variant = r.systemContext.VariantChoice + } + var ( pulledImages []string pullError error @@ -323,7 +337,7 @@ func (r *Runtime) copyFromRegistry(ctx context.Context, ref types.ImageReference // from a registry. On successful pull it returns the used fully-qualified // name that can later be used to look up the image in the local containers // storage. -func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName string, pullPolicy config.PullPolicy, options *PullOptions) ([]string, error) { +func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName string, pullPolicy config.PullPolicy, options *PullOptions) ([]string, error) { //nolint:gocyclo // Sanity check. if err := pullPolicy.Validate(); err != nil { return nil, err @@ -339,11 +353,41 @@ func (r *Runtime) copySingleImageFromRegistry(ctx context.Context, imageName str // resolved name for pulling. Assume we're doing a `pull foo`. // If there's already a local image "localhost/foo", then we should // attempt pulling that instead of doing the full short-name dance. - localImage, resolvedImageName, err = r.LookupImage(imageName, nil) + lookupOptions := &LookupImageOptions{ + // NOTE: we must ignore the platform of a local image when + // doing lookups. Some images set an incorrect or even invalid + // platform (see containers/podman/issues/10682). Doing the + // lookup while ignoring the platform checks prevents + // redundantly downloading the same image. + IgnorePlatform: true, + } + localImage, resolvedImageName, err = r.LookupImage(imageName, lookupOptions) if err != nil && errors.Cause(err) != storage.ErrImageUnknown { logrus.Errorf("Looking up %s in local storage: %v", imageName, err) } + // If the local image is corrupted, we need to repull it. + if localImage != nil { + if err := localImage.isCorrupted(imageName); err != nil { + logrus.Error(err) + localImage = nil + } + } + + // Unless the pull policy is "always", we must pessimistically assume + // that the local image has an invalid architecture (see + // containers/podman/issues/10682). Hence, whenever the user requests + // a custom platform, set the pull policy to "always" to make sure + // we're pulling down the image. + // + // NOTE that this is will even override --pull={false,never}. This is + // very likely a bug but a consistent one in Podman/Buildah and should + // be addressed at a later point. + if pullPolicy != config.PullPolicyAlways && len(options.Architecture)+len(options.OS)+len(options.Variant) > 0 { + logrus.Debugf("Enforcing pull policy to %q to support custom platform (arch: %q, os: %q, variant: %q)", "always", options.Architecture, options.OS, options.Variant) + pullPolicy = config.PullPolicyAlways + } + if pullPolicy == config.PullPolicyNever { if localImage != nil { logrus.Debugf("Pull policy %q but no local image has been found for %s", pullPolicy, imageName) diff --git a/vendor/github.com/containers/common/libimage/runtime.go b/vendor/github.com/containers/common/libimage/runtime.go index 382a28c8bd..5e1b6a411e 100644 --- a/vendor/github.com/containers/common/libimage/runtime.go +++ b/vendor/github.com/containers/common/libimage/runtime.go @@ -141,9 +141,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 diff --git a/vendor/github.com/containers/common/pkg/config/config.go b/vendor/github.com/containers/common/pkg/config/config.go index 9ac71c6c8a..6fad393fa0 100644 --- a/vendor/github.com/containers/common/pkg/config/config.go +++ b/vendor/github.com/containers/common/pkg/config/config.go @@ -981,7 +981,7 @@ func (c *Config) Write() error { if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { return err } - configFile, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0600) + configFile, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644) if err != nil { return err } diff --git a/vendor/github.com/containers/common/version/version.go b/vendor/github.com/containers/common/version/version.go index 6af1594bac..60cfd6acdb 100644 --- a/vendor/github.com/containers/common/version/version.go +++ b/vendor/github.com/containers/common/version/version.go @@ -1,4 +1,4 @@ package version // Version is the version of the build. -const Version = "0.38.9" +const Version = "0.38.10" diff --git a/vendor/modules.txt b/vendor/modules.txt index 03099235ee..6be35cb065 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -91,7 +91,7 @@ github.com/containers/buildah/pkg/overlay github.com/containers/buildah/pkg/parse github.com/containers/buildah/pkg/rusage github.com/containers/buildah/util -# github.com/containers/common v0.38.9 +# github.com/containers/common v0.38.10 github.com/containers/common/libimage github.com/containers/common/libimage/manifests github.com/containers/common/pkg/apparmor From 6f769bc0e9879e981fc4b8bc0d474b7ad4049431 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Mon, 21 Jun 2021 10:51:51 +0200 Subject: [PATCH 2/2] 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 --- cmd/podman/containers/create.go | 58 ++++++-------------- pkg/api/handlers/compat/containers_create.go | 5 +- pkg/api/handlers/libpod/images.go | 2 +- pkg/api/handlers/libpod/images_pull.go | 26 ++++++--- pkg/api/handlers/libpod/manifests.go | 2 +- pkg/api/handlers/utils/images.go | 25 ++++----- pkg/api/server/register_images.go | 4 ++ pkg/bindings/images/pull.go | 10 ++-- pkg/bindings/images/types.go | 3 + pkg/bindings/images/types_pull_options.go | 16 ++++++ pkg/domain/infra/tunnel/images.go | 2 +- pkg/errorhandling/errorhandling.go | 9 ++- pkg/specgen/generate/container.go | 3 +- pkg/specgen/generate/container_create.go | 3 +- 14 files changed, 88 insertions(+), 80 deletions(-) diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index f06869c4eb..52cf8fea41 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -8,7 +8,6 @@ import ( "strings" "github.com/containers/common/pkg/config" - storageTransport "github.com/containers/image/v5/storage" "github.com/containers/image/v5/transports/alltransports" "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" @@ -16,9 +15,7 @@ import ( "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/podman/v3/pkg/util" - "github.com/containers/storage" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -238,30 +235,12 @@ func createInit(c *cobra.Command) error { return nil } -// TODO: we should let the backend take care of the pull policy (which it -// does!). The code below is at risk of causing regression and code divergence. func pullImage(imageName string) (string, error) { pullPolicy, err := config.ValidatePullPolicy(cliVals.Pull) if err != nil { return "", err } - // Check if the image is missing and hence if we need to pull it. - imageMissing := true - imageRef, err := alltransports.ParseImageName(imageName) - switch { - case err != nil: - // Assume we specified a local image without the explicit storage transport. - fallthrough - - case imageRef.Transport().Name() == storageTransport.Transport.Name(): - br, err := registry.ImageEngine().Exists(registry.GetContext(), imageName) - if err != nil { - return "", err - } - imageMissing = !br.Value - } - if cliVals.Platform != "" || cliVals.Arch != "" || cliVals.OS != "" { if cliVals.Platform != "" { if cliVals.Arch != "" || cliVals.OS != "" { @@ -273,31 +252,28 @@ func pullImage(imageName string) (string, error) { cliVals.Arch = split[1] } } + } - if pullPolicy != config.PullPolicyAlways { - logrus.Info("--platform --arch and --os causes the pull policy to be \"always\"") - pullPolicy = config.PullPolicyAlways - } + pullReport, pullErr := registry.ImageEngine().Pull(registry.GetContext(), imageName, entities.ImagePullOptions{ + Authfile: cliVals.Authfile, + Quiet: cliVals.Quiet, + Arch: cliVals.Arch, + OS: cliVals.OS, + Variant: cliVals.Variant, + SignaturePolicy: cliVals.SignaturePolicy, + PullPolicy: pullPolicy, + }) + if pullErr != nil { + return "", pullErr } - if imageMissing || pullPolicy == config.PullPolicyAlways { - if pullPolicy == config.PullPolicyNever { - return "", errors.Wrap(storage.ErrImageUnknown, imageName) - } - pullReport, pullErr := registry.ImageEngine().Pull(registry.GetContext(), imageName, entities.ImagePullOptions{ - Authfile: cliVals.Authfile, - Quiet: cliVals.Quiet, - Arch: cliVals.Arch, - OS: cliVals.OS, - Variant: cliVals.Variant, - SignaturePolicy: cliVals.SignaturePolicy, - PullPolicy: pullPolicy, - }) - if pullErr != nil { - return "", pullErr - } + // Return the input name such that the image resolves to correct + // repo/tag in the backend (see #8082). Unless we're referring to + // the image via a transport. + if _, err := alltransports.ParseImageName(imageName); err == nil { imageName = pullReport.Images[0] } + return imageName, nil } diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go index 162a98135b..15eb86422d 100644 --- a/pkg/api/handlers/compat/containers_create.go +++ b/pkg/api/handlers/compat/containers_create.go @@ -71,13 +71,12 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { imgNameOrID := newImage.ID() // if the img had multi names with the same sha256 ID, should use the InputName, not the ID if len(newImage.Names()) > 1 { - imageRef, err := utils.ParseDockerReference(resolvedName) - if err != nil { + if err := utils.IsRegistryReference(resolvedName); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return } // maybe the InputName has no tag, so use full name to display - imgNameOrID = imageRef.DockerReference().String() + imgNameOrID = resolvedName } sg := specgen.NewSpecGenerator(imgNameOrID, cliOpts.RootFS) diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index a90408bfdd..fc6ab4b4c4 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -482,7 +482,7 @@ func PushImage(w http.ResponseWriter, r *http.Request) { destination = source } - if _, err := utils.ParseDockerReference(destination); err != nil { + if err := utils.IsRegistryReference(destination); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return } diff --git a/pkg/api/handlers/libpod/images_pull.go b/pkg/api/handlers/libpod/images_pull.go index 73d08a26e1..04b4156386 100644 --- a/pkg/api/handlers/libpod/images_pull.go +++ b/pkg/api/handlers/libpod/images_pull.go @@ -26,14 +26,16 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { - Reference string `schema:"reference"` - OS string `schema:"OS"` - Arch string `schema:"Arch"` - Variant string `schema:"Variant"` - TLSVerify bool `schema:"tlsVerify"` - AllTags bool `schema:"allTags"` + Reference string `schema:"reference"` + OS string `schema:"OS"` + Arch string `schema:"Arch"` + Variant string `schema:"Variant"` + TLSVerify bool `schema:"tlsVerify"` + AllTags bool `schema:"allTags"` + PullPolicy string `schema:"policy"` }{ - TLSVerify: true, + TLSVerify: true, + PullPolicy: "always", } if err := decoder.Decode(&query, r.URL.Query()); err != nil { @@ -48,7 +50,7 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { } // Make sure that the reference has no transport or the docker one. - if _, err := utils.ParseDockerReference(query.Reference); err != nil { + if err := utils.IsRegistryReference(query.Reference); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return } @@ -83,12 +85,18 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { pullOptions.Writer = writer + pullPolicy, err := config.ParsePullPolicy(query.PullPolicy) + if err != nil { + utils.Error(w, "failed to parse pull policy", http.StatusBadRequest, err) + return + } + var pulledImages []*libimage.Image var pullError error runCtx, cancel := context.WithCancel(r.Context()) go func() { defer cancel() - pulledImages, pullError = runtime.LibimageRuntime().Pull(runCtx, query.Reference, config.PullPolicyAlways, pullOptions) + pulledImages, pullError = runtime.LibimageRuntime().Pull(runCtx, query.Reference, pullPolicy, pullOptions) }() flush := func() { diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go index f21eb2e80f..2f36db583a 100644 --- a/pkg/api/handlers/libpod/manifests.go +++ b/pkg/api/handlers/libpod/manifests.go @@ -169,7 +169,7 @@ func ManifestPush(w http.ResponseWriter, r *http.Request) { errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } - if _, err := utils.ParseDockerReference(query.Destination); err != nil { + if err := utils.IsRegistryReference(query.Destination); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return } diff --git a/pkg/api/handlers/utils/images.go b/pkg/api/handlers/utils/images.go index 2662cd368b..2a1908d63e 100644 --- a/pkg/api/handlers/utils/images.go +++ b/pkg/api/handlers/utils/images.go @@ -15,22 +15,19 @@ import ( "github.com/pkg/errors" ) -// ParseDockerReference parses the specified image name to a -// `types.ImageReference` and enforces it to refer to a docker-transport -// reference. -func ParseDockerReference(name string) (types.ImageReference, error) { - dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name()) +// IsRegistryReference checks if the specified name points to the "docker://" +// transport. If it points to no supported transport, we'll assume a +// non-transport reference pointing to an image (e.g., "fedora:latest"). +func IsRegistryReference(name string) error { imageRef, err := alltransports.ParseImageName(name) - if err == nil && imageRef.Transport().Name() != docker.Transport.Name() { - return nil, errors.Errorf("reference %q must be a docker reference", name) - } else if err != nil { - origErr := err - imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, name)) - if err != nil { - return nil, errors.Wrapf(origErr, "reference %q must be a docker reference", name) - } + if err != nil { + // No supported transport -> assume a docker-stype reference. + return nil } - return imageRef, nil + if imageRef.Transport().Name() == docker.Transport.Name() { + return nil + } + return errors.Errorf("unsupport transport %s in %q: only docker transport is supported", imageRef.Transport().Name(), name) } // ParseStorageReference parses the specified image name to a diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index b32c0df206..2641809ee5 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -964,6 +964,10 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // description: Pull image for the specified variant. // type: string // - in: query + // name: policy + // description: Pull policy, "always" (default), "missing", "newer", "never". + // type: string + // - in: query // name: tlsVerify // description: Require TLS verification. // type: boolean diff --git a/pkg/bindings/images/pull.go b/pkg/bindings/images/pull.go index 9780c3bfff..7dfe9560c2 100644 --- a/pkg/bindings/images/pull.go +++ b/pkg/bindings/images/pull.go @@ -13,7 +13,7 @@ import ( "github.com/containers/podman/v3/pkg/auth" "github.com/containers/podman/v3/pkg/bindings" "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/hashicorp/go-multierror" + "github.com/containers/podman/v3/pkg/errorhandling" "github.com/pkg/errors" ) @@ -65,7 +65,7 @@ func Pull(ctx context.Context, rawImage string, options *PullOptions) ([]string, dec := json.NewDecoder(response.Body) var images []string - var mErr error + var pullErrors []error for { var report entities.ImagePullReport if err := dec.Decode(&report); err != nil { @@ -77,7 +77,7 @@ func Pull(ctx context.Context, rawImage string, options *PullOptions) ([]string, select { case <-response.Request.Context().Done(): - return images, mErr + break default: // non-blocking select } @@ -86,7 +86,7 @@ func Pull(ctx context.Context, rawImage string, options *PullOptions) ([]string, case report.Stream != "": fmt.Fprint(stderr, report.Stream) case report.Error != "": - mErr = multierror.Append(mErr, errors.New(report.Error)) + pullErrors = append(pullErrors, errors.New(report.Error)) case len(report.Images) > 0: images = report.Images case report.ID != "": @@ -94,5 +94,5 @@ func Pull(ctx context.Context, rawImage string, options *PullOptions) ([]string, return images, errors.Errorf("failed to parse pull results stream, unexpected input: %v", report) } } - return images, mErr + return images, errorhandling.JoinErrors(pullErrors) } diff --git a/pkg/bindings/images/types.go b/pkg/bindings/images/types.go index 1f3e46729f..0aa75a81e1 100644 --- a/pkg/bindings/images/types.go +++ b/pkg/bindings/images/types.go @@ -147,6 +147,9 @@ type PullOptions struct { // OS will overwrite the local operating system (OS) for image // pulls. OS *string + // Policy is the pull policy. Supported values are "missing", "never", + // "newer", "always". An empty string defaults to "always". + Policy *string // Password for authenticating against the registry. Password *string // Quiet can be specified to suppress pull progress when pulling. Ignored diff --git a/pkg/bindings/images/types_pull_options.go b/pkg/bindings/images/types_pull_options.go index 0611c4447d..8fcf499eb9 100644 --- a/pkg/bindings/images/types_pull_options.go +++ b/pkg/bindings/images/types_pull_options.go @@ -84,6 +84,22 @@ func (o *PullOptions) GetOS() string { return *o.OS } +// WithPolicy +func (o *PullOptions) WithPolicy(value string) *PullOptions { + v := &value + o.Policy = v + return o +} + +// GetPolicy +func (o *PullOptions) GetPolicy() string { + var policy string + if o.Policy == nil { + return policy + } + return *o.Policy +} + // WithPassword func (o *PullOptions) WithPassword(value string) *PullOptions { v := &value diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index 3fd9a755d3..42027a2dc3 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -107,7 +107,7 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, opts entities. options := new(images.PullOptions) options.WithAllTags(opts.AllTags).WithAuthfile(opts.Authfile).WithArch(opts.Arch).WithOS(opts.OS) options.WithVariant(opts.Variant).WithPassword(opts.Password) - options.WithQuiet(opts.Quiet).WithUsername(opts.Username) + options.WithQuiet(opts.Quiet).WithUsername(opts.Username).WithPolicy(opts.PullPolicy.String()) if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { if s == types.OptionalBoolTrue { options.WithSkipTLSVerify(true) diff --git a/pkg/errorhandling/errorhandling.go b/pkg/errorhandling/errorhandling.go index 9b1740006f..6adbc9f344 100644 --- a/pkg/errorhandling/errorhandling.go +++ b/pkg/errorhandling/errorhandling.go @@ -15,6 +15,12 @@ func JoinErrors(errs []error) error { return nil } + // If there's just one error, return it. This prevents the "%d errors + // occurred:" header plus list from the multierror package. + if len(errs) == 1 { + return errs[0] + } + // `multierror` appends new lines which we need to remove to prevent // blank lines when printing the error. var multiE *multierror.Error @@ -24,9 +30,6 @@ func JoinErrors(errs []error) error { if finalErr == nil { return finalErr } - if len(multiE.WrappedErrors()) == 1 && logrus.IsLevelEnabled(logrus.TraceLevel) { - return multiE.WrappedErrors()[0] - } return errors.New(strings.TrimSpace(finalErr.Error())) } diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index d00e51e825..e7276892d8 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -24,7 +24,8 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat var inspectData *libimage.ImageData var err error if s.Image != "" { - newImage, _, err = r.LibimageRuntime().LookupImage(s.Image, nil) + lookupOptions := &libimage.LookupImageOptions{IgnorePlatform: true} + newImage, _, err = r.LibimageRuntime().LookupImage(s.Image, lookupOptions) if err != nil { return nil, err } diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index 7682367b7c..e53032ebea 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -92,7 +92,8 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener options = append(options, libpod.WithRootFS(s.Rootfs)) } else { var resolvedImageName string - newImage, resolvedImageName, err = rt.LibimageRuntime().LookupImage(s.Image, nil) + lookupOptions := &libimage.LookupImageOptions{IgnorePlatform: true} + newImage, resolvedImageName, err = rt.LibimageRuntime().LookupImage(s.Image, lookupOptions) if err != nil { return nil, err }