mirror of
https://github.com/moby/moby.git
synced 2026-08-03 22:51:03 +00:00
These comments were added to enforce using the correct import path for
our packages ("github.com/docker/docker", not "github.com/moby/moby").
However, when working in go module mode (not GOPATH / vendor), they have
no effect, so their impact is limited.
Remove these imports in preparation of migrating our code to become an
actual go module.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
125 lines
3.8 KiB
Go
125 lines
3.8 KiB
Go
package httpstatus
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
cerrdefs "github.com/containerd/errdefs"
|
|
"github.com/containerd/log"
|
|
"github.com/docker/distribution/registry/api/errcode"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
)
|
|
|
|
// FromError retrieves status code from error message.
|
|
func FromError(err error) int {
|
|
if err == nil {
|
|
log.G(context.TODO()).WithError(err).Error("unexpected HTTP error handling")
|
|
return http.StatusInternalServerError
|
|
}
|
|
|
|
// Resolve the error to ensure status is chosen from the first outermost error
|
|
rerr := cerrdefs.Resolve(err)
|
|
|
|
// Note that the below functions are already checking the error causal chain for matches.
|
|
// Only check errors from the errdefs package, no new error type checking may be added
|
|
switch {
|
|
case cerrdefs.IsNotFound(rerr):
|
|
return http.StatusNotFound
|
|
case cerrdefs.IsInvalidArgument(rerr):
|
|
return http.StatusBadRequest
|
|
case cerrdefs.IsConflict(rerr):
|
|
return http.StatusConflict
|
|
case cerrdefs.IsUnauthorized(rerr):
|
|
return http.StatusUnauthorized
|
|
case cerrdefs.IsUnavailable(rerr):
|
|
return http.StatusServiceUnavailable
|
|
case cerrdefs.IsPermissionDenied(rerr):
|
|
return http.StatusForbidden
|
|
case cerrdefs.IsNotModified(rerr):
|
|
return http.StatusNotModified
|
|
case cerrdefs.IsNotImplemented(rerr):
|
|
return http.StatusNotImplemented
|
|
case cerrdefs.IsInternal(rerr) || cerrdefs.IsDataLoss(rerr) || cerrdefs.IsDeadlineExceeded(rerr) || cerrdefs.IsCanceled(rerr):
|
|
return http.StatusInternalServerError
|
|
default:
|
|
if statusCode := statusCodeFromGRPCError(err); statusCode != http.StatusInternalServerError {
|
|
return statusCode
|
|
}
|
|
if statusCode := statusCodeFromDistributionError(err); statusCode != http.StatusInternalServerError {
|
|
return statusCode
|
|
}
|
|
switch e := err.(type) {
|
|
case interface{ Unwrap() error }:
|
|
return FromError(e.Unwrap())
|
|
case interface{ Unwrap() []error }:
|
|
for _, ue := range e.Unwrap() {
|
|
if statusCode := FromError(ue); statusCode != http.StatusInternalServerError {
|
|
return statusCode
|
|
}
|
|
}
|
|
}
|
|
|
|
if !cerrdefs.IsUnknown(err) {
|
|
log.G(context.TODO()).WithFields(log.Fields{
|
|
"module": "api",
|
|
"error": err,
|
|
"error_type": fmt.Sprintf("%T", err),
|
|
}).Debug("FIXME: Got an API for which error does not match any expected type!!!")
|
|
}
|
|
|
|
return http.StatusInternalServerError
|
|
}
|
|
}
|
|
|
|
// statusCodeFromGRPCError returns status code according to gRPC error
|
|
func statusCodeFromGRPCError(err error) int {
|
|
switch status.Code(err) {
|
|
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:
|
|
// codes.Canceled(1)
|
|
// codes.Unknown(2)
|
|
// codes.DeadlineExceeded(4)
|
|
// codes.ResourceExhausted(8)
|
|
// codes.Aborted(10)
|
|
// codes.Internal(13)
|
|
// codes.DataLoss(15)
|
|
return http.StatusInternalServerError
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|
|
return http.StatusInternalServerError
|
|
}
|