2
0

More than 3 years have passed since last update.

lambdaをローカルで作成してデプロイする。

Posted at

AWSを弄っているとなにかと必要になるのがslackへの通知関数。
今回はこの通知関数をローカルで作成し、ユニットテスト実行、デプロイまでを試みました。
なお、slackのURLのような平文にしておきたくないものは環境変数ではなくパラメータストアを使って扱いたいと思います。

contents
1. ローカルでSlack通知関数を作成
2. mocha + chaiで単体テスト
3. lambdaへデプロイ

前提条件

  • Slack WebhookのURLを作成・取得済み。
  • ローカルにaws-sam-cliを導入済み。

1. ローカルでSlack通知関数を作成

今回はプロジェクト名をlocalSlackとします。

$ sam --version                                         
SAM CLI, version 0.38.0

$ sam init --runtime nodejs10.x
Which template source would you like to use?
    1 - AWS Quick Start Templates
    2 - Custom Template Location
Choice: 1

Project name [sam-app]: localSlack

Quick start templates may have been updated. Do you want to re-download the latest [Y/n]: Y

AWS quick start application templates:
    1 - Hello World Example
    2 - Quick Start: From Scratch
    3 - Quick Start: Scheduled Events
    4 - Quick Start: S3
    5 - Quick Start: SNS
    6 - Quick Start: SQS
    7 - Quick Start: Web Backend
Template selection: 1

-----------------------
Generating application:
-----------------------
Name: localSlack
Runtime: nodejs10.x
Dependency Manager: npm
Application Template: hello-world
Output Directory: .

Next steps can be found in the README file at ./localSlack/README.md

$ tree
localSlack
├── README.md
├── events
│   └── event.json
├── hello-world
│   ├── app.js //関数本体
│   ├── package.json
│   └── tests
│       └── unit
│           └── test-handler.js
└── template.yaml

IAMロール作成

CloudWatchへのログ書き込みとSessionManagerのパラメータストアからの読み取りを許可したロールを作成。ここではarn:aws:iam::xxxxxxxxxxxx:role/slackroleと命名する。

template.ymlの編集

template.ymlはCloudFormationでlambdaを作成する時に指定したものと同じです。ここでlambdaに関するロールや環境変数、ランタイムなどの指定を行います。
以下のように整形。

template.yml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Globals:
  Function:
    Timeout: 3

Resources:
  notifySlack:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello-world/
      Handler: app.handler
      Runtime: nodejs10.x
      Role: 'arn:aws:iam::xxxxxxxxxxxx:role/slackrole'
      #環境変数でURLを指定する場合には以下。可能ならSSM推奨。暗号化できるし。
      Environment:
        Variables:
          SLACK_WEBHOOK_URL: >-
            https://hooks.slack.com/xxxxxx

event.jsonの編集

lambda関数の実行時に渡すeventを定義します。
今回は、メッセージ送信先のワークスペースとチャンネル、メッセージ内容を指定します。

event.json
{
  "workspace": "SLACK_WEBHOOK_URL",
  "channel": "#test",
  "text": "test message"
}

SSM パラメータストアでの環境変数の設定

設定項目 設定内容
名前 SLACK_WEBHOOK_URL
タイプ 安全な文字列
"自身のslackのurl"

取得する場合は以下。

const AWS = require('aws-sdk');
const ssm = new AWS.SSM();
const request = {
    Name: "パラメータ名",
    WithDecryption: "true/false" //復号化の有無を指定。
};
ssm.getParameter(request);

関数本体の編集

app.js
const AWS = require('aws-sdk');
const ssm = new AWS.SSM();
const https = require('https');
const url = require('url');

exports.handler = function(event, context){
    let slack_url = "";
    (async function () {
        return await ssm.getParameter({Name: event.workspace, WithDecryption: true}).promise();
    }()).then(result => {
        slack_url = result.Parameter.Value;
    }).then(() =>{
        const slack_req_ops = url.parse(slack_url);
        slack_req_ops.method = 'POST';
        slack_req_ops.header = {'Content-Type':'application/json'};
        if(event.text){
            var req = https.request(slack_req_ops, function(res){
                if(res.statusCode == 200){
                    context.succeed('sent message');
                }else{
                    context.fail('[ERROR] status code : ' + res.statusCode);
                }
            });

            req.on('error', function(e){
                console.log('[ERROR] problem with request', e.message);
                context.fail(e.message);
            });

            let channel = event.channel;
            let text = event.text;

            req.write(JSON.stringify({"channel":channel,"text":text}));
            req.end();
        }
    })
};

まず手動実行。

$ cd localSlack
$ sam local invoke notifySlack --event events/event.json

2. mocha + chaiで単体テスト

このままだとcontextが定義されてないとエラーするので以下のモジュールを入れる。
リファレンス:https://www.npmjs.com/package/aws-lambda-mock-context

$ cd hello-world 
$ npm install
$ npm install aws-sdk 
$ npm install aws-lambda-mock-context
$ ls
app.js      package-lock.json   tests
node_modules        package.json

テストコードの作成。

test-handler.js
'use strict';

const index = require('../../app.js');
const chai = require('chai');
const expect = chai.expect;
const context = require('aws-lambda-mock-context');

describe('test', function () {
    it('Nomal test', async () => {
        const ctx = context();
        let event = require('../../../events/event.json');
        let value = "";
        index.handler(event, ctx)
        await ctx.Promise
            .then((val) => {
                //=> succeed() called
                value = val;
            })
            .catch(err => {
                //=> fail() called
                value = err;
                console.log(err);
            });
        expect(value).to.equal("sent message");
    });
});

ユニットテストの実行。

$ cd hello-world
$ env AWS_SDK_LOAD_CONFIG=true ./node_modules/.bin/mocha ./tests/unit/test-handler.js


  test
    ✓ Nomal test (616ms)


  1 passing (686ms)

なお、AWS_SDK_LOAD_CONFIGを指定しないで実行すると以下のエラーが発生します。

UnhandledPromiseRejectionWarning: ConfigError: Missing region in config

ただ、上記の方法でcontext.succeedの値を取得し比較することはできますが、context.failの値を比較しようとすると、エラーログがずらずらと含まれてしまい、比較できませんでした、、(誰か教えて欲しい、、)

3. lambdaへデプロイ

s3にバケットを作成したのち、下記コマンドでデプロイする。

$ cd localSlack
$ sam package --template-file template.yaml --output-template-file package.yml --s3-bucket バケット名 

$ sam deploy --template-file package.yml --stack-name スタック名 --capabilities CAPABILITY_IAM
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0