77 "net/http"
88 "net/url"
99 "regexp"
10+ "slices"
1011
1112 "github.com/hashicorp/go-retryablehttp"
1213)
1819 notTrustedErrorRe = regexp .MustCompile (`certificate is not trusted` )
1920)
2021
21- func NewRetryClient (retriesCfg RetriesCfg , transport * http.Transport ) * http.Client {
22+ func NewRetryClient (retriesCfg RetriesCfg , transport * http.Transport , checkRetry func ( ctx context. Context , resp * http. Response , err error ) ( bool , error ) ) * http.Client {
2223 retryClient := retryablehttp .NewClient ()
2324 if transport != nil {
2425 retryClient .HTTPClient .Transport = transport
@@ -27,19 +28,29 @@ func NewRetryClient(retriesCfg RetriesCfg, transport *http.Transport) *http.Clie
2728 retryClient .RetryMax = int (retriesCfg .MaxAttempts )
2829 retryClient .RetryWaitMin = retriesCfg .MinWaitInterval
2930 retryClient .RetryWaitMax = retriesCfg .MaxWaitInterval
30- retryClient .CheckRetry = lakectlRetryPolicy
31+ retryClient .CheckRetry = checkRetry
3132 return retryClient .StandardClient ()
3233}
3334
34- // lakectl retry policy - we retry in the following cases:
35- // HTTP status 429 - too many requests
36- // HTTP status 500 - internal server error - could be recoverable
37- // HTTP status 503 - service unavailable
38- // We retry on all client transport errors except for:
39- // - too many redirects
35+ // ShouldRetry checks if we should retry on this (HTTP) status.
36+ type ShouldRetryer []int
37+
38+ func (s ShouldRetryer ) Retry (status int ) bool {
39+ return slices .Contains (s , status )
40+ }
41+
42+ // MakeRetryPolicy makes a retry policy.
43+ //
44+ // - It will _never_ retry on these unrecoverable errors:
45+ //
46+ // - a context error (typically canceled or deadline exceeded);
4047// - invalid http scheme/protocol
41- // - TLS cert verification failure
42- func lakectlRetryPolicy (ctx context.Context , resp * http.Response , err error ) (bool , error ) {
48+ // - TLS cert validation failure
49+ //
50+ // - Any other error is retriable.
51+ //
52+ // - When there is no error, it will retry if should says to retry this status.
53+ func CheckRetry (ctx context.Context , resp * http.Response , err error , should ShouldRetryer ) (bool , error ) {
4354 // do not retry on context.Canceled or context.DeadlineExceeded
4455 if ctx .Err () != nil {
4556 return false , ctx .Err ()
@@ -58,16 +69,29 @@ func lakectlRetryPolicy(ctx context.Context, resp *http.Response, err error) (bo
5869 return false , errors .Unwrap (v )
5970 }
6071 }
61- // The stblib http.Client wraps the above errors in a url.Error
72+ // The standard http.Client wraps the above errors in a url.Error
6273 // They aren't retryable. Other errors are retryable.
6374 return true , nil
6475 }
6576
6677 // handle HTTP response status code
67- if resp .StatusCode == http .StatusTooManyRequests ||
68- resp .StatusCode == http .StatusInternalServerError ||
69- resp .StatusCode == http .StatusServiceUnavailable {
70- return true , nil
71- }
72- return false , nil
78+ return should .Retry (resp .StatusCode ), nil
79+ }
80+
81+ // lakectl retry policy - we retry in the following cases:
82+ // HTTP status 429 - too many requests
83+ // HTTP status 500 - internal server error - could be recoverable
84+ // HTTP status 503 - service unavailable
85+ // We retry on all client transport errors except for:
86+ // - too many redirects
87+ // - invalid http scheme/protocol
88+ // - TLS cert verification failure
89+ func lakectlRetryPolicy (ctx context.Context , resp * http.Response , err error ) (bool , error ) {
90+ return CheckRetry (ctx , resp , err , lakectlDefaultRetryStatuses )
91+ }
92+
93+ var lakectlDefaultRetryStatuses = ShouldRetryer {
94+ http .StatusTooManyRequests ,
95+ http .StatusInternalServerError ,
96+ http .StatusServiceUnavailable ,
7397}
0 commit comments