]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package resource |
2 | ||
3 | import ( | |
4 | "sync" | |
5 | "time" | |
6 | ) | |
7 | ||
8 | // Retry is a basic wrapper around StateChangeConf that will just retry | |
9 | // a function until it no longer returns an error. | |
10 | func Retry(timeout time.Duration, f RetryFunc) error { | |
11 | // These are used to pull the error out of the function; need a mutex to | |
12 | // avoid a data race. | |
13 | var resultErr error | |
14 | var resultErrMu sync.Mutex | |
15 | ||
16 | c := &StateChangeConf{ | |
17 | Pending: []string{"retryableerror"}, | |
18 | Target: []string{"success"}, | |
19 | Timeout: timeout, | |
20 | MinTimeout: 500 * time.Millisecond, | |
21 | Refresh: func() (interface{}, string, error) { | |
22 | rerr := f() | |
23 | ||
24 | resultErrMu.Lock() | |
25 | defer resultErrMu.Unlock() | |
26 | ||
27 | if rerr == nil { | |
28 | resultErr = nil | |
29 | return 42, "success", nil | |
30 | } | |
31 | ||
32 | resultErr = rerr.Err | |
33 | ||
34 | if rerr.Retryable { | |
35 | return 42, "retryableerror", nil | |
36 | } | |
37 | return nil, "quit", rerr.Err | |
38 | }, | |
39 | } | |
40 | ||
41 | _, waitErr := c.WaitForState() | |
42 | ||
43 | // Need to acquire the lock here to be able to avoid race using resultErr as | |
44 | // the return value | |
45 | resultErrMu.Lock() | |
46 | defer resultErrMu.Unlock() | |
47 | ||
48 | // resultErr may be nil because the wait timed out and resultErr was never | |
49 | // set; this is still an error | |
50 | if resultErr == nil { | |
51 | return waitErr | |
52 | } | |
53 | // resultErr takes precedence over waitErr if both are set because it is | |
54 | // more likely to be useful | |
55 | return resultErr | |
56 | } | |
57 | ||
58 | // RetryFunc is the function retried until it succeeds. | |
59 | type RetryFunc func() *RetryError | |
60 | ||
61 | // RetryError is the required return type of RetryFunc. It forces client code | |
62 | // to choose whether or not a given error is retryable. | |
63 | type RetryError struct { | |
64 | Err error | |
65 | Retryable bool | |
66 | } | |
67 | ||
68 | // RetryableError is a helper to create a RetryError that's retryable from a | |
69 | // given error. | |
70 | func RetryableError(err error) *RetryError { | |
71 | if err == nil { | |
72 | return nil | |
73 | } | |
74 | return &RetryError{Err: err, Retryable: true} | |
75 | } | |
76 | ||
77 | // NonRetryableError is a helper to create a RetryError that's _not)_ retryable | |
78 | // from a given error. | |
79 | func NonRetryableError(err error) *RetryError { | |
80 | if err == nil { | |
81 | return nil | |
82 | } | |
83 | return &RetryError{Err: err, Retryable: false} | |
84 | } |