5
5
from unittest import TestCase , main
6
6
from pathlib import Path
7
7
import time
8
-
8
+ import os
9
9
import requests
10
+ from contextlib import contextmanager
10
11
from parameterized import parameterized
11
12
12
13
SLEEP_TIME = 2
13
14
DEFAULT_1P_ENTRYPOINT = "/lambda-entrypoint.sh"
14
15
ARCHS = ["x86_64" , "arm64" , "" ]
15
16
16
17
18
+
17
19
class TestEndToEnd (TestCase ):
20
+ ARCH = os .environ .get ('TEST_ARCH' , "" )
21
+ PORT = os .environ .get ('TEST_PORT' , 8002 )
18
22
@classmethod
19
23
def setUpClass (cls ):
20
24
testdata_path = Path (__file__ ).resolve ().parents [1 ].joinpath ("testdata" )
@@ -23,48 +27,30 @@ def setUpClass(cls):
23
27
cls .path_to_binary = Path ().resolve ().joinpath ("bin" )
24
28
25
29
# 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 ()
30
+ image_name = cls .image_name if cls .ARCH == "" else f"{ cls .image_name } -{ cls .ARCH } "
31
+ architecture = cls .ARCH if cls .ARCH == "arm64" else "amd64"
32
+ docker_arch = cls .ARCH if cls .ARCH == "arm64" else "x86_64"
33
+
34
+ build_cmd = [
35
+ "docker" ,
36
+ "build" ,
37
+ "--platform" ,
38
+ f"linux/{ architecture } " ,
39
+ "-t" ,
40
+ image_name ,
41
+ "-f" ,
42
+ str (dockerfile_path ),
43
+ str (testdata_path ),
44
+ "--build-arg" ,
45
+ f"IMAGE_ARCH={ docker_arch } " ,
46
+ ]
47
+ print (build_cmd )
48
+ Popen (build_cmd ).communicate ()
41
49
42
50
@classmethod
43
51
def tearDownClass (cls ):
44
- images_to_delete = [
45
- "envvarcheck" ,
46
- "twoinvokes" ,
47
- "arnexists" ,
48
- "customname" ,
49
- "timeout" ,
50
- "exception" ,
51
- "remaining_time_in_three_seconds" ,
52
- "remaining_time_in_ten_seconds" ,
53
- "remaining_time_in_default_deadline" ,
54
- "pre-runtime-api" ,
55
- "assert-overwritten" ,
56
- "port_override"
57
- ]
58
-
59
- for image in images_to_delete :
60
- for arch in ARCHS :
61
- arch_tag = "" if arch == "" else f"-{ arch } "
62
- cmd = f"docker rm -f { image } { arch_tag } "
63
- Popen (cmd .split (" " )).communicate ()
64
-
65
- for arch in ARCHS :
66
- arch_tag = "" if arch == "" else f"-{ arch } "
67
- Popen (f"docker rmi { cls .image_name } { arch_tag } " .split (" " )).communicate ()
52
+ arch_tag = "" if cls .ARCH == "" else f"-{ cls .ARCH } "
53
+ Popen (f"docker rmi { cls .image_name } { arch_tag } " .split (" " )).communicate ()
68
54
69
55
def tagged_name (self , name , architecture ):
70
56
tag = self .get_tag (architecture )
@@ -84,148 +70,170 @@ def invoke_function(self, port):
84
70
f"http://localhost:{ port } /2015-03-31/functions/function/invocations" , json = {}
85
71
)
86
72
87
- def create_container_and_invoke_function (self , cmd , port ):
88
- self .run_command (cmd )
89
-
90
- # sleep 1s to give enough time for the endpoint to be up to curl
91
- self .sleep_1s ()
92
-
93
- return self .invoke_function (port )
94
-
95
- @parameterized .expand ([("x86_64" , "8000" ), ("arm64" , "9000" ), ("" , "9050" )])
96
- def test_env_var_with_equal_sign (self , arch , port ):
73
+ @contextmanager
74
+ def create_container (self , cmd , image ):
75
+ try :
76
+ platform = "x86_64" if self .ARCH == "" else self .ARCH
77
+ cmd_full = f"docker run --platform linux/{ platform } { cmd } "
78
+ self .run_command (cmd_full )
79
+
80
+ # sleep 1s to give enough time for the endpoint to be up to curl
81
+ self .sleep_1s ()
82
+ yield
83
+ except Exception as e :
84
+ print (f"An error occurred while executing cmd: { cmd_full } . error: { e } " )
85
+ raise e
86
+ finally :
87
+ self .run_command (f"docker stop { image } " )
88
+ self .run_command (f"docker rm -f { image } " )
89
+
90
+
91
+ def test_env_var_with_equal_sign (self , arch = ARCH , port = PORT ):
97
92
image , rie , image_name = self .tagged_name ("envvarcheck" , arch )
98
-
99
- 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"
93
+ cmd = f"--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"
100
94
101
- r = self .create_container_and_invoke_function (cmd , port )
95
+ with self .create_container (cmd , image ):
96
+ r = self .invoke_function (port )
102
97
103
- self .assertEqual (b'"4=4"' , r .content )
98
+ self .assertEqual (b'"4=4"' , r .content )
99
+
104
100
105
- @parameterized .expand ([("x86_64" , "8001" ), ("arm64" , "9001" ), ("" , "9051" )])
106
- def test_two_invokes (self , arch , port ):
101
+ def test_two_invokes (self , arch = ARCH , port = PORT ):
107
102
image , rie , image_name = self .tagged_name ("twoinvokes" , arch )
108
103
109
- 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"
104
+ cmd = f"--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"
110
105
111
- r = self .create_container_and_invoke_function (cmd , port )
106
+ with self .create_container (cmd , image ):
107
+ r = self .invoke_function (port )
112
108
113
- self .assertEqual (b'"My lambda ran succesfully"' , r .content )
109
+ self .assertEqual (b'"My lambda ran succesfully"' , r .content )
110
+
111
+ # Make sure we can invoke the function twice
112
+ r = self .invoke_function (port )
113
+
114
+ self .assertEqual (b'"My lambda ran succesfully"' , r .content )
114
115
115
- # Make sure we can invoke the function twice
116
- r = self .invoke_function (port )
117
-
118
- self .assertEqual (b'"My lambda ran succesfully"' , r .content )
119
116
120
- @parameterized .expand ([("x86_64" , "8002" ), ("arm64" , "9002" ), ("" , "9052" )])
121
- def test_lambda_function_arn_exists (self , arch , port ):
117
+ def test_lambda_function_arn_exists (self , arch = ARCH , port = PORT ):
122
118
image , rie , image_name = self .tagged_name ("arnexists" , arch )
123
119
124
- 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"
120
+ cmd = f"--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"
125
121
126
- r = self .create_container_and_invoke_function (cmd , port )
122
+ with self .create_container (cmd , image ):
123
+ r = self .invoke_function (port )
127
124
128
- self .assertEqual (b'"My lambda ran succesfully"' , r .content )
125
+ self .assertEqual (b'"My lambda ran succesfully"' , r .content )
129
126
130
- @ parameterized . expand ([( "x86_64" , "8003" ), ( "arm64" , "9003" ), ( "" , "9053" )])
131
- def test_lambda_function_arn_exists_with_defining_custom_name (self , arch , port ):
127
+
128
+ def test_lambda_function_arn_exists_with_defining_custom_name (self , arch = ARCH , port = PORT ):
132
129
image , rie , image_name = self .tagged_name ("customname" , arch )
133
130
134
- 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"
131
+ cmd = f"--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"
135
132
136
- r = self .create_container_and_invoke_function (cmd , port )
133
+ with self .create_container (cmd , image ):
134
+ r = self .invoke_function (port )
137
135
138
- self .assertEqual (b'"My lambda ran succesfully"' , r .content )
136
+ self .assertEqual (b'"My lambda ran succesfully"' , r .content )
137
+
139
138
140
- @parameterized .expand ([("x86_64" , "8004" ), ("arm64" , "9004" ), ("" , "9054" )])
141
- def test_timeout_invoke (self , arch , port ):
139
+ def test_timeout_invoke (self , arch = ARCH , port = PORT ):
142
140
image , rie , image_name = self .tagged_name ("timeout" , arch )
143
141
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"
142
+ cmd = f"--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
143
146
- r = self .create_container_and_invoke_function (cmd , port )
144
+ with self .create_container (cmd , image ):
145
+ r = self .invoke_function (port )
147
146
148
- self .assertEqual (b"Task timed out after 1.00 seconds" , r .content )
147
+ self .assertEqual (b"Task timed out after 1.00 seconds" , r .content )
149
148
150
- @ parameterized . expand ([( "x86_64" , "8005" ), ( "arm64" , "9005" ), ( "" , "9055" )])
151
- def test_exception_returned (self , arch , port ):
149
+
150
+ def test_exception_returned (self , arch = ARCH , port = PORT ):
152
151
image , rie , image_name = self .tagged_name ("exception" , arch )
153
152
154
- 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"
153
+ cmd = f"--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"
154
+
155
+ with self .create_container (cmd , image ):
156
+ r = self .invoke_function (port )
157
+
158
+ # ignore request_id in python3.12 lambda
159
+ result = r .json ()
160
+ self .assertEqual (result ["errorMessage" ],"Raising an exception" )
161
+ self .assertEqual (result ["errorType" ],"Exception" )
162
+ self .assertEqual (result ["stackTrace" ],[" File \" /var/task/main.py\" , line 13, in exception_handler\n raise Exception(\" Raising an exception\" )\n " ])
155
163
156
- r = self .create_container_and_invoke_function (cmd , port )
157
-
158
- self .assertEqual (
159
- 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"]}' ,
160
- r .content ,
161
- )
162
164
163
- @parameterized .expand ([("x86_64" , "8006" ), ("arm64" , "9006" ), ("" , "9056" )])
164
- def test_context_get_remaining_time_in_three_seconds (self , arch , port ):
165
+ def test_context_get_remaining_time_in_three_seconds (self , arch = ARCH , port = PORT ):
165
166
image , rie , image_name = self .tagged_name ("remaining_time_in_three_seconds" , arch )
166
167
167
- cmd = f"docker run --name { image } -d --env AWS_LAMBDA_FUNCTION_TIMEOUT=3 -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_remaining_time_handler"
168
+ cmd = f"--name { image } -d --env AWS_LAMBDA_FUNCTION_TIMEOUT=3 -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_remaining_time_handler"
168
169
169
- r = self .create_container_and_invoke_function (cmd , port )
170
+ with self .create_container (cmd , image ):
171
+ r = self .invoke_function (port )
170
172
171
- # Execution time is not decided, 1.0s ~ 3.0s is a good estimation
172
- self .assertLess (int (r .content ), 3000 )
173
- self .assertGreater (int (r .content ), 1000 )
173
+ # Execution time is not decided, 1.0s ~ 3.0s is a good estimation
174
+ self .assertLess (int (r .content ), 3000 )
175
+ self .assertGreater (int (r .content ), 1000 )
174
176
175
- @ parameterized . expand ([( "x86_64" , "8007" ), ( "arm64" , "9007" ), ( "" , "9057" )])
176
- def test_context_get_remaining_time_in_ten_seconds (self , arch , port ):
177
+
178
+ def test_context_get_remaining_time_in_ten_seconds (self , arch = ARCH , port = PORT ):
177
179
image , rie , image_name = self .tagged_name ("remaining_time_in_ten_seconds" , arch )
178
180
179
- cmd = f"docker run --name { image } -d --env AWS_LAMBDA_FUNCTION_TIMEOUT=10 -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_remaining_time_handler"
181
+ cmd = f"--name { image } -d --env AWS_LAMBDA_FUNCTION_TIMEOUT=10 -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_remaining_time_handler"
180
182
181
- r = self .create_container_and_invoke_function (cmd , port )
183
+ with self .create_container (cmd , image ):
184
+ r = self .invoke_function (port )
182
185
183
- # Execution time is not decided, 8.0s ~ 10.0s is a good estimation
184
- self .assertLess (int (r .content ), 10000 )
185
- self .assertGreater (int (r .content ), 8000 )
186
+ # Execution time is not decided, 8.0s ~ 10.0s is a good estimation
187
+ self .assertLess (int (r .content ), 10000 )
188
+ self .assertGreater (int (r .content ), 8000 )
189
+
186
190
187
- @parameterized .expand ([("x86_64" , "8008" ), ("arm64" , "9008" ), ("" , "9058" )])
188
- def test_context_get_remaining_time_in_default_deadline (self , arch , port ):
191
+ def test_context_get_remaining_time_in_default_deadline (self , arch = ARCH , port = PORT ):
189
192
image , rie , image_name = self .tagged_name ("remaining_time_in_default_deadline" , arch )
190
193
191
- 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_remaining_time_handler"
194
+ cmd = f"--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_remaining_time_handler"
195
+
196
+ with self .create_container (cmd , image ):
197
+ r = self .invoke_function (port )
192
198
193
- r = self .create_container_and_invoke_function (cmd , port )
199
+ # Executation time is not decided, 298.0s ~ 300.0s is a good estimation
200
+ self .assertLess (int (r .content ), 300000 )
201
+ self .assertGreater (int (r .content ), 298000 )
194
202
195
- # Executation time is not decided, 298.0s ~ 300.0s is a good estimation
196
- self .assertLess (int (r .content ), 300000 )
197
- self .assertGreater (int (r .content ), 298000 )
198
203
199
- @parameterized .expand ([("x86_64" , "8009" ), ("arm64" , "9009" ), ("" , "9059" )])
200
- def test_invoke_with_pre_runtime_api_runtime (self , arch , port ):
204
+ def test_invoke_with_pre_runtime_api_runtime (self , arch = ARCH , port = PORT ):
201
205
image , rie , image_name = self .tagged_name ("pre-runtime-api" , arch )
202
206
203
- 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"
207
+ cmd = f"--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"
204
208
205
- r = self .create_container_and_invoke_function (cmd , port )
209
+ with self .create_container (cmd , image ):
210
+ r = self .invoke_function (port )
206
211
207
- self .assertEqual (b'"My lambda ran succesfully"' , r .content )
212
+ self .assertEqual (b'"My lambda ran succesfully"' , r .content )
208
213
209
- @ parameterized . expand ([( "x86_64" , "8010" ), ( "arm64" , "9010" ), ( "" , "9060" )])
210
- def test_function_name_is_overriden (self , arch , port ):
214
+
215
+ def test_function_name_is_overriden (self , arch = ARCH , port = PORT ):
211
216
image , rie , image_name = self .tagged_name ("assert-overwritten" , arch )
212
217
213
- 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"
218
+ cmd = f"--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"
214
219
215
- r = self .create_container_and_invoke_function (cmd , port )
220
+ with self .create_container (cmd , image ):
221
+ r = self .invoke_function (port )
216
222
217
- self .assertEqual (b'"My lambda ran succesfully"' , r .content )
223
+ self .assertEqual (b'"My lambda ran succesfully"' , r .content )
224
+
218
225
219
- @parameterized .expand ([("x86_64" , "8011" ), ("arm64" , "9011" ), ("" , "9061" )])
220
- def test_port_override (self , arch , port ):
226
+ def test_port_override (self , arch = ARCH , port = PORT ):
221
227
image , rie , image_name = self .tagged_name ("port_override" , arch )
222
228
223
229
# Use port 8081 inside the container instead of 8080
224
- cmd = f"docker run --name { image } -d -v { self .path_to_binary } :/local-lambda-runtime-server -p { port } :8081 --entrypoint /local-lambda-runtime-server/{ rie } { image_name } { DEFAULT_1P_ENTRYPOINT } main.success_handler --runtime-interface-emulator-address 0.0.0.0:8081"
230
+ cmd = f"--name { image } -d -v { self .path_to_binary } :/local-lambda-runtime-server -p { port } :8081 --entrypoint /local-lambda-runtime-server/{ rie } { image_name } { DEFAULT_1P_ENTRYPOINT } main.success_handler --runtime-interface-emulator-address 0.0.0.0:8081"
225
231
226
- r = self .create_container_and_invoke_function (cmd , port )
232
+ with self .create_container (cmd , image ):
233
+ r = self .invoke_function (port )
227
234
228
- self .assertEqual (b'"My lambda ran succesfully"' , r .content )
235
+ self .assertEqual (b'"My lambda ran succesfully"' , r .content )
236
+
229
237
230
238
231
239
if __name__ == "__main__" :
0 commit comments