Skip to content

Commit d7a1c34

Browse files
authored
fix(lambda-nodejs): Required auto prefix of handler with index. breaks custom non-index handler settings used by layers (#24406)
Using `lambda-nodejs` makes it very easy to bundle functions with `esbuild`, but the code currently *always* prefixes the `handler` value with `index.`, which makes it impossible to use some lambda extensions together with this module as they require setting `handler` to specific values and the `index.` prefixing breaks the ability to set the handler to those values. This PR avoids adding the `index.` prefix if the specified `handler` value contains a `.` already. Closes #24403 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent a70ff1a commit d7a1c34

File tree

15 files changed

+643
-41
lines changed

15 files changed

+643
-41
lines changed

packages/@aws-cdk/aws-lambda-nodejs/README.md

+4
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ new nodejs.NodejsFunction(this, 'MyFunction', {
5353
});
5454
```
5555

56+
The handler value will be automatically prefixed with the bundled output file name, `index.`,
57+
unless the handler value contains a `.` character, in which case the handler value is used as-is to
58+
allow for values needed by some Lambda extensions.
59+
5660
For monorepos, the reference architecture becomes:
5761

5862
```plaintext

packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ export interface NodejsFunctionProps extends lambda.FunctionOptions {
2525
/**
2626
* The name of the exported handler in the entry file.
2727
*
28+
* The handler is prefixed with `index.` unless the specified handler value contains a `.`,
29+
* in which case it is used as-is.
30+
*
2831
* @default handler
2932
*/
3033
readonly handler?: string;
@@ -108,7 +111,7 @@ export class NodejsFunction extends lambda.Function {
108111
depsLockFilePath,
109112
projectRoot,
110113
}),
111-
handler: `index.${handler}`,
114+
handler: handler.indexOf('.') !== -1 ? `${handler}` : `index.${handler}`,
112115
});
113116

114117
// Enable connection reuse for aws-sdk

packages/@aws-cdk/aws-lambda-nodejs/test/function.test.ts

+32
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,38 @@ test('NodejsFunction with .ts handler', () => {
4545
});
4646
});
4747

