Skip to content

Commit

Permalink
fix(cli): cdk deploy cannot update stacks in REVIEW_IN_PROGRESS status (
Browse files Browse the repository at this point in the history
#7731)

Stacks only end up in `REVIEW_IN_PROGRESS` state when they have never
been deployed by executing a changeset. It exists, but has no resources or a template.

Users can end up in this state if a changeset takes too long to deploy or if
the `--no-execute` flag is used the first time a stack is deployed. There are likely other
scenarios.

If a `cdk deploy` bails and a changeset finishes creation but does not get executed, a stack
will have `REVIEW_IN_PROGRESS` as its status

Root cause:

We're trying to `update` a stack that was never deployed.
CloudFormation considers a stack ID with no template or resources to be a
stack that does not exist. When we try to set the `ChangeSetType` to `UPDATE`,
it results in a `ValidationException` from CloudFormation

Fix:

When a stack is in `REVIEW_IN_PROGRESS` state, ensure we set the `ChangeSetType` to `CREATE`

Closes #6674
  • Loading branch information
shivlaks committed May 4, 2020
1 parent a93534f commit a52b3e3
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 1 deletion.
2 changes: 1 addition & 1 deletion packages/aws-cdk/lib/api/deploy-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ export async function deployStack(options: DeployStackOptions): Promise<DeploySt
await publishAssets(legacyAssets.toManifest(stackArtifact.assembly.directory), options.sdkProvider, stackEnv);

const changeSetName = `CDK-${executionId}`;
const update = cloudFormationStack.exists;
const update = cloudFormationStack.exists && cloudFormationStack.stackStatus.name !== 'REVIEW_IN_PROGRESS';

debug(`Attempting to create ChangeSet ${changeSetName} to ${update ? 'update' : 'create'} stack ${deployName}`);
print('%s: creating CloudFormation changeset...', colors.bold(deployName));
Expand Down
57 changes: 57 additions & 0 deletions packages/aws-cdk/test/api/deploy-stack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,63 @@ test('not executed and no error if --no-execute is given', async () => {
expect(cfnMocks.executeChangeSet).not.toHaveBeenCalled();
});

test('changeset is created when stack exists in REVIEW_IN_PROGRESS status', async () => {
// GIVEN
givenStackExists({
StackStatus: 'REVIEW_IN_PROGRESS',
Tags: [
{ Key: 'Key1', Value: 'Value1' },
{ Key: 'Key2', Value: 'Value2' },
],
});

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

// THEN
expect(cfnMocks.createChangeSet).toHaveBeenCalledWith(
expect.objectContaining({
ChangeSetType: 'CREATE',
StackName: 'withouterrors',
}),
);
expect(cfnMocks.executeChangeSet).not.toHaveBeenCalled();
});

test('changeset is updated when stack exists in CREATE_COMPLETE status', async () => {
// GIVEN
givenStackExists({
Tags: [
{ Key: 'Key1', Value: 'Value1' },
{ Key: 'Key2', Value: 'Value2' },
],
});

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

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

/**
* Set up the mocks so that it looks like the stack exists to start with
*/
Expand Down

0 comments on commit a52b3e3

Please sign in to comment.