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  }