# Boilerplate for AWS Lambda functions

This boilerplate defines project structure for Lambda functions at The Orchard. Following this boilerplate's prescribed project structure will allow Systems to easily integrate your new Lambda source code with Orchard's SDLC:

* Jenkins job for running PR checks
* Jenkins job for deploying to dev and production AWS accounts

As of Apr 18, 2017, AWS now supports Python 2.7 and 3.6 for lambda.

## Developing Lambda functions

Lambda Python code must be placed in a repository of your choice, together with other Lambda functions in that repo under a common "lambda" directory. Within this directory, there will be any number of functions and optionally one special directory called "common". The contents of "common" will be treated as another dependency that is injected into deployed zip file at build time.

For example:

```text
repo_root
|- ... -lambda
|       |- common (optional)
|       |  |- <shared modules>
|       |  |- tests
|       |  |  \- <test modules>
|       |  |- requirements-dev.txt
|       |  \- requirements.txt
|       |- myfunc1
|       |  |- <impl modules>
|       |  |- tests
|       |  |  \- <test modules>
|       |  |- requirements-dev.txt
|       |  \- requirements.txt
|       |- myfunc2
|       |  \- ...
|       ...
...
```

During local development, consider using the Docker image to run unit tests in the same environment, as what will be run by CI. Let's assume that you are working with the example Lambda function provided and that your `docs` repo is cloned into `/proj/docs` on your dev environment. We will also assume that your Docker is installed and set up.

### Getting started

Prepare your Docker image \(we will call it `ci-lambda`\):

```text
$ docker build -t ci-lambda /var/www/html/docs/boilerplates/python-lambda/build/
```

### Development

It may not be possible to run your Lambda function outside of the Lambda environment. It should be possible, however, to use the build script provided by this Docker container to ensure all dependencies are satisfied and unit tests pass.

To prepare a deploy file and run tests:

```text
$ docker run -it \
> --volume /var/www/html/docs/boilerplates/python-lambda/:/reporoot \
> ci-lambda \
> /reporoot/build/build.sh dev /reporoot/example/lambda myfunc
```

### Deployment

During development, it would be possible to deploy the function within dev AWS account using the following command:

```text
$ /proj/docs/boilerplates/lambda/build/deploy.sh dev /proj/docs/boilerplates/python-lambda/example/lambda/ myfunc aws_lambda_name
```

* _dev_ – environment, matches the AWS Lambda function name prefix

  \(that is part of the name you've used during the Lambda function creation\).

* _myfunc_ – python package name.
* _aws\_lambda\_name_ the AWS lambda function name without the _prefix-_.

This assumes that your default AWS credentials configured in _~/.aws/credentials_ matche profile pointing at Orchard's dev account. You can verify your AWS configuration as follows:

```text
$ aws configure list
      Name                    Value             Type    Location
      ----                    -----             ----    --------
   profile                      dev           manual    --profile
access_key     ****************IDJA shared-credentials-file    
secret_key     ****************osO9 shared-credentials-file    
    region                <not set>             None    None
```

The following would indicate a missing default profile:

```text
$ aws configure list
      Name                    Value             Type    Location
      ----                    -----             ----    --------
   profile                      dev           manual    --profile

The config profile (dev) could not be found
```

If you have configured multiple AWS accounts in _~/.aws/_, `aws` command \(and the `deploy.sh` that uses it\) should be prefixed with the env variable that is set to the target account:

```text
$ AWS_PROFILE=orchard-dev aws configure list
$ AWS_PROFILE=orchard-dev /proj/docs/boilerplates/lambda/build/deploy.sh dev /proj/docs/boilerplates/python-lambda/example/lambda/ myfunc aws_lambda_name
```

## Continuous integration

Jenkins will be serve an interface for automatic building and deploying of Lambda functions outside a local development environment. A Jenkins job can be created with the following parameters:

* repo
* relative path to lambda dir \(including lambda dir itself\)
* Lambda function name \(must match subdirectory\)
* environment \(dev, prod\)

This job will perform the following steps:

1. clone the repo
2. install dependencies as per `requirements.txt`
3. create a zip file containing function's source code and its dependencies
4. install dependencies as per `requirements-dev.txt`
5. run pytests and flake8, halt on failure
6. deploy zip file into appropriate AWS account

In the example provided in this boilerplate, assuming this repo is checked out in `$REPO_HOME` and the image is `ci-lambda` and we're targeting environment $ENV, docker command will look like this:

```text
docker run -it --volume $REPO_HOME:/reporoot ci-lambda /etc/lambda/build.sh $ENV /reporoot/boilerplates/python-lambda/example/lambda myfunc
```

