先日発表したChatOps事例のスライドを共有します。
www.slideshare.net
連携のハマりどころ
この発表でちゃんと伝えてないので補足です。
APIGatewayとLambdaの連携
APIGatewayを使うにあたりSlack連携で、GETリクエストを受け取るのは簡単なんですが、POSTリクエストが実はちょっと面倒なんですね。
p18でも書いてますが、Slackから飛んでくるリクエストがJsonではなくFormなんです。
Slackから飛ぶForm内容は、以下のような感じなんですが、
token=xxxxxxxxxxxxx team_id=T0001 team_domain=example channel_id=C1234567 channel_name=test user_id=U1234567 user_name=Steve command=/weather text=its\ time\ to\ wake\ up response_url=https://hooks.slack.com/commands/1234/5678
これがAPIGatewayだとTemplateマッピングってシロモノを使わなきゃできない感じなんです。(と思っていた。)
対処方法としては、
- テンプレートマッピングを使う
- lambdaプロキシを使う
テンプレートマッピング使い方
テンプレートマッピングに関しての使い方は公式に詳しく書いてあるので、こちらを参照にしてみてください。
API Gateway API リクエストとレスポンスペイロードのマッピングテンプレートのリファレンス - Amazon API Gateway
実際自分が作ったものだと
APIGateway→メソッドを選択→統合リクエストを選択→本文マッピングテンプレートで以下のようなテンプレートを記載しました。
ContentTypeは"application/x-www-form-urlencoded"と記載。
{ "body": $input.json("$") }
これは、formのインプットに対して、bodyをキーにしたjsonに変換するという処理です。
実際受け取るときは、
{ "body": {"token":"xxxxxxxxxxxxx", "team_id":"T0001", ... } }
みたいな感じに変換されてLambdaに渡されます。
Lambdaプロキシを使う
テンプレートマッピングって独自仕様すぎてわかりづらいですよね。
そこでlambdaブロキシを使うと楽できます。
"Lambda プロキシ統合の使用"にチェックを入れると以下のようなリクエストが渡されます。
{ 'body': 'token=xxxxxxxxxxxxx&team_id=T0001&team_domain=example&channel_id=C1234567&channel_name=test&user_id=U1234567&user_name=Steve&command=/weather&text=its\ time\ to\ wake\ up&response_url=https://hooks.slack.com/commands/1234/5678', 'resource': '/start-stop-api', 'requestContext': { 'resourceId': 'xxxx', 'apiId': 'xxxxxx', 'resourcePath': '/test', 'httpMethod': 'POST', 'requestId': 'xxxxxxxx', 'accountId': 'xxxxxxxx', 'identity': { 'apiKey': None, 'userArn': None, 'cognitoAuthenticationType': None, 'accessKey': None, 'caller': None, 'userAgent': 'Slackbot 1.0 (+https://api.slack.com/robots)', 'user': None, 'cognitoIdentityPoolId': None, 'cognitoIdentityId': None, 'cognitoAuthenticationProvider': None, 'sourceIp': '', // AWSのIP 'accountId': None }, 'stage': 'dev' }, 'queryStringParameters': None, 'httpMethod': 'POST', 'pathParameters': None, 'headers': { 'Content-Type': 'application/x-www-form-urlencoded', 'Via': '1.1 xxxxxxxxxxxxxxx.cloudfront.net (CloudFront)', 'Accept-Encoding': 'gzip,deflate', 'CloudFront-Is-SmartTV-Viewer': 'false', 'CloudFront-Forwarded-Proto': 'https', 'X-Forwarded-For': '', 'CloudFront-Viewer-Country': 'US', 'Accept': 'application/json,*/*', 'User-Agent': 'Slackbot 1.0 (+https://api.slack.com/robots)', 'Host': 'xxxxx.execute-api.ap-northeast-1.amazonaws.com', 'X-Forwarded-Proto': 'https', 'X-Amz-Cf-Id': 'xxxxxx', 'CloudFront-Is-Tablet-Viewer': 'false', 'X-Forwarded-Port': '443', 'CloudFront-Is-Mobile-Viewer': 'false', 'CloudFront-Is-Desktop-Viewer': 'true' }, 'stageVariables': None, 'path': '/test', 'isBase64Encoded': False }
これを使えば、テンプレートマッピングなんかしなくても、bodyにformの値が入ってくれるので、パースできちゃいます。
CloudWatchのログ設定
自分は、グループの作成に関しては、以下のような名前で手動で実施しました。
/aws/lambda/{lambda名}
AMIに以下の権限を付与してあげれば、作成されます。
{ "Effect": "Allow", "Action": [ "cloudwatch:GetMetricStatistics", "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "*" }
TIPS
lambda自体のTIPSですが、boto3は重いです。
なので、pythonコードとしてimport するときは、
from boto3 import resource
みたいな感じで、必要のないimportを無くすように心がけましょう。
lambdaは実行時間課金なので、無駄な処理は極力削除!!
ということで、ChatOpsどんどんやっていきましょー。