AWS SDK for Go V2 を使ってみる

自分用メモです。
なお、Go について詳しいなどではないため、誤りなどがあればご了承ください。

# 環境

+ Mac OS X 12.6.1
+ Go 19.4
+ `aws configure` コマンドを実行し、認証情報は設定済み

# Go のインストール

homebrew を使って入れる

```
$brew install go

$which go
/usr/local/bin/go

$go version
go version go1.19.4 darwin/amd64
```

# とりあえず HelloWorld

[サンプルで学ぶ Go 言語：Hello World](https://www.spinute.org/go-by-example/hello-world)

```
package main
import "fmt"
func main() {
    fmt.Println("hello world")
}
```

```
# 実行
$go run hello-world.go
hello world

# ビルド
$go build hello-world.go
$ls
hello-world    hello-world.go

# バイナリの実行
$./hello-world
hello world
```

# Getting Started を試す

以下を試す

[Getting Started with the AWS SDK for Go V2](https://aws.github.io/aws-sdk-go-v2/docs/getting-started/)


## Install the AWS SDK for Go V2

AWS SDK for Go V2 は Go モジュールを使っており、ローカルプロジェクトの initialize には `go mod init`コマンドを使うようなのでその通りやってみる

```
# フォルダ作成および移動
$mkdir example
$cd example

# go mod init 実行
$go mod init example
go: creating new go.mod: module example

# 確認
$ls
go.mod

$cat go.mod
module example

go 1.19

```

`go.mod` というファイルが作成され、モジュール名、Go のバージョンが記載されている。

次に `go get` を使って SDK および依存するモジュールのスタンダードセットを取得する。

```
$go get github.com/aws/aws-sdk-go-v2
go: added github.com/aws/aws-sdk-go-v2 v1.17.3

$go get github.com/aws/aws-sdk-go-v2/config
go: added github.com/aws/aws-sdk-go-v2/config v1.18.5
go: added github.com/aws/aws-sdk-go-v2/credentials v1.13.5
go: added github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21
go: added github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27
go: added github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21
go: added github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28
go: added github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21
go: added github.com/aws/aws-sdk-go-v2/service/sso v1.11.27
go: added github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.10
go: added github.com/aws/aws-sdk-go-v2/service/sts v1.17.7
```

改めてファイルを見てみる

```
# go.sum というファイルが増えている
$ls
go.mod go.sum


# go.mod には取得したもの、および依存するモジュールが記載
$cat go.mod
module example

go 1.19

require (
        github.com/aws/aws-sdk-go-v2 v1.17.3 // indirect
        github.com/aws/aws-sdk-go-v2/config v1.18.5 // indirect
        github.com/aws/aws-sdk-go-v2/credentials v1.13.5 // indirect
        github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 // indirect
        github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 // indirect
        github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 // indirect
        github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 // indirect
        github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 // indirect
        github.com/aws/aws-sdk-go-v2/service/sso v1.11.27 // indirect
        github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.10 // indirect
        github.com/aws/aws-sdk-go-v2/service/sts v1.17.7 // indirect
        github.com/aws/smithy-go v1.13.5 // indirect
)


# チェックサム用のファイルぽい
$cat go.sum
github.com/aws/aws-sdk-go-v2 v1.17.3 h1:shN7NlnV....
github.com/aws/aws-sdk-go-v2 v1.17.3/go.mod h1:u...

・・・（略)
```

次にプログラムで使う API クライアントのモジュールも取得する。
今回の例では S3 を扱うので S3 用のモジュールを取得する。

```
$go get github.com/aws/aws-sdk-go-v2/service/s3

go: added github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10
go: added github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.18
go: added github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11
go: added github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.22
go: added github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.21
go: added github.com/aws/aws-sdk-go-v2/service/s3 v1.29.6
```

## Get your AWS access keys

`aws configure` を実行済みであり、対応済みのためにパス

## Invoke an Operation

ドキュメント記載のコードをコピーする。
念の為、転機。

```
package main

import (
	"context"
	"log"
	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/s3"
)

func main() {
	// Load the Shared AWS Configuration (~/.aws/config)
	cfg, err := config.LoadDefaultConfig(context.TODO())
	if err != nil {
		log.Fatal(err)
	}

	// Create an Amazon S3 service client
	client := s3.NewFromConfig(cfg)

	// Get the first page of results for ListObjectsV2 for a bucket
	output, err := client.ListObjectsV2(context.TODO(), &s3.ListObjectsV2Input{
		Bucket: aws.String("my-bucket"),
	})
	if err != nil {
		log.Fatal(err)
	}

	log.Println("first page results:")
	for _, object := range output.Contents {
		log.Printf("key=%s size=%d", aws.ToString(object.Key), object.Size)
	}
}
```

ただし、この例だと以下のようになっており、`my-bucket` というのは利用する AWS アカウントになく、エラーになる。

```
	// Get the first page of results for ListObjectsV2 for a bucket
	output, err := client.ListObjectsV2(context.TODO(), &s3.ListObjectsV2Input{
		Bucket: aws.String("my-bucket"),
	})
	if err
```

そのため、上記だけ、利用する AWS アカウントで作成済みのバケット名に変更する。
変更後、以下を実行する。

```
# 実行
$go run s3.go
2022/12/19 05:29:25 first page results:
2022/12/19 05:29:25 key=1 size=145
2022/12/19 05:29:25 key=2 size=145
...

# ビルド
$go build s3.go

# バイナリの実行
$./s3
2022/12/19 05:31:08 first page results:
2022/12/19 05:31:08 key=1 size=145
2022/12/19 05:31:08 key=2 size=145
・・・
```

想定通りできた。

## ドキュメントみる

これだけだとつまらないので、今回利用した S3 の `ListObjectsV2` について確認してみる。

以下の `service` を開くと AWS のサービスごとにリンクがある。

[Go SDK](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2#section-readme)

今回、S3 なので s3 というモジュールのページを開く

[s3](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/s3)

ここを見ると s3 モジュールの情報があるようなので `ListObjectsV2` を確認する。

[ListObjectV2](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/s3#Client.ListObjectsV2)

>func (c *Client) ListObjectsV2(ctx context.Context, params *ListObjectsV2Input, optFns ...func(*Options)) (*ListObjectsV2Output, error)

上記のように何を渡せば良いかなどが書いてある。
上記を踏まえ、今回のサンプルを見てみる。

```
	// Get the first page of results for ListObjectsV2 for a bucket
	output, err := client.ListObjectsV2(context.TODO(), &s3.ListObjectsV2Input{
		Bucket: aws.String("my-bucket"),
	})
```

これを踏まえると、`ListObjectsV2Input` にてバケット名を指定していることが分かる。
他にどのような値を指定できるかというのは `ListObjectsV2Input` を確認すれば良さそうである。

[ListObjectsV2Input](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/s3#ListObjectsV2Input)

```
type ListObjectsV2Input struct {

	// Bucket name to list. When using this action with an access point, you must
	// direct requests to the access point hostname. The access point hostname takes
	// the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When
	// using this action with an access point through the Amazon Web Services SDKs, you
	// provide the access point ARN in place of the bucket name. For more information
	// about access point ARNs, see Using access points
	// (https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-access-points.html)
	// in the Amazon S3 User Guide. When using this action with Amazon S3 on Outposts,
	// you must direct requests to the S3 on Outposts hostname. The S3 on Outposts
	// hostname takes the form
	// AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using
	// this action with S3 on Outposts through the Amazon Web Services SDKs, you
	// provide the Outposts bucket ARN in place of the bucket name. For more
	// information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts
	// (https://docs.aws.amazon.com/AmazonS3/latest/userguide/S3onOutposts.html) in the
	// Amazon S3 User Guide.
	//
	// This member is required.
	Bucket *string

	// ContinuationToken indicates Amazon S3 that the list is being continued on this
	// bucket with a token. ContinuationToken is obfuscated and is not a real key.
	ContinuationToken *string

	// A delimiter is a character you use to group keys.
	Delimiter *string

	// Encoding type used by Amazon S3 to encode object keys in the response.
	EncodingType types.EncodingType

	// The account ID of the expected bucket owner. If the bucket is owned by a
	// different account, the request fails with the HTTP status code 403 Forbidden
	// (access denied).
	ExpectedBucketOwner *string

	// The owner field is not present in listV2 by default, if you want to return owner
	// field with each key in the result then set the fetch owner field to true.
	FetchOwner bool

	// Sets the maximum number of keys returned in the response. By default the action
	// returns up to 1,000 key names. The response might contain fewer keys but will
	// never contain more.
	MaxKeys int32

	// Limits the response to keys that begin with the specified prefix.
	Prefix *string

	// Confirms that the requester knows that she or he will be charged for the list
	// objects request in V2 style. Bucket owners need not specify this parameter in
	// their requests.
	RequestPayer types.RequestPayer

	// StartAfter is where you want Amazon S3 to start listing from. Amazon S3 starts
	// listing after this specified key. StartAfter can be any key in the bucket.
	StartAfter *string
	// contains filtered or unexported fields
}
```

試しに `Prefix` を指定してレスポンスを絞り込んで取得す
以下変更部分のみ記載。

```
  // Get the first page of results for ListObjectsV2 for a bucket
  output, err := client.ListObjectsV2(context.TODO(), &s3.ListObjectsV2Input{
    Bucket: aws.String("aws-codestar-ap-northeast-1-323817733012"),
    Prefix: aws.String("cwe"),
  })
```

実行してみる。

```
$go run  s3.go
2022/12/19 05:44:57 first page results:
2022/12/19 05:44:57 key=cwe1 size=145
2022/12/19 05:44:57 key=cwe2 size=145
```

想定通り、`cwe` という単語で始まるオブジェクトのみ取得するように変更することができた。