forked from GoogleCloudPlatform/magic-modules
/
retry_utils.go
104 lines (91 loc) · 2.43 KB
/
retry_utils.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package transport
import (
"log"
"time"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)
type RetryOptions struct {
RetryFunc func() error
Timeout time.Duration
PollInterval time.Duration
ErrorRetryPredicates []RetryErrorPredicateFunc
ErrorAbortPredicates []RetryErrorPredicateFunc
}
func Retry(opt RetryOptions) error {
if opt.Timeout == 0 {
opt.Timeout = 1 * time.Minute
}
if opt.PollInterval != 0 {
refreshFunc := func() (interface{}, string, error) {
err := opt.RetryFunc()
if err == nil {
return "", "done", nil
}
// Check if it is a retryable error.
if IsRetryableError(err, opt.ErrorRetryPredicates, opt.ErrorAbortPredicates) {
return "", "retrying", nil
}
// The error is not retryable.
return "", "done", err
}
stateChange := &resource.StateChangeConf{
Pending: []string{
"retrying",
},
Target: []string{
"done",
},
Refresh: refreshFunc,
Timeout: opt.Timeout,
PollInterval: opt.PollInterval,
}
_, err := stateChange.WaitForState()
return err
}
return resource.Retry(opt.Timeout, func() *resource.RetryError {
err := opt.RetryFunc()
if err == nil {
return nil
}
if IsRetryableError(err, opt.ErrorRetryPredicates, opt.ErrorAbortPredicates) {
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
})
}
func IsRetryableError(topErr error, retryPredicates, abortPredicates []RetryErrorPredicateFunc) bool {
if topErr == nil {
return false
}
retryPredicates = append(
// Global error retry predicates are registered in this default list.
defaultErrorRetryPredicates,
retryPredicates...)
// Check all wrapped errors for an abortable error status.
isAbortable := false
errwrap.Walk(topErr, func(werr error) {
for _, pred := range abortPredicates {
if predAbort, predReason := pred(werr); predAbort {
log.Printf("[DEBUG] Dismissed an error as abortable. %s - %s", predReason, werr)
isAbortable = true
return
}
}
})
if isAbortable {
return false
}
// Check all wrapped errors for a retryable error status.
isRetryable := false
errwrap.Walk(topErr, func(werr error) {
for _, pred := range retryPredicates {
if predRetry, predReason := pred(werr); predRetry {
log.Printf("[DEBUG] Dismissed an error as retryable. %s - %s", predReason, werr)
isRetryable = true
return
}
}
})
return isRetryable
}