💨

AWS SAM CLI「sam remote invoke」コマンドでクラウド上のLambda関数をテストする

2023/08/12に公開

はじめに

SAM CLIのバージョン1.88.0が2023年6月23日にリリースされ、Lambda関数のリモート呼び出し機能が追加されました。これにより、クラウドにデプロイされたLambda関数をSAM CLIのコマンドsam remote invokeでテストできるようになりました。このコマンドには様々なオプションが追加可能であり、複数の呼び出しモードがサポートされています。

レスポンスストリーミング、非同期呼び出し、ドライラン、リクエスト/レスポンスなど、複数の呼び出しモードがサポートされています。
AWS公式より引用

これまでLambda関数のテストは主にAWSコンソール画面もしくはAWS CLIを使用していましたが、新たな選択肢として、sam syncコマンドとsam remote invokeコマンドを組み合わせた方法が紹介されています。sam syncコマンドを使うことでローカルとリモートを同期させながら開発できるため、構築、デプロイ、テストの反復ループを効率的に高速化できます。

事前準備

sam cliコマンドで環境構築します。
今回検証したバージョンです。

$ sam --version
SAM CLI, version 1.93.0

sam initコマンドを使用して、テンプレートとして「Hello World Example」を選択し、言語としてGoを選びました。

$ sam init

You can preselect a particular runtime or package type when using the `sam init` experience.
Call `sam init --help` to learn more.

Which template source would you like to use?
	1 - AWS Quick Start Templates
	2 - Custom Template Location
Choice: 1

Choose an AWS Quick Start application template
	1 - Hello World Example
	2 - Data processing
	3 - Hello World Example with Powertools for AWS Lambda
	4 - Multi-step workflow
	5 - Scheduled task
	6 - Standalone function
	7 - Serverless API
	8 - Infrastructure event management
	9 - Lambda Response Streaming
	10 - Serverless Connector Hello World Example
	11 - Multi-step workflow with Connectors
	12 - Full Stack
	13 - Lambda EFS example
	14 - Hello World Example With Powertools
	15 - DynamoDB Example
	16 - Machine Learning
Template: 1

Use the most popular runtime and package type? (Python and zip) [y/N]: N

Which runtime would you like to use?
	1 - aot.dotnet7 (provided.al2)
	2 - dotnet6
	3 - go1.x
	4 - go (provided.al2)
	5 - graalvm.java11 (provided.al2)
	6 - graalvm.java17 (provided.al2)
	7 - java17
	8 - java11
	9 - java8.al2
	10 - java8
	11 - nodejs18.x
	12 - nodejs16.x
	13 - nodejs14.x
	14 - nodejs12.x
	15 - python3.9
	16 - python3.8
	17 - python3.7
	18 - python3.11
	19 - python3.10
	20 - ruby3.2
	21 - ruby2.7
	22 - rust (provided.al2)
Runtime: 3

作成されたアプリは、API GatewayとLambdaのシンプルな構成となっています。手順に従うと、「sam-app/template.yaml」ファイルにシステム構成が定義されていますので、詳細を知りたい方はこのファイルを参照してください。

sam remote invokeコマンド試す

Lambda関数

main.go
package main

import (
	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
)

func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	return events.APIGatewayProxyResponse{
		Body:       request.Body,
		StatusCode: 200,
	}, nil
}

func main() {
	lambda.Start(handler)
}

API Gatewayを介して送信されるリクエストに対して、返却処理を実装しています。ローカルで簡単に確認するためには、「events/events.json」ファイルにsam initによって予め用意されているAPI Gatewayを通じてリクエストされるテストデータを利用することがおすすめです。

$ sam local invoke HelloWorldFunction --event events/event.json

↓出力結果
{"statusCode":200,"headers":null,"multiValueHeaders":null,"body":"{\"message\": \"hello world\"}"}

ビルドとデプロイ

$ sam build --use-container
$ sam deploy --guided

sam deploy guidedコマンドを実行する際に表示される質問については、すべて「yes」またはデフォルトの回答で進行して問題ありません。

sam syncで確認

ここからは、ローカルとクラウドを同期させ、迅速にクラウド上でテストを試せる手順を体験します。まず最初に、同期を行うためのコマンドを実行します。

