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 }