Skip to content

Commit

Permalink
fix(cli): unable to update stacks in UPDATE_ROLLBACK_COMPLETE
Browse files Browse the repository at this point in the history
The CLI determined that a stack in UPDATE_ROLLBACK_COMPLETE
status is not updatable.

This change cleans up this logic so that a stack update will fail
only if the stack is in _FAILED status, which is a non-updatable state.

Closes #8126 #5151
  • Loading branch information
shivlaks committed Jul 8, 2020
1 parent c424c57 commit 684646d
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 12 deletions.
6 changes: 2 additions & 4 deletions packages/aws-cdk/lib/api/util/cloudformation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,10 +260,8 @@ export async function waitForStack(
if (!stack) { return undefined; }

const status = stack.stackStatus;
if (status.isCreationFailure) {
throw new Error(`The stack named ${stackName} failed creation, it may need to be manually deleted from the AWS console: ${status}`);
} else if (!status.isSuccess) {
throw new Error(`The stack named ${stackName} is in a failed state: ${status}`);
if (status.isFailure) {
throw new Error(`The stack named ${stackName} is in a failed state. You may need to "continue update rollback" or delete it from the AWS console : ${status}`);
} else if (status.isDeleted) {
if (failOnDeletedStack) { throw new Error(`The stack named ${stackName} was deleted`); }
return undefined;
Expand Down
8 changes: 0 additions & 8 deletions packages/aws-cdk/lib/api/util/cloudformation/stack-status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@ export class StackStatus {
return this.name.endsWith('FAILED');
}

get isRollback(): boolean {
return this.name.indexOf('ROLLBACK') !== -1;
}

get isStable(): boolean {
return !this.name.endsWith('_IN_PROGRESS');
}
Expand All @@ -37,10 +33,6 @@ export class StackStatus {
return this.name === 'NOT_FOUND';
}

get isSuccess(): boolean {
return !this.isNotFound && !this.isRollback && !this.isFailure;
}

public toString(): string {
return this.name + (this.reason ? ` (${this.reason})` : '');
}
Expand Down
23 changes: 23 additions & 0 deletions packages/aws-cdk/test/api/deploy-stack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,29 @@ test('deploy not skipped if template did not change but one tag removed', async
expect(cfnMocks.getTemplate).toHaveBeenCalledWith({ StackName: 'withouterrors', TemplateStage: 'Original' });
});

test('existing stack in UPDATE_ROLLBACK_COMPLETE state can be updated', async () => {
// GIVEN
givenStackExists(
{ StackStatus: 'UPDATE_ROLLBACK_COMPLETE' }, // This is for the initial check
{ StackStatus: 'UPDATE_COMPLETE' }, // Poll the update
);
givenTemplateIs({ changed: 123 });

// WHEN
await deployStack({
stack: FAKE_STACK,
sdk,
sdkProvider,
resolvedEnvironment: mockResolvedEnvironment(),
});

// THEN
expect(cfnMocks.deleteStack).not.toHaveBeenCalled();
expect(cfnMocks.createChangeSet).toHaveBeenCalledWith(expect.objectContaining({
ChangeSetType: 'UPDATE',
}));
});

test('deploy not skipped if template changed', async () => {
// GIVEN
givenStackExists();
Expand Down
51 changes: 51 additions & 0 deletions packages/aws-cdk/test/integ/cli/cli.integtest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,57 @@ integTest('deploy with parameters', async () => {
]);
});

integTest('stack in UPDATE_ROLLBACK_COMPLETE state can be updated', async () => {
// GIVEN
const stackArn = await cdkDeploy('param-test-1', {
options: [
'--parameters', `TopicNameParam=${STACK_NAME_PREFIX}nice`,
],
captureStderr: false,
});

let response = await cloudFormation('describeStacks', {
StackName: stackArn,
});

expect(response.Stacks?.[0].StackStatus).toEqual('CREATE_COMPLETE');

// bad parameter name with @ will put stack into UPDATE_ROLLBACK_COMPLETE
await cdkDeploy('param-test-1', {
options: [
'--parameters', `TopicNameParam=${STACK_NAME_PREFIX}@aww`,
],
captureStderr: false,
});

response = await cloudFormation('describeStacks', {
StackName: stackArn,
});

expect(response.Stacks?.[0].StackStatus).toEqual('UPDATE_ROLLBACK_COMPLETE');

// WHEN
await cdkDeploy('param-test-1', {
options: [
'--parameters', `TopicNameParam=${STACK_NAME_PREFIX}allgood`,
],
captureStderr: false,
});

response = await cloudFormation('describeStacks', {
StackName: stackArn,
});

// THEN
expect(response.Stacks?.[0].StackStatus).toEqual('UPDATE_COMPLETE');
expect(response.Stacks?.[0].Parameters).toEqual([
{
ParameterKey: 'TopicNameParam',
ParameterValue: `${STACK_NAME_PREFIX}allgood`,
},
]);
});

integTest('deploy with wildcard and parameters', async () => {
await cdkDeploy('param-test-*', {
options: [
Expand Down

0 comments on commit 684646d

Please sign in to comment.