[アップデート]AWS SAM CLIでAWS環境上のLambda関数のテストイベントを操作できるようになりました

2023.10.04

初めに

本日AWS SAM CLI v1.98.0がリリースされました。

今回のアップデートではsam remoteコマンドに新たなサブコマンドtest-eventが追加されSAM経由でAWS環境上のLambda関数のテストイベントを操作できるようになります。

また合わせてsam remote invokeコマンドに新たなオプション--test-event-nameが追加されAWS環境上に設置されているイベントデータを使えるようになっております。

remote invokeコマンドについては今年の6月ごろのアップデート追加されたサブコマンドでSAMを通してローカルではなく実際のAWS環境上のLambda関数を実行できる機能となります。

以前はsam remote invokeは実際にデプロイされたAWS環境上の関数を呼び出すことができたのですがテストイベントは取り扱うことができず、イベントデータに依存する関数の場合はローカルにそのデータを準備して実行する必要がありました。

そのため運用等の兼ね合いでマネージメントコンソール上で作成されたイベントが欲しい場合や、複数人で共有したい場合はSAM外の別の手段を用意する必要がありこの点がネックとなっておりました。

今回のアップデートによってローカル・リモート間でのテストデータのやり取りがSAM上で完結できるようになりよりスムーズに開発検証が行えるようになります。

なお普段AWS側に上がらないことの多いSAMのアップデートですが珍しく本機能はWhat's newにも取り上げられております。

追加されたコマンド

4種類のサブコマンドが追加されています。

  • sam remote test-event list
    • AWS環境上のテストイベントの一覧を取得
  • sam remote test-event put
    • ローカルからAWS環境上にテストイベントをアップロード
  • sam remote test-event get
    • AWS環境上の特定のテストイベントを取得
  • sam remote test-event delete
    • AWS環境上の特定のテストイベントを削除する

またsam remote invokeに対して追加されたオプションとしては--test-event-nameとなり、こちらにAWS環境上に保存されているイベント名を指定することでそのイベントを利用しての実行が可能です。

試してみる

サンプルのHello Wolrdの関数をデプロイして実行してみます。

SAMには既存コマンドでローカル上でテストイベントデータを作成するsam local generate-eventがあるのでこちらでイベントを作成してアップロードします。

