github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/api/errors/errors.go (about)

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package errors
    18  
    19  import (
    20  	"encoding/json"
    21  	"errors"
    22  	"fmt"
    23  	"net/http"
    24  	"reflect"
    25  	"strings"
    26  
    27  	metav1 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1"
    28  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime"
    29  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/schema"
    30  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/validation/field"
    31  )
    32  
    33  // StatusError is an error intended for consumption by a REST API server; it can also be
    34  // reconstructed by clients from a REST response. Public to allow easy type switches.
    35  type StatusError struct {
    36  	ErrStatus metav1.Status
    37  }
    38  
    39  // APIStatus is exposed by errors that can be converted to an api.Status object
    40  // for finer grained details.
    41  type APIStatus interface {
    42  	Status() metav1.Status
    43  }
    44  
    45  var _ error = &StatusError{}
    46  
    47  var knownReasons = map[metav1.StatusReason]struct{}{
    48  	// metav1.StatusReasonUnknown : {}
    49  	metav1.StatusReasonUnauthorized:          {},
    50  	metav1.StatusReasonForbidden:             {},
    51  	metav1.StatusReasonNotFound:              {},
    52  	metav1.StatusReasonAlreadyExists:         {},
    53  	metav1.StatusReasonConflict:              {},
    54  	metav1.StatusReasonGone:                  {},
    55  	metav1.StatusReasonInvalid:               {},
    56  	metav1.StatusReasonServerTimeout:         {},
    57  	metav1.StatusReasonTimeout:               {},
    58  	metav1.StatusReasonTooManyRequests:       {},
    59  	metav1.StatusReasonBadRequest:            {},
    60  	metav1.StatusReasonMethodNotAllowed:      {},
    61  	metav1.StatusReasonNotAcceptable:         {},
    62  	metav1.StatusReasonRequestEntityTooLarge: {},
    63  	metav1.StatusReasonUnsupportedMediaType:  {},
    64  	metav1.StatusReasonInternalError:         {},
    65  	metav1.StatusReasonExpired:               {},
    66  	metav1.StatusReasonServiceUnavailable:    {},
    67  }
    68  
    69  // Error implements the Error interface.
    70  func (e *StatusError) Error() string {
    71  	return e.ErrStatus.Message
    72  }
    73  
    74  // Status allows access to e's status without having to know the detailed workings
    75  // of StatusError.
    76  func (e *StatusError) Status() metav1.Status {
    77  	return e.ErrStatus
    78  }
    79  
    80  // DebugError reports extended info about the error to debug output.
    81  func (e *StatusError) DebugError() (string, []interface{}) {
    82  	if out, err := json.MarshalIndent(e.ErrStatus, "", "  "); err == nil {
    83  		return "server response object: %s", []interface{}{string(out)}
    84  	}
    85  	return "server response object: %#v", []interface{}{e.ErrStatus}
    86  }
    87  
    88  // HasStatusCause returns true if the provided error has a details cause
    89  // with the provided type name.
    90  // It supports wrapped errors and returns false when the error is nil.
    91  func HasStatusCause(err error, name metav1.CauseType) bool {
    92  	_, ok := StatusCause(err, name)
    93  	return ok
    94  }
    95  
    96  // StatusCause returns the named cause from the provided error if it exists and
    97  // the error unwraps to the type APIStatus. Otherwise it returns false.
    98  func StatusCause(err error, name metav1.CauseType) (metav1.StatusCause, bool) {
    99  	status, ok := err.(APIStatus)
   100  	if (ok || errors.As(err, &status)) && status.Status().Details != nil {
   101  		for _, cause := range status.Status().Details.Causes {
   102  			if cause.Type == name {
   103  				return cause, true
   104  			}
   105  		}
   106  	}
   107  	return metav1.StatusCause{}, false
   108  }
   109  
   110  // UnexpectedObjectError can be returned by FromObject if it's passed a non-status object.
   111  type UnexpectedObjectError struct {
   112  	Object runtime.Object
   113  }
   114  
   115  // Error returns an error message describing 'u'.
   116  func (u *UnexpectedObjectError) Error() string {
   117  	return fmt.Sprintf("unexpected object: %v", u.Object)
   118  }
   119  
   120  // FromObject generates an StatusError from an metav1.Status, if that is the type of obj; otherwise,
   121  // returns an UnexpecteObjectError.
   122  func FromObject(obj runtime.Object) error {
   123  	switch t := obj.(type) {
   124  	case *metav1.Status:
   125  		return &StatusError{ErrStatus: *t}
   126  	case runtime.Unstructured:
   127  		var status metav1.Status
   128  		obj := t.UnstructuredContent()
   129  		if !reflect.DeepEqual(obj["kind"], "Status") {
   130  			break
   131  		}
   132  		if err := runtime.DefaultUnstructuredConverter.FromUnstructured(t.UnstructuredContent(), &status); err != nil {
   133  			return err
   134  		}
   135  		if status.APIVersion != "v1" && status.APIVersion != "meta.k8s.io/v1" {
   136  			break
   137  		}
   138  		return &StatusError{ErrStatus: status}
   139  	}
   140  	return &UnexpectedObjectError{obj}
   141  }
   142  
   143  // NewNotFound returns a new error which indicates that the resource of the kind and the name was not found.
   144  func NewNotFound(qualifiedResource schema.GroupResource, name string) *StatusError {
   145  	return &StatusError{metav1.Status{
   146  		Status: metav1.StatusFailure,
   147  		Code:   http.StatusNotFound,
   148  		Reason: metav1.StatusReasonNotFound,
   149  		Details: &metav1.StatusDetails{
   150  			Group: qualifiedResource.Group,
   151  			Kind:  qualifiedResource.Resource,
   152  			Name:  name,
   153  		},
   154  		Message: fmt.Sprintf("%s %q not found", qualifiedResource.String(), name),
   155  	}}
   156  }
   157  
   158  // NewAlreadyExists returns an error indicating the item requested exists by that identifier.
   159  func NewAlreadyExists(qualifiedResource schema.GroupResource, name string) *StatusError {
   160  	return &StatusError{metav1.Status{
   161  		Status: metav1.StatusFailure,
   162  		Code:   http.StatusConflict,
   163  		Reason: metav1.StatusReasonAlreadyExists,
   164  		Details: &metav1.StatusDetails{
   165  			Group: qualifiedResource.Group,
   166  			Kind:  qualifiedResource.Resource,
   167  			Name:  name,
   168  		},
   169  		Message: fmt.Sprintf("%s %q already exists", qualifiedResource.String(), name),
   170  	}}
   171  }
   172  
   173  // NewGenerateNameConflict returns an error indicating the server
   174  // was not able to generate a valid name for a resource.
   175  func NewGenerateNameConflict(qualifiedResource schema.GroupResource, name string, retryAfterSeconds int) *StatusError {
   176  	return &StatusError{metav1.Status{
   177  		Status: metav1.StatusFailure,
   178  		Code:   http.StatusConflict,
   179  		Reason: metav1.StatusReasonAlreadyExists,
   180  		Details: &metav1.StatusDetails{
   181  			Group:             qualifiedResource.Group,
   182  			Kind:              qualifiedResource.Resource,
   183  			Name:              name,
   184  			RetryAfterSeconds: int32(retryAfterSeconds),
   185  		},
   186  		Message: fmt.Sprintf(
   187  			"%s %q already exists, the server was not able to generate a unique name for the object",
   188  			qualifiedResource.String(), name),
   189  	}}
   190  }
   191  
   192  // NewUnauthorized returns an error indicating the client is not authorized to perform the requested
   193  // action.
   194  func NewUnauthorized(reason string) *StatusError {
   195  	message := reason
   196  	if len(message) == 0 {
   197  		message = "not authorized"
   198  	}
   199  	return &StatusError{metav1.Status{
   200  		Status:  metav1.StatusFailure,
   201  		Code:    http.StatusUnauthorized,
   202  		Reason:  metav1.StatusReasonUnauthorized,
   203  		Message: message,
   204  	}}
   205  }
   206  
   207  // NewForbidden returns an error indicating the requested action was forbidden
   208  func NewForbidden(qualifiedResource schema.GroupResource, name string, err error) *StatusError {
   209  	var message string
   210  	if qualifiedResource.Empty() {
   211  		message = fmt.Sprintf("forbidden: %v", err)
   212  	} else if name == "" {
   213  		message = fmt.Sprintf("%s is forbidden: %v", qualifiedResource.String(), err)
   214  	} else {
   215  		message = fmt.Sprintf("%s %q is forbidden: %v", qualifiedResource.String(), name, err)
   216  	}
   217  	return &StatusError{metav1.Status{
   218  		Status: metav1.StatusFailure,
   219  		Code:   http.StatusForbidden,
   220  		Reason: metav1.StatusReasonForbidden,
   221  		Details: &metav1.StatusDetails{
   222  			Group: qualifiedResource.Group,
   223  			Kind:  qualifiedResource.Resource,
   224  			Name:  name,
   225  		},
   226  		Message: message,
   227  	}}
   228  }
   229  
   230  // NewConflict returns an error indicating the item can't be updated as provided.
   231  func NewConflict(qualifiedResource schema.GroupResource, name string, err error) *StatusError {
   232  	return &StatusError{metav1.Status{
   233  		Status: metav1.StatusFailure,
   234  		Code:   http.StatusConflict,
   235  		Reason: metav1.StatusReasonConflict,
   236  		Details: &metav1.StatusDetails{
   237  			Group: qualifiedResource.Group,
   238  			Kind:  qualifiedResource.Resource,
   239  			Name:  name,
   240  		},
   241  		Message: fmt.Sprintf("Operation cannot be fulfilled on %s %q: %v", qualifiedResource.String(), name, err),
   242  	}}
   243  }
   244  
   245  // NewApplyConflict returns an error including details on the requests apply conflicts
   246  func NewApplyConflict(causes []metav1.StatusCause, message string) *StatusError {
   247  	return &StatusError{ErrStatus: metav1.Status{
   248  		Status: metav1.StatusFailure,
   249  		Code:   http.StatusConflict,
   250  		Reason: metav1.StatusReasonConflict,
   251  		Details: &metav1.StatusDetails{
   252  			// TODO: Get obj details here?
   253  			Causes: causes,
   254  		},
   255  		Message: message,
   256  	}}
   257  }
   258  
   259  // NewGone returns an error indicating the item no longer available at the server and no forwarding address is known.
   260  // DEPRECATED: Please use NewResourceExpired instead.
   261  func NewGone(message string) *StatusError {
   262  	return &StatusError{metav1.Status{
   263  		Status:  metav1.StatusFailure,
   264  		Code:    http.StatusGone,
   265  		Reason:  metav1.StatusReasonGone,
   266  		Message: message,
   267  	}}
   268  }
   269  
   270  // NewResourceExpired creates an error that indicates that the requested resource content has expired from
   271  // the server (usually due to a resourceVersion that is too old).
   272  func NewResourceExpired(message string) *StatusError {
   273  	return &StatusError{metav1.Status{
   274  		Status:  metav1.StatusFailure,
   275  		Code:    http.StatusGone,
   276  		Reason:  metav1.StatusReasonExpired,
   277  		Message: message,
   278  	}}
   279  }
   280  
   281  // NewInvalid returns an error indicating the item is invalid and cannot be processed.
   282  func NewInvalid(qualifiedKind schema.GroupKind, name string, errs field.ErrorList) *StatusError {
   283  	causes := make([]metav1.StatusCause, 0, len(errs))
   284  	for i := range errs {
   285  		err := errs[i]
   286  		causes = append(causes, metav1.StatusCause{
   287  			Type:    metav1.CauseType(err.Type),
   288  			Message: err.ErrorBody(),
   289  			Field:   err.Field,
   290  		})
   291  	}
   292  	err := &StatusError{metav1.Status{
   293  		Status: metav1.StatusFailure,
   294  		Code:   http.StatusUnprocessableEntity,
   295  		Reason: metav1.StatusReasonInvalid,
   296  		Details: &metav1.StatusDetails{
   297  			Group:  qualifiedKind.Group,
   298  			Kind:   qualifiedKind.Kind,
   299  			Name:   name,
   300  			Causes: causes,
   301  		},
   302  	}}
   303  	aggregatedErrs := errs.ToAggregate()
   304  	if aggregatedErrs == nil {
   305  		err.ErrStatus.Message = fmt.Sprintf("%s %q is invalid", qualifiedKind.String(), name)
   306  	} else {
   307  		err.ErrStatus.Message = fmt.Sprintf("%s %q is invalid: %v", qualifiedKind.String(), name, aggregatedErrs)
   308  	}
   309  	return err
   310  }
   311  
   312  // NewBadRequest creates an error that indicates that the request is invalid and can not be processed.
   313  func NewBadRequest(reason string) *StatusError {
   314  	return &StatusError{metav1.Status{
   315  		Status:  metav1.StatusFailure,
   316  		Code:    http.StatusBadRequest,
   317  		Reason:  metav1.StatusReasonBadRequest,
   318  		Message: reason,
   319  	}}
   320  }
   321  
   322  // NewTooManyRequests creates an error that indicates that the client must try again later because
   323  // the specified endpoint is not accepting requests. More specific details should be provided
   324  // if client should know why the failure was limited.
   325  func NewTooManyRequests(message string, retryAfterSeconds int) *StatusError {
   326  	return &StatusError{metav1.Status{
   327  		Status:  metav1.StatusFailure,
   328  		Code:    http.StatusTooManyRequests,
   329  		Reason:  metav1.StatusReasonTooManyRequests,
   330  		Message: message,
   331  		Details: &metav1.StatusDetails{
   332  			RetryAfterSeconds: int32(retryAfterSeconds),
   333  		},
   334  	}}
   335  }
   336  
   337  // NewServiceUnavailable creates an error that indicates that the requested service is unavailable.
   338  func NewServiceUnavailable(reason string) *StatusError {
   339  	return &StatusError{metav1.Status{
   340  		Status:  metav1.StatusFailure,
   341  		Code:    http.StatusServiceUnavailable,
   342  		Reason:  metav1.StatusReasonServiceUnavailable,
   343  		Message: reason,
   344  	}}
   345  }
   346  
   347  // NewMethodNotSupported returns an error indicating the requested action is not supported on this kind.
   348  func NewMethodNotSupported(qualifiedResource schema.GroupResource, action string) *StatusError {
   349  	return &StatusError{metav1.Status{
   350  		Status: metav1.StatusFailure,
   351  		Code:   http.StatusMethodNotAllowed,
   352  		Reason: metav1.StatusReasonMethodNotAllowed,
   353  		Details: &metav1.StatusDetails{
   354  			Group: qualifiedResource.Group,
   355  			Kind:  qualifiedResource.Resource,
   356  		},
   357  		Message: fmt.Sprintf("%s is not supported on resources of kind %q", action, qualifiedResource.String()),
   358  	}}
   359  }
   360  
   361  // NewServerTimeout returns an error indicating the requested action could not be completed due to a
   362  // transient error, and the client should try again.
   363  func NewServerTimeout(qualifiedResource schema.GroupResource, operation string, retryAfterSeconds int) *StatusError {
   364  	return &StatusError{metav1.Status{
   365  		Status: metav1.StatusFailure,
   366  		Code:   http.StatusInternalServerError,
   367  		Reason: metav1.StatusReasonServerTimeout,
   368  		Details: &metav1.StatusDetails{
   369  			Group:             qualifiedResource.Group,
   370  			Kind:              qualifiedResource.Resource,
   371  			Name:              operation,
   372  			RetryAfterSeconds: int32(retryAfterSeconds),
   373  		},
   374  		Message: fmt.Sprintf("The %s operation against %s could not be completed at this time, please try again.", operation, qualifiedResource.String()),
   375  	}}
   376  }
   377  
   378  // NewServerTimeoutForKind should not exist.  Server timeouts happen when accessing resources, the Kind is just what we
   379  // happened to be looking at when the request failed.  This delegates to keep code sane, but we should work towards removing this.
   380  func NewServerTimeoutForKind(qualifiedKind schema.GroupKind, operation string, retryAfterSeconds int) *StatusError {
   381  	return NewServerTimeout(schema.GroupResource{Group: qualifiedKind.Group, Resource: qualifiedKind.Kind}, operation, retryAfterSeconds)
   382  }
   383  
   384  // NewInternalError returns an error indicating the item is invalid and cannot be processed.
   385  func NewInternalError(err error) *StatusError {
   386  	return &StatusError{metav1.Status{
   387  		Status: metav1.StatusFailure,
   388  		Code:   http.StatusInternalServerError,
   389  		Reason: metav1.StatusReasonInternalError,
   390  		Details: &metav1.StatusDetails{
   391  			Causes: []metav1.StatusCause{{Message: err.Error()}},
   392  		},
   393  		Message: fmt.Sprintf("Internal error occurred: %v", err),
   394  	}}
   395  }
   396  
   397  // NewTimeoutError returns an error indicating that a timeout occurred before the request
   398  // could be completed.  Clients may retry, but the operation may still complete.
   399  func NewTimeoutError(message string, retryAfterSeconds int) *StatusError {
   400  	return &StatusError{metav1.Status{
   401  		Status:  metav1.StatusFailure,
   402  		Code:    http.StatusGatewayTimeout,
   403  		Reason:  metav1.StatusReasonTimeout,
   404  		Message: fmt.Sprintf("Timeout: %s", message),
   405  		Details: &metav1.StatusDetails{
   406  			RetryAfterSeconds: int32(retryAfterSeconds),
   407  		},
   408  	}}
   409  }
   410  
   411  // NewTooManyRequestsError returns an error indicating that the request was rejected because
   412  // the server has received too many requests. Client should wait and retry. But if the request
   413  // is perishable, then the client should not retry the request.
   414  func NewTooManyRequestsError(message string) *StatusError {
   415  	return &StatusError{metav1.Status{
   416  		Status:  metav1.StatusFailure,
   417  		Code:    http.StatusTooManyRequests,
   418  		Reason:  metav1.StatusReasonTooManyRequests,
   419  		Message: fmt.Sprintf("Too many requests: %s", message),
   420  	}}
   421  }
   422  
   423  // NewRequestEntityTooLargeError returns an error indicating that the request
   424  // entity was too large.
   425  func NewRequestEntityTooLargeError(message string) *StatusError {
   426  	return &StatusError{metav1.Status{
   427  		Status:  metav1.StatusFailure,
   428  		Code:    http.StatusRequestEntityTooLarge,
   429  		Reason:  metav1.StatusReasonRequestEntityTooLarge,
   430  		Message: fmt.Sprintf("Request entity too large: %s", message),
   431  	}}
   432  }
   433  
   434  // NewGenericServerResponse returns a new error for server responses that are not in a recognizable form.
   435  func NewGenericServerResponse(code int, verb string, qualifiedResource schema.GroupResource, name, serverMessage string, retryAfterSeconds int, isUnexpectedResponse bool) *StatusError {
   436  	reason := metav1.StatusReasonUnknown
   437  	message := fmt.Sprintf("the server responded with the status code %d but did not return more information", code)
   438  	switch code {
   439  	case http.StatusConflict:
   440  		if verb == "POST" {
   441  			reason = metav1.StatusReasonAlreadyExists
   442  		} else {
   443  			reason = metav1.StatusReasonConflict
   444  		}
   445  		message = "the server reported a conflict"
   446  	case http.StatusNotFound:
   447  		reason = metav1.StatusReasonNotFound
   448  		message = "the server could not find the requested resource"
   449  	case http.StatusBadRequest:
   450  		reason = metav1.StatusReasonBadRequest
   451  		message = "the server rejected our request for an unknown reason"
   452  	case http.StatusUnauthorized:
   453  		reason = metav1.StatusReasonUnauthorized
   454  		message = "the server has asked for the client to provide credentials"
   455  	case http.StatusForbidden:
   456  		reason = metav1.StatusReasonForbidden
   457  		// the server message has details about who is trying to perform what action.  Keep its message.
   458  		message = serverMessage
   459  	case http.StatusNotAcceptable:
   460  		reason = metav1.StatusReasonNotAcceptable
   461  		// the server message has details about what types are acceptable
   462  		if len(serverMessage) == 0 || serverMessage == "unknown" {
   463  			message = "the server was unable to respond with a content type that the client supports"
   464  		} else {
   465  			message = serverMessage
   466  		}
   467  	case http.StatusUnsupportedMediaType:
   468  		reason = metav1.StatusReasonUnsupportedMediaType
   469  		// the server message has details about what types are acceptable
   470  		message = serverMessage
   471  	case http.StatusMethodNotAllowed:
   472  		reason = metav1.StatusReasonMethodNotAllowed
   473  		message = "the server does not allow this method on the requested resource"
   474  	case http.StatusUnprocessableEntity:
   475  		reason = metav1.StatusReasonInvalid
   476  		message = "the server rejected our request due to an error in our request"
   477  	case http.StatusServiceUnavailable:
   478  		reason = metav1.StatusReasonServiceUnavailable
   479  		message = "the server is currently unable to handle the request"
   480  	case http.StatusGatewayTimeout:
   481  		reason = metav1.StatusReasonTimeout
   482  		message = "the server was unable to return a response in the time allotted, but may still be processing the request"
   483  	case http.StatusTooManyRequests:
   484  		reason = metav1.StatusReasonTooManyRequests
   485  		message = "the server has received too many requests and has asked us to try again later"
   486  	default:
   487  		if code >= 500 {
   488  			reason = metav1.StatusReasonInternalError
   489  			message = fmt.Sprintf("an error on the server (%q) has prevented the request from succeeding", serverMessage)
   490  		}
   491  	}
   492  	switch {
   493  	case !qualifiedResource.Empty() && len(name) > 0:
   494  		message = fmt.Sprintf("%s (%s %s %s)", message, strings.ToLower(verb), qualifiedResource.String(), name)
   495  	case !qualifiedResource.Empty():
   496  		message = fmt.Sprintf("%s (%s %s)", message, strings.ToLower(verb), qualifiedResource.String())
   497  	}
   498  	var causes []metav1.StatusCause
   499  	if isUnexpectedResponse {
   500  		causes = []metav1.StatusCause{
   501  			{
   502  				Type:    metav1.CauseTypeUnexpectedServerResponse,
   503  				Message: serverMessage,
   504  			},
   505  		}
   506  	} else {
   507  		causes = nil
   508  	}
   509  	return &StatusError{metav1.Status{
   510  		Status: metav1.StatusFailure,
   511  		Code:   int32(code),
   512  		Reason: reason,
   513  		Details: &metav1.StatusDetails{
   514  			Group: qualifiedResource.Group,
   515  			Kind:  qualifiedResource.Resource,
   516  			Name:  name,
   517  
   518  			Causes:            causes,
   519  			RetryAfterSeconds: int32(retryAfterSeconds),
   520  		},
   521  		Message: message,
   522  	}}
   523  }
   524  
   525  // IsNotFound returns true if the specified error was created by NewNotFound.
   526  // It supports wrapped errors and returns false when the error is nil.
   527  func IsNotFound(err error) bool {
   528  	reason, code := reasonAndCodeForError(err)
   529  	if reason == metav1.StatusReasonNotFound {
   530  		return true
   531  	}
   532  	if _, ok := knownReasons[reason]; !ok && code == http.StatusNotFound {
   533  		return true
   534  	}
   535  	return false
   536  }
   537  
   538  // IsAlreadyExists determines if the err is an error which indicates that a specified resource already exists.
   539  // It supports wrapped errors and returns false when the error is nil.
   540  func IsAlreadyExists(err error) bool {
   541  	return ReasonForError(err) == metav1.StatusReasonAlreadyExists
   542  }
   543  
   544  // IsConflict determines if the err is an error which indicates the provided update conflicts.
   545  // It supports wrapped errors and returns false when the error is nil.
   546  func IsConflict(err error) bool {
   547  	reason, code := reasonAndCodeForError(err)
   548  	if reason == metav1.StatusReasonConflict {
   549  		return true
   550  	}
   551  	if _, ok := knownReasons[reason]; !ok && code == http.StatusConflict {
   552  		return true
   553  	}
   554  	return false
   555  }
   556  
   557  // IsInvalid determines if the err is an error which indicates the provided resource is not valid.
   558  // It supports wrapped errors and returns false when the error is nil.
   559  func IsInvalid(err error) bool {
   560  	reason, code := reasonAndCodeForError(err)
   561  	if reason == metav1.StatusReasonInvalid {
   562  		return true
   563  	}
   564  	if _, ok := knownReasons[reason]; !ok && code == http.StatusUnprocessableEntity {
   565  		return true
   566  	}
   567  	return false
   568  }
   569  
   570  // IsGone is true if the error indicates the requested resource is no longer available.
   571  // It supports wrapped errors and returns false when the error is nil.
   572  func IsGone(err error) bool {
   573  	reason, code := reasonAndCodeForError(err)
   574  	if reason == metav1.StatusReasonGone {
   575  		return true
   576  	}
   577  	if _, ok := knownReasons[reason]; !ok && code == http.StatusGone {
   578  		return true
   579  	}
   580  	return false
   581  }
   582  
   583  // IsResourceExpired is true if the error indicates the resource has expired and the current action is
   584  // no longer possible.
   585  // It supports wrapped errors and returns false when the error is nil.
   586  func IsResourceExpired(err error) bool {
   587  	return ReasonForError(err) == metav1.StatusReasonExpired
   588  }
   589  
   590  // IsNotAcceptable determines if err is an error which indicates that the request failed due to an invalid Accept header
   591  // It supports wrapped errors and returns false when the error is nil.
   592  func IsNotAcceptable(err error) bool {
   593  	reason, code := reasonAndCodeForError(err)
   594  	if reason == metav1.StatusReasonNotAcceptable {
   595  		return true
   596  	}
   597  	if _, ok := knownReasons[reason]; !ok && code == http.StatusNotAcceptable {
   598  		return true
   599  	}
   600  	return false
   601  }
   602  
   603  // IsUnsupportedMediaType determines if err is an error which indicates that the request failed due to an invalid Content-Type header
   604  // It supports wrapped errors and returns false when the error is nil.
   605  func IsUnsupportedMediaType(err error) bool {
   606  	reason, code := reasonAndCodeForError(err)
   607  	if reason == metav1.StatusReasonUnsupportedMediaType {
   608  		return true
   609  	}
   610  	if _, ok := knownReasons[reason]; !ok && code == http.StatusUnsupportedMediaType {
   611  		return true
   612  	}
   613  	return false
   614  }
   615  
   616  // IsMethodNotSupported determines if the err is an error which indicates the provided action could not
   617  // be performed because it is not supported by the server.
   618  // It supports wrapped errors and returns false when the error is nil.
   619  func IsMethodNotSupported(err error) bool {
   620  	reason, code := reasonAndCodeForError(err)
   621  	if reason == metav1.StatusReasonMethodNotAllowed {
   622  		return true
   623  	}
   624  	if _, ok := knownReasons[reason]; !ok && code == http.StatusMethodNotAllowed {
   625  		return true
   626  	}
   627  	return false
   628  }
   629  
   630  // IsServiceUnavailable is true if the error indicates the underlying service is no longer available.
   631  // It supports wrapped errors and returns false when the error is nil.
   632  func IsServiceUnavailable(err error) bool {
   633  	reason, code := reasonAndCodeForError(err)
   634  	if reason == metav1.StatusReasonServiceUnavailable {
   635  		return true
   636  	}
   637  	if _, ok := knownReasons[reason]; !ok && code == http.StatusServiceUnavailable {
   638  		return true
   639  	}
   640  	return false
   641  }
   642  
   643  // IsBadRequest determines if err is an error which indicates that the request is invalid.
   644  // It supports wrapped errors and returns false when the error is nil.
   645  func IsBadRequest(err error) bool {
   646  	reason, code := reasonAndCodeForError(err)
   647  	if reason == metav1.StatusReasonBadRequest {
   648  		return true
   649  	}
   650  	if _, ok := knownReasons[reason]; !ok && code == http.StatusBadRequest {
   651  		return true
   652  	}
   653  	return false
   654  }
   655  
   656  // IsUnauthorized determines if err is an error which indicates that the request is unauthorized and
   657  // requires authentication by the user.
   658  // It supports wrapped errors and returns false when the error is nil.
   659  func IsUnauthorized(err error) bool {
   660  	reason, code := reasonAndCodeForError(err)
   661  	if reason == metav1.StatusReasonUnauthorized {
   662  		return true
   663  	}
   664  	if _, ok := knownReasons[reason]; !ok && code == http.StatusUnauthorized {
   665  		return true
   666  	}
   667  	return false
   668  }
   669  
   670  // IsForbidden determines if err is an error which indicates that the request is forbidden and cannot
   671  // be completed as requested.
   672  // It supports wrapped errors and returns false when the error is nil.
   673  func IsForbidden(err error) bool {
   674  	reason, code := reasonAndCodeForError(err)
   675  	if reason == metav1.StatusReasonForbidden {
   676  		return true
   677  	}
   678  	if _, ok := knownReasons[reason]; !ok && code == http.StatusForbidden {
   679  		return true
   680  	}
   681  	return false
   682  }
   683  
   684  // IsTimeout determines if err is an error which indicates that request times out due to long
   685  // processing.
   686  // It supports wrapped errors and returns false when the error is nil.
   687  func IsTimeout(err error) bool {
   688  	reason, code := reasonAndCodeForError(err)
   689  	if reason == metav1.StatusReasonTimeout {
   690  		return true
   691  	}
   692  	if _, ok := knownReasons[reason]; !ok && code == http.StatusGatewayTimeout {
   693  		return true
   694  	}
   695  	return false
   696  }
   697  
   698  // IsServerTimeout determines if err is an error which indicates that the request needs to be retried
   699  // by the client.
   700  // It supports wrapped errors and returns false when the error is nil.
   701  func IsServerTimeout(err error) bool {
   702  	// do not check the status code, because no https status code exists that can
   703  	// be scoped to retryable timeouts.
   704  	return ReasonForError(err) == metav1.StatusReasonServerTimeout
   705  }
   706  
   707  // IsInternalError determines if err is an error which indicates an internal server error.
   708  // It supports wrapped errors and returns false when the error is nil.
   709  func IsInternalError(err error) bool {
   710  	reason, code := reasonAndCodeForError(err)
   711  	if reason == metav1.StatusReasonInternalError {
   712  		return true
   713  	}
   714  	if _, ok := knownReasons[reason]; !ok && code == http.StatusInternalServerError {
   715  		return true
   716  	}
   717  	return false
   718  }
   719  
   720  // IsTooManyRequests determines if err is an error which indicates that there are too many requests
   721  // that the server cannot handle.
   722  // It supports wrapped errors and returns false when the error is nil.
   723  func IsTooManyRequests(err error) bool {
   724  	reason, code := reasonAndCodeForError(err)
   725  	if reason == metav1.StatusReasonTooManyRequests {
   726  		return true
   727  	}
   728  
   729  	// IsTooManyRequests' checking of code predates the checking of the code in
   730  	// the other Is* functions. In order to maintain backward compatibility, this
   731  	// does not check that the reason is unknown.
   732  	if code == http.StatusTooManyRequests {
   733  		return true
   734  	}
   735  	return false
   736  }
   737  
   738  // IsRequestEntityTooLargeError determines if err is an error which indicates
   739  // the request entity is too large.
   740  // It supports wrapped errors and returns false when the error is nil.
   741  func IsRequestEntityTooLargeError(err error) bool {
   742  	reason, code := reasonAndCodeForError(err)
   743  	if reason == metav1.StatusReasonRequestEntityTooLarge {
   744  		return true
   745  	}
   746  
   747  	// IsRequestEntityTooLargeError's checking of code predates the checking of
   748  	// the code in the other Is* functions. In order to maintain backward
   749  	// compatibility, this does not check that the reason is unknown.
   750  	if code == http.StatusRequestEntityTooLarge {
   751  		return true
   752  	}
   753  	return false
   754  }
   755  
   756  // IsUnexpectedServerError returns true if the server response was not in the expected API format,
   757  // and may be the result of another HTTP actor.
   758  // It supports wrapped errors and returns false when the error is nil.
   759  func IsUnexpectedServerError(err error) bool {
   760  	status, ok := err.(APIStatus)
   761  	if (ok || errors.As(err, &status)) && status.Status().Details != nil {
   762  		for _, cause := range status.Status().Details.Causes {
   763  			if cause.Type == metav1.CauseTypeUnexpectedServerResponse {
   764  				return true
   765  			}
   766  		}
   767  	}
   768  	return false
   769  }
   770  
   771  // IsUnexpectedObjectError determines if err is due to an unexpected object from the master.
   772  // It supports wrapped errors and returns false when the error is nil.
   773  func IsUnexpectedObjectError(err error) bool {
   774  	uoe, ok := err.(*UnexpectedObjectError)
   775  	return err != nil && (ok || errors.As(err, &uoe))
   776  }
   777  
   778  // SuggestsClientDelay returns true if this error suggests a client delay as well as the
   779  // suggested seconds to wait, or false if the error does not imply a wait. It does not
   780  // address whether the error *should* be retried, since some errors (like a 3xx) may
   781  // request delay without retry.
   782  // It supports wrapped errors and returns false when the error is nil.
   783  func SuggestsClientDelay(err error) (int, bool) {
   784  	t, ok := err.(APIStatus)
   785  	if (ok || errors.As(err, &t)) && t.Status().Details != nil {
   786  		switch t.Status().Reason {
   787  		// this StatusReason explicitly requests the caller to delay the action
   788  		case metav1.StatusReasonServerTimeout:
   789  			return int(t.Status().Details.RetryAfterSeconds), true
   790  		}
   791  		// If the client requests that we retry after a certain number of seconds
   792  		if t.Status().Details.RetryAfterSeconds > 0 {
   793  			return int(t.Status().Details.RetryAfterSeconds), true
   794  		}
   795  	}
   796  	return 0, false
   797  }
   798  
   799  // ReasonForError returns the HTTP status for a particular error.
   800  // It supports wrapped errors and returns StatusReasonUnknown when
   801  // the error is nil or doesn't have a status.
   802  func ReasonForError(err error) metav1.StatusReason {
   803  	if status, ok := err.(APIStatus); ok || errors.As(err, &status) {
   804  		return status.Status().Reason
   805  	}
   806  	return metav1.StatusReasonUnknown
   807  }
   808  
   809  func reasonAndCodeForError(err error) (metav1.StatusReason, int32) {
   810  	if status, ok := err.(APIStatus); ok || errors.As(err, &status) {
   811  		return status.Status().Reason, status.Status().Code
   812  	}
   813  	return metav1.StatusReasonUnknown, 0
   814  }
   815  
   816  // ErrorReporter converts generic errors into runtime.Object errors without
   817  // requiring the caller to take a dependency on meta/v1 (where Status lives).
   818  // This prevents circular dependencies in core watch code.
   819  type ErrorReporter struct {
   820  	code   int
   821  	verb   string
   822  	reason string
   823  }
   824  
   825  // NewClientErrorReporter will respond with valid v1.Status objects that report
   826  // unexpected server responses. Primarily used by watch to report errors when
   827  // we attempt to decode a response from the server and it is not in the form
   828  // we expect. Because watch is a dependency of the core api, we can't return
   829  // meta/v1.Status in that package and so much inject this interface to convert a
   830  // generic error as appropriate. The reason is passed as a unique status cause
   831  // on the returned status, otherwise the generic "ClientError" is returned.
   832  func NewClientErrorReporter(code int, verb string, reason string) *ErrorReporter {
   833  	return &ErrorReporter{
   834  		code:   code,
   835  		verb:   verb,
   836  		reason: reason,
   837  	}
   838  }
   839  
   840  // AsObject returns a valid error runtime.Object (a v1.Status) for the given
   841  // error, using the code and verb of the reporter type. The error is set to
   842  // indicate that this was an unexpected server response.
   843  func (r *ErrorReporter) AsObject(err error) runtime.Object {
   844  	status := NewGenericServerResponse(r.code, r.verb, schema.GroupResource{}, "", err.Error(), 0, true)
   845  	if status.ErrStatus.Details == nil {
   846  		status.ErrStatus.Details = &metav1.StatusDetails{}
   847  	}
   848  	reason := r.reason
   849  	if len(reason) == 0 {
   850  		reason = "ClientError"
   851  	}
   852  	status.ErrStatus.Details.Causes = append(status.ErrStatus.Details.Causes, metav1.StatusCause{
   853  		Type:    metav1.CauseType(reason),
   854  		Message: err.Error(),
   855  	})
   856  	return &status.ErrStatus
   857  }