github.com/tonistiigi/docker@v0.10.1-0.20240229224939-974013b0dc6a/distribution/errors.go (about)

     1  package distribution // import "github.com/docker/docker/distribution"
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/url"
     7  	"strings"
     8  	"syscall"
     9  
    10  	"github.com/containerd/log"
    11  	"github.com/distribution/reference"
    12  	"github.com/docker/distribution"
    13  	"github.com/docker/distribution/registry/api/errcode"
    14  	v2 "github.com/docker/distribution/registry/api/v2"
    15  	"github.com/docker/distribution/registry/client"
    16  	"github.com/docker/distribution/registry/client/auth"
    17  	"github.com/docker/docker/distribution/xfer"
    18  	"github.com/docker/docker/errdefs"
    19  	"github.com/pkg/errors"
    20  )
    21  
    22  // fallbackError wraps an error that can possibly allow fallback to a different
    23  // endpoint.
    24  type fallbackError struct {
    25  	// err is the error being wrapped.
    26  	err error
    27  	// transportOK is set to true if we managed to speak HTTP with the
    28  	// registry. This confirms that we're using appropriate TLS settings
    29  	// (or lack of TLS).
    30  	transportOK bool
    31  }
    32  
    33  // Error renders the FallbackError as a string.
    34  func (f fallbackError) Error() string {
    35  	return f.Cause().Error()
    36  }
    37  
    38  func (f fallbackError) Cause() error {
    39  	return f.err
    40  }
    41  
    42  type notFoundError struct {
    43  	cause errcode.Error
    44  	ref   reference.Named
    45  }
    46  
    47  func (e notFoundError) Error() string {
    48  	switch e.cause.Code {
    49  	case errcode.ErrorCodeDenied:
    50  		// ErrorCodeDenied is used when access to the repository was denied
    51  		return errors.Wrapf(e.cause, "pull access denied for %s, repository does not exist or may require 'docker login'", reference.FamiliarName(e.ref)).Error()
    52  	case v2.ErrorCodeManifestUnknown:
    53  		return errors.Wrapf(e.cause, "manifest for %s not found", reference.FamiliarString(e.ref)).Error()
    54  	case v2.ErrorCodeNameUnknown:
    55  		return errors.Wrapf(e.cause, "repository %s not found", reference.FamiliarName(e.ref)).Error()
    56  	}
    57  	// Shouldn't get here, but this is better than returning an empty string
    58  	return e.cause.Message
    59  }
    60  
    61  func (e notFoundError) NotFound() {}
    62  
    63  func (e notFoundError) Cause() error {
    64  	return e.cause
    65  }
    66  
    67  // unsupportedMediaTypeError is an error issued when attempted
    68  // to pull unsupported content.
    69  type unsupportedMediaTypeError struct {
    70  	MediaType string
    71  }
    72  
    73  func (e unsupportedMediaTypeError) InvalidParameter() {}
    74  
    75  // Error returns the error string for unsupportedMediaTypeError.
    76  func (e unsupportedMediaTypeError) Error() string {
    77  	return "unsupported media type " + e.MediaType
    78  }
    79  
    80  // translatePullError is used to convert an error from a registry pull
    81  // operation to an error representing the entire pull operation. Any error
    82  // information which is not used by the returned error gets output to
    83  // log at info level.
    84  func translatePullError(err error, ref reference.Named) error {
    85  	switch v := err.(type) {
    86  	case errcode.Errors:
    87  		if len(v) != 0 {
    88  			for _, extra := range v[1:] {
    89  				log.G(context.TODO()).WithError(extra).Infof("Ignoring extra error returned from registry")
    90  			}
    91  			return translatePullError(v[0], ref)
    92  		}
    93  	case errcode.Error:
    94  		switch v.Code {
    95  		case errcode.ErrorCodeDenied, v2.ErrorCodeManifestUnknown, v2.ErrorCodeNameUnknown:
    96  			return notFoundError{v, ref}
    97  		}
    98  	case xfer.DoNotRetry:
    99  		return translatePullError(v.Err, ref)
   100  	}
   101  
   102  	return errdefs.Unknown(err)
   103  }
   104  
   105  func isNotFound(err error) bool {
   106  	switch v := err.(type) {
   107  	case errcode.Errors:
   108  		for _, e := range v {
   109  			if isNotFound(e) {
   110  				return true
   111  			}
   112  		}
   113  	case errcode.Error:
   114  		switch v.Code {
   115  		case errcode.ErrorCodeDenied, v2.ErrorCodeManifestUnknown, v2.ErrorCodeNameUnknown:
   116  			return true
   117  		}
   118  	}
   119  	return false
   120  }
   121  
   122  // continueOnError returns true if we should fallback to the next endpoint
   123  // as a result of this error.
   124  func continueOnError(err error, mirrorEndpoint bool) bool {
   125  	switch v := err.(type) {
   126  	case errcode.Errors:
   127  		if len(v) == 0 {
   128  			return true
   129  		}
   130  		return continueOnError(v[0], mirrorEndpoint)
   131  	case errcode.Error:
   132  		return mirrorEndpoint
   133  	case *client.UnexpectedHTTPResponseError:
   134  		return true
   135  	case imageConfigPullError:
   136  		// imageConfigPullError only happens with v2 images, v1 fallback is
   137  		// unnecessary.
   138  		// Failures from a mirror endpoint should result in fallback to the
   139  		// canonical repo.
   140  		return mirrorEndpoint
   141  	case unsupportedMediaTypeError:
   142  		return false
   143  	case error:
   144  		return !strings.Contains(err.Error(), strings.ToLower(syscall.ESRCH.Error()))
   145  	}
   146  	// let's be nice and fallback if the error is a completely
   147  	// unexpected one.
   148  	// If new errors have to be handled in some way, please
   149  	// add them to the switch above.
   150  	return true
   151  }
   152  
   153  // retryOnError wraps the error in xfer.DoNotRetry if we should not retry the
   154  // operation after this error.
   155  func retryOnError(err error) error {
   156  	switch v := err.(type) {
   157  	case errcode.Errors:
   158  		if len(v) != 0 {
   159  			return retryOnError(v[0])
   160  		}
   161  	case errcode.Error:
   162  		switch v.Code {
   163  		case errcode.ErrorCodeUnauthorized, errcode.ErrorCodeUnsupported, errcode.ErrorCodeDenied, errcode.ErrorCodeTooManyRequests, v2.ErrorCodeNameUnknown:
   164  			return xfer.DoNotRetry{Err: err}
   165  		}
   166  	case *url.Error:
   167  		switch v.Err {
   168  		case auth.ErrNoBasicAuthCredentials, auth.ErrNoToken:
   169  			return xfer.DoNotRetry{Err: v.Err}
   170  		}
   171  		return retryOnError(v.Err)
   172  	case *client.UnexpectedHTTPResponseError, unsupportedMediaTypeError:
   173  		return xfer.DoNotRetry{Err: err}
   174  	case error:
   175  		if err == distribution.ErrBlobUnknown {
   176  			return xfer.DoNotRetry{Err: err}
   177  		}
   178  		if strings.Contains(err.Error(), strings.ToLower(syscall.ENOSPC.Error())) {
   179  			return xfer.DoNotRetry{Err: err}
   180  		}
   181  	}
   182  	// let's be nice and fallback if the error is a completely
   183  	// unexpected one.
   184  	// If new errors have to be handled in some way, please
   185  	// add them to the switch above.
   186  	return err
   187  }
   188  
   189  type invalidManifestClassError struct {
   190  	mediaType string
   191  	class     string
   192  }
   193  
   194  func (e invalidManifestClassError) Error() string {
   195  	return fmt.Sprintf("Encountered remote %q(%s) when fetching", e.mediaType, e.class)
   196  }
   197  
   198  func (e invalidManifestClassError) InvalidParameter() {}
   199  
   200  type invalidManifestFormatError struct{}
   201  
   202  func (invalidManifestFormatError) Error() string {
   203  	return "unsupported manifest format"
   204  }
   205  
   206  func (invalidManifestFormatError) InvalidParameter() {}
   207  
   208  type reservedNameError string
   209  
   210  func (e reservedNameError) Error() string {
   211  	return "'" + string(e) + "' is a reserved name"
   212  }
   213  
   214  func (e reservedNameError) Forbidden() {}
   215  
   216  type invalidArgumentErr struct{ error }
   217  
   218  func (invalidArgumentErr) InvalidParameter() {}
   219  
   220  func DeprecatedSchema1ImageError(ref reference.Named) error {
   221  	msg := "[DEPRECATION NOTICE] Docker Image Format v1 and Docker Image manifest version 2, schema 1 support is disabled by default and will be removed in an upcoming release."
   222  	if ref != nil {
   223  		msg += " Suggest the author of " + ref.String() + " to upgrade the image to the OCI Format or Docker Image manifest v2, schema 2."
   224  	}
   225  	msg += " More information at https://docs.docker.com/go/deprecated-image-specs/"
   226  	return invalidArgumentErr{errors.New(msg)}
   227  }