sigs.k8s.io/cluster-api-provider-azure@v1.17.0/azure/errors.go (about) 1 /* 2 Copyright 2019 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 azure 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "net/http" 24 "time" 25 26 "github.com/Azure/azure-sdk-for-go/sdk/azcore" 27 infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" 28 ) 29 30 // ResourceNotFound parses an error to check if its status code is Not Found (404). 31 func ResourceNotFound(err error) bool { 32 var rerr *azcore.ResponseError 33 return errors.As(err, &rerr) && rerr.StatusCode == http.StatusNotFound 34 } 35 36 // VMDeletedError is returned when a virtual machine is deleted outside of capz. 37 type VMDeletedError struct { 38 ProviderID string 39 } 40 41 // Error returns the error string. 42 func (vde VMDeletedError) Error() string { 43 return fmt.Sprintf("VM with provider id %q has been deleted", vde.ProviderID) 44 } 45 46 // ReconcileError represents an error that is not automatically recoverable 47 // errorType indicates what type of action is required to recover. It can take two values: 48 // 1. `Transient` - Can be recovered through manual intervention, will be requeued after. 49 // 2. `Terminal` - Cannot be recovered, will not be requeued. 50 type ReconcileError struct { 51 error 52 errorType ReconcileErrorType 53 requestAfter time.Duration 54 } 55 56 // ReconcileErrorType represents the type of a ReconcileError. 57 type ReconcileErrorType string 58 59 const ( 60 // TransientErrorType can be recovered, will be requeued after a configured time interval. 61 TransientErrorType ReconcileErrorType = "Transient" 62 // TerminalErrorType cannot be recovered, will not be requeued. 63 TerminalErrorType ReconcileErrorType = "Terminal" 64 ) 65 66 // Error returns the error message for a ReconcileError. 67 func (t ReconcileError) Error() string { 68 var errStr string 69 if t.error != nil { 70 errStr = t.error.Error() 71 } 72 switch t.errorType { 73 case TransientErrorType: 74 return fmt.Sprintf("%s. Object will be requeued after %s", errStr, t.requestAfter.String()) 75 case TerminalErrorType: 76 return fmt.Sprintf("reconcile error that cannot be recovered occurred: %s. Object will not be requeued", errStr) 77 default: 78 return fmt.Sprintf("reconcile error occurred with unknown recovery type. The actual error is: %s", errStr) 79 } 80 } 81 82 // IsTransient returns if the ReconcileError is recoverable. 83 func (t ReconcileError) IsTransient() bool { 84 return t.errorType == TransientErrorType 85 } 86 87 // IsTerminal returns if the ReconcileError is recoverable. 88 func (t ReconcileError) IsTerminal() bool { 89 return t.errorType == TerminalErrorType 90 } 91 92 // Is returns true if the target is a ReconcileError. 93 func (t ReconcileError) Is(target error) bool { 94 return errors.As(target, &ReconcileError{}) 95 } 96 97 // RequeueAfter returns requestAfter value. 98 func (t ReconcileError) RequeueAfter() time.Duration { 99 return t.requestAfter 100 } 101 102 // WithTransientError wraps the error in a ReconcileError with errorType as `Transient`. 103 func WithTransientError(err error, requeueAfter time.Duration) ReconcileError { 104 return ReconcileError{error: err, errorType: TransientErrorType, requestAfter: requeueAfter} 105 } 106 107 // WithTerminalError wraps the error in a ReconcileError with errorType as `Terminal`. 108 func WithTerminalError(err error) ReconcileError { 109 return ReconcileError{error: err, errorType: TerminalErrorType} 110 } 111 112 // OperationNotDoneError is used to represent a long-running operation that is not yet complete. 113 type OperationNotDoneError struct { 114 Future *infrav1.Future 115 } 116 117 // NewOperationNotDoneError returns a new OperationNotDoneError wrapping a Future. 118 func NewOperationNotDoneError(future *infrav1.Future) OperationNotDoneError { 119 return OperationNotDoneError{ 120 Future: future, 121 } 122 } 123 124 // Error returns the error represented as a string. 125 func (onde OperationNotDoneError) Error() string { 126 return fmt.Sprintf("operation type %s on Azure resource %s/%s is not done", onde.Future.Type, onde.Future.ResourceGroup, onde.Future.Name) 127 } 128 129 // Is returns true if the target is an OperationNotDoneError. 130 func (onde OperationNotDoneError) Is(target error) bool { 131 return IsOperationNotDoneError(target) 132 } 133 134 // IsOperationNotDoneError returns true if the target is an OperationNotDoneError. 135 func IsOperationNotDoneError(target error) bool { 136 reconcileErr := &ReconcileError{} 137 if errors.As(target, reconcileErr) { 138 return IsOperationNotDoneError(reconcileErr.error) 139 } 140 return errors.As(target, &OperationNotDoneError{}) 141 } 142 143 // IsContextDeadlineExceededOrCanceledError checks if it's a context deadline 144 // exceeded or canceled error. 145 func IsContextDeadlineExceededOrCanceledError(err error) bool { 146 if err == nil { 147 return false 148 } 149 return errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) 150 }