Skip to content

Commit 9ad3353

Browse files
committed
move e2e tests to cdk v2
1 parent 4941a34 commit 9ad3353

File tree

11 files changed

+784
-1455
lines changed

11 files changed

+784
-1455
lines changed

package-lock.json

Lines changed: 521 additions & 1428 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@
4747
},
4848
"homepage": "https://github.com/awslabs/aws-lambda-powertools-typescript#readme",
4949
"devDependencies": {
50-
"@aws-cdk/aws-dynamodb": "^1.146.0",
51-
"@aws-cdk/aws-lambda-nodejs": "^1.146.0",
52-
"@aws-cdk/cloudformation-diff": "^1.146.0",
53-
"@aws-cdk/core": "^1.146.0",
50+
"@aws-cdk/cloud-assembly-schema": "^2.17.0",
51+
"@aws-cdk/cloudformation-diff": "^2.17.0",
52+
"aws-cdk-lib": "^2.17.0",
53+
"@aws-cdk/cx-api": "^2.17.0",
5454
"@commitlint/cli": "^16.2.1",
5555
"@middy/core": "^2.5.6",
5656
"@types/aws-lambda": "^8.10.72",
@@ -59,9 +59,9 @@
5959
"@typescript-eslint/eslint-plugin": "^5.12.1",
6060
"@typescript-eslint/parser": "^5.12.1",
6161
"archiver": "^5.3.0",
62-
"aws-cdk": "^1.146.0",
62+
"aws-cdk": "^2.17.0",
6363
"aws-sdk": "^2.1082.0",
64-
"cdk-assets": "^1.146.0",
64+
"cdk-assets": "^2.17.0",
6565
"esbuild": "^0.14.23",
6666
"eslint": "^8.4.0",
6767
"eslint-import-resolver-node": "^0.3.6",
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
// This file is duplicating CloudFormationDeployments define in https://github.com/aws/aws-cdk/blob/master/packages/aws-cdk/lib/api/cloudformation-deployments.ts
2+
// This was needed to overwrite isAssetManifestArtifact function which was preventing proper assets to be created due to difference between cloudAssembly in @aws-cdk vs. aws-cdk-lib
3+
4+
/* eslint-disable @typescript-eslint/ban-ts-comment */
5+
/* eslint-disable @typescript-eslint/explicit-function-return-type */
6+
/* eslint-disable @typescript-eslint/member-ordering */
7+
/* eslint-disable @typescript-eslint/explicit-member-accessibility */
8+
/* eslint-disable @typescript-eslint/member-delimiter-style */
9+
/* eslint-disable newline-before-return */
10+
/* eslint-disable func-style */
11+
import * as cxapi from '@aws-cdk/cx-api';
12+
import { AssetManifest } from 'cdk-assets';
13+
import { debug } from 'aws-cdk/lib/logging';
14+
import { publishAssets } from 'aws-cdk/lib/util/asset-publishing';
15+
import { Mode } from 'aws-cdk/lib/api/aws-auth/credentials';
16+
import { ISDK } from 'aws-cdk/lib/api/aws-auth/sdk';
17+
import { SdkProvider } from 'aws-cdk/lib/api/aws-auth/sdk-provider';
18+
import { deployStack, DeployStackResult, destroyStack } from 'aws-cdk/lib/api/deploy-stack';
19+
import { loadCurrentTemplateWithNestedStacks, loadCurrentTemplate } from 'aws-cdk/lib/api/nested-stack-helpers';
20+
import { ToolkitInfo } from 'aws-cdk/lib/api/toolkit-info';
21+
import { CloudFormationStack, Template } from 'aws-cdk/lib/api/util/cloudformation';
22+
import { replaceEnvPlaceholders } from 'aws-cdk/lib/api/util/placeholders';
23+
import {
24+
ProvisionerProps,
25+
DeployStackOptions,
26+
DestroyStackOptions,
27+
prepareSdkWithLookupRoleFor,
28+
StackExistsOptions,
29+
PreparedSdkForEnvironment,
30+
} from 'aws-cdk/lib/api/cloudformation-deployments';
31+
32+
/**
33+
* Helper class for CloudFormation deployments
34+
*
35+
* Looks us the right SDK and Bootstrap stack to deploy a given
36+
* stack artifact.
37+
*/
38+
export class CloudFormationDeployments {
39+
private readonly sdkProvider: SdkProvider;
40+
41+
constructor(props: ProvisionerProps) {
42+
this.sdkProvider = props.sdkProvider;
43+
}
44+
45+
public async readCurrentTemplateWithNestedStacks(
46+
rootStackArtifact: cxapi.CloudFormationStackArtifact
47+
): Promise<Template> {
48+
const sdk = await this.prepareSdkWithLookupOrDeployRole(rootStackArtifact);
49+
return (await loadCurrentTemplateWithNestedStacks(rootStackArtifact, sdk)).deployedTemplate;
50+
}
51+
52+
public async readCurrentTemplate(stackArtifact: cxapi.CloudFormationStackArtifact): Promise<Template> {
53+
debug(`Reading existing template for stack ${stackArtifact.displayName}.`);
54+
const sdk = await this.prepareSdkWithLookupOrDeployRole(stackArtifact);
55+
return loadCurrentTemplate(stackArtifact, sdk);
56+
}
57+
58+
public async deployStack(options: DeployStackOptions): Promise<DeployStackResult> {
59+
const { stackSdk, resolvedEnvironment, cloudFormationRoleArn } = await this.prepareSdkFor(
60+
options.stack,
61+
options.roleArn
62+
);
63+
64+
const toolkitInfo = await ToolkitInfo.lookup(resolvedEnvironment, stackSdk, options.toolkitStackName);
65+
66+
// Publish any assets before doing the actual deploy
67+
await this.publishStackAssets(options.stack, toolkitInfo);
68+
69+
// Do a verification of the bootstrap stack version
70+
await this.validateBootstrapStackVersion(
71+
options.stack.stackName,
72+
options.stack.requiresBootstrapStackVersion,
73+
options.stack.bootstrapStackVersionSsmParameter,
74+
toolkitInfo
75+
);
76+
77+
return deployStack({
78+
stack: options.stack,
79+
resolvedEnvironment,
80+
deployName: options.deployName,
81+
notificationArns: options.notificationArns,
82+
quiet: options.quiet,
83+
sdk: stackSdk,
84+
sdkProvider: this.sdkProvider,
85+
roleArn: cloudFormationRoleArn,
86+
reuseAssets: options.reuseAssets,
87+
toolkitInfo,
88+
tags: options.tags,
89+
execute: options.execute,
90+
changeSetName: options.changeSetName,
91+
force: options.force,
92+
parameters: options.parameters,
93+
usePreviousParameters: options.usePreviousParameters,
94+
progress: options.progress,
95+
ci: options.ci,
96+
rollback: options.rollback,
97+
hotswap: options.hotswap,
98+
extraUserAgent: options.extraUserAgent,
99+
});
100+
}
101+
102+
public async destroyStack(options: DestroyStackOptions): Promise<void> {
103+
const { stackSdk, cloudFormationRoleArn: roleArn } = await this.prepareSdkFor(options.stack, options.roleArn);
104+
105+
return destroyStack({
106+
sdk: stackSdk,
107+
roleArn,
108+
stack: options.stack,
109+
deployName: options.deployName,
110+
quiet: options.quiet,
111+
});
112+
}
113+
114+
public async stackExists(options: StackExistsOptions): Promise<boolean> {
115+
const { stackSdk } = await this.prepareSdkFor(options.stack, undefined, Mode.ForReading);
116+
const stack = await CloudFormationStack.lookup(
117+
stackSdk.cloudFormation(),
118+
options.deployName ?? options.stack.stackName
119+
);
120+
return stack.exists;
121+
}
122+
123+
private async prepareSdkWithLookupOrDeployRole(stackArtifact: cxapi.CloudFormationStackArtifact): Promise<ISDK> {
124+
// try to assume the lookup role
125+
try {
126+
const result = await prepareSdkWithLookupRoleFor(this.sdkProvider, stackArtifact);
127+
if (result.didAssumeRole) {
128+
return result.sdk;
129+
}
130+
} catch {}
131+
// fall back to the deploy role
132+
return (await this.prepareSdkFor(stackArtifact, undefined, Mode.ForReading)).stackSdk;
133+
}
134+
135+
/**
136+
* Get the environment necessary for touching the given stack
137+
*
138+
* Returns the following:
139+
*
140+
* - The resolved environment for the stack (no more 'unknown-account/unknown-region')
141+
* - SDK loaded with the right credentials for calling `CreateChangeSet`.
142+
* - The Execution Role that should be passed to CloudFormation.
143+
*/
144+
private async prepareSdkFor(
145+
stack: cxapi.CloudFormationStackArtifact,
146+
roleArn?: string,
147+
mode = Mode.ForWriting
148+
): Promise<PreparedSdkForEnvironment> {
149+
if (!stack.environment) {
150+
throw new Error(`The stack ${stack.displayName} does not have an environment`);
151+
}
152+
153+
const resolvedEnvironment = await this.sdkProvider.resolveEnvironment(stack.environment);
154+
155+
// Substitute any placeholders with information about the current environment
156+
const arns = await replaceEnvPlaceholders(
157+
{
158+
assumeRoleArn: stack.assumeRoleArn,
159+
160+
// Use the override if given, otherwise use the field from the stack
161+
cloudFormationRoleArn: roleArn ?? stack.cloudFormationExecutionRoleArn,
162+
},
163+
resolvedEnvironment,
164+
this.sdkProvider
165+
);
166+
167+
const stackSdk = await this.sdkProvider.forEnvironment(resolvedEnvironment, mode, {
168+
assumeRoleArn: arns.assumeRoleArn,
169+
assumeRoleExternalId: stack.assumeRoleExternalId,
170+
});
171+
172+
return {
173+
stackSdk: stackSdk.sdk,
174+
resolvedEnvironment,
175+
cloudFormationRoleArn: arns.cloudFormationRoleArn,
176+
};
177+
}
178+
179+
/**
180+
* Publish all asset manifests that are referenced by the given stack
181+
*/
182+
private async publishStackAssets(stack: cxapi.CloudFormationStackArtifact, toolkitInfo: ToolkitInfo) {
183+
const stackEnv = await this.sdkProvider.resolveEnvironment(stack.environment);
184+
const assetArtifacts = stack.dependencies.filter(isAssetManifestArtifact);
185+
186+
for (const assetArtifact of assetArtifacts) {
187+
await this.validateBootstrapStackVersion(
188+
stack.stackName,
189+
assetArtifact.requiresBootstrapStackVersion,
190+
assetArtifact.bootstrapStackVersionSsmParameter,
191+
toolkitInfo
192+
);
193+
194+
const manifest = AssetManifest.fromFile(assetArtifact.file);
195+
await publishAssets(manifest, this.sdkProvider, stackEnv);
196+
}
197+
}
198+
199+
/**
200+
* Validate that the bootstrap stack has the right version for this stack
201+
*/
202+
private async validateBootstrapStackVersion(
203+
stackName: string,
204+
requiresBootstrapStackVersion: number | undefined,
205+
bootstrapStackVersionSsmParameter: string | undefined,
206+
toolkitInfo: ToolkitInfo
207+
) {
208+
if (requiresBootstrapStackVersion === undefined) {
209+
return;
210+
}
211+
212+
try {
213+
await toolkitInfo.validateVersion(requiresBootstrapStackVersion, bootstrapStackVersionSsmParameter);
214+
} catch (e) {
215+
// @ts-ignore
216+
throw new Error(`${stackName}: ${e.message}`);
217+
}
218+
}
219+
}
220+
221+
function isAssetManifestArtifact(art: cxapi.CloudArtifact): art is cxapi.AssetManifestArtifact {
222+
return Object.keys(art).includes('file');
223+
}

packages/commons/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"inlineSourceMap": true,
1313
"moduleResolution": "node",
1414
"resolveJsonModule": true,
15+
"useUnknownInCatchVariables": false,
1516
"pretty": true,
1617
"baseUrl": "src/",
1718
"rootDirs": [ "src/" ]

packages/logger/tests/e2e/basicFeatures.middy.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import path from 'path';
1111
import { randomUUID } from 'crypto';
12-
import { App, Stack } from '@aws-cdk/core';
12+
import { App, Stack } from 'aws-cdk-lib';
1313
import { createStackWithLambdaFunction, deployStack, destroyStack, generateUniqueName, invokeFunction, isValidRuntimeKey } from '../helpers/e2eUtils';
1414
import { InvocationLogs } from '../helpers/InvocationLogs';
1515

packages/logger/tests/e2e/childLogger.manual.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import path from 'path';
1111
import { randomUUID } from 'crypto';
12-
import { App, Stack } from '@aws-cdk/core';
12+
import { App, Stack } from 'aws-cdk-lib';
1313
import { createStackWithLambdaFunction, deployStack, destroyStack, generateUniqueName, invokeFunction, isValidRuntimeKey } from '../helpers/e2eUtils';
1414
import { InvocationLogs } from '../helpers/InvocationLogs';
1515

packages/logger/tests/e2e/sampleRate.decorator.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import path from 'path';
1111
import { randomUUID } from 'crypto';
12-
import { App, Stack } from '@aws-cdk/core';
12+
import { App, Stack } from 'aws-cdk-lib';
1313
import { createStackWithLambdaFunction, deployStack, destroyStack, generateUniqueName, invokeFunction, isValidRuntimeKey } from '../helpers/e2eUtils';
1414
import { InvocationLogs } from '../helpers/InvocationLogs';
1515

packages/logger/tests/helpers/e2eUtils.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1+
/* eslint-disable @typescript-eslint/ban-ts-comment */
12
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
23
// SPDX-License-Identifier: MIT-0
34

4-
import { CloudFormationStackArtifact } from '@aws-cdk/cx-api';
5+
import { CloudFormationStackArtifact } from 'aws-cdk-lib/cx-api';
56
import { SdkProvider } from 'aws-cdk/lib/api/aws-auth';
6-
import { CloudFormationDeployments } from 'aws-cdk/lib/api/cloudformation-deployments';
7-
import { App, CfnOutput, Stack } from '@aws-cdk/core';
8-
import * as lambda from '@aws-cdk/aws-lambda-nodejs';
9-
import { Runtime } from '@aws-cdk/aws-lambda';
7+
import { CloudFormationDeployments } from '../../../commons/tests/utils/cloudformation-deployments';
8+
import { App, CfnOutput, Stack } from 'aws-cdk-lib';
9+
import * as lambda from 'aws-cdk-lib/aws-lambda-nodejs';
10+
import { Runtime } from 'aws-cdk-lib/aws-lambda';
1011
import * as AWS from 'aws-sdk';
1112

1213
import { InvocationLogs } from './InvocationLogs';
@@ -61,6 +62,7 @@ export const deployStack = async (stackArtifact: CloudFormationStackArtifact ):
6162

6263
// WHEN lambda function is deployed
6364
const result = await cloudFormation.deployStack({
65+
// @ts-ignore
6466
stack: stackArtifact,
6567
quiet: true,
6668
});
@@ -107,6 +109,7 @@ export const destroyStack = async (app: App, stack: Stack): Promise<void> => {
107109
const cloudFormation = new CloudFormationDeployments({ sdkProvider });
108110

109111
await cloudFormation.destroyStack({
112+
// @ts-ignore
110113
stack: stackArtifact,
111114
quiet: true,
112115
});

packages/metrics/tests/e2e/decorator.test.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable @typescript-eslint/ban-ts-comment */
12
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
23
// SPDX-License-Identifier: MIT-0
34

@@ -8,11 +9,11 @@
89
*/
910

1011
import { randomUUID } from 'crypto';
11-
import * as lambda from '@aws-cdk/aws-lambda-nodejs';
12-
import { Tracing } from '@aws-cdk/aws-lambda';
13-
import { App, Stack } from '@aws-cdk/core';
12+
import * as lambda from 'aws-cdk-lib/aws-lambda-nodejs';
13+
import { Tracing } from 'aws-cdk-lib/aws-lambda';
14+
import { App, Stack } from 'aws-cdk-lib';
1415
import { SdkProvider } from 'aws-cdk/lib/api/aws-auth';
15-
import { CloudFormationDeployments } from 'aws-cdk/lib/api/cloudformation-deployments';
16+
import { CloudFormationDeployments } from '../../../commons/tests/utils/cloudformation-deployments';
1617
import * as AWS from 'aws-sdk';
1718
import { MetricUnits } from '../../src';
1819
import { getMetrics } from '../helpers/metricsUtils';
@@ -70,6 +71,7 @@ describe('happy cases', () => {
7071
// WHEN
7172
// lambda function is deployed
7273
await cloudFormation.deployStack({
74+
// @ts-ignore
7375
stack: stackArtifact,
7476
quiet: true,
7577
});
@@ -169,6 +171,7 @@ describe('happy cases', () => {
169171
const cloudFormation = new CloudFormationDeployments({ sdkProvider });
170172

171173
await cloudFormation.destroyStack({
174+
// @ts-ignore
172175
stack: stackArtifact,
173176
quiet: true,
174177
});

packages/metrics/tests/e2e/standardFunctions.test.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable @typescript-eslint/ban-ts-comment */
12
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
23
// SPDX-License-Identifier: MIT-0
34

@@ -8,11 +9,11 @@
89
*/
910

1011
import { randomUUID } from 'crypto';
11-
import * as lambda from '@aws-cdk/aws-lambda-nodejs';
12-
import { Tracing } from '@aws-cdk/aws-lambda';
13-
import { App, Stack } from '@aws-cdk/core';
12+
import * as lambda from 'aws-cdk-lib/aws-lambda-nodejs';
13+
import { Tracing } from 'aws-cdk-lib/aws-lambda';
14+
import { App, Stack } from 'aws-cdk-lib';
1415
import { SdkProvider } from 'aws-cdk/lib/api/aws-auth';
15-
import { CloudFormationDeployments } from 'aws-cdk/lib/api/cloudformation-deployments';
16+
import { CloudFormationDeployments } from '../../../commons/tests/utils/cloudformation-deployments';
1617
import * as AWS from 'aws-sdk';
1718
import { MetricUnits } from '../../src';
1819
import { getMetrics } from '../helpers/metricsUtils';
@@ -70,6 +71,7 @@ describe('happy cases', () => {
7071
// WHEN
7172
// lambda function is deployed
7273
await cloudFormation.deployStack({
74+
// @ts-ignore
7375
stack: stackArtifact,
7476
quiet: true,
7577
});
@@ -167,6 +169,7 @@ describe('happy cases', () => {
167169
const cloudFormation = new CloudFormationDeployments({ sdkProvider });
168170

169171
await cloudFormation.destroyStack({
172+
// @ts-ignore
170173
stack: stackArtifact,
171174
quiet: true,
172175
});

0 commit comments

Comments
 (0)