AWSを弄っているとなにかと必要になるのがslackへの通知関数。
今回はこの通知関数をローカルで作成し、ユニットテスト実行、デプロイまでを試みました。
なお、slackのURLのような平文にしておきたくないものは環境変数ではなくパラメータストアを使って扱いたいと思います。
contents
- ローカルでSlack通知関数を作成
- mocha + chaiで単体テスト
- 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に関するロールや環境変数、ランタイムなどの指定を行います。
以下のように整形。
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を定義します。
今回は、メッセージ送信先のワークスペースとチャンネル、メッセージ内容を指定します。
{
"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);
関数本体の編集
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
テストコードの作成。
'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