Skip to content

Commit acb83fe

Browse files
committed
Add AWS SAM integration
Why these changes are being introduced: AWS SAM is a way to run the Lambda as a container in a way that simulates much of the deployed context: HTTP requests, memory resources, etc. How this addresses that need: * Adds new 'sam' module in tests * Adds documentation and Makefile commands to support testing the lambda function with AWS SAM Side effects of this change: * None Relevant ticket(s): * None
1 parent fef161f commit acb83fe

File tree

5 files changed

+109
-24
lines changed

5 files changed

+109
-24
lines changed

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,8 @@ dmypy.json
136136
.vscode/
137137

138138
# jetbrains
139-
.idea/
139+
.idea/
140+
141+
# SAM
142+
.aws-sam/
143+
tests/sam/env.json

Makefile

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,18 @@ black-apply: # Apply changes with 'black'
6464
uv run black .
6565

6666
ruff-apply: # Resolve 'fixable errors' with 'ruff'
67-
uv run ruff check --fix .
67+
uv run ruff check --fix .
68+
69+
####################################
70+
# SAM Lambda
71+
####################################
72+
sam-build: # Build SAM image for running Lambda locally
73+
sam build --template tests/sam/template.yaml
74+
75+
sam-http-run: # Run lambda locally as an HTTP server
76+
sam local start-api --template tests/sam/template.yaml --env-vars tests/sam/env.json
77+
78+
sam-http-ping: # Send curl command to SAM HTTP server
79+
curl --location 'http://localhost:3000/myapp' \
80+
--header 'Content-Type: application/json' \
81+
--data '{"msg":"in a bottle"}'

README.md

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,42 +32,78 @@ Description of the function/functions.
3232
- To run unit tests: `make test`
3333
- To lint the repo: `make lint`
3434

35-
## Running Locally with Docker
35+
## Testing Locally with AWS SAM
3636

37-
<https://docs.aws.amazon.com/lambda/latest/dg/images-test.html>
37+
### SAM Installation
3838

39-
- Build the container:
39+
Ensure that AWS SAM CLI is installed: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html.
4040

41-
```bash
42-
docker build -t my_function:latest .
43-
```
41+
All following actions and commands should be performed from the root of the project (i.e. same directory as the `Dockerfile`).
4442

45-
- Run the default handler for the container:
43+
### Building and Configuration
4644

47-
```bash
48-
docker run -e WORKSPACE=dev -p 9000:8080 my_function:latest
49-
```
45+
1- Create a JSON file for SAM that has environment variables for the container
5046

51-
- Post to the container:
47+
- copy `tests/sam/env.json.template` to `tests/sam/env.json` (which is git ignored)
48+
- fill in missing sensitive env vars
5249

53-
```bash
54-
curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
55-
```
50+
**NOTE:** AWS credentials are automatically passed from the terminal context that runs `make sam-run`; they do not need to be explicitly set as env vars.
5651

57-
- Observe output:
52+
2- Build Docker image:
53+
```shell
54+
make sam-build
55+
```
56+
57+
### Invoking Lambda via HTTP requests
5858

59-
```
60-
"You have successfully called this lambda!"
61-
```
59+
The following outlines how to run the Lambda SAM docker image as an HTTP endpoint, accepting requests and returning respnoses similar to a lambda behind an ALB, Function URL, or API Gateway.
6260

63-
## Running a Specific Handler Locally with Docker
61+
1- Ensure any required AWS credentials set in terminal, and any other env vars in `tests/sam/env.json` up-to-date.
62+
63+
2- Run HTTP server:
64+
```shell
65+
make sam-http-run
66+
```
6467

65-
If this repo contains multiple lambda functions, you can call any handler you copy into the container (see Dockerfile) by name as part of the `docker run` command:
68+
This starts a server at `http://localhost:3000`. Requests must include a path, e.g. `/myapp`, but are arbitrary insofar as the lambda does not utilize them in the request payload.
6669

67-
```bash
68-
docker run -p 9000:8080 my_function:latest lambdas.<a-different-module>.lambda_handler
70+
3- In another terminal, perform an HTTP request via another `Makefile` command:
71+
```shell
72+
make sam-http-ping
6973
```
7074

75+
Response should have an HTTP status of `200` and respond with:
76+
```json
77+
{
78+
"response": "pong"
79+
}
80+
```
81+
82+
### Invoking Lambda directly
83+
84+
While Lambdas can be invoked via HTTP methods (ALB, Function URL, etc.), they are also often invoked directly with an `event` payload. To do so with SAM, you do **not** need to first start an HTTP server with `make sam-run`, you can invoke the function image directly:
85+
86+
```shell
87+
echo '{"action": "ping","challenge_secret":"totally-local-archival-packaging"}' | sam local invoke -e -
88+
```
89+
90+
Response:
91+
```text
92+
{"statusCode": 200, "statusDescription": "200 OK", "headers": {"Content-Type": "application/json"}, "isBase64Encoded": false, "body": "{\"response\": \"pong\"}"}
93+
```
94+
95+
As you can see from this response, the lambda is still returning a dictionary that _would_ work for an HTTP response, but is actually just a dictionary with the required information.
96+
97+
It's unknown at this time if APT will get invoked via non-HTTP methods, but SAM will be helpful for testing and development if so.
98+
99+
### Troubleshoot
100+
101+
#### Encounter error `botocore.exceptions.TokenRetrievalError`
102+
103+
When running a Lambda via SAM, it attempts to parse and setup AWS credentials just like a real Lambda would establish them. Depending on how you setup AWS credentials on your host machine, if they are stale or invalid, you may encounter this error when making your first requests of the Lambda.
104+
105+
**Solution:** Stop the SAM container, refresh AWS credentials, and restart it.
106+
71107
## Environment Variables
72108

73109
### Required

tests/sam/env.json.template

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"MyApp": {}
3+
}

tests/sam/template.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
AWSTemplateFormatVersion: '2010-09-09'
2+
Transform: AWS::Serverless-2016-10-31
3+
Resources:
4+
MyApp:
5+
Type: AWS::Serverless::Function
6+
Properties:
7+
PackageType: Image
8+
Timeout: 900
9+
MemorySize: 1024
10+
Events:
11+
BagitApi:
12+
Type: HttpApi
13+
Properties:
14+
Path: /{proxy+}
15+
Method: ANY
16+
ImageUri: myapp:latest
17+
Environment:
18+
# While tests/sam/env.json is required for sensitive env vars, ALL env vars
19+
# used in the lambda must exist here as well, even just as "..." placeholders.
20+
Variables:
21+
WORKSPACE: "dev"
22+
WARNING_ONLY_LOGGERS: "asyncio,botocore,urllib3,s3transfer,boto3"
23+
#SENTRY_DSN: "None"
24+
Metadata:
25+
DockerContext: ../../. # Build Lambda Dockerfile
26+
DockerTag: latest
27+
Dockerfile: Dockerfile
28+
SamResourceId: MyApp

0 commit comments

Comments
 (0)