48+
test('NodejsFunction with overridden handler - no dots', () => {
49+
// WHEN
50+
new NodejsFunction(stack, 'handler1', {
51+
handler: 'myHandler',
52+
});
53+
54+
expect(Bundling.bundle).toHaveBeenCalledWith(expect.objectContaining({
55+
entry: expect.stringContaining('function.test.handler1.ts'), // Automatically finds .ts handler file
56+
}));
57+
58+
Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', {
59+
Handler: 'index.myHandler', // automatic index. prefix
60+
Runtime: 'nodejs14.x',
61+
});
62+
});
63+
64+
test('NodejsFunction with overridden handler - with dots', () => {
65+
// WHEN
66+
new NodejsFunction(stack, 'handler1', {
67+
handler: 'run.sh',
68+
});
69+
70+
expect(Bundling.bundle).toHaveBeenCalledWith(expect.objectContaining({
71+
entry: expect.stringContaining('function.test.handler1.ts'), // Automatically finds .ts handler file
72+
}));
73+
74+
Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', {
75+
Handler: 'run.sh', // No index. prefix
76+
Runtime: 'nodejs14.x',
77+
});
78+
});
79+
4880
test('NodejsFunction with .js handler', () => {
4981
// WHEN
5082
new NodejsFunction(stack, 'handler2');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Server } from 'http';
2+
import { mult } from './util';
3+
4+
// Create simple http server
5+
const server = new Server((_req, res) => {
6+
res.writeHead(200, { 'Content-Type': 'text/plain' });
7+
res.end(`${mult(3, 4)}`);
8+
console.log(mult(3, 4)); // eslint-disable-line no-console
9+
});
10+
11+
const port = parseInt(process.env.PORT || '3001', 10);
12+
server.listen(port);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/sh
2+
3+
# For AWS Lambda Adapter
4+
# https://github.com/awslabs/aws-lambda-web-adapter
5+
export READINESS_CHECK_PATH="${READINESS_CHECK_PATH:-/health}"
6+
export AWS_LAMBDA_EXEC_WRAPPER="${AWS_LAMBDA_EXEC_WRAPPER:-/opt/bootstrap}"
7+
export RUST_LOG="${RUST_LOG:-info}"
8+
export AWS_LWA_ENABLE_COMPRESSION="${AWS_LWA_ENABLE_COMPRESSION:-true}"
9+
export PORT="${PORT:-3001}"
10+
11+
exec node index.js

packages/@aws-cdk/aws-lambda-nodejs/test/integ.function.js.snapshot/asset.a33898f49e24c41ff9be236439c418d30e576c5f57763097162d9ec4245216ce/index.js

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk/aws-lambda-nodejs/test/integ.function.js.snapshot/asset.a33898f49e24c41ff9be236439c418d30e576c5f57763097162d9ec4245216ce/index.js.map

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/sh
2+
3+
# For AWS Lambda Adapter
4+
# https://github.com/awslabs/aws-lambda-web-adapter
5+
export READINESS_CHECK_PATH="${READINESS_CHECK_PATH:-/health}"
6+
export AWS_LAMBDA_EXEC_WRAPPER="${AWS_LAMBDA_EXEC_WRAPPER:-/opt/bootstrap}"
7+
export RUST_LOG="${RUST_LOG:-info}"
8+
export AWS_LWA_ENABLE_COMPRESSION="${AWS_LWA_ENABLE_COMPRESSION:-true}"
9+
export PORT="${PORT:-3001}"
10+
11+
exec node index.js

packages/@aws-cdk/aws-lambda-nodejs/test/integ.function.js.snapshot/cdk-integ-lambda-nodejs.assets.json

+16-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "20.0.0",
2+
"version": "30.1.0",
33
"files": {
44
"5017e4b2e278e32bc634202d075b7ed8961b0d784f75450f7918a6a4f6f7df4a": {
55
"source": {
@@ -40,15 +40,28 @@
4040
}
4141
}
4242
},
43-
"90cb9162c12b37a3990ecbde27ea037907171ccb64eeb60f046f098a9ae069cd": {
43+
"a33898f49e24c41ff9be236439c418d30e576c5f57763097162d9ec4245216ce": {
44+
"source": {
45+
"path": "asset.a33898f49e24c41ff9be236439c418d30e576c5f57763097162d9ec4245216ce",
46+
"packaging": "zip"
47+
},
48+
"destinations": {
49+
"current_account-current_region": {
50+
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
51+
"objectKey": "a33898f49e24c41ff9be236439c418d30e576c5f57763097162d9ec4245216ce.zip",
52+
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
53+
}
54+
}
55+
},
56+
"76dea8b92881f37d669c02f0271a5aa08e02b115b3fbe57063dbb99c5acb1932": {
4457
"source": {
4558
"path": "cdk-integ-lambda-nodejs.template.json",
4659
"packaging": "file"
4760
},
4861
"destinations": {
4962
"current_account-current_region": {
5063
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
51-
"objectKey": "90cb9162c12b37a3990ecbde27ea037907171ccb64eeb60f046f098a9ae069cd.json",
64+
"objectKey": "76dea8b92881f37d669c02f0271a5aa08e02b115b3fbe57063dbb99c5acb1932.json",
5265
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
5366
}
5467
}

packages/@aws-cdk/aws-lambda-nodejs/test/integ.function.js.snapshot/cdk-integ-lambda-nodejs.template.json

+130
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,136 @@
626626
"VpcPrivateSubnet2DefaultRoute060D2087",
627627
"VpcPrivateSubnet2RouteTableAssociationA89CAD56"
628628
]
629+
},
630+
"tshandlercustomhandlernodotsServiceRole1775E15E": {
631+
"Type": "AWS::IAM::Role",
632+
"Properties": {
633+
"AssumeRolePolicyDocument": {
634+
"Statement": [
635+
{
636+
"Action": "sts:AssumeRole",
637+
"Effect": "Allow",
638+
"Principal": {
639+
"Service": "lambda.amazonaws.com"
640+
}
641+
}
642+
],
643+
"Version": "2012-10-17"
644+
},
645+
"ManagedPolicyArns": [
646+
{
647+
"Fn::Join": [
648+
"",
649+
[
650+
"arn:",
651+
{
652+
"Ref": "AWS::Partition"
653+
},
654+
":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
655+
]
656+
]
657+
}
658+
]
659+
}
660+
},
661+
"tshandlercustomhandlernodots381F62EE": {
662+
"Type": "AWS::Lambda::Function",
663+
"Properties": {
664+
"Code": {
665+
"S3Bucket": {
666+
"Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
667+
},
668+
"S3Key": "5017e4b2e278e32bc634202d075b7ed8961b0d784f75450f7918a6a4f6f7df4a.zip"
669+
},
670+
"Role": {
671+
"Fn::GetAtt": [
672+
"tshandlercustomhandlernodotsServiceRole1775E15E",
673+
"Arn"
674+
]
675+
},
676+
"Environment": {
677+
"Variables": {
678+
"AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1"
679+
}
680+
},
681+
"Handler": "index.handler",
682+
"Runtime": "nodejs14.x"
683+
},
684+
"DependsOn": [
685+
"tshandlercustomhandlernodotsServiceRole1775E15E"
686+
]
687+
},
688+
"tshandlercustomhandlerdotsServiceRole0575D3CB": {
689+
"Type": "AWS::IAM::Role",
690+
"Properties": {
691+
"AssumeRolePolicyDocument": {
692+
"Statement": [
693+
{
694+
"Action": "sts:AssumeRole",
695+
"Effect": "Allow",
696+
"Principal": {
697+
"Service": "lambda.amazonaws.com"
698+
}
699+
}
700+
],
701+
"Version": "2012-10-17"
702+
},
703+
"ManagedPolicyArns": [
704+
{
705+
"Fn::Join": [
706+
"",
707+
[
708+
"arn:",
709+
{
710+
"Ref": "AWS::Partition"
711+
},
712+
":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
713+
]
714+
]
715+
}
716+
]
717+
}
718+
},
719+
"tshandlercustomhandlerdots2695F653": {
720+
"Type": "AWS::Lambda::Function",
721+
"Properties": {
722+
"Code": {
723+
"S3Bucket": {
724+
"Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
725+
},
726+
"S3Key": "a33898f49e24c41ff9be236439c418d30e576c5f57763097162d9ec4245216ce.zip"
727+
},
728+
"Role": {
729+
"Fn::GetAtt": [
730+
"tshandlercustomhandlerdotsServiceRole0575D3CB",
731+
"Arn"
732+
]
733+
},
734+
"Environment": {
735+
"Variables": {
736+
"AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1"
737+
}
738+
},
739+
"Handler": "ts-web.run.sh",
740+
"Layers": [
741+
{
742+
"Fn::Join": [
743+
"",
744+
[
745+
"arn:aws:lambda:",
746+
{
747+
"Ref": "AWS::Region"
748+
},
749+
":753240598075:layer:LambdaAdapterLayerX86:13"
750+
]
751+
]
752+
}
753+
],
754+
"Runtime": "nodejs14.x"
755+
},
756+
"DependsOn": [
757+
"tshandlercustomhandlerdotsServiceRole0575D3CB"
758+
]
629759
}
630760
},
631761
"Parameters": {
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"version":"20.0.0"}
1+
{"version":"30.1.0"}

