diff --git a/backend/s3proxy/s3.go b/backend/s3proxy/s3.go
index a6d9fdb3..3b8ec26b 100644
--- a/backend/s3proxy/s3.go
+++ b/backend/s3proxy/s3.go
@@ -204,6 +204,16 @@ func (s *S3Proxy) CreateBucket(ctx context.Context, input *s3.CreateBucketInput,
}
}
+ // the bucket is created in the backend region, which is independent of the
+ // gateway region the client's constraint was validated against; a regional
+ // S3 endpoint outside us-east-1 rejects a CreateBucket without a constraint
+ if region := s.client.Options().Region; region != "" && region != "us-east-1" {
+ if input.CreateBucketConfiguration == nil {
+ input.CreateBucketConfiguration = &types.CreateBucketConfiguration{}
+ }
+ input.CreateBucketConfiguration.LocationConstraint = types.BucketLocationConstraint(region)
+ }
+
// drop an empty CreateBucketConfiguration: strict backends (Ceph RGW) reject the empty element
if input.CreateBucketConfiguration != nil &&
len(input.CreateBucketConfiguration.Tags) == 0 &&
diff --git a/backend/s3proxy/s3_test.go b/backend/s3proxy/s3_test.go
index 5b26e9e1..31f74e28 100644
--- a/backend/s3proxy/s3_test.go
+++ b/backend/s3proxy/s3_test.go
@@ -54,10 +54,15 @@ func (c *captureHTTPClient) Do(req *http.Request) (*http.Response, error) {
}
func newCaptureProxy(t *testing.T) (*S3Proxy, *captureHTTPClient) {
+ t.Helper()
+ return newCaptureProxyWithRegion(t, "us-east-1")
+}
+
+func newCaptureProxyWithRegion(t *testing.T, region string) (*S3Proxy, *captureHTTPClient) {
t.Helper()
capture := &captureHTTPClient{}
client := s3.New(s3.Options{
- Region: "us-east-1",
+ Region: region,
BaseEndpoint: aws.String("http://backend.test"),
UsePathStyle: true,
Credentials: credentials.NewStaticCredentialsProvider("access", "secret", ""),
@@ -169,3 +174,45 @@ func TestCreateBucketKeepsBucketInfo(t *testing.T) {
t.Errorf("CreateBucket with bucket info must forward the configuration, got %q", capture.body)
}
}
+
+// The bucket is created in the backend region, not the gateway region the
+// api layer validated the client's constraint against. A regional endpoint
+// outside us-east-1 rejects a CreateBucket that carries no constraint, so the
+// backend has to add its own even when the api layer sent none.
+func TestCreateBucketSetsBackendRegionConstraint(t *testing.T) {
+ p, capture := newCaptureProxyWithRegion(t, "eu-west-1")
+
+ err := p.CreateBucket(context.Background(), &s3.CreateBucketInput{
+ Bucket: aws.String("test-bucket"),
+ CreateBucketConfiguration: &types.CreateBucketConfiguration{},
+ }, []byte{})
+ if err != nil {
+ t.Fatalf("CreateBucket: %v", err)
+ }
+ if !strings.Contains(string(capture.body), "eu-west-1") {
+ t.Errorf("CreateBucket against a regional backend must carry the backend region constraint, got %q", capture.body)
+ }
+}
+
+func TestCreateBucketKeepsTagsAndSetsBackendRegionConstraint(t *testing.T) {
+ p, capture := newCaptureProxyWithRegion(t, "eu-west-1")
+
+ err := p.CreateBucket(context.Background(), &s3.CreateBucketInput{
+ Bucket: aws.String("test-bucket"),
+ CreateBucketConfiguration: &types.CreateBucketConfiguration{
+ Tags: []types.Tag{
+ {Key: aws.String("env"), Value: aws.String("test")},
+ },
+ },
+ }, []byte{})
+ if err != nil {
+ t.Fatalf("CreateBucket: %v", err)
+ }
+ body := string(capture.body)
+ if !strings.Contains(body, "eu-west-1") {
+ t.Errorf("CreateBucket against a regional backend must carry the backend region constraint, got %q", body)
+ }
+ if !strings.Contains(body, "env") || !strings.Contains(body, "test") {
+ t.Errorf("CreateBucket must keep the requested tags, got %q", body)
+ }
+}