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: improve localstack test performance #184
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
import { performance, PerformanceObserver } from "node:perf_hooks"; | ||
import * as cxapi from "@aws-cdk/cx-api"; | ||
import { App, CfnOutput, Stack } from "aws-cdk-lib"; | ||
import { SdkProvider } from "aws-cdk/lib/api/aws-auth"; | ||
|
@@ -21,7 +22,10 @@ export const clientConfig = { | |
const CF = new CloudFormation(clientConfig); | ||
|
||
export const deployStack = async (app: App, stack: Stack) => { | ||
const cloudAssembly = await asyncSynth(app); | ||
performance.mark("AwaitSynth"); | ||
const cloudAssembly = await asyncSynth(app, {}); | ||
performance.measure("After await synth", "AwaitSynth"); | ||
performance.measure("Add resources", "InvokeResourceGetter"); | ||
|
||
const sdkProvider = await SdkProvider.withAwsCliCompatibleDefaults({ | ||
httpOptions: clientConfig as any, | ||
|
@@ -38,21 +42,29 @@ export const deployStack = async (app: App, stack: Stack) => { | |
credentials: clientConfig.credentials, | ||
}; | ||
|
||
performance.mark("CfnCompile"); | ||
|
||
const cfn = new CloudFormationDeployments({ | ||
sdkProvider, | ||
}); | ||
|
||
const assembly = new cxapi.CloudAssembly(cloudAssembly.directory); | ||
const stackArtifact = cxapi.CloudFormationStackArtifact.fromManifest( | ||
assembly, | ||
stack.artifactId, | ||
cloudAssembly.getStackArtifact(stack.artifactId).manifest | ||
) as cxapi.CloudFormationStackArtifact; | ||
// const assembly = new cxapi.CloudAssembly(cloudAssembly.directory); | ||
// const stackArtifact = cxapi.CloudFormationStackArtifact.fromManifest( | ||
// cloudAssembly, | ||
// stack.artifactId, | ||
// cloudAssembly.getStackArtifact(stack.artifactId).manifest | ||
// ) as cxapi.CloudFormationStackArtifact; | ||
|
||
performance.measure("Complete CfnCompile", "CfnCompile"); | ||
|
||
performance.mark("DeployStack"); | ||
await cfn.deployStack({ | ||
stack: stackArtifact, | ||
stack: cloudAssembly.getStackArtifact( | ||
stack.artifactId | ||
) as unknown as cxapi.CloudFormationStackArtifact, | ||
Comment on lines
+48
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How does this work? What is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cloud assembly is what CDK calls the output of a stack synth. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Original inspiration: aws/aws-cdk#18667 (comment) |
||
force: true, | ||
}); | ||
performance.measure("Deploy Stack complete", "DeployStack"); | ||
}; | ||
|
||
interface ResourceReference<Outputs extends Record<string, string>> { | ||
|
@@ -105,7 +117,19 @@ export const localstackTestSuite = ( | |
|
||
let stackOutputs: CloudFormation.Outputs | undefined; | ||
|
||
const obs = new PerformanceObserver((items) => { | ||
items | ||
.getEntries() | ||
.forEach(({ duration, startTime, name, entryType }) => | ||
console.log(entryType, name, startTime, duration) | ||
); | ||
}); | ||
obs.observe({ entryTypes: ["mark", "measure", "function"], buffered: true }); | ||
performance.measure("Start to Now"); | ||
|
||
beforeAll(async () => { | ||
performance.mark("Before"); | ||
|
||
testContexts = tests.map(({ resources, skip }, i) => { | ||
// create the construct on skip to reduce output changes when moving between skip and not skip | ||
const construct = new Construct(stack, `parent${i}`); | ||
|
@@ -128,11 +152,18 @@ export const localstackTestSuite = ( | |
return {}; | ||
}); | ||
|
||
await deployStack(app, stack); | ||
performance.mark("DeployStack"); | ||
await performance.timerify(deployStack)(app, stack); | ||
performance.measure("After deploy stack", "DeployStack"); | ||
|
||
performance.mark("DescribeStacks"); | ||
|
||
stackOutputs = ( | ||
await CF.describeStacks({ StackName: stack.stackName }).promise() | ||
).Stacks?.[0].Outputs; | ||
|
||
performance.measure("After describe stacks", "DescribeStacks"); | ||
performance.mark("BeforeEnd"); | ||
}); | ||
|
||
// @ts-ignore | ||
|
@@ -144,8 +175,11 @@ export const localstackTestSuite = ( | |
}; | ||
|
||
// register tests | ||
performance.mark("GetTests"); | ||
fn(testResource, stack, app); | ||
performance.measure("After getting tests", "GetTests"); | ||
|
||
performance.mark("InvokeResourceGetter"); | ||
tests.forEach(({ name, test: testFunc, skip }, i) => { | ||
if (!skip) { | ||
test(name, () => { | ||
|
@@ -164,4 +198,6 @@ export const localstackTestSuite = ( | |
test.skip(name, () => {}); | ||
} | ||
}); | ||
performance.mark("SyncResource"); | ||
performance.measure("Sync resource time", "SyncResource"); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is the problem that localstack uses CDK 1.x ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nah, there is no localstack here (except for SDK props hack above). This is all CDK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right but presumably we only do this to meet localstacks expected CDK 1.x input?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ohh, no, cdk is running locally and localstack only sees the cfn. This is all because CDK doesn't support (aka makes it hard as fuck) to synth and deploy programmatically.