AWS LambdaのDestinationsを試してみる

AWSから割とすごい機能が発表されました。

Introducing AWS Lambda Destinations
https://aws.amazon.com/jp/blogs/compute/introducing-aws-lambda-destinations/

![スクリーンショット 2019-11-27 6.51.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102769/8dcc8bb4-f376-17ae-1ccf-b64739c3289b.png)

Lambdaの実行結果に従って次のアクション（AWSサービス）を指定できる、というものです。
成功／失敗の条件で流れを制御したい場合には、Step Functionsを使う必要もなく、Lambdaだけで完結することができるようになりました。

早速試してみます。

# 呼び出し元のLambdaを適当に定義

こんな感じのPythonを書きます。（Python 3.7を利用）

```python
import json
import logging

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


def destination_sample_handler(event, context):
    logger.info("destination sample lambda started.")
    if event['Success'] == True:
        return {
            'statusCode': 200,
            'body': event
        }
    else:
        raise Exception('Success is False', event)
    
```

呼び出す際、event内にSuccess変数を定義し、その中身がTrueかFalseで判断を変えています。

# Destinationを設定する
AWSブログではSNSとLambdaで成功／失敗の呼び出し先を変えてますが、手を抜いて両方Lambdaにしちゃいます。

Lambda関数にアクセスした際に、「Designer」パート「Cofiguration」タグ内に、「Add destination」という項目が見つかるはずです。

![スクリーンショット 2019-11-29 7.10.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102769/eee125d2-35b4-ca6d-6b0e-6bec013fc647.png)


こちらは成功パターン
![スクリーンショット 2019-11-27 7.16.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102769/ba221bf9-eb83-5652-2b2b-557a0a1ec6f6.png)

成功パターンの中身はこんな感じ。ただLogに出力しているだけです。

```python
import json
import logging

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

def lambda_handler(event, context):
    logger.info("Success dest lambda started.")
    logger.info(event)
    return {
        'statusCode': 200,
        'message': 'Success in dest lambda',
        'body': event
    }
```

こちらは失敗のDestination設定
![スクリーンショット 2019-11-27 7.18.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102769/d56544d6-0cb1-870f-22e5-bb61cbd30b14.png)

中身は成功パターンと同じです

```python

import json
import logging

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

def lambda_handler(event, context):
    logger.info("Failure dest lambda started.")
    logger.info(event)
    return {
        'statusCode': 200,
        'message': "Failure in dest lambda",
        'body': event
    }

```

# 実行してみる
実行はCLIから実施します。

```sh
$ aws lambda invoke --function-name destination-sample --invocation-type Event --payload '{ "Success": false }' response.json
{
    "StatusCode": 202
}
```

実行が成功すると202がStatusCodeとして返ってくるようです。

## 成功

成功させて見ると成功側のCloudWatch Logsにこんな感じの出力がありました。
CloudWatch Logs

```
[INFO]	2019-11-26T22:26:49.792Z	xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx	{'version': '1.0', 'timestamp': '2019-11-26T22:26:49.425Z', 'requestContext': {'requestId': '26fab6f9-a2b7-45f2-bce2-47c612cdb8b7', 'functionArn': 'arn:aws:lambda:us-east-1:000000000000:function:destination-sample:$LATEST', 'condition': 'Success', 'approximateInvokeCount': 1}, 'requestPayload': {'Success': True}, 'responseContext': {'statusCode': 200, 'executedVersion': '$LATEST'}, 'responsePayload': {'statusCode': 200, 'body': {'Success': True}}}
```

呼び出し先のeventの中身をログ出力しましたが、以下のようになっているようです。

- conditionに成功／失敗が入る
- requestPayloadに大元の呼び出し元eventが入る
- responsePayloadに呼び出し元からreturnされた値が入る

少しコードを編集してeventにMessage要素を入れたところ、responsePayloadの中身が追加されました。

```python
    if event['Success'] == True:
        event['Message'] = "Successful finished."
        return {
            'statusCode': 200,
            'body': event
        }
```

```
[INFO]	2019-11-26T23:41:21.229Z	xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx	{'version': '1.0', 'timestamp': '2019-11-26T23:41:20.730Z', 'requestContext': {'requestId': 'ba73f7a2-77f4-4166-a95d-d43020e5cc32', 'functionArn': 'arn:aws:lambda:us-east-1:000000000000:function:destination-sample:$LATEST', 'condition': 'Success', 'approximateInvokeCount': 1}, 'requestPayload': {'Success': True}, 'responseContext': {'statusCode': 200, 'executedVersion': '$LATEST'}, 'responsePayload': {'statusCode': 200, 'body': {'Success': True, 'Message': 'Successful finished.'}}}
```

ちなみに成功時のDestinationを一つ増やしたところ、On success側の設定が上書きされました。一つの条件に対して呼び出せるサービスは一つのようです。

![スクリーンショット 2019-11-27 8.55.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102769/f55de896-2127-db0e-1749-0b7fb1646bee.png)


Add destinationを実施してもOn successの呼び出し先が上書きされるだけ。

![スクリーンショット 2019-11-27 8.40.59_deco.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102769/6009036d-8a91-15d9-6890-01e75b329482.png)



## 失敗
失敗側も似たようなログがCloudWatch Logsに出ました。

```
[INFO]	2019-11-26T22:30:46.594Z	xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx	{'version': '1.0', 'timestamp': '2019-11-26T22:30:46.203Z', 'requestContext': {'requestId': '6fc2d258-fafb-40a2-8d18-886df8510fa1', 'functionArn': 'arn:aws:lambda:us-east-1:000000000000:function:destination-sample:$LATEST', 'condition': 'RetriesExhausted', 'approximateInvokeCount': 3}, 'requestPayload': {'Success': False}, 'responseContext': {'statusCode': 200, 'executedVersion': '$LATEST', 'functionError': 'Unhandled'}, 'responsePayload': {'errorMessage': "('Success is False', {'Success': False})", 'errorType': 'Exception', 'stackTrace': ['  File "/var/task/lambda_function.py", line 17, in destination_sample_handler\n    raise Exception(\'Success is False\', event)\n']}}
```

Exceptionの中身もきちんと伝搬してますね。

## ちなみにX-Rayでの表示は？
X-RayではきちんとLambdaから処理が分岐しました。想像した通りとはいえ、これはすごい。ほぼこれでトレースもできますね。

![スクリーンショット 2019-11-27 8.51.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102769/1cc8ca08-7d1f-6de5-7104-33139bf7df6c.png)


# まとめ
簡単に成功／失敗での処理の分岐が実現できてしまいました。ちょっとしたスクリプトの制御であればこれで十分ですね。