diff --git a/README.md b/README.md index 457db00..f4d0b5d 100644 --- a/README.md +++ b/README.md @@ -41,5 +41,19 @@ const handler = new LlrtFunction(this, 'Handler', { }); ``` +### Docker bundling +In some environments, LlrtFunction's bundling steps will fail because the underlying `NodejsFunction` [sometimes runs the commands](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda_nodejs-readme.html#local-bundling) on your host machine, and your environment does not support these commands or syntax. To avoid this error, you can force docker bundling for these commands not to depend on your execution environment. + +```ts +new LlrtFunction(this, 'Handler', { + entry: 'handler.ts', + bundling: { + forceDockerBundling: true, // Add this! + }, +}); +``` + +Note that we enable this flag automatically on Windows platform. + ## Examples See [example](./example/README.md) for examples to use `LlrtFunction` construct. diff --git a/src/llrt-function.ts b/src/llrt-function.ts index 1e5625d..386c329 100644 --- a/src/llrt-function.ts +++ b/src/llrt-function.ts @@ -1,7 +1,8 @@ import { CfnResource } from 'aws-cdk-lib'; -import { Architecture } from 'aws-cdk-lib/aws-lambda'; +import { Architecture, Runtime, RuntimeFamily } from 'aws-cdk-lib/aws-lambda'; import { NodejsFunction, NodejsFunctionProps, OutputFormat } from 'aws-cdk-lib/aws-lambda-nodejs'; import { Construct } from 'constructs'; +import { posix } from 'path'; export interface LlrtFunctionProps extends NodejsFunctionProps { /** @@ -23,7 +24,10 @@ export class LlrtFunction extends NodejsFunction { const cacheDir = `.tmp/llrt/${version}/${arch}`; super(scope, id, { + // set this to remove an unnecessary environment variable. awsSdkConnectionReuse: false, + // set this to remove a warning about runtime. we use al2023 runtime anyway. + runtime: new Runtime('nodejs20.x', RuntimeFamily.NODEJS), ...props, bundling: { target: 'es2020', @@ -33,17 +37,19 @@ export class LlrtFunction extends NodejsFunction { beforeBundling: (_i, _o) => [], afterBundling: (i, o) => [ // Download llrt binary from GitHub release and cache it - `if [ ! -e ${i}/${cacheDir}/bootstrap ]; then - mkdir -p ${i}/${cacheDir} - cd ${i}/${cacheDir} + `if [ ! -e ${posix.join(i, cacheDir, 'bootstrap')} ]; then + mkdir -p ${posix.join(i, cacheDir)} + cd ${posix.join(i, cacheDir)} curl -L -o llrt_temp.zip ${binaryUrl} unzip llrt_temp.zip rm -rf llrt_temp.zip fi`, - `cp ${i}/${cacheDir}/bootstrap ${o}/`, + `cp ${posix.join(i, cacheDir, 'bootstrap')} ${o}`, ], beforeInstall: (_i, _o) => [], }, + // set this because local bundling will not work on Windows + forceDockerBundling: process.platform == 'win32' ? true : undefined, // Dependencies bundled in the runtime // https://github.com/awslabs/llrt?tab=readme-ov-file#using-aws-sdk-v3-with-llrt externalModules: [ diff --git a/test/integ.llrt-function.ts b/test/integ.llrt-function.ts index bbd0873..7c01dc5 100644 --- a/test/integ.llrt-function.ts +++ b/test/integ.llrt-function.ts @@ -1,6 +1,7 @@ import { IntegTest } from '@aws-cdk/integ-tests-alpha'; import { Stack, StackProps, App } from 'aws-cdk-lib'; import { PolicyStatement } from 'aws-cdk-lib/aws-iam'; +import { Architecture } from 'aws-cdk-lib/aws-lambda'; import { Construct } from 'constructs'; import { LlrtFunction } from '../src/llrt-function'; @@ -20,6 +21,12 @@ class TestStack extends Stack { resources: ['*'], }), ); + + new LlrtFunction(this, 'ArmHandler', { + entry: '../example/lambda/s3.ts', + architecture: Architecture.ARM_64, + llrtVersion: 'v0.1.9-beta', + }); } } diff --git a/test/llrt-function.integ.snapshot/LlrtFunctionIntegTest.template.json b/test/llrt-function.integ.snapshot/LlrtFunctionIntegTest.template.json index f9a0b4c..fd0e333 100644 --- a/test/llrt-function.integ.snapshot/LlrtFunctionIntegTest.template.json +++ b/test/llrt-function.integ.snapshot/LlrtFunctionIntegTest.template.json @@ -106,6 +106,94 @@ "HandlerServiceRoleDefaultPolicyCBD0CC91", "HandlerServiceRoleFCDC14AE" ] + }, + "ArmHandlerServiceRole413AC94A": { + "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" + ] + ] + } + ] + } + }, + "ArmHandler7C85DE11": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AssetParametersae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1S3BucketE3B05D82" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1S3VersionKey734C08B9" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1S3VersionKey734C08B9" + } + ] + } + ] + } + ] + ] + } + }, + "Role": { + "Fn::GetAtt": [ + "ArmHandlerServiceRole413AC94A", + "Arn" + ] + }, + "Architectures": [ + "arm64" + ], + "Handler": "index.handler", + "Runtime": "provided.al2023" + }, + "DependsOn": [ + "ArmHandlerServiceRole413AC94A" + ] } }, "Parameters": { @@ -120,6 +208,18 @@ "AssetParametersa9dfae58cfbbde294851a4c7c1be5c9a51d391e56c0f89a47059ebf572270a67ArtifactHashE37AF42F": { "Type": "String", "Description": "Artifact hash for asset \"a9dfae58cfbbde294851a4c7c1be5c9a51d391e56c0f89a47059ebf572270a67\"" + }, + "AssetParametersae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1S3BucketE3B05D82": { + "Type": "String", + "Description": "S3 bucket for asset \"ae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1\"" + }, + "AssetParametersae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1S3VersionKey734C08B9": { + "Type": "String", + "Description": "S3 key for asset version \"ae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1\"" + }, + "AssetParametersae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1ArtifactHash59DF556C": { + "Type": "String", + "Description": "Artifact hash for asset \"ae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1\"" } } } \ No newline at end of file diff --git a/test/llrt-function.integ.snapshot/asset.ae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1/bootstrap b/test/llrt-function.integ.snapshot/asset.ae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1/bootstrap new file mode 100755 index 0000000..ca06c39 Binary files /dev/null and b/test/llrt-function.integ.snapshot/asset.ae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1/bootstrap differ diff --git a/test/llrt-function.integ.snapshot/asset.ae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1/index.mjs b/test/llrt-function.integ.snapshot/asset.ae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1/index.mjs new file mode 100644 index 0000000..604f484 --- /dev/null +++ b/test/llrt-function.integ.snapshot/asset.ae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1/index.mjs @@ -0,0 +1 @@ +import{ListBucketsCommand as t,S3Client as e}from"@aws-sdk/client-s3";var o=new e({}),c=async(s,r)=>{let n=await o.send(new t({}));console.log(n)};export{c as handler}; diff --git a/test/llrt-function.integ.snapshot/manifest.json b/test/llrt-function.integ.snapshot/manifest.json index dd43ca5..562407b 100644 --- a/test/llrt-function.integ.snapshot/manifest.json +++ b/test/llrt-function.integ.snapshot/manifest.json @@ -27,6 +27,18 @@ "s3KeyParameter": "AssetParametersa9dfae58cfbbde294851a4c7c1be5c9a51d391e56c0f89a47059ebf572270a67S3VersionKeyE44872CB", "artifactHashParameter": "AssetParametersa9dfae58cfbbde294851a4c7c1be5c9a51d391e56c0f89a47059ebf572270a67ArtifactHashE37AF42F" } + }, + { + "type": "aws:cdk:asset", + "data": { + "path": "asset.ae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1", + "id": "ae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1", + "packaging": "zip", + "sourceHash": "ae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1", + "s3BucketParameter": "AssetParametersae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1S3BucketE3B05D82", + "s3KeyParameter": "AssetParametersae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1S3VersionKey734C08B9", + "artifactHashParameter": "AssetParametersae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1ArtifactHash59DF556C" + } } ], "/LlrtFunctionIntegTest/Handler/ServiceRole/Resource": [ @@ -64,6 +76,36 @@ "type": "aws:cdk:logicalId", "data": "AssetParametersa9dfae58cfbbde294851a4c7c1be5c9a51d391e56c0f89a47059ebf572270a67ArtifactHashE37AF42F" } + ], + "/LlrtFunctionIntegTest/AssetParameters/ae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1/S3Bucket": [ + { + "type": "aws:cdk:logicalId", + "data": "AssetParametersae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1S3BucketE3B05D82" + } + ], + "/LlrtFunctionIntegTest/AssetParameters/ae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1/S3VersionKey": [ + { + "type": "aws:cdk:logicalId", + "data": "AssetParametersae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1S3VersionKey734C08B9" + } + ], + "/LlrtFunctionIntegTest/AssetParameters/ae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1/ArtifactHash": [ + { + "type": "aws:cdk:logicalId", + "data": "AssetParametersae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1ArtifactHash59DF556C" + } + ], + "/LlrtFunctionIntegTest/ArmHandler/ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ArmHandlerServiceRole413AC94A" + } + ], + "/LlrtFunctionIntegTest/ArmHandler/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "ArmHandler7C85DE11" + } ] }, "displayName": "LlrtFunctionIntegTest" diff --git a/test/llrt-function.integ.snapshot/tree.json b/test/llrt-function.integ.snapshot/tree.json index a888eb8..30f9173 100644 --- a/test/llrt-function.integ.snapshot/tree.json +++ b/test/llrt-function.integ.snapshot/tree.json @@ -184,13 +184,8 @@ "Arn" ] }, - "environment": { - "variables": { - "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1" - } - }, "handler": "index.handler", - "runtime": "nodejs14.x" + "runtime": "nodejs20.x" } }, "constructInfo": { @@ -241,12 +236,193 @@ "fqn": "constructs.Construct", "version": "10.0.5" } + }, + "ae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1": { + "id": "ae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1", + "path": "LlrtFunctionIntegTest/AssetParameters/ae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1", + "children": { + "S3Bucket": { + "id": "S3Bucket", + "path": "LlrtFunctionIntegTest/AssetParameters/ae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1/S3Bucket", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "2.38.0" + } + }, + "S3VersionKey": { + "id": "S3VersionKey", + "path": "LlrtFunctionIntegTest/AssetParameters/ae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1/S3VersionKey", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "2.38.0" + } + }, + "ArtifactHash": { + "id": "ArtifactHash", + "path": "LlrtFunctionIntegTest/AssetParameters/ae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1/ArtifactHash", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "2.38.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.0.5" + } } }, "constructInfo": { "fqn": "constructs.Construct", "version": "10.0.5" } + }, + "ArmHandler": { + "id": "ArmHandler", + "path": "LlrtFunctionIntegTest/ArmHandler", + "children": { + "ServiceRole": { + "id": "ServiceRole", + "path": "LlrtFunctionIntegTest/ArmHandler/ServiceRole", + "children": { + "Resource": { + "id": "Resource", + "path": "LlrtFunctionIntegTest/ArmHandler/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": "2.38.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "2.38.0" + } + }, + "Code": { + "id": "Code", + "path": "LlrtFunctionIntegTest/ArmHandler/Code", + "children": { + "Stage": { + "id": "Stage", + "path": "LlrtFunctionIntegTest/ArmHandler/Code/Stage", + "constructInfo": { + "fqn": "aws-cdk-lib.AssetStaging", + "version": "2.38.0" + } + }, + "AssetBucket": { + "id": "AssetBucket", + "path": "LlrtFunctionIntegTest/ArmHandler/Code/AssetBucket", + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.BucketBase", + "version": "2.38.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3_assets.Asset", + "version": "2.38.0" + } + }, + "Resource": { + "id": "Resource", + "path": "LlrtFunctionIntegTest/ArmHandler/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "s3Bucket": { + "Ref": "AssetParametersae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1S3BucketE3B05D82" + }, + "s3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1S3VersionKey734C08B9" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersae84126e8a9b6d1c3747b012550b99c68bcc1436f31d984985e687525e8d3cb1S3VersionKey734C08B9" + } + ] + } + ] + } + ] + ] + } + }, + "role": { + "Fn::GetAtt": [ + "ArmHandlerServiceRole413AC94A", + "Arn" + ] + }, + "architectures": [ + "arm64" + ], + "handler": "index.handler", + "runtime": "nodejs20.x" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", + "version": "2.38.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda_nodejs.NodejsFunction", + "version": "2.38.0" + } } }, "constructInfo": {