1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
|
package manager
import (
"context"
"errors"
"fmt"
"net/http"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/aws/smithy-go/middleware"
smithyhttp "github.com/aws/smithy-go/transport/http"
)
const bucketRegionHeader = "X-Amz-Bucket-Region"
// GetBucketRegion will attempt to get the region for a bucket using the
// client's configured region to determine which AWS partition to perform the query on.
//
// The request will not be signed, and will not use your AWS credentials.
//
// A BucketNotFound error will be returned if the bucket does not exist in the
// AWS partition the client region belongs to.
//
// For example to get the region of a bucket which exists in "eu-central-1"
// you could provide a region hint of "us-west-2".
//
// cfg, err := config.LoadDefaultConfig(context.TODO())
// if err != nil {
// log.Println("error:", err)
// return
// }
//
// bucket := "my-bucket"
// region, err := manager.GetBucketRegion(ctx, s3.NewFromConfig(cfg), bucket)
// if err != nil {
// var bnf manager.BucketNotFound
// if errors.As(err, &bnf) {
// fmt.Fprintf(os.Stderr, "unable to find bucket %s's region\n", bucket)
// }
// return
// }
// fmt.Printf("Bucket %s is in %s region\n", bucket, region)
//
// By default the request will be made to the Amazon S3 endpoint using the virtual-hosted-style addressing.
//
// bucketname.s3.us-west-2.amazonaws.com/
//
// To configure the GetBucketRegion to make a request via the Amazon
// S3 FIPS endpoints directly when a FIPS region name is not available, (e.g.
// fips-us-gov-west-1) set the EndpointResolver on the config or client the
// utility is called with.
//
// cfg, err := config.LoadDefaultConfig(context.TODO(),
// config.WithEndpointResolver(
// aws.EndpointResolverFunc(func(service, region string) (aws.Endpoint, error) {
// return aws.Endpoint{URL: "https://s3-fips.us-west-2.amazonaws.com"}, nil
// }),
// )
// if err != nil {
// panic(err)
// }
func GetBucketRegion(ctx context.Context, client HeadBucketAPIClient, bucket string, optFns ...func(*s3.Options)) (string, error) {
var captureBucketRegion deserializeBucketRegion
clientOptionFns := make([]func(*s3.Options), len(optFns)+1)
clientOptionFns[0] = func(options *s3.Options) {
options.Credentials = aws.AnonymousCredentials{}
options.APIOptions = append(options.APIOptions, captureBucketRegion.RegisterMiddleware)
}
copy(clientOptionFns[1:], optFns)
_, err := client.HeadBucket(ctx, &s3.HeadBucketInput{
Bucket: aws.String(bucket),
}, clientOptionFns...)
if len(captureBucketRegion.BucketRegion) == 0 && err != nil {
var httpStatusErr interface {
HTTPStatusCode() int
}
if !errors.As(err, &httpStatusErr) {
return "", err
}
if httpStatusErr.HTTPStatusCode() == http.StatusNotFound {
return "", &bucketNotFound{}
}
return "", err
}
return captureBucketRegion.BucketRegion, nil
}
type deserializeBucketRegion struct {
BucketRegion string
}
func (d *deserializeBucketRegion) RegisterMiddleware(stack *middleware.Stack) error {
return stack.Deserialize.Add(d, middleware.After)
}
func (d *deserializeBucketRegion) ID() string {
return "DeserializeBucketRegion"
}
func (d *deserializeBucketRegion) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) (
out middleware.DeserializeOutput, metadata middleware.Metadata, err error,
) {
out, metadata, err = next.HandleDeserialize(ctx, in)
if err != nil {
return out, metadata, err
}
resp, ok := out.RawResponse.(*smithyhttp.Response)
if !ok {
return out, metadata, fmt.Errorf("unknown transport type %T", out.RawResponse)
}
d.BucketRegion = resp.Header.Get(bucketRegionHeader)
return out, metadata, err
}
// BucketNotFound indicates the bucket was not found in the partition when calling GetBucketRegion.
type BucketNotFound interface {
error
isBucketNotFound()
}
type bucketNotFound struct{}
func (b *bucketNotFound) Error() string {
return "bucket not found"
}
func (b *bucketNotFound) isBucketNotFound() {}
var _ BucketNotFound = (*bucketNotFound)(nil)
|