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

fix(cli, codepipeline): renamed bootstrap stack still not supported #12771

Merged
merged 3 commits into from Feb 1, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
212 changes: 109 additions & 103 deletions packages/@aws-cdk/aws-codepipeline/test/cross-env.test.ts
Expand Up @@ -6,130 +6,136 @@ import * as codepipeline from '../lib';
import { FakeBuildAction } from './fake-build-action';
import { FakeSourceAction } from './fake-source-action';

let app: App;
let stack: Stack;
let sourceArtifact: codepipeline.Artifact;
let initialStages: codepipeline.StageProps[];

beforeEach(() => {
app = new App();
stack = new Stack(app, 'PipelineStack', { env: { account: '2222', region: 'us-east-1' } });
sourceArtifact = new codepipeline.Artifact();
initialStages = [
{
stageName: 'Source',
actions: [new FakeSourceAction({
actionName: 'Source',
output: sourceArtifact,
})],
},
{
stageName: 'Build',
actions: [new FakeBuildAction({
actionName: 'Build',
input: sourceArtifact,
})],
},
];
});

describe('crossAccountKeys=false', () => {
let pipeline: codepipeline.Pipeline;
describe.each(['legacy', 'modern'])('with %s synthesis', (synthesisStyle: string) => {
let app: App;
let stack: Stack;
let sourceArtifact: codepipeline.Artifact;
let initialStages: codepipeline.StageProps[];

beforeEach(() => {
pipeline = new codepipeline.Pipeline(stack, 'Pipeline', {
crossAccountKeys: false,
stages: initialStages,
app = new App({
context: {
...synthesisStyle === 'modern' ? { '@aws-cdk/core:newStyleStackSynthesis': true } : undefined,
},
});
stack = new Stack(app, 'PipelineStack', { env: { account: '2222', region: 'us-east-1' } });
sourceArtifact = new codepipeline.Artifact();
initialStages = [
{
stageName: 'Source',
actions: [new FakeSourceAction({
actionName: 'Source',
output: sourceArtifact,
})],
},
{
stageName: 'Build',
actions: [new FakeBuildAction({
actionName: 'Build',
input: sourceArtifact,
})],
},
];
});

test('creates a bucket but no keys', () => {
// THEN
expect(stack).not.toHaveResource('AWS::KMS::Key');
expect(stack).toHaveResource('AWS::S3::Bucket');
});

describe('prevents adding a cross-account action', () => {
const expectedError = 'crossAccountKeys: true';

let stage: codepipeline.IStage;
describe('crossAccountKeys=false', () => {
let pipeline: codepipeline.Pipeline;
beforeEach(() => {
stage = pipeline.addStage({ stageName: 'Deploy' });
pipeline = new codepipeline.Pipeline(stack, 'Pipeline', {
crossAccountKeys: false,
stages: initialStages,
});
});

test('by role', () => {
// WHEN
expect(() => {
stage.addAction(new FakeBuildAction({
actionName: 'Deploy',
input: sourceArtifact,
role: iam.Role.fromRoleArn(stack, 'Role', 'arn:aws:iam::1111:role/some-role'),
}));
}).toThrow(expectedError);
test('creates a bucket but no keys', () => {
// THEN
expect(stack).not.toHaveResource('AWS::KMS::Key');
expect(stack).toHaveResource('AWS::S3::Bucket');
});

test('by resource', () => {
// WHEN
expect(() => {
stage.addAction(new FakeBuildAction({
actionName: 'Deploy',
input: sourceArtifact,
resource: s3.Bucket.fromBucketAttributes(stack, 'Bucket', {
bucketName: 'foo',
describe('prevents adding a cross-account action', () => {
const expectedError = 'crossAccountKeys: true';

let stage: codepipeline.IStage;
beforeEach(() => {
stage = pipeline.addStage({ stageName: 'Deploy' });
});

test('by role', () => {
// WHEN
expect(() => {
stage.addAction(new FakeBuildAction({
actionName: 'Deploy',
input: sourceArtifact,
role: iam.Role.fromRoleArn(stack, 'Role', 'arn:aws:iam::1111:role/some-role'),
}));
}).toThrow(expectedError);
});

test('by resource', () => {
// WHEN
expect(() => {
stage.addAction(new FakeBuildAction({
actionName: 'Deploy',
input: sourceArtifact,
resource: s3.Bucket.fromBucketAttributes(stack, 'Bucket', {
bucketName: 'foo',
account: '1111',
}),
}));
}).toThrow(expectedError);
});

test('by declared account', () => {
// WHEN
expect(() => {
stage.addAction(new FakeBuildAction({
actionName: 'Deploy',
input: sourceArtifact,
account: '1111',
}),
}));
}).toThrow(expectedError);
}));
}).toThrow(expectedError);
});
});

test('by declared account', () => {
// WHEN
expect(() => {
describe('also affects cross-region support stacks', () => {
let stage: codepipeline.IStage;
beforeEach(() => {
stage = pipeline.addStage({ stageName: 'Deploy' });
});

test('when making a support stack', () => {
// WHEN
stage.addAction(new FakeBuildAction({
actionName: 'Deploy',
input: sourceArtifact,
account: '1111',
// No resource to grab onto forces creating a fresh support stack
region: 'eu-west-1',
}));
}).toThrow(expectedError);
});
});

describe('also affects cross-region support stacks', () => {
let stage: codepipeline.IStage;
beforeEach(() => {
stage = pipeline.addStage({ stageName: 'Deploy' });
});

test('when making a support stack', () => {
// WHEN
stage.addAction(new FakeBuildAction({
actionName: 'Deploy',
input: sourceArtifact,
// No resource to grab onto forces creating a fresh support stack
region: 'eu-west-1',
}));

// THEN
const asm = app.synth();
const supportStack = asm.getStack(`${stack.stackName}-support-eu-west-1`);
// THEN
const asm = app.synth();
const supportStack = asm.getStack(`${stack.stackName}-support-eu-west-1`);

// THEN
expect(supportStack).not.toHaveResource('AWS::KMS::Key');
expect(supportStack).toHaveResource('AWS::S3::Bucket');
});
// THEN
expect(supportStack).not.toHaveResource('AWS::KMS::Key');
expect(supportStack).toHaveResource('AWS::S3::Bucket');
});

test('when twiddling another stack', () => {
const stack2 = new Stack(app, 'Stack2', { env: { account: '2222', region: 'eu-west-1' } });
test('when twiddling another stack', () => {
const stack2 = new Stack(app, 'Stack2', { env: { account: '2222', region: 'eu-west-1' } });

// WHEN
stage.addAction(new FakeBuildAction({
actionName: 'Deploy',
input: sourceArtifact,
resource: new iam.User(stack2, 'DoesntMatterWhatThisIs'),
}));
// WHEN
stage.addAction(new FakeBuildAction({
actionName: 'Deploy',
input: sourceArtifact,
resource: new iam.User(stack2, 'DoesntMatterWhatThisIs'),
}));

// THEN
expect(stack2).not.toHaveResource('AWS::KMS::Key');
expect(stack2).toHaveResource('AWS::S3::Bucket');
// THEN
expect(stack2).not.toHaveResource('AWS::KMS::Key');
expect(stack2).toHaveResource('AWS::S3::Bucket');
});
});
});
});
Expand Up @@ -57,7 +57,6 @@ export class BootstraplessSynthesizer extends DefaultStackSynthesizer {
this.emitStackArtifact(this.stack, session, {
assumeRoleArn: this.deployRoleArn,
cloudFormationExecutionRoleArn: this.cloudFormationExecutionRoleArn,
requiresBootstrapStackVersion: 1,
});
}
}
4 changes: 0 additions & 4 deletions packages/aws-cdk/lib/api/cloudformation-deployments.ts
Expand Up @@ -282,10 +282,6 @@ export class CloudFormationDeployments {

if (requiresBootstrapStackVersion === undefined) { return; }

if (!toolkitInfo.found) {
throw new Error(`${stackName}: publishing assets requires bootstrap stack version '${requiresBootstrapStackVersion}', no bootstrap stack found. Please run 'cdk bootstrap'.`);
}

try {
await toolkitInfo.validateVersion(requiresBootstrapStackVersion, bootstrapStackVersionSsmParameter);
} catch (e) {
Expand Down
2 changes: 1 addition & 1 deletion packages/aws-cdk/lib/api/cxapp/exec.ts
Expand Up @@ -92,7 +92,7 @@ export async function execProgram(aws: SdkProvider, config: Configuration): Prom
}

async function exec() {
return new Promise<string>((ok, fail) => {
return new Promise<void>((ok, fail) => {
// We use a slightly lower-level interface to:
//
// - Pass arguments in an array instead of a string, to get around a
Expand Down
Expand Up @@ -76,7 +76,7 @@ test('deployment fails if bootstrap stack is missing', async () => {
requiresBootstrapStackVersion: 99,
},
}),
})).rejects.toThrow(/no bootstrap stack found/);
})).rejects.toThrow(/requires a bootstrap stack/);
});

test('deployment fails if bootstrap stack is too old', async () => {
Expand Down
5 changes: 3 additions & 2 deletions packages/aws-cdk/test/integ/cli/bootstrapping.integtest.ts
Expand Up @@ -259,8 +259,9 @@ integTest('can deploy modern-synthesized stack even if bootstrap stack name is u
// Deploy stack that uses file assets
await fixture.cdkDeploy('lambda', {
options: [
// Next line explicitly commented to show that we don't pass it!
// '--toolkit-stack-name', bootstrapStackName,
// Explicity pass a name that's sure to not exist, otherwise the CLI might accidentally find a
// default bootstracp stack if that happens to be in the account already.
'--toolkit-stack-name', 'DefinitelyDoesNotExist',
'--context', `@aws-cdk/core:bootstrapQualifier=${fixture.qualifier}`,
'--context', '@aws-cdk/core:newStyleStackSynthesis=1',
],
Expand Down