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 }