github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+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 // continueOnError returns true if we should fallback to the next endpoint 116 // as a result of this error. 117 func continueOnError(err error, mirrorEndpoint bool) bool { 118 switch v := err.(type) { 119 case errcode.Errors: 120 if len(v) == 0 { 121 return true 122 } 123 return continueOnError(v[0], mirrorEndpoint) 124 case ErrNoSupport: 125 return continueOnError(v.Err, mirrorEndpoint) 126 case errcode.Error: 127 return mirrorEndpoint || shouldV2Fallback(v) 128 case *client.UnexpectedHTTPResponseError: 129 return true 130 case ImageConfigPullError: 131 // ImageConfigPullError only happens with v2 images, v1 fallback is 132 // unnecessary. 133 // Failures from a mirror endpoint should result in fallback to the 134 // canonical repo. 135 return mirrorEndpoint 136 case error: 137 return !strings.Contains(err.Error(), strings.ToLower(syscall.ESRCH.Error())) 138 } 139 // let's be nice and fallback if the error is a completely 140 // unexpected one. 141 // If new errors have to be handled in some way, please 142 // add them to the switch above. 143 return true 144 } 145 146 // retryOnError wraps the error in xfer.DoNotRetry if we should not retry the 147 // operation after this error. 148 func retryOnError(err error) error { 149 switch v := err.(type) { 150 case errcode.Errors: 151 if len(v) != 0 { 152 return retryOnError(v[0]) 153 } 154 case errcode.Error: 155 switch v.Code { 156 case errcode.ErrorCodeUnauthorized, errcode.ErrorCodeUnsupported, errcode.ErrorCodeDenied, errcode.ErrorCodeTooManyRequests, v2.ErrorCodeNameUnknown: 157 return xfer.DoNotRetry{Err: err} 158 } 159 case *url.Error: 160 switch v.Err { 161 case auth.ErrNoBasicAuthCredentials, auth.ErrNoToken: 162 return xfer.DoNotRetry{Err: v.Err} 163 } 164 return retryOnError(v.Err) 165 case *client.UnexpectedHTTPResponseError: 166 return xfer.DoNotRetry{Err: err} 167 case error: 168 if err == distribution.ErrBlobUnknown { 169 return xfer.DoNotRetry{Err: err} 170 } 171 if strings.Contains(err.Error(), strings.ToLower(syscall.ENOSPC.Error())) { 172 return xfer.DoNotRetry{Err: err} 173 } 174 } 175 // let's be nice and fallback if the error is a completely 176 // unexpected one. 177 // If new errors have to be handled in some way, please 178 // add them to the switch above. 179 return err 180 } 181 182 type invalidManifestClassError struct { 183 mediaType string 184 class string 185 } 186 187 func (e invalidManifestClassError) Error() string { 188 return fmt.Sprintf("Encountered remote %q(%s) when fetching", e.mediaType, e.class) 189 } 190 191 func (e invalidManifestClassError) InvalidParameter() {} 192 193 type invalidManifestFormatError struct{} 194 195 func (invalidManifestFormatError) Error() string { 196 return "unsupported manifest format" 197 } 198 199 func (invalidManifestFormatError) InvalidParameter() {} 200 201 type reservedNameError string 202 203 func (e reservedNameError) Error() string { 204 return "'" + string(e) + "' is a reserved name" 205 } 206 207 func (e reservedNameError) Forbidden() {}