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