LambdaからLambdaをさっくりデプロイするためのメモ

## tl;dr

- 管理ソースが多くなるにつれていちいちコンソールで編集やアップロードが面倒になったのでCIからデプロイで自動化したくなった
- LambdaへのデプロイをLambdaにやらせるのが手っ取り早そう
- CI(zip化してS3デプロイ) -> S3 -> イベントトリガーでLambda起動(デプロイ) -> Lambda関数の更新

## 用意するもの

- CI（よしなに）
- zip放置用のS3
- デプロイ実行用のLambda関数
- デプロイされるLambda関数

## CI（よしなに）

- 好きなものをどうぞ
- ビルド -> zip化 -> S3までデプロイする感じで

```python 

import boto3
import glob
import os
import shutil
import mimetypes
from datetime import datetime
from datetime import timedelta
import traceback
import json

BUILD_DIR = "YOUR_BUILD_DIR"

LAMBDA_PROJECT_LIST_JSON = "./YOUR_PROJECT.json"

AWS_ACCESS_KEY = "YOUR_AWS_ACCESS_KEY"
AWS_ACCESS_SECRET = "YOUR_AWS_ACCESS_SECRET"

AWS_S3_BUCKET = "YOUR_AWS_S3_BUCKET"
AWS_REGION = "YOUR_AWS_REGION"

def run():
  # YOUR_BUILD_DIRディレクトリ削除
  # YOUR_BUILD_DIRディレクトリ作成
  # zipファイル作ってYOUR_BUILD_DIRに突っ込む
  # s3デプロイ

  with open(LAMBDA_PROJECT_LIST_JSON,"r") as l:
    lambda_project_list = json.load(l)

    try:
      if (AWS_ACCESS_KEY in os.environ)==False or (AWS_ACCESS_SECRET in os.environ)==False:
        raise "undefined access_key"
    except:
      traceback.print_exception()
      return

    session = boto3.session.Session(
                aws_access_key_id=os.environ[AWS_ACCESS_KEY],
                aws_secret_access_key=os.environ[AWS_ACCESS_SECRET],
                region_name=AWS_REGION,
              )

    client = session.client('s3')

    shutil.rmtree(BUILD_DIR, ignore_errors=True)
    os.makedirs(BUILD_DIR, exist_ok=True)

    keys = lambda_project_list.keys()

    for keyname in keys:
      val = lambda_project_list[keyname]
      shutil.make_archive(BUILD_DIR+"/"+val["lambda"], 'zip', root_dir="./"+keyname)

    file_list = glob.glob(BUILD_DIR+"/*.zip")
    
    now = datetime.today()
    expires = now+timedelta(hours=1)

    print("DEPLOY:"+AWS_REGION)

    for val in file_list:
      mimetype = mimetypes.guess_type(val)[0]
      fileneme = os.path.basename(val)
      with open(val,"rb") as f:
        try:
          response = client.put_object(
            ACL='private',
            Body=f,
            Key=fileneme,
            Bucket=AWS_S3_BUCKET,
            ContentType=mimetype,
            Expires=expires,
          )
          print("UPLOAD:"+fileneme)
        except:
          traceback.print_exception()
          break


if __name__ == "__main__":
  run()
```
ビルドははしょり（懺悔）subprocess使えばいいんじゃなかろうか（適当）


## デプロイ実行用のLambda関数

IAMはよしなに付与しましょう

```python

import sys
import boto3
import os

AWS_REGION = "YOUR_AWS_REGION"

def lambda_handler(event, context):

    # s3に上がったzipを取得
    # lambdaへデプロイ
    if len(event["Records"])==0:
      return

    s3_data = event["Records"][0]["s3"]

    _,ext = os.path.splitext(s3_data["object"]["key"])

    # zip以外にアップロードされたものは除外
    if ext != ".zip":
      return

    function_name = s3_data["object"]["key"].split(".")[0]
 
    session = boto3.session.Session(
                region_name=AWS_REGION,
              )

    client = session.client("lambda")

    response = client.update_function_code(
      FunctionName=function_name,
      S3Bucket=s3_data["bucket"]["name"],
      S3Key=s3_data["object"]["key"],
      DryRun=False,
    )

    print(response,file=sys.stderr)
```

`DryRun=False` は必須。  
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/lambda.html#Lambda.Client.update_function_code  
> DryRun (boolean) -- Set to true to validate the request parameters and access permissions without modifying the function code.

## zip放置用のS3

- よしなに作成。
- リージョンは `デプロイ実行用のLambda関数` と合わせた方が楽。
- バケットの `プロパティ > Events` から PUTで `デプロイ実行用のLambda関数` が起動するように設定

## デプロイされるLambda関数

- よしなに指定。
- リージョンは合わせる
- `LAMBDA_PROJECT_LIST_JSON` に放り込んでいく

## 課題

- 雑に作ったのでビルドやってないので後日作る（といいなあ）

## 参考

https://dev.classmethod.jp/cloud/aws/2018-solo-boto3-advent-calendar-day14/
https://qiita.com/asahi0301/items/a88760dd38fe0f5fcee1