Python AWS Lambda Monorepo — Part 3: Test, Build, and Deploy

  • AWS SAM CLI 0.18
Jurassic Park III (2001) | Universal Studios/Amblin

Is this how you make dinosaurs?

Testing Locally

Serverless Application Model

With all our functions ready, we can test our functions locally to see if our program is working correctly. AWS SAM uses template files to define the AWS resources of the application. These templates can be used with CloudFormation to deploy the application. They can also be used to spin up a local Lambda environment to test our code. This will be our main use of SAM.

.
├── /packages
├── /services
├── Makefile
└── template.yml
AWSTemplateFormatVersion : '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Python Lambda Monorepo - Example
Resources:
createdinosaur:
FunctionName: create_dinosaur
Description: Create Dinosaur Lambda
Type: AWS::Serverless::Function
Properties:
Runtime: python3.7
Handler: main.handler
CodeUri: services/create_dinosaur
Timeout: 20
Environment:
Variables:
PYTHONPATH: ./packages
Events:
Api:
Type: Api
Properties:
Path: /fight_dinosaurs
Method: any
  • Handler specifies the code entry point for the Lambda function. The format is file.function where function is the entry function where our code starts executing and file is the script file containing our main function. In our case,main is the main.py file and handler is the entry function.
  • CodeUri specifies the directory path of the function relative to the template file.
  • Timeout specifies the max time the Lambda can run for before timing out.
  • Environment.Variables allow us to define variables to our Python environment. In our case, we add the ./packages path to thePYTHONPATH variable since this will be the installation location of our packages.
  • Events.Api.Type defines the event source that is attached to the function to execute it. In our case we are attaching an API Gateway endpoint to be used for testing
  • Events.Api.Properties.Path defines the path endpoint for our function
  • Events.Api.Properties.Method states what HTTP method to use
sam local invoke createdinosaur --no-event
.
├── /packages
├── /services
├── /requests
| └─ tyrannosaurus.json
├── Makefile
└── template.yml
// tyrannosaurus.json
{
"dinosaur":
{
"name": "Tyrannosaurus rex",
"diet": "carnivore",
"period": "Cretaceous",
"weight": 28700,
"armor": false,
"hybrid": false
}
}
sam local invoke createdinosaur -e requests/tyrannosaurus.json

Build and Deploy

We have our code, we have our packages. It’s time to start pushing all these files to our AWS environment. To do so we’ll use the CircleCi setup we did in Part 1. To define the CircleCi jobs we want to run we need to add our CircleCI config script to our project.

.
├── /.circleci
| └─ config.yml
├── /packages
├── /services
├── /requests
├── Makefile
└── template.yml
version: 2.1
orbs:
aws-cli: circleci/aws-cli@0.1.4
jobs:
build:
docker:
- image: circleci/python:3.7.0
working_directory: ~/tmp
steps:
- checkout
- aws-cli/install
- run:
name: Configure ENV files based on environment
command: |
echo "Setting AWS environment"
aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID
aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY
aws configure set region us-east-1
...
...
- run:
name: Zip and deploy Lambda Functions
command: |
ROOT=$PWD
# iterate through lambdas
for LAMBDA_PATH in services/*/
do
FUNCTION_NAME=$(basename $LAMBDA_PATH)
if [[ -f services/$FUNCTION_NAME/requirements.txt ]]; then
echo "Installing packages..."
pip install -r services/$FUNCTION_NAME/requirements.txt --target services/$FUNCTION_NAME/packages/ --find-links ./packages
fi
cd services/$FUNCTION_NAME
echo "Building $FUNCTION_NAME..."
zip main.zip *.py */ -r
echo "Deploying $FUNCTION_NAME..."
aws lambda update-function-code --function-name $FUNCTION_NAME --region us-east-1 --zip-file fileb://main.zip
cd $ROOT
done
workflows:
version: 2
build:
jobs:
- build:
filters:
branches:
only:
- master
Old CircleCI UI
New CircleCI UI
Old and New CircleCI UI

Run it!

Inside our Lambda function we see all our files are uploaded. They are now ready to run requests on AWS. Success!

Conclusion

This marks the end of our 3 part series. This was the overall structure of our monorepo:

.
├── /.circleci
| └─ config.yml
├── /packages
| └── /package1
| ├── /package1
| | ├── __init__.py
| | └── core.py
| └── setup.py
| ...
├── /services
| ├─ /function1
| | ├─ main.py
| | └─ requirements.txt
| ...
├── /requests
├── Makefile
└── template.yml

Co-founder and CTO of Hecdemi / Hyperion. Computer Engineer and entrepreneur from Puerto Rico 🇵🇷 Interested in combining tech, business, and product design.

Get the Medium app