From b4377a5a3ad6d30730a084de6bc29c909a5c0e9d Mon Sep 17 00:00:00 2001 From: Renato Valenzuela <37676028+valerena@users.noreply.github.com> Date: Fri, 19 Jul 2024 12:37:55 -0700 Subject: [PATCH] feat(lambda): add SnapStart support for arm64 functions (#30898) ### Issue # (if applicable) Closes #30899. ### Reason for this change Lambda released SnapStart support for arm64 functions: https://aws.amazon.com/about-aws/whats-new/2024/07/aws-lambda-snapstart-java-functions-arm64-architecture/ ### Description of changes Allow configuring SnapStart on arm64 functions. ### Description of how you validated changes ```yarn test``` ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-cdk-lambda-1.assets.json | 6 +- .../aws-cdk-lambda-1.template.json | 60 ++++++++ .../test/integ.lambda.js.snapshot/cdk.out | 2 +- .../test/integ.lambda.js.snapshot/integ.json | 2 +- .../integ.lambda.js.snapshot/manifest.json | 32 +++-- .../test/integ.lambda.js.snapshot/tree.json | 128 +++++++++++++++++- .../test/aws-lambda/test/integ.lambda.ts | 8 ++ packages/aws-cdk-lib/aws-lambda/README.md | 2 +- .../aws-cdk-lib/aws-lambda/lib/function.ts | 6 +- .../aws-lambda/test/function.test.ts | 23 +++- 10 files changed, 241 insertions(+), 28 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/aws-cdk-lambda-1.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/aws-cdk-lambda-1.assets.json index e158b22c60790..77386dc0c7512 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/aws-cdk-lambda-1.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/aws-cdk-lambda-1.assets.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "36.0.0", "files": { "a37d3ef54c18e7738fe5dc008504591bd3b1f14c6a09ee91eac6d55f7ca5ba5f": { "source": { @@ -14,7 +14,7 @@ } } }, - "aeb38ab17304d1f5dafe1758949dbde748ba823d4a2a513c5135e2b99c0ce4bd": { + "d5186e951f469ea8e615892ba5e275c8d10861c611945ad7f91447b00da5ad60": { "source": { "path": "aws-cdk-lambda-1.template.json", "packaging": "file" @@ -22,7 +22,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "aeb38ab17304d1f5dafe1758949dbde748ba823d4a2a513c5135e2b99c0ce4bd.json", + "objectKey": "d5186e951f469ea8e615892ba5e275c8d10861c611945ad7f91447b00da5ad60.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/aws-cdk-lambda-1.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/aws-cdk-lambda-1.template.json index 20b71fb442494..dea3c1531706b 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/aws-cdk-lambda-1.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/aws-cdk-lambda-1.template.json @@ -201,6 +201,66 @@ "DependsOn": [ "MySnapStartLambdaServiceRoleE0F04324" ] + }, + "MySnapStartLambdaArmServiceRole731A8472": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "MySnapStartLambdaArmEDD3F420": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Architectures": [ + "arm64" + ], + "Code": { + "S3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "S3Key": "a37d3ef54c18e7738fe5dc008504591bd3b1f14c6a09ee91eac6d55f7ca5ba5f.zip" + }, + "Description": "version-hash:522e22f485651f1644be7e317c07836f", + "Handler": "example.Handler::handleRequest", + "Role": { + "Fn::GetAtt": [ + "MySnapStartLambdaArmServiceRole731A8472", + "Arn" + ] + }, + "Runtime": "java21", + "SnapStart": { + "ApplyOn": "PublishedVersions" + } + }, + "DependsOn": [ + "MySnapStartLambdaArmServiceRole731A8472" + ] } }, "Parameters": { diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/cdk.out index 2313ab5436501..1f0068d32659a 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/cdk.out +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"34.0.0"} \ No newline at end of file +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/integ.json index abb70ea0d1a4e..9accee5b3ffe1 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/integ.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "36.0.0", "testCases": { "integ.lambda": { "stacks": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/manifest.json index c1960eaec0790..97556db4ae034 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "34.0.0", + "version": "36.0.0", "artifacts": { "aws-cdk-lambda-1.assets": { "type": "cdk:asset-manifest", @@ -14,10 +14,11 @@ "environment": "aws://unknown-account/unknown-region", "properties": { "templateFile": "aws-cdk-lambda-1.template.json", + "terminationProtection": false, "validateOnSynth": false, "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", - "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/aeb38ab17304d1f5dafe1758949dbde748ba823d4a2a513c5135e2b99c0ce4bd.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/d5186e951f469ea8e615892ba5e275c8d10861c611945ad7f91447b00da5ad60.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ @@ -105,25 +106,34 @@ "data": "MySnapStartLambda8F562E6E" } ], - "/aws-cdk-lambda-1/BootstrapVersion": [ + "/aws-cdk-lambda-1/MySnapStartLambdaArm": [ + { + "type": "aws:cdk:warning", + "data": "SnapStart only support published Lambda versions. Ignore if function already have published versions [ack: @aws-cdk/aws-lambda:snapStartRequirePublish]" + } + ], + "/aws-cdk-lambda-1/MySnapStartLambdaArm/ServiceRole/Resource": [ { "type": "aws:cdk:logicalId", - "data": "BootstrapVersion" + "data": "MySnapStartLambdaArmServiceRole731A8472" } ], - "/aws-cdk-lambda-1/CheckBootstrapVersion": [ + "/aws-cdk-lambda-1/MySnapStartLambdaArm/Resource": [ { "type": "aws:cdk:logicalId", - "data": "CheckBootstrapVersion" + "data": "MySnapStartLambdaArmEDD3F420" } ], - "MyLambdaCurrentVersionE7A382CC1bbf26fffae216f511ef7223d3cba742": [ + "/aws-cdk-lambda-1/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-cdk-lambda-1/CheckBootstrapVersion": [ { "type": "aws:cdk:logicalId", - "data": "MyLambdaCurrentVersionE7A382CC1bbf26fffae216f511ef7223d3cba742", - "trace": [ - "!!DESTRUCTIVE_CHANGES: WILL_DESTROY" - ] + "data": "CheckBootstrapVersion" } ] }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/tree.json index 1545ca4b36bbd..62e11bab692b9 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.js.snapshot/tree.json @@ -426,6 +426,132 @@ "version": "0.0.0" } }, + "MySnapStartLambdaArm": { + "id": "MySnapStartLambdaArm", + "path": "aws-cdk-lambda-1/MySnapStartLambdaArm", + "children": { + "ServiceRole": { + "id": "ServiceRole", + "path": "aws-cdk-lambda-1/MySnapStartLambdaArm/ServiceRole", + "children": { + "ImportServiceRole": { + "id": "ImportServiceRole", + "path": "aws-cdk-lambda-1/MySnapStartLambdaArm/ServiceRole/ImportServiceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-lambda-1/MySnapStartLambdaArm/ServiceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "managedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Code": { + "id": "Code", + "path": "aws-cdk-lambda-1/MySnapStartLambdaArm/Code", + "children": { + "Stage": { + "id": "Stage", + "path": "aws-cdk-lambda-1/MySnapStartLambdaArm/Code/Stage", + "constructInfo": { + "fqn": "aws-cdk-lib.AssetStaging", + "version": "0.0.0" + } + }, + "AssetBucket": { + "id": "AssetBucket", + "path": "aws-cdk-lambda-1/MySnapStartLambdaArm/Code/AssetBucket", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.BucketBase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3_assets.Asset", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "aws-cdk-lambda-1/MySnapStartLambdaArm/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "architectures": [ + "arm64" + ], + "code": { + "s3Bucket": { + "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" + }, + "s3Key": "a37d3ef54c18e7738fe5dc008504591bd3b1f14c6a09ee91eac6d55f7ca5ba5f.zip" + }, + "handler": "example.Handler::handleRequest", + "role": { + "Fn::GetAtt": [ + "MySnapStartLambdaArmServiceRole731A8472", + "Arn" + ] + }, + "runtime": "java21", + "snapStart": { + "applyOn": "PublishedVersions" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.Function", + "version": "0.0.0" + } + }, "BootstrapVersion": { "id": "BootstrapVersion", "path": "aws-cdk-lambda-1/BootstrapVersion", @@ -453,7 +579,7 @@ "path": "Tree", "constructInfo": { "fqn": "constructs.Construct", - "version": "10.2.70" + "version": "10.3.0" } } }, diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.ts index bd04994c36065..8f465b8588031 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda/test/integ.lambda.ts @@ -45,4 +45,12 @@ new lambda.Function(stack, 'MySnapStartLambda', { snapStart: lambda.SnapStartConf.ON_PUBLISHED_VERSIONS, }); +new lambda.Function(stack, 'MySnapStartLambdaArm', { + code: lambda.Code.fromAsset(path.join(__dirname, 'handler-snapstart.zip')), + handler: 'example.Handler::handleRequest', + runtime: lambda.Runtime.JAVA_21, + architecture: lambda.Architecture.ARM_64, + snapStart: lambda.SnapStartConf.ON_PUBLISHED_VERSIONS, +}); + app.synth(); diff --git a/packages/aws-cdk-lib/aws-lambda/README.md b/packages/aws-cdk-lib/aws-lambda/README.md index 83384b195e8c5..58dc34ef74d53 100644 --- a/packages/aws-cdk-lib/aws-lambda/README.md +++ b/packages/aws-cdk-lib/aws-lambda/README.md @@ -967,7 +967,7 @@ managing concurrency. ## Lambda with SnapStart -SnapStart is currently supported only on Java 11/Java 17 runtime. SnapStart does not support provisioned concurrency, the arm64 architecture, Amazon Elastic File System (Amazon EFS), or ephemeral storage greater than 512 MB. After you enable Lambda SnapStart for a particular Lambda function, publishing a new version of the function will trigger an optimization process. +SnapStart is currently supported only on Java 11 and later [Java managed runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). SnapStart does not support provisioned concurrency, Amazon Elastic File System (Amazon EFS), or ephemeral storage greater than 512 MB. After you enable Lambda SnapStart for a particular Lambda function, publishing a new version of the function will trigger an optimization process. See [the AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html) to learn more about AWS Lambda SnapStart diff --git a/packages/aws-cdk-lib/aws-lambda/lib/function.ts b/packages/aws-cdk-lib/aws-lambda/lib/function.ts index 45c67d9ea3d4b..7d15500da259c 100644 --- a/packages/aws-cdk-lib/aws-lambda/lib/function.ts +++ b/packages/aws-cdk-lib/aws-lambda/lib/function.ts @@ -1554,7 +1554,7 @@ Environment variables can be marked for removal when used in Lambda@Edge by sett return undefined; } - // SnapStart does not support arm64 architecture, Amazon Elastic File System (Amazon EFS), or ephemeral storage greater than 512 MB. + // SnapStart does not support Amazon Elastic File System (Amazon EFS), or ephemeral storage greater than 512 MB. // SnapStart doesn't support provisioned concurrency either, but that's configured at the version level, // so it can't be checked at function set up time // SnapStart supports the Java 11 and Java 17 (java11 and java17) managed runtimes. @@ -1565,10 +1565,6 @@ Environment variables can be marked for removal when used in Lambda@Edge by sett throw new Error(`SnapStart currently not supported by runtime ${props.runtime.name}`); } - if (props.architecture == Architecture.ARM_64) { - throw new Error('SnapStart is currently not supported on Arm_64'); - } - if (props.filesystem) { throw new Error('SnapStart is currently not supported using EFS'); } diff --git a/packages/aws-cdk-lib/aws-lambda/test/function.test.ts b/packages/aws-cdk-lib/aws-lambda/test/function.test.ts index 652b333c64b69..c5228bb2cd82c 100644 --- a/packages/aws-cdk-lib/aws-lambda/test/function.test.ts +++ b/packages/aws-cdk-lib/aws-lambda/test/function.test.ts @@ -3429,16 +3429,29 @@ describe('function', () => { })).toThrowError('SnapStart currently not supported by runtime nodejs18.x'); }); - test('arm64 validation for snapStart', () => { + test('arm64 function using snapStart', () => { const stack = new cdk.Stack(); - - expect(() => new lambda.Function(stack, 'MyLambda', { + //WHEN + new lambda.Function(stack, 'MyLambda', { code: lambda.Code.fromAsset(path.join(__dirname, 'handler.zip')), handler: 'example.Handler::handleRequest', runtime: lambda.Runtime.JAVA_11, architecture: lambda.Architecture.ARM_64, snapStart: lambda.SnapStartConf.ON_PUBLISHED_VERSIONS, - })).toThrowError('SnapStart is currently not supported on Arm_64'); + }); + + //THEN + Template.fromStack(stack).hasResource('AWS::Lambda::Function', { + Properties: + { + Handler: 'example.Handler::handleRequest', + Runtime: 'java11', + Architectures: ['arm64'], + SnapStart: { + ApplyOn: 'PublishedVersions', + }, + }, + }); }); test('EFS validation for snapStart', () => { @@ -3463,7 +3476,7 @@ describe('function', () => { })).toThrowError('SnapStart is currently not supported using EFS'); }); - test('arm64 validation for snapStart', () => { + test('ephemeral storage limit validation for snapStart', () => { const stack = new cdk.Stack(); expect(() => new lambda.Function(stack, 'MyLambda', {