erllambda SAM example

This is a sample AWS Lambda function built using Erlang for demonstration purposes: how to build and deploy AWS Lambda with a provided runtime using AWS SAM and erllambda library.

Packaging and deployment

This project is built using rebar3_erllambda plugin. Please see getting started section on how to bootstrap development environment.

Create a package

Create a package as it's shown in create package section.

If environment is configured correctly it should be as easy as:

git clone https://github.com/alertlogic/erllambda_sam_example.git
cd erllambda_sam_example
rebar3 get-deps
rebar3 compile
rebar3 release
rebar3 erllambda zip

Note: you might want to run compile, release and erllambda zip commands from erllambda docker container as it's described in rebar3_erllambda readme since some artifacts are sensitive to a target platform.

Run lambda locally

Before function is deployed in to AWS there's an option to start function locally and verify that it works as expected without creating all necessary resources in AWS. This possibility provided by AWS sam tool.

Local package

The only difference in a local package and a package that is created for production deployment is a config file, that is shipped with erlang release. For local stack package will be packaged with local-sys.config, which has a section for erlcloud library that allows to modify AWS config variables to route calls to AWS services to a specified host.

In this example dynamodb will be started on a local machine in a docker container. In order to be able to talk to dynamodb from lambda container, erlcloud library should be configured to point to a docker host address, which can route all requests to a process listening on a provided port.

{erlcloud,
  [{aws_config,
    [{ddb_scheme, "http://"},
     {ddb_host, "host.docker.internal"},
     {ddb_port, 8000}]}
  ]}

Build a package for local stack

Build local package as you would do it for AWS stack but specify local profile for rebar3 commands:

rebar3 as local compile
rebar3 as local release
rebar3 as local erllambda release

Start dependencies

This example requires running dynamodb to function properly. For the local stack we are going to use official dynamodb docker image.

docker pull amazon/dynamodb-local

To be able to talk to dynamodb instance from multiple hosts, container should be started with a shared DB:

docker run --rm -v `pwd`/_data:/var/dynamodb_data -d -p 8000:8000 --name dynamodb \
    amazon/dynamodb-local -jar DynamoDBLocal.jar -sharedDb -dbPath /var/dynamodb_data

Create dynamodb table for the lambda function. Table will be created automatically by AWS cloudformation template, but for local stack this is a manual process:

aws dynamodb create-table                                    \
    --endpoint http://localhost:8000                         \
    --table-name test-table                                  \
    --attribute-definitions AttributeName=id,AttributeType=S \
    --key-schema AttributeName=id,KeyType=HASH               \
    --provisioned-throughput ReadCapacityUnits=10,WriteCapacityUnits=10

Start function locally

Using sam local start local AWS Lambda runtime with a configured in the template API Gateway:

TABLE_NAME=test-table sam local start-api \
    -t ./etc/local-template.yaml --region us-east-1

Call local API Gateway

Once sam local has been initialised and started your function will be accessible on port 3000:

$ curl http://localhost:3000/resource -XPOST -d '{"id": "bar", "field": "bar-field"}'
[]
$ curl http://localhost:3000/resource
[{"field":"bar-field","id":"bar"}]

Deploy

  1. We need a S3 bucket where we can upload our Lambda functions packaged as ZIP before we deploy anything - If you don't have a S3 bucket to store code artifacts then this is a good time to create one:

    aws s3 mb s3://BUCKET_NAME
  2. Package lambda function to S3:

    sam package \
        --template-file etc/template.yaml    \
        --output-template-file packaged.yaml \
        --s3-bucket REPLACE_THIS_WITH_YOUR_S3_BUCKET_NAME
  3. create a Cloudformation Stack and deploy your SAM resources.

    sam deploy \
        --template-file packaged.yaml      \
        --stack-name erllambda-sam-example \
        --capabilities CAPABILITY_IAM

Call deployed function through API Gateway

To get a deployed API Gateway Endpoint URL run the following command:

aws cloudformation describe-stacks     \
    --stack-name erllambda-sam-example \
    --query 'Stacks[].Outputs'

This should return URL similar to https://ea5728392s9.execute-api.us-east-1.amazonaws.com/Prod/MyResource/resource

Create an item

$ curl https://ea5728392s9.execute-api.us-east-1.amazonaws.com/Prod/resource \
    -XPOST -d '{"id": "foo", "field": "foo"}'
[]
$ curl https://ea5728392s9.execute-api.us-east-1.amazonaws.com/Prod/resource \
    -XPOST -d '{"id": "bar", "field": "bar"}'
[]

Get an item by id

$ curl https://ea5728392s9.execute-api.us-east-1.amazonaws.com/Prod/resource/foo
{"id":"foo","field":"foo"}
$ curl https://ea5728392s9.execute-api.us-east-1.amazonaws.com/Prod/resource/bar
{"id":"bar","field":"bar"}

Get all items

$ curl https://ea5728392s9.execute-api.us-east-1.amazonaws.com/Prod/resource
[{"id":"foo","field":"foo"},{"id":"bar","field":"bar"}]

List items matching a given filter

$ curl 'https://ea5728392s9.execute-api.us-east-1.amazonaws.com/Prod/resource?field=bar'
[{"id":"bar","field":"bar"}]

Update item with a provided id

$ curl https://ea5728392s9.execute-api.us-east-1.amazonaws.com/Prod/resource/bar \
    -XPUT -d '{"field": "new-bar"}'
[]

Delete item with a given id

$ curl 'https://ea5728392s9.execute-api.us-east-1.amazonaws.com/Prod/resource/foo \
    -XDELETE
[]
$ curl https://ea5728392s9.execute-api.us-east-1.amazonaws.com/Prod/resource
[{"id":"bar","field":"new-bar"}]

Delete stack

When you do not need stack anymore you can delete it using the following command:

aws cloudformation delete-stack --stack-name erllambda-sam-example

How to contribute

Contributions to this repo are always welcome. If you have an idea for improving the this or related components, please submit a github issue or simply submit a PR directly that implements your improvement.

For complex changes, or the introduction of a major feature, it is beneficial to discuss ideas before implementing them, so that your efforts can focus on pull requests that will be accepted more easily.

As you prepare your pull request, make sure that you follow the coding conventions that exist in the files, and always make sure that all unit and common tests run. Please ensure that your contribution always adds to the coverage percentage, and does not decrease it.

How to report defects

If you encounter an problem, or simply have a question about using this repo, please submit a github issue.