From f292bf0f4fd0907312b2982fcbf37ab06f526d7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Thu, 27 Oct 2022 14:31:35 +0200 Subject: [PATCH] distribution: Error when pulling OCI artifacts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently an attempt to pull a reference which resolves to an OCI artifact (Helm chart for example), results in a bit unrelated error message `invalid rootfs in image configuration`. This provides a more meaningful error in case a user attempts to download a media type which isn't image related. Signed-off-by: Paweł Gronowski (cherry picked from commit 407e3a455231bcf5b1c3e18a9e682a646b6e96ab) Signed-off-by: Sebastiaan van Stijn --- distribution/errors.go | 17 ++++++++++++++++- distribution/pull_v2.go | 22 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/distribution/errors.go b/distribution/errors.go index 02f587020ebf5..5aae6f03801b3 100644 --- a/distribution/errors.go +++ b/distribution/errors.go @@ -87,6 +87,19 @@ func (e notFoundError) Cause() error { return e.cause } +// unsupportedMediaTypeError is an error issued when attempted +// to pull unsupported content. +type unsupportedMediaTypeError struct { + MediaType string +} + +func (e unsupportedMediaTypeError) InvalidParameter() {} + +// Error returns the error string for unsupportedMediaTypeError. +func (e unsupportedMediaTypeError) Error() string { + return "unsupported media type " + e.MediaType +} + // TranslatePullError is used to convert an error from a registry pull // operation to an error representing the entire pull operation. Any error // information which is not used by the returned error gets output to @@ -150,6 +163,8 @@ func continueOnError(err error, mirrorEndpoint bool) bool { // Failures from a mirror endpoint should result in fallback to the // canonical repo. return mirrorEndpoint + case unsupportedMediaTypeError: + return false case error: return !strings.Contains(err.Error(), strings.ToLower(syscall.ESRCH.Error())) } @@ -179,7 +194,7 @@ func retryOnError(err error) error { return xfer.DoNotRetry{Err: v.Err} } return retryOnError(v.Err) - case *client.UnexpectedHTTPResponseError: + case *client.UnexpectedHTTPResponseError, unsupportedMediaTypeError: return xfer.DoNotRetry{Err: err} case error: if err == distribution.ErrBlobUnknown { diff --git a/distribution/pull_v2.go b/distribution/pull_v2.go index 5abd18b6cf7cd..871a0aee70ddb 100644 --- a/distribution/pull_v2.go +++ b/distribution/pull_v2.go @@ -619,6 +619,21 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv return imageID, manifestDigest, nil } +func checkSupportedMediaType(mediaType string) error { + supportedMediaTypes := []string{ + "application/vnd.oci.image.", + "application/vnd.docker.", + } + + lowerMt := strings.ToLower(mediaType) + for _, mt := range supportedMediaTypes { + if strings.HasPrefix(lowerMt, mt) { + return nil + } + } + return unsupportedMediaTypeError{MediaType: mediaType} +} + func (p *v2Puller) pullSchema2Layers(ctx context.Context, target distribution.Descriptor, layers []distribution.Descriptor, platform *specs.Platform) (id digest.Digest, err error) { if _, err := p.config.ImageStore.Get(ctx, target.Digest); err == nil { // If the image already exists locally, no need to pull @@ -626,6 +641,10 @@ func (p *v2Puller) pullSchema2Layers(ctx context.Context, target distribution.De return target.Digest, nil } + if err := checkSupportedMediaType(target.MediaType); err != nil { + return "", err + } + var descriptors []xfer.DownloadDescriptor // Note that the order of this loop is in the direction of bottom-most @@ -634,6 +653,9 @@ func (p *v2Puller) pullSchema2Layers(ctx context.Context, target distribution.De if err := d.Digest.Validate(); err != nil { return "", errors.Wrapf(err, "could not validate layer digest %q", d.Digest) } + if err := checkSupportedMediaType(d.MediaType); err != nil { + return "", err + } layerDescriptor := &v2LayerDescriptor{ digest: d.Digest, repo: p.repo,