やりたいこと
- TypeScript で書かれている AWS Lambda のコードを AWS SAM Local で動かしたい
- TypeScriptのコードとbuild後のコードは別ディレクトリで保持したい
- AWS SAM LocalのREADMEではそこの説明が無いので、自分でなんとか動く方法を模索する必要がある
結論
CodeUri プロパティはcloudformation.ymlからの相対パスを指定する必要がある
※ Lambdaと AWS SAM の初心者です。間違ったことを言ってる。もしくはもっと良い方法を見つけられていない可能性があります。なにかありましたら、コメント頂けますと幸いです!
AWS SAM Localとは
https://github.com/awslabs/aws-sam-local#advanced
AWS Lambda関数をローカルで実行しテストができるようになるツール。 S3, DynamoDB, Kinesis 等のリソースからのイベントをエミュレートすることもできる。 API Gatewayをlocalで動かし、Lambdaコードの変更をhot reloadすることもできる。
AWS SAM のinstall
npm install -g aws-sam-local sam --version # installできているか確認する
上記コマンドだけで installが可能。Dockerも必要なので気をつけること。
設定内容
AWS SAMの説明とcloudformation.yml の書き方についてはここに書いてあるので、ここを見ながら設定を行う。 https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
yamlの設定内容
# cloudformation.yml AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: Simple Lambda functions Resources: HelloWorld: Type: AWS::Serverless::Function Properties: CodeUri: build/src Handler: index.post Runtime: nodejs6.10 Events: GetResource: Type: Api Properties: Path: /chat-bot Method: post
上記の設定ファイルが SAM Localを使う上で、自分が設定した CloudFormationのymlである。CloudFormationに Transform: AWS::Serverless-2016-10-31
という値を追加することで、SAMの定義をCloudFormationで行うことが可能となる。
In order to include objects defined by AWS SAM within a CloudFormation template, the template must include a Transform section in the document root with a value of AWS::Serverless-2016-10-31.
ここで重要なのは HelloWorldリソースのCodeUri
プロパティで、こいつはjsファイルのディレクトリを指す。気をつけなければならないのは、指定するディレクトリパスは絶対パスか、cloudformation.ymlからの相対パスを指定しなければならないということ。よって、このときのディレクトリ構成は下記のようになる。
➜ speee_pf_checker git:(aws_saml) ✗ tree -I node_modules . ├── build │ └── src │ ├── index.js | ... │ └── index.js.map ├── cloudformation.yml ├── package.json ├── src │ ├── application.ts │ ├── index.ts | ... │ └── pc_environment.ts ├── cloudformation.yml ├── tsconfig.json ├── tslint.json └── yarn.lock
ちなみにドキュメントでは下記のように記載されている。
For example, if your AWS Lambda function source code is in the /home/user/code/lambdafunction/ folder, specify
CodeUri: /home/user/code/lambdafunction
for the AWS::Serverless::Function resource. The command returns a template and replaces the local path with the S3 location: CodeUri: s3://mybucket/lambdafunction.zip.
ということで、絶対パスじゃないといけないかと思ったら、絶対パス指定して実行したら下記のエラーとなった。
➜ speee_pf_checker git:(aws_saml) ✗ echo '{"body": "chiba"}' | sam local invoke HelloWorld --template cloudformation.yml 2017/11/27 19:22:28 Successfully parsed cloudformation.yml 2017/11/27 19:22:28 Connected to Docker 1.34 2017/11/27 19:22:28 Fetching lambci/lambda:nodejs6.10 image for nodejs6.10 runtime... nodejs6.10: Pulling from lambci/lambda Digest: sha256:0721cee9614fe0c995c2eb0f52b9803a23d1c2da3007d46cb54b745d970850a0 Status: Image is up to date for lambci/lambda:nodejs6.10 2017/11/27 19:22:31 Reading invoke payload from stdin (you can also pass it from file with --event) 2017/11/27 19:22:31 Invoking index.post (nodejs6.10) START RequestId: 08f01507-747c-1789-1312-b3368c5a26ad Version: $LATEST Unable to import module 'index': Error at Function.Module._resolveFilename (module.js:469:15) at Function.Module._load (module.js:417:25) at Module.require (module.js:497:17) at require (internal/module.js:20:19) END RequestId: 08f01507-747c-1789-1312-b3368c5a26ad REPORT RequestId: 08f01507-747c-1789-1312-b3368c5a26ad Duration: 13.31 ms Billed Duration: 0 ms Memory Size: 0 MB Max Memory Used: 29 MB {"errorMessage":"Cannot find module '/var/task/index'","errorType":"Error","stackTrace":["Function.Module._load (module.js:417:25)","Module.require (module.js:497:17)","require (internal/module.js:20:19)"]}
JavaScriptのコード
// build/src/index.js 'use strict'; console.log('Loading function'); exports.post = (event, context, callback) => { callback(null, { statusCode: 200, headers: { "x-custom-header" : "my custom header value" }, body: "Hello " + event.body }); };
確認用のシンプルなJSコード。
動作確認
➜ speee_pf_checker git:(aws_saml) yarn build yarn run v1.3.2 $ tsc ✨ Done in 3.25s. ➜ lambda git:(aws_saml) ✗ echo '{"body": "chiba"}' | sam local invoke HelloWorld --template cloudformation.yml 2017/11/27 18:04:54 Successfully parsed cloudformation.yml ... {"statusCode":200,"headers":{"x-custom-header":"my custom header value"},"body":"Hello chiba"}
上記コマンドで確認した結果、aws sam localが動いていることが確認できた。