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 }