Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / k8s.io / apimachinery / pkg / api / errors / errors.go
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         "fmt"
22         "net/http"
23         "reflect"
24         "strings"
25
26         metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27         "k8s.io/apimachinery/pkg/runtime"
28         "k8s.io/apimachinery/pkg/runtime/schema"
29         "k8s.io/apimachinery/pkg/util/validation/field"
30 )
31
32 const (
33         // StatusTooManyRequests means the server experienced too many requests within a
34         // given window and that the client must wait to perform the action again.
35         StatusTooManyRequests = 429
36 )
37
38 // StatusError is an error intended for consumption by a REST API server; it can also be
39 // reconstructed by clients from a REST response. Public to allow easy type switches.
40 type StatusError struct {
41         ErrStatus metav1.Status
42 }
43
44 // APIStatus is exposed by errors that can be converted to an api.Status object
45 // for finer grained details.
46 type APIStatus interface {
47         Status() metav1.Status
48 }
49
50 var _ error = &StatusError{}
51
52 // Error implements the Error interface.
53 func (e *StatusError) Error() string {
54         return e.ErrStatus.Message
55 }
56
57 // Status allows access to e's status without having to know the detailed workings
58 // of StatusError.
59 func (e *StatusError) Status() metav1.Status {
60         return e.ErrStatus
61 }
62
63 // DebugError reports extended info about the error to debug output.
64 func (e *StatusError) DebugError() (string, []interface{}) {
65         if out, err := json.MarshalIndent(e.ErrStatus, "", "  "); err == nil {
66                 return "server response object: %s", []interface{}{string(out)}
67         }
68         return "server response object: %#v", []interface{}{e.ErrStatus}
69 }
70
71 // UnexpectedObjectError can be returned by FromObject if it's passed a non-status object.
72 type UnexpectedObjectError struct {
73         Object runtime.Object
74 }
75
76 // Error returns an error message describing 'u'.
77 func (u *UnexpectedObjectError) Error() string {
78         return fmt.Sprintf("unexpected object: %v", u.Object)
79 }
80
81 // FromObject generates an StatusError from an metav1.Status, if that is the type of obj; otherwise,
82 // returns an UnexpecteObjectError.
83 func FromObject(obj runtime.Object) error {
84         switch t := obj.(type) {
85         case *metav1.Status:
86                 return &StatusError{ErrStatus: *t}
87         case runtime.Unstructured:
88                 var status metav1.Status
89                 obj := t.UnstructuredContent()
90                 if !reflect.DeepEqual(obj["kind"], "Status") {
91                         break
92                 }
93                 if err := runtime.DefaultUnstructuredConverter.FromUnstructured(t.UnstructuredContent(), &status); err != nil {
94                         return err
95                 }
96                 if status.APIVersion != "v1" && status.APIVersion != "meta.k8s.io/v1" {
97                         break
98                 }
99                 return &StatusError{ErrStatus: status}
100         }
101         return &UnexpectedObjectError{obj}
102 }
103
104 // NewNotFound returns a new error which indicates that the resource of the kind and the name was not found.
105 func NewNotFound(qualifiedResource schema.GroupResource, name string) *StatusError {
106         return &StatusError{metav1.Status{
107                 Status: metav1.StatusFailure,
108                 Code:   http.StatusNotFound,
109                 Reason: metav1.StatusReasonNotFound,
110                 Details: &metav1.StatusDetails{
111                         Group: qualifiedResource.Group,
112                         Kind:  qualifiedResource.Resource,
113                         Name:  name,
114                 },
115                 Message: fmt.Sprintf("%s %q not found", qualifiedResource.String(), name),
116         }}
117 }
118
119 // NewAlreadyExists returns an error indicating the item requested exists by that identifier.
120 func NewAlreadyExists(qualifiedResource schema.GroupResource, name string) *StatusError {
121         return &StatusError{metav1.Status{
122                 Status: metav1.StatusFailure,
123                 Code:   http.StatusConflict,
124                 Reason: metav1.StatusReasonAlreadyExists,
125                 Details: &metav1.StatusDetails{
126                         Group: qualifiedResource.Group,
127                         Kind:  qualifiedResource.Resource,
128                         Name:  name,
129                 },
130                 Message: fmt.Sprintf("%s %q already exists", qualifiedResource.String(), name),
131         }}
132 }
133
134 // NewUnauthorized returns an error indicating the client is not authorized to perform the requested
135 // action.
136 func NewUnauthorized(reason string) *StatusError {
137         message := reason
138         if len(message) == 0 {
139                 message = "not authorized"
140         }
141         return &StatusError{metav1.Status{
142                 Status:  metav1.StatusFailure,
143                 Code:    http.StatusUnauthorized,
144                 Reason:  metav1.StatusReasonUnauthorized,
145                 Message: message,
146         }}
147 }
148
149 // NewForbidden returns an error indicating the requested action was forbidden
150 func NewForbidden(qualifiedResource schema.GroupResource, name string, err error) *StatusError {
151         var message string
152         if qualifiedResource.Empty() {
153                 message = fmt.Sprintf("forbidden: %v", err)
154         } else if name == "" {
155                 message = fmt.Sprintf("%s is forbidden: %v", qualifiedResource.String(), err)
156         } else {
157                 message = fmt.Sprintf("%s %q is forbidden: %v", qualifiedResource.String(), name, err)
158         }
159         return &StatusError{metav1.Status{
160                 Status: metav1.StatusFailure,
161                 Code:   http.StatusForbidden,
162                 Reason: metav1.StatusReasonForbidden,
163                 Details: &metav1.StatusDetails{
164                         Group: qualifiedResource.Group,
165                         Kind:  qualifiedResource.Resource,
166                         Name:  name,
167                 },
168                 Message: message,
169         }}
170 }
171
172 // NewConflict returns an error indicating the item can't be updated as provided.
173 func NewConflict(qualifiedResource schema.GroupResource, name string, err error) *StatusError {
174         return &StatusError{metav1.Status{
175                 Status: metav1.StatusFailure,
176                 Code:   http.StatusConflict,
177                 Reason: metav1.StatusReasonConflict,
178                 Details: &metav1.StatusDetails{
179                         Group: qualifiedResource.Group,
180                         Kind:  qualifiedResource.Resource,
181                         Name:  name,
182                 },
183                 Message: fmt.Sprintf("Operation cannot be fulfilled on %s %q: %v", qualifiedResource.String(), name, err),
184         }}
185 }
186
187 // NewGone returns an error indicating the item no longer available at the server and no forwarding address is known.
188 func NewGone(message string) *StatusError {
189         return &StatusError{metav1.Status{
190                 Status:  metav1.StatusFailure,
191                 Code:    http.StatusGone,
192                 Reason:  metav1.StatusReasonGone,
193                 Message: message,
194         }}
195 }
196
197 // NewResourceExpired creates an error that indicates that the requested resource content has expired from
198 // the server (usually due to a resourceVersion that is too old).
199 func NewResourceExpired(message string) *StatusError {
200         return &StatusError{metav1.Status{
201                 Status:  metav1.StatusFailure,
202                 Code:    http.StatusGone,
203                 Reason:  metav1.StatusReasonExpired,
204                 Message: message,
205         }}
206 }
207
208 // NewInvalid returns an error indicating the item is invalid and cannot be processed.
209 func NewInvalid(qualifiedKind schema.GroupKind, name string, errs field.ErrorList) *StatusError {
210         causes := make([]metav1.StatusCause, 0, len(errs))
211         for i := range errs {
212                 err := errs[i]
213                 causes = append(causes, metav1.StatusCause{
214                         Type:    metav1.CauseType(err.Type),
215                         Message: err.ErrorBody(),
216                         Field:   err.Field,
217                 })
218         }
219         return &StatusError{metav1.Status{
220                 Status: metav1.StatusFailure,
221                 Code:   http.StatusUnprocessableEntity,
222                 Reason: metav1.StatusReasonInvalid,
223                 Details: &metav1.StatusDetails{
224                         Group:  qualifiedKind.Group,
225                         Kind:   qualifiedKind.Kind,
226                         Name:   name,
227                         Causes: causes,
228                 },
229                 Message: fmt.Sprintf("%s %q is invalid: %v", qualifiedKind.String(), name, errs.ToAggregate()),
230         }}
231 }
232
233 // NewBadRequest creates an error that indicates that the request is invalid and can not be processed.
234 func NewBadRequest(reason string) *StatusError {
235         return &StatusError{metav1.Status{
236                 Status:  metav1.StatusFailure,
237                 Code:    http.StatusBadRequest,
238                 Reason:  metav1.StatusReasonBadRequest,
239                 Message: reason,
240         }}
241 }
242
243 // NewTooManyRequests creates an error that indicates that the client must try again later because
244 // the specified endpoint is not accepting requests. More specific details should be provided
245 // if client should know why the failure was limited4.
246 func NewTooManyRequests(message string, retryAfterSeconds int) *StatusError {
247         return &StatusError{metav1.Status{
248                 Status:  metav1.StatusFailure,
249                 Code:    http.StatusTooManyRequests,
250                 Reason:  metav1.StatusReasonTooManyRequests,
251                 Message: message,
252                 Details: &metav1.StatusDetails{
253                         RetryAfterSeconds: int32(retryAfterSeconds),
254                 },
255         }}
256 }
257
258 // NewServiceUnavailable creates an error that indicates that the requested service is unavailable.
259 func NewServiceUnavailable(reason string) *StatusError {
260         return &StatusError{metav1.Status{
261                 Status:  metav1.StatusFailure,
262                 Code:    http.StatusServiceUnavailable,
263                 Reason:  metav1.StatusReasonServiceUnavailable,
264                 Message: reason,
265         }}
266 }
267
268 // NewMethodNotSupported returns an error indicating the requested action is not supported on this kind.
269 func NewMethodNotSupported(qualifiedResource schema.GroupResource, action string) *StatusError {
270         return &StatusError{metav1.Status{
271                 Status: metav1.StatusFailure,
272                 Code:   http.StatusMethodNotAllowed,
273                 Reason: metav1.StatusReasonMethodNotAllowed,
274                 Details: &metav1.StatusDetails{
275                         Group: qualifiedResource.Group,
276                         Kind:  qualifiedResource.Resource,
277                 },
278                 Message: fmt.Sprintf("%s is not supported on resources of kind %q", action, qualifiedResource.String()),
279         }}
280 }
281
282 // NewServerTimeout returns an error indicating the requested action could not be completed due to a
283 // transient error, and the client should try again.
284 func NewServerTimeout(qualifiedResource schema.GroupResource, operation string, retryAfterSeconds int) *StatusError {
285         return &StatusError{metav1.Status{
286                 Status: metav1.StatusFailure,
287                 Code:   http.StatusInternalServerError,
288                 Reason: metav1.StatusReasonServerTimeout,
289                 Details: &metav1.StatusDetails{
290                         Group:             qualifiedResource.Group,
291                         Kind:              qualifiedResource.Resource,
292                         Name:              operation,
293                         RetryAfterSeconds: int32(retryAfterSeconds),
294                 },
295                 Message: fmt.Sprintf("The %s operation against %s could not be completed at this time, please try again.", operation, qualifiedResource.String()),
296         }}
297 }
298
299 // NewServerTimeoutForKind should not exist.  Server timeouts happen when accessing resources, the Kind is just what we
300 // happened to be looking at when the request failed.  This delegates to keep code sane, but we should work towards removing this.
301 func NewServerTimeoutForKind(qualifiedKind schema.GroupKind, operation string, retryAfterSeconds int) *StatusError {
302         return NewServerTimeout(schema.GroupResource{Group: qualifiedKind.Group, Resource: qualifiedKind.Kind}, operation, retryAfterSeconds)
303 }
304
305 // NewInternalError returns an error indicating the item is invalid and cannot be processed.
306 func NewInternalError(err error) *StatusError {
307         return &StatusError{metav1.Status{
308                 Status: metav1.StatusFailure,
309                 Code:   http.StatusInternalServerError,
310                 Reason: metav1.StatusReasonInternalError,
311                 Details: &metav1.StatusDetails{
312                         Causes: []metav1.StatusCause{{Message: err.Error()}},
313                 },
314                 Message: fmt.Sprintf("Internal error occurred: %v", err),
315         }}
316 }
317
318 // NewTimeoutError returns an error indicating that a timeout occurred before the request
319 // could be completed.  Clients may retry, but the operation may still complete.
320 func NewTimeoutError(message string, retryAfterSeconds int) *StatusError {
321         return &StatusError{metav1.Status{
322                 Status:  metav1.StatusFailure,
323                 Code:    http.StatusGatewayTimeout,
324                 Reason:  metav1.StatusReasonTimeout,
325                 Message: fmt.Sprintf("Timeout: %s", message),
326                 Details: &metav1.StatusDetails{
327                         RetryAfterSeconds: int32(retryAfterSeconds),
328                 },
329         }}
330 }
331
332 // NewTooManyRequestsError returns an error indicating that the request was rejected because
333 // the server has received too many requests. Client should wait and retry. But if the request
334 // is perishable, then the client should not retry the request.
335 func NewTooManyRequestsError(message string) *StatusError {
336         return &StatusError{metav1.Status{
337                 Status:  metav1.StatusFailure,
338                 Code:    StatusTooManyRequests,
339                 Reason:  metav1.StatusReasonTooManyRequests,
340                 Message: fmt.Sprintf("Too many requests: %s", message),
341         }}
342 }
343
344 // NewRequestEntityTooLargeError returns an error indicating that the request
345 // entity was too large.
346 func NewRequestEntityTooLargeError(message string) *StatusError {
347         return &StatusError{metav1.Status{
348                 Status:  metav1.StatusFailure,
349                 Code:    http.StatusRequestEntityTooLarge,
350                 Reason:  metav1.StatusReasonRequestEntityTooLarge,
351                 Message: fmt.Sprintf("Request entity too large: %s", message),
352         }}
353 }
354
355 // NewGenericServerResponse returns a new error for server responses that are not in a recognizable form.
356 func NewGenericServerResponse(code int, verb string, qualifiedResource schema.GroupResource, name, serverMessage string, retryAfterSeconds int, isUnexpectedResponse bool) *StatusError {
357         reason := metav1.StatusReasonUnknown
358         message := fmt.Sprintf("the server responded with the status code %d but did not return more information", code)
359         switch code {
360         case http.StatusConflict:
361                 if verb == "POST" {
362                         reason = metav1.StatusReasonAlreadyExists
363                 } else {
364                         reason = metav1.StatusReasonConflict
365                 }
366                 message = "the server reported a conflict"
367         case http.StatusNotFound:
368                 reason = metav1.StatusReasonNotFound
369                 message = "the server could not find the requested resource"
370         case http.StatusBadRequest:
371                 reason = metav1.StatusReasonBadRequest
372                 message = "the server rejected our request for an unknown reason"
373         case http.StatusUnauthorized:
374                 reason = metav1.StatusReasonUnauthorized
375                 message = "the server has asked for the client to provide credentials"
376         case http.StatusForbidden:
377                 reason = metav1.StatusReasonForbidden
378                 // the server message has details about who is trying to perform what action.  Keep its message.
379                 message = serverMessage
380         case http.StatusNotAcceptable:
381                 reason = metav1.StatusReasonNotAcceptable
382                 // the server message has details about what types are acceptable
383                 message = serverMessage
384         case http.StatusUnsupportedMediaType:
385                 reason = metav1.StatusReasonUnsupportedMediaType
386                 // the server message has details about what types are acceptable
387                 message = serverMessage
388         case http.StatusMethodNotAllowed:
389                 reason = metav1.StatusReasonMethodNotAllowed
390                 message = "the server does not allow this method on the requested resource"
391         case http.StatusUnprocessableEntity:
392                 reason = metav1.StatusReasonInvalid
393                 message = "the server rejected our request due to an error in our request"
394         case http.StatusServiceUnavailable:
395                 reason = metav1.StatusReasonServiceUnavailable
396                 message = "the server is currently unable to handle the request"
397         case http.StatusGatewayTimeout:
398                 reason = metav1.StatusReasonTimeout
399                 message = "the server was unable to return a response in the time allotted, but may still be processing the request"
400         case http.StatusTooManyRequests:
401                 reason = metav1.StatusReasonTooManyRequests
402                 message = "the server has received too many requests and has asked us to try again later"
403         default:
404                 if code >= 500 {
405                         reason = metav1.StatusReasonInternalError
406                         message = fmt.Sprintf("an error on the server (%q) has prevented the request from succeeding", serverMessage)
407                 }
408         }
409         switch {
410         case !qualifiedResource.Empty() && len(name) > 0:
411                 message = fmt.Sprintf("%s (%s %s %s)", message, strings.ToLower(verb), qualifiedResource.String(), name)
412         case !qualifiedResource.Empty():
413                 message = fmt.Sprintf("%s (%s %s)", message, strings.ToLower(verb), qualifiedResource.String())
414         }
415         var causes []metav1.StatusCause
416         if isUnexpectedResponse {
417                 causes = []metav1.StatusCause{
418                         {
419                                 Type:    metav1.CauseTypeUnexpectedServerResponse,
420                                 Message: serverMessage,
421                         },
422                 }
423         } else {
424                 causes = nil
425         }
426         return &StatusError{metav1.Status{
427                 Status: metav1.StatusFailure,
428                 Code:   int32(code),
429                 Reason: reason,
430                 Details: &metav1.StatusDetails{
431                         Group: qualifiedResource.Group,
432                         Kind:  qualifiedResource.Resource,
433                         Name:  name,
434
435                         Causes:            causes,
436                         RetryAfterSeconds: int32(retryAfterSeconds),
437                 },
438                 Message: message,
439         }}
440 }
441
442 // IsNotFound returns true if the specified error was created by NewNotFound.
443 func IsNotFound(err error) bool {
444         return ReasonForError(err) == metav1.StatusReasonNotFound
445 }
446
447 // IsAlreadyExists determines if the err is an error which indicates that a specified resource already exists.
448 func IsAlreadyExists(err error) bool {
449         return ReasonForError(err) == metav1.StatusReasonAlreadyExists
450 }
451
452 // IsConflict determines if the err is an error which indicates the provided update conflicts.
453 func IsConflict(err error) bool {
454         return ReasonForError(err) == metav1.StatusReasonConflict
455 }
456
457 // IsInvalid determines if the err is an error which indicates the provided resource is not valid.
458 func IsInvalid(err error) bool {
459         return ReasonForError(err) == metav1.StatusReasonInvalid
460 }
461
462 // IsGone is true if the error indicates the requested resource is no longer available.
463 func IsGone(err error) bool {
464         return ReasonForError(err) == metav1.StatusReasonGone
465 }
466
467 // IsResourceExpired is true if the error indicates the resource has expired and the current action is
468 // no longer possible.
469 func IsResourceExpired(err error) bool {
470         return ReasonForError(err) == metav1.StatusReasonExpired
471 }
472
473 // IsNotAcceptable determines if err is an error which indicates that the request failed due to an invalid Accept header
474 func IsNotAcceptable(err error) bool {
475         return ReasonForError(err) == metav1.StatusReasonNotAcceptable
476 }
477
478 // IsUnsupportedMediaType determines if err is an error which indicates that the request failed due to an invalid Content-Type header
479 func IsUnsupportedMediaType(err error) bool {
480         return ReasonForError(err) == metav1.StatusReasonUnsupportedMediaType
481 }
482
483 // IsMethodNotSupported determines if the err is an error which indicates the provided action could not
484 // be performed because it is not supported by the server.
485 func IsMethodNotSupported(err error) bool {
486         return ReasonForError(err) == metav1.StatusReasonMethodNotAllowed
487 }
488
489 // IsServiceUnavailable is true if the error indicates the underlying service is no longer available.
490 func IsServiceUnavailable(err error) bool {
491         return ReasonForError(err) == metav1.StatusReasonServiceUnavailable
492 }
493
494 // IsBadRequest determines if err is an error which indicates that the request is invalid.
495 func IsBadRequest(err error) bool {
496         return ReasonForError(err) == metav1.StatusReasonBadRequest
497 }
498
499 // IsUnauthorized determines if err is an error which indicates that the request is unauthorized and
500 // requires authentication by the user.
501 func IsUnauthorized(err error) bool {
502         return ReasonForError(err) == metav1.StatusReasonUnauthorized
503 }
504
505 // IsForbidden determines if err is an error which indicates that the request is forbidden and cannot
506 // be completed as requested.
507 func IsForbidden(err error) bool {
508         return ReasonForError(err) == metav1.StatusReasonForbidden
509 }
510
511 // IsTimeout determines if err is an error which indicates that request times out due to long
512 // processing.
513 func IsTimeout(err error) bool {
514         return ReasonForError(err) == metav1.StatusReasonTimeout
515 }
516
517 // IsServerTimeout determines if err is an error which indicates that the request needs to be retried
518 // by the client.
519 func IsServerTimeout(err error) bool {
520         return ReasonForError(err) == metav1.StatusReasonServerTimeout
521 }
522
523 // IsInternalError determines if err is an error which indicates an internal server error.
524 func IsInternalError(err error) bool {
525         return ReasonForError(err) == metav1.StatusReasonInternalError
526 }
527
528 // IsTooManyRequests determines if err is an error which indicates that there are too many requests
529 // that the server cannot handle.
530 func IsTooManyRequests(err error) bool {
531         if ReasonForError(err) == metav1.StatusReasonTooManyRequests {
532                 return true
533         }
534         switch t := err.(type) {
535         case APIStatus:
536                 return t.Status().Code == http.StatusTooManyRequests
537         }
538         return false
539 }
540
541 // IsRequestEntityTooLargeError determines if err is an error which indicates
542 // the request entity is too large.
543 func IsRequestEntityTooLargeError(err error) bool {
544         if ReasonForError(err) == metav1.StatusReasonRequestEntityTooLarge {
545                 return true
546         }
547         switch t := err.(type) {
548         case APIStatus:
549                 return t.Status().Code == http.StatusRequestEntityTooLarge
550         }
551         return false
552 }
553
554 // IsUnexpectedServerError returns true if the server response was not in the expected API format,
555 // and may be the result of another HTTP actor.
556 func IsUnexpectedServerError(err error) bool {
557         switch t := err.(type) {
558         case APIStatus:
559                 if d := t.Status().Details; d != nil {
560                         for _, cause := range d.Causes {
561                                 if cause.Type == metav1.CauseTypeUnexpectedServerResponse {
562                                         return true
563                                 }
564                         }
565                 }
566         }
567         return false
568 }
569
570 // IsUnexpectedObjectError determines if err is due to an unexpected object from the master.
571 func IsUnexpectedObjectError(err error) bool {
572         _, ok := err.(*UnexpectedObjectError)
573         return err != nil && ok
574 }
575
576 // SuggestsClientDelay returns true if this error suggests a client delay as well as the
577 // suggested seconds to wait, or false if the error does not imply a wait. It does not
578 // address whether the error *should* be retried, since some errors (like a 3xx) may
579 // request delay without retry.
580 func SuggestsClientDelay(err error) (int, bool) {
581         switch t := err.(type) {
582         case APIStatus:
583                 if t.Status().Details != nil {
584                         switch t.Status().Reason {
585                         // this StatusReason explicitly requests the caller to delay the action
586                         case metav1.StatusReasonServerTimeout:
587                                 return int(t.Status().Details.RetryAfterSeconds), true
588                         }
589                         // If the client requests that we retry after a certain number of seconds
590                         if t.Status().Details.RetryAfterSeconds > 0 {
591                                 return int(t.Status().Details.RetryAfterSeconds), true
592                         }
593                 }
594         }
595         return 0, false
596 }
597
598 // ReasonForError returns the HTTP status for a particular error.
599 func ReasonForError(err error) metav1.StatusReason {
600         switch t := err.(type) {
601         case APIStatus:
602                 return t.Status().Reason
603         }
604         return metav1.StatusReasonUnknown
605 }