diff --git a/hadoop-hdds/docs/content/interface/S3.md b/hadoop-hdds/docs/content/interface/S3.md index 1edc89f809d4..b43abb13db59 100644 --- a/hadoop-hdds/docs/content/interface/S3.md +++ b/hadoop-hdds/docs/content/interface/S3.md @@ -68,7 +68,6 @@ The Ozone S3 Gateway implements a substantial subset of the Amazon S3 REST API. | ✅ [CreateBucket](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html) | Creates a new bucket. | **Non-compliant behavior:** The default bucket ACL may include extra group permissions instead of being strictly private. Bucket names must adhere to S3 naming conventions. | | ✅ [HeadBucket](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadBucket.html) | Checks for the existence of a bucket. | Returns a 200 status if the bucket exists. | | ✅ [DeleteBucket](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucket.html) | Deletes a bucket. | Bucket must be empty before deletion. | -| ✅ [GetBucketLocation](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLocation.html) | Retrieves the location (region) of a bucket. | Typically returns a default region (e.g., `us-east-1`), which may differ from AWS if region-specific responses are expected. | ### Object Operations diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketEndpoint.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketEndpoint.java index 2fd6dee16db7..80d82b50f93f 100644 --- a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketEndpoint.java +++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketEndpoint.java @@ -419,6 +419,7 @@ protected void init() { // initialize handlers BucketOperationHandler chain = BucketOperationHandlerChain.newBuilder(this) + .add(new BucketGetLocationHandler()) .add(new BucketAclHandler()) .add(new ListMultipartUploadsHandler()) .add(new BucketCrudHandler()) diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketGetLocationHandler.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketGetLocationHandler.java new file mode 100644 index 000000000000..0aeaf414685b --- /dev/null +++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/BucketGetLocationHandler.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.ozone.s3.endpoint; + +import static org.apache.hadoop.ozone.s3.exception.S3ErrorTable.NOT_IMPLEMENTED; +import static org.apache.hadoop.ozone.s3.exception.S3ErrorTable.newError; + +import java.io.IOException; +import javax.ws.rs.core.Response; +import org.apache.hadoop.ozone.audit.S3GAction; +import org.apache.hadoop.ozone.s3.exception.OS3Exception; +import org.apache.hadoop.ozone.s3.util.S3Consts.QueryParams; + +/** + * Handles GET bucket {@code ?location} ({@code GetBucketLocation}). + *

+ * This operation is not implemented; previously the request incorrectly fell + * through to list-objects and returned a {@code ListBucketResult} body. + */ +class BucketGetLocationHandler extends BucketOperationHandler { + + @Override + Response handleGetRequest(S3RequestContext context, String bucketName) + throws IOException, OS3Exception { + if (queryParams().get(QueryParams.LOCATION) == null) { + return null; + } + + context.setAction(S3GAction.GET_BUCKET); + throw newError(NOT_IMPLEMENTED, "GetBucketLocation"); + } +} diff --git a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/S3Consts.java b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/S3Consts.java index 76addc4b9e3d..157324d4a24c 100644 --- a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/S3Consts.java +++ b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/util/S3Consts.java @@ -130,6 +130,8 @@ public static final class QueryParams { public static final String DELIMITER = "delimiter"; public static final String ENCODING_TYPE = "encoding-type"; public static final String KEY_MARKER = "key-marker"; + // GetBucketLocation is not implemented + public static final String LOCATION = "location"; public static final String MARKER = "marker"; public static final String MAX_KEYS = "max-keys"; public static final String MAX_PARTS = "max-parts"; diff --git a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestBucketGetLocation.java b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestBucketGetLocation.java new file mode 100644 index 000000000000..0844effd0638 --- /dev/null +++ b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestBucketGetLocation.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.ozone.s3.endpoint; + +import static java.net.HttpURLConnection.HTTP_NOT_IMPLEMENTED; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.io.IOException; +import org.apache.hadoop.ozone.client.OzoneClient; +import org.apache.hadoop.ozone.client.OzoneClientStub; +import org.apache.hadoop.ozone.s3.exception.OS3Exception; +import org.apache.hadoop.ozone.s3.util.S3Consts.QueryParams; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** Tests for {@code GET /{bucket}?location} ({@code GetBucketLocation}). */ +public class TestBucketGetLocation { + + private static final String BUCKET_NAME = "my bucket"; + private BucketEndpoint bucketEndpoint; + + @BeforeEach + public void setup() throws IOException { + final OzoneClient clientStub = new OzoneClientStub(); + clientStub.getObjectStore().createS3Bucket(BUCKET_NAME); + + bucketEndpoint = EndpointBuilder.newBucketEndpointBuilder() + .setClient(clientStub) + .build(); + } + + @Test + public void getBucketLocationIsNotImplemented() { + bucketEndpoint.queryParamsForTest().set(QueryParams.LOCATION, ""); + + final OS3Exception ex = assertThrows(OS3Exception.class, () -> bucketEndpoint.get(BUCKET_NAME)); + assertEquals(HTTP_NOT_IMPLEMENTED, ex.getHttpCode()); + assertEquals("NotImplemented", ex.getCode()); + } +}