Serverless Frameworkで特定のタイミングで独自の処理を行うプラグインを作成する
背景
Serverless Frameworkでデプロイ等を行う際になにか独自の処理をはさみたいということがあると思います。この課題をServerless Frameworkのプラグインを作って解決します。
環境
下記の環境で試しています。
- Node.js: v14.16.1
- Serverless Framework: v2.50.0
サンプルプロジェクトの作成
まず、serverless
コマンドでexample
というサンプルのプロジェクトを作成します。
$ serverless What do you want to make? AWS - Node.js - Starter What do you want to call this project? example Downloading "aws-node" template... Project successfully created in example2 folder You are not logged in or you do not have a Serverless account. Do you want to login/register to Serverless Dashboard? No Do you want to deploy your project? No ... 略
プラグインの作成
次に単純に標準出力するだけのプラグインを作成してみます。
プラグイン用のディレクトリを作成してnpm init
します。
$ mkdir plugin $ npm init
package.json
ではmain
にindex.js
を指定するようにします。
{ "name": "example-plugin", "version": "1.0.0", "description": "", "main": "index.js", "scripts": {}, "author": "", "license": "ISC" }
この段階でディレクトリ構成は下記のようになります。
. ├── example --> メインのコードが入ったディレクトリ │ ├── handler.js │ ├── package-lock.json │ ├── package.json │ └── serverless.yml └── plugin --> プラグイン向けのディレクトリ └── package.json
次にindex.js
にプラグインを実装します。
$ cd plugin $ touch index.js
まずはコンストラクタで標準出力するだけのコードを書いてみます。
// plugin/index.js class ExamplePlugin { constructor() { console.log('this is a example-plugin'); }; } module.exports = ExamplePlugin;
このプラグインをserverless.yml
のplugins
に指定します。通常はnpmパッケージの依存関係に追加した上でnpm install
する必要がありますが、それ以外にもパス指定で追加することができます。
service: main frameworkVersion: '2' plugins: # プラグインは記述した順番に実行される # v1系だとこのパス指定する書き方はできない模様 # https://www.serverless.com/framework/docs/providers/aws/guide/plugins#service-local-plugin - ../plugin ... 略
指定方法についての詳細は下記のドキュメントを参照ください。
この状態で何かしらのコマンドを実行すると、コンストラクタに書いたthis is a example-plugin
が標準出力されます。
$ sls deploy this is a example-plugin Serverless: Packaging service... Serverless: Excluding development dependencies...
デプロイ前に確認を促すようにしてみる
もう少し具体的なサンプルを作ってみます。具体的には下記のようなものを目指します。
- デプロイ前にデプロイする環境を表示する
- デプロイするかどうか
y/n
で入力させる y
以外を入力するとデプロイを中止する
これを実現するためにはhooksという機能を使用します。
Advanced Plugin Development - Extending The Serverless Core Lifecycle
標準入力にはNode.js
のreadline
を使います。
先程のplugin/index.js
のコードを下記のように書き換えます。
// https://nodejs.org/docs/latest-v14.x/api/readline.html const readline = require('readline'); class ExamplePlugin { constructor(serverless, options) { this.options = options; this.serverless = serverless; // AWS向けなので注意 this.stage = this.serverless.providers.aws.getStage().toLowerCase(); // coreライフサイクルを拡張する // https://www.serverless.com/blog/advanced-plugin-development-extending-the-core-lifecycle this.hooks = { 'before:deploy:deploy': () => this.confirmDeploy(this.stage) // serverless frameworkがserverless.ymlに記述したリソースのチェックなどを行う前に実行したい前ならinitialize // 'before:deploy:initialize': () => this.confirmationDialog(this.stage) } }; // readlineで入力を待ち受けるためのPromiseを返す関数 confirmationDialog(stage) { const readLine = readline.createInterface({ input: process.stdin, output: process.stdout, }); return new Promise((resolve, reject) => readLine.question(`${stage} : Do you wanna deploy? [y/n]`, (answer) => { readLine.close(); if (answer.toLocaleLowerCase() !== 'y') { // reject するとエラーで止まる reject("Deploy aborted!"); } resolve(); })); } } module.exports = ExamplePlugin;
これでプラグインの変更が終わったのでデプロイ時にメッセージが表示されるかどうか確認します。y
以外の値を入力するとデプロイが中断されるという挙動になっていることが確認できました。
$ sls deploy --stage dev dev : Do you wanna deploy? [y/n]n Exception ----------------------------------------------- 'Deploy aborted!' For debugging logs, run again after setting the "SLS_DEBUG=*" environment variable. Get Support -------------------------------------------- Docs: docs.serverless.com Bugs: github.com/serverless/serverless/issues Issues: forum.serverless.com ... 略
まとめ
思っていたよりも簡単に独自の処理を挟むことができました。プラグインもpackage.json
に依存関係を指定しなくてもserverless.yml
にパス指定すれば動作するなど、開発も容易だと感じました。
参考
Advanced Plugin Development - Extending The Serverless Core Lifecycle