Skip to content

Commit

Permalink
feat(cli): support Fn::ImportValue intrinsic function for hotswap d…
Browse files Browse the repository at this point in the history
…eployments (#27292)

## Purpose 🎯 

Extend the `EvaluateCloudFormationTemplate` class to support the `Fn::ImportValue` intrinsic function. This allows for more diverse templates to be evaluated for the purposes of determining eligibility for `--hotswap` deployments

Closes aws/aws-cdk#21320

## Approach 🧠 

Implement `LazyLookupExport` in similar fashion to `LazyListStackResources` to cache required CloudFormation API calls _(preference was to implement using a generator function instead so style is not entirely consistent, is this an issue?)_

Add some basic unit tests for `EvaluateCloudFormationTemplate.evaluateCfnExpression()` is they were absent, then add some tests for `Fn::ImportValue`

## Todo 📝 

- [x] Update doco where appropriate
- [x] Add to hotswap deployment tests
- [x] Look for appropriate integration tests to update

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
tomwwright authored Oct 19, 2023
1 parent ff95db7 commit ce6e2fa
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 2 deletions.
26 changes: 24 additions & 2 deletions packages/@aws-cdk-testing/cli-integ/resources/cdk-apps/app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,12 @@ class LambdaHotswapStack extends cdk.Stack {
handler: 'index.handler',
description: process.env.DYNAMIC_LAMBDA_PROPERTY_VALUE ?? "description",
environment: {
SomeVariable: process.env.DYNAMIC_LAMBDA_PROPERTY_VALUE ?? "environment",
}
SomeVariable:
process.env.DYNAMIC_LAMBDA_PROPERTY_VALUE ?? "environment",
ImportValueVariable: process.env.USE_IMPORT_VALUE_LAMBDA_PROPERTY
? cdk.Fn.importValue(TEST_EXPORT_OUTPUT_NAME)
: "no-import",
},
});

new cdk.CfnOutput(this, 'FunctionName', { value: fn.functionName });
Expand Down Expand Up @@ -343,6 +347,22 @@ class ConditionalResourceStack extends cdk.Stack {
}
}

const TEST_EXPORT_OUTPUT_NAME = 'test-export-output';

class ExportValueStack extends cdk.Stack {
constructor(parent, id, props) {
super(parent, id, props);

// just need any resource to exist within the stack
const topic = new sns.Topic(this, 'Topic');

new cdk.CfnOutput(this, 'ExportValueOutput', {
exportName: TEST_EXPORT_OUTPUT_NAME,
value: topic.topicArn,
});
}
}

class BundlingStage extends cdk.Stage {
constructor(parent, id, props) {
super(parent, id, props);
Expand Down Expand Up @@ -450,6 +470,8 @@ switch (stackSet) {

new ImportableStack(app, `${stackPrefix}-importable-stack`);

new ExportValueStack(app, `${stackPrefix}-export-value-stack`);

new BundlingStage(app, `${stackPrefix}-bundling-stage`);
break;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1226,6 +1226,48 @@ integTest('hotswap deployment supports Lambda function\'s description and enviro
expect(deployOutput).toContain(`Lambda Function '${functionName}' hotswapped!`);
}));

integTest('hotswap deployment supports Fn::ImportValue intrinsic', withDefaultFixture(async (fixture) => {
// GIVEN
try {
await fixture.cdkDeploy('export-value-stack');
const stackArn = await fixture.cdkDeploy('lambda-hotswap', {
captureStderr: false,
modEnv: {
DYNAMIC_LAMBDA_PROPERTY_VALUE: 'original value',
USE_IMPORT_VALUE_LAMBDA_PROPERTY: 'true',
},
});

// WHEN
const deployOutput = await fixture.cdkDeploy('lambda-hotswap', {
options: ['--hotswap'],
captureStderr: true,
onlyStderr: true,
modEnv: {
DYNAMIC_LAMBDA_PROPERTY_VALUE: 'new value',
USE_IMPORT_VALUE_LAMBDA_PROPERTY: 'true',
},
});

const response = await fixture.aws.cloudFormation('describeStacks', {
StackName: stackArn,
});
const functionName = response.Stacks?.[0].Outputs?.[0].OutputValue;

// THEN

// The deployment should not trigger a full deployment, thus the stack's status must remains
// "CREATE_COMPLETE"
expect(response.Stacks?.[0].StackStatus).toEqual('CREATE_COMPLETE');
expect(deployOutput).toContain(`Lambda Function '${functionName}' hotswapped!`);

} finally {
// Ensure cleanup in reverse order due to use of import/export
await fixture.cdkDestroy('lambda-hotswap');
await fixture.cdkDestroy('export-value-stack');
}
}));

async function listChildren(parent: string, pred: (x: string) => Promise<boolean>) {
const ret = new Array<string>();
for (const child of await fs.readdir(parent, { encoding: 'utf-8' })) {
Expand Down

0 comments on commit ce6e2fa

Please sign in to comment.