summaryrefslogtreecommitdiff
path: root/distribution/errors.go
diff options
context:
space:
mode:
authorBrian Goff <cpuguy83@gmail.com>2017-07-19 10:20:13 -0400
committerBrian Goff <cpuguy83@gmail.com>2017-08-15 16:01:11 -0400
commitebcb7d6b406fe50ea9a237c73004d75884184c33 (patch)
treec87316ace5b2ddc5efabeb311d47e98fd4113ce9 /distribution/errors.go
parentb6498340b2baa6596553b2b56b43990a365a7b6a (diff)
downloaddocker-ebcb7d6b406fe50ea9a237c73004d75884184c33.tar.gz
Remove string checking in API error handling
Use strongly typed errors to set HTTP status codes. Error interfaces are defined in the api/errors package and errors returned from controllers are checked against these interfaces. Errors can be wraeped in a pkg/errors.Causer, as long as somewhere in the line of causes one of the interfaces is implemented. The special error interfaces take precedence over Causer, meaning if both Causer and one of the new error interfaces are implemented, the Causer is not traversed. Signed-off-by: Brian Goff <cpuguy83@gmail.com>
Diffstat (limited to 'distribution/errors.go')
-rw-r--r--distribution/errors.go84
1 files changed, 70 insertions, 14 deletions
diff --git a/distribution/errors.go b/distribution/errors.go
index 0e4942d6a6..dd6ff0a9aa 100644
--- a/distribution/errors.go
+++ b/distribution/errors.go
@@ -1,6 +1,7 @@
package distribution
import (
+ "fmt"
"net/url"
"strings"
"syscall"
@@ -12,7 +13,6 @@ import (
"github.com/docker/distribution/registry/client"
"github.com/docker/distribution/registry/client/auth"
"github.com/docker/docker/distribution/xfer"
- "github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -60,6 +60,45 @@ func shouldV2Fallback(err errcode.Error) bool {
return false
}
+type notFoundError struct {
+ cause errcode.Error
+ ref reference.Named
+}
+
+func (e notFoundError) Error() string {
+ switch e.cause.Code {
+ case errcode.ErrorCodeDenied:
+ // ErrorCodeDenied is used when access to the repository was denied
+ return fmt.Sprintf("pull access denied for %s, repository does not exist or may require 'docker login'", reference.FamiliarName(e.ref))
+ case v2.ErrorCodeManifestUnknown:
+ return fmt.Sprintf("manifest for %s not found", reference.FamiliarString(e.ref))
+ case v2.ErrorCodeNameUnknown:
+ return fmt.Sprintf("repository %s not found", reference.FamiliarName(e.ref))
+ }
+ // Shouldn't get here, but this is better than returning an empty string
+ return e.cause.Message
+}
+
+func (e notFoundError) NotFound() {}
+
+func (e notFoundError) Cause() error {
+ return e.cause
+}
+
+type unknownError struct {
+ cause error
+}
+
+func (e unknownError) Error() string {
+ return e.cause.Error()
+}
+
+func (e unknownError) Cause() error {
+ return e.cause
+}
+
+func (e unknownError) Unknown() {}
+
// TranslatePullError is used to convert an error from a registry pull
// operation to an error representing the entire pull operation. Any error
// information which is not used by the returned error gets output to
@@ -74,25 +113,15 @@ func TranslatePullError(err error, ref reference.Named) error {
return TranslatePullError(v[0], ref)
}
case errcode.Error:
- var newErr error
switch v.Code {
- case errcode.ErrorCodeDenied:
- // ErrorCodeDenied is used when access to the repository was denied
- newErr = errors.Errorf("pull access denied for %s, repository does not exist or may require 'docker login'", reference.FamiliarName(ref))
- case v2.ErrorCodeManifestUnknown:
- newErr = errors.Errorf("manifest for %s not found", reference.FamiliarString(ref))
- case v2.ErrorCodeNameUnknown:
- newErr = errors.Errorf("repository %s not found", reference.FamiliarName(ref))
- }
- if newErr != nil {
- logrus.Infof("Translating %q to %q", err, newErr)
- return newErr
+ case errcode.ErrorCodeDenied, v2.ErrorCodeManifestUnknown, v2.ErrorCodeNameUnknown:
+ return notFoundError{v, ref}
}
case xfer.DoNotRetry:
return TranslatePullError(v.Err, ref)
}
- return err
+ return unknownError{err}
}
// continueOnError returns true if we should fallback to the next endpoint
@@ -157,3 +186,30 @@ func retryOnError(err error) error {
// add them to the switch above.
return err
}
+
+type invalidManifestClassError struct {
+ mediaType string
+ class string
+}
+
+func (e invalidManifestClassError) Error() string {
+ return fmt.Sprintf("Encountered remote %q(%s) when fetching", e.mediaType, e.class)
+}
+
+func (e invalidManifestClassError) InvalidParameter() {}
+
+type invalidManifestFormatError struct{}
+
+func (invalidManifestFormatError) Error() string {
+ return "unsupported manifest format"
+}
+
+func (invalidManifestFormatError) InvalidParameter() {}
+
+type reservedNameError string
+
+func (e reservedNameError) Error() string {
+ return "'" + string(e) + "' is a reserved name"
+}
+
+func (e reservedNameError) Forbidden() {}