...

Source file src/edge-infra.dev/pkg/k8s/runtime/controller/reconcile/recerr/errors.go

Documentation: edge-infra.dev/pkg/k8s/runtime/controller/reconcile/recerr

     1  // Package recerr implements custom controller reconciliation error types and
     2  // utilities used during reconciliaton result summarization.
     3  //
     4  // This package allows for reconcilers to propagate contextual errors from internal
     5  // functions to the top-level Reconcile function by capturing the error message
     6  // and reason (to be displayed on the status condition) and providing a helper
     7  // ([ToCondition]) for updating a condition with that state. This allows the
     8  // internal reconcile functions to avoid requiring the condition to be manipulated
     9  // on each of their own error paths without losing context (namely, the reason
    10  // for the condition change).
    11  package recerr
    12  
    13  import (
    14  	"time"
    15  
    16  	corev1 "k8s.io/api/core/v1"
    17  
    18  	"edge-infra.dev/pkg/k8s/runtime/conditions"
    19  )
    20  
    21  const EventTypeNone = "None"
    22  
    23  // Error defines the common behaviors of the reconciler errors this package
    24  // provides. This behavior may be extended by specific error implementations,
    25  // such as [Wait], which can be checked via type switches / assertions. This
    26  // interface should be used when propagating reconciliation errors through
    27  // function chains.
    28  type Error interface {
    29  	error
    30  	Unwrap() error
    31  
    32  	// ToCondition uses the errors internal state to update condition on the
    33  	// setter. It can be used to easily propagate specialized reconcile error
    34  	// state to conditions.
    35  	ToCondition(to conditions.Setter, condition string)
    36  
    37  	// TODO(aw185176): ToNotification
    38  }
    39  
    40  // Base is a generic reconciliation error that public errors should be
    41  // created from. It contains state for determining if the error should produce
    42  // a K8s event, be logged, etc. It takes in a Config struct T that is available
    43  // to processing and parsing logic for errors encountered during reconciliation.
    44  type Base[T any] struct {
    45  	// Err is the error that caused the error condition. This can be used as the
    46  	// message for the condition.
    47  	Err error
    48  
    49  	// Reason is the string that is applied to a K8s Condition's Reason field,
    50  	// it describes "why" or "when" an error would have occurred.
    51  	Reason string
    52  
    53  	// Event is the event type of an error. It is used to configure what type of
    54  	// event an error should result in.
    55  	// Valid values:
    56  	//   - EventTypeNone
    57  	//   - corev1.EventTypeNormal
    58  	//   - corev1.EventTypeWarning
    59  	Event string
    60  
    61  	// Notification controls whether or not the Event is actually pushed to the K8s
    62  	// control plane. By default corev1.EventTypeNormal is not emitted.
    63  	Notification bool
    64  
    65  	// Config is the internal error-specific configuration used during error
    66  	// processing, it should be set by public error constructors.
    67  	Config T
    68  }
    69  
    70  // Error implements error interface.
    71  func (e *Base[T]) Error() string {
    72  	return e.Err.Error()
    73  }
    74  
    75  // Unwrap returns the underlying error.
    76  func (e *Base[T]) Unwrap() error {
    77  	return e.Err
    78  }
    79  
    80  // ToCondition updates a condition on a K8s object based on the error's context,
    81  // setting the error's Reason as the condition's Reason field and the Message
    82  // to the error's text.
    83  func (e *Base[T]) ToCondition(to conditions.Setter, t string) {
    84  	conditions.MarkFalse(to, t, e.Reason, "%v", e)
    85  }
    86  
    87  // Generic is a basic reconciliation error that doesn't contain any specialized
    88  // configuration and doesn't imply specific states (such as [Wait] or [Stalled]).
    89  // It sends a warning notification by default.
    90  type Generic = Base[struct{}]
    91  
    92  // New creates a [Generic] error.
    93  func New(err error, reason string, opts ...Option) Error {
    94  	o := makeOptions(&options{}, opts...)
    95  	return &Generic{
    96  		Err:          err,
    97  		Reason:       reason,
    98  		Event:        o.eventType,
    99  		Notification: o.notification,
   100  	}
   101  }
   102  
   103  // WaitConfig contains configuration for requeue interval to wait for.
   104  type WaitConfig struct{ RequeueAfter time.Duration }
   105  
   106  // Wait represents a reconciliation error for a resource that is still waiting
   107  // on some external state with a reason, e.g., resource dependencies.
   108  //
   109  // Wait errors are not returned to the runtime and are logged explicitly.
   110  // Since this failure results in reconciliation delay, sends an informational
   111  // notification.
   112  type Wait = Base[WaitConfig]
   113  
   114  // NewWait creates a Wait error and returns it.
   115  func NewWait(err error, reason string, requeueAfter time.Duration, opts ...Option) *Wait {
   116  	o := makeOptions(&options{
   117  		eventType: corev1.EventTypeNormal, notification: true,
   118  	}, opts...)
   119  	return &Wait{
   120  		Reason:       reason,
   121  		Err:          err,
   122  		Event:        o.eventType,
   123  		Notification: o.notification,
   124  		Config:       WaitConfig{RequeueAfter: requeueAfter},
   125  	}
   126  }
   127  
   128  type stalledCfg struct{}
   129  
   130  // Stalled represents a reconciliation error for a reasource that is stalled
   131  // with a reason for stalling.
   132  //
   133  // Stalled errors are not returned to the runtime and are logged explicitly.
   134  // Since this failure requires user interaction, it sends a warning notification.
   135  type Stalled = Base[stalledCfg]
   136  
   137  // NewStalled creates a Stalled error and returns it.
   138  func NewStalled(e error, reason string, opts ...Option) *Stalled {
   139  	o := makeOptions(&options{}, opts...)
   140  	return &Stalled{
   141  		Reason:       reason,
   142  		Err:          e,
   143  		Event:        o.eventType,
   144  		Notification: o.notification,
   145  	}
   146  }
   147  

View as plain text