# デプロイ直後は何もイベントがない。空の結果が返却されるのではなくエラーとなるらしい
% sam remote test-event list                     
Error: lambda-testevent-schemas registry not found. There are no saved events.
# アップロードのためにローカル上でAPI Gateway経由のアクセスのイベントをファイルに保存する(既存コマンド)
% sam local generate-event apigateway aws-proxy > api-event.json
# 先ほどのファイルを指定してアップロードする
% sam remote test-event put --name simple-api-event --file events/api-event.json
Put remote event 'simple-api-event' completed successfully
# AWS環境上に先ほどのイベントが上がっていることが確認できる
# listは名前のみの一覧
% sam remote test-event list
simple-api-event
# getでは具体的なイベントの中身が見られるので保存が必要ならリダイレクトでよしなにする。
% sam remote test-event get --name simple-api-event
{"body": "eyJ0ZXN0IjoiYm9keSJ9", "resource": "/{proxy+}", "path": "/path/to/resource", "httpMethod": "POST", "isBase64Encoded": true, "queryStringParameters": {"foo": "bar"}, "multiValueQueryStringParameters": {"foo": ["bar"]}, "pathParameters": {"proxy": "/path/to/resource"}, "stageVariables": {"baz": "qux"}, "headers": {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Encoding": "gzip, deflate, sdch", "Accept-Language": "en-US,en;q=0.8", "Cache-Control": "max-age=0", "CloudFront-Forwarded-Proto": "https", "CloudFront-Is-Desktop-Viewer": "true", "CloudFront-Is-Mobile-Viewer": "false", "CloudFront-Is-SmartTV-Viewer": "false", "CloudFront-Is-Tablet-Viewer": "false", "CloudFront-Viewer-Country": "US", "Host": "1234567890.execute-api.us-east-1.amazonaws.com", "Upgrade-Insecure-Requests": "1", "User-Agent": "Custom User Agent String", "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", "X-Forwarded-For": "127.0.0.1, 127.0.0.2", "X-Forwarded-Port": "443", "X-Forwarded-Proto": "https"}, "multiValueHeaders": {"Accept": ["text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"], "Accept-Encoding": ["gzip, deflate, sdch"], "Accept-Language": ["en-US,en;q=0.8"], "Cache-Control": ["max-age=0"], "CloudFront-Forwarded-Proto": ["https"], "CloudFront-Is-Desktop-Viewer": ["true"], "CloudFront-Is-Mobile-Viewer": ["false"], "CloudFront-Is-SmartTV-Viewer": ["false"], "CloudFront-Is-Tablet-Viewer": ["false"], "CloudFront-Viewer-Country": ["US"], "Host": ["0123456789.execute-api.us-east-1.amazonaws.com"], "Upgrade-Insecure-Requests": ["1"], "User-Agent": ["Custom User Agent String"], "Via": ["1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)"], "X-Amz-Cf-Id": ["cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA=="], "X-Forwarded-For": ["127.0.0.1, 127.0.0.2"], "X-Forwarded-Port": ["443"], "X-Forwarded-Proto": ["https"]}, "requestContext": {"accountId": "123456789012", "resourceId": "123456", "stage": "prod", "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", "requestTime": "09/Apr/2015:12:34:56 +0000", "requestTimeEpoch": 1428582896000, "identity": {"cognitoIdentityPoolId": null, "accountId": null, "cognitoIdentityId": null, "caller": null, "accessKey": null, "sourceIp": "127.0.0.1", "cognitoAuthenticationType": null, "cognitoAuthenticationProvider": null, "userArn": null, "userAgent": "Custom User Agent String", "user": null}, "path": "/prod/path/to/resource", "resourcePath": "/{proxy+}", "httpMethod": "POST", "apiId": "1234567890", "protocol": "HTTP/1.1"}}

AWS環境上を確認すると作成したイベントがアップロードされていることが確認できます。

少し不便な点としてはputの際は上記のように改行やインデントを保持したままアップロードされているようですが、getで取得されたデータには改行やインデントがなくなってしまっているのでデータ次第で手で触るなら一度整形しないといけない点です(大元のAPIの仕様のせいな気がする)。

せっかくなのでremote invoke経由でアップロードしたイベントを使った実行を試してみます。

返却するmessage値を"Hello World"固定のメッセージではなくeventを返すようにしています。

# 特に指定しないとイベントがないのでmessageの値は空
$ sam remote invoke
Invoking Lambda Function HelloWorldFunction                                                                                                                                            
START RequestId: e6fb9028-7ca3-43f2-8ec6-98266989c795 Version: $LATEST
END RequestId: e6fb9028-7ca3-43f2-8ec6-98266989c795
REPORT RequestId: e6fb9028-7ca3-43f2-8ec6-98266989c795  Duration: 1.40 ms       Billed Duration: 2 ms   Memory Size: 128 MB     Max Memory Used: 36 MB  Init Duration: 120.34 ms
{"statusCode": 200, "body": "{\"message\": {}}"}%
# 先ほど指定したイベント名を指定するとその内容が出力されていることが確認できる。
$ sam remote invoke --test-event-name simple-api-event
Invoking Lambda Function HelloWorldFunction                                                                                                                                            
START RequestId: 4fc102a5-7139-4527-9871-b3a616a44191 Version: $LATEST
END RequestId: 4fc102a5-7139-4527-9871-b3a616a44191
REPORT RequestId: 4fc102a5-7139-4527-9871-b3a616a44191  Duration: 19.65 ms      Billed Duration: 20 ms  Memory Size: 128 MB     Max Memory Used: 37 MB
{"statusCode": 200, "body": "{\"message\": {\"body\": \"eyJ0ZXN0IjoiYm9keSJ9\", \"resource\": \"/{proxy+}\", \"path\": \"/path/to/resource\", \"httpMethod\": \"POST\", \"isBase64Encoded\": true, \"queryStringParameters\": {\"foo\": \"bar\"}, \"multiValueQueryStringParameters\": {\"foo\": [\"bar\"]}, \"pathParameters\": {\"proxy\": \"/path/to/resource\"}, \"stageVariables\": {\"baz\": \"qux\"}, \"headers\": {\"Accept\": \"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\", \"Accept-Encoding\": \"gzip, deflate, sdch\", \"Accept-Language\": \"en-US,en;q=0.8\", \"Cache-Control\": \"max-age=0\", \"CloudFront-Forwarded-Proto\": \"https\", \"CloudFront-Is-Desktop-Viewer\": \"true\", \"CloudFront-Is-Mobile-Viewer\": \"false\", \"CloudFront-Is-SmartTV-Viewer\": \"false\", \"CloudFront-Is-Tablet-Viewer\": \"false\", \"CloudFront-Viewer-Country\": \"US\", \"Host\": \"1234567890.execute-api.us-east-1.amazonaws.com\", \"Upgrade-Insecure-Requests\": \"1\", \"User-Agent\": \"Custom User Agent String\", \"Via\": \"1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)\", \"X-Amz-Cf-Id\": \"cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==\", \"X-Forwarded-For\": \"127.0.0.1, 127.0.0.2\", \"X-Forwarded-Port\": \"443\", \"X-Forwarded-Proto\": \"https\"}, \"multiValueHeaders\": {\"Accept\": [\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\"], \"Accept-Encoding\": [\"gzip, deflate, sdch\"], \"Accept-Language\": [\"en-US,en;q=0.8\"], \"Cache-Control\": [\"max-age=0\"], \"CloudFront-Forwarded-Proto\": [\"https\"], \"CloudFront-Is-Desktop-Viewer\": [\"true\"], \"CloudFront-Is-Mobile-Viewer\": [\"false\"], \"CloudFront-Is-SmartTV-Viewer\": [\"false\"], \"CloudFront-Is-Tablet-Viewer\": [\"false\"], \"CloudFront-Viewer-Country\": [\"US\"], \"Host\": [\"0123456789.execute-api.us-east-1.amazonaws.com\"], \"Upgrade-Insecure-Requests\": [\"1\"], \"User-Agent\": [\"Custom User Agent String\"], \"Via\": [\"1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)\"], \"X-Amz-Cf-Id\": [\"cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==\"], \"X-Forwarded-For\": [\"127.0.0.1, 127.0.0.2\"], \"X-Forwarded-Port\": [\"443\"], \"X-Forwarded-Proto\": [\"https\"]}, \"requestContext\": {\"accountId\": \"123456789012\", \"resourceId\": \"123456\", \"stage\": \"prod\", \"requestId\": \"c6af9ac6-7b61-11e6-9a41-93e8deadbeef\", \"requestTime\": \"09/Apr/2015:12:34:56 +0000\", \"requestTimeEpoch\": 1428582896000, \"identity\": {\"cognitoIdentityPoolId\": null, \"accountId\": null, \"cognitoIdentityId\": null, \"caller\": null, \"accessKey\": null, \"sourceIp\": \"127.0.0.1\", \"cognitoAuthenticationType\": null, \"cognitoAuthenticationProvider\": null, \"userArn\": null, \"userAgent\": \"Custom User Agent String\", \"user\": null}, \"path\": \"/prod/path/to/resource\", \"resourcePath\": \"/{proxy+}\", \"httpMethod\": \"POST\", \"apiId\": \"1234567890\", \"protocol\": \"HTTP/1.1\"}}}"}%

最後に設置したテストイベントを消しておきます。

% sam remote test-event delete --name simple-api-event
Delete remote event 'simple-api-event' completed successfully
% sam remote test-event list
Error: No events found for function HelloWorldFunction

なお今回は単一の関数のみが存在するテンプレートだったので特別指定は行っていなかったのですが、複数関数があるような環境では実行対象の関数名を指定して実行する必要があります。

 % sam remote test-event list HelloWorldFunction                                 
simple-api-event

終わりに

今回のアップデートは複数人開発を行っている場合では非常に嬉しい機能ではないでしょうか。

remote invokeを利用している場合はより便利にできますが、特に利用していなくとも開発メンバー間、運用・開発間のメンバーのデータ受け渡し等様々な場面でサクッとデータのやり取りができます(実際の環境に上がるのでそれがいいかはどうかは別として)。

これでチャットツール側の不要な整形やコピペミス等に怯える必要もありません。