diff --git a/Makefile b/Makefile index 95b1fa1..9d1b490 100644 --- a/Makefile +++ b/Makefile @@ -2,31 +2,43 @@ RELEASE_BUILD_LINKER_FLAGS=-s -w BINARY_NAME=aws-lambda-rie -DESTINATION=bin/${BINARY_NAME} +ARCH=x86_64 +GO_ARCH_old := amd64 +GO_ARCH_x86_64 := amd64 +GO_ARCH_arm64 := arm64 +DESTINATION_old:= bin/${BINARY_NAME} +DESTINATION_x86_64 := bin/${BINARY_NAME}-x86_64 +DESTINATION_arm64 := bin/${BINARY_NAME}-arm64 + +compile-with-docker-all: + make ARCH=x86_64 compile-with-docker + make ARCH=arm64 compile-with-docker + make ARCH=old compile-with-docker + +compile-lambda-linux-all: + make ARCH=x86_64 compile-lambda-linux + make ARCH=arm64 compile-lambda-linux + make ARCH=old compile-lambda-linux compile-with-docker: - docker run --env GOPROXY=direct -v $(shell pwd):/LambdaRuntimeLocal -w /LambdaRuntimeLocal golang:1.14 make compile-lambda-linux + docker run --env GOPROXY=direct -v $(shell pwd):/LambdaRuntimeLocal -w /LambdaRuntimeLocal golang:1.14 make ARCH=${ARCH} compile-lambda-linux compile-lambda-linux: - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "${RELEASE_BUILD_LINKER_FLAGS}" -o ${DESTINATION} ./cmd/aws-lambda-rie + CGO_ENABLED=0 GOOS=linux GOARCH=${GO_ARCH_${ARCH}} go build -ldflags "${RELEASE_BUILD_LINKER_FLAGS}" -o ${DESTINATION_${ARCH}} ./cmd/aws-lambda-rie tests: go test ./... -integ-tests-and-compile: tests compile-lambda-linux - python3 -m venv .venv - .venv/bin/pip install --upgrade pip - .venv/bin/pip install requests - .venv/bin/python3 test/integration/local_lambda/end-to-end-test.py - -integ-tests-with-docker: tests compile-with-docker - python3 -m venv .venv - .venv/bin/pip install --upgrade pip - .venv/bin/pip install requests - .venv/bin/python3 test/integration/local_lambda/end-to-end-test.py +integ-tests-and-compile: tests + make compile-lambda-linux-all + make integ-tests +integ-tests-with-docker: tests + make compile-with-docker-all + make integ-tests + integ-tests: python3 -m venv .venv .venv/bin/pip install --upgrade pip - .venv/bin/pip install requests - .venv/bin/python3 test/integration/local_lambda/end-to-end-test.py + .venv/bin/pip install requests parameterized + .venv/bin/python3 test/integration/local_lambda/test_end_to_end.py diff --git a/README.md b/README.md index 1fe10f6..0eec533 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,10 @@ Instructions for installing AWS Lambda Runtime Interface Emulator for your platf | Platform | Command to install | |---------|--------- -| macOS | `mkdir -p ~/.aws-lambda-rie && curl -Lo ~/.aws-lambda-rie/aws-lambda-rie https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie && chmod +x ~/.aws-lambda-rie/aws-lambda-rie` | -| Linux | `mkdir -p ~/.aws-lambda-rie && curl -Lo ~/.aws-lambda-rie/aws-lambda-rie https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie && chmod +x ~/.aws-lambda-rie/aws-lambda-rie` | -| Windows | `Invoke-WebRequest -OutFile 'C:\Program Files\aws lambda\aws-lambda-rie' https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie` | +| macOS/Linux x86\_64 | `mkdir -p ~/.aws-lambda-rie && curl -Lo ~/.aws-lambda-rie/aws-lambda-rie https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie && chmod +x ~/.aws-lambda-rie/aws-lambda-rie` | +| macOS/Linux arm64 | `mkdir -p ~/.aws-lambda-rie && curl -Lo ~/.aws-lambda-rie/aws-lambda-rie https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie-arm64 && chmod +x ~/.aws-lambda-rie/aws-lambda-rie` | +| Windows x86\_64 | `Invoke-WebRequest -OutFile 'C:\Program Files\aws lambda\aws-lambda-rie' https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie` | +| Windows arm64 | `Invoke-WebRequest -OutFile 'C:\Program Files\aws lambda\aws-lambda-rie' https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie-arm64` | ## Getting started @@ -53,10 +54,14 @@ The AWS base images for Lambda include the runtime interface emulator. You can a ### Build RIE into your base image You can build RIE into a base image. Download the RIE from GitHub to your local machine and update your Dockerfile to install RIE. - + #### To build the emulator into your image -1. Create a script and save it in your project directory. The following example shows a typical script for a Node.js function. The presence of the AWS_LAMBDA_RUNTIME_API environment variable indicates the presence of the runtime API. If the runtime API is present, the script runs the runtime interface client (https://docs.aws.amazon.com/lambda/latest/dg/runtimes-images.html#runtimes-api-client). Otherwise, the script runs the runtime interface emulator. +1. Create a script and save it in your project directory. Set execution permissions for the script file. + +The script checks for the presence of the `AWS_LAMBDA_RUNTIME_API` environment variable, which indicates the presence of the runtime API. If the runtime API is present, the script runs [the runtime interface client](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-images.html#runtimes-api-client). Otherwise, the script runs the runtime interface emulator. + +The following example shows a typical script for a Node.js function. ``` #!/bin/sh if [ -z "${AWS_LAMBDA_RUNTIME_API}" ]; then @@ -65,45 +70,56 @@ You can build RIE into a base image. Download the RIE from GitHub to your local exec /usr/bin/npx aws-lambda-ric fi ``` - -2. Download the runtime interface emulator (https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie) from GitHub into your project directory. -3. Install the emulator package and change ENTRYPOINT to run the new script by adding the following lines to your Dockerfile: +2. Download the [runtime interface emulator](https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest) for your target architecture (`aws-lambda-rie` for x86\_64 or `aws-lambda-rie-arm64` for arm64) from GitHub into your project directory. + +3. Install the emulator package and change `ENTRYPOINT` to run the new script by adding the following lines to your Dockerfile: + +To use the default x86\_64 architecture + ``` + ADD aws-lambda-rie /usr/local/bin/aws-lambda-rie + ENTRYPOINT [ "/entry_script.sh" ] + ``` + +To use the arm64 architecture: ``` - ADD aws-lambda-rie /usr/local/bin/aws-lambda-rie + ADD aws-lambda-rie-arm64 /usr/local/bin/aws-lambda-rie ENTRYPOINT [ "/entry_script.sh" ] ``` -4. Build your image locally using the docker build command. +4. Build your image locally using the docker build command. ``` docker build -t myfunction:latest . ``` -5. Run your image locally using the docker run command. +5. Run your image locally using the docker run command. ``` docker run -p 9000:8080 myfunction:latest ``` ### Test an image without adding RIE to the image -You install the runtime interface emulator to your local machine. When you run the image function, you set the entry point to be the emulator. +You install the runtime interface emulator to your local machine. When you run the container image, you set the entry point to be the emulator. *To test an image without adding RIE to the image * -1. From your project directory, run the following command to download the RIE from GitHub and install it on your local machine. +1. From your project directory, run the following command to download the RIE (x86-64 architecture) from GitHub and install it on your local machine. ``` - mkdir -p ~/.aws-lambda-rie && curl -Lo ~/.aws-lambda-rie/aws-lambda-rie \ - https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie \ - && chmod +x ~/.aws-lambda-rie/aws-lambda-rie - ``` + mkdir -p ~/.aws-lambda-rie && curl -Lo ~/.aws-lambda-rie/aws-lambda-rie \ + https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie \ + && chmod +x ~/.aws-lambda-rie/aws-lambda-rie + ``` -2. Run your Lambda image function using the docker run command. +To download the RIE for arm64 architecture, use the previous command with a different GitHub download url. + ``` + https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie-arm64 \ + ``` +2. Run your Lambda image function using the docker run command. + ``` + docker run -d -v ~/.aws-lambda-rie:/aws-lambda -p 9000:8080 myfunction:latest + --entrypoint /aws-lambda/aws-lambda-rie <(optional) image command>` ``` - docker run -d -v ~/.aws-lambda-rie:/aws-lambda -p 9000:8080 \ - --entrypoint /aws-lambda/aws-lambda-rie \ - myfunction:latest <(optional) image command> - ```` This runs the image as a container and starts up an endpoint locally at `localhost:9000/2015-03-31/functions/function/invocations`. @@ -142,7 +158,7 @@ configurations that will not be emulated by this component. * You can also use it to test extensions and agents built into the container image against the Lambda Extensions API. * This component does _not_ emulate Lambda’s orchestration, or security and authentication configurations. * The component does _not_ support X-ray and other Lambda integrations locally. -* The component supports only Linux x86-64 architectures. +* The component supports only Linux, for x86-64 and arm64 architectures. ## Security diff --git a/cmd/aws-lambda-rie/main.go b/cmd/aws-lambda-rie/main.go index 4c28f51..a151ae7 100644 --- a/cmd/aws-lambda-rie/main.go +++ b/cmd/aws-lambda-rie/main.go @@ -56,16 +56,34 @@ func getCLIArgs() (options, []string) { return opts, args } +func isBootstrapFileExist(filePath string) bool { + file, err := os.Stat(filePath) + return !os.IsNotExist(err) && !file.IsDir() +} + func getBootstrap(args []string, opts options) (*rapidcore.Bootstrap, string) { var bootstrapLookupCmd []string var handler string currentWorkingDir := "/var/task" // default value if len(args) <= 1 { + // set default value to /var/task/bootstrap, but switch to the other options if it doesn't exist bootstrapLookupCmd = []string{ fmt.Sprintf("%s/bootstrap", currentWorkingDir), - optBootstrap, - runtimeBootstrap, + } + + if !isBootstrapFileExist(bootstrapLookupCmd[0]) { + var bootstrapCmdCandidates = []string{ + optBootstrap, + runtimeBootstrap, + } + + for i, bootstrapCandidate := range bootstrapCmdCandidates { + if isBootstrapFileExist(bootstrapCandidate) { + bootstrapLookupCmd = []string{bootstrapCmdCandidates[i]} + break + } + } } // handler is used later to set an env var for Lambda Image support diff --git a/test/integration/local_lambda/end-to-end-test.py b/test/integration/local_lambda/end-to-end-test.py deleted file mode 100644 index 27d0e07..0000000 --- a/test/integration/local_lambda/end-to-end-test.py +++ /dev/null @@ -1,198 +0,0 @@ -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 - -from subprocess import Popen, PIPE -from unittest import TestCase, main -from pathlib import Path -import time - -import requests - -SLEEP_TIME = 2 -DEFAULT_1P_ENTRYPOINT = "/lambda-entrypoint.sh" - -class TestEndToEnd(TestCase): - - @classmethod - def setUpClass(cls): - testdata_path = Path(__file__).resolve().parents[1].joinpath("testdata") - dockerfile_path = testdata_path.joinpath("Dockerfile-allinone") - cls.image_name = "aws-lambda-local:testing" - cls.path_to_binary = Path().resolve().joinpath("bin") - - - # build image - build_cmd = ["docker", "build", "-t", cls.image_name, "-f", str(dockerfile_path), str(testdata_path)] - Popen(build_cmd).communicate() - - @classmethod - def tearDownClass(cls): - cmds_to_delete_images = ["docker rm -f envvarcheck", "docker rm -f testing", "docker rm -f timeout", "docker rm -f exception"] - - for cmd in cmds_to_delete_images: - Popen(cmd.split(' ')).communicate() - - Popen(f"docker rmi {cls.image_name}".split(' ')).communicate() - - - def test_env_var_with_eqaul_sign(self): - cmd = f"docker run --name envvarcheck -d -v {self.path_to_binary}:/local-lambda-runtime-server -p 9003:8080 --entrypoint /local-lambda-runtime-server/aws-lambda-rie {self.image_name} {DEFAULT_1P_ENTRYPOINT} main.check_env_var_handler" - - Popen(cmd.split(' ')).communicate() - - # sleep 1s to give enough time for the endpoint to be up to curl - time.sleep(SLEEP_TIME) - - r = requests.post("http://localhost:9003/2015-03-31/functions/function/invocations", json={}) - self.assertEqual(b'"4=4"', r.content) - - def test_two_invokes(self): - cmd = f"docker run --name testing -d -v {self.path_to_binary}:/local-lambda-runtime-server -p 9000:8080 --entrypoint /local-lambda-runtime-server/aws-lambda-rie {self.image_name} {DEFAULT_1P_ENTRYPOINT} main.success_handler" - - Popen(cmd.split(' ')).communicate() - - # sleep 1s to give enough time for the endpoint to be up to curl - time.sleep(SLEEP_TIME) - - r = requests.post("http://localhost:9000/2015-03-31/functions/function/invocations", json={}) - self.assertEqual(b'"My lambda ran succesfully"', r.content) - - # Make sure we can invoke the function twice - r = requests.post("http://localhost:9000/2015-03-31/functions/function/invocations", json={}) - self.assertEqual(b'"My lambda ran succesfully"', r.content) - - def test_lambda_function_arn_exists(self): - cmd = f"docker run --name testing -d -v {self.path_to_binary}:/local-lambda-runtime-server -p 9000:8080 --entrypoint /local-lambda-runtime-server/aws-lambda-rie {self.image_name} {DEFAULT_1P_ENTRYPOINT} main.assert_lambda_arn_in_context" - - Popen(cmd.split(' ')).communicate() - - # sleep 1s to give enough time for the endpoint to be up to curl - time.sleep(SLEEP_TIME) - - r = requests.post("http://localhost:9000/2015-03-31/functions/function/invocations", json={}) - self.assertEqual(b'"My lambda ran succesfully"', r.content) - - def test_lambda_function_arn_exists_with_defining_custom_name(self): - cmd = f"docker run --name testing --env AWS_LAMBDA_FUNCTION_NAME=MyCoolName -d -v {self.path_to_binary}:/local-lambda-runtime-server -p 9000:8080 --entrypoint /local-lambda-runtime-server/aws-lambda-rie {self.image_name} {DEFAULT_1P_ENTRYPOINT} main.assert_lambda_arn_in_context" - - Popen(cmd.split(' ')).communicate() - - # sleep 1s to give enough time for the endpoint to be up to curl - time.sleep(SLEEP_TIME) - - r = requests.post("http://localhost:9000/2015-03-31/functions/function/invocations", json={}) - self.assertEqual(b'"My lambda ran succesfully"', r.content) - - - def test_timeout_invoke(self): - cmd = f"docker run --name timeout -d --env AWS_LAMBDA_FUNCTION_TIMEOUT=1 -v {self.path_to_binary}:/local-lambda-runtime-server -p 9001:8080 --entrypoint /local-lambda-runtime-server/aws-lambda-rie {self.image_name} {DEFAULT_1P_ENTRYPOINT} main.sleep_handler" - - Popen(cmd.split(' ')).communicate() - - # sleep 1s to give enough time for the endpoint to be up to curl - time.sleep(SLEEP_TIME) - - r = requests.post("http://localhost:9001/2015-03-31/functions/function/invocations", json={}) - self.assertEqual(b"Task timed out after 1.00 seconds", r.content) - - def test_exception_returned(self): - cmd = f"docker run --name exception -d -v {self.path_to_binary}:/local-lambda-runtime-server -p 9002:8080 --entrypoint /local-lambda-runtime-server/aws-lambda-rie {self.image_name} {DEFAULT_1P_ENTRYPOINT} main.exception_handler" - - Popen(cmd.split(' ')).communicate() - - # sleep 1s to give enough time for the endpoint to be up to curl - time.sleep(SLEEP_TIME) - - r = requests.post("http://localhost:9002/2015-03-31/functions/function/invocations", json={}) - self.assertEqual(b'{"errorMessage": "Raising an exception", "errorType": "Exception", "stackTrace": [" File \\"/var/task/main.py\\", line 13, in exception_handler\\n raise Exception(\\"Raising an exception\\")\\n"]}', r.content) - - def test_context_get_remaining_time_in_three_seconds(self): - cmd = f"docker run --name remainingtimethree -d --env AWS_LAMBDA_FUNCTION_TIMEOUT=3 -v {self.path_to_binary}:/local-lambda-runtime-server -p 9004:8080 --entrypoint /local-lambda-runtime-server/aws-lambda-rie {self.image_name} {DEFAULT_1P_ENTRYPOINT} main.check_remaining_time_handler" - - Popen(cmd.split(' ')).communicate() - - r = requests.post("http://localhost:9004/2015-03-31/functions/function/invocations", json={}) - - # sleep 1s to give enough time for the endpoint to be up to curl - time.sleep(SLEEP_TIME) - # Executation time is not decided, 1.0s ~ 3.0s is a good estimation - self.assertLess(int(r.content), 3000) - self.assertGreater(int(r.content), 1000) - - - def test_context_get_remaining_time_in_ten_seconds(self): - cmd = f"docker run --name remainingtimeten -d --env AWS_LAMBDA_FUNCTION_TIMEOUT=10 -v {self.path_to_binary}:/local-lambda-runtime-server -p 9005:8080 --entrypoint /local-lambda-runtime-server/aws-lambda-rie {self.image_name} {DEFAULT_1P_ENTRYPOINT} main.check_remaining_time_handler" - - Popen(cmd.split(' ')).communicate() - - r = requests.post("http://localhost:9005/2015-03-31/functions/function/invocations", json={}) - - # sleep 1s to give enough time for the endpoint to be up to curl - time.sleep(SLEEP_TIME) - # Executation time is not decided, 8.0s ~ 10.0s is a good estimation - self.assertLess(int(r.content), 10000) - self.assertGreater(int(r.content), 8000) - - - def test_context_get_remaining_time_in_default_deadline(self): - cmd = f"docker run --name remainingtimedefault -d -v {self.path_to_binary}:/local-lambda-runtime-server -p 9006:8080 --entrypoint /local-lambda-runtime-server/aws-lambda-rie {self.image_name} {DEFAULT_1P_ENTRYPOINT} main.check_remaining_time_handler" - - Popen(cmd.split(' ')).communicate() - - r = requests.post("http://localhost:9006/2015-03-31/functions/function/invocations", json={}) - - # sleep 1s to give enough time for the endpoint to be up to curl - time.sleep(SLEEP_TIME) - # Executation time is not decided, 298.0s ~ 300.0s is a good estimation - self.assertLess(int(r.content), 300000) - self.assertGreater(int(r.content), 298000) - - -class TestPython36Runtime(TestCase): - - @classmethod - def setUpClass(cls): - testdata_path = Path(__file__).resolve().parents[1].joinpath("testdata") - dockerfile_path = testdata_path.joinpath("Dockerfile-python36") - cls.image_name = "aws-lambda-local:testing-py36" - cls.path_to_binary = Path().resolve().joinpath("bin") - - - # build image - build_cmd = ["docker", "build", "-t", cls.image_name, "-f", str(dockerfile_path), str(testdata_path)] - Popen(build_cmd).communicate() - - @classmethod - def tearDownClass(cls): - cmds_to_delete_images = ["docker rm -f testing", "docker rm -f assert-overwritten"] - - for cmd in cmds_to_delete_images: - Popen(cmd.split(' ')).communicate() - - Popen(f"docker rmi {cls.image_name}".split(' ')).communicate() - - def test_invoke_with_pre_runtime_api_runtime(self): - cmd = f"docker run --name testing -d -v {self.path_to_binary}:/local-lambda-runtime-server -p 9000:8080 --entrypoint /local-lambda-runtime-server/aws-lambda-rie {self.image_name} {DEFAULT_1P_ENTRYPOINT} main.success_handler" - - Popen(cmd.split(' ')).communicate() - - # sleep 1s to give enough time for the endpoint to be up to curl - time.sleep(SLEEP_TIME) - - r = requests.post("http://localhost:9000/2015-03-31/functions/function/invocations", json={}) - self.assertEqual(b'"My lambda ran succesfully"', r.content) - - def test_function_name_is_overriden(self): - cmd = f"docker run --name assert-overwritten -d --env AWS_LAMBDA_FUNCTION_NAME=MyCoolName -v {self.path_to_binary}:/local-lambda-runtime-server -p 9009:8080 --entrypoint /local-lambda-runtime-server/aws-lambda-rie {self.image_name} {DEFAULT_1P_ENTRYPOINT} main.assert_env_var_is_overwritten" - - Popen(cmd.split(' ')).communicate() - - # sleep 1s to give enough time for the endpoint to be up to curl - time.sleep(SLEEP_TIME) - - r = requests.post("http://localhost:9009/2015-03-31/functions/function/invocations", json={}) - self.assertEqual(b'"My lambda ran succesfully"', r.content) - - -if __name__ == "__main__": - main() diff --git a/test/integration/local_lambda/test_end_to_end.py b/test/integration/local_lambda/test_end_to_end.py new file mode 100644 index 0000000..2fe9bfc --- /dev/null +++ b/test/integration/local_lambda/test_end_to_end.py @@ -0,0 +1,209 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +from subprocess import Popen, PIPE +from unittest import TestCase, main +from pathlib import Path +import time + +import requests +from parameterized import parameterized + +SLEEP_TIME = 2 +DEFAULT_1P_ENTRYPOINT = "/lambda-entrypoint.sh" +ARCHS = ["x86_64", "arm64", ""] + + +class TestEndToEnd(TestCase): + @classmethod + def setUpClass(cls): + testdata_path = Path(__file__).resolve().parents[1].joinpath("testdata") + dockerfile_path = testdata_path.joinpath("Dockerfile-allinone") + cls.image_name = "aws-lambda-local:testing" + cls.path_to_binary = Path().resolve().joinpath("bin") + + # build image + for arch in ARCHS: + image_name = cls.image_name if arch == "" else f"{cls.image_name}-{arch}" + architecture = arch if arch == "arm64" else "amd64" + build_cmd = [ + "docker", + "build", + "--platform", + f"linux/{architecture}", + "-t", + image_name, + "-f", + str(dockerfile_path), + str(testdata_path), + ] + Popen(build_cmd).communicate() + + @classmethod + def tearDownClass(cls): + images_to_delete = [ + "envvarcheck", + "twoinvokes", + "arnexists", + "customname", + "timeout", + "exception", + "pre-runtime-api", + "assert-overwritten", + ] + + for image in images_to_delete: + for arch in ARCHS: + arch_tag = "" if arch == "" else f"-{arch}" + cmd = f"docker rm -f {image}{arch_tag}" + Popen(cmd.split(" ")).communicate() + + for arch in ARCHS: + arch_tag = "" if arch == "" else f"-{arch}" + Popen(f"docker rmi {cls.image_name}{arch_tag}".split(" ")).communicate() + + def tagged_name(self, name, architecture): + tag = self.get_tag(architecture) + return (name + tag, "aws-lambda-rie" + tag, self.image_name + tag) + + def get_tag(self, architecture): + return "" if architecture == "" else str(f"-{architecture}") + + @parameterized.expand([("x86_64", "8000"), ("arm64", "9001"), ("", "9003")]) + def test_env_var_with_equal_sign(self, arch, port): + image, rie, image_name = self.tagged_name("envvarcheck", arch) + + cmd = f"docker run --name {image} -d -v {self.path_to_binary}:/local-lambda-runtime-server -p {port}:8080 --entrypoint /local-lambda-runtime-server/{rie} {image_name} {DEFAULT_1P_ENTRYPOINT} main.check_env_var_handler" + Popen(cmd.split(" ")).communicate() + + # sleep 1s to give enough time for the endpoint to be up to curl + time.sleep(SLEEP_TIME) + + r = requests.post( + f"http://localhost:{port}/2015-03-31/functions/function/invocations", json={} + ) + self.assertEqual(b'"4=4"', r.content) + + @parameterized.expand([("x86_64", "8001"), ("arm64", "9002"), ("", "9005")]) + def test_two_invokes(self, arch, port): + image, rie, image_name = self.tagged_name("twoinvokes", arch) + + cmd = f"docker run --name {image} -d -v {self.path_to_binary}:/local-lambda-runtime-server -p {port}:8080 --entrypoint /local-lambda-runtime-server/{rie} {image_name} {DEFAULT_1P_ENTRYPOINT} main.success_handler" + + Popen(cmd.split(" ")).communicate() + + # sleep 1s to give enough time for the endpoint to be up to curl + time.sleep(SLEEP_TIME) + + r = requests.post( + f"http://localhost:{port}/2015-03-31/functions/function/invocations", json={} + ) + self.assertEqual(b'"My lambda ran succesfully"', r.content) + + # Make sure we can invoke the function twice + r = requests.post( + f"http://localhost:{port}/2015-03-31/functions/function/invocations", json={} + ) + self.assertEqual(b'"My lambda ran succesfully"', r.content) + + @parameterized.expand([("x86_64", "8002"), ("arm64", "9004"), ("", "9007")]) + def test_lambda_function_arn_exists(self, arch, port): + image, rie, image_name = self.tagged_name("arnexists", arch) + + cmd = f"docker run --name {image} -d -v {self.path_to_binary}:/local-lambda-runtime-server -p {port}:8080 --entrypoint /local-lambda-runtime-server/{rie} {image_name} {DEFAULT_1P_ENTRYPOINT} main.assert_lambda_arn_in_context" + + Popen(cmd.split(" ")).communicate() + + # sleep 1s to give enough time for the endpoint to be up to curl + time.sleep(SLEEP_TIME) + + r = requests.post( + f"http://localhost:{port}/2015-03-31/functions/function/invocations", json={} + ) + self.assertEqual(b'"My lambda ran succesfully"', r.content) + + @parameterized.expand([("x86_64", "8003"), ("arm64", "9006"), ("", "9009")]) + def test_lambda_function_arn_exists_with_defining_custom_name(self, arch, port): + image, rie, image_name = self.tagged_name("customname", arch) + + cmd = f"docker run --name {image} --env AWS_LAMBDA_FUNCTION_NAME=MyCoolName -d -v {self.path_to_binary}:/local-lambda-runtime-server -p {port}:8080 --entrypoint /local-lambda-runtime-server/{rie} {image_name} {DEFAULT_1P_ENTRYPOINT} main.assert_lambda_arn_in_context" + Popen(cmd.split(" ")).communicate() + + # sleep 1s to give enough time for the endpoint to be up to curl + time.sleep(SLEEP_TIME) + + r = requests.post( + f"http://localhost:{port}/2015-03-31/functions/function/invocations", json={} + ) + self.assertEqual(b'"My lambda ran succesfully"', r.content) + + @parameterized.expand([("x86_64", "8004"), ("arm64", "9008"), ("", "9011")]) + def test_timeout_invoke(self, arch, port): + image, rie, image_name = self.tagged_name("timeout", arch) + + cmd = f"docker run --name {image} -d --env AWS_LAMBDA_FUNCTION_TIMEOUT=1 -v {self.path_to_binary}:/local-lambda-runtime-server -p {port}:8080 --entrypoint /local-lambda-runtime-server/{rie} {image_name} {DEFAULT_1P_ENTRYPOINT} main.sleep_handler" + + Popen(cmd.split(" ")).communicate() + + # sleep 1s to give enough time for the endpoint to be up to curl + time.sleep(SLEEP_TIME) + + r = requests.post( + f"http://localhost:{port}/2015-03-31/functions/function/invocations", json={} + ) + self.assertEqual(b"Task timed out after 1.00 seconds", r.content) + + @parameterized.expand([("x86_64", "8005"), ("arm64", "9010"), ("", "9013")]) + def test_exception_returned(self, arch, port): + image, rie, image_name = self.tagged_name("exception", arch) + + cmd = f"docker run --name {image} -d -v {self.path_to_binary}:/local-lambda-runtime-server -p {port}:8080 --entrypoint /local-lambda-runtime-server/{rie} {image_name} {DEFAULT_1P_ENTRYPOINT} main.exception_handler" + + Popen(cmd.split(" ")).communicate() + + # sleep 1s to give enough time for the endpoint to be up to curl + time.sleep(SLEEP_TIME) + + r = requests.post( + f"http://localhost:{port}/2015-03-31/functions/function/invocations", json={} + ) + self.assertEqual( + b'{"errorMessage": "Raising an exception", "errorType": "Exception", "stackTrace": [" File \\"/var/task/main.py\\", line 13, in exception_handler\\n raise Exception(\\"Raising an exception\\")\\n"]}', + r.content, + ) + + @parameterized.expand([("x86_64", "8006"), ("arm64", "9012"), ("", "9015")]) + def test_invoke_with_pre_runtime_api_runtime(self, arch, port): + image, rie, image_name = self.tagged_name("pre-runtime-api", arch) + + cmd = f"docker run --name {image} -d -v {self.path_to_binary}:/local-lambda-runtime-server -p {port}:8080 --entrypoint /local-lambda-runtime-server/{rie} {image_name} {DEFAULT_1P_ENTRYPOINT} main.success_handler" + + Popen(cmd.split(" ")).communicate() + + # sleep 1s to give enough time for the endpoint to be up to curl + time.sleep(SLEEP_TIME) + + r = requests.post( + f"http://localhost:{port}/2015-03-31/functions/function/invocations", json={} + ) + self.assertEqual(b'"My lambda ran succesfully"', r.content) + + @parameterized.expand([("x86_64", "8007"), ("arm64", "9014"), ("", "9016")]) + def test_function_name_is_overriden(self, arch, port): + image, rie, image_name = self.tagged_name("assert-overwritten", arch) + + cmd = f"docker run --name {image} -d --env AWS_LAMBDA_FUNCTION_NAME=MyCoolName -v {self.path_to_binary}:/local-lambda-runtime-server -p {port}:8080 --entrypoint /local-lambda-runtime-server/{rie} {image_name} {DEFAULT_1P_ENTRYPOINT} main.assert_env_var_is_overwritten" + + Popen(cmd.split(" ")).communicate() + + # sleep 1s to give enough time for the endpoint to be up to curl + time.sleep(SLEEP_TIME) + + r = requests.post( + f"http://localhost:{port}/2015-03-31/functions/function/invocations", json={} + ) + self.assertEqual(b'"My lambda ran succesfully"', r.content) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/test/integration/testdata/Dockerfile-allinone b/test/integration/testdata/Dockerfile-allinone index 36faf0b..b804e5c 100644 --- a/test/integration/testdata/Dockerfile-allinone +++ b/test/integration/testdata/Dockerfile-allinone @@ -4,4 +4,4 @@ WORKDIR /var/task COPY ./ ./ # This is to verify env vars are parsed correctly before executing the function -ENV MyEnv="4=4" \ No newline at end of file +ENV MyEnv="4=4" diff --git a/test/integration/testdata/Dockerfile-python36 b/test/integration/testdata/Dockerfile-python36 deleted file mode 100644 index 36faf0b..0000000 --- a/test/integration/testdata/Dockerfile-python36 +++ /dev/null @@ -1,7 +0,0 @@ -FROM public.ecr.aws/lambda/python:3.8 - -WORKDIR /var/task -COPY ./ ./ - -# This is to verify env vars are parsed correctly before executing the function -ENV MyEnv="4=4" \ No newline at end of file