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