2a9c987e |
package errdefs // import "github.com/docker/docker/errdefs" |
a793564b |
import ( |
ebcb7d6b |
"fmt" |
a793564b |
"net/http"
|
7b2d5556 |
containerderrors "github.com/containerd/containerd/errdefs" |
cdcea6f4 |
"github.com/docker/distribution/registry/api/errcode" |
1009e6a4 |
"github.com/sirupsen/logrus" |
f257f77c |
"google.golang.org/grpc/codes" |
72b0b038 |
"google.golang.org/grpc/status" |
a793564b |
)
|
ac9c47e2 |
// GetHTTPErrorStatusCode retrieves status code from error message. |
91e5bb95 |
func GetHTTPErrorStatusCode(err error) int {
if err == nil {
logrus.WithFields(logrus.Fields{"error": err}).Error("unexpected HTTP error handling")
return http.StatusInternalServerError |
a793564b |
}
var statusCode int
|
ebcb7d6b |
// Stop right there
// Are you sure you should be adding a new error class here? Do one of the existing ones work?
// Note that the below functions are already checking the error causal chain for matches.
switch { |
2a9c987e |
case IsNotFound(err): |
ebcb7d6b |
statusCode = http.StatusNotFound |
2a9c987e |
case IsInvalidParameter(err): |
a793564b |
statusCode = http.StatusBadRequest |
7d4b7883 |
case IsConflict(err): |
ebcb7d6b |
statusCode = http.StatusConflict |
2a9c987e |
case IsUnauthorized(err): |
ebcb7d6b |
statusCode = http.StatusUnauthorized |
2a9c987e |
case IsUnavailable(err): |
ebcb7d6b |
statusCode = http.StatusServiceUnavailable |
2a9c987e |
case IsForbidden(err): |
ebcb7d6b |
statusCode = http.StatusForbidden |
2a9c987e |
case IsNotModified(err): |
ebcb7d6b |
statusCode = http.StatusNotModified |
2a9c987e |
case IsNotImplemented(err): |
ebcb7d6b |
statusCode = http.StatusNotImplemented |
2a9c987e |
case IsSystem(err) || IsUnknown(err) || IsDataLoss(err) || IsDeadline(err) || IsCancelled(err): |
ebcb7d6b |
statusCode = http.StatusInternalServerError |
a793564b |
default: |
f257f77c |
statusCode = statusCodeFromGRPCError(err)
if statusCode != http.StatusInternalServerError {
return statusCode
} |
7b2d5556 |
statusCode = statusCodeFromContainerdError(err)
if statusCode != http.StatusInternalServerError {
return statusCode
} |
cdcea6f4 |
statusCode = statusCodeFromDistributionError(err)
if statusCode != http.StatusInternalServerError {
return statusCode
} |
ebcb7d6b |
if e, ok := err.(causer); ok {
return GetHTTPErrorStatusCode(e.Cause()) |
a793564b |
} |
ebcb7d6b |
logrus.WithFields(logrus.Fields{
"module": "api",
"error_type": fmt.Sprintf("%T", err),
}).Debugf("FIXME: Got an API for which error does not match any expected type!!!: %+v", err) |
a793564b |
}
if statusCode == 0 {
statusCode = http.StatusInternalServerError
}
|
91e5bb95 |
return statusCode
}
|
2a9c987e |
// FromStatusCode creates an errdef error, based on the provided HTTP status-code |
1af30c50 |
func FromStatusCode(err error, statusCode int) error {
if err == nil {
return err
}
switch statusCode {
case http.StatusNotFound: |
2a9c987e |
err = NotFound(err) |
1af30c50 |
case http.StatusBadRequest: |
2a9c987e |
err = InvalidParameter(err) |
1af30c50 |
case http.StatusConflict: |
2a9c987e |
err = Conflict(err) |
1af30c50 |
case http.StatusUnauthorized: |
2a9c987e |
err = Unauthorized(err) |
1af30c50 |
case http.StatusServiceUnavailable: |
2a9c987e |
err = Unavailable(err) |
1af30c50 |
case http.StatusForbidden: |
2a9c987e |
err = Forbidden(err) |
1af30c50 |
case http.StatusNotModified: |
2a9c987e |
err = NotModified(err) |
1af30c50 |
case http.StatusNotImplemented: |
2a9c987e |
err = NotImplemented(err) |
1af30c50 |
case http.StatusInternalServerError: |
2a9c987e |
if !IsSystem(err) && !IsUnknown(err) && !IsDataLoss(err) && !IsDeadline(err) && !IsCancelled(err) {
err = System(err) |
1af30c50 |
}
default:
logrus.WithFields(logrus.Fields{
"module": "api",
"status_code": fmt.Sprintf("%d", statusCode),
}).Debugf("FIXME: Got an status-code for which error does not match any expected type!!!: %d", statusCode)
switch {
case statusCode >= 200 && statusCode < 400:
// it's a client error
case statusCode >= 400 && statusCode < 500: |
2a9c987e |
err = InvalidParameter(err) |
1af30c50 |
case statusCode >= 500 && statusCode < 600: |
2a9c987e |
err = System(err) |
1af30c50 |
default: |
2a9c987e |
err = Unknown(err) |
1af30c50 |
}
}
return err
}
|
f257f77c |
// statusCodeFromGRPCError returns status code according to gRPC error
func statusCodeFromGRPCError(err error) int { |
72b0b038 |
switch status.Code(err) { |
f257f77c |
case codes.InvalidArgument: // code 3
return http.StatusBadRequest
case codes.NotFound: // code 5
return http.StatusNotFound
case codes.AlreadyExists: // code 6
return http.StatusConflict
case codes.PermissionDenied: // code 7
return http.StatusForbidden
case codes.FailedPrecondition: // code 9
return http.StatusBadRequest
case codes.Unauthenticated: // code 16
return http.StatusUnauthorized
case codes.OutOfRange: // code 11
return http.StatusBadRequest
case codes.Unimplemented: // code 12
return http.StatusNotImplemented
case codes.Unavailable: // code 14
return http.StatusServiceUnavailable
default: |
ebcb7d6b |
if e, ok := err.(causer); ok {
return statusCodeFromGRPCError(e.Cause())
} |
f257f77c |
// codes.Canceled(1)
// codes.Unknown(2)
// codes.DeadlineExceeded(4)
// codes.ResourceExhausted(8)
// codes.Aborted(10)
// codes.Internal(13)
// codes.DataLoss(15)
return http.StatusInternalServerError
}
} |
cdcea6f4 |
// statusCodeFromDistributionError returns status code according to registry errcode
// code is loosely based on errcode.ServeJSON() in docker/distribution
func statusCodeFromDistributionError(err error) int {
switch errs := err.(type) {
case errcode.Errors:
if len(errs) < 1 {
return http.StatusInternalServerError
}
if _, ok := errs[0].(errcode.ErrorCoder); ok {
return statusCodeFromDistributionError(errs[0])
}
case errcode.ErrorCoder:
return errs.ErrorCode().Descriptor().HTTPStatusCode
default:
if e, ok := err.(causer); ok {
return statusCodeFromDistributionError(e.Cause())
}
}
return http.StatusInternalServerError
} |
7b2d5556 |
// statusCodeFromContainerdError returns status code for containerd errors when |
8ab5e2a0 |
// consumed directly (not through gRPC) |
7b2d5556 |
func statusCodeFromContainerdError(err error) int {
switch {
case containerderrors.IsInvalidArgument(err):
return http.StatusBadRequest
case containerderrors.IsNotFound(err):
return http.StatusNotFound
case containerderrors.IsAlreadyExists(err):
return http.StatusConflict
case containerderrors.IsFailedPrecondition(err):
return http.StatusPreconditionFailed
case containerderrors.IsUnavailable(err):
return http.StatusServiceUnavailable
case containerderrors.IsNotImplemented(err):
return http.StatusNotImplemented
default:
return http.StatusInternalServerError
}
} |