Skip to content

Commit

Permalink
Merge pull request #5316 from aws/fix-waitermatchbool
Browse files Browse the repository at this point in the history
add missing any/none waiter error matching
  • Loading branch information
lucix-aws authored Jul 24, 2024
2 parents 42cbf2a + d68c8e4 commit 070dd38
Show file tree
Hide file tree
Showing 3 changed files with 224 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG_PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
### SDK Enhancements

### SDK Bugs
* Add missing bool error matching.
* This enables waiters defined to match on presence/absence of errors.
13 changes: 11 additions & 2 deletions aws/request/waiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,17 @@ func (a *WaiterAcceptor) match(name string, l aws.Logger, req *Request, err erro
s := a.Expected.(int)
result = s == req.HTTPResponse.StatusCode
case ErrorWaiterMatch:
if aerr, ok := err.(awserr.Error); ok {
result = aerr.Code() == a.Expected.(string)
switch ex := a.Expected.(type) {
case string:
if aerr, ok := err.(awserr.Error); ok {
result = aerr.Code() == ex
}
case bool:
if ex {
result = err != nil
} else {
result = err == nil
}
}
default:
waiterLogf(l, "WARNING: Waiter %s encountered unexpected matcher: %s",
Expand Down
211 changes: 211 additions & 0 deletions aws/request/waiter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,217 @@ func TestWaiterError(t *testing.T) {
}
}

func TestWaiterRetryAnyError(t *testing.T) {
svc := &mockClient{Client: awstesting.NewClient(&aws.Config{
Region: aws.String("mock-region"),
})}
svc.Handlers.Send.Clear() // mock sending
svc.Handlers.Unmarshal.Clear()
svc.Handlers.UnmarshalMeta.Clear()
svc.Handlers.UnmarshalError.Clear()
svc.Handlers.ValidateResponse.Clear()

var reqNum int
results := []struct {
Out *MockOutput
Err error
}{
{ // retry
Err: awserr.New(
"MockException1", "mock exception message", nil,
),
},
{ // retry
Err: awserr.New(
"MockException2", "mock exception message", nil,
),
},
{ // success
Out: &MockOutput{
States: []*MockState{
{aws.String("running")},
{aws.String("running")},
},
},
},
{ // shouldn't happen
Out: &MockOutput{
States: []*MockState{
{aws.String("running")},
{aws.String("running")},
},
},
},
}

numBuiltReq := 0
svc.Handlers.Build.PushBack(func(r *request.Request) {
numBuiltReq++
})
svc.Handlers.Send.PushBack(func(r *request.Request) {
code := http.StatusOK
r.HTTPResponse = &http.Response{
StatusCode: code,
Status: http.StatusText(code),
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
}
})
svc.Handlers.Unmarshal.PushBack(func(r *request.Request) {
if reqNum >= len(results) {
t.Errorf("too many polling requests made")
return
}
r.Data = results[reqNum].Out
reqNum++
})
svc.Handlers.UnmarshalMeta.PushBack(func(r *request.Request) {
// If there was an error unmarshal error will be called instead of unmarshal
// need to increment count here also
if err := results[reqNum].Err; err != nil {
r.Error = err
reqNum++
}
})

w := request.Waiter{
MaxAttempts: 10,
Delay: request.ConstantWaiterDelay(0),
SleepWithContext: aws.SleepWithContext,
Acceptors: []request.WaiterAcceptor{
{
State: request.SuccessWaiterState,
Matcher: request.PathAllWaiterMatch,
Argument: "States[].State",
Expected: "running",
},
{
State: request.RetryWaiterState,
Matcher: request.ErrorWaiterMatch,
Argument: "",
Expected: true,
},
{
State: request.FailureWaiterState,
Matcher: request.ErrorWaiterMatch,
Argument: "",
Expected: "FailureException",
},
},
NewRequest: BuildNewMockRequest(svc, &MockInput{}),
}

err := w.WaitWithContext(aws.BackgroundContext())
if err != nil {
t.Fatalf("expected no error, but did get one: %v", err)
}
if e, a := 3, numBuiltReq; e != a {
t.Errorf("expect %d built requests got %d", e, a)
}
if e, a := 3, reqNum; e != a {
t.Errorf("expect %d reqNum got %d", e, a)
}
}

func TestWaiterSuccessNoError(t *testing.T) {
svc := &mockClient{Client: awstesting.NewClient(&aws.Config{
Region: aws.String("mock-region"),
})}
svc.Handlers.Send.Clear() // mock sending
svc.Handlers.Unmarshal.Clear()
svc.Handlers.UnmarshalMeta.Clear()
svc.Handlers.UnmarshalError.Clear()
svc.Handlers.ValidateResponse.Clear()

var reqNum int
results := []struct {
Out *MockOutput
Err error
}{
{ // success
Out: &MockOutput{
States: []*MockState{
{aws.String("pending")},
},
},
},
{ // shouldn't happen
Out: &MockOutput{
States: []*MockState{
{aws.String("pending")},
{aws.String("pending")},
},
},
},
}

numBuiltReq := 0
svc.Handlers.Build.PushBack(func(r *request.Request) {
numBuiltReq++
})
svc.Handlers.Send.PushBack(func(r *request.Request) {
code := http.StatusOK
r.HTTPResponse = &http.Response{
StatusCode: code,
Status: http.StatusText(code),
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
}
})
svc.Handlers.Unmarshal.PushBack(func(r *request.Request) {
if reqNum >= len(results) {
t.Errorf("too many polling requests made")
return
}
r.Data = results[reqNum].Out
reqNum++
})
svc.Handlers.UnmarshalMeta.PushBack(func(r *request.Request) {
// If there was an error unmarshal error will be called instead of unmarshal
// need to increment count here also
if err := results[reqNum].Err; err != nil {
r.Error = err
reqNum++
}
})

w := request.Waiter{
MaxAttempts: 10,
Delay: request.ConstantWaiterDelay(0),
SleepWithContext: aws.SleepWithContext,
Acceptors: []request.WaiterAcceptor{
{
State: request.SuccessWaiterState,
Matcher: request.PathAllWaiterMatch,
Argument: "States[].State",
Expected: "running",
},
{
State: request.SuccessWaiterState,
Matcher: request.ErrorWaiterMatch,
Argument: "",
Expected: false,
},
{
State: request.FailureWaiterState,
Matcher: request.ErrorWaiterMatch,
Argument: "",
Expected: "FailureException",
},
},
NewRequest: BuildNewMockRequest(svc, &MockInput{}),
}

err := w.WaitWithContext(aws.BackgroundContext())
if err != nil {
t.Fatalf("expected no error, but did get one")
}
if e, a := 1, numBuiltReq; e != a {
t.Errorf("expect %d built requests got %d", e, a)
}
if e, a := 1, reqNum; e != a {
t.Errorf("expect %d reqNum got %d", e, a)
}
}

func TestWaiterStatus(t *testing.T) {
svc := &mockClient{Client: awstesting.NewClient(&aws.Config{
Region: aws.String("mock-region"),
Expand Down

0 comments on commit 070dd38

Please sign in to comment.