1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00
moby--moby/api/server/httputils/errors.go
Stephen J Day ce22bc39dc api/server/httputils: service unavailable for disable swarm
When swarm-mode is disabled, we need to return an error indicating this.
406 was chosen for the "Not Acceptable" verbiage, but this code has
specific semantics in relation to the `Accept` header, which aren't
applicable here.

We now use a 503 for this case. While it is not a perfect match, it does
make it clear that the particular "service" (read: API endpoint) is not
available. The body of the message provides the user with enough
information to take action on it by enabling swarm-mode and ensuring the
service is available.

Signed-off-by: Stephen J Day <stephen.day@docker.com>
(cherry picked from commit 1d90d76048)
Signed-off-by: Victor Vieux <victorvieux@gmail.com>
2016-11-18 13:44:36 -08:00

101 lines
2.8 KiB
Go

package httputils
import (
"net/http"
"strings"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/versions"
"github.com/gorilla/mux"
"google.golang.org/grpc"
)
// httpStatusError is an interface
// that errors with custom status codes
// implement to tell the api layer
// which response status to set.
type httpStatusError interface {
HTTPErrorStatusCode() int
}
// inputValidationError is an interface
// that errors generated by invalid
// inputs can implement to tell the
// api layer to set a 400 status code
// in the response.
type inputValidationError interface {
IsValidationError() bool
}
// GetHTTPErrorStatusCode retrieves status code from error message
func GetHTTPErrorStatusCode(err error) int {
if err == nil {
logrus.WithFields(logrus.Fields{"error": err}).Error("unexpected HTTP error handling")
return http.StatusInternalServerError
}
var statusCode int
errMsg := err.Error()
switch e := err.(type) {
case httpStatusError:
statusCode = e.HTTPErrorStatusCode()
case inputValidationError:
statusCode = http.StatusBadRequest
default:
// FIXME: this is brittle and should not be necessary, but we still need to identify if
// there are errors falling back into this logic.
// If we need to differentiate between different possible error types,
// we should create appropriate error types that implement the httpStatusError interface.
errStr := strings.ToLower(errMsg)
for _, status := range []struct {
keyword string
code int
}{
{"not found", http.StatusNotFound},
{"no such", http.StatusNotFound},
{"bad parameter", http.StatusBadRequest},
{"no command", http.StatusBadRequest},
{"conflict", http.StatusConflict},
{"impossible", http.StatusNotAcceptable},
{"wrong login/password", http.StatusUnauthorized},
{"unauthorized", http.StatusUnauthorized},
{"hasn't been activated", http.StatusForbidden},
{"this node", http.StatusServiceUnavailable},
} {
if strings.Contains(errStr, status.keyword) {
statusCode = status.code
break
}
}
}
if statusCode == 0 {
statusCode = http.StatusInternalServerError
}
return statusCode
}
func apiVersionSupportsJSONErrors(version string) bool {
const firstAPIVersionWithJSONErrors = "1.23"
return version == "" || versions.GreaterThan(version, firstAPIVersionWithJSONErrors)
}
// MakeErrorHandler makes an HTTP handler that decodes a Docker error and
// returns it in the response.
func MakeErrorHandler(err error) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
statusCode := GetHTTPErrorStatusCode(err)
vars := mux.Vars(r)
if apiVersionSupportsJSONErrors(vars["version"]) {
response := &types.ErrorResponse{
Message: err.Error(),
}
WriteJSON(w, statusCode, response)
} else {
http.Error(w, grpc.ErrorDesc(err), statusCode)
}
}
}