@@ -135,9 +135,7 @@ func NewClientFromURL(s3URL string) (*Client, error) {
135
135
secretKey := os .Getenv ("AWS_SECRET_ACCESS_KEY" )
136
136
endpoint := os .Getenv ("AWS_ENDPOINT" )
137
137
region := os .Getenv ("AWS_REGION" )
138
- if region == "" {
139
- region = "us-east-1" // Default region
140
- }
138
+ // Don't default to us-east-1, let AWS SDK detect the region
141
139
142
140
// Path style defaults to false unless env var is set
143
141
pathStyle := false
@@ -254,9 +252,8 @@ func NewClient(endpoint, bucket, accessKey, secretKey, region string, sslMode, p
254
252
if envRegion := os .Getenv ("AWS_REGION" ); envRegion != "" {
255
253
region = envRegion
256
254
// fmt.Printf("S3 Client Debug - Using region from environment: %s\n", region)
257
- } else {
258
- region = "us-east-1" // Default region
259
255
}
256
+ // Don't default to us-east-1, let AWS SDK detect the region from config/metadata
260
257
}
261
258
262
259
// Only use environment endpoint if no explicit endpoint was provided
@@ -323,9 +320,7 @@ func NewClientFromEnv(bucket string) (*Client, error) {
323
320
secretKey := os .Getenv ("AWS_SECRET_ACCESS_KEY" )
324
321
endpoint := os .Getenv ("AWS_ENDPOINT" )
325
322
region := os .Getenv ("AWS_REGION" )
326
- if region == "" {
327
- region = "us-east-1"
328
- }
323
+ // Don't default to us-east-1, let AWS SDK detect the region
329
324
330
325
// Path style is determined from environment
331
326
pathStyle := false
@@ -376,49 +371,71 @@ func (c *Client) initialize(ctx context.Context) error {
376
371
}
377
372
}
378
373
379
- // Create custom resolver for endpoint
380
- // This is critical for making localstack work with AWS SDK v2
381
- customResolver := aws .EndpointResolverWithOptionsFunc (func (service , region string , options ... interface {}) (aws.Endpoint , error ) {
382
- // Always return the custom endpoint for any service in any region
383
- // This is necessary for localstack compatibility
384
- endpoint := aws.Endpoint {
385
- URL : c .Endpoint ,
386
- SigningRegion : c .Region ,
387
- HostnameImmutable : true ,
388
- }
389
- // fmt.Printf("S3 Client Debug - Resolving endpoint to URL: '%s', SigningRegion: '%s'\n",
390
- // endpoint.URL, endpoint.SigningRegion)
391
- return endpoint , nil
392
- })
393
-
394
374
// Load config using options
395
375
var configOpts []func (* config.LoadOptions ) error
396
376
397
- // Always use custom endpoint resolver if an endpoint is provided
377
+ // Only use custom endpoint resolver if an endpoint is provided (for localstack, etc.)
398
378
if c .Endpoint != "" {
379
+ // Create custom resolver for endpoint
380
+ // This is critical for making localstack work with AWS SDK v2
381
+ customResolver := aws .EndpointResolverWithOptionsFunc (func (service , region string , options ... interface {}) (aws.Endpoint , error ) {
382
+ // Always return the custom endpoint for any service in any region
383
+ // This is necessary for localstack compatibility
384
+ signingRegion := c .Region
385
+ if signingRegion == "" {
386
+ // Use the region passed by the SDK if we don't have one
387
+ signingRegion = region
388
+ }
389
+ endpoint := aws.Endpoint {
390
+ URL : c .Endpoint ,
391
+ SigningRegion : signingRegion ,
392
+ HostnameImmutable : true ,
393
+ }
394
+ // fmt.Printf("S3 Client Debug - Resolving endpoint to URL: '%s', SigningRegion: '%s'\n",
395
+ // endpoint.URL, endpoint.SigningRegion)
396
+ return endpoint , nil
397
+ })
398
+
399
399
// fmt.Printf("S3 Client Debug - Using custom endpoint: %s\n", c.Endpoint)
400
400
configOpts = append (configOpts , config .WithEndpointResolverWithOptions (customResolver ))
401
401
402
402
// Disable AWS SDK v2's automatic endpoint resolution
403
403
configOpts = append (configOpts , config .WithRetryMaxAttempts (1 ))
404
404
}
405
405
406
- // Add region
407
- configOpts = append (configOpts , config .WithRegion (c .Region ))
406
+ // Add region if explicitly provided
407
+ if c .Region != "" {
408
+ configOpts = append (configOpts , config .WithRegion (c .Region ))
409
+ }
410
+ // If no region provided, LoadDefaultConfig will use the default region chain:
411
+ // 1. AWS_REGION environment variable
412
+ // 2. AWS_DEFAULT_REGION environment variable
413
+ // 3. Region from shared config file (~/.aws/config)
414
+ // 4. EC2 instance metadata service (if on EC2)
408
415
409
- // Add credentials if provided
416
+ // Add credentials if provided, otherwise use default AWS credential chain
410
417
if c .AccessKey != "" && c .SecretKey != "" {
411
418
configOpts = append (configOpts , config .WithCredentialsProvider (
412
419
credentials .NewStaticCredentialsProvider (c .AccessKey , c .SecretKey , "" ),
413
420
))
414
421
}
422
+ // If no credentials provided, LoadDefaultConfig will use the default credential chain:
423
+ // 1. Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN)
424
+ // 2. Shared credentials file (~/.aws/credentials)
425
+ // 3. IAM role (if running on EC2/ECS/Lambda)
415
426
416
427
// Load the AWS configuration
417
428
cfg , err := config .LoadDefaultConfig (ctx , configOpts ... )
418
429
if err != nil {
419
430
return errors .Wrap (err , "failed to load AWS config" )
420
431
}
421
432
433
+ // If region was not explicitly set, store the detected region
434
+ if c .Region == "" {
435
+ c .Region = cfg .Region
436
+ // fmt.Printf("S3 Client Debug - Using detected region: %s\n", c.Region)
437
+ }
438
+
422
439
// Create S3 client with path style config if needed
423
440
s3Client := s3 .NewFromConfig (cfg , func (o * s3.Options ) {
424
441
// Always use path style for LocalStack
@@ -598,6 +615,17 @@ func (c *Client) BucketExists(ctx context.Context) (bool, error) {
598
615
if strings .Contains (err .Error (), "NotFound" ) || strings .Contains (err .Error (), "404" ) {
599
616
return false , nil
600
617
}
618
+ // Check for 301 redirect which means wrong region
619
+ if strings .Contains (err .Error (), "301" ) || strings .Contains (err .Error (), "MovedPermanently" ) {
620
+ // For AWS S3, a 301 error means the bucket exists but is in a different region
621
+ // We'll print a helpful warning but not fail the check
622
+ fmt .Printf ("WARNING: Failed to check if bucket exists: %v\n " , err )
623
+ fmt .Printf ("This usually means the bucket '%s' exists but is in a different region than '%s'.\n " , c .Bucket , c .Region )
624
+ fmt .Printf ("Please set the correct region using AWS_REGION environment variable.\n " )
625
+ // Since we can't properly verify the bucket, we'll return an error
626
+ // This prevents potential issues with trying to use a bucket in the wrong region
627
+ return false , fmt .Errorf ("bucket appears to be in a different region (got 301 redirect)" )
628
+ }
601
629
return false , errors .Wrap (err , "failed to check bucket existence" )
602
630
}
603
631
0 commit comments