Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# LocalStack Demo: Lambda Layers
# Lambda Layers with Serverless Framework

Simple demo application illustrating Lambda layers using LocalStack, deployed via the Serverless framework.

Expand Down
6 changes: 6 additions & 0 deletions lambda-layers/terraform/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.terraform
.venv
build
.terraform.lock.hcl
output.json
terraform.*
52 changes: 52 additions & 0 deletions lambda-layers/terraform/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
export AWS_ACCESS_KEY_ID ?= test
export AWS_SECRET_ACCESS_KEY ?= test
export AWS_DEFAULT_REGION = us-east-1
SHELL := /bin/bash
PYTHON_BIN ?= $(shell which python3 || which python)

usage: ## Show this help
@fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//'

start:
localstack start -d

install: ## Install dependencies
@which localstack || pip install localstack
@which awslocal || pip install awscli-local
@which terraform || (\
echo 'Terraform was not found, installing locally' && \
wget https://releases.hashicorp.com/terraform/1.0.8/terraform_1.0.8_linux_amd64.zip && \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Outdated Terraform version and hardcoding amd64 fails on ARM machines.
https://developer.hashicorp.com/terraform/install

unzip terraform_*.zip && \
rm terraform_*.zip)
@test -e .venv || ($(PYTHON_BIN) -m venv .venv; source .venv/bin/activate; pip3 install terraform-local;)

package:
source .venv/bin/activate; pip install \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment pointing to the AWS packaging docs for Python would be helpful here: https://docs.aws.amazon.com/lambda/latest/dg/python-package.html
In particular, mention the limitation that dependencies with native dependencies require further configuration and should ideally be build using Docker.

--platform manylinux2014_x86_64 \
--target=package \
--implementation cp \
--only-binary=:all: \
--upgrade \
--requirement requirements.txt \
--target build/my-lambda-layer/python

init:
tflocal init

deploy:
tflocal apply --auto-approve

invoke:
awslocal lambda invoke \
--function-name my-lambda-function \
output.json
cat output.json

run: start install package init deploy invoke
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding some (minimal) validation step. We should at least check that the Lambda invoked successfully and did not fail for the CI to catch regressions.


clean:
rm -rf build
rm -rf .terraform
rf -f output.json

.PHONY: start install init deploy invoke clean
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: incomplete missing (e.g., missing run, start, ...)

49 changes: 49 additions & 0 deletions lambda-layers/terraform/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Lambda Layers with Terraform

Simple demo application illustrating Lambda layers using LocalStack, deployed via Terraform.

## Prerequisites

* LocalStack
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should clarify that Layers require the Pro image here

* Docker
* Terraform & `tflocal`
* Python & `pip`
* `make`

## Installing

To install the dependencies:
```
make install
```

## Running

Make sure that LocalStack is started:

```
LOCALSTACK_AUTH_TOKEN=... DEBUG=1 localstack start
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General Idea for docs: Would it make sense to use a variable such as $LOCALSTACK_AUTH_TOKEN to facilitate copy/pasting the command rather using some bash-incompatible placeholder (e.g., ... or <>)?

```

Deploy the app locally and run a Lambda test invocation:

```
make run
```

You should see a success output in the terminal:
```
{"status": "success"}
```

... and your LocalStack container should contain output similar to this:

```
2024-05-17T15:46:22.870 DEBUG --- [et.reactor-1] l.s.l.i.version_manager : [my-lambda-function-52785b61-d14d-4871-8074-d5ab5fc49bb1] REPORT RequestId: 52785b61-d14d-4871-8074-d5ab5fc49bb1 Duration: 20.65 ms Billed Duration: 21 ms Memory Size: 128 MBMax Memory Used: 128 MB
2024-05-17T15:46:22.872 DEBUG --- [et.reactor-1] l.s.lambda_.provider : Lambda invocation duration: 2230.03ms
2024-05-17T15:46:22.874 INFO --- [et.reactor-1] localstack.request.aws : AWS lambda.Invoke => 200
```

## License

This code is available under the Apache 2.0 license.
100 changes: 100 additions & 0 deletions lambda-layers/terraform/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}

provider "aws" {
region = "us-east-1" # Replace with your preferred region
}

# Lambda Layer
data "archive_file" "lambda_layer_zip" {
type = "zip"
source_dir = "${path.module}/build/my-lambda-layer" # Path to your Python dependencies directory
output_path = "${path.module}/build/lambda_layer.zip"
}

# Layer bucket
resource "aws_s3_bucket" "lambda_layer_bucket" {
bucket = "my-lambda-layer-bucket"
}

# Layer ZIP upload
resource "aws_s3_object" "lambda_layer" {
bucket = aws_s3_bucket.lambda_layer_bucket.id
key = "lambda_layer.zip"
source = data.archive_file.lambda_layer_zip.output_path
depends_on = [data.archive_file.lambda_layer_zip] # Triggered only if the zip file is created
}

# Lambda Layer from S3
resource "aws_lambda_layer_version" "dependencies" {
s3_bucket = aws_s3_bucket.lambda_layer_bucket.id
s3_key = aws_s3_object.lambda_layer.key
layer_name = "my-lambda-layer"
compatible_runtimes = ["python3.12"]
depends_on = [aws_s3_object.lambda_layer] # Triggered only if the zip file is uploaded to the bucket
}

# Lambda Function
data "archive_file" "lambda_function" {
type = "zip"
source_file = "${path.module}/src/lambda_function.py"
output_path = "${path.module}/build/lambda_function.zip"
}

resource "aws_lambda_function" "my_lambda" {
filename = data.archive_file.lambda_function.output_path
function_name = "my-lambda-function"
role = aws_iam_role.lambda_role.arn # See IAM Role below
handler = "lambda_function.handler"
runtime = "python3.12"

layers = [aws_lambda_layer_version.dependencies.arn]
}

# IAM Role for Lambda
resource "aws_iam_role" "lambda_role" {
name = "lambda_basic_execution"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow"
}
]
}
EOF
}

resource "aws_iam_role_policy" "lambda_logs_policy" {
name = "lambda_logs_policy"
role = aws_iam_role.lambda_role.id

policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*",
"Effect": "Allow"
}
]
}
EOF
}
1 change: 1 addition & 0 deletions lambda-layers/terraform/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PyYAML==6.0.1
8 changes: 8 additions & 0 deletions lambda-layers/terraform/src/lambda_function.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import yaml

def handler(event, context):
status_yaml = """
status: success
"""
status = yaml.safe_load(status_yaml)
return status