mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 10:18:13 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			429 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			429 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| Copyright 2014 The Kubernetes Authors All rights reserved.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| package errors
 | |
| 
 | |
| import (
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"net/http"
 | |
| 	"strings"
 | |
| 
 | |
| 	"k8s.io/kubernetes/pkg/api"
 | |
| 	"k8s.io/kubernetes/pkg/runtime"
 | |
| 	"k8s.io/kubernetes/pkg/util/errors"
 | |
| 	"k8s.io/kubernetes/pkg/util/fielderrors"
 | |
| )
 | |
| 
 | |
| // HTTP Status codes not in the golang http package.
 | |
| const (
 | |
| 	StatusUnprocessableEntity = 422
 | |
| 	StatusTooManyRequests     = 429
 | |
| 	// HTTP recommendations are for servers to define 5xx error codes
 | |
| 	// for scenarios not covered by behavior. In this case, ServerTimeout
 | |
| 	// 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
 | |
| // reconstructed by clients from a REST response. Public to allow easy type switches.
 | |
| type StatusError struct {
 | |
| 	ErrStatus api.Status
 | |
| }
 | |
| 
 | |
| var _ error = &StatusError{}
 | |
| 
 | |
| // Error implements the Error interface.
 | |
| func (e *StatusError) Error() string {
 | |
| 	return e.ErrStatus.Message
 | |
| }
 | |
| 
 | |
| // Status allows access to e's status without having to know the detailed workings
 | |
| // of StatusError. Used by pkg/apiserver.
 | |
| func (e *StatusError) Status() api.Status {
 | |
| 	return e.ErrStatus
 | |
| }
 | |
| 
 | |
| // DebugError reports extended info about the error to debug output.
 | |
| func (e *StatusError) DebugError() (string, []interface{}) {
 | |
| 	if out, err := json.MarshalIndent(e.ErrStatus, "", "  "); err == nil {
 | |
| 		return "server response object: %s", []interface{}{string(out)}
 | |
| 	}
 | |
| 	return "server response object: %#v", []interface{}{e.ErrStatus}
 | |
| }
 | |
| 
 | |
| // UnexpectedObjectError can be returned by FromObject if it's passed a non-status object.
 | |
| type UnexpectedObjectError struct {
 | |
| 	Object runtime.Object
 | |
| }
 | |
| 
 | |
| // Error returns an error message describing 'u'.
 | |
| func (u *UnexpectedObjectError) Error() string {
 | |
| 	return fmt.Sprintf("unexpected object: %v", u.Object)
 | |
| }
 | |
| 
 | |
| // FromObject generates an StatusError from an api.Status, if that is the type of obj; otherwise,
 | |
| // returns an UnexpecteObjectError.
 | |
| func FromObject(obj runtime.Object) error {
 | |
| 	switch t := obj.(type) {
 | |
| 	case *api.Status:
 | |
| 		return &StatusError{*t}
 | |
| 	}
 | |
| 	return &UnexpectedObjectError{obj}
 | |
| }
 | |
| 
 | |
| // NewNotFound returns a new error which indicates that the resource of the kind and the name was not found.
 | |
| func NewNotFound(kind, name string) error {
 | |
| 	return &StatusError{api.Status{
 | |
| 		Status: api.StatusFailure,
 | |
| 		Code:   http.StatusNotFound,
 | |
| 		Reason: api.StatusReasonNotFound,
 | |
| 		Details: &api.StatusDetails{
 | |
| 			Kind: kind,
 | |
| 			Name: name,
 | |
| 		},
 | |
| 		Message: fmt.Sprintf("%s %q not found", kind, name),
 | |
| 	}}
 | |
| }
 | |
| 
 | |
| // NewAlreadyExists returns an error indicating the item requested exists by that identifier.
 | |
| func NewAlreadyExists(kind, name string) error {
 | |
| 	return &StatusError{api.Status{
 | |
| 		Status: api.StatusFailure,
 | |
| 		Code:   http.StatusConflict,
 | |
| 		Reason: api.StatusReasonAlreadyExists,
 | |
| 		Details: &api.StatusDetails{
 | |
| 			Kind: kind,
 | |
| 			Name: name,
 | |
| 		},
 | |
| 		Message: fmt.Sprintf("%s %q already exists", kind, name),
 | |
| 	}}
 | |
| }
 | |
| 
 | |
| // NewUnauthorized returns an error indicating the client is not authorized to perform the requested
 | |
| // action.
 | |
| func NewUnauthorized(reason string) error {
 | |
| 	message := reason
 | |
| 	if len(message) == 0 {
 | |
| 		message = "not authorized"
 | |
| 	}
 | |
| 	return &StatusError{api.Status{
 | |
| 		Status:  api.StatusFailure,
 | |
| 		Code:    http.StatusUnauthorized,
 | |
| 		Reason:  api.StatusReasonUnauthorized,
 | |
| 		Message: message,
 | |
| 	}}
 | |
| }
 | |
| 
 | |
| // NewForbidden returns an error indicating the requested action was forbidden
 | |
| func NewForbidden(kind, name string, err error) error {
 | |
| 	return &StatusError{api.Status{
 | |
| 		Status: api.StatusFailure,
 | |
| 		Code:   http.StatusForbidden,
 | |
| 		Reason: api.StatusReasonForbidden,
 | |
| 		Details: &api.StatusDetails{
 | |
| 			Kind: kind,
 | |
| 			Name: name,
 | |
| 		},
 | |
| 		Message: fmt.Sprintf("%s %q is forbidden: %v", kind, name, err),
 | |
| 	}}
 | |
| }
 | |
| 
 | |
| // NewConflict returns an error indicating the item can't be updated as provided.
 | |
| func NewConflict(kind, name string, err error) error {
 | |
| 	return &StatusError{api.Status{
 | |
| 		Status: api.StatusFailure,
 | |
| 		Code:   http.StatusConflict,
 | |
| 		Reason: api.StatusReasonConflict,
 | |
| 		Details: &api.StatusDetails{
 | |
| 			Kind: kind,
 | |
| 			Name: name,
 | |
| 		},
 | |
| 		Message: fmt.Sprintf("%s %q cannot be updated: %v", kind, name, err),
 | |
| 	}}
 | |
| }
 | |
| 
 | |
| // NewInvalid returns an error indicating the item is invalid and cannot be processed.
 | |
| func NewInvalid(kind, name string, errs fielderrors.ValidationErrorList) error {
 | |
| 	causes := make([]api.StatusCause, 0, len(errs))
 | |
| 	for i := range errs {
 | |
| 		if err, ok := errs[i].(*fielderrors.ValidationError); ok {
 | |
| 			causes = append(causes, api.StatusCause{
 | |
| 				Type:    api.CauseType(err.Type),
 | |
| 				Message: err.ErrorBody(),
 | |
| 				Field:   err.Field,
 | |
| 			})
 | |
| 		}
 | |
| 	}
 | |
| 	return &StatusError{api.Status{
 | |
| 		Status: api.StatusFailure,
 | |
| 		Code:   StatusUnprocessableEntity, // RFC 4918: StatusUnprocessableEntity
 | |
| 		Reason: api.StatusReasonInvalid,
 | |
| 		Details: &api.StatusDetails{
 | |
| 			Kind:   kind,
 | |
| 			Name:   name,
 | |
| 			Causes: causes,
 | |
| 		},
 | |
| 		Message: fmt.Sprintf("%s %q is invalid: %v", kind, name, errors.NewAggregate(errs)),
 | |
| 	}}
 | |
| }
 | |
| 
 | |
| // NewBadRequest creates an error that indicates that the request is invalid and can not be processed.
 | |
| func NewBadRequest(reason string) error {
 | |
| 	return &StatusError{api.Status{
 | |
| 		Status:  api.StatusFailure,
 | |
| 		Code:    http.StatusBadRequest,
 | |
| 		Reason:  api.StatusReasonBadRequest,
 | |
| 		Message: reason,
 | |
| 	}}
 | |
| }
 | |
| 
 | |
| // NewServiceUnavailable creates an error that indicates that the requested service is unavailable.
 | |
| func NewServiceUnavailable(reason string) error {
 | |
| 	return &StatusError{api.Status{
 | |
| 		Status:  api.StatusFailure,
 | |
| 		Code:    http.StatusServiceUnavailable,
 | |
| 		Reason:  api.StatusReasonServiceUnavailable,
 | |
| 		Message: reason,
 | |
| 	}}
 | |
| }
 | |
| 
 | |
| // NewMethodNotSupported returns an error indicating the requested action is not supported on this kind.
 | |
| func NewMethodNotSupported(kind, action string) error {
 | |
| 	return &StatusError{api.Status{
 | |
| 		Status: api.StatusFailure,
 | |
| 		Code:   http.StatusMethodNotAllowed,
 | |
| 		Reason: api.StatusReasonMethodNotAllowed,
 | |
| 		Details: &api.StatusDetails{
 | |
| 			Kind: kind,
 | |
| 		},
 | |
| 		Message: fmt.Sprintf("%s is not supported on resources of kind %q", action, kind),
 | |
| 	}}
 | |
| }
 | |
| 
 | |
| // NewServerTimeout returns an error indicating the requested action could not be completed due to a
 | |
| // transient error, and the client should try again.
 | |
| func NewServerTimeout(kind, operation string, retryAfterSeconds int) error {
 | |
| 	return &StatusError{api.Status{
 | |
| 		Status: api.StatusFailure,
 | |
| 		Code:   http.StatusInternalServerError,
 | |
| 		Reason: api.StatusReasonServerTimeout,
 | |
| 		Details: &api.StatusDetails{
 | |
| 			Kind:              kind,
 | |
| 			Name:              operation,
 | |
| 			RetryAfterSeconds: retryAfterSeconds,
 | |
| 		},
 | |
| 		Message: fmt.Sprintf("The %s operation against %s could not be completed at this time, please try again.", operation, kind),
 | |
| 	}}
 | |
| }
 | |
| 
 | |
| // NewInternalError returns an error indicating the item is invalid and cannot be processed.
 | |
| func NewInternalError(err error) error {
 | |
| 	return &StatusError{api.Status{
 | |
| 		Status: api.StatusFailure,
 | |
| 		Code:   http.StatusInternalServerError,
 | |
| 		Reason: api.StatusReasonInternalError,
 | |
| 		Details: &api.StatusDetails{
 | |
| 			Causes: []api.StatusCause{{Message: err.Error()}},
 | |
| 		},
 | |
| 		Message: fmt.Sprintf("Internal error occurred: %v", err),
 | |
| 	}}
 | |
| }
 | |
| 
 | |
| // NewTimeoutError returns an error indicating that a timeout occurred before the request
 | |
| // could be completed.  Clients may retry, but the operation may still complete.
 | |
| func NewTimeoutError(message string, retryAfterSeconds int) error {
 | |
| 	return &StatusError{api.Status{
 | |
| 		Status:  api.StatusFailure,
 | |
| 		Code:    StatusServerTimeout,
 | |
| 		Reason:  api.StatusReasonTimeout,
 | |
| 		Message: fmt.Sprintf("Timeout: %s", message),
 | |
| 		Details: &api.StatusDetails{
 | |
| 			RetryAfterSeconds: retryAfterSeconds,
 | |
| 		},
 | |
| 	}}
 | |
| }
 | |
| 
 | |
| // NewGenericServerResponse returns a new error for server responses that are not in a recognizable form.
 | |
| func NewGenericServerResponse(code int, verb, kind, name, serverMessage string, retryAfterSeconds int, isUnexpectedResponse bool) error {
 | |
| 	reason := api.StatusReasonUnknown
 | |
| 	message := fmt.Sprintf("the server responded with the status code %d but did not return more information", code)
 | |
| 	switch code {
 | |
| 	case http.StatusConflict:
 | |
| 		if verb == "POST" {
 | |
| 			reason = api.StatusReasonAlreadyExists
 | |
| 		} else {
 | |
| 			reason = api.StatusReasonConflict
 | |
| 		}
 | |
| 		message = "the server reported a conflict"
 | |
| 	case http.StatusNotFound:
 | |
| 		reason = api.StatusReasonNotFound
 | |
| 		message = "the server could not find the requested resource"
 | |
| 	case http.StatusBadRequest:
 | |
| 		reason = api.StatusReasonBadRequest
 | |
| 		message = "the server rejected our request for an unknown reason"
 | |
| 	case http.StatusUnauthorized:
 | |
| 		reason = api.StatusReasonUnauthorized
 | |
| 		message = "the server has asked for the client to provide credentials"
 | |
| 	case http.StatusForbidden:
 | |
| 		reason = api.StatusReasonForbidden
 | |
| 		message = "the server does not allow access to the requested resource"
 | |
| 	case http.StatusMethodNotAllowed:
 | |
| 		reason = api.StatusReasonMethodNotAllowed
 | |
| 		message = "the server does not allow this method on the requested resource"
 | |
| 	case StatusUnprocessableEntity:
 | |
| 		reason = api.StatusReasonInvalid
 | |
| 		message = "the server rejected our request due to an error in our request"
 | |
| 	case StatusServerTimeout:
 | |
| 		reason = api.StatusReasonServerTimeout
 | |
| 		message = "the server cannot complete the requested operation at this time, try again later"
 | |
| 	case StatusTooManyRequests:
 | |
| 		reason = api.StatusReasonTimeout
 | |
| 		message = "the server has received too many requests and has asked us to try again later"
 | |
| 	default:
 | |
| 		if code >= 500 {
 | |
| 			reason = api.StatusReasonInternalError
 | |
| 			message = "an error on the server has prevented the request from succeeding"
 | |
| 		}
 | |
| 	}
 | |
| 	switch {
 | |
| 	case len(kind) > 0 && len(name) > 0:
 | |
| 		message = fmt.Sprintf("%s (%s %s %s)", message, strings.ToLower(verb), kind, name)
 | |
| 	case len(kind) > 0:
 | |
| 		message = fmt.Sprintf("%s (%s %s)", message, strings.ToLower(verb), kind)
 | |
| 	}
 | |
| 	var causes []api.StatusCause
 | |
| 	if isUnexpectedResponse {
 | |
| 		causes = []api.StatusCause{
 | |
| 			{
 | |
| 				Type:    api.CauseTypeUnexpectedServerResponse,
 | |
| 				Message: serverMessage,
 | |
| 			},
 | |
| 		}
 | |
| 	} else {
 | |
| 		causes = nil
 | |
| 	}
 | |
| 	return &StatusError{api.Status{
 | |
| 		Status: api.StatusFailure,
 | |
| 		Code:   code,
 | |
| 		Reason: reason,
 | |
| 		Details: &api.StatusDetails{
 | |
| 			Kind: kind,
 | |
| 			Name: name,
 | |
| 
 | |
| 			Causes:            causes,
 | |
| 			RetryAfterSeconds: retryAfterSeconds,
 | |
| 		},
 | |
| 		Message: message,
 | |
| 	}}
 | |
| }
 | |
| 
 | |
| // IsNotFound returns true if the specified error was created by NewNotFound.
 | |
| func IsNotFound(err error) bool {
 | |
| 	return reasonForError(err) == api.StatusReasonNotFound
 | |
| }
 | |
| 
 | |
| // IsAlreadyExists determines if the err is an error which indicates that a specified resource already exists.
 | |
| func IsAlreadyExists(err error) bool {
 | |
| 	return reasonForError(err) == api.StatusReasonAlreadyExists
 | |
| }
 | |
| 
 | |
| // IsConflict determines if the err is an error which indicates the provided update conflicts.
 | |
| func IsConflict(err error) bool {
 | |
| 	return reasonForError(err) == api.StatusReasonConflict
 | |
| }
 | |
| 
 | |
| // IsInvalid determines if the err is an error which indicates the provided resource is not valid.
 | |
| func IsInvalid(err error) bool {
 | |
| 	return reasonForError(err) == api.StatusReasonInvalid
 | |
| }
 | |
| 
 | |
| // IsMethodNotSupported determines if the err is an error which indicates the provided action could not
 | |
| // be performed because it is not supported by the server.
 | |
| func IsMethodNotSupported(err error) bool {
 | |
| 	return reasonForError(err) == api.StatusReasonMethodNotAllowed
 | |
| }
 | |
| 
 | |
| // IsBadRequest determines if err is an error which indicates that the request is invalid.
 | |
| func IsBadRequest(err error) bool {
 | |
| 	return reasonForError(err) == api.StatusReasonBadRequest
 | |
| }
 | |
| 
 | |
| // IsUnauthorized determines if err is an error which indicates that the request is unauthorized and
 | |
| // requires authentication by the user.
 | |
| func IsUnauthorized(err error) bool {
 | |
| 	return reasonForError(err) == api.StatusReasonUnauthorized
 | |
| }
 | |
| 
 | |
| // IsForbidden determines if err is an error which indicates that the request is forbidden and cannot
 | |
| // be completed as requested.
 | |
| func IsForbidden(err error) bool {
 | |
| 	return reasonForError(err) == api.StatusReasonForbidden
 | |
| }
 | |
| 
 | |
| // IsServerTimeout determines if err is an error which indicates that the request needs to be retried
 | |
| // by the client.
 | |
| func IsServerTimeout(err error) bool {
 | |
| 	return reasonForError(err) == api.StatusReasonServerTimeout
 | |
| }
 | |
| 
 | |
| // IsUnexpectedServerError returns true if the server response was not in the expected API format,
 | |
| // and may be the result of another HTTP actor.
 | |
| func IsUnexpectedServerError(err error) bool {
 | |
| 	switch t := err.(type) {
 | |
| 	case *StatusError:
 | |
| 		if d := t.Status().Details; d != nil {
 | |
| 			for _, cause := range d.Causes {
 | |
| 				if cause.Type == api.CauseTypeUnexpectedServerResponse {
 | |
| 					return true
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| // IsUnexpectedObjectError determines if err is due to an unexpected object from the master.
 | |
| func IsUnexpectedObjectError(err error) bool {
 | |
| 	_, ok := err.(*UnexpectedObjectError)
 | |
| 	return err != nil && ok
 | |
| }
 | |
| 
 | |
| // SuggestsClientDelay returns true if this error suggests a client delay as well as the
 | |
| // suggested seconds to wait, or false if the error does not imply a wait.
 | |
| func SuggestsClientDelay(err error) (int, bool) {
 | |
| 	switch t := err.(type) {
 | |
| 	case *StatusError:
 | |
| 		if t.Status().Details != nil {
 | |
| 			switch t.Status().Reason {
 | |
| 			case api.StatusReasonServerTimeout, api.StatusReasonTimeout:
 | |
| 				return t.Status().Details.RetryAfterSeconds, true
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return 0, false
 | |
| }
 | |
| 
 | |
| func reasonForError(err error) api.StatusReason {
 | |
| 	switch t := err.(type) {
 | |
| 	case *StatusError:
 | |
| 		return t.ErrStatus.Reason
 | |
| 	}
 | |
| 	return api.StatusReasonUnknown
 | |
| }
 | 