$ sam sync --stack-name sample-app --watch

同期が完了したら、テストを実行します。
テストデータが「events/event.json」に用意されていますので、コマンドのオプションでそれを使用して確認します。

$ sam remote invoke HelloWorldFunction --stack-name sam-app --event-file events/event.json

Invoking Lambda Function HelloWorldFunction                                                                                                                                                                       
START RequestId: 7ef6f15f-6194-43d7-8252-359abdc7b0a2 Version: $LATEST
END RequestId: 7ef6f15f-6194-43d7-8252-359abdc7b0a2
REPORT RequestId: 7ef6f15f-6194-43d7-8252-359abdc7b0a2  Duration: 21.95 ms      Billed Duration: 22 ms  Memory Size: 128 MB     Max Memory Used: 30 MB
{"statusCode":200,"headers":null,"multiValueHeaders":null,"body":"{\"message\": \"hello world\"}"}%

値が正しく出力されていることを確認できました。
ローカルでの修正をクラウド上ですぐに確認する手応えを感じるために、Lambda関数を修正します。
修正内容は、返却部分にリクエスト時間を含めるようにすることです。

go
package main

import (
	"fmt"
	"time"

	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
)

func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	formattedTime, err := formatRequestTime(request.RequestContext.RequestTime)
	if err != nil {
		return errorResponse(err), nil
	}

	responseBody := fmt.Sprintf("requestTime:%v, body: %v", formattedTime, request.Body)
	return successResponse(responseBody), nil
}

func formatRequestTime(t string) (string, error) {
	const inputLayout = "02/Jan/2006:15:04:05 -0700"
	parsedTime, err := time.Parse(inputLayout, t)
	if err != nil {
		return "", err
	}

	const outputLayout = "2006/01/02 15:04" // YYYY/mm/dd HH:ii 形式
	return parsedTime.Format(outputLayout), nil
}

func errorResponse(err error) events.APIGatewayProxyResponse {
	return events.APIGatewayProxyResponse{
		Body:       err.Error(),
		StatusCode: 500,
	}
}

func successResponse(body string) events.APIGatewayProxyResponse {
	return events.APIGatewayProxyResponse{
		Body:       body,
		StatusCode: 200,
	}
}

func main() {
	lambda.Start(handler)
}

ローカルでコードを変更して保存すると、数秒でAWS上のLambda関数にも変更が反映されます。

Syncing Lambda Function HelloWorldFunction...                                                                                                                                                                     
Cache is invalid, running build and copying resources for following functions (HelloWorldFunction)                                                                                                                
Building codeuri: /Users/XXXXX/go/src/github.com/XXXXX/sam-app/hello-world runtime: go1.x metadata: {} architecture: x86_64 functions: HelloWorldFunction                                                   
Running GoModulesBuilder:Build                                                                                                                                                                                    
Updated source_hash and manifest_hash field in build.toml for function with UUID e4859b3c-1a01-4c4d-96fa-31b662f3c746                                                                                             
Finished syncing Lambda Function HelloWorldFunction. 

修正したコードのテストを再度確認します。

sam remote invoke HelloWorldFunction --stack-name sam-app --event-file events/event.json
Invoking Lambda Function HelloWorldFunction                                                                                                                                                                       
START RequestId: a2efc0cb-c713-4bce-91af-c6d69ae454db Version: $LATEST
END RequestId: a2efc0cb-c713-4bce-91af-c6d69ae454db
REPORT RequestId: a2efc0cb-c713-4bce-91af-c6d69ae454db  Duration: 6.05 ms       Billed Duration: 7 ms   Memory Size: 128 MB     Max Memory Used: 30 MB
{"statusCode":200,"headers":null,"multiValueHeaders":null,"body":"requestTime:2015/04/09 12:34, body: {\"message\": \"hello world\"}"}%   

問題なく出力されていることを確認しました。

おわりに

今回は、AWS SAM CLIのリモート呼び出し機能について説明しました。また、sam remote invokeコマンドにはさまざまなオプションがあり、詳細を知りたい場合は公式ドキュメントをご参照ください。

https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-remote-invoke.html

Discussion