Skip to content

Commit

Permalink
add error handling for describe account creation status (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
shuqz authored Sep 6, 2024
1 parent 52d44dc commit ae4b416
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ public abstract class BaseHandlerStd extends BaseHandler<CallbackContext> {
protected static final String CREATE_ACCOUNT_FAILURE_REASON_ACCOUNT_LIMIT_EXCEEDED = "ACCOUNT_LIMIT_EXCEEDED";
protected static final String CREATE_ACCOUNT_FAILURE_REASON_INVALID_ADDRESS = "INVALID_ADDRESS";
protected static final String CREATE_ACCOUNT_FAILURE_REASON_INVALID_EMAIL = "INVALID_EMAIL";
protected static final String CREATE_ACCOUNT_FAILURE_REASON_CONCURRENT_ACCOUNT_MODIFICATION = "CONCURRENT_ACCOUNT_MODIFICATION";
protected static final String CREATE_ACCOUNT_FAILURE_REASON_FAILED_BUSINESS_VALIDATION = "FAILED_BUSINESS_VALIDATION";
protected static final String CREATE_ACCOUNT_FAILURE_REASON_IDENTITY_INVALID_BUSINESS_VALIDATION = "IDENTITY_INVALID_BUSINESS_VALIDATION";
protected static final String CREATE_ACCOUNT_FAILURE_REASON_INVALID_PAYMENT_INSTRUMENT = "INVALID_PAYMENT_INSTRUMENT";
protected static final String CREATE_ACCOUNT_FAILURE_REASON_INTERNAL_FAILURE = "INTERNAL_FAILURE";
protected static final String CREATE_ACCOUNT_FAILURE_REASON_MISSING_BUSINESS_VALIDATION = "MISSING_BUSINESS_VALIDATION";
protected static final String CREATE_ACCOUNT_FAILURE_REASON_MISSING_PAYMENT_INSTRUMENT = "MISSING_PAYMENT_INSTRUMENT";
protected static final String CREATE_ACCOUNT_FAILURE_REASON_PENDING_BUSINESS_VALIDATION = "PENDING_BUSINESS_VALIDATION";
protected static final String CREATE_ACCOUNT_FAILURE_REASON_UNKNOWN_BUSINESS_VALIDATION = "UNKNOWN_BUSINESS_VALIDATION";
protected static final String ACCOUNT_CREATION_STATUS_SUCCEEDED = "SUCCEEDED";
protected static final String ACCOUNT_CREATION_STATUS_FAILED = "FAILED";
// ExponentialBackoffJitter Constants
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,17 +146,30 @@ private ProgressEvent<ResourceModel, CallbackContext> handleAccountCreationError
String errMsg = String.format("Account creation failed with reason [%s] for request id: %s", failureReason, callbackContext.getCreateAccountRequestId());
logger.log(errMsg);

switch (failureReason) {
case CREATE_ACCOUNT_FAILURE_REASON_EMAIL_ALREADY_EXISTS:
case CREATE_ACCOUNT_FAILURE_REASON_GOVCLOUD_ACCOUNT_ALREADY_EXISTS:
return ProgressEvent.failed(model, callbackContext, HandlerErrorCode.AlreadyExists, errMsg);
case CREATE_ACCOUNT_FAILURE_REASON_ACCOUNT_LIMIT_EXCEEDED:
return ProgressEvent.failed(model, callbackContext, HandlerErrorCode.ServiceLimitExceeded, errMsg);
case CREATE_ACCOUNT_FAILURE_REASON_INVALID_ADDRESS:
case CREATE_ACCOUNT_FAILURE_REASON_INVALID_EMAIL:
return ProgressEvent.failed(model, callbackContext, HandlerErrorCode.InvalidRequest, errMsg);
HandlerErrorCode errorCode = HandlerErrorCode.GeneralServiceException;
if (failureReason.equals(CREATE_ACCOUNT_FAILURE_REASON_EMAIL_ALREADY_EXISTS) ||
failureReason.equals(CREATE_ACCOUNT_FAILURE_REASON_GOVCLOUD_ACCOUNT_ALREADY_EXISTS)
) {
errorCode = HandlerErrorCode.AlreadyExists;
} else if (failureReason.equals(CREATE_ACCOUNT_FAILURE_REASON_ACCOUNT_LIMIT_EXCEEDED)) {
errorCode = HandlerErrorCode.ServiceLimitExceeded;
} else if (failureReason.equals(CREATE_ACCOUNT_FAILURE_REASON_INTERNAL_FAILURE)) {
errorCode = HandlerErrorCode.ServiceInternalError;
} else if (failureReason.equals(CREATE_ACCOUNT_FAILURE_REASON_INVALID_ADDRESS) ||
failureReason.equals(CREATE_ACCOUNT_FAILURE_REASON_INVALID_EMAIL) ||
failureReason.equals(CREATE_ACCOUNT_FAILURE_REASON_FAILED_BUSINESS_VALIDATION) ||
failureReason.equals(CREATE_ACCOUNT_FAILURE_REASON_IDENTITY_INVALID_BUSINESS_VALIDATION) ||
failureReason.equals(CREATE_ACCOUNT_FAILURE_REASON_INVALID_PAYMENT_INSTRUMENT) ||
failureReason.equals(CREATE_ACCOUNT_FAILURE_REASON_MISSING_BUSINESS_VALIDATION) ||
failureReason.equals(CREATE_ACCOUNT_FAILURE_REASON_MISSING_PAYMENT_INSTRUMENT) ||
failureReason.equals(CREATE_ACCOUNT_FAILURE_REASON_PENDING_BUSINESS_VALIDATION) ||
failureReason.equals(CREATE_ACCOUNT_FAILURE_REASON_UNKNOWN_BUSINESS_VALIDATION) ||
failureReason.equals(CREATE_ACCOUNT_FAILURE_REASON_CONCURRENT_ACCOUNT_MODIFICATION)
) {
errorCode = HandlerErrorCode.InvalidRequest;
}
return ProgressEvent.failed(model, callbackContext, HandlerErrorCode.GeneralServiceException, errMsg);
logger.log(String.format("[Exception] ProgressEvent failed in account creation, translated FailureReason: [%s] to CloudFormation error code: [%s].", failureReason, errorCode));
return ProgressEvent.failed(model, callbackContext, errorCode, errMsg);
}

protected ProgressEvent<ResourceModel, CallbackContext> moveAccount(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public class AbstractTestBase {
protected static final String ACCOUNT_LIMIT_EXCEEDED = "ACCOUNT_LIMIT_EXCEEDED";
protected static final String INVALID_EMAIL = "INVALID_EMAIL";
protected static final String INTERNAL_FAILURE = "INTERNAL_FAILURE";
protected static final String UNKNOWN_FAILURE = "UNKNOWN_FAILURE";
protected static final String CREATE_ACCOUNT_STATUS_ID = "car-123456789023";
protected static final Instant REQUESTED_TIMESTAMP = Instant.parse("2017-02-03T10:37:30.00Z");
protected static final Instant COMPLETED_TIMESTAMP = Instant.parse("2017-02-03T10:47:30.00Z");
Expand Down Expand Up @@ -86,11 +87,18 @@ public class AbstractTestBase {
.build();

protected static final CreateAccountStatus CreateAccountStatusFailedWithInternalFailure = CreateAccountStatus.builder()
.failureReason(INTERNAL_FAILURE)
.id(CREATE_ACCOUNT_STATUS_ID)
.state(FAILED)
.requestedTimestamp(REQUESTED_TIMESTAMP)
.build();
.failureReason(INTERNAL_FAILURE)
.id(CREATE_ACCOUNT_STATUS_ID)
.state(FAILED)
.requestedTimestamp(REQUESTED_TIMESTAMP)
.build();

protected static final CreateAccountStatus CreateAccountStatusFailedWithUnknownFailure = CreateAccountStatus.builder()
.failureReason(UNKNOWN_FAILURE)
.id(CREATE_ACCOUNT_STATUS_ID)
.state(FAILED)
.requestedTimestamp(REQUESTED_TIMESTAMP)
.build();

protected static final CreateAccountStatus CreateAccountStatusSucceeded = CreateAccountStatus.builder()
.accountId(TEST_ACCOUNT_ID)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,13 +323,13 @@ public void handleRequest_FailedWithCfnGeneralServiceException() {
final ResourceModel model = generateCreateResourceModel();

final ResourceHandlerRequest<ResourceModel> request = ResourceHandlerRequest.<ResourceModel>builder()
.desiredResourceState(model)
.build();
.desiredResourceState(model)
.build();

final CreateAccountResponse createAccountResponse = getCreateAccountResponse();
final DescribeCreateAccountStatusResponse describeCreateAccountStatusResponse = DescribeCreateAccountStatusResponse.builder()
.createAccountStatus(CreateAccountStatusFailedWithInternalFailure)
.build();
.createAccountStatus(CreateAccountStatusFailedWithUnknownFailure)
.build();

when(mockProxyClient.client().createAccount(any(CreateAccountRequest.class))).thenReturn(createAccountResponse);
when(mockProxyClient.client().describeCreateAccountStatus(any(DescribeCreateAccountStatusRequest.class))).thenReturn(describeCreateAccountStatusResponse);
Expand All @@ -351,6 +351,39 @@ public void handleRequest_FailedWithCfnGeneralServiceException() {
verify(mockProxyClient.client(), times(0)).moveAccount(any(MoveAccountRequest.class));
}

@Test
public void handleRequest_FailedWithCfnServiceInternalError() {
final ResourceModel model = generateCreateResourceModel();

final ResourceHandlerRequest<ResourceModel> request = ResourceHandlerRequest.<ResourceModel>builder()
.desiredResourceState(model)
.build();

final CreateAccountResponse createAccountResponse = getCreateAccountResponse();
final DescribeCreateAccountStatusResponse describeCreateAccountStatusResponse = DescribeCreateAccountStatusResponse.builder()
.createAccountStatus(CreateAccountStatusFailedWithInternalFailure)
.build();

when(mockProxyClient.client().createAccount(any(CreateAccountRequest.class))).thenReturn(createAccountResponse);
when(mockProxyClient.client().describeCreateAccountStatus(any(DescribeCreateAccountStatusRequest.class))).thenReturn(describeCreateAccountStatusResponse);
final ProgressEvent<ResourceModel, CallbackContext> response = createHandler.handleRequest(mockAwsClientProxy, request, new CallbackContext(), mockProxyClient, logger);

assertThat(response).isNotNull();
assertThat(response.getStatus()).isEqualTo(OperationStatus.FAILED);
assertThat(response.getCallbackDelaySeconds()).isEqualTo(0);
assertThat(response.getResourceModel()).isNotNull();
assertThat(response.getErrorCode()).isEqualTo(HandlerErrorCode.ServiceInternalError);
assertThat(response.getResourceModel().getAccountId()).isNull();
assertThat(response.getResourceModel().getEmail()).isEqualTo(TEST_ACCOUNT_EMAIL);
assertThat(response.getResourceModel().getAccountName()).isEqualTo(TEST_ACCOUNT_NAME);
assertThat(response.getResourceModel().getParentIds()).isEqualTo(TEST_PARENT_IDS);
assertThat(TagTestResourcesHelper.tagsEqual(response.getResourceModel().getTags(), TagTestResourcesHelper.defaultTags));

verify(mockProxyClient.client()).createAccount(any(CreateAccountRequest.class));
verify(mockProxyClient.client(), atLeast(1)).describeCreateAccountStatus(any(DescribeCreateAccountStatusRequest.class));
verify(mockProxyClient.client(), times(0)).moveAccount(any(MoveAccountRequest.class));
}

@Test
public void handleRequest_shouldFailWhenNoAccountNameAndEmailAddress() {
final ResourceModel model = generateCreateResourceModelNoAccountNameAndEmail();
Expand Down

0 comments on commit ae4b416

Please sign in to comment.