Skip to content

Commit ef25786

Browse files
authored
Merge branch 'main' into merge-back/2.155.0
2 parents 34dcc5a + 574d383 commit ef25786

36 files changed

Lines changed: 4404 additions & 629 deletions

packages/@aws-cdk-testing/cli-integ/lib/aws.ts

Lines changed: 135 additions & 172 deletions
Large diffs are not rendered by default.

packages/@aws-cdk-testing/cli-integ/lib/staging/codeartifact.ts

Lines changed: 191 additions & 86 deletions
Large diffs are not rendered by default.

packages/@aws-cdk-testing/cli-integ/lib/with-cdk-app.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import * as fs from 'fs';
33
import * as os from 'os';
44
import * as path from 'path';
5+
import { DescribeStacksCommand, Stack } from '@aws-sdk/client-cloudformation';
56
import { outputFromStack, AwsClients } from './aws';
67
import { TestContext } from './integ-test';
78
import { findYarnPackages } from './package-sources/repo-source';
@@ -511,7 +512,14 @@ export class TestFixture extends ShellHelper {
511512
const imageRepositoryNames = stacksToDelete.map(stack => outputFromStack('ImageRepositoryName', stack)).filter(defined);
512513
await Promise.all(imageRepositoryNames.map(r => this.aws.deleteImageRepository(r)));
513514

514-
await this.aws.deleteStacks(...stacksToDelete.map(s => s.StackName));
515+
await this.aws.deleteStacks(
516+
...stacksToDelete.map((s) => {
517+
if (!s.StackName) {
518+
throw new Error('Stack name is required to delete a stack.');
519+
}
520+
return s.StackName;
521+
}),
522+
);
515523

516524
// We might have leaked some buckets by upgrading the bootstrap stack. Be
517525
// sure to clean everything.
@@ -529,7 +537,7 @@ export class TestFixture extends ShellHelper {
529537
/**
530538
* Return the stacks starting with our testing prefix that should be deleted
531539
*/
532-
private async deleteableStacks(prefix: string): Promise<AWS.CloudFormation.Stack[]> {
540+
private async deleteableStacks(prefix: string): Promise<Stack[]> {
533541
const statusFilter = [
534542
'CREATE_IN_PROGRESS', 'CREATE_FAILED', 'CREATE_COMPLETE',
535543
'ROLLBACK_IN_PROGRESS', 'ROLLBACK_FAILED', 'ROLLBACK_COMPLETE',
@@ -544,16 +552,21 @@ export class TestFixture extends ShellHelper {
544552
'IMPORT_ROLLBACK_COMPLETE',
545553
];
546554

547-
const response = await this.aws.cloudFormation('describeStacks', {});
555+
const response = await this.aws.cloudFormation.send(new DescribeStacksCommand({}));
548556

549557
return (response.Stacks ?? [])
550-
.filter(s => s.StackName.startsWith(prefix))
551-
.filter(s => statusFilter.includes(s.StackStatus))
552-
.filter(s => s.RootId === undefined); // Only delete parent stacks. Nested stacks are deleted in the process
558+
.filter((s) => s.StackName && s.StackName.startsWith(prefix))
559+
.filter((s) => s.StackStatus && statusFilter.includes(s.StackStatus))
560+
.filter((s) => s.RootId === undefined); // Only delete parent stacks. Nested stacks are deleted in the process
553561
}
554562

555-
private sortBootstrapStacksToTheEnd(stacks: AWS.CloudFormation.Stack[]) {
563+
private sortBootstrapStacksToTheEnd(stacks: Stack[]) {
556564
stacks.sort((a, b) => {
565+
566+
if (!a.StackName || !b.StackName) {
567+
throw new Error('Stack names do not exists. These are required for sorting the bootstrap stacks.');
568+
}
569+
557570
const aBs = a.StackName.startsWith(this.bootstrapStackName);
558571
const bBs = b.StackName.startsWith(this.bootstrapStackName);
559572

packages/@aws-cdk-testing/cli-integ/package.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,19 @@
3939
},
4040
"dependencies": {
4141
"@octokit/rest": "^18.12.0",
42-
"aws-sdk": "^2.1653.0",
42+
"@aws-sdk/client-codeartifact": "3.637.0",
43+
"@aws-sdk/client-cloudformation": "3.637.0",
44+
"@aws-sdk/client-ecr": "3.637.0",
45+
"@aws-sdk/client-ecs": "3.637.0",
46+
"@aws-sdk/client-iam": "3.637.0",
47+
"@aws-sdk/client-lambda": "3.637.0",
48+
"@aws-sdk/client-s3": "3.637.0",
49+
"@aws-sdk/client-sns": "3.637.0",
50+
"@aws-sdk/client-sso": "3.637.0",
51+
"@aws-sdk/client-sts": "3.637.0",
52+
"@aws-sdk/credential-providers": "3.637.0",
53+
"@smithy/util-retry": "3.0.3",
54+
"@smithy/types": "3.3.0",
4355
"axios": "^1.7.2",
4456
"chalk": "^4",
4557
"fs-extra": "^9.1.0",

packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/bootstrapping.integtest.ts

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
/* eslint-disable @aws-cdk/no-literal-partition */
22
import * as fs from 'fs';
33
import * as path from 'path';
4+
import { DescribeStackResourcesCommand, DescribeStacksCommand } from '@aws-sdk/client-cloudformation';
5+
import { DescribeRepositoriesCommand } from '@aws-sdk/client-ecr';
6+
import { CreatePolicyCommand, DeletePolicyCommand, GetRoleCommand } from '@aws-sdk/client-iam';
47
import * as yaml from 'yaml';
58
import { integTest, randomString, withoutBootstrap } from '../../lib';
69
import eventually from '../../lib/eventually';
@@ -15,9 +18,11 @@ integTest('can bootstrap without execution', withoutBootstrap(async (fixture) =>
1518
noExecute: true,
1619
});
1720

18-
const resp = await fixture.aws.cloudFormation('describeStacks', {
19-
StackName: bootstrapStackName,
20-
});
21+
const resp = await fixture.aws.cloudFormation.send(
22+
new DescribeStacksCommand({
23+
StackName: bootstrapStackName,
24+
}),
25+
);
2126

2227
expect(resp.Stacks?.[0].StackStatus).toEqual('REVIEW_IN_PROGRESS');
2328
}));
@@ -145,7 +150,7 @@ integTest('can create a legacy bootstrap stack with --public-access-block-config
145150
tags: 'Foo=Bar',
146151
});
147152

148-
const response = await fixture.aws.cloudFormation('describeStacks', { StackName: bootstrapStackName });
153+
const response = await fixture.aws.cloudFormation.send(new DescribeStacksCommand({ StackName: bootstrapStackName }));
149154
expect(response.Stacks?.[0].Tags).toEqual([
150155
{ Key: 'Foo', Value: 'Bar' },
151156
]);
@@ -167,7 +172,7 @@ integTest('can create multiple legacy bootstrap stacks', withoutBootstrap(async
167172
toolkitStackName: bootstrapStackName2,
168173
});
169174

170-
const response = await fixture.aws.cloudFormation('describeStacks', { StackName: bootstrapStackName1 });
175+
const response = await fixture.aws.cloudFormation.send(new DescribeStacksCommand({ StackName: bootstrapStackName1 }));
171176
expect(response.Stacks?.[0].Tags).toEqual([
172177
{ Key: 'Foo', Value: 'Bar' },
173178
]);
@@ -272,17 +277,19 @@ integTest('can remove customPermissionsBoundary', withoutBootstrap(async (fixtur
272277
const policyName = `${bootstrapStackName}-pb`;
273278
let policyArn;
274279
try {
275-
const policy = await fixture.aws.iam('createPolicy', {
276-
PolicyName: policyName,
277-
PolicyDocument: JSON.stringify({
278-
Version: '2012-10-17',
279-
Statement: {
280-
Action: ['*'],
281-
Resource: ['*'],
282-
Effect: 'Allow',
283-
},
280+
const policy = await fixture.aws.iam.send(
281+
new CreatePolicyCommand({
282+
PolicyName: policyName,
283+
PolicyDocument: JSON.stringify({
284+
Version: '2012-10-17',
285+
Statement: {
286+
Action: ['*'],
287+
Resource: ['*'],
288+
Effect: 'Allow',
289+
},
290+
}),
284291
}),
285-
});
292+
);
286293
policyArn = policy.Policy?.Arn;
287294

288295
// Policy creation and consistency across regions is "almost immediate"
@@ -295,7 +302,9 @@ integTest('can remove customPermissionsBoundary', withoutBootstrap(async (fixtur
295302
customPermissionsBoundary: policyName,
296303
});
297304

298-
const response = await fixture.aws.cloudFormation('describeStacks', { StackName: bootstrapStackName });
305+
const response = await fixture.aws.cloudFormation.send(
306+
new DescribeStacksCommand({ StackName: bootstrapStackName }),
307+
);
299308
expect(
300309
response.Stacks?.[0].Parameters?.some(
301310
param => (param.ParameterKey === 'InputPermissionsBoundary' && param.ParameterValue === policyName),
@@ -309,20 +318,27 @@ integTest('can remove customPermissionsBoundary', withoutBootstrap(async (fixtur
309318
toolkitStackName: bootstrapStackName,
310319
usePreviousParameters: false,
311320
});
312-
const response2 = await fixture.aws.cloudFormation('describeStacks', { StackName: bootstrapStackName });
321+
const response2 = await fixture.aws.cloudFormation.send(
322+
new DescribeStacksCommand({ StackName: bootstrapStackName }),
323+
);
313324
expect(
314325
response2.Stacks?.[0].Parameters?.some(
315326
param => (param.ParameterKey === 'InputPermissionsBoundary' && !param.ParameterValue),
316327
)).toEqual(true);
317328

318329
const region = fixture.aws.region;
319330
const account = await fixture.aws.account();
320-
const role = await fixture.aws.iam('getRole', { RoleName: `cdk-${fixture.qualifier}-cfn-exec-role-${account}-${region}` });
331+
const role = await fixture.aws.iam.send(
332+
new GetRoleCommand({ RoleName: `cdk-${fixture.qualifier}-cfn-exec-role-${account}-${region}` }),
333+
);
334+
if (!role.Role) {
335+
throw new Error('Role not found');
336+
}
321337
expect(role.Role.PermissionsBoundary).toBeUndefined();
322338

323339
} finally {
324340
if (policyArn) {
325-
await fixture.aws.iam('deletePolicy', { PolicyArn: policyArn });
341+
await fixture.aws.iam.send(new DeletePolicyCommand({ PolicyArn: policyArn }));
326342
}
327343
}
328344
}));
@@ -342,7 +358,7 @@ integTest('switch on termination protection, switch is left alone on re-bootstra
342358
force: true,
343359
});
344360

345-
const response = await fixture.aws.cloudFormation('describeStacks', { StackName: bootstrapStackName });
361+
const response = await fixture.aws.cloudFormation.send(new DescribeStacksCommand({ StackName: bootstrapStackName }));
346362
expect(response.Stacks?.[0].EnableTerminationProtection).toEqual(true);
347363
}));
348364

@@ -361,7 +377,7 @@ integTest('add tags, left alone on re-bootstrap', withoutBootstrap(async (fixtur
361377
force: true,
362378
});
363379

364-
const response = await fixture.aws.cloudFormation('describeStacks', { StackName: bootstrapStackName });
380+
const response = await fixture.aws.cloudFormation.send(new DescribeStacksCommand({ StackName: bootstrapStackName }));
365381
expect(response.Stacks?.[0].Tags).toEqual([
366382
{ Key: 'Foo', Value: 'Bar' },
367383
]);
@@ -384,7 +400,7 @@ integTest('can add tags then update tags during re-bootstrap', withoutBootstrap(
384400
force: true,
385401
});
386402

387-
const response = await fixture.aws.cloudFormation('describeStacks', { StackName: bootstrapStackName });
403+
const response = await fixture.aws.cloudFormation.send(new DescribeStacksCommand({ StackName: bootstrapStackName }));
388404
expect(response.Stacks?.[0].Tags).toEqual([
389405
{ Key: 'Foo', Value: 'BarBaz' },
390406
]);
@@ -418,18 +434,22 @@ integTest('create ECR with tag IMMUTABILITY to set on', withoutBootstrap(async (
418434
toolkitStackName: bootstrapStackName,
419435
});
420436

421-
const response = await fixture.aws.cloudFormation('describeStackResources', {
422-
StackName: bootstrapStackName,
423-
});
437+
const response = await fixture.aws.cloudFormation.send(
438+
new DescribeStackResourcesCommand({
439+
StackName: bootstrapStackName,
440+
}),
441+
);
424442
const ecrResource = response.StackResources?.find(resource => resource.LogicalResourceId === 'ContainerAssetsRepository');
425443
expect(ecrResource).toBeDefined();
426444

427-
const ecrResponse = await fixture.aws.ecr('describeRepositories', {
428-
repositoryNames: [
429-
// This is set, as otherwise we don't end up here
430-
ecrResource?.PhysicalResourceId ?? '',
431-
],
432-
});
445+
const ecrResponse = await fixture.aws.ecr.send(
446+
new DescribeRepositoriesCommand({
447+
repositoryNames: [
448+
// This is set, as otherwise we don't end up here
449+
ecrResource?.PhysicalResourceId ?? '',
450+
],
451+
}),
452+
);
433453

434454
expect(ecrResponse.repositories?.[0].imageTagMutability).toEqual('IMMUTABLE');
435455
}));
Lines changed: 78 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,90 @@
1+
import { DescribeStackResourcesCommand, DescribeStacksCommand } from '@aws-sdk/client-cloudformation';
12
import { integTest, withCliLibFixture } from '../../lib';
23

34
jest.setTimeout(2 * 60 * 60_000); // Includes the time to acquire locks, worst-case single-threaded runtime
45

5-
integTest('cli-lib synth', withCliLibFixture(async (fixture) => {
6-
await fixture.cdk(['synth', fixture.fullStackName('simple-1')]);
7-
expect(fixture.template('simple-1')).toEqual(expect.objectContaining({
8-
// Checking for a small subset is enough as proof that synth worked
9-
Resources: expect.objectContaining({
10-
queue276F7297: expect.objectContaining({
11-
Type: 'AWS::SQS::Queue',
12-
Properties: {
13-
VisibilityTimeout: 300,
14-
},
15-
Metadata: {
16-
'aws:cdk:path': `${fixture.stackNamePrefix}-simple-1/queue/Resource`,
17-
},
6+
integTest(
7+
'cli-lib synth',
8+
withCliLibFixture(async (fixture) => {
9+
await fixture.cdk(['synth', fixture.fullStackName('simple-1')]);
10+
expect(fixture.template('simple-1')).toEqual(
11+
expect.objectContaining({
12+
// Checking for a small subset is enough as proof that synth worked
13+
Resources: expect.objectContaining({
14+
queue276F7297: expect.objectContaining({
15+
Type: 'AWS::SQS::Queue',
16+
Properties: {
17+
VisibilityTimeout: 300,
18+
},
19+
Metadata: {
20+
'aws:cdk:path': `${fixture.stackNamePrefix}-simple-1/queue/Resource`,
21+
},
22+
}),
23+
}),
1824
}),
19-
}),
20-
}));
21-
}));
25+
);
26+
}),
27+
);
2228

23-
integTest('cli-lib list', withCliLibFixture(async (fixture) => {
24-
const listing = await fixture.cdk(['list'], { captureStderr: false });
25-
expect(listing).toContain(fixture.fullStackName('simple-1'));
26-
}));
29+
integTest(
30+
'cli-lib list',
31+
withCliLibFixture(async (fixture) => {
32+
const listing = await fixture.cdk(['list'], { captureStderr: false });
33+
expect(listing).toContain(fixture.fullStackName('simple-1'));
34+
}),
35+
);
2736

28-
integTest('cli-lib deploy', withCliLibFixture(async (fixture) => {
29-
const stackName = fixture.fullStackName('simple-1');
37+
integTest(
38+
'cli-lib deploy',
39+
withCliLibFixture(async (fixture) => {
40+
const stackName = fixture.fullStackName('simple-1');
3041

31-
try {
32-
// deploy the stack
33-
await fixture.cdk(['deploy', stackName], {
34-
neverRequireApproval: true,
35-
});
42+
try {
43+
// deploy the stack
44+
await fixture.cdk(['deploy', stackName], {
45+
neverRequireApproval: true,
46+
});
3647

37-
// verify the number of resources in the stack
38-
const expectedStack = await fixture.aws.cloudFormation('describeStackResources', {
39-
StackName: stackName,
40-
});
41-
expect(expectedStack.StackResources?.length).toEqual(3);
42-
} finally {
43-
// delete the stack
44-
await fixture.cdk(['destroy', stackName], {
45-
captureStderr: false,
46-
});
47-
}
48-
}));
48+
// verify the number of resources in the stack
49+
const expectedStack = await fixture.aws.cloudFormation.send(
50+
new DescribeStackResourcesCommand({
51+
StackName: stackName,
52+
}),
53+
);
54+
expect(expectedStack.StackResources?.length).toEqual(3);
55+
} finally {
56+
// delete the stack
57+
await fixture.cdk(['destroy', stackName], {
58+
captureStderr: false,
59+
});
60+
}
61+
}),
62+
);
4963

50-
integTest('security related changes without a CLI are expected to fail when approval is required', withCliLibFixture(async (fixture) => {
51-
const stdErr = await fixture.cdk(['deploy', fixture.fullStackName('simple-1')], {
52-
onlyStderr: true,
53-
captureStderr: true,
54-
allowErrExit: true,
55-
neverRequireApproval: false,
56-
});
64+
integTest(
65+
'security related changes without a CLI are expected to fail when approval is required',
66+
withCliLibFixture(async (fixture) => {
67+
const stdErr = await fixture.cdk(['deploy', fixture.fullStackName('simple-1')], {
68+
onlyStderr: true,
69+
captureStderr: true,
70+
allowErrExit: true,
71+
neverRequireApproval: false,
72+
});
5773

58-
expect(stdErr).toContain('This deployment will make potentially sensitive changes according to your current security approval level');
59-
expect(stdErr).toContain('Deployment failed: Error: \"--require-approval\" is enabled and stack includes security-sensitive updates');
74+
expect(stdErr).toContain(
75+
'This deployment will make potentially sensitive changes according to your current security approval level',
76+
);
77+
expect(stdErr).toContain(
78+
'Deployment failed: Error: "--require-approval" is enabled and stack includes security-sensitive updates',
79+
);
6080

61-
// Ensure stack was not deployed
62-
await expect(fixture.aws.cloudFormation('describeStacks', {
63-
StackName: fixture.fullStackName('simple-1'),
64-
})).rejects.toThrow('does not exist');
65-
}));
81+
// Ensure stack was not deployed
82+
await expect(
83+
fixture.aws.cloudFormation.send(
84+
new DescribeStacksCommand({
85+
StackName: fixture.fullStackName('simple-1'),
86+
}),
87+
),
88+
).rejects.toThrow('does not exist');
89+
}),
90+
);

0 commit comments

Comments
 (0)