Skip to content

Commit

Permalink
chore(cli integ tests): delete bootstrap stacks last (#14505)
Browse files Browse the repository at this point in the history
We are running into conflicts where the toolkit stack canary
is deleting bootstrap stacks before application stacks, which ends
up with the `cfn-exec-role` being deleted before the application stack
is deleted, causing errors.


----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
rix0rrr authored May 3, 2021
1 parent 39d4e96 commit 12b6da0
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 15 deletions.
30 changes: 15 additions & 15 deletions packages/aws-cdk/test/integ/cli/bootstrapping.integtest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { integTest } from '../helpers/test-helpers';
jest.setTimeout(600_000);

integTest('can bootstrap without execution', withDefaultFixture(async (fixture) => {
const bootstrapStackName = fixture.fullStackName('bootstrap-stack');
const bootstrapStackName = fixture.bootstrapStackName;

await fixture.cdkBootstrapLegacy({
toolkitStackName: bootstrapStackName,
Expand All @@ -21,7 +21,7 @@ integTest('can bootstrap without execution', withDefaultFixture(async (fixture)
}));

integTest('upgrade legacy bootstrap stack to new bootstrap stack while in use', withDefaultFixture(async (fixture) => {
const bootstrapStackName = fixture.fullStackName('bootstrap-stack');
const bootstrapStackName = fixture.bootstrapStackName;

const legacyBootstrapBucketName = `aws-cdk-bootstrap-integ-test-legacy-bckt-${randomString()}`;
const newBootstrapBucketName = `aws-cdk-bootstrap-integ-test-v2-bckt-${randomString()}`;
Expand Down Expand Up @@ -57,7 +57,7 @@ integTest('upgrade legacy bootstrap stack to new bootstrap stack while in use',
}));

integTest('can and deploy if omitting execution policies', withDefaultFixture(async (fixture) => {
const bootstrapStackName = fixture.fullStackName('bootstrap-stack');
const bootstrapStackName = fixture.bootstrapStackName;

await fixture.cdkBootstrapModern({
toolkitStackName: bootstrapStackName,
Expand All @@ -74,7 +74,7 @@ integTest('can and deploy if omitting execution policies', withDefaultFixture(as
}));

integTest('deploy new style synthesis to new style bootstrap', withDefaultFixture(async (fixture) => {
const bootstrapStackName = fixture.fullStackName('bootstrap-stack');
const bootstrapStackName = fixture.bootstrapStackName;

await fixture.cdkBootstrapModern({
toolkitStackName: bootstrapStackName,
Expand All @@ -92,7 +92,7 @@ integTest('deploy new style synthesis to new style bootstrap', withDefaultFixtur
}));

integTest('deploy new style synthesis to new style bootstrap (with docker image)', withDefaultFixture(async (fixture) => {
const bootstrapStackName = fixture.fullStackName('bootstrap-stack');
const bootstrapStackName = fixture.bootstrapStackName;

await fixture.cdkBootstrapModern({
toolkitStackName: bootstrapStackName,
Expand All @@ -110,7 +110,7 @@ integTest('deploy new style synthesis to new style bootstrap (with docker image)
}));

integTest('deploy old style synthesis to new style bootstrap', withDefaultFixture(async (fixture) => {
const bootstrapStackName = fixture.fullStackName('bootstrap-stack');
const bootstrapStackName = fixture.bootstrapStackName;

await fixture.cdkBootstrapModern({
toolkitStackName: bootstrapStackName,
Expand All @@ -126,7 +126,7 @@ integTest('deploy old style synthesis to new style bootstrap', withDefaultFixtur
}));

integTest('deploying new style synthesis to old style bootstrap fails', withDefaultFixture(async (fixture) => {
const bootstrapStackName = fixture.fullStackName('bootstrap-stack');
const bootstrapStackName = fixture.bootstrapStackName;

await fixture.cdkBootstrapLegacy({
toolkitStackName: bootstrapStackName,
Expand All @@ -143,7 +143,7 @@ integTest('deploying new style synthesis to old style bootstrap fails', withDefa
}));

integTest('can create a legacy bootstrap stack with --public-access-block-configuration=false', withDefaultFixture(async (fixture) => {
const bootstrapStackName = fixture.fullStackName('bootstrap-stack-1');
const bootstrapStackName = fixture.bootstrapStackName;

await fixture.cdkBootstrapLegacy({
verbose: true,
Expand All @@ -159,8 +159,8 @@ integTest('can create a legacy bootstrap stack with --public-access-block-config
}));

integTest('can create multiple legacy bootstrap stacks', withDefaultFixture(async (fixture) => {
const bootstrapStackName1 = fixture.fullStackName('bootstrap-stack-1');
const bootstrapStackName2 = fixture.fullStackName('bootstrap-stack-2');
const bootstrapStackName1 = `${fixture.bootstrapStackName}-1`;
const bootstrapStackName2 = `${fixture.bootstrapStackName}-2`;

// deploy two toolkit stacks into the same environment (see #1416)
// one with tags
Expand All @@ -183,7 +183,7 @@ integTest('can create multiple legacy bootstrap stacks', withDefaultFixture(asyn
integTest('can dump the template, modify and use it to deploy a custom bootstrap stack', withDefaultFixture(async (fixture) => {
let template = await fixture.cdkBootstrapModern({
// toolkitStackName doesn't matter for this particular invocation
toolkitStackName: fixture.fullStackName('bootstrap-stack'),
toolkitStackName: fixture.bootstrapStackName,
showTemplate: true,
cliOptions: {
captureStderr: false,
Expand All @@ -200,14 +200,14 @@ integTest('can dump the template, modify and use it to deploy a custom bootstrap
const filename = path.join(fixture.integTestDir, `${fixture.qualifier}-template.yaml`);
fs.writeFileSync(filename, template, { encoding: 'utf-8' });
await fixture.cdkBootstrapModern({
toolkitStackName: fixture.fullStackName('bootstrap-stack'),
toolkitStackName: fixture.bootstrapStackName,
template: filename,
cfnExecutionPolicy: 'arn:aws:iam::aws:policy/AdministratorAccess',
});
}));

integTest('switch on termination protection, switch is left alone on re-bootstrap', withDefaultFixture(async (fixture) => {
const bootstrapStackName = fixture.fullStackName('bootstrap-stack');
const bootstrapStackName = fixture.bootstrapStackName;

await fixture.cdkBootstrapModern({
verbose: true,
Expand All @@ -226,7 +226,7 @@ integTest('switch on termination protection, switch is left alone on re-bootstra
}));

integTest('add tags, left alone on re-bootstrap', withDefaultFixture(async (fixture) => {
const bootstrapStackName = fixture.fullStackName('bootstrap-stack');
const bootstrapStackName = fixture.bootstrapStackName;

await fixture.cdkBootstrapModern({
verbose: true,
Expand All @@ -247,7 +247,7 @@ integTest('add tags, left alone on re-bootstrap', withDefaultFixture(async (fixt
}));

integTest('can deploy modern-synthesized stack even if bootstrap stack name is unknown', withDefaultFixture(async (fixture) => {
const bootstrapStackName = fixture.fullStackName('bootstrap-stack');
const bootstrapStackName = fixture.bootstrapStackName;

await fixture.cdkBootstrapModern({
toolkitStackName: bootstrapStackName,
Expand Down
18 changes: 18 additions & 0 deletions packages/aws-cdk/test/integ/helpers/cdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,10 @@ export class TestFixture {
});
}

public get bootstrapStackName() {
return this.fullStackName('bootstrap-stack');
}

public fullStackName(stackName: string): string;
public fullStackName(stackNames: string[]): string[];
public fullStackName(stackNames: string | string[]): string | string[] {
Expand All @@ -408,6 +412,8 @@ export class TestFixture {
public async dispose(success: boolean) {
const stacksToDelete = await this.deleteableStacks(this.stackNamePrefix);

this.sortBootstrapStacksToTheEnd(stacksToDelete);

// Bootstrap stacks have buckets that need to be cleaned
const bucketNames = stacksToDelete.map(stack => outputFromStack('BucketName', stack)).filter(defined);
await Promise.all(bucketNames.map(b => this.aws.emptyBucket(b)));
Expand Down Expand Up @@ -458,6 +464,18 @@ export class TestFixture {
.filter(s => statusFilter.includes(s.StackStatus))
.filter(s => s.RootId === undefined); // Only delete parent stacks. Nested stacks are deleted in the process
}

private sortBootstrapStacksToTheEnd(stacks: AWS.CloudFormation.Stack[]) {
stacks.sort((a, b) => {
const aBs = a.StackName.startsWith(this.bootstrapStackName);
const bBs = b.StackName.startsWith(this.bootstrapStackName);

return aBs != bBs
// '+' converts a boolean to 0 or 1
? (+aBs) - (+bBs)
: a.StackName.localeCompare(b.StackName);
});
}
}

/**
Expand Down

0 comments on commit 12b6da0

Please sign in to comment.