github.com/aavshr/aws-sdk-go@v1.41.3/service/s3/s3manager/bucket_region.go (about)

     1  package s3manager
     2  
     3  import (
     4  	"github.com/aavshr/aws-sdk-go/aws"
     5  	"github.com/aavshr/aws-sdk-go/aws/client"
     6  	"github.com/aavshr/aws-sdk-go/aws/corehandlers"
     7  	"github.com/aavshr/aws-sdk-go/aws/credentials"
     8  	"github.com/aavshr/aws-sdk-go/aws/request"
     9  	"github.com/aavshr/aws-sdk-go/service/s3"
    10  	"github.com/aavshr/aws-sdk-go/service/s3/s3iface"
    11  )
    12  
    13  // GetBucketRegion will attempt to get the region for a bucket using the
    14  // regionHint to determine which AWS partition to perform the query on.
    15  //
    16  // The request will not be signed, and will not use your AWS credentials.
    17  //
    18  // A "NotFound" error code will be returned if the bucket does not exist in the
    19  // AWS partition the regionHint belongs to. If the regionHint parameter is an
    20  // empty string GetBucketRegion will fallback to the ConfigProvider's region
    21  // config. If the regionHint is empty, and the ConfigProvider does not have a
    22  // region value, an error will be returned..
    23  //
    24  // For example to get the region of a bucket which exists in "eu-central-1"
    25  // you could provide a region hint of "us-west-2".
    26  //
    27  //    sess := session.Must(session.NewSession())
    28  //
    29  //    bucket := "my-bucket"
    30  //    region, err := s3manager.GetBucketRegion(ctx, sess, bucket, "us-west-2")
    31  //    if err != nil {
    32  //        if aerr, ok := err.(awserr.Error); ok && aerr.Code() == "NotFound" {
    33  //             fmt.Fprintf(os.Stderr, "unable to find bucket %s's region not found\n", bucket)
    34  //        }
    35  //        return err
    36  //    }
    37  //    fmt.Printf("Bucket %s is in %s region\n", bucket, region)
    38  //
    39  // By default the request will be made to the Amazon S3 endpoint using the Path
    40  // style addressing.
    41  //
    42  //    s3.us-west-2.amazonaws.com/bucketname
    43  //
    44  // This is not compatible with Amazon S3's FIPS endpoints. To override this
    45  // behavior to use Virtual Host style addressing, provide a functional option
    46  // that will set the Request's Config.S3ForcePathStyle to aws.Bool(false).
    47  //
    48  //    region, err := s3manager.GetBucketRegion(ctx, sess, "bucketname", "us-west-2", func(r *request.Request) {
    49  //        r.S3ForcePathStyle = aws.Bool(false)
    50  //    })
    51  //
    52  // To configure the GetBucketRegion to make a request via the Amazon
    53  // S3 FIPS endpoints directly when a FIPS region name is not available, (e.g.
    54  // fips-us-gov-west-1) set the Config.Endpoint on the Session, or client the
    55  // utility is called with. The hint region will be ignored if an endpoint URL
    56  // is configured on the session or client.
    57  //
    58  //    sess, err := session.NewSession(&aws.Config{
    59  //        Endpoint: aws.String("https://s3-fips.us-west-2.amazonaws.com"),
    60  //    })
    61  //
    62  //    region, err := s3manager.GetBucketRegion(context.Background(), sess, "bucketname", "")
    63  func GetBucketRegion(ctx aws.Context, c client.ConfigProvider, bucket, regionHint string, opts ...request.Option) (string, error) {
    64  	var cfg aws.Config
    65  	if len(regionHint) != 0 {
    66  		cfg.Region = aws.String(regionHint)
    67  	}
    68  	svc := s3.New(c, &cfg)
    69  	return GetBucketRegionWithClient(ctx, svc, bucket, opts...)
    70  }
    71  
    72  const bucketRegionHeader = "X-Amz-Bucket-Region"
    73  
    74  // GetBucketRegionWithClient is the same as GetBucketRegion with the exception
    75  // that it takes a S3 service client instead of a Session. The regionHint is
    76  // derived from the region the S3 service client was created in.
    77  //
    78  // By default the request will be made to the Amazon S3 endpoint using the Path
    79  // style addressing.
    80  //
    81  //    s3.us-west-2.amazonaws.com/bucketname
    82  //
    83  // This is not compatible with Amazon S3's FIPS endpoints. To override this
    84  // behavior to use Virtual Host style addressing, provide a functional option
    85  // that will set the Request's Config.S3ForcePathStyle to aws.Bool(false).
    86  //
    87  //    region, err := s3manager.GetBucketRegionWithClient(ctx, client, "bucketname", func(r *request.Request) {
    88  //        r.S3ForcePathStyle = aws.Bool(false)
    89  //    })
    90  //
    91  // To configure the GetBucketRegion to make a request via the Amazon
    92  // S3 FIPS endpoints directly when a FIPS region name is not available, (e.g.
    93  // fips-us-gov-west-1) set the Config.Endpoint on the Session, or client the
    94  // utility is called with. The hint region will be ignored if an endpoint URL
    95  // is configured on the session or client.
    96  //
    97  //    region, err := s3manager.GetBucketRegionWithClient(context.Background(),
    98  //    s3.New(sess, &aws.Config{
    99  //        Endpoint: aws.String("https://s3-fips.us-west-2.amazonaws.com"),
   100  //    }),
   101  //    "bucketname")
   102  //
   103  // See GetBucketRegion for more information.
   104  func GetBucketRegionWithClient(ctx aws.Context, svc s3iface.S3API, bucket string, opts ...request.Option) (string, error) {
   105  	req, _ := svc.HeadBucketRequest(&s3.HeadBucketInput{
   106  		Bucket: aws.String(bucket),
   107  	})
   108  	req.Config.S3ForcePathStyle = aws.Bool(true)
   109  
   110  	req.Config.Credentials = credentials.AnonymousCredentials
   111  	req.SetContext(ctx)
   112  
   113  	// Disable HTTP redirects to prevent an invalid 301 from eating the response
   114  	// because Go's HTTP client will fail, and drop the response if an 301 is
   115  	// received without a location header. S3 will return a 301 without the
   116  	// location header for HeadObject API calls.
   117  	req.DisableFollowRedirects = true
   118  
   119  	var bucketRegion string
   120  	req.Handlers.Send.PushBack(func(r *request.Request) {
   121  		bucketRegion = r.HTTPResponse.Header.Get(bucketRegionHeader)
   122  		if len(bucketRegion) == 0 {
   123  			return
   124  		}
   125  		r.HTTPResponse.StatusCode = 200
   126  		r.HTTPResponse.Status = "OK"
   127  		r.Error = nil
   128  	})
   129  	// Replace the endpoint validation handler to not require a region if an
   130  	// endpoint URL was specified. Since these requests are not authenticated,
   131  	// requiring a region is not needed when an endpoint URL is provided.
   132  	req.Handlers.Validate.Swap(
   133  		corehandlers.ValidateEndpointHandler.Name,
   134  		request.NamedHandler{
   135  			Name: "validateEndpointWithoutRegion",
   136  			Fn:   validateEndpointWithoutRegion,
   137  		},
   138  	)
   139  
   140  	req.ApplyOptions(opts...)
   141  
   142  	if err := req.Send(); err != nil {
   143  		return "", err
   144  	}
   145  
   146  	bucketRegion = s3.NormalizeBucketLocation(bucketRegion)
   147  
   148  	return bucketRegion, nil
   149  }
   150  
   151  func validateEndpointWithoutRegion(r *request.Request) {
   152  	// Check if the caller provided an explicit URL instead of one derived by
   153  	// the SDK's endpoint resolver. For GetBucketRegion, with an explicit
   154  	// endpoint URL, a region is not needed. If no endpoint URL is provided,
   155  	// fallback the SDK's standard endpoint validation handler.
   156  	if len(aws.StringValue(r.Config.Endpoint)) == 0 {
   157  		corehandlers.ValidateEndpointHandler.Fn(r)
   158  	}
   159  }