Skip to content

Commit 6bc4a28

Browse files
authoredSep 29, 2021
Support for arm64 architecture (aws#52)
1 parent 7aa1883 commit 6bc4a28

File tree

6 files changed

+277
-245
lines changed

6 files changed

+277
-245
lines changed
 

‎Makefile

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,43 @@
22
RELEASE_BUILD_LINKER_FLAGS=-s -w
33

44
BINARY_NAME=aws-lambda-rie
5-
DESTINATION=bin/${BINARY_NAME}
5+
ARCH=x86_64
6+
GO_ARCH_old := amd64
7+
GO_ARCH_x86_64 := amd64
8+
GO_ARCH_arm64 := arm64
9+
DESTINATION_old:= bin/${BINARY_NAME}
10+
DESTINATION_x86_64 := bin/${BINARY_NAME}-x86_64
11+
DESTINATION_arm64 := bin/${BINARY_NAME}-arm64
12+
13+
compile-with-docker-all:
14+
make ARCH=x86_64 compile-with-docker
15+
make ARCH=arm64 compile-with-docker
16+
make ARCH=old compile-with-docker
17+
18+
compile-lambda-linux-all:
19+
make ARCH=x86_64 compile-lambda-linux
20+
make ARCH=arm64 compile-lambda-linux
21+
make ARCH=old compile-lambda-linux
622

723
compile-with-docker:
8-
docker run --env GOPROXY=direct -v $(shell pwd):/LambdaRuntimeLocal -w /LambdaRuntimeLocal golang:1.14 make compile-lambda-linux
24+
docker run --env GOPROXY=direct -v $(shell pwd):/LambdaRuntimeLocal -w /LambdaRuntimeLocal golang:1.14 make ARCH=${ARCH} compile-lambda-linux
925

1026
compile-lambda-linux:
11-
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "${RELEASE_BUILD_LINKER_FLAGS}" -o ${DESTINATION} ./cmd/aws-lambda-rie
27+
CGO_ENABLED=0 GOOS=linux GOARCH=${GO_ARCH_${ARCH}} go build -ldflags "${RELEASE_BUILD_LINKER_FLAGS}" -o ${DESTINATION_${ARCH}} ./cmd/aws-lambda-rie
1228

1329
tests:
1430
go test ./...
1531

16-
integ-tests-and-compile: tests compile-lambda-linux
17-
python3 -m venv .venv
18-
.venv/bin/pip install --upgrade pip
19-
.venv/bin/pip install requests
20-
.venv/bin/python3 test/integration/local_lambda/end-to-end-test.py
21-
22-
integ-tests-with-docker: tests compile-with-docker
23-
python3 -m venv .venv
24-
.venv/bin/pip install --upgrade pip
25-
.venv/bin/pip install requests
26-
.venv/bin/python3 test/integration/local_lambda/end-to-end-test.py
32+
integ-tests-and-compile: tests
33+
make compile-lambda-linux-all
34+
make integ-tests
2735

36+
integ-tests-with-docker: tests
37+
make compile-with-docker-all
38+
make integ-tests
39+
2840
integ-tests:
2941
python3 -m venv .venv
3042
.venv/bin/pip install --upgrade pip
31-
.venv/bin/pip install requests
32-
.venv/bin/python3 test/integration/local_lambda/end-to-end-test.py
43+
.venv/bin/pip install requests parameterized
44+
.venv/bin/python3 test/integration/local_lambda/test_end_to_end.py

‎README.md

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ Instructions for installing AWS Lambda Runtime Interface Emulator for your platf
1818

1919
| Platform | Command to install |
2020
|---------|---------
21-
| 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` |
22-
| 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` |
23-
| 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` |
21+
| 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` |
22+
| 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` |
23+
| 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` |
24+
| 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` |
2425

2526

2627
## Getting started
@@ -53,10 +54,14 @@ The AWS base images for Lambda include the runtime interface emulator. You can a
5354
### Build RIE into your base image
5455

5556
You can build RIE into a base image. Download the RIE from GitHub to your local machine and update your Dockerfile to install RIE.
56-
57+
5758
#### To build the emulator into your image
5859

59-
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.
60+
1. Create a script and save it in your project directory. Set execution permissions for the script file.
61+
62+
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.
63+
64+
The following example shows a typical script for a Node.js function.
6065
```
6166
#!/bin/sh
6267
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
6570
exec /usr/bin/npx aws-lambda-ric
6671
fi
6772
```
68-
69-
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.
7073

71-
3. Install the emulator package and change ENTRYPOINT to run the new script by adding the following lines to your Dockerfile:
74+
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.
75+
76+
3. Install the emulator package and change `ENTRYPOINT` to run the new script by adding the following lines to your Dockerfile:
77+
78+
To use the default x86\_64 architecture
79+
```
80+
ADD aws-lambda-rie /usr/local/bin/aws-lambda-rie
81+
ENTRYPOINT [ "/entry_script.sh" ]
82+
```
83+
84+
To use the arm64 architecture:
7285
```
73-
ADD aws-lambda-rie /usr/local/bin/aws-lambda-rie
86+
ADD aws-lambda-rie-arm64 /usr/local/bin/aws-lambda-rie
7487
ENTRYPOINT [ "/entry_script.sh" ]
7588
```
7689

77-
4. Build your image locally using the docker build command.
90+
4. Build your image locally using the docker build command.
7891
```
7992
docker build -t myfunction:latest .
8093
```
8194
82-
5. Run your image locally using the docker run command.
95+
5. Run your image locally using the docker run command.
8396
```
8497
docker run -p 9000:8080 myfunction:latest
8598
```
8699
87100
### Test an image without adding RIE to the image
88101
89-
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.
102+
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.
90103
*To test an image without adding RIE to the image *
91104
92-
1. From your project directory, run the following command to download the RIE from GitHub and install it on your local machine.
105+
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.
93106
94107
```
95-
mkdir -p ~/.aws-lambda-rie && curl -Lo ~/.aws-lambda-rie/aws-lambda-rie \
96-
https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie \
97-
&& chmod +x ~/.aws-lambda-rie/aws-lambda-rie
98-
```
108+
mkdir -p ~/.aws-lambda-rie && curl -Lo ~/.aws-lambda-rie/aws-lambda-rie \
109+
https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie \
110+
&& chmod +x ~/.aws-lambda-rie/aws-lambda-rie
111+
```
99112
100-
2. Run your Lambda image function using the docker run command.
113+
To download the RIE for arm64 architecture, use the previous command with a different GitHub download url.
114+
```
115+
https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie-arm64 \
116+
```
101117
118+
2. Run your Lambda image function using the docker run command.
119+
```
120+
docker run -d -v ~/.aws-lambda-rie:/aws-lambda -p 9000:8080 myfunction:latest
121+
--entrypoint /aws-lambda/aws-lambda-rie <image entrypoint> <(optional) image command>`
102122
```
103-
docker run -d -v ~/.aws-lambda-rie:/aws-lambda -p 9000:8080 \
104-
--entrypoint /aws-lambda/aws-lambda-rie \
105-
myfunction:latest <image entrypoint> <(optional) image command>
106-
````
107123
108124
This runs the image as a container and starts up an endpoint locally at `localhost:9000/2015-03-31/functions/function/invocations`.
109125
@@ -142,7 +158,7 @@ configurations that will not be emulated by this component.
142158
* You can also use it to test extensions and agents built into the container image against the Lambda Extensions API.
143159
* This component does _not_ emulate Lambda’s orchestration, or security and authentication configurations.
144160
* The component does _not_ support X-ray and other Lambda integrations locally.
145-
* The component supports only Linux x86-64 architectures.
161+
* The component supports only Linux, for x86-64 and arm64 architectures.
146162
147163
## Security
148164

‎test/integration/local_lambda/end-to-end-test.py

Lines changed: 0 additions & 198 deletions
This file was deleted.
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
from subprocess import Popen, PIPE
5+
from unittest import TestCase, main
6+
from pathlib import Path
7+
import time
8+
9+
import requests
10+
from parameterized import parameterized
11+
12+
SLEEP_TIME = 2
13+
DEFAULT_1P_ENTRYPOINT = "/lambda-entrypoint.sh"
14+
ARCHS = ["x86_64", "arm64", ""]
15+
16+
17+
class TestEndToEnd(TestCase):
18+
@classmethod
19+
def setUpClass(cls):
20+
testdata_path = Path(__file__).resolve().parents[1].joinpath("testdata")
21+
dockerfile_path = testdata_path.joinpath("Dockerfile-allinone")
22+
cls.image_name = "aws-lambda-local:testing"
23+
cls.path_to_binary = Path().resolve().joinpath("bin")
24+
25+
# build image
26+
for arch in ARCHS:
27+
image_name = cls.image_name if arch == "" else f"{cls.image_name}-{arch}"
28+
architecture = arch if arch == "arm64" else "amd64"
29+
build_cmd = [
30+
"docker",
31+
"build",
32+
"--platform",
33+
f"linux/{architecture}",
34+
"-t",
35+
image_name,
36+
"-f",
37+
str(dockerfile_path),
38+
str(testdata_path),
39+
]
40+
Popen(build_cmd).communicate()
41+
42+
@classmethod
43+
def tearDownClass(cls):
44+
images_to_delete = [
45+
"envvarcheck",
46+
"twoinvokes",
47+
"arnexists",
48+
"customname",
49+
"timeout",
50+
"exception",
51+
"pre-runtime-api",
52+
"assert-overwritten",
53+
]
54+
55+
for image in images_to_delete:
56+
for arch in ARCHS:
57+
arch_tag = "" if arch == "" else f"-{arch}"
58+
cmd = f"docker rm -f {image}{arch_tag}"
59+
Popen(cmd.split(" ")).communicate()
60+
61+
for arch in ARCHS:
62+
arch_tag = "" if arch == "" else f"-{arch}"
63+
Popen(f"docker rmi {cls.image_name}{arch_tag}".split(" ")).communicate()
64+
65+
def tagged_name(self, name, architecture):
66+
tag = self.get_tag(architecture)
67+
return (name + tag, "aws-lambda-rie" + tag, self.image_name + tag)
68+
69+
def get_tag(self, architecture):
70+
return "" if architecture == "" else str(f"-{architecture}")
71+
72+
@parameterized.expand([("x86_64", "8000"), ("arm64", "9001"), ("", "9003")])
73+
def test_env_var_with_equal_sign(self, arch, port):
74+
image, rie, image_name = self.tagged_name("envvarcheck", arch)
75+
76+
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"
77+
Popen(cmd.split(" ")).communicate()
78+
79+
# sleep 1s to give enough time for the endpoint to be up to curl
80+
time.sleep(SLEEP_TIME)
81+
82+
r = requests.post(
83+
f"http://localhost:{port}/2015-03-31/functions/function/invocations", json={}
84+
)
85+
self.assertEqual(b'"4=4"', r.content)
86+
87+
@parameterized.expand([("x86_64", "8001"), ("arm64", "9002"), ("", "9005")])
88+
def test_two_invokes(self, arch, port):
89+
image, rie, image_name = self.tagged_name("twoinvokes", arch)
90+
91+
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"
92+
93+
Popen(cmd.split(" ")).communicate()
94+
95+
# sleep 1s to give enough time for the endpoint to be up to curl
96+
time.sleep(SLEEP_TIME)
97+
98+
r = requests.post(
99+
f"http://localhost:{port}/2015-03-31/functions/function/invocations", json={}
100+
)
101+
self.assertEqual(b'"My lambda ran succesfully"', r.content)
102+
103+
# Make sure we can invoke the function twice
104+
r = requests.post(
105+
f"http://localhost:{port}/2015-03-31/functions/function/invocations", json={}
106+
)
107+
self.assertEqual(b'"My lambda ran succesfully"', r.content)
108+
109+
@parameterized.expand([("x86_64", "8002"), ("arm64", "9004"), ("", "9007")])
110+
def test_lambda_function_arn_exists(self, arch, port):
111+
image, rie, image_name = self.tagged_name("arnexists", arch)
112+
113+
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"
114+
115+
Popen(cmd.split(" ")).communicate()
116+
117+
# sleep 1s to give enough time for the endpoint to be up to curl
118+
time.sleep(SLEEP_TIME)
119+
120+
r = requests.post(
121+
f"http://localhost:{port}/2015-03-31/functions/function/invocations", json={}
122+
)
123+
self.assertEqual(b'"My lambda ran succesfully"', r.content)
124+
125+
@parameterized.expand([("x86_64", "8003"), ("arm64", "9006"), ("", "9009")])
126+
def test_lambda_function_arn_exists_with_defining_custom_name(self, arch, port):
127+
image, rie, image_name = self.tagged_name("customname", arch)
128+
129+
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"
130+
Popen(cmd.split(" ")).communicate()
131+
132+
# sleep 1s to give enough time for the endpoint to be up to curl
133+
time.sleep(SLEEP_TIME)
134+
135+
r = requests.post(
136+
f"http://localhost:{port}/2015-03-31/functions/function/invocations", json={}
137+
)
138+
self.assertEqual(b'"My lambda ran succesfully"', r.content)
139+
140+
@parameterized.expand([("x86_64", "8004"), ("arm64", "9008"), ("", "9011")])
141+
def test_timeout_invoke(self, arch, port):
142+
image, rie, image_name = self.tagged_name("timeout", arch)
143+
144+
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"
145+
146+
Popen(cmd.split(" ")).communicate()
147+
148+
# sleep 1s to give enough time for the endpoint to be up to curl
149+
time.sleep(SLEEP_TIME)
150+
151+
r = requests.post(
152+
f"http://localhost:{port}/2015-03-31/functions/function/invocations", json={}
153+
)
154+
self.assertEqual(b"Task timed out after 1.00 seconds", r.content)
155+
156+
@parameterized.expand([("x86_64", "8005"), ("arm64", "9010"), ("", "9013")])
157+
def test_exception_returned(self, arch, port):
158+
image, rie, image_name = self.tagged_name("exception", arch)
159+
160+
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"
161+
162+
Popen(cmd.split(" ")).communicate()
163+
164+
# sleep 1s to give enough time for the endpoint to be up to curl
165+
time.sleep(SLEEP_TIME)
166+
167+
r = requests.post(
168+
f"http://localhost:{port}/2015-03-31/functions/function/invocations", json={}
169+
)
170+
self.assertEqual(
171+
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"]}',
172+
r.content,
173+
)
174+
175+
@parameterized.expand([("x86_64", "8006"), ("arm64", "9012"), ("", "9015")])
176+
def test_invoke_with_pre_runtime_api_runtime(self, arch, port):
177+
image, rie, image_name = self.tagged_name("pre-runtime-api", arch)
178+
179+
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"
180+
181+
Popen(cmd.split(" ")).communicate()
182+
183+
# sleep 1s to give enough time for the endpoint to be up to curl
184+
time.sleep(SLEEP_TIME)
185+
186+
r = requests.post(
187+
f"http://localhost:{port}/2015-03-31/functions/function/invocations", json={}
188+
)
189+
self.assertEqual(b'"My lambda ran succesfully"', r.content)
190+
191+
@parameterized.expand([("x86_64", "8007"), ("arm64", "9014"), ("", "9016")])
192+
def test_function_name_is_overriden(self, arch, port):
193+
image, rie, image_name = self.tagged_name("assert-overwritten", arch)
194+
195+
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"
196+
197+
Popen(cmd.split(" ")).communicate()
198+
199+
# sleep 1s to give enough time for the endpoint to be up to curl
200+
time.sleep(SLEEP_TIME)
201+
202+
r = requests.post(
203+
f"http://localhost:{port}/2015-03-31/functions/function/invocations", json={}
204+
)
205+
self.assertEqual(b'"My lambda ran succesfully"', r.content)
206+
207+
208+
if __name__ == "__main__":
209+
main()

‎test/integration/testdata/Dockerfile-allinone

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ WORKDIR /var/task
44
COPY ./ ./
55

66
# This is to verify env vars are parsed correctly before executing the function
7-
ENV MyEnv="4=4"
7+
ENV MyEnv="4=4"

‎test/integration/testdata/Dockerfile-python36

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)