github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/engine/distribution/errors.go (about)

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