github.com/verrazzano/verrazzano@v1.7.0/pkg/controller/errors/errors.go (about) 1 // Copyright (c) 2021, 2023, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package spi 5 6 import ( 7 "fmt" 8 "strings" 9 10 controllerruntime "sigs.k8s.io/controller-runtime" 11 ) 12 13 // RetryableError an error that can be used to indicate to a controller that a requeue is needed, with an optional custom result 14 type RetryableError struct { 15 // The source of the error 16 Source string 17 // The operation type 18 Operation string 19 // The root cause error 20 Cause error 21 // An optional Result type to return to the controllerruntime 22 Result controllerruntime.Result 23 } 24 25 // HasCause indicates whether or not the error has a root cause 26 func (r RetryableError) HasCause() bool { 27 return r.Cause != nil 28 } 29 30 var _ error = RetryableError{} 31 32 // Error implements the basic Go error contract 33 func (r RetryableError) Error() string { 34 var builder strings.Builder 35 builder.WriteString("Retryable error") 36 if r.Source != "" { 37 builder.WriteString(fmt.Sprintf(", source: %s", r.Source)) 38 } 39 if r.Operation != "" { 40 builder.WriteString(fmt.Sprintf(", operation: %s", r.Operation)) 41 } 42 if r.Cause != nil { 43 builder.WriteString(fmt.Sprintf(", cause: %s", r.Cause)) 44 } 45 if !r.Result.IsZero() { 46 builder.WriteString(fmt.Sprintf(", result: {requeue: %v, requeueAfter: %s}", r.Result.Requeue, r.Result.RequeueAfter)) 47 } 48 return builder.String() 49 } 50 51 // IsRetryableError returns true if the error is a RetryableError. 52 func IsRetryableError(err error) bool { 53 _, ok := err.(RetryableError) 54 return ok 55 } 56 57 // IsUpdateConflict returns true if the error is an update conflict error. This is occurs when the controller-runtime cache 58 // is out of sync with the etcd database 59 func IsUpdateConflict(err error) bool { 60 return strings.Contains(err.Error(), "the object has been modified; please apply your changes to the latest version") 61 } 62 63 // ShouldLogKubernetesAPIError returns true if error should be logged. This is used 64 // when calling the Kubernetes API, so conflict and webhook 65 // errors are not logged, the controller will just retry. 66 func ShouldLogKubernetesAPIError(err error) bool { 67 if err == nil { 68 return false 69 } 70 if IsUpdateConflict(err) || IsRetryableError(err) { 71 return false 72 } 73 return true 74 } 75 76 // IsK8sAPIServerError returns false if the error is not due to Kubernetes API server load or too many requests rate limit. 77 // The goal is to reduce log noise in platform operator and provide meaningful information 78 func IsK8sAPIServerError(err error) (bool, string) { 79 if err == nil { 80 return false, "" 81 } 82 var message string 83 if strings.Contains(err.Error(), "the server is currently unable to handle the request") { 84 message = "KubernetesAPIServerError: the server is currently unable to handle the request" 85 return true, message 86 } 87 88 if strings.Contains(err.Error(), "too many requests and has asked us to try again") { 89 message = "KubernetesAPIServerError: the server has received too many requests and has asked us to try again later" 90 return true, message 91 } 92 93 return false, message 94 }