AWS Step FunctionsでRedshift準備完了まで待機する

# 目的
AWSでサーバーインスタンス起動系などのリクエストをすると数分待つことになります。
対象や実現方法は色々考えられますが、今回はRedshiftを起動し、使える状態になるまで待機する部分を[AWS Step Functions](https://aws.amazon.com/jp/step-functions/)で実装してみます。

# 概要
- [チュートリアル](http://docs.aws.amazon.com/ja_jp/step-functions/latest/dg/tutorial-creating-job-status-poller.html)のターゲットをRedshiftに変えて実際にやってみたという内容です。
- LambdaはPython3.6で記述します。

# 手順
- 実行用のロールや環境設定は省略します


## Redshift起動Lambdaを作成
`createRedshiftCluster` という名前でLambda Functionを作成します。
パラメータはハードコーディングになっています。（記事用にシンプルにするためです 汗）

```py3:createRedshiftCluster
import boto3

def lambda_handler(event, context):
    redshift = boto3.client('redshift')
    redshift.create_cluster(
        ClusterIdentifier='test-cluster',
        DBName='dev',
        Port=5439,
        MasterUsername='testuser',
        MasterUserPassword='myPassword1234',
        VpcSecurityGroupIds=['sg-abcdef12'], 
        ClusterSubnetGroupName='csg1',
        NodeType='dc2.large',
        ClusterType='single-node',
        PubliclyAccessible=True,
        EnhancedVpcRouting=False)
    event['result'] = 'SUCCESS'
    return event
```

## Redshift起動完了確認Lambdaを作成
`waitRedshift` という名前でLambda Functionを作成します。
WaiterAPIを利用しますがここではリトライはしません。(MaxAttempts=1は1回実行して実行中なら再試行しません）
StepFunctions側でリトライを制御します。
エラー処理は微妙かもしれません。現物合わせです。
起動中なら`CREATING`、準備完了なら `RUNNING` を返すようにしてあります。

```py3:waitRedshift
import boto3
import botocore

def lambda_handler(event, context):
    redshift = boto3.client('redshift')
    waiter = redshift.get_waiter('cluster_available')
    try:
        waiter.wait(
            ClusterIdentifier='test-cluster',
            WaiterConfig={
                'Delay': 3,
                'MaxAttempts': 1})
        event['result'] = 'RUNNING'
    except botocore.exceptions.WaiterError as e:
        if not hasattr(e, 'last_response'):
            raise e
        if 'Error' in e.last_response:
            raise e
        event['result'] = 'CREATING'
    return event
```

## 全体の流れをStepFunctionsで作成
`PrepareRedshift` という名前でStepFunctionsを作成します。
上記で作成した `lambda:createRedshiftCluster` を実行し、20秒待ち。
`lambda:waitRedshift`で完了チェックし、まだなら再度20秒待ち。これを繰り返します。
APIコール部分はリトライも記述してあります。

```json:PrepareRedshift
{
  "Comment": "Create and Wait for Redshift Cluster",
  "StartAt": "CreateCluster",
  "States": {
    "CreateCluster": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:us-east-1:123456789012:function:createRedshiftCluster",
      "Next": "Wait",
      "Retry": [
        {
          "ErrorEquals": ["States.ALL"],
          "IntervalSeconds": 5,
          "MaxAttempts": 3,
          "BackoffRate": 2.0
        }
      ]
    },
    "Wait": {
      "Type": "Wait",
      "Seconds" : 20,
      "Next": "CheckStatus"
    },
    "CheckStatus": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:us-east-1:123456789012:function:waitRedshift",
      "Next": "IsRunning",
      "Retry": [
        {
          "ErrorEquals": ["States.ALL"],
          "IntervalSeconds": 5,
          "MaxAttempts": 3,
          "BackoffRate": 2.0
        }
      ]
    },
    "IsRunning": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.result",
          "StringEquals": "RUNNING",
          "Next": "Success"
        },
        {
          "Variable": "$.result",
          "StringEquals": "CREATING",
          "Next": "Wait"
        }
      ],
      "Default": "Abort"
    },
    "Abort": {
      "Type": "Fail"
    },
    "Success": {
      "Type": "Succeed"
    }
  }
}
```

### 記述について

素晴らしい機能ですが、[Amazon States Language](https://states-language.net/spec.html) なる言語(?)で記述する必要があります。
あとフロー中の変数取り回しができるのですが、 `$.var` のような記述でこれは[JsonPath](https://github.com/json-path/JsonPath)というようです。
ちょっと大変ですが、ひと通り目を通しておいたほうが良さそうです。
ビジュアライズしてくれるのでそこはホッとします。

### 表示例：（Endがつながっていませんが大丈夫です :sweat_drops:）

![image.png](https://qiita-image-store.s3.amazonaws.com/0/53115/3d219f5a-ad91-08df-30e3-6d5243b7c7cd.png)

# 実行結果
マネージメントコンソールから実行すると実行中、実行終了のステップに着色されていきます。
今回は4分ほどで成功しました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/53115/cfbddbf3-ca17-730c-dd55-3407adefd53e.png)

# まとめ
Redshiftクラスタが操作可能になるまで待機する、というパーツはできました。（※エラー処理とかテストとか全然できてませんが :smile: ）
現実的には、処理の起動トリガーをどうするか、後続処理を実装する等、システムとして組み立てることになると思います。
プロジェクトとしては、いわゆるビジネスロジック（古い表現かも）部分に時間を掛けたいはずですよね。
**今回のような準備処理は時間をかけず、サラリと実現するためのツールが [Cloud Automator](https://cloudautomator.com/)ですねー**