github.com/vmware/govmomi@v0.51.0/vim25/retry.go (about) 1 // © Broadcom. All Rights Reserved. 2 // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. 3 // SPDX-License-Identifier: Apache-2.0 4 5 package vim25 6 7 import ( 8 "context" 9 "time" 10 11 "github.com/vmware/govmomi/vim25/soap" 12 ) 13 14 type RetryFunc func(err error) (retry bool, delay time.Duration) 15 16 // TemporaryNetworkError is deprecated. Use Retry() with RetryTemporaryNetworkError and retryAttempts instead. 17 func TemporaryNetworkError(n int) RetryFunc { 18 return func(err error) (bool, time.Duration) { 19 if IsTemporaryNetworkError(err) { 20 // Don't retry if we're out of tries. 21 if n--; n <= 0 { 22 return false, 0 23 } 24 return true, 0 25 } 26 return false, 0 27 } 28 } 29 30 // RetryTemporaryNetworkError returns a RetryFunc that returns IsTemporaryNetworkError(err) 31 func RetryTemporaryNetworkError(err error) (bool, time.Duration) { 32 return IsTemporaryNetworkError(err), 0 33 } 34 35 // IsTemporaryNetworkError returns false unless the error implements 36 // a Temporary() bool method such as url.Error and net.Error. 37 // Otherwise, returns the value of the Temporary() method. 38 func IsTemporaryNetworkError(err error) bool { 39 t, ok := err.(interface { 40 // Temporary is implemented by url.Error and net.Error 41 Temporary() bool 42 }) 43 44 if !ok { 45 // Not a Temporary error. 46 return false 47 } 48 49 return t.Temporary() 50 } 51 52 type retry struct { 53 roundTripper soap.RoundTripper 54 55 // fn is a custom function that is called when an error occurs. 56 // It returns whether or not to retry, and if so, how long to 57 // delay before retrying. 58 fn RetryFunc 59 maxRetryAttempts int 60 } 61 62 // Retry wraps the specified soap.RoundTripper and invokes the 63 // specified RetryFunc. The RetryFunc returns whether or not to 64 // retry the call, and if so, how long to wait before retrying. If 65 // the result of this function is to not retry, the original error 66 // is returned from the RoundTrip function. 67 // The soap.RoundTripper will return the original error if retryAttempts is specified and reached. 68 func Retry(roundTripper soap.RoundTripper, fn RetryFunc, retryAttempts ...int) soap.RoundTripper { 69 r := &retry{ 70 roundTripper: roundTripper, 71 fn: fn, 72 maxRetryAttempts: 1, 73 } 74 75 if len(retryAttempts) == 1 { 76 r.maxRetryAttempts = retryAttempts[0] 77 } 78 79 return r 80 } 81 82 func (r *retry) RoundTrip(ctx context.Context, req, res soap.HasFault) error { 83 var err error 84 85 for attempt := 0; attempt < r.maxRetryAttempts; attempt++ { 86 err = r.roundTripper.RoundTrip(ctx, req, res) 87 if err == nil { 88 break 89 } 90 91 // Invoke retry function to see if another attempt should be made. 92 if retry, delay := r.fn(err); retry { 93 time.Sleep(delay) 94 continue 95 } 96 97 break 98 } 99 100 return err 101 }