summaryrefslogtreecommitdiff
path: root/distribution/errors.go
diff options
context:
space:
mode:
authorAaron Lehmann <aaron.lehmann@docker.com>2016-02-11 14:08:49 -0800
committerAaron Lehmann <aaron.lehmann@docker.com>2016-02-11 16:28:56 -0800
commit8f26fe4f59ce515c68440da1443ace4c96e89d4a (patch)
tree8ee7b932c6f1147fde4bbb5eb2d5f848ca3ac708 /distribution/errors.go
parent2a16099f5792d360be5e08d05c5742190eec03e3 (diff)
downloaddocker-8f26fe4f59ce515c68440da1443ace4c96e89d4a.tar.gz
Push/pull errors improvement and cleanup
Several improvements to error handling: - Introduce ImageConfigPullError type, wrapping errors related to downloading the image configuration blob in schema2. This allows for a more descriptive error message to be seen by the end user. - Change some logrus.Debugf calls that display errors to logrus.Errorf. Add log lines in the push/pull fallback cases to make sure the errors leading to the fallback are shown. - Move error-related types and functions which are only used by the distribution package out of the registry package. Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
Diffstat (limited to 'distribution/errors.go')
-rw-r--r--distribution/errors.go102
1 files changed, 102 insertions, 0 deletions
diff --git a/distribution/errors.go b/distribution/errors.go
new file mode 100644
index 0000000000..9f9dcf6978
--- /dev/null
+++ b/distribution/errors.go
@@ -0,0 +1,102 @@
+package distribution
+
+import (
+ "net/url"
+ "strings"
+ "syscall"
+
+ "github.com/docker/distribution/registry/api/errcode"
+ "github.com/docker/distribution/registry/api/v2"
+ "github.com/docker/distribution/registry/client"
+ "github.com/docker/docker/distribution/xfer"
+)
+
+// ErrNoSupport is an error type used for errors indicating that an operation
+// is not supported. It encapsulates a more specific error.
+type ErrNoSupport struct{ Err error }
+
+func (e ErrNoSupport) Error() string {
+ if e.Err == nil {
+ return "not supported"
+ }
+ return e.Err.Error()
+}
+
+// fallbackError wraps an error that can possibly allow fallback to a different
+// endpoint.
+type fallbackError struct {
+ // err is the error being wrapped.
+ err error
+ // confirmedV2 is set to true if it was confirmed that the registry
+ // supports the v2 protocol. This is used to limit fallbacks to the v1
+ // protocol.
+ confirmedV2 bool
+}
+
+// Error renders the FallbackError as a string.
+func (f fallbackError) Error() string {
+ return f.err.Error()
+}
+
+// shouldV2Fallback returns true if this error is a reason to fall back to v1.
+func shouldV2Fallback(err errcode.Error) bool {
+ switch err.Code {
+ case errcode.ErrorCodeUnauthorized, v2.ErrorCodeManifestUnknown, v2.ErrorCodeNameUnknown:
+ return true
+ }
+ return false
+}
+
+// continueOnError returns true if we should fallback to the next endpoint
+// as a result of this error.
+func continueOnError(err error) bool {
+ switch v := err.(type) {
+ case errcode.Errors:
+ if len(v) == 0 {
+ return true
+ }
+ return continueOnError(v[0])
+ case ErrNoSupport:
+ return continueOnError(v.Err)
+ case errcode.Error:
+ return shouldV2Fallback(v)
+ case *client.UnexpectedHTTPResponseError:
+ return true
+ case ImageConfigPullError:
+ return false
+ case error:
+ return !strings.Contains(err.Error(), strings.ToLower(syscall.ENOSPC.Error()))
+ }
+ // let's be nice and fallback if the error is a completely
+ // unexpected one.
+ // If new errors have to be handled in some way, please
+ // add them to the switch above.
+ return true
+}
+
+// retryOnError wraps the error in xfer.DoNotRetry if we should not retry the
+// operation after this error.
+func retryOnError(err error) error {
+ switch v := err.(type) {
+ case errcode.Errors:
+ return retryOnError(v[0])
+ case errcode.Error:
+ switch v.Code {
+ case errcode.ErrorCodeUnauthorized, errcode.ErrorCodeUnsupported, errcode.ErrorCodeDenied:
+ return xfer.DoNotRetry{Err: err}
+ }
+ case *url.Error:
+ return retryOnError(v.Err)
+ case *client.UnexpectedHTTPResponseError:
+ return xfer.DoNotRetry{Err: err}
+ case error:
+ if strings.Contains(err.Error(), strings.ToLower(syscall.ENOSPC.Error())) {
+ return xfer.DoNotRetry{Err: err}
+ }
+ }
+ // let's be nice and fallback if the error is a completely
+ // unexpected one.
+ // If new errors have to be handled in some way, please
+ // add them to the switch above.
+ return err
+}