From 407e3a455231bcf5b1c3e18a9e682a646b6e96ab 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 --- distribution/errors.go | 17 ++++++++++++++++- distribution/pull_v2.go | 23 +++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/distribution/errors.go b/distribution/errors.go index fa1e2214eb..d0de8d9e19 100644 --- a/distribution/errors.go +++ b/distribution/errors.go @@ -63,6 +63,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 @@ -124,6 +137,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())) } @@ -153,7 +168,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 2af5251fd5..420a5af436 100644 --- a/distribution/pull_v2.go +++ b/distribution/pull_v2.go @@ -7,6 +7,7 @@ import ( "io" "os" "runtime" + "strings" "time" "github.com/containerd/containerd/log" @@ -606,6 +607,21 @@ func (p *puller) pullSchema1(ctx context.Context, ref reference.Reference, unver 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 *puller) 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 @@ -613,6 +629,10 @@ func (p *puller) pullSchema2Layers(ctx context.Context, target distribution.Desc 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 @@ -621,6 +641,9 @@ func (p *puller) pullSchema2Layers(ctx context.Context, target distribution.Desc 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 := &layerDescriptor{ digest: d.Digest, repo: p.repo,