Serverless Frameworkもv1.0.0-rc.1が出てきて、いよいよ正式リリースが待ち遠いですね。
今回は、開発を進めていく中でデプロイ時にフレームワーク内で生成されたものに対して手を加えたい。といった場合を想定して、そのためのプラグインを作ってみたいと思います。
動作環境
- Serverless v1.0.0-rc.1
- nodejs(v4.3.2)
今回の目標
- deployコマンドをフックする。
- serverlessのdeployコマンド内で作られたCloudFormationテンプレートに手を加える。
- APIGatewayのステージ変数を設定する。
※ステージ変数は、serverless.yml の中で
custom:
stageVariables:
stageValue1: value1
stageValue2: value2
のようにカスタム変数で定義されているstageVariablesを設定します。
上記のことをやっていこうと思います。
deployコマンドをフックする
クラスの作成
プラグインに関する基本的な情報は公式ドキュメントに載っています。
まずは枠のクラスだけ作ってしまいましょう。
'use strict';
const _ = require('lodash');
class StageVariables {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options;
this.provider = 'aws';
}
};
module.exports = StageVariables;
フックのトリガーを決める
deployのフックを行いたいため、deployコマンドのライフサイクルを確認します。
- deploy:initialize
- deploy:setupProviderConfiguration
- deploy:compileFunctions
- deploy:compileEvents
- deploy:createDeploymentArtifacts
- deploy:deploy
いろいろと書いてありますが、
deploy:compileEvents
After that the events which are defined in the serverless.yml file on a per function basis should be compiled to provider specific resources and also stored into memory.
こちらが良さそうです。
フックする処理の記述
compileEventsをフックする処理をconstructor内に書き、そこから呼び出される関数を書きます。
constructor(serverless, options) {
this.serverless = serverless;
this.options = options;
this.provider = 'aws';
this.hooks = {
'after:deploy:compileEvents': this.compileStageVariables.bind(this),
};
compileStageVariables() {
// ここに実際の処理を書きます
console.log('hook deploy:compileEvents');
}
これでcompileEventsがフックできます。
一度実行してみましょう
実際にデプロイされてしまうと既存の環境を壊してしまう可能性があるので、 --noDeploy
オプションを忘れずに
% sls deploy --stage pluginTest -v --noDeploy
Serverless: Packaging service...
hook deploy:compileEvents
Serverless: Did not deploy due to --noDeploy
deployコマンドの中でコンソールログが出力されることが確認できました。うまくフックできているようです。
serverlessのdeployコマンド内で作られたCloudFormationテンプレートに手を加える
CloudFormationテンプレートを格納しているおブジェクトがどこか、ざっとドキュメントを読んだだけでは見当たらなかったのですがserverless/lib/plugins/aws/deploy/compile/events/apiGateway/lib/deployment.js
を読んだところ、 this.serverless.service.provider.compiledCloudFormationTemplate
というオブジェクトに格納されているようです。
今回はリソースの変更をする予定なので、 compiledCloudFormationTemplate.Resources
に手を加えます
APIGatewayのステージ変数を設定する
使用するテンプレートの確認
APIGatewayでのステージ変数が設定可能なCloudFormationを確認したところ、AWS::ApiGateway::Deployment
と AWS::ApiGateway::Stage
で可能なようです。
http://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-deployment.html
http://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-apigateway-stage.html#cfn-apigateway-stage-deploymentid
今回はAWS::ApiGateway::Deployment
に手を加えていきます。
このテンプレート内の StageDescription に値を設定すればいいはずです。
Serverless Framework内での命名の確認
Serverless Framework内で使用されているリソース名はCloudformation Resource Reference で確認できます。
AWS::ApiGateway::Deployment
の項目を見ると、ApiGatewayDeployment{randomNumber}
と書いてありますね。
内部でランダムな数字を振っているようです。
ApiGatewayDeployment の値を書き換える
まずはステージ変数を格納するオブジェクトを作成しておきます。
const stageVariables = {
Variables : {}
};
_.forEach(this.serverless.service.custom.stageVariables, (val, key) => {
stageVariables.Variables[key] = val;
});
stageVariables.StageName = this.options.stage;
次に、CloudFormation内でApiGatewayDeployment
で始まるキーを探して値を書き換えます。
_.forEach(this.serverless.service.provider.compiledCloudFormationTemplate.Resources, (val, key) => {
if (key.startsWith('ApiGatewayDeployment')) {
if (!val.Properties.StageDescription) {
val.Properties.StageDescription = stageVariables;
}
}
});
これでステージ変数の格納が可能なはずです。最後に実行してみましょう。
% sls deploy --stage pluginTest -v
Serverless: Packaging service...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading service .zip file to S3...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
CloudFormation - CREATE_COMPLETE - AWS::IAM::Role - IamRoleLambdaExecution
CloudFormation - CREATE_IN_PROGRESS - AWS::IAM::Policy - IamPolicyLambdaExecution
CloudFormation - CREATE_IN_PROGRESS - AWS::ApiGateway::Deployment - ApiGatewayDeployment1474550078492
CloudFormation - CREATE_COMPLETE - AWS::ApiGateway::Deployment - ApiGatewayDeployment1474550078492
CloudFormation - UPDATE_COMPLETE_CLEANUP_IN_PROGRESS - AWS::CloudFormation::Stack - plugin-test-pluginTest
CloudFormation - UPDATE_COMPLETE - AWS::CloudFormation::Stack - plugin-test-pluginTest
Serverless: Stack update finished...
Service Information
service: plugin-test
stage: pluginTest
region: ap-northeast-1
上手くいったようです。APIGatewayの管理画面も確認してみます。
きちんとステージ変数が設定されていますね。
最後に
荒削りな部分も多いServerless Frameworkですが、内部的にはCloudFormationのテンプレートを作ってそこから反映。という形をとってるので、CloudFormationの書き方さえ覚えてしまえば、どんどん拡張していけそうです。
serverlessという構成を取るために考えることや、専用の設計などやることはたくさんありますが、かなり実用的になってきているように思えます。
今回のコードはgithubでも公開していますので、プラグインを作る際の参考にでもなればと思います。
github