github.com/argoproj/argo-events@v1.9.1/common/retry.go (about) 1 /* 2 Copyright 2018 BlackRock, Inc. 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 common 18 19 import ( 20 "fmt" 21 "time" 22 23 apierr "k8s.io/apimachinery/pkg/api/errors" 24 "k8s.io/apimachinery/pkg/util/wait" 25 26 apicommon "github.com/argoproj/argo-events/pkg/apis/common" 27 ) 28 29 var ( 30 defaultFactor = apicommon.NewAmount("1.0") 31 defaultJitter = apicommon.NewAmount("1") 32 defaultDuration = apicommon.FromString("1s") 33 34 DefaultBackoff = apicommon.Backoff{ 35 Steps: 5, 36 Duration: &defaultDuration, 37 Factor: &defaultFactor, 38 Jitter: &defaultJitter, 39 } 40 ) 41 42 // IsRetryableKubeAPIError returns if the error is a retryable kubernetes error 43 func IsRetryableKubeAPIError(err error) bool { 44 // get original error if it was wrapped 45 if apierr.IsNotFound(err) || apierr.IsForbidden(err) || apierr.IsInvalid(err) || apierr.IsMethodNotSupported(err) { 46 return false 47 } 48 return true 49 } 50 51 // Convert2WaitBackoff converts to a wait backoff option 52 func Convert2WaitBackoff(backoff *apicommon.Backoff) (*wait.Backoff, error) { 53 result := wait.Backoff{} 54 55 d := backoff.Duration 56 if d == nil { 57 d = &defaultDuration 58 } 59 if d.Type == apicommon.Int64 { 60 result.Duration = time.Duration(d.Int64Value()) 61 } else { 62 parsedDuration, err := time.ParseDuration(d.StrVal) 63 if err != nil { 64 return nil, err 65 } 66 result.Duration = parsedDuration 67 } 68 69 factor := backoff.Factor 70 if factor == nil { 71 factor = &defaultFactor 72 } 73 f, err := factor.Float64() 74 if err != nil { 75 return nil, fmt.Errorf("invalid factor, %w", err) 76 } 77 result.Factor = f 78 79 jitter := backoff.Jitter 80 if jitter == nil { 81 jitter = &defaultJitter 82 } 83 j, err := jitter.Float64() 84 if err != nil { 85 return nil, fmt.Errorf("invalid jitter, %w", err) 86 } 87 result.Jitter = j 88 89 if backoff.Steps > 0 { 90 result.Steps = backoff.GetSteps() 91 } else { 92 result.Steps = int(DefaultBackoff.Steps) 93 } 94 return &result, nil 95 } 96 97 func DoWithRetry(backoff *apicommon.Backoff, f func() error) error { 98 if backoff == nil { 99 backoff = &DefaultBackoff 100 } 101 b, err := Convert2WaitBackoff(backoff) 102 if err != nil { 103 return fmt.Errorf("invalid backoff configuration, %w", err) 104 } 105 _ = wait.ExponentialBackoff(*b, func() (bool, error) { 106 if err = f(); err != nil { 107 return false, nil 108 } 109 return true, nil 110 }) 111 if err != nil { 112 return fmt.Errorf("failed after retries: %w", err) 113 } 114 return nil 115 }