やろうとしたこと
GuardDuty の検知を起点に Lambda が自動で対応する仕組みを作り、その結合テストを設計していました。本物の検知を待っていてはテストにならないので、GuardDuty が出すイベントと同じ JSON を aws events put-events で流し、EventBridge のルールから先が動くかを確かめる、という方針です。
この方針は成立しませんでした。理由は2つあって、2つ目のほうが厄介でした。
source の aws. は投入できない
EventBridge のイベントには source があり、AWS サービスが出すイベントは aws.guardduty のように aws. で始まります。この名前空間は AWS が予約していて、PutEvents から同じ値のイベントを投入することはできません。
PutEvents の API リファレンスは「Sends custom events to Amazon EventBridge」という一文で始まります。この API はカスタムイベントのためのもので、AWS サービス由来のイベントをここから流すことは想定されていません。公式ドキュメントに「aws. は使えない」と明記した箇所は見つけられませんでしたが、EventBridge のエミュレータ実装や周辺ツールのドキュメントは、いずれもこの制約を前提に書かれています。独自イベントの source には com.example.orders のような逆ドメイン形式を使う、という例示もそこから来ています。
つまり、AWS サービス由来のイベントをそのままの形で再現投入する手段はありません。
呼び出しは成功して返る
2つ目はこれです。エントリが受け付けられなくても、PutEvents は HTTP 200 で返ります。
API リファレンスの Response Elements にはこうあります。
If the action is successful, the service sends back an HTTP 200 response.
Errors のセクションに並んでいるのは InternalException(500)だけで、個々のエントリの失敗は例外になりません。失敗はレスポンスの中に入ります。
{
"FailedEntryCount": 1,
"Entries": [
{
"ErrorCode": "...",
"ErrorMessage": "..."
}
]
}
成功したエントリには EventId が入り、失敗したエントリには ErrorCode と ErrorMessage が入ります。配列の並び順はリクエストと同じなので、何件目が落ちたかはここで分かります。
CLI を叩いて終了コードだけを見ていると、投入できたと思い込みます。スクリプトからイベントを流すなら、見るべきは FailedEntryCount です。
aws events put-events --entries file://event.json \
--query 'FailedEntryCount' --output text
0 以外が返ったら Entries の ErrorCode を見る。テストの前処理でイベントを流す場合は、ここで止める作りにしておかないと、ルールが発火しなかったのか、そもそもイベントが入っていないのかを切り分けられなくなります。結合テストで最初に疑いたいのはルールのイベントパターンなので、前処理の失敗がそこに化けるのはかなり困ります。
回避しても検証できないもの
投入そのものは、source を独自の値に変えれば通ります。テスト用のイベントバスと、そのイベントを受けるテスト用のルールを別に用意する形です。
ただし、これで確かめられるのは「ルールが発火したら後続が動くか」までです。本番のルールのイベントパターンが、実際に AWS から飛んでくるイベントに一致するかは検証できていません。source を書き換えた時点で、投入するイベントは本物と別物になっています。
JSON の階層やフィールド名の思い違いでルールが発火しない、というのは結合テストで見つけたい不具合の筆頭です。そこを確かめたいなら別の手段が要ります。
GuardDuty の場合は CreateSampleFindings があり、これは本物の finding として発行されるのでルールの一致を確かめられます。公式ドキュメントも、EventBridge のイベントやフィルタの設定を試す用途を挙げています。ただし実際のログ解析を経ずに finding を直接作るので、検知ロジックそのものは検証範囲の外です。
それと、サンプルの finding は本物とまったく同じではありません。公式には次のように書かれています。
The generated samples are approximations populated with placeholder values.
タイトルは [SAMPLE] で始まり、additionalInfo に "sample": true が入ります。リソース ID などはプレースホルダ値なので、特定のリソースを絞り込む条件を書いたイベントパターンだと一致しないことがあります。ルールの検証に使うときは、パターンのどの条件がサンプルで満たされるのかを先に確認しておくほうが安全です。
結局、テストケースごとに「どの方式でイベントを起こしたか」と「それで何を確かめたことになるか」を書き分けるしかありません。仕様書にその1行があるかどうかで、レビューの通りやすさが変わります。
まとめ
-
put-eventsではaws.で始まる source を投入できない。AWS サービス由来のイベントはそのままの形では再現できない - PutEvents は 200 で返る。失敗は
FailedEntryCountとEntries[].ErrorCodeに入るので、そこを見ないと投入できたと誤解する - source を変えて通したテストは、本番のイベントパターンが実イベントに一致するかの検証にはならない