github.com/wozhu6104/docker@v20.10.10+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 // 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 // TranslatePullError is used to convert an error from a registry pull 91 // operation to an error representing the entire pull operation. Any error 92 // information which is not used by the returned error gets output to 93 // log at info level. 94 func TranslatePullError(err error, ref reference.Named) error { 95 switch v := err.(type) { 96 case errcode.Errors: 97 if len(v) != 0 { 98 for _, extra := range v[1:] { 99 logrus.Infof("Ignoring extra error returned from registry: %v", extra) 100 } 101 return TranslatePullError(v[0], ref) 102 } 103 case errcode.Error: 104 switch v.Code { 105 case errcode.ErrorCodeDenied, v2.ErrorCodeManifestUnknown, v2.ErrorCodeNameUnknown: 106 return notFoundError{v, ref} 107 } 108 case xfer.DoNotRetry: 109 return TranslatePullError(v.Err, ref) 110 } 111 112 return errdefs.Unknown(err) 113 } 114 115 func isNotFound(err error) bool { 116 switch v := err.(type) { 117 case errcode.Errors: 118 for _, e := range v { 119 if isNotFound(e) { 120 return true 121 } 122 } 123 case errcode.Error: 124 switch v.Code { 125 case errcode.ErrorCodeDenied, v2.ErrorCodeManifestUnknown, v2.ErrorCodeNameUnknown: 126 return true 127 } 128 } 129 return false 130 } 131 132 // continueOnError returns true if we should fallback to the next endpoint 133 // as a result of this error. 134 func continueOnError(err error, mirrorEndpoint bool) bool { 135 switch v := err.(type) { 136 case errcode.Errors: 137 if len(v) == 0 { 138 return true 139 } 140 return continueOnError(v[0], mirrorEndpoint) 141 case ErrNoSupport: 142 return continueOnError(v.Err, mirrorEndpoint) 143 case errcode.Error: 144 return mirrorEndpoint || shouldV2Fallback(v) 145 case *client.UnexpectedHTTPResponseError: 146 return true 147 case ImageConfigPullError: 148 // ImageConfigPullError only happens with v2 images, v1 fallback is 149 // unnecessary. 150 // Failures from a mirror endpoint should result in fallback to the 151 // canonical repo. 152 return mirrorEndpoint 153 case error: 154 return !strings.Contains(err.Error(), strings.ToLower(syscall.ESRCH.Error())) 155 } 156 // let's be nice and fallback if the error is a completely 157 // unexpected one. 158 // If new errors have to be handled in some way, please 159 // add them to the switch above. 160 return true 161 } 162 163 // retryOnError wraps the error in xfer.DoNotRetry if we should not retry the 164 // operation after this error. 165 func retryOnError(err error) error { 166 switch v := err.(type) { 167 case errcode.Errors: 168 if len(v) != 0 { 169 return retryOnError(v[0]) 170 } 171 case errcode.Error: 172 switch v.Code { 173 case errcode.ErrorCodeUnauthorized, errcode.ErrorCodeUnsupported, errcode.ErrorCodeDenied, errcode.ErrorCodeTooManyRequests, v2.ErrorCodeNameUnknown: 174 return xfer.DoNotRetry{Err: err} 175 } 176 case *url.Error: 177 switch v.Err { 178 case auth.ErrNoBasicAuthCredentials, auth.ErrNoToken: 179 return xfer.DoNotRetry{Err: v.Err} 180 } 181 return retryOnError(v.Err) 182 case *client.UnexpectedHTTPResponseError: 183 return xfer.DoNotRetry{Err: err} 184 case error: 185 if err == distribution.ErrBlobUnknown { 186 return xfer.DoNotRetry{Err: err} 187 } 188 if strings.Contains(err.Error(), strings.ToLower(syscall.ENOSPC.Error())) { 189 return xfer.DoNotRetry{Err: err} 190 } 191 } 192 // let's be nice and fallback if the error is a completely 193 // unexpected one. 194 // If new errors have to be handled in some way, please 195 // add them to the switch above. 196 return err 197 } 198 199 type invalidManifestClassError struct { 200 mediaType string 201 class string 202 } 203 204 func (e invalidManifestClassError) Error() string { 205 return fmt.Sprintf("Encountered remote %q(%s) when fetching", e.mediaType, e.class) 206 } 207 208 func (e invalidManifestClassError) InvalidParameter() {} 209 210 type invalidManifestFormatError struct{} 211 212 func (invalidManifestFormatError) Error() string { 213 return "unsupported manifest format" 214 } 215 216 func (invalidManifestFormatError) InvalidParameter() {} 217 218 type reservedNameError string 219 220 func (e reservedNameError) Error() string { 221 return "'" + string(e) + "' is a reserved name" 222 } 223 224 func (e reservedNameError) Forbidden() {}