Update generic errors with the new http package codes
All of these errors are now part of the standard HTTP method. Formalize those into our error types and remove duplication and unclear separation.
This commit is contained in:
parent
6000712803
commit
d3be1ac92e
@ -39,7 +39,6 @@ import (
|
|||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
@ -484,7 +483,7 @@ func (s *Server) getContainerLogs(request *restful.Request, response *restful.Re
|
|||||||
}
|
}
|
||||||
logOptions.TypeMeta = metav1.TypeMeta{}
|
logOptions.TypeMeta = metav1.TypeMeta{}
|
||||||
if errs := validation.ValidatePodLogOptions(logOptions); len(errs) > 0 {
|
if errs := validation.ValidatePodLogOptions(logOptions); len(errs) > 0 {
|
||||||
response.WriteError(apierrs.StatusUnprocessableEntity, fmt.Errorf(`{"message": "Invalid request."}`))
|
response.WriteError(http.StatusUnprocessableEntity, fmt.Errorf(`{"message": "Invalid request."}`))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,6 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/httpstream"
|
"k8s.io/apimachinery/pkg/util/httpstream"
|
||||||
@ -1065,7 +1064,7 @@ func TestContainerLogsWithInvalidTail(t *testing.T) {
|
|||||||
t.Errorf("Got error GETing: %v", err)
|
t.Errorf("Got error GETing: %v", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if resp.StatusCode != apierrs.StatusUnprocessableEntity {
|
if resp.StatusCode != http.StatusUnprocessableEntity {
|
||||||
t.Errorf("Unexpected non-error reading container logs: %#v", resp)
|
t.Errorf("Unexpected non-error reading container logs: %#v", resp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,16 +28,6 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HTTP Status codes not in the golang http package.
|
|
||||||
const (
|
|
||||||
StatusUnprocessableEntity = 422
|
|
||||||
StatusTooManyRequests = 429
|
|
||||||
// StatusServerTimeout is an indication that a transient server error has
|
|
||||||
// occurred and the client *should* retry, with an optional Retry-After
|
|
||||||
// header to specify the back off window.
|
|
||||||
StatusServerTimeout = 504
|
|
||||||
)
|
|
||||||
|
|
||||||
// StatusError is an error intended for consumption by a REST API server; it can also be
|
// StatusError is an error intended for consumption by a REST API server; it can also be
|
||||||
// reconstructed by clients from a REST response. Public to allow easy type switches.
|
// reconstructed by clients from a REST response. Public to allow easy type switches.
|
||||||
type StatusError struct {
|
type StatusError struct {
|
||||||
@ -189,7 +179,7 @@ func NewInvalid(qualifiedKind schema.GroupKind, name string, errs field.ErrorLis
|
|||||||
}
|
}
|
||||||
return &StatusError{metav1.Status{
|
return &StatusError{metav1.Status{
|
||||||
Status: metav1.StatusFailure,
|
Status: metav1.StatusFailure,
|
||||||
Code: StatusUnprocessableEntity, // RFC 4918: StatusUnprocessableEntity
|
Code: http.StatusUnprocessableEntity,
|
||||||
Reason: metav1.StatusReasonInvalid,
|
Reason: metav1.StatusReasonInvalid,
|
||||||
Details: &metav1.StatusDetails{
|
Details: &metav1.StatusDetails{
|
||||||
Group: qualifiedKind.Group,
|
Group: qualifiedKind.Group,
|
||||||
@ -211,6 +201,21 @@ func NewBadRequest(reason string) *StatusError {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewTooManyRequests creates an error that indicates that the client must try again later because
|
||||||
|
// the specified endpoint is not accepting requests. More specific details should be provided
|
||||||
|
// if client should know why the failure was limited4.
|
||||||
|
func NewTooManyRequests(message string, retryAfterSeconds int) *StatusError {
|
||||||
|
return &StatusError{metav1.Status{
|
||||||
|
Status: metav1.StatusFailure,
|
||||||
|
Code: http.StatusTooManyRequests,
|
||||||
|
Reason: metav1.StatusReasonTooManyRequests,
|
||||||
|
Message: message,
|
||||||
|
Details: &metav1.StatusDetails{
|
||||||
|
RetryAfterSeconds: int32(retryAfterSeconds),
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
// NewServiceUnavailable creates an error that indicates that the requested service is unavailable.
|
// NewServiceUnavailable creates an error that indicates that the requested service is unavailable.
|
||||||
func NewServiceUnavailable(reason string) *StatusError {
|
func NewServiceUnavailable(reason string) *StatusError {
|
||||||
return &StatusError{metav1.Status{
|
return &StatusError{metav1.Status{
|
||||||
@ -276,7 +281,7 @@ func NewInternalError(err error) *StatusError {
|
|||||||
func NewTimeoutError(message string, retryAfterSeconds int) *StatusError {
|
func NewTimeoutError(message string, retryAfterSeconds int) *StatusError {
|
||||||
return &StatusError{metav1.Status{
|
return &StatusError{metav1.Status{
|
||||||
Status: metav1.StatusFailure,
|
Status: metav1.StatusFailure,
|
||||||
Code: StatusServerTimeout,
|
Code: http.StatusGatewayTimeout,
|
||||||
Reason: metav1.StatusReasonTimeout,
|
Reason: metav1.StatusReasonTimeout,
|
||||||
Message: fmt.Sprintf("Timeout: %s", message),
|
Message: fmt.Sprintf("Timeout: %s", message),
|
||||||
Details: &metav1.StatusDetails{
|
Details: &metav1.StatusDetails{
|
||||||
@ -313,14 +318,14 @@ func NewGenericServerResponse(code int, verb string, qualifiedResource schema.Gr
|
|||||||
case http.StatusMethodNotAllowed:
|
case http.StatusMethodNotAllowed:
|
||||||
reason = metav1.StatusReasonMethodNotAllowed
|
reason = metav1.StatusReasonMethodNotAllowed
|
||||||
message = "the server does not allow this method on the requested resource"
|
message = "the server does not allow this method on the requested resource"
|
||||||
case StatusUnprocessableEntity:
|
case http.StatusUnprocessableEntity:
|
||||||
reason = metav1.StatusReasonInvalid
|
reason = metav1.StatusReasonInvalid
|
||||||
message = "the server rejected our request due to an error in our request"
|
message = "the server rejected our request due to an error in our request"
|
||||||
case StatusServerTimeout:
|
case http.StatusGatewayTimeout:
|
||||||
reason = metav1.StatusReasonServerTimeout
|
|
||||||
message = "the server cannot complete the requested operation at this time, try again later"
|
|
||||||
case StatusTooManyRequests:
|
|
||||||
reason = metav1.StatusReasonTimeout
|
reason = metav1.StatusReasonTimeout
|
||||||
|
message = "the server was unable to return a response in the time allotted, but may still be processing the request"
|
||||||
|
case http.StatusTooManyRequests:
|
||||||
|
reason = metav1.StatusReasonTooManyRequests
|
||||||
message = "the server has received too many requests and has asked us to try again later"
|
message = "the server has received too many requests and has asked us to try again later"
|
||||||
default:
|
default:
|
||||||
if code >= 500 {
|
if code >= 500 {
|
||||||
@ -423,11 +428,13 @@ func IsInternalError(err error) bool {
|
|||||||
|
|
||||||
// IsTooManyRequests determines if err is an error which indicates that there are too many requests
|
// IsTooManyRequests determines if err is an error which indicates that there are too many requests
|
||||||
// that the server cannot handle.
|
// that the server cannot handle.
|
||||||
// TODO: update IsTooManyRequests() when the TooManyRequests(429) error returned from the API server has a non-empty Reason field
|
|
||||||
func IsTooManyRequests(err error) bool {
|
func IsTooManyRequests(err error) bool {
|
||||||
|
if reasonForError(err) == metav1.StatusReasonTooManyRequests {
|
||||||
|
return true
|
||||||
|
}
|
||||||
switch t := err.(type) {
|
switch t := err.(type) {
|
||||||
case APIStatus:
|
case APIStatus:
|
||||||
return t.Status().Code == StatusTooManyRequests
|
return t.Status().Code == http.StatusTooManyRequests
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -586,6 +586,15 @@ const (
|
|||||||
// Status code 504
|
// Status code 504
|
||||||
StatusReasonTimeout StatusReason = "Timeout"
|
StatusReasonTimeout StatusReason = "Timeout"
|
||||||
|
|
||||||
|
// StatusReasonTooManyRequests means the server experienced too many requests within a
|
||||||
|
// given window and that the client must wait to perform the action again. A client may
|
||||||
|
// always retry the request that led to this error, although the client should wait at least
|
||||||
|
// the number of seconds specified by the retryAfterSeconds field.
|
||||||
|
// Details (optional):
|
||||||
|
// "retryAfterSeconds" int32 - the number of seconds before the operation should be retried
|
||||||
|
// Status code 429
|
||||||
|
StatusReasonTooManyRequests StatusReason = "TooManyRequests"
|
||||||
|
|
||||||
// StatusReasonBadRequest means that the request itself was invalid, because the request
|
// StatusReasonBadRequest means that the request itself was invalid, because the request
|
||||||
// doesn't make any sense, for example deleting a read-only object. This is different than
|
// doesn't make any sense, for example deleting a read-only object. This is different than
|
||||||
// StatusReasonInvalid above which indicates that the API call could possibly succeed, but the
|
// StatusReasonInvalid above which indicates that the API call could possibly succeed, but the
|
||||||
|
@ -3834,7 +3834,7 @@ func TestCreateTimeout(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
itemOut := expectApiStatus(t, "POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/foo?timeout=4ms", data, apierrs.StatusServerTimeout)
|
itemOut := expectApiStatus(t, "POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/foo?timeout=4ms", data, http.StatusGatewayTimeout)
|
||||||
if itemOut.Status != metav1.StatusFailure || itemOut.Reason != metav1.StatusReasonTimeout {
|
if itemOut.Status != metav1.StatusFailure || itemOut.Reason != metav1.StatusReasonTimeout {
|
||||||
t.Errorf("Unexpected status %#v", itemOut)
|
t.Errorf("Unexpected status %#v", itemOut)
|
||||||
}
|
}
|
||||||
|
@ -509,7 +509,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
|||||||
// http.StatusUnsupportedMediaType, http.StatusNotAcceptable,
|
// http.StatusUnsupportedMediaType, http.StatusNotAcceptable,
|
||||||
// http.StatusBadRequest, http.StatusUnauthorized, http.StatusForbidden,
|
// http.StatusBadRequest, http.StatusUnauthorized, http.StatusForbidden,
|
||||||
// http.StatusRequestTimeout, http.StatusConflict, http.StatusPreconditionFailed,
|
// http.StatusRequestTimeout, http.StatusConflict, http.StatusPreconditionFailed,
|
||||||
// 422 (StatusUnprocessableEntity), http.StatusInternalServerError,
|
// http.StatusUnprocessableEntity, http.StatusInternalServerError,
|
||||||
// http.StatusServiceUnavailable
|
// http.StatusServiceUnavailable
|
||||||
// and api error codes
|
// and api error codes
|
||||||
// Note that if we specify a versioned Status object here, we may need to
|
// Note that if we specify a versioned Status object here, we may need to
|
||||||
|
@ -123,5 +123,5 @@ func WithMaxInFlightLimit(
|
|||||||
func tooManyRequests(req *http.Request, w http.ResponseWriter) {
|
func tooManyRequests(req *http.Request, w http.ResponseWriter) {
|
||||||
// Return a 429 status indicating "Too Many Requests"
|
// Return a 429 status indicating "Too Many Requests"
|
||||||
w.Header().Set("Retry-After", retryAfter)
|
w.Header().Set("Retry-After", retryAfter)
|
||||||
http.Error(w, "Too many requests, please try again later.", errors.StatusTooManyRequests)
|
http.Error(w, "Too many requests, please try again later.", http.StatusTooManyRequests)
|
||||||
}
|
}
|
||||||
|
@ -889,7 +889,7 @@ func isTextResponse(resp *http.Response) bool {
|
|||||||
func checkWait(resp *http.Response) (int, bool) {
|
func checkWait(resp *http.Response) (int, bool) {
|
||||||
switch r := resp.StatusCode; {
|
switch r := resp.StatusCode; {
|
||||||
// any 500 error code and 429 can trigger a wait
|
// any 500 error code and 429 can trigger a wait
|
||||||
case r == errors.StatusTooManyRequests, r >= 500:
|
case r == http.StatusTooManyRequests, r >= 500:
|
||||||
default:
|
default:
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
@ -1130,7 +1130,7 @@ func TestCheckRetryClosesBody(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set("Retry-After", "1")
|
w.Header().Set("Retry-After", "1")
|
||||||
http.Error(w, "Too many requests, please try again later.", apierrors.StatusTooManyRequests)
|
http.Error(w, "Too many requests, please try again later.", http.StatusTooManyRequests)
|
||||||
}))
|
}))
|
||||||
defer testServer.Close()
|
defer testServer.Close()
|
||||||
|
|
||||||
@ -1204,7 +1204,7 @@ func TestCheckRetryHandles429And5xx(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set("Retry-After", "0")
|
w.Header().Set("Retry-After", "0")
|
||||||
w.WriteHeader([]int{apierrors.StatusTooManyRequests, 500, 501, 504}[count])
|
w.WriteHeader([]int{http.StatusTooManyRequests, 500, 501, 504}[count])
|
||||||
count++
|
count++
|
||||||
}))
|
}))
|
||||||
defer testServer.Close()
|
defer testServer.Close()
|
||||||
@ -1234,7 +1234,7 @@ func BenchmarkCheckRetryClosesBody(b *testing.B) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set("Retry-After", "0")
|
w.Header().Set("Retry-After", "0")
|
||||||
w.WriteHeader(apierrors.StatusTooManyRequests)
|
w.WriteHeader(http.StatusTooManyRequests)
|
||||||
}))
|
}))
|
||||||
defer testServer.Close()
|
defer testServer.Close()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user