[AWS] Lambdaをローカルで開発するためにAWS SAMに入門してみた (S3のputイベント検知編)


Pickup


- aws-sam-cliを利用した ローカルで擬似的にS3のputイベントを検知させるテスト with golang の備忘録です


  • 事前にhello world編を見ておいて頂けると環境が整います


Requirement


  • golang:1.11.4

  • aws-cli

  • aws-sam-cli

  • docker (local lambda test)


Document


■ どうやったらlocalでS3のputイベント作れる?


  • そもそもどうやってsamコマンドってどんなことできるぞや?

  • イベントとかはどうやってつくる?

の疑問を解消させていくために、samコマンドのヘルプを参照してみる。 

(※ 以下、ざっくり和訳なのであしからず)


sam


  • aws-sam-cliのalias

  • cloudformationのサーバーレス拡張版

  • オプションでやることが大分違うが、「生成 / ビルド / localでのテスト / バリデーションチェック / deploy」など一通りこの子で完結できる

→ sam自体が色々なことができる模様。

寄り道して他のものも見てみたくなるが、今回は local にのみピックアップ。

$ sam --help

Usage: sam [OPTIONS] COMMAND [ARGS]...

AWS Serverless Application Model (SAM) CLI

The AWS Serverless Application Model extends AWS CloudFormation to provide
a simplified way of defining the Amazon API Gateway APIs, AWS Lambda
functions, and Amazon DynamoDB tables needed by your serverless
application. You can find more in-depth guide about the SAM specification
here: https://github.com/awslabs/serverless-application-model.

Commands:
logs Fetch logs for a function
deploy Deploy an AWS SAM application. This is an alias for 'aws
cloudformation deploy'.
package Package an AWS SAM application. This is an alias for 'aws
cloudformation package'.
publish Publish a packaged AWS SAM template to the AWS Serverless
Application Repository.
init Initialize a serverless application with a...
build Build your Lambda function code
validate Validate an AWS SAM template.
local Run your Serverless application locally for...


sam local



  • generate-event:


    • 疑似イベントつくる




  • invoke:


    • Lambda functionの実行




  • start-api:


    • 開発するAPIのテストする時にローカルエンドポイントをセットする。

    • なお、 hot-reloadingしているためfunctionに変更を加えた場合でもRestartする必要はない




  • start-lambda:


    • ローカルエンドポイントに設定したLambda funcを使えるように起動するコマンド



→ 今回のお目当ては generate-event

$ sam local --help

Usage: sam local [OPTIONS] COMMAND [ARGS]...

Run your Serverless application locally for quick development & testing

Options:
--help Show this message and exit.

Commands:
generate-event You can use this command to generate sample...
invoke Invokes a local Lambda function once.
start-api Sets up a local endpoint you can use to test your API.
Supports hot-reloading so you don't need to restart this
service when you make changes to your function.
start-lambda Starts a local endpoint you can use to invoke your local
Lambda functions.


sam local generate-event

helpにS3イベントの作成コマンドがまとまっているのでこれを活用する。

$ sam local generate-event --help

Usage: sam local generate-event [OPTIONS] COMMAND [ARGS]...

~ (中略) ~
Generate the event that S3 sends to your Lambda function when a new object is uploaded
$ sam local generate-event s3 [put/delete]

You can even customize the event by adding parameter flags. To find which flags apply to your command,
run:

$ sam local generate-event s3 [put/delete] --help

Then you can add in those flags that you wish to customize using

$ sam local generate-event s3 [put/delete] --bucket <bucket> --key <key>

After you generate a sample event, you can use it to test your Lambda function locally
$ sam local generate-event s3 [put/delete] --bucket <bucket> --key <key> | sam local invoke <function logical id>

~ (後略) ~


■ S3のputイベントを作る

$ sam local generate-event s3 put --bucket test.secondly --key app/test.json

※ bucketは指定しないと example-bucket が指定されるのでbucket指定操作を行う場合は注意

※ 今回はkeyは別に入れなくても問題ないが、見栄えのためいれた


■ S3の擬似putイベントをgolangに検知させる

どんなイベントをキャッチしたかわかりやすいようにmain.goを修正。

$ cat hello-world/main.go 

package main

import (
"fmt"
"context"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
)

func handler(ctx context.Context, s3Event events.S3Event) {
for _, record := range s3Event.Records {
if record.EventName == "ObjectCreated:Put" {
s3 := record.S3
fmt.Printf("[SUCCESS] event doing!! - %s \n",record.EventName)
fmt.Printf("[%s - %s] Bucket = %s, Key = %s \n", record.EventSource, record.EventTime, s3.Bucket.Name, s3.Object.Key)
} else {
fmt.Printf("[Skip] event doing!! - %s \n",record.EventName)
}
}
}

func main(){
lambda.Start(handler)
}

※ どんなjsonが取得できるかは↓のawsが提供しているリポジトリを参照

https://github.com/aws/aws-lambda-go/blob/master/events/s3.go#L13-L23

buildし直して、実行してみる。

$ make build

GOOS=linux GOARCH=amd64 go build -o hello-world/hello-world ./hello-world

$ sam local invoke "HelloWorldFunction" -e event.json
2019-01-07 13:50:13 Found credentials in shared credentials file: ~/.aws/credentials
2019-01-07 13:50:13 Invoking hello-world (go1.x)

~ (中略) ~

[SUCCESS] event doing!! - ObjectCreated:Put
[aws:s3 - 1970-01-01 00:00:00 +0000 UTC] Bucket = test.secondly, Key = app/test.json
null

~ (後略) ~

→ ObjectCreated:Put イベントをキャッチし、どんなbucketに対してputイベントがあったかを無事検知できた。