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() {}