Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add error handling for describe account creation status #79

Merged
merged 1 commit into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading