AWS codepipelineで手動承認が複数発生した場合

#TL;DR
- AWS codepipelineで承認ボタンを押しても、ボタンが残るケースがある
- 自分の場合、Lambdaでcodepipelineをよしなにする処理を追加
- 他にいい方法ご存知の方は、コメントいただけると嬉しいです

#背景

以下のような形で承認付きのAWS+Docker+BlueGreenDeploymentを構築していました。

![blue-green sample.png](https://qiita-image-store.s3.amazonaws.com/0/158500/da1a36d2-312a-d114-7f61-5a9cc1f76582.png)

図のざっくりな流れとしては、

1.githubでmasterのマージ

2.AWS Codebuild経由で最新dockerイメージをGreen系にデプロイ

3.承認者へslackで通知し、AWSコンソールより承認ボタンpush

4.承認ボタンpush後、ELBのbluegreen切り替え

という感じです。

参考リンク：
https://aws.amazon.com/jp/blogs/news/bluegreen-deployments-with-amazon-ecs/
https://dev.classmethod.jp/cloud/aws/codepipeline-approval-action/



こちらをテスト運用してみたところ、

・問題なく承認してリリースした場合
・codepipelineで一度否認してから再度マージする場合
であれば問題ないんですが、

・否認するまえに再度マージしたpipeline処理が追いついてしまった
場合、codepipelineの承認ボタンを一度押してもまた残ってしまうケースがありました。

<img width="193" alt="スクリーンショット 2017-12-20 14.09.53.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/158500/3977b584-b81c-3e07-3d80-07665f3a50f8.jpeg">
　　※押しても承認ボタンが復活してしまう。否認しても再試行ボタンは残ってしまう
　　もちろん承認ボタンを押してしまうと、以降のpipeline処理は動いてしまう。

#対応
残った承認ボタンによる不要なpipelineの実行を防ぎたかったので、
pipeline実行時に
承認アクションの状態を確認→承認待ちなら自動的に否認
という処理をLambda(python,boto3)でなんとかしています。
以下参考ですがコードです。

~~~
import boto3
import logging
import time

logger = logging.getLogger()
logger.setLevel(logging.INFO)

elb = boto3.client('elbv2')
ecs = boto3.client('ecs')
asg = boto3.client('autoscaling')
cf = boto3.resource('cloudformation')
code_pipeline = boto3.client('codepipeline')

def reject_approve(pipeline):
    # get pipeline state
    response = code_pipeline.get_pipeline_state(
        name = pipeline
    )
    target_stage_pos = response['stageStates'][xxx] //xxx にはApprovalステージの場所
    target_action_json = target_stage_pos['actionStates'][0]
    print target_action_json
    print "-----------"
    
    if 'latestExecution' not in target_action_json:
        print "no execution history in this stage."
        return

    target_stage_name = target_stage_pos['stageName']
    print "Check stage name."
    print target_stage_name
    print "-----------"

    target_action_name = target_stage_pos['actionStates'][0]['actionName']
    print "Check action name."
    print target_action_name

    print "-----------"
    target_action_status = target_action_json['latestExecution']['status']
    print "Check status."
    print target_action_status

    if target_action_status == 'InProgress':
        print "-----------"
        target_action_token = target_action_json['latestExecution']['token']
        print "Get token."
        print target_action_token
        
    print "-----------"
    if target_stage_name == 'YOUR_APPROVAL_STAGE_NAME' and target_action_name == 'Approve' and target_action_status == 'InProgress':
        print "target check OK."
        print "-----------"
        print "Reject target approval."
        reject_response = code_pipeline.put_approval_result(
            pipelineName = pipeline,
            stageName = target_stage_name,
            actionName = target_action_name,
            result={
                'summary': target_action_status,
                'status': 'Rejected'
            },
            token = target_action_token
        )        
        print reject_response
    else:
        print "No action to reject."

def lambda_handler(event, context):
    try:
        # consts
        pipeline_name = 'YOUR_PIPELINE_NAME'
        job_id = event['CodePipeline.job']['id']
        reject_approve(pipeline_name)
    	code_pipeline.put_job_success_result(jobId=job_id)

    except Exception as e:
        logger.error(e)
        raise e  

~~~

#まとめ
pipelineでの手動承認やslack通知についての記事は調べればいくつか出てきますが、
案外こういう細かいケースでの対応はまだ無かったので投稿してみました。

他にいいやり方などありましたら、コメントいただけると幸いです。