Edited at

AWS SAM LocalのCodeUri設定に罠があった

More than 1 year has passed since last update.

この記事は、自分のブログからの転載です。

http://selmertsx.hatenablog.com/


やりたいこと


  • 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が動いていることが確認できた。


後程確認すること


  • node_modulesを AWS SAM Localで読み込む

  • API GatewayをLocalで使う