diff options
Diffstat (limited to 'vendor/github.com/aws/aws-sdk-go/aws/request/waiter.go')
-rw-r--r-- | vendor/github.com/aws/aws-sdk-go/aws/request/waiter.go | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/vendor/github.com/aws/aws-sdk-go/aws/request/waiter.go b/vendor/github.com/aws/aws-sdk-go/aws/request/waiter.go new file mode 100644 index 0000000..22d2f80 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/aws/request/waiter.go | |||
@@ -0,0 +1,287 @@ | |||
1 | package request | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "time" | ||
6 | |||
7 | "github.com/aws/aws-sdk-go/aws" | ||
8 | "github.com/aws/aws-sdk-go/aws/awserr" | ||
9 | "github.com/aws/aws-sdk-go/aws/awsutil" | ||
10 | ) | ||
11 | |||
12 | // WaiterResourceNotReadyErrorCode is the error code returned by a waiter when | ||
13 | // the waiter's max attempts have been exhausted. | ||
14 | const WaiterResourceNotReadyErrorCode = "ResourceNotReady" | ||
15 | |||
16 | // A WaiterOption is a function that will update the Waiter value's fields to | ||
17 | // configure the waiter. | ||
18 | type WaiterOption func(*Waiter) | ||
19 | |||
20 | // WithWaiterMaxAttempts returns the maximum number of times the waiter should | ||
21 | // attempt to check the resource for the target state. | ||
22 | func WithWaiterMaxAttempts(max int) WaiterOption { | ||
23 | return func(w *Waiter) { | ||
24 | w.MaxAttempts = max | ||
25 | } | ||
26 | } | ||
27 | |||
28 | // WaiterDelay will return a delay the waiter should pause between attempts to | ||
29 | // check the resource state. The passed in attempt is the number of times the | ||
30 | // Waiter has checked the resource state. | ||
31 | // | ||
32 | // Attempt is the number of attempts the Waiter has made checking the resource | ||
33 | // state. | ||
34 | type WaiterDelay func(attempt int) time.Duration | ||
35 | |||
36 | // ConstantWaiterDelay returns a WaiterDelay that will always return a constant | ||
37 | // delay the waiter should use between attempts. It ignores the number of | ||
38 | // attempts made. | ||
39 | func ConstantWaiterDelay(delay time.Duration) WaiterDelay { | ||
40 | return func(attempt int) time.Duration { | ||
41 | return delay | ||
42 | } | ||
43 | } | ||
44 | |||
45 | // WithWaiterDelay will set the Waiter to use the WaiterDelay passed in. | ||
46 | func WithWaiterDelay(delayer WaiterDelay) WaiterOption { | ||
47 | return func(w *Waiter) { | ||
48 | w.Delay = delayer | ||
49 | } | ||
50 | } | ||
51 | |||
52 | // WithWaiterLogger returns a waiter option to set the logger a waiter | ||
53 | // should use to log warnings and errors to. | ||
54 | func WithWaiterLogger(logger aws.Logger) WaiterOption { | ||
55 | return func(w *Waiter) { | ||
56 | w.Logger = logger | ||
57 | } | ||
58 | } | ||
59 | |||
60 | // WithWaiterRequestOptions returns a waiter option setting the request | ||
61 | // options for each request the waiter makes. Appends to waiter's request | ||
62 | // options already set. | ||
63 | func WithWaiterRequestOptions(opts ...Option) WaiterOption { | ||
64 | return func(w *Waiter) { | ||
65 | w.RequestOptions = append(w.RequestOptions, opts...) | ||
66 | } | ||
67 | } | ||
68 | |||
69 | // A Waiter provides the functionality to perform a blocking call which will | ||
70 | // wait for a resource state to be satisfied by a service. | ||
71 | // | ||
72 | // This type should not be used directly. The API operations provided in the | ||
73 | // service packages prefixed with "WaitUntil" should be used instead. | ||
74 | type Waiter struct { | ||
75 | Name string | ||
76 | Acceptors []WaiterAcceptor | ||
77 | Logger aws.Logger | ||
78 | |||
79 | MaxAttempts int | ||
80 | Delay WaiterDelay | ||
81 | |||
82 | RequestOptions []Option | ||
83 | NewRequest func([]Option) (*Request, error) | ||
84 | } | ||
85 | |||
86 | // ApplyOptions updates the waiter with the list of waiter options provided. | ||
87 | func (w *Waiter) ApplyOptions(opts ...WaiterOption) { | ||
88 | for _, fn := range opts { | ||
89 | fn(w) | ||
90 | } | ||
91 | } | ||
92 | |||
93 | // WaiterState are states the waiter uses based on WaiterAcceptor definitions | ||
94 | // to identify if the resource state the waiter is waiting on has occurred. | ||
95 | type WaiterState int | ||
96 | |||
97 | // String returns the string representation of the waiter state. | ||
98 | func (s WaiterState) String() string { | ||
99 | switch s { | ||
100 | case SuccessWaiterState: | ||
101 | return "success" | ||
102 | case FailureWaiterState: | ||
103 | return "failure" | ||
104 | case RetryWaiterState: | ||
105 | return "retry" | ||
106 | default: | ||
107 | return "unknown waiter state" | ||
108 | } | ||
109 | } | ||
110 | |||
111 | // States the waiter acceptors will use to identify target resource states. | ||
112 | const ( | ||
113 | SuccessWaiterState WaiterState = iota // waiter successful | ||
114 | FailureWaiterState // waiter failed | ||
115 | RetryWaiterState // waiter needs to be retried | ||
116 | ) | ||
117 | |||
118 | // WaiterMatchMode is the mode that the waiter will use to match the WaiterAcceptor | ||
119 | // definition's Expected attribute. | ||
120 | type WaiterMatchMode int | ||
121 | |||
122 | // Modes the waiter will use when inspecting API response to identify target | ||
123 | // resource states. | ||
124 | const ( | ||
125 | PathAllWaiterMatch WaiterMatchMode = iota // match on all paths | ||
126 | PathWaiterMatch // match on specific path | ||
127 | PathAnyWaiterMatch // match on any path | ||
128 | PathListWaiterMatch // match on list of paths | ||
129 | StatusWaiterMatch // match on status code | ||
130 | ErrorWaiterMatch // match on error | ||
131 | ) | ||
132 | |||
133 | // String returns the string representation of the waiter match mode. | ||
134 | func (m WaiterMatchMode) String() string { | ||
135 | switch m { | ||
136 | case PathAllWaiterMatch: | ||
137 | return "pathAll" | ||
138 | case PathWaiterMatch: | ||
139 | return "path" | ||
140 | case PathAnyWaiterMatch: | ||
141 | return "pathAny" | ||
142 | case PathListWaiterMatch: | ||
143 | return "pathList" | ||
144 | case StatusWaiterMatch: | ||
145 | return "status" | ||
146 | case ErrorWaiterMatch: | ||
147 | return "error" | ||
148 | default: | ||
149 | return "unknown waiter match mode" | ||
150 | } | ||
151 | } | ||
152 | |||
153 | // WaitWithContext will make requests for the API operation using NewRequest to | ||
154 | // build API requests. The request's response will be compared against the | ||
155 | // Waiter's Acceptors to determine the successful state of the resource the | ||
156 | // waiter is inspecting. | ||
157 | // | ||
158 | // The passed in context must not be nil. If it is nil a panic will occur. The | ||
159 | // Context will be used to cancel the waiter's pending requests and retry delays. | ||
160 | // Use aws.BackgroundContext if no context is available. | ||
161 | // | ||
162 | // The waiter will continue until the target state defined by the Acceptors, | ||
163 | // or the max attempts expires. | ||
164 | // | ||
165 | // Will return the WaiterResourceNotReadyErrorCode error code if the waiter's | ||
166 | // retryer ShouldRetry returns false. This normally will happen when the max | ||
167 | // wait attempts expires. | ||
168 | func (w Waiter) WaitWithContext(ctx aws.Context) error { | ||
169 | |||
170 | for attempt := 1; ; attempt++ { | ||
171 | req, err := w.NewRequest(w.RequestOptions) | ||
172 | if err != nil { | ||
173 | waiterLogf(w.Logger, "unable to create request %v", err) | ||
174 | return err | ||
175 | } | ||
176 | req.Handlers.Build.PushBack(MakeAddToUserAgentFreeFormHandler("Waiter")) | ||
177 | err = req.Send() | ||
178 | |||
179 | // See if any of the acceptors match the request's response, or error | ||
180 | for _, a := range w.Acceptors { | ||
181 | if matched, matchErr := a.match(w.Name, w.Logger, req, err); matched { | ||
182 | return matchErr | ||
183 | } | ||
184 | } | ||
185 | |||
186 | // The Waiter should only check the resource state MaxAttempts times | ||
187 | // This is here instead of in the for loop above to prevent delaying | ||
188 | // unnecessary when the waiter will not retry. | ||
189 | if attempt == w.MaxAttempts { | ||
190 | break | ||
191 | } | ||
192 | |||
193 | // Delay to wait before inspecting the resource again | ||
194 | delay := w.Delay(attempt) | ||
195 | if sleepFn := req.Config.SleepDelay; sleepFn != nil { | ||
196 | // Support SleepDelay for backwards compatibility and testing | ||
197 | sleepFn(delay) | ||
198 | } else if err := aws.SleepWithContext(ctx, delay); err != nil { | ||
199 | return awserr.New(CanceledErrorCode, "waiter context canceled", err) | ||
200 | } | ||
201 | } | ||
202 | |||
203 | return awserr.New(WaiterResourceNotReadyErrorCode, "exceeded wait attempts", nil) | ||
204 | } | ||
205 | |||
206 | // A WaiterAcceptor provides the information needed to wait for an API operation | ||
207 | // to complete. | ||
208 | type WaiterAcceptor struct { | ||
209 | State WaiterState | ||
210 | Matcher WaiterMatchMode | ||
211 | Argument string | ||
212 | Expected interface{} | ||
213 | } | ||
214 | |||
215 | // match returns if the acceptor found a match with the passed in request | ||
216 | // or error. True is returned if the acceptor made a match, error is returned | ||
217 | // if there was an error attempting to perform the match. | ||
218 | func (a *WaiterAcceptor) match(name string, l aws.Logger, req *Request, err error) (bool, error) { | ||
219 | result := false | ||
220 | var vals []interface{} | ||
221 | |||
222 | switch a.Matcher { | ||
223 | case PathAllWaiterMatch, PathWaiterMatch: | ||
224 | // Require all matches to be equal for result to match | ||
225 | vals, _ = awsutil.ValuesAtPath(req.Data, a.Argument) | ||
226 | if len(vals) == 0 { | ||
227 | break | ||
228 | } | ||
229 | result = true | ||
230 | for _, val := range vals { | ||
231 | if !awsutil.DeepEqual(val, a.Expected) { | ||
232 | result = false | ||
233 | break | ||
234 | } | ||
235 | } | ||
236 | case PathAnyWaiterMatch: | ||
237 | // Only a single match needs to equal for the result to match | ||
238 | vals, _ = awsutil.ValuesAtPath(req.Data, a.Argument) | ||
239 | for _, val := range vals { | ||
240 | if awsutil.DeepEqual(val, a.Expected) { | ||
241 | result = true | ||
242 | break | ||
243 | } | ||
244 | } | ||
245 | case PathListWaiterMatch: | ||
246 | // ignored matcher | ||
247 | case StatusWaiterMatch: | ||
248 | s := a.Expected.(int) | ||
249 | result = s == req.HTTPResponse.StatusCode | ||
250 | case ErrorWaiterMatch: | ||
251 | if aerr, ok := err.(awserr.Error); ok { | ||
252 | result = aerr.Code() == a.Expected.(string) | ||
253 | } | ||
254 | default: | ||
255 | waiterLogf(l, "WARNING: Waiter %s encountered unexpected matcher: %s", | ||
256 | name, a.Matcher) | ||
257 | } | ||
258 | |||
259 | if !result { | ||
260 | // If there was no matching result found there is nothing more to do | ||
261 | // for this response, retry the request. | ||
262 | return false, nil | ||
263 | } | ||
264 | |||
265 | switch a.State { | ||
266 | case SuccessWaiterState: | ||
267 | // waiter completed | ||
268 | return true, nil | ||
269 | case FailureWaiterState: | ||
270 | // Waiter failure state triggered | ||
271 | return true, awserr.New(WaiterResourceNotReadyErrorCode, | ||
272 | "failed waiting for successful resource state", err) | ||
273 | case RetryWaiterState: | ||
274 | // clear the error and retry the operation | ||
275 | return false, nil | ||
276 | default: | ||
277 | waiterLogf(l, "WARNING: Waiter %s encountered unexpected state: %s", | ||
278 | name, a.State) | ||
279 | return false, nil | ||
280 | } | ||
281 | } | ||
282 | |||
283 | func waiterLogf(logger aws.Logger, msg string, args ...interface{}) { | ||
284 | if logger != nil { | ||
285 | logger.Log(fmt.Sprintf(msg, args...)) | ||
286 | } | ||
287 | } | ||