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