@aws-cdk-testing/cli-integ
Version:
Integration tests for the AWS CDK CLI
659 lines • 107 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = require("fs");
const os = require("os");
const path = require("path");
const aws_1 = require("../helpers/aws");
const cdk_1 = require("../helpers/cdk");
const test_helpers_1 = require("../helpers/test-helpers");
jest.setTimeout(600 * 1000);
test_helpers_1.integTest('VPC Lookup', cdk_1.withDefaultFixture(async (fixture) => {
fixture.log('Making sure we are clean before starting.');
await fixture.cdkDestroy('define-vpc', { modEnv: { ENABLE_VPC_TESTING: 'DEFINE' } });
fixture.log('Setting up: creating a VPC with known tags');
await fixture.cdkDeploy('define-vpc', { modEnv: { ENABLE_VPC_TESTING: 'DEFINE' } });
fixture.log('Setup complete!');
fixture.log('Verifying we can now import that VPC');
await fixture.cdkDeploy('import-vpc', { modEnv: { ENABLE_VPC_TESTING: 'IMPORT' } });
}));
test_helpers_1.integTest('Two ways of shoing the version', cdk_1.withDefaultFixture(async (fixture) => {
const version1 = await fixture.cdk(['version'], { verbose: false });
const version2 = await fixture.cdk(['--version'], { verbose: false });
expect(version1).toEqual(version2);
}));
test_helpers_1.integTest('Termination protection', cdk_1.withDefaultFixture(async (fixture) => {
const stackName = 'termination-protection';
await fixture.cdkDeploy(stackName);
// Try a destroy that should fail
await expect(fixture.cdkDestroy(stackName)).rejects.toThrow('exited with error');
// Can update termination protection even though the change set doesn't contain changes
await fixture.cdkDeploy(stackName, { modEnv: { TERMINATION_PROTECTION: 'FALSE' } });
await fixture.cdkDestroy(stackName);
}));
test_helpers_1.integTest('cdk synth', cdk_1.withDefaultFixture(async (fixture) => {
await fixture.cdk(['synth', fixture.fullStackName('test-1')]);
const template1 = await readTemplate(fixture, 'test-1');
expect(template1).toEqual({
Resources: {
topic69831491: {
Type: 'AWS::SNS::Topic',
Metadata: {
'aws:cdk:path': `${fixture.stackNamePrefix}-test-1/topic/Resource`,
},
},
},
});
await fixture.cdk(['synth', fixture.fullStackName('test-2')], { verbose: false });
const template2 = await readTemplate(fixture, 'test-2');
expect(template2).toEqual({
Resources: {
topic152D84A37: {
Type: 'AWS::SNS::Topic',
Metadata: {
'aws:cdk:path': `${fixture.stackNamePrefix}-test-2/topic1/Resource`,
},
},
topic2A4FB547F: {
Type: 'AWS::SNS::Topic',
Metadata: {
'aws:cdk:path': `${fixture.stackNamePrefix}-test-2/topic2/Resource`,
},
},
},
});
}));
test_helpers_1.integTest('ssm parameter provider error', cdk_1.withDefaultFixture(async (fixture) => {
await expect(fixture.cdk(['synth',
fixture.fullStackName('missing-ssm-parameter'),
'-c', 'test:ssm-parameter-name=/does/not/exist'], {
allowErrExit: true,
})).resolves.toContain('SSM parameter not available in account');
}));
test_helpers_1.integTest('automatic ordering', cdk_1.withDefaultFixture(async (fixture) => {
// Deploy the consuming stack which will include the producing stack
await fixture.cdkDeploy('order-consuming');
// Destroy the providing stack which will include the consuming stack
await fixture.cdkDestroy('order-providing');
}));
test_helpers_1.integTest('context setting', cdk_1.withDefaultFixture(async (fixture) => {
await fs_1.promises.writeFile(path.join(fixture.integTestDir, 'cdk.context.json'), JSON.stringify({
contextkey: 'this is the context value',
}));
try {
await expect(fixture.cdk(['context'])).resolves.toContain('this is the context value');
// Test that deleting the contextkey works
await fixture.cdk(['context', '--reset', 'contextkey']);
await expect(fixture.cdk(['context'])).resolves.not.toContain('this is the context value');
// Test that forced delete of the context key does not throw
await fixture.cdk(['context', '-f', '--reset', 'contextkey']);
}
finally {
await fs_1.promises.unlink(path.join(fixture.integTestDir, 'cdk.context.json'));
}
}));
test_helpers_1.integTest('context in stage propagates to top', cdk_1.withDefaultFixture(async (fixture) => {
await expect(fixture.cdkSynth({
// This will make it error to prove that the context bubbles up, and also that we can fail on command
options: ['--no-lookups'],
modEnv: {
INTEG_STACK_SET: 'stage-using-context',
},
allowErrExit: true,
})).resolves.toContain('Context lookups have been disabled');
}));
test_helpers_1.integTest('deploy', cdk_1.withDefaultFixture(async (fixture) => {
var _a;
const stackArn = await fixture.cdkDeploy('test-2', { captureStderr: false });
// verify the number of resources in the stack
const response = await fixture.aws.cloudFormation('describeStackResources', {
StackName: stackArn,
});
expect((_a = response.StackResources) === null || _a === void 0 ? void 0 : _a.length).toEqual(2);
}));
test_helpers_1.integTest('deploy all', cdk_1.withDefaultFixture(async (fixture) => {
const arns = await fixture.cdkDeploy('test-*', { captureStderr: false });
// verify that we only deployed a single stack (there's a single ARN in the output)
expect(arns.split('\n').length).toEqual(2);
}));
test_helpers_1.integTest('nested stack with parameters', cdk_1.withDefaultFixture(async (fixture) => {
var _a;
// STACK_NAME_PREFIX is used in MyTopicParam to allow multiple instances
// of this test to run in parallel, othewise they will attempt to create the same SNS topic.
const stackArn = await fixture.cdkDeploy('with-nested-stack-using-parameters', {
options: ['--parameters', `MyTopicParam=${fixture.stackNamePrefix}ThereIsNoSpoon`],
captureStderr: false,
});
// verify that we only deployed a single stack (there's a single ARN in the output)
expect(stackArn.split('\n').length).toEqual(1);
// verify the number of resources in the stack
const response = await fixture.aws.cloudFormation('describeStackResources', {
StackName: stackArn,
});
expect((_a = response.StackResources) === null || _a === void 0 ? void 0 : _a.length).toEqual(1);
}));
test_helpers_1.integTest('deploy without execute a named change set', cdk_1.withDefaultFixture(async (fixture) => {
var _a;
const changeSetName = 'custom-change-set-name';
const stackArn = await fixture.cdkDeploy('test-2', {
options: ['--no-execute', '--change-set-name', changeSetName],
captureStderr: false,
});
// verify that we only deployed a single stack (there's a single ARN in the output)
expect(stackArn.split('\n').length).toEqual(1);
const response = await fixture.aws.cloudFormation('describeStacks', {
StackName: stackArn,
});
expect((_a = response.Stacks) === null || _a === void 0 ? void 0 : _a[0].StackStatus).toEqual('REVIEW_IN_PROGRESS');
//verify a change set was created with the provided name
const changeSetResponse = await fixture.aws.cloudFormation('listChangeSets', {
StackName: stackArn,
});
const changeSets = changeSetResponse.Summaries || [];
expect(changeSets.length).toEqual(1);
expect(changeSets[0].ChangeSetName).toEqual(changeSetName);
expect(changeSets[0].Status).toEqual('CREATE_COMPLETE');
}));
test_helpers_1.integTest('security related changes without a CLI are expected to fail', cdk_1.withDefaultFixture(async (fixture) => {
// redirect /dev/null to stdin, which means there will not be tty attached
// since this stack includes security-related changes, the deployment should
// immediately fail because we can't confirm the changes
const stackName = 'iam-test';
await expect(fixture.cdkDeploy(stackName, {
options: ['<', '/dev/null'],
neverRequireApproval: false,
})).rejects.toThrow('exited with error');
// Ensure stack was not deployed
await expect(fixture.aws.cloudFormation('describeStacks', {
StackName: fixture.fullStackName(stackName),
})).rejects.toThrow('does not exist');
}));
test_helpers_1.integTest('deploy wildcard with outputs', cdk_1.withDefaultFixture(async (fixture) => {
const outputsFile = path.join(fixture.integTestDir, 'outputs', 'outputs.json');
await fs_1.promises.mkdir(path.dirname(outputsFile), { recursive: true });
await fixture.cdkDeploy(['outputs-test-*'], {
options: ['--outputs-file', outputsFile],
});
const outputs = JSON.parse((await fs_1.promises.readFile(outputsFile, { encoding: 'utf-8' })).toString());
expect(outputs).toEqual({
[`${fixture.stackNamePrefix}-outputs-test-1`]: {
TopicName: `${fixture.stackNamePrefix}-outputs-test-1MyTopic`,
},
[`${fixture.stackNamePrefix}-outputs-test-2`]: {
TopicName: `${fixture.stackNamePrefix}-outputs-test-2MyOtherTopic`,
},
});
}));
test_helpers_1.integTest('deploy with parameters', cdk_1.withDefaultFixture(async (fixture) => {
var _a;
const stackArn = await fixture.cdkDeploy('param-test-1', {
options: [
'--parameters', `TopicNameParam=${fixture.stackNamePrefix}bazinga`,
],
captureStderr: false,
});
const response = await fixture.aws.cloudFormation('describeStacks', {
StackName: stackArn,
});
expect((_a = response.Stacks) === null || _a === void 0 ? void 0 : _a[0].Parameters).toEqual([
{
ParameterKey: 'TopicNameParam',
ParameterValue: `${fixture.stackNamePrefix}bazinga`,
},
]);
}));
test_helpers_1.integTest('update to stack in ROLLBACK_COMPLETE state will delete stack and create a new one', cdk_1.withDefaultFixture(async (fixture) => {
var _a, _b, _c, _d;
// GIVEN
await expect(fixture.cdkDeploy('param-test-1', {
options: [
'--parameters', `TopicNameParam=${fixture.stackNamePrefix}@aww`,
],
captureStderr: false,
})).rejects.toThrow('exited with error');
const response = await fixture.aws.cloudFormation('describeStacks', {
StackName: fixture.fullStackName('param-test-1'),
});
const stackArn = (_a = response.Stacks) === null || _a === void 0 ? void 0 : _a[0].StackId;
expect((_b = response.Stacks) === null || _b === void 0 ? void 0 : _b[0].StackStatus).toEqual('ROLLBACK_COMPLETE');
// WHEN
const newStackArn = await fixture.cdkDeploy('param-test-1', {
options: [
'--parameters', `TopicNameParam=${fixture.stackNamePrefix}allgood`,
],
captureStderr: false,
});
const newStackResponse = await fixture.aws.cloudFormation('describeStacks', {
StackName: newStackArn,
});
// THEN
expect(stackArn).not.toEqual(newStackArn); // new stack was created
expect((_c = newStackResponse.Stacks) === null || _c === void 0 ? void 0 : _c[0].StackStatus).toEqual('CREATE_COMPLETE');
expect((_d = newStackResponse.Stacks) === null || _d === void 0 ? void 0 : _d[0].Parameters).toEqual([
{
ParameterKey: 'TopicNameParam',
ParameterValue: `${fixture.stackNamePrefix}allgood`,
},
]);
}));
test_helpers_1.integTest('stack in UPDATE_ROLLBACK_COMPLETE state can be updated', cdk_1.withDefaultFixture(async (fixture) => {
var _a, _b, _c, _d;
// GIVEN
const stackArn = await fixture.cdkDeploy('param-test-1', {
options: [
'--parameters', `TopicNameParam=${fixture.stackNamePrefix}nice`,
],
captureStderr: false,
});
let response = await fixture.aws.cloudFormation('describeStacks', {
StackName: stackArn,
});
expect((_a = response.Stacks) === null || _a === void 0 ? void 0 : _a[0].StackStatus).toEqual('CREATE_COMPLETE');
// bad parameter name with @ will put stack into UPDATE_ROLLBACK_COMPLETE
await expect(fixture.cdkDeploy('param-test-1', {
options: [
'--parameters', `TopicNameParam=${fixture.stackNamePrefix}@aww`,
],
captureStderr: false,
})).rejects.toThrow('exited with error');
;
response = await fixture.aws.cloudFormation('describeStacks', {
StackName: stackArn,
});
expect((_b = response.Stacks) === null || _b === void 0 ? void 0 : _b[0].StackStatus).toEqual('UPDATE_ROLLBACK_COMPLETE');
// WHEN
await fixture.cdkDeploy('param-test-1', {
options: [
'--parameters', `TopicNameParam=${fixture.stackNamePrefix}allgood`,
],
captureStderr: false,
});
response = await fixture.aws.cloudFormation('describeStacks', {
StackName: stackArn,
});
// THEN
expect((_c = response.Stacks) === null || _c === void 0 ? void 0 : _c[0].StackStatus).toEqual('UPDATE_COMPLETE');
expect((_d = response.Stacks) === null || _d === void 0 ? void 0 : _d[0].Parameters).toEqual([
{
ParameterKey: 'TopicNameParam',
ParameterValue: `${fixture.stackNamePrefix}allgood`,
},
]);
}));
test_helpers_1.integTest('deploy with wildcard and parameters', cdk_1.withDefaultFixture(async (fixture) => {
await fixture.cdkDeploy('param-test-*', {
options: [
'--parameters', `${fixture.stackNamePrefix}-param-test-1:TopicNameParam=${fixture.stackNamePrefix}bazinga`,
'--parameters', `${fixture.stackNamePrefix}-param-test-2:OtherTopicNameParam=${fixture.stackNamePrefix}ThatsMySpot`,
'--parameters', `${fixture.stackNamePrefix}-param-test-3:DisplayNameParam=${fixture.stackNamePrefix}HeyThere`,
'--parameters', `${fixture.stackNamePrefix}-param-test-3:OtherDisplayNameParam=${fixture.stackNamePrefix}AnotherOne`,
],
});
}));
test_helpers_1.integTest('deploy with parameters multi', cdk_1.withDefaultFixture(async (fixture) => {
var _a;
const paramVal1 = `${fixture.stackNamePrefix}bazinga`;
const paramVal2 = `${fixture.stackNamePrefix}=jagshemash`;
const stackArn = await fixture.cdkDeploy('param-test-3', {
options: [
'--parameters', `DisplayNameParam=${paramVal1}`,
'--parameters', `OtherDisplayNameParam=${paramVal2}`,
],
captureStderr: false,
});
const response = await fixture.aws.cloudFormation('describeStacks', {
StackName: stackArn,
});
expect((_a = response.Stacks) === null || _a === void 0 ? void 0 : _a[0].Parameters).toEqual([
{
ParameterKey: 'DisplayNameParam',
ParameterValue: paramVal1,
},
{
ParameterKey: 'OtherDisplayNameParam',
ParameterValue: paramVal2,
},
]);
}));
test_helpers_1.integTest('deploy with notification ARN', cdk_1.withDefaultFixture(async (fixture) => {
var _a;
const topicName = `${fixture.stackNamePrefix}-test-topic`;
const response = await fixture.aws.sns('createTopic', { Name: topicName });
const topicArn = response.TopicArn;
try {
await fixture.cdkDeploy('test-2', {
options: ['--notification-arns', topicArn],
});
// verify that the stack we deployed has our notification ARN
const describeResponse = await fixture.aws.cloudFormation('describeStacks', {
StackName: fixture.fullStackName('test-2'),
});
expect((_a = describeResponse.Stacks) === null || _a === void 0 ? void 0 : _a[0].NotificationARNs).toEqual([topicArn]);
}
finally {
await fixture.aws.sns('deleteTopic', {
TopicArn: topicArn,
});
}
}));
test_helpers_1.integTest('deploy with role', cdk_1.withDefaultFixture(async (fixture) => {
const roleName = `${fixture.stackNamePrefix}-test-role`;
await deleteRole();
const createResponse = await fixture.aws.iam('createRole', {
RoleName: roleName,
AssumeRolePolicyDocument: JSON.stringify({
Version: '2012-10-17',
Statement: [{
Action: 'sts:AssumeRole',
Principal: { Service: 'cloudformation.amazonaws.com' },
Effect: 'Allow',
}, {
Action: 'sts:AssumeRole',
Principal: { AWS: (await fixture.aws.sts('getCallerIdentity', {})).Arn },
Effect: 'Allow',
}],
}),
});
const roleArn = createResponse.Role.Arn;
try {
await fixture.aws.iam('putRolePolicy', {
RoleName: roleName,
PolicyName: 'DefaultPolicy',
PolicyDocument: JSON.stringify({
Version: '2012-10-17',
Statement: [{
Action: '*',
Resource: '*',
Effect: 'Allow',
}],
}),
});
await aws_1.retry(fixture.output, 'Trying to assume fresh role', aws_1.retry.forSeconds(300), async () => {
await fixture.aws.sts('assumeRole', {
RoleArn: roleArn,
RoleSessionName: 'testing',
});
});
// In principle, the role has replicated from 'us-east-1' to wherever we're testing.
// Give it a little more sleep to make sure CloudFormation is not hitting a box
// that doesn't have it yet.
await aws_1.sleep(5000);
await fixture.cdkDeploy('test-2', {
options: ['--role-arn', roleArn],
});
// Immediately delete the stack again before we delete the role.
//
// Since roles are sticky, if we delete the role before the stack, subsequent DeleteStack
// operations will fail when CloudFormation tries to assume the role that's already gone.
await fixture.cdkDestroy('test-2');
}
finally {
await deleteRole();
}
async function deleteRole() {
try {
for (const policyName of (await fixture.aws.iam('listRolePolicies', { RoleName: roleName })).PolicyNames) {
await fixture.aws.iam('deleteRolePolicy', {
RoleName: roleName,
PolicyName: policyName,
});
}
await fixture.aws.iam('deleteRole', { RoleName: roleName });
}
catch (e) {
if (e.message.indexOf('cannot be found') > -1) {
return;
}
throw e;
}
}
}));
test_helpers_1.integTest('cdk diff', cdk_1.withDefaultFixture(async (fixture) => {
const diff1 = await fixture.cdk(['diff', fixture.fullStackName('test-1')]);
expect(diff1).toContain('AWS::SNS::Topic');
const diff2 = await fixture.cdk(['diff', fixture.fullStackName('test-2')]);
expect(diff2).toContain('AWS::SNS::Topic');
// We can make it fail by passing --fail
await expect(fixture.cdk(['diff', '--fail', fixture.fullStackName('test-1')]))
.rejects.toThrow('exited with error');
}));
test_helpers_1.integTest('cdk diff --fail on multiple stacks exits with error if any of the stacks contains a diff', cdk_1.withDefaultFixture(async (fixture) => {
// GIVEN
const diff1 = await fixture.cdk(['diff', fixture.fullStackName('test-1')]);
expect(diff1).toContain('AWS::SNS::Topic');
await fixture.cdkDeploy('test-2');
const diff2 = await fixture.cdk(['diff', fixture.fullStackName('test-2')]);
expect(diff2).toContain('There were no differences');
// WHEN / THEN
await expect(fixture.cdk(['diff', '--fail', fixture.fullStackName('test-1'), fixture.fullStackName('test-2')])).rejects.toThrow('exited with error');
}));
test_helpers_1.integTest('cdk diff --fail with multiple stack exits with if any of the stacks contains a diff', cdk_1.withDefaultFixture(async (fixture) => {
// GIVEN
await fixture.cdkDeploy('test-1');
const diff1 = await fixture.cdk(['diff', fixture.fullStackName('test-1')]);
expect(diff1).toContain('There were no differences');
const diff2 = await fixture.cdk(['diff', fixture.fullStackName('test-2')]);
expect(diff2).toContain('AWS::SNS::Topic');
// WHEN / THEN
await expect(fixture.cdk(['diff', '--fail', fixture.fullStackName('test-1'), fixture.fullStackName('test-2')])).rejects.toThrow('exited with error');
}));
test_helpers_1.integTest('cdk diff --security-only --fail exits when security changes are present', cdk_1.withDefaultFixture(async (fixture) => {
const stackName = 'iam-test';
await expect(fixture.cdk(['diff', '--security-only', '--fail', fixture.fullStackName(stackName)])).rejects.toThrow('exited with error');
}));
test_helpers_1.integTest('deploy stack with docker asset', cdk_1.withDefaultFixture(async (fixture) => {
await fixture.cdkDeploy('docker');
}));
test_helpers_1.integTest('deploy and test stack with lambda asset', cdk_1.withDefaultFixture(async (fixture) => {
var _a, _b;
const stackArn = await fixture.cdkDeploy('lambda', { captureStderr: false });
const response = await fixture.aws.cloudFormation('describeStacks', {
StackName: stackArn,
});
const lambdaArn = (_b = (_a = response.Stacks) === null || _a === void 0 ? void 0 : _a[0].Outputs) === null || _b === void 0 ? void 0 : _b[0].OutputValue;
if (lambdaArn === undefined) {
throw new Error('Stack did not have expected Lambda ARN output');
}
const output = await fixture.aws.lambda('invoke', {
FunctionName: lambdaArn,
});
expect(JSON.stringify(output.Payload)).toContain('dear asset');
}));
test_helpers_1.integTest('cdk ls', cdk_1.withDefaultFixture(async (fixture) => {
const listing = await fixture.cdk(['ls'], { captureStderr: false });
const expectedStacks = [
'conditional-resource',
'docker',
'docker-with-custom-file',
'failed',
'iam-test',
'lambda',
'missing-ssm-parameter',
'order-providing',
'outputs-test-1',
'outputs-test-2',
'param-test-1',
'param-test-2',
'param-test-3',
'termination-protection',
'test-1',
'test-2',
'with-nested-stack',
'with-nested-stack-using-parameters',
'order-consuming',
];
for (const stack of expectedStacks) {
expect(listing).toContain(fixture.fullStackName(stack));
}
}));
test_helpers_1.integTest('synthing a stage with errors leads to failure', cdk_1.withDefaultFixture(async (fixture) => {
const output = await fixture.cdk(['synth'], {
allowErrExit: true,
modEnv: {
INTEG_STACK_SET: 'stage-with-errors',
},
});
expect(output).toContain('This is an error');
}));
test_helpers_1.integTest('synthing a stage with errors can be suppressed', cdk_1.withDefaultFixture(async (fixture) => {
await fixture.cdk(['synth', '--no-validation'], {
modEnv: {
INTEG_STACK_SET: 'stage-with-errors',
},
});
}));
test_helpers_1.integTest('deploy stack without resource', cdk_1.withDefaultFixture(async (fixture) => {
// Deploy the stack without resources
await fixture.cdkDeploy('conditional-resource', { modEnv: { NO_RESOURCE: 'TRUE' } });
// This should have succeeded but not deployed the stack.
await expect(fixture.aws.cloudFormation('describeStacks', { StackName: fixture.fullStackName('conditional-resource') }))
.rejects.toThrow('conditional-resource does not exist');
// Deploy the stack with resources
await fixture.cdkDeploy('conditional-resource');
// Then again WITHOUT resources (this should destroy the stack)
await fixture.cdkDeploy('conditional-resource', { modEnv: { NO_RESOURCE: 'TRUE' } });
await expect(fixture.aws.cloudFormation('describeStacks', { StackName: fixture.fullStackName('conditional-resource') }))
.rejects.toThrow('conditional-resource does not exist');
}));
test_helpers_1.integTest('IAM diff', cdk_1.withDefaultFixture(async (fixture) => {
const output = await fixture.cdk(['diff', fixture.fullStackName('iam-test')]);
// Roughly check for a table like this:
//
// ┌───┬─────────────────┬────────┬────────────────┬────────────────────────────-──┬───────────┐
// │ │ Resource │ Effect │ Action │ Principal │ Condition │
// ├───┼─────────────────┼────────┼────────────────┼───────────────────────────────┼───────────┤
// │ + │ ${SomeRole.Arn} │ Allow │ sts:AssumeRole │ Service:ec2.amazonaws.com │ │
// └───┴─────────────────┴────────┴────────────────┴───────────────────────────────┴───────────┘
expect(output).toContain('${SomeRole.Arn}');
expect(output).toContain('sts:AssumeRole');
expect(output).toContain('ec2.amazonaws.com');
}));
test_helpers_1.integTest('fast deploy', cdk_1.withDefaultFixture(async (fixture) => {
// we are using a stack with a nested stack because CFN will always attempt to
// update a nested stack, which will allow us to verify that updates are actually
// skipped unless --force is specified.
const stackArn = await fixture.cdkDeploy('with-nested-stack', { captureStderr: false });
const changeSet1 = await getLatestChangeSet();
// Deploy the same stack again, there should be no new change set created
await fixture.cdkDeploy('with-nested-stack');
const changeSet2 = await getLatestChangeSet();
expect(changeSet2.ChangeSetId).toEqual(changeSet1.ChangeSetId);
// Deploy the stack again with --force, now we should create a changeset
await fixture.cdkDeploy('with-nested-stack', { options: ['--force'] });
const changeSet3 = await getLatestChangeSet();
expect(changeSet3.ChangeSetId).not.toEqual(changeSet2.ChangeSetId);
// Deploy the stack again with tags, expected to create a new changeset
// even though the resources didn't change.
await fixture.cdkDeploy('with-nested-stack', { options: ['--tags', 'key=value'] });
const changeSet4 = await getLatestChangeSet();
expect(changeSet4.ChangeSetId).not.toEqual(changeSet3.ChangeSetId);
async function getLatestChangeSet() {
var _a, _b, _c;
const response = await fixture.aws.cloudFormation('describeStacks', { StackName: stackArn });
if (!((_a = response.Stacks) === null || _a === void 0 ? void 0 : _a[0])) {
throw new Error('Did not get a ChangeSet at all');
}
fixture.log(`Found Change Set ${(_b = response.Stacks) === null || _b === void 0 ? void 0 : _b[0].ChangeSetId}`);
return (_c = response.Stacks) === null || _c === void 0 ? void 0 : _c[0];
}
}));
test_helpers_1.integTest('failed deploy does not hang', cdk_1.withDefaultFixture(async (fixture) => {
// this will hang if we introduce https://github.com/aws/aws-cdk/issues/6403 again.
await expect(fixture.cdkDeploy('failed')).rejects.toThrow('exited with error');
}));
test_helpers_1.integTest('can still load old assemblies', cdk_1.withDefaultFixture(async (fixture) => {
const cxAsmDir = path.join(os.tmpdir(), 'cdk-integ-cx');
const testAssembliesDirectory = path.join(__dirname, 'cloud-assemblies');
for (const asmdir of await listChildDirs(testAssembliesDirectory)) {
fixture.log(`ASSEMBLY ${asmdir}`);
await cdk_1.cloneDirectory(asmdir, cxAsmDir);
// Some files in the asm directory that have a .js extension are
// actually treated as templates. Evaluate them using NodeJS.
const templates = await listChildren(cxAsmDir, fullPath => Promise.resolve(fullPath.endsWith('.js')));
for (const template of templates) {
const targetName = template.replace(/.js$/, '');
await cdk_1.shell([process.execPath, template, '>', targetName], {
cwd: cxAsmDir,
output: fixture.output,
modEnv: {
TEST_ACCOUNT: await fixture.aws.account(),
TEST_REGION: fixture.aws.region,
},
});
}
// Use this directory as a Cloud Assembly
const output = await fixture.cdk([
'--app', cxAsmDir,
'-v',
'synth',
]);
// Assert that there was no providerError in CDK's stderr
// Because we rely on the app/framework to actually error in case the
// provider fails, we inspect the logs here.
expect(output).not.toContain('$providerError');
}
}));
test_helpers_1.integTest('generating and loading assembly', cdk_1.withDefaultFixture(async (fixture) => {
const asmOutputDir = `${fixture.integTestDir}-cdk-integ-asm`;
await fixture.shell(['rm', '-rf', asmOutputDir]);
// Synthesize a Cloud Assembly tothe default directory (cdk.out) and a specific directory.
await fixture.cdk(['synth']);
await fixture.cdk(['synth', '--output', asmOutputDir]);
// cdk.out in the current directory and the indicated --output should be the same
await fixture.shell(['diff', 'cdk.out', asmOutputDir]);
// Check that we can 'ls' the synthesized asm.
// Change to some random directory to make sure we're not accidentally loading cdk.json
const list = await fixture.cdk(['--app', asmOutputDir, 'ls'], { cwd: os.tmpdir() });
// Same stacks we know are in the app
expect(list).toContain(`${fixture.stackNamePrefix}-lambda`);
expect(list).toContain(`${fixture.stackNamePrefix}-test-1`);
expect(list).toContain(`${fixture.stackNamePrefix}-test-2`);
// Check that we can use '.' and just synth ,the generated asm
const stackTemplate = await fixture.cdk(['--app', '.', 'synth', fixture.fullStackName('test-2')], {
cwd: asmOutputDir,
});
expect(stackTemplate).toContain('topic152D84A37');
// Deploy a Lambda from the copied asm
await fixture.cdkDeploy('lambda', { options: ['-a', '.'], cwd: asmOutputDir });
// Remove (rename) the original custom docker file that was used during synth.
// this verifies that the assemly has a copy of it and that the manifest uses
// relative paths to reference to it.
const customDockerFile = path.join(fixture.integTestDir, 'docker', 'Dockerfile.Custom');
await fs_1.promises.rename(customDockerFile, `${customDockerFile}~`);
try {
// deploy a docker image with custom file without synth (uses assets)
await fixture.cdkDeploy('docker-with-custom-file', { options: ['-a', '.'], cwd: asmOutputDir });
}
finally {
// Rename back to restore fixture to original state
await fs_1.promises.rename(`${customDockerFile}~`, customDockerFile);
}
}));
test_helpers_1.integTest('templates on disk contain metadata resource, also in nested assemblies', cdk_1.withDefaultFixture(async (fixture) => {
// Synth first, and switch on version reporting because cdk.json is disabling it
await fixture.cdk(['synth', '--version-reporting=true']);
// Load template from disk from root assembly
const templateContents = await fixture.shell(['cat', 'cdk.out/*-lambda.template.json']);
expect(JSON.parse(templateContents).Resources.CDKMetadata).toBeTruthy();
// Load template from nested assembly
const nestedTemplateContents = await fixture.shell(['cat', 'cdk.out/assembly-*-stage/*-stage-StackInStage.template.json']);
expect(JSON.parse(nestedTemplateContents).Resources.CDKMetadata).toBeTruthy();
}));
async function listChildren(parent, pred) {
const ret = new Array();
for (const child of await fs_1.promises.readdir(parent, { encoding: 'utf-8' })) {
const fullPath = path.join(parent, child.toString());
if (await pred(fullPath)) {
ret.push(fullPath);
}
}
return ret;
}
async function listChildDirs(parent) {
return listChildren(parent, async (fullPath) => (await fs_1.promises.stat(fullPath)).isDirectory());
}
async function readTemplate(fixture, stackName) {
const fullStackName = fixture.fullStackName(stackName);
const templatePath = path.join(fixture.integTestDir, 'cdk.out', `${fullStackName}.template.json`);
return JSON.parse((await fs_1.promises.readFile(templatePath, { encoding: 'utf-8' })).toString());
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLmludGVndGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImNsaS5pbnRlZ3Rlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSwyQkFBb0M7QUFDcEMseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3Qix3Q0FBOEM7QUFDOUMsd0NBQXdGO0FBQ3hGLDBEQUFvRDtBQUVwRCxJQUFJLENBQUMsVUFBVSxDQUFDLEdBQUcsR0FBRyxJQUFJLENBQUMsQ0FBQztBQUU1Qix3QkFBUyxDQUFDLFlBQVksRUFBRSx3QkFBa0IsQ0FBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLEVBQUU7SUFDM0QsT0FBTyxDQUFDLEdBQUcsQ0FBQywyQ0FBMkMsQ0FBQyxDQUFDO0lBQ3pELE1BQU0sT0FBTyxDQUFDLFVBQVUsQ0FBQyxZQUFZLEVBQUUsRUFBRSxNQUFNLEVBQUUsRUFBRSxrQkFBa0IsRUFBRSxRQUFRLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFFckYsT0FBTyxDQUFDLEdBQUcsQ0FBQyw0Q0FBNEMsQ0FBQyxDQUFDO0lBQzFELE1BQU0sT0FBTyxDQUFDLFNBQVMsQ0FBQyxZQUFZLEVBQUUsRUFBRSxNQUFNLEVBQUUsRUFBRSxrQkFBa0IsRUFBRSxRQUFRLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDcEYsT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO0lBRS9CLE9BQU8sQ0FBQyxHQUFHLENBQUMsc0NBQXNDLENBQUMsQ0FBQztJQUNwRCxNQUFNLE9BQU8sQ0FBQyxTQUFTLENBQUMsWUFBWSxFQUFFLEVBQUUsTUFBTSxFQUFFLEVBQUUsa0JBQWtCLEVBQUUsUUFBUSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBQ3RGLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFFSix3QkFBUyxDQUFDLGdDQUFnQyxFQUFFLHdCQUFrQixDQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsRUFBRTtJQUMvRSxNQUFNLFFBQVEsR0FBRyxNQUFNLE9BQU8sQ0FBQyxHQUFH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