-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathretry_until.go
129 lines (106 loc) · 3.03 KB
/
retry_until.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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package core
import (
"context"
"encoding/json"
"errors"
"fmt"
"time"
"github.com/MagaluCloud/magalu/mgc/core/utils"
)
var ErrorResultHasNoValue = errors.New("result has no value")
type RetryUntilCheck func(ctx context.Context, value Value) (finished bool, err error)
type RetryUntilConfig struct {
MaxRetries int `json:"maxRetries,omitempty"`
Interval time.Duration `json:"interval,omitempty"`
JSONPathQuery string `json:"jsonPathQuery,omitempty"`
TemplateQuery string `json:"templateQuery,omitempty"`
}
func (c *RetryUntilConfig) Build() (r *RetryUntil, err error) {
if c == nil {
return nil, nil
}
var check RetryUntilCheck
if c.JSONPathQuery != "" && c.TemplateQuery != "" {
err = errors.New("cannot specify both jsonPathQuery and templateQuery")
} else if c.JSONPathQuery != "" {
check, err = NewRetryUntilCheckFromJsonPath(c.JSONPathQuery)
} else if c.TemplateQuery != "" {
check, err = NewRetryUntilCheckFromTemplate(c.TemplateQuery)
} else {
err = errors.New("need one of jsonPathQuery or templateQuery")
}
if err != nil {
return nil, err
}
return &RetryUntil{
MaxRetries: c.MaxRetries,
Interval: c.Interval,
Check: check,
}, nil
}
func (c *RetryUntilConfig) UnmarshalJSON(data []byte) (err error) {
m := map[string]any{}
err = json.Unmarshal(data, &m) // decoding interval to time.Duration would fail
if err != nil {
return
}
return utils.DecodeValue(m, c) // nicely decodes time.Duration
}
var _ json.Unmarshaler = (*RetryUntilConfig)(nil)
type RetryUntil struct {
MaxRetries int
Interval time.Duration
Check RetryUntilCheck
}
type RetryUntilCb func() (result Result, err error)
func (r *RetryUntil) Run(ctx context.Context, cb RetryUntilCb) (result Result, err error) {
if r == nil {
return cb()
}
for i := 0; i < r.MaxRetries; i++ {
result, err = cb()
if err != nil {
return result, err
}
resultWithValue, ok := ResultAs[ResultWithValue](result)
if !ok {
return result, ErrorResultHasNoValue
}
finished, err := r.Check(ctx, resultWithValue.Value())
if err != nil {
return result, err
}
if finished {
return result, nil
}
timer := time.NewTimer(r.Interval)
select {
case <-ctx.Done():
timer.Stop()
return nil, ctx.Err()
case <-timer.C:
}
}
msg := fmt.Sprintf("exceeded maximum retries %d with interval %s", r.MaxRetries, r.Interval)
return nil, FailedTerminationError{Result: result, Message: msg}
}
func NewRetryUntilCheckFromJsonPath(expression string) (check RetryUntilCheck, err error) {
jpChecker, err := utils.CreateJsonPathChecker(expression)
if err != nil {
return nil, err
}
check = func(ctx context.Context, value Value) (finished bool, err error) {
return jpChecker(value)
}
return
}
func NewRetryUntilCheckFromTemplate(expression string) (check RetryUntilCheck, err error) {
tmplChecker, err := utils.CreateTemplateChecker(expression)
if err != nil {
return nil, err
}
check = func(ctx context.Context, value Value) (finished bool, err error) {
return tmplChecker(value)
}
return
}