github.com/kyma-project/kyma-environment-broker@v0.0.1/internal/error/errors.go (about)

     1  package error
     2  
     3  import (
     4  	"strings"
     5  
     6  	"errors"
     7  
     8  	gcli "github.com/kyma-project/kyma-environment-broker/internal/third_party/machinebox/graphql"
     9  	apierr "k8s.io/apimachinery/pkg/api/errors"
    10  	apierr2 "k8s.io/apimachinery/pkg/api/meta"
    11  )
    12  
    13  const OperationTimeOutMsg string = "operation has reached the time limit"
    14  
    15  // error reporter
    16  type LastError struct {
    17  	message   string
    18  	reason    ErrReason
    19  	component ErrComponent
    20  }
    21  
    22  type ErrorReporter interface {
    23  	error
    24  	Reason() ErrReason
    25  	Component() ErrComponent
    26  }
    27  
    28  type ErrReason string
    29  
    30  const (
    31  	ErrKEBInternal              ErrReason = "err_keb_internal"
    32  	ErrKEBTimeOut               ErrReason = "err_keb_timeout"
    33  	ErrProvisionerNilLastError  ErrReason = "err_provisioner_nil_last_error"
    34  	ErrHttpStatusCode           ErrReason = "err_http_status_code"
    35  	ErrReconcilerNilFailures    ErrReason = "err_reconciler_nil_failures"
    36  	ErrClusterNotFound          ErrReason = "err_cluster_not_found"
    37  	ErrK8SUnexpectedServerError ErrReason = "err_k8s_unexpected_server_error"
    38  	ErrK8SUnexpectedObjectError ErrReason = "err_k8s_unexpected_object_error"
    39  	ErrK8SNoMatchError          ErrReason = "err_k8s_no_match_error"
    40  	ErrK8SAmbiguousError        ErrReason = "err_k8s_ambiguous_error"
    41  )
    42  
    43  type ErrComponent string
    44  
    45  const (
    46  	ErrDB          ErrComponent = "db - keb"
    47  	ErrK8SClient   ErrComponent = "k8s client - keb"
    48  	ErrKEB         ErrComponent = "keb"
    49  	ErrEDP         ErrComponent = "edp"
    50  	ErrProvisioner ErrComponent = "provisioner"
    51  	ErrReconciler  ErrComponent = "reconciler"
    52  	ErrAVS         ErrComponent = "avs"
    53  )
    54  
    55  func (err LastError) Reason() ErrReason {
    56  	return err.reason
    57  }
    58  
    59  func (err LastError) Component() ErrComponent {
    60  	return err.component
    61  }
    62  
    63  func (err LastError) Error() string {
    64  	return err.message
    65  }
    66  
    67  func (err LastError) SetComponent(component ErrComponent) LastError {
    68  	err.component = component
    69  	return err
    70  }
    71  
    72  func (err LastError) SetReason(reason ErrReason) LastError {
    73  	err.reason = reason
    74  	return err
    75  }
    76  
    77  func (err LastError) SetMessage(msg string) LastError {
    78  	err.message = msg
    79  	return err
    80  }
    81  
    82  func TimeoutError(msg string) LastError {
    83  	return LastError{
    84  		message:   msg,
    85  		reason:    ErrKEBTimeOut,
    86  		component: ErrKEB,
    87  	}
    88  }
    89  
    90  // resolve error component and reason
    91  func ReasonForError(err error) LastError {
    92  	if err == nil {
    93  		return LastError{}
    94  	}
    95  
    96  	cause := UnwrapAll(err)
    97  
    98  	if lastErr := checkK8SError(cause); lastErr.component == ErrK8SClient {
    99  		lastErr.message = err.Error()
   100  		return lastErr
   101  	}
   102  
   103  	if status := ErrorReporter(nil); errors.As(cause, &status) {
   104  		return LastError{
   105  			message:   err.Error(),
   106  			reason:    status.Reason(),
   107  			component: status.Component(),
   108  		}
   109  	}
   110  
   111  	if ee, ok := cause.(gcli.ExtendedError); ok {
   112  		var errReason ErrReason
   113  		var errComponent ErrComponent
   114  
   115  		reason, found := ee.Extensions()["error_reason"]
   116  		if found {
   117  			if r, ok := reason.(string); ok {
   118  				errReason = ErrReason(r)
   119  			}
   120  		}
   121  		component, found := ee.Extensions()["error_component"]
   122  		if found {
   123  			if c, ok := component.(string); ok {
   124  				errComponent = ErrComponent(c)
   125  			}
   126  		}
   127  
   128  		return LastError{
   129  			message:   err.Error(),
   130  			reason:    errReason,
   131  			component: errComponent,
   132  		}
   133  	}
   134  
   135  	if strings.Contains(err.Error(), OperationTimeOutMsg) {
   136  		return TimeoutError(err.Error())
   137  	}
   138  
   139  	return LastError{
   140  		message:   err.Error(),
   141  		reason:    ErrKEBInternal,
   142  		component: ErrKEB,
   143  	}
   144  }
   145  
   146  func checkK8SError(cause error) LastError {
   147  	lastErr := LastError{}
   148  	status := apierr.APIStatus(nil)
   149  
   150  	switch {
   151  	case errors.As(cause, &status):
   152  		if apierr.IsUnexpectedServerError(cause) {
   153  			lastErr.reason = ErrK8SUnexpectedServerError
   154  		} else {
   155  			// reason could be an empty unknown ""
   156  			lastErr.reason = ErrReason(apierr.ReasonForError(cause))
   157  		}
   158  		lastErr.component = ErrK8SClient
   159  		return lastErr
   160  	case apierr.IsUnexpectedObjectError(cause):
   161  		lastErr.reason = ErrK8SUnexpectedObjectError
   162  	case apierr2.IsAmbiguousError(cause):
   163  		lastErr.reason = ErrK8SAmbiguousError
   164  	case apierr2.IsNoMatchError(cause):
   165  		lastErr.reason = ErrK8SNoMatchError
   166  	}
   167  
   168  	if lastErr.reason != "" {
   169  		lastErr.component = ErrK8SClient
   170  	}
   171  
   172  	return lastErr
   173  }
   174  
   175  // UnwrapOnce accesses the direct cause of the error if any, otherwise
   176  // returns nil.
   177  func UnwrapOnce(err error) (cause error) {
   178  	switch e := err.(type) {
   179  	case interface{ Unwrap() error }:
   180  		return e.Unwrap()
   181  	}
   182  	return nil
   183  }
   184  
   185  // UnwrapAll accesses the root cause object of the error.
   186  // If the error has no cause (leaf error), it is returned directly.
   187  // this is a replacement for github.com/pkg/errors.Cause
   188  func UnwrapAll(err error) error {
   189  	for {
   190  		if cause := UnwrapOnce(err); cause != nil {
   191  			err = cause
   192  			continue
   193  		}
   194  		break
   195  	}
   196  	return err
   197  }