1
1
import json
2
2
import os
3
3
import re
4
+ import subprocess
4
5
import time
5
- import uuid
6
6
import zipfile
7
7
8
8
import boto3
@@ -126,9 +126,9 @@ def create_iam_role(role_name):
126
126
def deploy_lambda (
127
127
function_name ,
128
128
region ,
129
- zip_file_path ,
130
- handler_name ,
129
+ image_uri ,
131
130
role_arn ,
131
+ memory_size ,
132
132
runtime = "python3.10" ,
133
133
env_vars = None ,
134
134
):
@@ -138,32 +138,29 @@ def deploy_lambda(
138
138
Args:
139
139
function_name: str, the name of the Lambda function to
140
140
create or update.
141
- zip_file_path: str, the path to the zip file containing
142
- the Lambda function code.
143
141
region: str, the AWS region where the Lambda
144
142
function will be deployed.
145
- handler_name: str, the name of the handler function
146
- in the code (e.g., "main.lambda_handler").
143
+ image_uri: str, the URI of the Docker image in ECR.
147
144
role_arn: str, the ARN of the IAM role that Lambda will assume.
148
145
runtime: str, the runtime environment for the
149
146
Lambda function (default is "python3.10").
150
147
env_vars: dict, optional environment variables
151
148
to set for the Lambda function.
149
+ memory_size: int, the amount of memory allocated
150
+ to the Lambda function.
151
+
152
152
Returns:
153
153
None
154
154
"""
155
155
lambda_client = boto3 .client ('lambda' , region_name = region )
156
156
157
- with open (zip_file_path , 'rb' ) as f :
158
- zipped_code = f .read ()
159
-
160
157
try :
161
158
lambda_client .get_function (FunctionName = function_name )
162
159
click .echo (f"Function { function_name } already exists. Updating..." )
163
160
164
161
lambda_client .update_function_code (
165
162
FunctionName = function_name ,
166
- ZipFile = zipped_code
163
+ ImageUri = image_uri
167
164
)
168
165
wait_for_lambda_update (lambda_client , function_name , timeout = 120 )
169
166
lambda_client .update_function_configuration (
@@ -174,15 +171,18 @@ def deploy_lambda(
174
171
click .echo (f"Creating new function: { function_name } " )
175
172
176
173
try :
174
+ click .echo (
175
+ "Creating new container-based "
176
+ f"Lambda function: { function_name } "
177
+ )
177
178
lambda_client .create_function (
178
179
FunctionName = function_name ,
179
- Runtime = runtime ,
180
180
Role = role_arn ,
181
- Handler = handler_name ,
182
- Code = {'ZipFile' : zipped_code },
181
+ PackageType = "Image" ,
182
+ Code = {"ImageUri" : image_uri },
183
183
Timeout = 900 ,
184
- MemorySize = 256 ,
185
- Environment = {' Variables' : env_vars or {}}
184
+ MemorySize = memory_size ,
185
+ Environment = {" Variables" : env_vars or {}}
186
186
)
187
187
except Exception as e :
188
188
raise click .ClickException (
@@ -213,6 +213,82 @@ def s3_bucket_exists(bucket_name, region):
213
213
raise
214
214
215
215
216
+ def create_ecr_repository (repository_name , region ):
217
+ """
218
+ Function to create an ECR repository for storing Docker images.
219
+ It checks if the repository already exists and creates it if not.
220
+
221
+ Args:
222
+ repository_name: str, the name of the ECR repository to create.
223
+ region: str, the AWS region where the repository will be created.
224
+
225
+ Returns:
226
+ None
227
+ """
228
+
229
+ ecr = boto3 .client ('ecr' , region_name = region )
230
+ try :
231
+ response = ecr .create_repository (repositoryName = repository_name )
232
+ click .echo (
233
+ "Created ECR repository: "
234
+ f"{ response ['repository' ]['repositoryUri' ]} "
235
+ )
236
+ except ecr .exceptions .RepositoryAlreadyExistsException :
237
+ click .echo (f"ECR repository { repository_name } already exists." )
238
+
239
+
240
+ def build_and_push_docker_image (
241
+ repository_name , region , dockerfile_path = 'Dockerfile' , tag = 'latest'
242
+ ):
243
+ """
244
+ Function to build a Docker image and push it to an ECR repository.
245
+
246
+ Args:
247
+ repository_name: str, the name of the ECR repository.
248
+ region: str, the AWS region where the repository is located.
249
+ dockerfile_path: str, path to the Dockerfile (default is 'Dockerfile').
250
+ tag: str, the tag for the Docker image (default is 'latest').
251
+
252
+ Returns:
253
+ None
254
+ """
255
+
256
+ # Retrieve the ECR repository URI
257
+ ecr = boto3 .client ('ecr' , region_name = region )
258
+ try :
259
+ response = ecr .describe_repositories (repositoryNames = [repository_name ])
260
+ repository_uri = response ['repositories' ][0 ]['repositoryUri' ]
261
+ except ecr .exceptions .RepositoryNotFoundException :
262
+ raise click .ClickException (
263
+ f"ECR repository { repository_name } does "
264
+ f"not exist in region { region } ."
265
+ )
266
+
267
+ # Authenticate Docker to the ECR registry
268
+ auth = ecr .get_authorization_token ()
269
+ proxy = auth ['authorizationData' ][0 ]['proxyEndpoint' ]
270
+
271
+ click .echo (f"Authenticating Docker to ECR repository { repository_name } ..." )
272
+ subprocess .run (
273
+ f"aws ecr get-login-password --region { region } | "
274
+ f"docker login --username AWS --password-stdin { proxy } " ,
275
+ shell = True , check = True
276
+ )
277
+
278
+ click .echo (f"Building Docker image { repository_name } :{ tag } ..." )
279
+ # Build and push Docker image with the docker file path
280
+ image_full_uri = f"{ repository_uri } :{ tag } "
281
+ subprocess .run ([
282
+ "docker" , "build" ,
283
+ "--platform=linux/amd64" ,
284
+ "-t" , image_full_uri ,
285
+ "-f" , dockerfile_path ,
286
+ "."
287
+ ], check = True )
288
+ subprocess .run (f"docker push { image_full_uri } " , shell = True , check = True )
289
+ return image_full_uri
290
+
291
+
216
292
def create_s3_bucket (bucket_name , region ):
217
293
"""
218
294
Function to create an S3 bucket for storing Lambda function code.
@@ -273,7 +349,8 @@ def check_lambda_permissions(required_actions=None):
273
349
"lambda:GetFunction" ,
274
350
"lambda:UpdateFunctionCode" ,
275
351
"lambda:UpdateFunctionConfiguration" ,
276
- "lambda:CreateFunction"
352
+ "lambda:CreateFunction" ,
353
+ "ecr:CreateRepository"
277
354
]
278
355
279
356
sts = boto3 .client ("sts" )
@@ -356,9 +433,8 @@ def wait_for_lambda_update(lambda_client, function_name, timeout=60):
356
433
def command (
357
434
lambda_function_name ,
358
435
region ,
359
- lambda_handler ,
360
436
project_dir = None ,
361
- ignore_dirs = None
437
+ memory_size = 3000
362
438
):
363
439
"""
364
440
Command-line tool for deploying a trading bot to AWS Lambda.
@@ -368,9 +444,8 @@ def command(
368
444
region: str, the AWS region where the Lambda function will be deployed.
369
445
project_dir: str, the directory containing the Lambda function code.
370
446
If None, it defaults to the current directory.
371
- lambda_handler: str, the name of the handler function in the code
372
- (default is "aws_function.lambda_handler").
373
- ignore_dirs: list, directories to ignore when zipping the code.
447
+ memory_size: int, the amount of memory allocated
448
+ to the Lambda function
374
449
375
450
Returns:
376
451
None
@@ -381,12 +456,11 @@ def command(
381
456
382
457
check_lambda_permissions ()
383
458
384
- click .echo (f"Deploying to AWS Lambda "
385
- f"function: { lambda_function_name } in region: { region } " )
459
+ click .echo (
460
+ "Deploying to AWS Lambda "
461
+ f"function: { lambda_function_name } in region: { region } "
462
+ )
386
463
click .echo (f"Project directory: { project_dir } " )
387
- zip_file_path = f"/tmp/deploy-{ uuid .uuid4 ().hex } .zip"
388
- zip_code (project_dir , zip_file_path )
389
- click .echo (f"Zipped code to { zip_file_path } " )
390
464
391
465
# Create s3 bucket for state handler
392
466
bucket_name = f"{ lambda_function_name } -state-handler-{ region } "
@@ -405,13 +479,21 @@ def command(
405
479
click .echo ("Adding S3 bucket name to environment variables" )
406
480
env_vars [AWS_S3_STATE_BUCKET_NAME ] = bucket_name
407
481
482
+ click .echo ("Building and pushing Docker image to ECR" )
483
+ create_ecr_repository (lambda_function_name , region )
484
+ image_uri = build_and_push_docker_image (
485
+ lambda_function_name ,
486
+ region ,
487
+ dockerfile_path = os .path .join (project_dir , "Dockerfile" ),
488
+ tag = "latest"
489
+ )
408
490
click .echo ("Creating IAM role for Lambda execution" )
409
491
role_arn = create_iam_role ("lambda-execution-role" )
410
492
deploy_lambda (
411
493
lambda_function_name ,
412
- zip_file_path = zip_file_path ,
413
- handler_name = lambda_handler ,
494
+ image_uri = image_uri ,
414
495
role_arn = role_arn ,
415
496
env_vars = env_vars ,
416
- region = region
497
+ region = region ,
498
+ memory_size = memory_size
417
499
)
0 commit comments