packages/@aws-cdk/aws-lambda-nodejs/test/integ.function.js.snapshot/integ.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "20.0.0",
2+
"version": "30.1.0",
33
"testCases": {
44
"integ.function": {
55
"stacks": [

packages/@aws-cdk/aws-lambda-nodejs/test/integ.function.js.snapshot/manifest.json

+32-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
{
2-
"version": "20.0.0",
2+
"version": "30.1.0",
33
"artifacts": {
4-
"Tree": {
5-
"type": "cdk:tree",
6-
"properties": {
7-
"file": "tree.json"
8-
}
9-
},
104
"cdk-integ-lambda-nodejs.assets": {
115
"type": "cdk:asset-manifest",
126
"properties": {
@@ -23,7 +17,7 @@
2317
"validateOnSynth": false,
2418
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}",
2519
"cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}",
26-
"stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/90cb9162c12b37a3990ecbde27ea037907171ccb64eeb60f046f098a9ae069cd.json",
20+
"stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/76dea8b92881f37d669c02f0271a5aa08e02b115b3fbe57063dbb99c5acb1932.json",
2721
"requiresBootstrapStackVersion": 6,
2822
"bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version",
2923
"additionalDependencies": [
@@ -219,6 +213,30 @@
219213
"data": "tshandlervpcA502E26A"
220214
}
221215
],
216+
"/cdk-integ-lambda-nodejs/ts-handler-custom-handler-no-dots/ServiceRole/Resource": [
217+
{
218+
"type": "aws:cdk:logicalId",
219+
"data": "tshandlercustomhandlernodotsServiceRole1775E15E"
220+
}
221+
],
222+
"/cdk-integ-lambda-nodejs/ts-handler-custom-handler-no-dots/Resource": [
223+
{
224+
"type": "aws:cdk:logicalId",
225+
"data": "tshandlercustomhandlernodots381F62EE"
226+
}
227+
],
228+
"/cdk-integ-lambda-nodejs/ts-handler-custom-handler-dots/ServiceRole/Resource": [
229+
{
230+
"type": "aws:cdk:logicalId",
231+
"data": "tshandlercustomhandlerdotsServiceRole0575D3CB"
232+
}
233+
],
234+
"/cdk-integ-lambda-nodejs/ts-handler-custom-handler-dots/Resource": [
235+
{
236+
"type": "aws:cdk:logicalId",
237+
"data": "tshandlercustomhandlerdots2695F653"
238+
}
239+
],
222240
"/cdk-integ-lambda-nodejs/BootstrapVersion": [
223241
{
224242
"type": "aws:cdk:logicalId",
@@ -233,6 +251,12 @@
233251
]
234252
},
235253
"displayName": "cdk-integ-lambda-nodejs"
254+
},
255+
"Tree": {
256+
"type": "cdk:tree",
257+
"properties": {
258+
"file": "tree.json"
259+
}
236260
}
237261
}
238262
}

0 commit comments

Comments
 (0)