├── .dockerignore ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── auto ├── dev-runtime ├── dev-runtime-cmd ├── package ├── package-cmd └── publish ├── bootstrap-sample.sh ├── container └── Dockerfile ├── docker-compose.yml ├── package-lock.json ├── package.json ├── runtime-api ├── api.js └── server.js ├── src ├── bootstrap └── start.js └── test ├── Dockerfile └── hello.js /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | stage/ 3 | .git/ 4 | tmp/ 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | stage/ 3 | tmp/ 4 | dist/ 5 | *.zip 6 | deployment*.txt 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16.0.0-buster-slim 2 | 3 | RUN apt-get update && apt-get install -y p7zip-full curl xz-utils gnupg && apt-get clean all 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS Lambda NodeJS-16 Runtime 2 | 3 | AWS Lambda runtime API implemented in Node.js. The supported version is usually the latest LTS. 4 | 5 | It's easy to use this project and build Node.js runtime that will target any version - just replace the version number in `auto/package` with your preferred one. 6 | 7 | ## Current versions 8 | 9 | * Node.js - **16.0.0** 10 | * aws-sdk - **2.889.0** 11 | 12 | ## Goals 13 | 14 | * Provide always up-to-date Node.js execution environment as a Lambda runtime layer and a Lambda container image. 15 | * Include the most recent `aws-cli` library. 16 | * Make the runtime environment compatible with the default node12.x and node14.x environments 17 | 18 | ## How to install? 19 | 20 | Prebuilt packages are available for use by any account in all AWS regions. Check the [releases page](https://github.com/janaz/aws-lambda-node-runtime/releases) for more information. 21 | 22 | The following steps require `docker` and `docker-compose` to be installed on the system. 23 | 24 | The minimum `awscli` version is 1.16.67. 25 | 26 | ### 1. Build 27 | 28 | Run: 29 | 30 | ```bash 31 | ./auto/package 32 | ``` 33 | 34 | The script will build the Lambda layer in `stage/layer.zip` file. 35 | 36 | ### 2. Deploy 37 | 38 | Deploy the runtime layer using the following command: 39 | 40 | ```bash 41 | aws lambda publish-layer-version \ 42 | --layer-name node-16-runtime \ 43 | --description "nodejs-16.0.0 aws-cli-2.889.0" \ 44 | --compatible-runtimes provided \ 45 | --license-info Apache-2.0 \ 46 | --zip-file fileb://stage/layer.zip 47 | ``` 48 | 49 | The output will look like this: 50 | ```json 51 | { 52 | "LayerVersionArn": "arn:aws:lambda:us-east-2:356111732087:layer:node-16-runtime:1", 53 | "Description": "nodejs-16.0.0 aws-cli-2.889.0", 54 | "CreatedDate": "2018-12-02T22:32:00.572+0000", 55 | "LayerArn": "arn:aws:lambda:us-east-2:356111732087:layer:node-16-runtime", 56 | "Content": { 57 | "CodeSize": 18104889, 58 | "CodeSha256": "VonrpX23FWJOmE4lvhpox+9PS9kuY4sng0o0wxNTROs=", 59 | "Location": "https://awslambda-us-east-2-layers.s3.us-east-2.amazonaws.com/snapshots/356111732087/node-16-runtime-f3415c38-d865-46b6-ae42-009985092116?......" 60 | }, 61 | "Version": 1, 62 | "CompatibleRuntimes": [ 63 | "provided" 64 | ], 65 | "LicenseInfo": "Apache-2.0" 66 | } 67 | ``` 68 | 69 | ### 3. Share 70 | 71 | You can share the layer with other AWS accounts by executing the following command: 72 | 73 | ```bash 74 | aws lambda add-layer-version-permission \ 75 | --layer-name node-16-runtime \ 76 | --version-number 1 \ 77 | --principal "*" \ 78 | --statement-id publish \ 79 | --action lambda:GetLayerVersion 80 | ``` 81 | 82 | Response: 83 | 84 | ```json 85 | { 86 | "RevisionId": "8b5b2e27-5013-4983-ac1a-9008dff90bac", 87 | "Statement": "{\"Sid\":\"publish\",\"Effect\":\"Allow\",\"Principal\":\"*\",\"Action\":\"lambda:GetLayerVersion\",\"Resource\":\"arn:aws:lambda:us-east-2:356111732087:layer:node-16-runtime:1\"}" 88 | } 89 | ``` 90 | 91 | ### 4. Deploy lambda function using this runtime 92 | 93 | Let's assume that your lambda function is packaged as `lambda.zip` file and the handler is called `hello.handler`. The following command will deploy this function with the runtime layer that has just been deployed. 94 | 95 | 1. Create the lambda zip file 96 | 97 | ```bash 98 | zip lambda.zip hello.js 99 | ``` 100 | 101 | 2. Create the function 102 | 103 | ```bash 104 | aws lambda create-function \ 105 | --region us-east-2 \ 106 | --function-name node-16-runtime-example \ 107 | --zip-file fileb://lambda.zip \ 108 | --handler hello.handler \ 109 | --runtime provided \ 110 | --layers "arn:aws:lambda:us-east-2:356111732087:layer:node-16-runtime:2" \ 111 | --role arn:aws:iam::356111732087:role/lambda-role 112 | out.txt 113 | ``` 114 | 115 | Response: 116 | 117 | ```json 118 | { 119 | "Layers": [ 120 | { 121 | "CodeSize": 18104889, 122 | "Arn": "arn:aws:lambda:us-east-2:356111732087:layer:node-16-runtime:1" 123 | } 124 | ], 125 | "FunctionName": "node-16-runtime-example", 126 | "LastModified": "2018-12-02T22:59:10.408+0000", 127 | "RevisionId": "32e7e8a1-b5ba-4388-b6be-596278e36126", 128 | "MemorySize": 128, 129 | "Version": "$LATEST", 130 | "Role": "arn:aws:iam::356111732087:role/service-role/lambda-role", 131 | "Timeout": 3, 132 | "Runtime": "provided", 133 | "TracingConfig": { 134 | "Mode": "PassThrough" 135 | }, 136 | "CodeSha256": "shSeSmJZHv8Z0WmOAcFcHeSUGbRYRR1cFdbEudkSJHo=", 137 | "Description": "", 138 | "CodeSize": 340, 139 | "FunctionArn": "arn:aws:lambda:us-east-2:356111732087:function:node-16-runtime-example", 140 | "Handler": "hello.handler" 141 | } 142 | ``` 143 | 144 | 3. Invoke the function 145 | 146 | ```bash 147 | aws lambda invoke \ 148 | --region us-east-2 \ 149 | --function-name node-16-runtime-example \ 150 | --payload '{"hello":"world"}' \ 151 | output.txt 152 | 153 | cat output.txt 154 | ``` 155 | 156 | ## Context object 157 | 158 | The context object is compatible with [the default node environments](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html). 159 | 160 | ### Context Methods 161 | 162 | * `getRemainingTimeInMillis()` – Returns the number of milliseconds left before the execution times out. 163 | 164 | ### Context Properties 165 | 166 | * `functionName` – The name of the Lambda function. 167 | * `functionVersion` – The version of the function. 168 | * `invokedFunctionArn` – The Amazon Resource Name (ARN) used to invoke the function. Indicates if the invoker specified a version number or alias. 169 | * `memoryLimitInMB` – The amount of memory configured on the function. 170 | * `awsRequestId` – The identifier of the invocation request. 171 | * `logGroupName` – The log group for the function. 172 | * `logStreamName` – The log stream for the function instance. 173 | * `callbackWaitsForEmptyEventLoop` – Set to false to send the response right away when the callback executes, instead of waiting for the Node.js event loop to be empty. 174 | * `identity` - Information about the Amazon Cognito identity that authorized the request. 175 | * `clientContext` - Client context provided to the Lambda invoker by the client application. 176 | 177 | _The [Runtime API](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html) exposes the above properties in the response headers from the `/runtime/invocation/next` endpoint, but the documentation doesn't exactly specify the format of the header value._ 178 | 179 | ## Environment variables 180 | 181 | Based on https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html 182 | 183 | | Key | Reserved | Value | 184 | |--|--|--| 185 | |_HANDLER|Yes|The handler location configured on the function.| 186 | |AWS_REGION|Yes|The AWS region where the Lambda function is executed.| 187 | |AWS_EXECUTION_ENV|Yes|The [runtime identifier](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html), prefixed by AWS_Lambda_. For example, AWS_Lambda_java8.| 188 | |AWS_LAMBDA_FUNCTION_NAME|Yes|The name of the function.| 189 | |AWS_LAMBDA_FUNCTION_MEMORY_SIZE|Yes|The amount of memory available to the function in MB.| 190 | |AWS_LAMBDA_FUNCTION_VERSION|Yes|The version of the function being executed.| 191 | AWS_LAMBDA_LOG_GROUP_NAME AWS_LAMBDA_LOG_STREAM_NAME|Yes|The name of the Amazon CloudWatch Logs group and stream for the function.| 192 | |AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN|Yes|Access keys obtained from the function's execution role.| 193 | |LANG|No|en_US.UTF-8. This is the locale of the runtime.| 194 | |TZ|Yes|The environment's timezone (UTC). The execution environment uses NTP to synchronize the system clock.| 195 | |LAMBDA_TASK_ROOT|Yes|The path to your Lambda function code.| 196 | |LAMBDA_RUNTIME_DIR|Yes|The path to runtime libraries.| 197 | |PATH|No|/usr/local/bin:/usr/bin/:/bin:/opt/bin| 198 | |LD_LIBRARY_PATH|No|/lib64:/usr/lib64:$LAMBDA_RUNTIME_DIR:$LAMBDA_RUNTIME_DIR/lib:$LAMBDA_TASK_ROOT:$LAMBDA_TASK_ROOT/lib:/opt/lib| 199 | |NODE_PATH|No|/opt/node_modules| 200 | |AWS_LAMBDA_RUNTIME_API|Yes|(custom runtime) The host and port of the [runtime API](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html).| 201 | 202 | ## The base container image 203 | 204 | The releases include a base Docker image that is compatible with the Lambda Container runtime and it's compatible with the Node.js Lambda runtime. 205 | 206 | ### Using the base image 207 | 208 | The example below assumes that the Dockerfile is in the root folder of the Lambda project and there's an `index.js` file that exports the handler function called `handler`. 209 | 210 | ```Dockerfile 211 | FROM janaz/aws-lambda-node-runtime:test-1 212 | 213 | COPY . ${LAMBDA_TASK_ROOT} 214 | RUN npm install 215 | 216 | CMD ["index.handler"] 217 | ``` 218 | 219 | The image created from that `Dockerfile` should be uploaded to your ECR registry and referenced in the Lambda configuration. 220 | -------------------------------------------------------------------------------- /auto/dev-runtime: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -ex 4 | 5 | DIR="$( cd "$( dirname "$0" )" && pwd )" 6 | 7 | cd "${DIR}/.." 8 | 9 | docker-compose build 10 | docker-compose up dev-runtime 11 | -------------------------------------------------------------------------------- /auto/dev-runtime-cmd: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DIR="$( cd "$( dirname "$0" )" && pwd )" 4 | 5 | export _HANDLER=hello.handler 6 | export LAMBDA_TASK_ROOT="${DIR}/.." 7 | 8 | export AWS_LAMBDA_FUNCTION_NAME=test-function-name 9 | export AWS_LAMBDA_FUNCTION_VERSION=test-function-version 10 | export AWS_LAMBDA_FUNCTION_MEMORY_SIZE=128 11 | export AWS_LAMBDA_LOG_GROUP_NAME=test-log-group-name 12 | export AWS_LAMBDA_LOG_STREAM_NAME=test-log-stream-name 13 | 14 | cd "${DIR}/.." 15 | 16 | sleep 5 17 | exec "./stage/work/bootstrap" 18 | -------------------------------------------------------------------------------- /auto/package: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -ex 4 | 5 | export NODE_VERSION=16.0.0 6 | 7 | DIR="$( cd "$( dirname "$0" )" && pwd )" 8 | 9 | cd "${DIR}/.." 10 | 11 | docker-compose build 12 | docker-compose run --rm \ 13 | -e NODE_VERSION \ 14 | ci auto/package-cmd 15 | -------------------------------------------------------------------------------- /auto/package-cmd: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | DIR="$( cd "$( dirname "$0" )" && pwd )" 6 | 7 | cd "${DIR}/.." 8 | 9 | tmp_dir="tmp" 10 | stage_dir="stage/work/" 11 | node_dir="${tmp_dir}/${NODE_VERSION}" 12 | node_archive="node-v${NODE_VERSION}-linux-x64.tar.xz" 13 | 14 | mkdir -p "${tmp_dir}" 15 | curl -L -o "${tmp_dir}/SHASUMS256.txt.asc" "https://nodejs.org/dist/v${NODE_VERSION}/SHASUMS256.txt.asc" 16 | if [ ! -f "${tmp_dir}/${node_archive}" ]; then 17 | curl -L -o "${tmp_dir}/${node_archive}" "https://nodejs.org/dist/v${NODE_VERSION}/${node_archive}" 18 | fi 19 | 20 | # Fetch release keys https://github.com/nodejs/node#release-keys 21 | gpg --keyserver pool.sks-keyservers.net --recv-keys 4ED778F539E3634C779C87C6D7062848A1AB005C 22 | gpg --keyserver pool.sks-keyservers.net --recv-keys 94AE36675C464D64BAFA68DD7434390BDBE9B9C5 23 | gpg --keyserver pool.sks-keyservers.net --recv-keys 74F12602B6F1C4E913FAA37AD3A89613643B6201 24 | gpg --keyserver pool.sks-keyservers.net --recv-keys 71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 25 | gpg --keyserver pool.sks-keyservers.net --recv-keys 8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 26 | gpg --keyserver pool.sks-keyservers.net --recv-keys C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 27 | gpg --keyserver pool.sks-keyservers.net --recv-keys C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C 28 | gpg --keyserver pool.sks-keyservers.net --recv-keys DD8F2338BAE7501E3DD5AC78C273792F7D83545D 29 | gpg --keyserver pool.sks-keyservers.net --recv-keys A48C2BEE680E841632CD4E44F07496B3EB3C1762 30 | gpg --keyserver pool.sks-keyservers.net --recv-keys 108F52B48DB57BB0CC439B2997B01419BD92F80A 31 | gpg --keyserver pool.sks-keyservers.net --recv-keys B9E2F5981AA6E0CD28160D9FF13993A75599653C 32 | 33 | # check integrity of the sha256 sum file 34 | rm -f ${tmp_dir}/SHASUMS256.txt 35 | gpg --batch --decrypt --output ${tmp_dir}/SHASUMS256.txt ${tmp_dir}/SHASUMS256.txt.asc 36 | 37 | #check sha256 38 | pushd "${tmp_dir}" 39 | grep " ${node_archive}\$" SHASUMS256.txt | sha256sum -c - 40 | popd 41 | 42 | #unpack node 43 | rm -rf "${node_dir}" 44 | mkdir -p "${node_dir}" 45 | tar -xJ -f "${tmp_dir}/${node_archive}" --strip 1 -C "${node_dir}" 46 | 47 | #prepare staging dir 48 | rm -rf "${stage_dir}" 49 | mkdir -p "${stage_dir}" 50 | cp -a package.json package-lock.json src/* "${node_dir}"/bin/node "${stage_dir}" 51 | 52 | #install deps and create archive 53 | cd "${stage_dir}" 54 | npm ci --production 55 | rm -rf package-lock.json package.json 56 | rm -f ../layer.zip 57 | 7za a -mx9 -r ../layer.zip . 58 | -------------------------------------------------------------------------------- /auto/publish: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -xe 4 | 5 | DIR="$( cd "$( dirname "$0" )" && pwd )" 6 | 7 | cd "${DIR}/.." 8 | 9 | # https://docs.aws.amazon.com/general/latest/gr/lambda-service.html 10 | # [...$('#w430aab9d229b5b5 tbody').rows].map(e=>e.cells[1].childNodes[0].nodeValue).sort().join("\n") 11 | REGIONS=" 12 | af-south-1 13 | ap-east-1 14 | ap-northeast-1 15 | ap-northeast-2 16 | ap-south-1 17 | ap-southeast-1 18 | ap-southeast-2 19 | ca-central-1 20 | eu-central-1 21 | eu-north-1 22 | eu-south-1 23 | eu-west-1 24 | eu-west-2 25 | eu-west-3 26 | me-south-1 27 | sa-east-1 28 | us-east-1 29 | us-east-2 30 | us-west-1 31 | us-west-2 32 | " 33 | 34 | TMP="deployment-$(date +%s).txt" 35 | LAYER_NAME=node-16-runtime 36 | for R in $REGIONS ; do 37 | echo "Deploying in ${R}" 38 | docker run -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY --rm \ 39 | -w /app \ 40 | -v $(pwd):/app \ 41 | janaz/aws-cli:1.18.41-alpine \ 42 | lambda publish-layer-version \ 43 | --region "${R}" \ 44 | --layer-name "${LAYER_NAME}" \ 45 | --description "nodejs-16.0.0 aws-cli-2.889.0" \ 46 | --compatible-runtimes provided \ 47 | --license-info Apache-2.0 \ 48 | --zip-file fileb://stage/layer.zip 49 | 50 | echo "Getting the version number" 51 | layer_version=$(docker run -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY --rm \ 52 | -w /app \ 53 | -v $(pwd):/app \ 54 | janaz/aws-cli:1.18.41-alpine \ 55 | lambda list-layer-versions \ 56 | --region "${R}" \ 57 | --layer-name "${LAYER_NAME}" \ 58 | --query 'max_by(LayerVersions, &Version).Version' \ 59 | --output text) 60 | 61 | echo "Setting permissions for the layer" 62 | docker run -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY --rm \ 63 | -w /app \ 64 | -v $(pwd):/app \ 65 | janaz/aws-cli:1.18.41-alpine \ 66 | lambda add-layer-version-permission \ 67 | --region "${R}" \ 68 | --layer-name "${LAYER_NAME}" \ 69 | --version-number "${layer_version}" \ 70 | --statement-id share-with-all-accounts \ 71 | --principal '*' \ 72 | --action 'lambda:GetLayerVersion' 73 | 74 | done | tee "$TMP" 75 | 76 | set +x 77 | # Markdown release notes 78 | echo "|Region|Layer arn|" 79 | echo "|--|--|" 80 | cat "$TMP" | grep LayerVersionArn | cut -d '"' -f4 | while read L; do 81 | R=$(echo $L | cut -d: -f4) 82 | echo "|$R|\`$L\`|" 83 | done 84 | -------------------------------------------------------------------------------- /bootstrap-sample.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -euo pipefail 3 | 4 | # Handler format: . 5 | # 6 | # The script file .sh must be located at the root of your 7 | # function's deployment package, alongside this bootstrap executable. 8 | source $(dirname "$0")/"$(echo $_HANDLER | cut -d. -f1).sh" 9 | 10 | while true 11 | do 12 | # Request the next event from the Lambda runtime 13 | HEADERS="$(mktemp)" 14 | EVENT_DATA=$(curl -v -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next") 15 | INVOCATION_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2) 16 | 17 | # Execute the handler function from the script 18 | RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA") 19 | 20 | # Send the response to Lambda runtime 21 | curl -v -sS -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$INVOCATION_ID/response" -d "$RESPONSE" 22 | done 23 | -------------------------------------------------------------------------------- /container/Dockerfile: -------------------------------------------------------------------------------- 1 | 2 | ARG NODE_VERSION=16.0.0 3 | ARG AWS_SDK_VERSION=2.889.0 4 | ARG AWS_LAMBDA_NODE_RUNTIME_VERSION=0.1.0 5 | 6 | FROM node:${NODE_VERSION}-buster-slim 7 | 8 | ARG BOOTSTRAP_DIR=/opt/bootstrap 9 | 10 | ENV NODE_PATH=${BOOTSTRAP_DIR}/node_modules 11 | ENV LAMBDA_TASK_ROOT=/var/task 12 | 13 | RUN mkdir ${BOOTSTRAP_DIR} && \ 14 | mkdir ${LAMBDA_TASK_ROOT} && \ 15 | cd ${BOOTSTRAP_DIR} && \ 16 | npm install --no-save aws-sdk@${AWS_SDK_VERSION} && \ 17 | npm install --no-save -g aws-lambda-node-runtime@${AWS_LAMBDA_NODE_RUNTIME_VERSION} 18 | 19 | WORKDIR ${LAMBDA_TASK_ROOT} 20 | 21 | ENTRYPOINT ["/usr/local/bin/npx", "aws-lambda-node-runtime"] 22 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | ci: 5 | build: . 6 | environment: 7 | NPM_CONFIG_CACHE: /tmp/npm-cache 8 | volumes: 9 | - .:/app 10 | - node-modules:/app/node_modules 11 | - npm-cache:/tmp/npm-cache 12 | working_dir: /app 13 | 14 | dev-runtime: 15 | build: . 16 | environment: 17 | NPM_CONFIG_CACHE: /tmp/npm-cache 18 | AWS_LAMBDA_RUNTIME_API: runtime-api:3000 19 | volumes: 20 | - .:/app 21 | - node-modules:/app/node_modules 22 | - npm-cache:/tmp/npm-cache 23 | working_dir: /app 24 | command: auto/dev-runtime-cmd 25 | networks: 26 | internal: 27 | depends_on: 28 | - runtime-api 29 | 30 | dev-runtime-docker: 31 | build: test 32 | environment: 33 | AWS_LAMBDA_FUNCTION_NAME: test-function-name 34 | AWS_LAMBDA_FUNCTION_VERSION: test-function-version 35 | AWS_LAMBDA_FUNCTION_MEMORY_SIZE: 128 36 | AWS_LAMBDA_LOG_GROUP_NAME: test-log-group-name 37 | AWS_LAMBDA_LOG_STREAM_NAME: test-log-stream-name 38 | AWS_LAMBDA_RUNTIME_API: runtime-api:3000 39 | networks: 40 | internal: 41 | depends_on: 42 | - runtime-api 43 | 44 | runtime-api: 45 | image: node:16.0.0-buster-slim 46 | environment: 47 | NPM_CONFIG_CACHE: /tmp/npm-cache 48 | volumes: 49 | - .:/app 50 | - node-modules:/app/node_modules 51 | - npm-cache:/tmp/npm-cache 52 | working_dir: /app 53 | command: sh -c "npm install && node runtime-api/server" 54 | networks: 55 | internal: 56 | 57 | networks: 58 | internal: 59 | 60 | volumes: 61 | node-modules: 62 | npm-cache: 63 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-custom-execution-environment", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.7", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 10 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 11 | "dev": true, 12 | "requires": { 13 | "mime-types": "~2.1.24", 14 | "negotiator": "0.6.2" 15 | } 16 | }, 17 | "array-flatten": { 18 | "version": "1.1.1", 19 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 20 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", 21 | "dev": true 22 | }, 23 | "aws-lambda-node-runtime": { 24 | "version": "0.1.0", 25 | "resolved": "https://registry.npmjs.org/aws-lambda-node-runtime/-/aws-lambda-node-runtime-0.1.0.tgz", 26 | "integrity": "sha512-UaDPDQmy5J1xNWuKFLYOgErZrLqC/vrPh+KHUaguRWpuVh26tqD+qxil6UBZoFexFz8VvxGcMJdj1TAYR/KrqQ==" 27 | }, 28 | "aws-sdk": { 29 | "version": "2.889.0", 30 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.889.0.tgz", 31 | "integrity": "sha512-+v77GmIJKXT3GMDg/HF9x8c7RSVU8Imfp/0n0Tuzf5AAE6eavpD3xzHABiK9zO9f+T8XzJDytl66UQ33YXavng==", 32 | "requires": { 33 | "buffer": "4.9.2", 34 | "events": "1.1.1", 35 | "ieee754": "1.1.13", 36 | "jmespath": "0.15.0", 37 | "querystring": "0.2.0", 38 | "sax": "1.2.1", 39 | "url": "0.10.3", 40 | "uuid": "3.3.2", 41 | "xml2js": "0.4.19" 42 | } 43 | }, 44 | "base64-js": { 45 | "version": "1.5.1", 46 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 47 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 48 | }, 49 | "body-parser": { 50 | "version": "1.19.0", 51 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 52 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 53 | "dev": true, 54 | "requires": { 55 | "bytes": "3.1.0", 56 | "content-type": "~1.0.4", 57 | "debug": "2.6.9", 58 | "depd": "~1.1.2", 59 | "http-errors": "1.7.2", 60 | "iconv-lite": "0.4.24", 61 | "on-finished": "~2.3.0", 62 | "qs": "6.7.0", 63 | "raw-body": "2.4.0", 64 | "type-is": "~1.6.17" 65 | } 66 | }, 67 | "buffer": { 68 | "version": "4.9.2", 69 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", 70 | "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", 71 | "requires": { 72 | "base64-js": "^1.0.2", 73 | "ieee754": "^1.1.4", 74 | "isarray": "^1.0.0" 75 | } 76 | }, 77 | "bytes": { 78 | "version": "3.1.0", 79 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 80 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", 81 | "dev": true 82 | }, 83 | "content-disposition": { 84 | "version": "0.5.3", 85 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 86 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 87 | "dev": true, 88 | "requires": { 89 | "safe-buffer": "5.1.2" 90 | } 91 | }, 92 | "content-type": { 93 | "version": "1.0.4", 94 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 95 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", 96 | "dev": true 97 | }, 98 | "cookie": { 99 | "version": "0.4.0", 100 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 101 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", 102 | "dev": true 103 | }, 104 | "cookie-signature": { 105 | "version": "1.0.6", 106 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 107 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", 108 | "dev": true 109 | }, 110 | "debug": { 111 | "version": "2.6.9", 112 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 113 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 114 | "dev": true, 115 | "requires": { 116 | "ms": "2.0.0" 117 | } 118 | }, 119 | "depd": { 120 | "version": "1.1.2", 121 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 122 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", 123 | "dev": true 124 | }, 125 | "destroy": { 126 | "version": "1.0.4", 127 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 128 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", 129 | "dev": true 130 | }, 131 | "ee-first": { 132 | "version": "1.1.1", 133 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 134 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", 135 | "dev": true 136 | }, 137 | "encodeurl": { 138 | "version": "1.0.2", 139 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 140 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", 141 | "dev": true 142 | }, 143 | "escape-html": { 144 | "version": "1.0.3", 145 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 146 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", 147 | "dev": true 148 | }, 149 | "etag": { 150 | "version": "1.8.1", 151 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 152 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", 153 | "dev": true 154 | }, 155 | "events": { 156 | "version": "1.1.1", 157 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", 158 | "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" 159 | }, 160 | "express": { 161 | "version": "4.17.1", 162 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 163 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 164 | "dev": true, 165 | "requires": { 166 | "accepts": "~1.3.7", 167 | "array-flatten": "1.1.1", 168 | "body-parser": "1.19.0", 169 | "content-disposition": "0.5.3", 170 | "content-type": "~1.0.4", 171 | "cookie": "0.4.0", 172 | "cookie-signature": "1.0.6", 173 | "debug": "2.6.9", 174 | "depd": "~1.1.2", 175 | "encodeurl": "~1.0.2", 176 | "escape-html": "~1.0.3", 177 | "etag": "~1.8.1", 178 | "finalhandler": "~1.1.2", 179 | "fresh": "0.5.2", 180 | "merge-descriptors": "1.0.1", 181 | "methods": "~1.1.2", 182 | "on-finished": "~2.3.0", 183 | "parseurl": "~1.3.3", 184 | "path-to-regexp": "0.1.7", 185 | "proxy-addr": "~2.0.5", 186 | "qs": "6.7.0", 187 | "range-parser": "~1.2.1", 188 | "safe-buffer": "5.1.2", 189 | "send": "0.17.1", 190 | "serve-static": "1.14.1", 191 | "setprototypeof": "1.1.1", 192 | "statuses": "~1.5.0", 193 | "type-is": "~1.6.18", 194 | "utils-merge": "1.0.1", 195 | "vary": "~1.1.2" 196 | } 197 | }, 198 | "finalhandler": { 199 | "version": "1.1.2", 200 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 201 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 202 | "dev": true, 203 | "requires": { 204 | "debug": "2.6.9", 205 | "encodeurl": "~1.0.2", 206 | "escape-html": "~1.0.3", 207 | "on-finished": "~2.3.0", 208 | "parseurl": "~1.3.3", 209 | "statuses": "~1.5.0", 210 | "unpipe": "~1.0.0" 211 | } 212 | }, 213 | "forwarded": { 214 | "version": "0.1.2", 215 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 216 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", 217 | "dev": true 218 | }, 219 | "fresh": { 220 | "version": "0.5.2", 221 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 222 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", 223 | "dev": true 224 | }, 225 | "http-errors": { 226 | "version": "1.7.2", 227 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 228 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 229 | "dev": true, 230 | "requires": { 231 | "depd": "~1.1.2", 232 | "inherits": "2.0.3", 233 | "setprototypeof": "1.1.1", 234 | "statuses": ">= 1.5.0 < 2", 235 | "toidentifier": "1.0.0" 236 | } 237 | }, 238 | "iconv-lite": { 239 | "version": "0.4.24", 240 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 241 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 242 | "dev": true, 243 | "requires": { 244 | "safer-buffer": ">= 2.1.2 < 3" 245 | } 246 | }, 247 | "ieee754": { 248 | "version": "1.1.13", 249 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", 250 | "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" 251 | }, 252 | "inherits": { 253 | "version": "2.0.3", 254 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 255 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 256 | "dev": true 257 | }, 258 | "ipaddr.js": { 259 | "version": "1.9.1", 260 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 261 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 262 | "dev": true 263 | }, 264 | "isarray": { 265 | "version": "1.0.0", 266 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 267 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 268 | }, 269 | "jmespath": { 270 | "version": "0.15.0", 271 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", 272 | "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" 273 | }, 274 | "media-typer": { 275 | "version": "0.3.0", 276 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 277 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", 278 | "dev": true 279 | }, 280 | "merge-descriptors": { 281 | "version": "1.0.1", 282 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 283 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", 284 | "dev": true 285 | }, 286 | "methods": { 287 | "version": "1.1.2", 288 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 289 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", 290 | "dev": true 291 | }, 292 | "mime": { 293 | "version": "1.6.0", 294 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 295 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 296 | "dev": true 297 | }, 298 | "mime-db": { 299 | "version": "1.44.0", 300 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", 301 | "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", 302 | "dev": true 303 | }, 304 | "mime-types": { 305 | "version": "2.1.27", 306 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", 307 | "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", 308 | "dev": true, 309 | "requires": { 310 | "mime-db": "1.44.0" 311 | } 312 | }, 313 | "ms": { 314 | "version": "2.0.0", 315 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 316 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 317 | "dev": true 318 | }, 319 | "negotiator": { 320 | "version": "0.6.2", 321 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 322 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", 323 | "dev": true 324 | }, 325 | "on-finished": { 326 | "version": "2.3.0", 327 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 328 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 329 | "dev": true, 330 | "requires": { 331 | "ee-first": "1.1.1" 332 | } 333 | }, 334 | "parseurl": { 335 | "version": "1.3.3", 336 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 337 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 338 | "dev": true 339 | }, 340 | "path-to-regexp": { 341 | "version": "0.1.7", 342 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 343 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", 344 | "dev": true 345 | }, 346 | "proxy-addr": { 347 | "version": "2.0.6", 348 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", 349 | "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", 350 | "dev": true, 351 | "requires": { 352 | "forwarded": "~0.1.2", 353 | "ipaddr.js": "1.9.1" 354 | } 355 | }, 356 | "punycode": { 357 | "version": "1.3.2", 358 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", 359 | "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" 360 | }, 361 | "qs": { 362 | "version": "6.7.0", 363 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 364 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", 365 | "dev": true 366 | }, 367 | "querystring": { 368 | "version": "0.2.0", 369 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", 370 | "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" 371 | }, 372 | "range-parser": { 373 | "version": "1.2.1", 374 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 375 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 376 | "dev": true 377 | }, 378 | "raw-body": { 379 | "version": "2.4.0", 380 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 381 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 382 | "dev": true, 383 | "requires": { 384 | "bytes": "3.1.0", 385 | "http-errors": "1.7.2", 386 | "iconv-lite": "0.4.24", 387 | "unpipe": "1.0.0" 388 | } 389 | }, 390 | "safe-buffer": { 391 | "version": "5.1.2", 392 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 393 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 394 | "dev": true 395 | }, 396 | "safer-buffer": { 397 | "version": "2.1.2", 398 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 399 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 400 | "dev": true 401 | }, 402 | "sax": { 403 | "version": "1.2.1", 404 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", 405 | "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" 406 | }, 407 | "send": { 408 | "version": "0.17.1", 409 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 410 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 411 | "dev": true, 412 | "requires": { 413 | "debug": "2.6.9", 414 | "depd": "~1.1.2", 415 | "destroy": "~1.0.4", 416 | "encodeurl": "~1.0.2", 417 | "escape-html": "~1.0.3", 418 | "etag": "~1.8.1", 419 | "fresh": "0.5.2", 420 | "http-errors": "~1.7.2", 421 | "mime": "1.6.0", 422 | "ms": "2.1.1", 423 | "on-finished": "~2.3.0", 424 | "range-parser": "~1.2.1", 425 | "statuses": "~1.5.0" 426 | }, 427 | "dependencies": { 428 | "ms": { 429 | "version": "2.1.1", 430 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 431 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", 432 | "dev": true 433 | } 434 | } 435 | }, 436 | "serve-static": { 437 | "version": "1.14.1", 438 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 439 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 440 | "dev": true, 441 | "requires": { 442 | "encodeurl": "~1.0.2", 443 | "escape-html": "~1.0.3", 444 | "parseurl": "~1.3.3", 445 | "send": "0.17.1" 446 | } 447 | }, 448 | "setprototypeof": { 449 | "version": "1.1.1", 450 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 451 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", 452 | "dev": true 453 | }, 454 | "statuses": { 455 | "version": "1.5.0", 456 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 457 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", 458 | "dev": true 459 | }, 460 | "toidentifier": { 461 | "version": "1.0.0", 462 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 463 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", 464 | "dev": true 465 | }, 466 | "type-is": { 467 | "version": "1.6.18", 468 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 469 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 470 | "dev": true, 471 | "requires": { 472 | "media-typer": "0.3.0", 473 | "mime-types": "~2.1.24" 474 | } 475 | }, 476 | "unpipe": { 477 | "version": "1.0.0", 478 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 479 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", 480 | "dev": true 481 | }, 482 | "url": { 483 | "version": "0.10.3", 484 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", 485 | "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", 486 | "requires": { 487 | "punycode": "1.3.2", 488 | "querystring": "0.2.0" 489 | } 490 | }, 491 | "utils-merge": { 492 | "version": "1.0.1", 493 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 494 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", 495 | "dev": true 496 | }, 497 | "uuid": { 498 | "version": "3.3.2", 499 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 500 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 501 | }, 502 | "vary": { 503 | "version": "1.1.2", 504 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 505 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", 506 | "dev": true 507 | }, 508 | "xml2js": { 509 | "version": "0.4.19", 510 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", 511 | "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", 512 | "requires": { 513 | "sax": ">=0.6.0", 514 | "xmlbuilder": "~9.0.1" 515 | } 516 | }, 517 | "xmlbuilder": { 518 | "version": "9.0.7", 519 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", 520 | "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" 521 | } 522 | } 523 | } 524 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-custom-execution-environment", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "author": "Tomasz Janowski ", 6 | "license": "MIT", 7 | "scripts": { 8 | "start-api": "node runtime-api/server", 9 | "start-runtime": "./auto/dev-runtime" 10 | }, 11 | "devDependencies": { 12 | "express": "^4.17.1" 13 | }, 14 | "dependencies": { 15 | "aws-lambda-node-runtime": "0.1.0", 16 | "aws-sdk": "^2.889.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /runtime-api/api.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const api = express(); 4 | 5 | api.use(express.json()); 6 | 7 | api.get('/2018-06-01/runtime/invocation/next', (_, res) => { 8 | console.log("invocation next"); 9 | setTimeout(() => { 10 | res.set({ 11 | 'lambda-runtime-aws-request-id': `aws-req-id-${Math.random()}`, 12 | 'lambda-runtime-deadline-ms': (new Date()).getTime() + 100, 13 | 'lambda-runtime-trace-id': `xray-trace-id-${Math.random()}`, 14 | 'lambda-runtime-invoked-function-arn': 'test-function-arn', 15 | 'lambda-runtime-client-context': JSON.stringify({ 16 | client: { 17 | installation_id: 'installation_id', 18 | app_title: 'app_title', 19 | app_version_name: 'app_version_name', 20 | app_version_code: 'app_version_code', 21 | app_package_name: 'app_package_name', 22 | }, 23 | env: { 24 | platform_version: 'platform_version', 25 | platform: 'platform', 26 | make: 'make', 27 | model: 'model', 28 | locale: 'locale', 29 | }, 30 | custom: { 31 | key: 'value', 32 | }, 33 | }), 34 | 'lambda-runtime-cognito-identity': JSON.stringify({ 35 | cognitoIdentityId: 'cognitoIdentityIdExample', 36 | cognitoIdentityPoolId: 'cognitoIdentityPoolIdExample' 37 | }), 38 | }); 39 | res.json({x: 42, y: 24}); 40 | }, 1000) 41 | }); 42 | 43 | api.post('/2018-06-01/runtime/invocation/:id/error', (req, res) => { 44 | console.log("invocation error", JSON.stringify(req.body)); 45 | res.json({}); 46 | }); 47 | 48 | api.post('/2018-06-01/runtime/invocation/:id/response', (req, res) => { 49 | console.log("invocation response", JSON.stringify(req.body)); 50 | res.json({}); 51 | }); 52 | 53 | api.post('/2018-06-01/runtime/init/error', (req, res) => { 54 | console.log("init error", JSON.stringify(req.body)); 55 | res.json({}); 56 | }); 57 | 58 | module.exports = api; 59 | -------------------------------------------------------------------------------- /runtime-api/server.js: -------------------------------------------------------------------------------- 1 | const api = require('./api'); 2 | api.listen(3000, '0.0.0.0', () => console.log('Listening on port 3000!')); 3 | -------------------------------------------------------------------------------- /src/bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | DIR="$( cd "$( dirname "$0" )" && pwd )" 6 | 7 | export NODE_PATH="${DIR}/node_modules" 8 | 9 | cd "${LAMBDA_TASK_ROOT}" 10 | 11 | # AWS_LAMBDA_FUNCTION_MEMORY_SIZE 12 | 13 | exec "${DIR}/node" "${DIR}/start.js" 14 | -------------------------------------------------------------------------------- /src/start.js: -------------------------------------------------------------------------------- 1 | const runtime = require('aws-lambda-node-runtime'); 2 | 3 | const done = (err) => { 4 | if (err) { 5 | console.error('Runtime exiting due to an error', err); 6 | } 7 | } 8 | 9 | runtime(done); 10 | -------------------------------------------------------------------------------- /test/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM janaz/aws-lambda-node-runtime:test-1 2 | 3 | COPY hello.js ${LAMBDA_TASK_ROOT}/ 4 | CMD ["hello.handler"] 5 | 6 | -------------------------------------------------------------------------------- /test/hello.js: -------------------------------------------------------------------------------- 1 | const aws = require('aws-sdk'); 2 | 3 | const handler = (event, ctx) => 4 | Promise.resolve({ 5 | event, 6 | ctx, 7 | node_version: process.version, 8 | remaining_time: ctx.getRemainingTimeInMillis(), 9 | }) 10 | 11 | module.exports = { handler }; 12 | --------------------------------------------------------------------------------