#author("2019-12-30T12:29:12+00:00","","") #mynavi(AWSメモ) #setlinebreak(on); * 概要 [#baf9a431] #html(<div class="pl10">) [[AWS SDK for Go>https://aws.amazon.com/jp/sdk-for-go/]] を触ってみる。 #html(</div>) * 目次 [#n8f6e596] #contents - 関連 -- [[Go言語]] -- [[AWSメモ]] - 参考 -- https://aws.amazon.com/jp/sdk-for-go/ -- https://docs.aws.amazon.com/lambda/latest/dg/go-programming-model.html -- https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/go-programming-model-handler-types.html -- https://github.com/aws/aws-lambda-go -- https://github.com/sbstjn/go-lambda-example * Go Lambda のハンドラ [#r9c95b8b] #html(<div class="pl10">) ** 基本形 [#mabb1221] #html(<div class="pl10">) 有効な関数ハンドラは以下のページに記載されている通り。 https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/go-programming-model-handler-types.html#go-programming-model-handler-types-signatures 例) #mycode2(){{ package main import ( "github.com/aws/aws-lambda-go/lambda" ) func hello() (string, error) { return "Hello World!", nil } func main() { lambda.Start(hello) } }} #html(</div>) ** Lambdaプロキシ統合を使用する場合 [#t0777c47] #html(<div class="pl10">) API Gateway用のLambdaで Lambdaプロキシ統合を使用する場合、 リクエストデータの受け取りには events.APIGatewayProxyRequest を、レスポンスデータの設定には events.APIGatewayProxyResponse を使用する。 https://github.com/aws/aws-lambda-go/blob/master/events/apigw.go 上記URLを見る限り、各パラメータはそれぞれ以下の通り取得できると思われる。 | パラメータ種別 | 取得方法 | 補足 |h | GETパラメータ | APIGatewayProxyRequest.QueryStringParameters || | POSTデータ | APIGatewayProxyRequest.Body | 恐らく文字列として取得される為、JSONエンコードが必要? | | PATHパラメータ | APIGatewayProxyRequest.PathParameters || | リソース名 | APIGatewayProxyRequest.Resource || sample.go #mycode2(){{ 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) } }} #html(</div>) #html(</div>) * SAM CLI によるローカルでの動作確認 [#he04f37f] #html(<div class="pl10">) ** ハンドラをビルドしておく [#fcc6d4af] #html(<div class="pl10">) #myterm2(){{ GOOS=linux GOARCH=amd64 go build -o sample sample.go }} #html(</div>) ** template.yml の作成 [#n66669fb] #html(<div class="pl10">) #mycode2(){{ 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 }} #html(</div>) ** SAM CLI で Lambda をinvoke [#p86fb77d] #html(<div class="pl10">) sample_request.json #mycode2(){{ { "path": "/", "httpMethod": "GET", "queryStringParameters": { "name": "Taro" } } }} イベントJSONの内容を入力として invoke #myterm2(){{ sam local invoke "SampleFunction" -e sample_request.json }} 標準入力からリクエストデータを渡す場合は -e に "-" を指定する。(JSONを作りたくない場合) #myterm2(){{ echo '{"path": "/", "httpMethod": "GET", "queryStringParameters": {"name": "Taro"}}' | sam local invoke -e "-" "SampleFunction" }} #html(</div>) ** SAM CLI で API を起動 [#y602bcbb] #html(<div class="pl10">) ローカルでAPI Gatewayを起動 #myterm2(){{ sam local start-api --template template.yml }} 動作確認 #myterm2(){{ curl http://127.0.0.1:3000/?name=Taro Hello Taro! }} #html(</div>) #html(</div>) * ビルド [#w404a51e] #html(<div class="pl10">) GOPATHを通しておく #myterm2(){{ export GOPATH=/usr/local/Cellar/go/1.13.5 }} 必要なライブラリをgo getしておく #myterm2(){{ go get github.com/aws/aws-lambda-go/lambda }} ビルド #myterm2(){{ GOOS=linux GOARCH=amd64 go build -o main main.go }} #html(</div>) * パッケージングとデプロイ [#j446ef26] #html(<div class="pl10">) #TODO: aws cloudformation package と deploy を使用する場合はzip圧縮等も自動でやってくれる?(未確認) https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-go-how-to-create-deployment-package.html #html(</div>) * 実装メモ [#d8c9b220] #html(<div class="pl10">) ** JSONデータを返却する [#o5b0e485] #html(<div class="pl10">) #mycode2(){{ 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) } }} #html(</div>) ** S3へのアクセス [#t40d3423] #html(<div class="pl10">) https://docs.aws.amazon.com/sdk-for-go/api/service/s3/ #mycode2(){{ 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) } }} #html(</div>) ** DynamoDBへのアクセス [#p18da634] #html(<div class="pl10">) テーブル名:SampleTable、パーティションキー: pkey、ソートキー: skey とした場合の、登録(PutItem)、検索(GetItem、Query)の例。 ※以下 scan や BatchXXX 系の例は記載しないが、Python 等と同じくメソッドは用意されている模様。 参考: https://docs.aws.amazon.com/sdk-for-go/api/service/dynamodb/ #mycode2(){{ 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) } }} #html(</div>) ** Credentialの取得 [#pb6ba495] #html(<div class="pl10">) 任意の Credential を指定する場合は credentials.NewChainCredentials などが利用できる。 ec2metrics.go #mycode2(){{ 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 } }} #html(</div>) #html(</div>)