Help us understand the problem. What is going on with this article?

Serverless Framework v1.0.0-rc.1でdeployコマンドをフックするプラグインを作る

More than 3 years have passed since last update.

Serverless Frameworkもv1.0.0-rc.1が出てきて、いよいよ正式リリースが待ち遠いですね。
今回は、開発を進めていく中でデプロイ時にフレームワーク内で生成されたものに対して手を加えたい。といった場合を想定して、そのためのプラグインを作ってみたいと思います。

動作環境

  • Serverless v1.0.0-rc.1
  • nodejs(v4.3.2)

今回の目標

  1. deployコマンドをフックする。
  2. serverlessのdeployコマンド内で作られたCloudFormationテンプレートに手を加える。
  3. 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::DeploymentAWS::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の管理画面も確認してみます。

スクリーンショット 2016-09-22 22.24.36.png

きちんとステージ変数が設定されていますね。

最後に

荒削りな部分も多いServerless Frameworkですが、内部的にはCloudFormationのテンプレートを作ってそこから反映。という形をとってるので、CloudFormationの書き方さえ覚えてしまえば、どんどん拡張していけそうです。
serverlessという構成を取るために考えることや、専用の設計などやることはたくさんありますが、かなり実用的になってきているように思えます。
今回のコードはgithubでも公開していますので、プラグインを作る際の参考にでもなればと思います。
github

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away