AWS SDK for Go を触ってみる。
有効な関数ハンドラは以下のページに記載されている通り。
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/go-programming-model-handler-types.html#go-programming-model-handler-types-signatures
例)
package main import ( "github.com/aws/aws-lambda-go/lambda" ) func hello() (string, error) { return "Hello World!", nil } func main() { lambda.Start(hello) }
API Gateway用のLambdaで Lambdaプロキシ統合を使用する場合、
リクエストデータの受け取りには events.APIGatewayProxyRequest を、レスポンスデータの設定には events.APIGatewayProxyResponse を使用する。
https://github.com/aws/aws-lambda-go/blob/master/events/apigw.go
上記URLを見る限り、各パラメータはそれぞれ以下の通り取得できると思われる。
パラメータ種別 | 取得方法 | 補足 |
GETパラメータ | APIGatewayProxyRequest.QueryStringParameters | |
POSTデータ | APIGatewayProxyRequest.Body | 恐らく文字列として取得される為、JSONエンコードが必要? |
PATHパラメータ | APIGatewayProxyRequest.PathParameters | |
リソース名 | APIGatewayProxyRequest.Resource |
sample.go
package main import ( "fmt" "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: fmt.Sprintf("Hello %s!", request.QueryStringParameters["name"]), StatusCode: 200, }, nil } func main() { lambda.Start(handler) }
GOOS=linux GOARCH=amd64 go build -o sample sample.go
AWSTemplateFormatVersion: 2010-09-09 Transform: AWS::Serverless-2016-10-31 Resources: SampleFunction: Type: AWS::Serverless::Function Properties: Handler: sample Runtime: go1.x Tracing: Active Events: GetEvent: Type: Api Properties: Path: / Method: get
sample_request.json
{ "path": "/", "httpMethod": "GET", "queryStringParameters": { "name": "Taro" } }
イベントJSONの内容を入力として invoke
sam local invoke "SampleFunction" -e sample_request.json
標準入力からリクエストデータを渡す場合は -e に "-" を指定する。(JSONを作りたくない場合)
echo '{"path": "/", "httpMethod": "GET", "queryStringParameters": {"name": "Taro"}}' | sam local invoke -e "-" "SampleFunction"
ローカルでAPI Gatewayを起動
sam local start-api --template template.yml
動作確認
curl http://127.0.0.1:3000/?name=Taro Hello Taro!
GOPATHを通しておく
export GOPATH=/usr/local/Cellar/go/1.13.5
必要なライブラリをgo getしておく
go get github.com/aws/aws-lambda-go/lambda
ビルド
GOOS=linux GOARCH=amd64 go build -o main main.go
#TODO: aws cloudformation package と deploy を使用する場合はzip圧縮等も自動でやってくれる?(未確認)
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-go-how-to-create-deployment-package.html
package main import ( "fmt" "encoding/json" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) type response struct { Name string `json:"name"` Message string `json:message` } func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { res := &response{ Name: request.QueryStringParameters["name"], Message: fmt.Sprintf("Hello %s!", request.QueryStringParameters["name"]), } body, err := json.Marshal(res) fmt.Printf("%+v", string(body)) if err != nil { return events.APIGatewayProxyResponse{}, err } return events.APIGatewayProxyResponse{ Headers: map[string]string{ "Content-Type": "application/json", }, Body: string(body), StatusCode: 200, }, nil } func main() { lambda.Start(handler) }
https://docs.aws.amazon.com/sdk-for-go/api/service/s3/
package main import ( "fmt" "encoding/json" "strings" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/aws" ) type response struct { Objects []string `json:objects` } var svc *s3.S3 var mybucket = "bucket.example" /** * 初期処理 */ func init() { svc = s3.New(session.New()) } /** * Lambdaハンドラ */ func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { // バケットにオブジェクトを追加 putInput := &s3.PutObjectInput{ Body: aws.ReadSeekCloser(strings.NewReader("test1 text!")), Bucket: aws.String(mybucket), Key: aws.String("test1.txt"), } putResult, putErr := svc.PutObject(putInput) if putErr != nil { fmt.Printf("Put Error!\n") fmt.Printf("%+v\n", putErr) } else { fmt.Printf("Put Success!\n") fmt.Printf("%+v\n", putResult) } // バケット内のオブジェクトを取得 getInput := &s3.GetObjectInput{ Bucket: aws.String(mybucket), Key: aws.String("test1.txt"), } getResult, getErr := svc.GetObject(getInput) if getErr != nil { fmt.Printf("Get Error!\n") fmt.Printf("%+v\n", getErr) } else { fmt.Printf("Get Success!\n") fmt.Printf("%+v\n", getResult) } // バケット内のオブジェクト一覧を取得 listInput := &s3.ListObjectsV2Input{Bucket: aws.String(mybucket)} result, _ := svc.ListObjectsV2(listInput) myObjects := result.Contents objects := []string{} for i := 0; i < len(myObjects); i++ { objects = append(objects, *myObjects[i].Key) } // レスポンスデータの設定 body, err := json.Marshal(&response{Objects: objects}) if err != nil { return events.APIGatewayProxyResponse{}, err } else { return events.APIGatewayProxyResponse{ Headers: map[string]string{ "Content-Type": "application/json", }, Body: string(body), StatusCode: 200, }, nil } } func main() { lambda.Start(handler) }
テーブル名:SampleTable、パーティションキー: pkey、ソートキー: skey とした場合の、登録(PutItem)、検索(GetItem、Query)の例。
※以下 scan や BatchXXX 系の例は記載しないが、Python 等と同じくメソッドは用意されている模様。
参考: https://docs.aws.amazon.com/sdk-for-go/api/service/dynamodb/
package main import ( "fmt" "encoding/json" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/dynamodb" "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute" "github.com/aws/aws-sdk-go/aws" ) type response struct { Items []Record `json:items` } type Record struct { Pkey string `json:pkey` Skey string `json:skey` Value string `json:value` } var svc *dynamodb.DynamoDB var mytable = "SampleTable" /** * 初期処理 */ func init() { svc = dynamodb.New(session.New()) } /** * Lambdaハンドラ */ func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { //------------------------------- // PutItem //------------------------------- putInput := &dynamodb.PutItemInput{ Item: map[string]*dynamodb.AttributeValue{ "pkey": {S: aws.String("test1")}, "skey": {S: aws.String("101")}, "value": {S: aws.String("sample data!")}, }, ReturnConsumedCapacity: aws.String("TOTAL"), TableName: aws.String(mytable), } putResult, putErr := svc.PutItem(putInput) if putErr != nil { fmt.Printf("Put Error!\n") fmt.Printf("%+v\n", putErr) } else { fmt.Printf("Put Success!\n") fmt.Printf("%+v\n", putResult) } //------------------------------- // GetItem //------------------------------- getInput := &dynamodb.GetItemInput{ Key: map[string]*dynamodb.AttributeValue{ "pkey": {S: aws.String("test1")}, "skey": {S: aws.String("101")}, }, TableName: aws.String(mytable), } getResult, getErr := svc.GetItem(getInput) if getErr != nil { fmt.Printf("Get Error!\n") fmt.Printf("%+v\n", getErr) } else { fmt.Printf("Get Success!\n") fmt.Printf("%+v\n", getResult) } //------------------------------- // Query //------------------------------- items := []Record{} queryInput := &dynamodb.QueryInput{ ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{ ":key": {S: aws.String("test1")}, }, KeyConditionExpression: aws.String("pkey = :key"), TableName: aws.String(mytable), } queryResult, queryErr := svc.Query(queryInput) if queryErr != nil { fmt.Printf("Query Error!\n") fmt.Printf("%+v\n", queryErr) } else { fmt.Printf("Query Success!\n") fmt.Printf("%+v\n", queryResult) // 取得結果の構造体への変換は dynamodbattribute を使用して行う事ができる // https://docs.aws.amazon.com/sdk-for-go/api/service/dynamodb/dynamodbattribute/ err := dynamodbattribute.UnmarshalListOfMaps(queryResult.Items, &items) if err != nil { panic(fmt.Sprintf("failed to unmarshal Dynamodb Scan Items, %v", err)) } } //------------------------------- // レスポンスデータの設定 //------------------------------- body, err := json.Marshal(&response{Items: items}) if err != nil { return events.APIGatewayProxyResponse{}, err } else { return events.APIGatewayProxyResponse{ Headers: map[string]string{ "Content-Type": "application/json", }, Body: string(body), StatusCode: 200, }, nil } } func main() { lambda.Start(handler) }
任意の Credential を指定する場合は credentials.NewChainCredentials などが利用できる。
ec2metrics.go
package main import ( "fmt" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awsutil" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/cloudwatch" ) func main() { region := "ap-northeast-1" accessKeyId := "XXXXXXXXXXXXXXXXXXXXXX" secretAccessKey := "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" value, err := getAllMetrics(region, accessKeyId, secretAccessKey) fmt.Printf("%+v", value) if err != nil{ fmt.Printf("Error!") } } /** * メトリクスの一覧を取得する. */ func getAllMetrics(region string, accessKeyId string, secretAccessKey string) (cloudwatch.ListMetricsOutput, error) { namespace := "AWS/EC2" //metric := "CPUUtilization" creds, err := GetCredentials(accessKeyId, secretAccessKey) if err != nil { return cloudwatch.ListMetricsOutput{}, err } cfg := &aws.Config{ Region: aws.String(region), Credentials: creds, } sess, err := session.NewSession(cfg) if err != nil { return cloudwatch.ListMetricsOutput{}, err } svc := cloudwatch.New(sess, cfg) params := &cloudwatch.ListMetricsInput{ Namespace: aws.String(namespace), //MetricName: aws.String(metric), } var resp cloudwatch.ListMetricsOutput err = svc.ListMetricsPages(params, func(page *cloudwatch.ListMetricsOutput, lastPage bool) bool { metrics, _ := awsutil.ValuesAtPath(page, "Metrics") for _, metric := range metrics { resp.Metrics = append(resp.Metrics, metric.(*cloudwatch.Metric)) } return !lastPage }) if err != nil { return resp, err } return resp, err } /** * クレデンシャル取得. */ func GetCredentials(accessKeyId string, secretAccessKey string) (*credentials.Credentials, error) { creds := credentials.NewChainCredentials( []credentials.Provider{ &credentials.StaticProvider{Value: credentials.Value{ AccessKeyID: accessKeyId, SecretAccessKey: secretAccessKey, }}, //&credentials.EnvProvider{}, // 環境変数から取得? }) return creds, nil }