Yoke
====

Harness your Lambdas to API Gateway quickly and easily.

```
usage: yoke [-h] [-v] [--debug] {deploy,build,decrypt,encrypt} ...

AWS Lambda + API Gateway Deployment Tool

positional arguments:
  {deploy,build,decrypt,encrypt}
    deploy              Deploy lambda and (optionally) API Gateway.
    build               Only template config files and build lambda package.

optional arguments:
  -h, --help            show this help message and exit
  -v, --version         show program's version number and exit
  --debug               Debug logging
```


# Getting Started
1. Add a [`yoke.yml`](#yokeyml) file to the root of the project.
2. Set the appropriate config values in `yoke.yml` for you Lambda, API Gateway (optional), and stages.
3. Test your configuration by running `yoke build --stage <stagename>`. Given the following example structure there will be some new files generated:

        myproject/
        |-- yoke.yml
        |-- swagger.yml (Templated Swagger file generated by Yoke {build,deploy})
        |-- swagger_template.yml
        |-- myapplication/ (Lambda path defined in yoke.yml)
        |   |-- handler.py (Lambda handler defined in yoke.yml)
        |   |-- lambda.json (Lambda function configuration generated by Yoke {build,deploy})
        |   |-- config.json (Config values for your application generated by Yoke {build,deploy})
This will let you verify that the `swagger.yml` and `config.json` are templated as desired.
4. Run `yoke deploy --stage <stagename>` to deploy your Lambda and (optionally) API Gateway.


# yoke.yml

The heart of Yoke is the `yoke.yml` file. This file tells Yoke all the necessary information it needs to template and upload your Lambda and API Gateway. The structure of the file is as follows:

* `Lambda`: Information about the Lambda function to deploy and how to build it.
  * `config`: Specific information about the Lambda function's configuration:
    * `name`: The name of the function.
    * `description`: Description of the function.
    * `handler`: The module to handle the Lambda event.
    * `timeout`: The number of seconds before timing out the function.
    * `memory`: The amount (in Megabytes) of memory to configure for the function.
    * `ignore`: A list of regex patterns of files to exclude when building and uploading the function.
    * `role`: The IAM role to assume when this function is run.
    * `runtime`: Lambda Runtime (default: `python2.7`).
    * `vpc`: Optional VPC configuration, when specified, both keys below are required (also make sure that [permissions are set up accordingly](http://docs.aws.amazon.com/lambda/latest/dg/vpc-ec-create-iam-role.html)):
      * `subnets`: List of subnets the Lambda function should run inside
      * `security_groups`: List of security groups the Lambda function should assume
  * `path`: The path to the root of your Lambda module.
  * `extraFiles`: A list of additional files or directories to include in the Lambda package when uploading. This is useful for including requirements using `pip install -t <requirements_directory> <somepackage>`.
  * `dependencies`: Optional information about dependencies of the function:
    * `build`: If set to `true`, the Python dependencies listed in the Lambda function's `requirements.txt` file will be built and packaged with the function (default: `false`).
    * `wheelhouse`: The path to the directory where the dependency packages will be stored (in wheel format, default: `../../wheelhouse`).
    * `install_dir`: The path to the directory where the dependencies will be installed (should be added to `extraFiles` above as well, otherwise they won't be included in the Lambda package, default: `./lib`).
    * `packages`: List of CentOS package names that should be installed before the dependencies are built. The following packages are installed regardless of what is provided here: `libffi-devel` and `openssl-devel`. Default: no additional packages.
* `apiGateway`: Optional - information about API Gateway configuration.
  * `name`: The name of the API Gateway.
  * `swaggerTemplate`: Path to the Swagger template file.
  * `role`: The IAM role to assume when API Gateway runs.
  * `customAuthorizer`: Optional - Custom Authorizer configuration.
    * `name`: The name of the Custom Authorizer
    * `role`: The role to assume when running the Custom Authorizer
    * `expression`: The matching expression used to validate the authorization header before it is passed to the Custom Authorizer.
    * `header`: The (case-sensitive) name of the header passed to the Custom Authorizer.
    * `ttl`: The time (in seconds) to cache authorization responses.
* `stages`: Application configuration per deployment stage.
  * `default`: The default stage - the configuration for this stage is applied to any stages not explicitly defined in this section. For example, if you have stages `default, dev, and prod`, but run `yoke deploy --stage awesome`, Yoke will use the configuration for the `default` stage.
    * `region`: What region to deploy to for this stage.
    * `keyName`: Optional - KMS key alias used to encrypt and decrypt the `secretConfig` section for this stage.
    * `keyRegion`: Optional - The region where `keyName` exists.
    * `secretConfig`: Optional - encrypted configuration for the stage - this section is decrypted and combined with `config` when running `yoke build` or `yoke deploy` and written to `config.json` in the `Lambda` path.
    * `config`: Optional - These values are combined with `secretConfig` and written to `config.json` in the `Lambda` path.

You can also template `yoke.yml` using Jinja-style templating. When you run `yoke {build,deploy}`, these template variables will be sourced from any variables you privide via `--environment/-e`. By default, `{{ stage }}` is automatically provided as it is a required argument for all operations.

For more information, see the [examples in this repo](examples/).


# Handling Secrets
So you have some secrets, it's ok, we all do - but let's not share them with everyone. Here's how you can encrypt your Lambda's configuration secrets.

### Encrypting `secretConfig`
In the `stages` section of `yoke.yml`, add a `secretConfig` section with plaintext secrets (don't worry, we won't keep it this way):

```
stages:
  default: &DEFAULT
    region: "us-east-1"
    keyName: my_kms_key
    keyRegion: us-east-1
    secretConfig:
      somesecret: "secret value"
      anothersecret: "ssshhhhhhhhh"
    config: &DEFAULT_CONFIG
      log_level: DEBUG
```

Once you have your secrets, run `yoke encrypt --stage <stagename>` to generate the encrypted secret. Replace `secretConfig` with the encrypted string:

```
stages:
  default: &DEFAULT
    region: "us-east-1"
    keyName: my_kms_key
    keyRegion: us-east-1
    secretConfig: "CiCCM/++OaG61j7Ld8RPAE32b1LZBas6Or1EVAwZxdThARLnAQEBAgB4gjP/vjmhutY+y3fETwBN9m9S2QWrOjq9RFQMGcXU4QEAAAC+MIG7BgkqhkiG9w0BBwagga0wgaoCAQAwgaQGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMJesOjJpuXDNgIfyKAgEQgHd7N0goSpc013D8CuSwaVqVWawgp9PJ6F/TkKKGFQVmL7PT+x72EknOFXNUriorHLBX/FBvopkRZLqxRTxRyW9T/Lwm1lXZjHuNF/JGNLr9+F9W8uNTREHf7ZjhfxHMmBG06oBtIWnwa0E8hPZ+XMy7TpzUu/DMXA=="
    config: &DEFAULT_CONFIG
      log_level: DEBUG
```

When you run `yoke build` or `yoke deploy`, `secretConfig` is decrypted and added to the generated `config.json`.


### Decrypting `secretConfig`

`yoke decrypt --stage <stagename>` will decrypt the `secretConfig` section for the specified stage and display it in your terminal.

### Updating secrets
1. Run `yoke decrypt --stage <stagename>` to get the plaintext secrets.
2. In `yoke.yml`, replace `secretConfig`'s value with the plaintext from `decrypt`.
3. Add values, change values, etc. to the `secretConfig` section.
4. Run `yoke encrypt --stage <stagename>` and copy the ciphertext output.
5. Replace the `secretConfig` value with the ciphertext from step 4.
