AWS SAM CLIでLambda Functionを開発する

#準備
SAM CLIはpythonで実装されているため、先にpythonの環境を設定。
Macのデフォルトはpython 2.7なので、pyenvなどで3.xにしておく。

実行時はDockerを使うので、Docker for macOSをインストールしておく。
さらに、AWS CKUもインストールしておく。

#インストール
pythonで書かれているのでpipでもインストールできるが、OSXならbrewでインストールした方が良さそう。

[Installing the AWS SAM CLI on macOS](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install-mac.html)

```
$ brew update
$ brew upgrade
$ brew tap aws/tap
$ brew install aws-sam-cli
```
でインストールできる。正常にインストールできたら、

```
$ sam --version
SAM CLI, version 0.10.0
```
と表示される。

#プロジェクトの作成
SAMを使ってpython3.7でのプロジェクト(Lambda Function)の雛形を生成します。

```
$ sam init --runtime python3.7
```
以下のファイルが生成されます。

```
sam-app
├── README.md
├── hello_world
│   ├── __init__.py
│   ├── __pycache__
│   ├── app.py
│   └── requirements.txt
├── template.yaml
└── tests
    └── unit
        ├── __init__.py
        ├── __pycache__
        └── test_handler.py
```
#Unit Test
ユニットテストも生成されているので実行してみる。

```
$ cd sam-app
$ python -m purest tests/ -v
============================== test session starts ==============================
platform darwin -- Python 3.7.2, pytest-4.2.0, py-1.7.0, pluggy-0.8.1 -- /Users/tetsuo/.pyenv/versions/3.7.2/bin/python
cachedir: .pytest_cache
rootdir: /Users/tetsuo/devel/src/github/FirstFourNotes/contract-dev/ISO-L-Team/ISO-Seminar-backend/functions/sam-app, inifile:
plugins: mock-1.10.0
collected 1 item

tests/unit/test_handler.py::test_lambda_handler PASSED  [100%]

============================== 1 passed in 0.10 seconds ==============================
```
生成したままなのでパスします。

# Build
AWS Lambdaはリソース等をフラットにフォルダーに置かなければならないので、まず、ビルドします。

```
$ sam build
2019-02-03 16:30:59 Found credentials in shared credentials file: ~/.aws/credentials
2019-02-03 16:30:59 Building resource 'HelloWorldFunction'
2019-02-03 16:30:59 Running PythonPipBuilder:ResolveDependencies
2019-02-03 16:31:00 Running PythonPipBuilder:CopySource

Build Succeeded

Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml

Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Package: sam package --s3-bucket <yourbucket>
```
#ローカルでの実行

```
$ sam local start-api
2019-02-03 18:00:04 Found credentials in shared credentials file: ~/.aws/credentials
2019-02-03 18:00:05 Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
2019-02-03 18:00:05 You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template
2019-02-03 18:00:05  * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
```
でサーバーが立ち上がる。ブラウザで```http://127.0.0.1:3000/hello```にアクセスすると、
ブラウザの画面に

```
{"message": "hello world", "location": "39.111.112.54"}
```
と表示され、コマンドラインにも以下が表示される。

```
2019-02-03 18:00:18 Invoking app.lambda_handler (python3.7)

Fetching lambci/lambda:python3.7 Docker container image......
2019-02-03 18:00:20 Mounting /Users/tetsuo/devel/src/github/FirstFourNotes/contract-dev/ISO-L-Team/ISO-Seminar-backend/functions/sam-app/.aws-sam/build/HelloWorldFunction as /var/task:ro inside runtime container
START RequestId: 52fdfc07-2182-154f-163f-5f0f9a621d72 Version: $LATEST
END RequestId: 52fdfc07-2182-154f-163f-5f0f9a621d72
REPORT RequestId: 52fdfc07-2182-154f-163f-5f0f9a621d72  Duration: 364.27 ms     Billed Duration: 400 ms Memory Size: 128 MB     Max Memory Used: 28 MB
2019-02-03 18:00:22 No Content-Type given. Defaulting to 'application/json'.
2019-02-03 18:00:22 127.0.0.1 - - [03/Feb/2019 18:00:22] "GET /hello HTTP/1.1" 200 -
```
#LocalStackとの連携
LocalStackを使うとS3やAPI Gatewayのアクセスをローカルで実行することができます。

## S3を使う


# AWSにデプロイする
##準備
インポートするモジュールは、requirements.txtに書いておきます。例えば、MuSQLとS3を使う場合、"pymysql"と"boto3"をインポートするので、requirements.txtは以下のようになります。

```
pymysql
boto3
```
##ビルド
アップロードに必要なモジュールやソースコードを集めてまとめます。

```
$ sam build
```
集められたファイルは```.aws-sam/build```に置かれます。

##パッケージングとアップロード
ビルドしたファイルをパッケージにして、S3にアップロードします。アップロードする先のbucketは先に作っておきます。
ここではadmin-functionsというbucketにアップロードします。

```
# sam package --s3-bucket admin-functions
Uploading to 9e6330051e2955355ab532773cb70983  6405104 / 6405104.0  (100.00%)
AWSTemplateFormatVersion: '2010-09-09'
Description: 'import_attende

  Sample SAM Template for import_attende

  '
Globals:
  Function:
    Timeout: 30
Outputs: null
Resources:
  ImportAttendeeFunction:
    Properties:
      CodeUri: s3://admin-functions/9e6330051e2955355ab532773cb70983
      Environment:
        Variables:
          PARAM1: VALUE
      Events:
        UploadCSV:
          Properties:
            Bucket: admin-upload
            Events: s3:ObjectCreated:*
          Type: S3
      Handler: app.lambda_handler
      Runtime: python3.7
    Type: AWS::Serverless::Function
  SrcBucket:
    Type: AWS::S3::Bucket
Transform: AWS::Serverless-2016-10-31
```
アップロードしたパッケージにアクセスするには```CodeUrl```を使います。上の場合は、```s3://admin-functions/9e6330051e2955355ab532773cb70983```となります。
また、呼び出すハンドラー名は```Handler```にあります。この場合は、```app.lambda_handler```です。
これは、```template.yml```で指定できます。
# Lambda関数を実行する
AWS LambdaのFunctionsページで、上でアップロードした関数を使うFunctionを作成します。作成したら、そのFunctionのページを表示して、```Function Code```の各項目を以下のように設定します。

|項目|値|
|:--|:---|
|Code entry type:|Upload a file from Amazon S3|
|Runtime|Python3.7|
|Handler|app.lambda_handler|
|Amazon S3 link URL|s3://admin-functions/9e6330051e2955355ab532773cb70983"|

設定が終われば、右上の```Save```ボタンを押します。これでLambda FunctionがS3から読み込まれ実行できるようになります。
# Lambda関数をテストする
右上の```Test```ボタンを押すと、初めてであればテスト用にイベントが作成され、表示されます。必要に応じてこれを編集し```Create```します。もう一度```Test```ボタンを押すとLambda関数が呼び出され、先ほど作ったイベントが引数として渡されます。