Skip to content
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

chore: set lambda runtime of all CDK-vended custom resources with CustomResourceConfig #31173

Merged
merged 11 commits into from
Aug 27, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export abstract class HandlerFrameworkClass extends ClassType {
['runtime', this.buildRuntimeProperty(scope, { runtime: props.runtime })],
]);
const metadataStatements: Statement[] = [
expr.directCode(`this._addMetadata('${CUSTOM_RESOURCE_RUNTIME_FAMILY}', this.runtime.family)`),
expr.directCode(`this.node.addMetadata('${CUSTOM_RESOURCE_RUNTIME_FAMILY}', this.runtime.family)`),
];
this.buildConstructor({
constructorPropsType: LAMBDA_MODULE.FunctionOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ export class TestFunction extends lambda.Function {
"handler": "index.handler",
"runtime": lambda.determineLatestNodeRuntime(scope)
});
this._addMetadata('aws:cdk:is-custom-resource-handler-runtime-family', this.runtime.family);
this.node.addMetadata('aws:cdk:is-custom-resource-handler-runtime-family', this.runtime.family);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ export class TestFunction extends lambda.Function {
"handler": "index.handler",
"runtime": lambda.Runtime.PYTHON_3_10
});
this._addMetadata('aws:cdk:is-custom-resource-handler-runtime-family', this.runtime.family);
this.node.addMetadata('aws:cdk:is-custom-resource-handler-runtime-family', this.runtime.family);
}
}
10 changes: 1 addition & 9 deletions packages/aws-cdk-lib/aws-lambda/lib/function.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Construct, IConstruct, MetadataOptions } from 'constructs';
import { Construct, IConstruct } from 'constructs';
import { AdotInstrumentationConfig, AdotLambdaExecWrapper } from './adot-layers';
import { AliasOptions, Alias } from './alias';
import { Architecture } from './architecture';
Expand Down Expand Up @@ -1155,14 +1155,6 @@ export class Function extends FunctionBase {
this.configureParamsAndSecretsExtension(props);
}

/**
* Use this method to write to the construct tree.
* The metadata entries are written to the Cloud Assembly Manifest if the `treeMetadata` property is specified in the props of the App that contains this Construct.
*/
/** @internal */
public _addMetadata(type: string, data: any, options?: MetadataOptions) {
this.node.addMetadata(type, data, options);
}
/**
* Adds an environment variable to this Lambda function.
* If this is a ref to a Lambda function, this operation results in a no-op.
Expand Down
2 changes: 1 addition & 1 deletion packages/aws-cdk-lib/custom-resources/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -929,7 +929,7 @@ new ses.ReceiptRuleSet(app, 'RuleSet', {

### Setting Lambda Runtimes

The following example configures the custom resource lambda runtime to `PYTHON_3_12`:
The `addLambdaRuntime` method of `CustomResourceConfig` will set every AWS-vended custom resource to the specified lambda runtime, provided that the custom resource lambda is in the same runtime family as the one you specified. The S3 BucketDeployment construct uses lambda runtime Python 3.9. The following example sets the custom resource lambda runtime to `PYTHON_3_12`:
```ts
import * as cdk from 'aws-cdk-lib';
import * as s3deploy from 'aws-cdk-lib/aws-s3-deployment';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,26 @@ export class CustomResourceConfig {

/**
* Set the log retention of AWS-vended custom resource lambdas.
*
* This feature is current experimental.
*/
public addLogRetentionLifetime(rentention: logs.RetentionDays) {
Aspects.of(this.scope).add(new CustomResourceLogRetention(rentention));
}

/**
* Set the removal policy of AWS-vended custom resource logGroup.
*
* This feature is current experimental.
*/
public addRemovalPolicy(removalPolicy: RemovalPolicy) {
Aspects.of(this.scope).add(new CustomResourceRemovalPolicy(removalPolicy));
}

/**
* Set the runtime version on AWS-vended custom resources lambdas.
awslukeguan marked this conversation as resolved.
Show resolved Hide resolved
*
* This feature is current experimental.
*/
public addLambdaRuntime(lambdaRuntime: lambda.Runtime) {
Aspects.of(this.scope).add(new CustomResourceLambdaRuntime(lambdaRuntime));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,14 +316,14 @@ describe('when custom resource logGroup removalPolicy is Retain', () => {
});
});

