私の中で Serverless ななんとかネタで鉄板なのが、Github の PRやIssue などの通知連携です。日々使っているものなのでついつい。
もちろん過去にも Lambda + Node.js や Azure Functions で作っています。
では .NET Core でもやってみましょう。
目次
連携の流れ
Azure Functions では、Azure Functions の Secret と URL を直接 Github Webhook に入力して連携しました。ただ入力するだけ、簡単ですね。
AWS Lambda の場合は、Webhook を API Gateway で受けるようなことはせず、Amazon SNS を使って連携することが定番になっているように思います。Amazon SNS にするとAPI Gatewayと違ってレスポンスの受け口を作ったり管理する必要がほぼないのである意味ではとても楽です。*1
ということで今回もこの構成で行ってみましょう。Amazon SNS ではなく API Gateway で作っていたこともあったのですが余り楽しくないので推奨しません。
参考
Node.js で行われていますが、構成は同じです。
下準備
いきなりコードに行きたいところですが、まずは下準備からです。
Slack で Incoming Webhook を作成
Lambda の受け口となる Webhookの口を Slack に作成します。通知先の Channel を選んで適当にどうぞ。
これで Lambda が送る先の Incoming Webhook URL が確定しました。URL は後ほどLambda の環境変数に仕込んでおくことになります。*2
SNS の Topic 作成
次にGithub が投げる先となる SNS の受け口 Topic を作成しておきます。作成する AWS Lambda と同一Region で作っておくといいでしょう。今回は ap-northeast-1 で作ります。
SNS から Create Topic を選んで
他の Topic と区別が付くようにつけておきましょう。
これで Topic を一意に示す Topic ARN が定まります。Github で入力するので把握しておきましょう。
今回は仮に arn:aws:sns:ap-northeast-1:123456789012:githublambdawebhook
だったとします。
Github から SNS に投げる限定権限の IAM 作成
Github の Application Integrations を使った Amazon SNS では IAM の access key / secret key を直接入れることになります。
ということで指示に従って SNS を投げることができるsns:Publish
権限だけの IAM ユーザーを作成しておきます。
ポリシーの Resource
には、先ほどの Sns ARN arn:aws:sns:ap-northeast-1:123456789012:githublambdawebhook
を指定します。
あとはIAM User作成時、あるいは後ほどでも Access Key / Secret Key を取得します。Secret Key は作成時の画面でしか確認できないのでお気をつけて。
仮に、Access Key を accesskey12345
、Secret Key を secretkey12345
と表現します。
Github に Amazon SNS との連携を組む
では、Github の連携させたいリポジトリの Settings 画面に行きましょう。
メニューから Integrations & services
を選択します。
サービス一覧から Amazon SNS
を選びます。
あとは、AWS で作成した SNS Topic の情報、SNS送信専用 IAM Userの Access Key、Secret Key を入れます。
項目 | 入力する内容 | 入力値の例 |
---|---|---|
Aws Key | SNS 専用 IAM User の Access Key | accesskey12345 |
Sns topic | 作成した Sns の TOPIC Arn | arn:aws:sns:ap-northeast-1:123456789012:githublambdawebhook |
Sns region | 作成した Sns の Region。SNS Topicに書いてありますが入れます | ap-northeast-1 |
Aws secret | SNS 専用 IAM User の Secret Key | secretkey12345 |
Integrations の Event を指定する
Github の Webhook
を使わない Integrations & services
で連携する場合の最大のめんどくさいポイントはこれです。
Github API の hook.json にあるとおり、Amazon SNS の デフォルト Event は Push のみです。
この変更は Web request でしかできないのでしかたいので cURL でやります。*3
対象とするイベントは、Webhooks で適当に決めます。
今回はIssue 処理時
、Issue コメント時
、Pull Request 処理時
、Pull Request レビューサブミット時
、Pull Request レビューコメント時
とします。
API経由で変更をかける際に利用する Application Token は、Github ユーザー プロファイルから作成できます。admin:repo_hook
や admin:org_hook
を有効にしておけばいいでしょう。
Webhook の ID は、URLに表示されています。
events が期待通りに設定されましたか? cURL の設定時のレスポンスで確認できます。
残りは Amazon SNS と Lambda のつなぎこみですが、Visual Studioから Lambda コード反映時にまとめて設定できるので後述します。
お疲れさまでした、長い下準備もおしまいです!
C# で Github Webhook を受ける
Azure Functions で書いていたコードを使って書いてみましょう。
サンプルJSON
今回サンプルで利用した、SNS から送ってくるJSON は次のフォーマットです。これは Issue のサンプルなので、コードもIssue を想定したものとします。
C# コード例
Issue が書かれたらSlack に飛ばすサンプルです。ほぼ Azure Functions のコードと違いはありません。
違いは2点のみです。
- フィールドに AWS Lambdaの環境変数から SlackWebhookUrl というキーを探して URL をstringとして取得するようにします。パスワードとかそういうのは Lambda の環境変数で KMS 使って暗号化がいいですね
- SNS Response のデシリアライズ処理が入っています
当たり前の注意点だけ
- X-Github-Event がプロパティ名に利用できないので JSONPropertyとして指定しておきます
- ただし、github のイベントJSONは issue だったり PR だったりでフォーマットが変わるので、そこは AzureFunctiosn 同様に dynamic で受けておきます
今回は Issue に限定しましたが、適当に他のイベント対応も switch に達せばいいでしょう。*4
xUnit テスト
ごく単純な実行テストです。様々なイベントに応じた JSON パターンをここでテストするといいでしょう。
ローカル実行
ローカル実行する場合は、buildOptions に emitEntryPoint
を true と設定してProgram.cs
を適当に書きましょう。
"buildOptions": { "emitEntryPoint": true },
これで普通のコンソールアプリケーションとして検証できます。
アップロード と 環境変数 と イベントソース設定
そろそろ CI ほしいですが、まだいったんVisual Studio でやります。適当にアップロード先 Function を作成します。
冒頭で作成した Slack Incoming Webhook のURL を環境変数に入れるのもここでできます。便利。
ついでに、Amazon SNS を起点に Lambda を実行するので、Event Sources も設定してしまいます。
もちろん Web Console 画面からやってもok です。
テスト実行
ローカル実行やテストする場合は、OSの環境変数に Lambda に設定する環境変数と同じものを仕込んでおきます。
Isssue を適当に立ててコメントしてみます。
うまく Slack に通知されましたね。
テスト中の実行グラフも出ています。
失敗や成功のログも Cloud Watch Logs に出力されています。
まとめ
Azure Functions と比べると SNS が増えてるだけなのに手間が大きく違いますね!シカタナイとは言え、Github とのつなぎこみに特化対応している Azure Functions はこの面で圧倒的に便利です。ただ、.csx でやり続けるのとどっちがいいかというと、どうでしょうかねぇ。
さて、今回のサンプルも挙げておきます。参考になれば幸いです。