sigs.k8s.io/cluster-api-provider-aws@v1.5.5/pkg/cloud/services/wait/wait.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 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 wait 18 19 import ( 20 "time" 21 22 "github.com/pkg/errors" 23 "k8s.io/apimachinery/pkg/util/wait" 24 25 "sigs.k8s.io/cluster-api-provider-aws/pkg/cloud/awserrors" 26 ) 27 28 /* 29 Ideally, this entire file would be replaced with returning a retryable 30 error and letting the actuator requeue deletion. Unfortunately, since 31 the retry behaviour is not tunable, with a max retry limit of 10, we 32 implement waits manually here. 33 */ 34 35 // NewBackoff creates a new API Machinery backoff parameter set suitable 36 // for use with AWS services. 37 func NewBackoff() wait.Backoff { 38 // Return a exponential backoff configuration which 39 // returns durations for a total time of ~5m. 40 // Example: 1s, 1.7s, 2.9s, 5s, 8.6s, 14.6s, 25s, 42.8s, 73.1s ... 125s 41 // Jitter is added as a random fraction of the duration multiplied by the 42 // jitter factor. 43 return wait.Backoff{ 44 Duration: time.Second, 45 Factor: 1.71, 46 Steps: 10, 47 Jitter: 0.4, 48 } 49 } 50 51 // WaitForWithRetryable repeats a condition check with exponential backoff. 52 func WaitForWithRetryable(backoff wait.Backoff, condition wait.ConditionFunc, retryableErrors ...string) error { 53 var errToReturn error 54 waitErr := wait.ExponentialBackoff(backoff, func() (bool, error) { 55 // clear errToReturn value from previous iteration 56 errToReturn = nil 57 58 ok, err := condition() 59 if ok { 60 // All done! 61 return true, nil 62 } 63 if err == nil { 64 // Not done, but no error, so keep waiting. 65 return false, nil 66 } 67 68 // If the returned error isn't empty, check if the error is a retryable one, 69 // or return immediately. 70 code, ok := awserrors.Code(errors.Cause(err)) 71 if !ok { 72 return false, err 73 } 74 75 for _, r := range retryableErrors { 76 if code == r { 77 // We should retry. 78 errToReturn = err 79 return false, nil 80 } 81 } 82 83 // Got an error that we can't retry, so return it. 84 return false, err 85 }) 86 87 // If the waitError is not a timeout error (nil or a non-retryable error), return it 88 if !errors.Is(waitErr, wait.ErrWaitTimeout) { 89 return waitErr 90 } 91 92 // A retryable error occurred, return the actual error 93 if errToReturn != nil { 94 return errToReturn 95 } 96 97 // The error was timeout error 98 return waitErr 99 }