describe('when custom resource lambda runtime is modified by addLambdaRuntime', () => {
test('addLambdaRuntime modifies custom resource lambda runtime to python3.12', () => {
describe('when custom resource lambda runtime is set by addLambdaRuntime', () => {
test('addLambdaRuntime sets custom resource lambda runtime to python3.12', () => {
// GIVEN
const customResourceRuntime = lambda.Runtime.PYTHON_3_12;
const app = new cdk.App();
const stack = new cdk.Stack(app);
const websiteBucket = new s3.Bucket(stack, 'WebsiteBucket', {});
new s3deploy.BucketDeployment(stack, 'BucketDeployment', {
new s3deploy.BucketDeployment(stack, 'BucketDeployment', { // BucketDeployment uses python3.9
sources: [s3deploy.Source.jsonData('file.json', { a: 'b' })],
destinationBucket: websiteBucket,
});
Expand All @@ -338,7 +338,7 @@ describe('when custom resource lambda runtime is modified by addLambdaRuntime',
});
});

test('addLambdaRuntime modifies custom resource lambda runtime to python3.12 and does not modify non custom resource lambda', () => {
test('addLambdaRuntime sets custom resource lambda runtime and does not modify non custom resource lambda', () => {
// GIVEN
const customResourceRuntime = lambda.Runtime.PYTHON_3_12;
const app = new cdk.App();
Expand Down Expand Up @@ -369,13 +369,13 @@ describe('when custom resource lambda runtime is modified by addLambdaRuntime',

});

test('addLambdaRuntime does not modify custom resource lambda runtime in a different runtime family', () => {
test('addLambdaRuntime does not set custom resource lambda runtime in a different runtime family', () => {
// GIVEN
const customResourceRuntime = lambda.Runtime.NODEJS_20_X;
const app = new cdk.App();
const stack = new cdk.Stack(app);
awslukeguan marked this conversation as resolved.
Show resolved Hide resolved
const websiteBucket = new s3.Bucket(stack, 'WebsiteBucket', {});
new s3deploy.BucketDeployment(stack, 'BucketDeployment', {
new s3deploy.BucketDeployment(stack, 'BucketDeployment', { // BucketDeployment uses Python, not node
sources: [s3deploy.Source.jsonData('file.json', { a: 'b' })],
destinationBucket: websiteBucket,
});
Expand All @@ -390,7 +390,7 @@ describe('when custom resource lambda runtime is modified by addLambdaRuntime',
});
});

test('addLambdaRuntime modifies custom resource lambda runtime to nodejs18.x', () => {
test('addLambdaRuntime sets custom resource lambda runtime to nodejs18.x', () => {
// GIVEN
const customResourceRuntime = lambda.Runtime.NODEJS_18_X;
const app = new cdk.App();
Expand All @@ -417,7 +417,49 @@ describe('when custom resource lambda runtime is modified by addLambdaRuntime',
const nestedTemplate = JSON.parse(readFileSync(path.join(assembly.directory, `${replicaArtifactId}.nested.template.json`), 'utf8'));
const template = Template.fromJSON(nestedTemplate);
template.resourcePropertiesCountIs('AWS::Lambda::Function', {
Runtime: lambda.Runtime.NODEJS_18_X.toString(),
Runtime: customResourceRuntime.toString(),
}, 2);
});

test('addLambdaRuntime sets two custom resource lambda runtime in their specified runtime family', () => {
// GIVEN
const dynamodbReplicaCustomResourceRuntime = lambda.Runtime.NODEJS_18_X;
const s3BucketDeploymentCustomResourceRuntime = lambda.Runtime.PYTHON_3_12;
const app = new cdk.App();
const stack = new cdk.Stack(app);
const table = new dynamodb.Table(stack, 'Table', {
partitionKey: {
name: 'id',
type: dynamodb.AttributeType.STRING,
},
replicationRegions: [
'us-east-2',
],
});
const websiteBucket = new s3.Bucket(stack, 'WebsiteBucket', {});
new s3deploy.BucketDeployment(stack, 'BucketDeployment', {
awslukeguan marked this conversation as resolved.
Show resolved Hide resolved
sources: [s3deploy.Source.jsonData('file.json', { a: 'b' })],
destinationBucket: websiteBucket,
});

// WHEN
CustomResourceConfig.of(app).addLambdaRuntime(dynamodbReplicaCustomResourceRuntime);
CustomResourceConfig.of(app).addLambdaRuntime(s3BucketDeploymentCustomResourceRuntime);

// THEN
const template1 = Template.fromStack(stack);
template1.hasResourceProperties('AWS::Lambda::Function', {
Runtime: s3BucketDeploymentCustomResourceRuntime.toString(),
});
const assembly = app.synth();
const replicaArtifactId = ReplicaProvider.getOrCreate(stack, {
awslukeguan marked this conversation as resolved.
Show resolved Hide resolved
regions: ['us-east-2'],
tableName: table.tableName,
}).artifactId;
const nestedTemplate = JSON.parse(readFileSync(path.join(assembly.directory, `${replicaArtifactId}.nested.template.json`), 'utf8'));
const template2 = Template.fromJSON(nestedTemplate);
template2.resourcePropertiesCountIs('AWS::Lambda::Function', {
Runtime: dynamodbReplicaCustomResourceRuntime.toString(),
}, 2);
});
});