This is an automated email from the ASF dual-hosted git repository.

chungen0126 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new 8638daf7f3e HDDS-16636. Remove redundant InfoBucket RPC from S3 
ListParts (#11358)
8638daf7f3e is described below

commit 8638daf7f3e4911257df2d7283b99c7939598f69
Author: KUAN-HAO HUANG <[email protected]>
AuthorDate: Wed Sep 30 14:42:21 2026 +0800

    HDDS-16636. Remove redundant InfoBucket RPC from S3 ListParts (#11358)
---
 .../ozone/s3/endpoint/MultipartKeyHandler.java     |  12 +--
 .../hadoop/ozone/s3/endpoint/TestListParts.java    | 108 +++++++++++++++++++++
 2 files changed, 114 insertions(+), 6 deletions(-)

diff --git 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/MultipartKeyHandler.java
 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/MultipartKeyHandler.java
index b9487b0f590..71d9c12f7ba 100644
--- 
a/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/MultipartKeyHandler.java
+++ 
b/hadoop-ozone/s3gateway/src/main/java/org/apache/hadoop/ozone/s3/endpoint/MultipartKeyHandler.java
@@ -29,7 +29,6 @@
 import org.apache.hadoop.ozone.audit.AuditLogger;
 import org.apache.hadoop.ozone.audit.AuditLogger.PerformanceStringBuilder;
 import org.apache.hadoop.ozone.audit.S3GAction;
-import org.apache.hadoop.ozone.client.OzoneBucket;
 import org.apache.hadoop.ozone.client.OzoneMultipartUploadPartListParts;
 import org.apache.hadoop.ozone.client.OzoneVolume;
 import org.apache.hadoop.ozone.om.exceptions.OMException;
@@ -61,7 +60,7 @@ Response handleGetRequest(ObjectEndpoint.ObjectRequestContext 
context, String ke
 
     try {
       int partMarker = parsePartNumberMarker(partNumberMarker);
-      Response response = listParts(context.getBucket(), keyPath, uploadId,
+      Response response = listParts(context.getVolume(), 
context.getBucketName(), keyPath, uploadId,
           partMarker, maxParts, perf);
       long opLatencyNs = 
getMetrics().updateListPartsSuccessStats(context.getStartNanos());
       perf.appendOpLatencyNanos(opLatencyNs);
@@ -114,7 +113,8 @@ private Response abortMultipartUpload(OzoneVolume volume, 
String bucket,
   /**
    * Returns response for the listParts request.
    * See: 
https://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadListParts.html
-   * @param ozoneBucket
+   * @param volume
+   * @param bucketName
    * @param key
    * @param uploadId
    * @param partNumberMarker
@@ -123,17 +123,17 @@ private Response abortMultipartUpload(OzoneVolume volume, 
String bucket,
    * @throws IOException
    * @throws OS3Exception
    */
-  private Response listParts(OzoneBucket ozoneBucket, String key, String 
uploadId,
+  private Response listParts(OzoneVolume volume, String bucketName, String 
key, String uploadId,
       int partNumberMarker, int maxParts,
       PerformanceStringBuilder perf)
       throws IOException, OS3Exception {
 
     ListPartsResponse resp = new ListPartsResponse();
-    String bucketName = ozoneBucket.getName();
 
     try {
       OzoneMultipartUploadPartListParts parts =
-          ozoneBucket.listParts(key, uploadId, partNumberMarker, maxParts);
+          getClientProtocol().listParts(volume.getName(), bucketName, key, 
uploadId,
+              partNumberMarker, maxParts);
 
       resp.setBucket(bucketName);
       resp.setKey(key);
diff --git 
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestListParts.java
 
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestListParts.java
index d9828eb2e8e..7a4184f6960 100644
--- 
a/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestListParts.java
+++ 
b/hadoop-ozone/s3gateway/src/test/java/org/apache/hadoop/ozone/s3/endpoint/TestListParts.java
@@ -20,24 +20,42 @@
 import static 
org.apache.hadoop.ozone.s3.endpoint.EndpointTestUtils.assertErrorResponse;
 import static 
org.apache.hadoop.ozone.s3.endpoint.EndpointTestUtils.initiateMultipartUpload;
 import static org.apache.hadoop.ozone.s3.endpoint.EndpointTestUtils.uploadPart;
+import static 
org.apache.hadoop.ozone.s3.util.S3Consts.EXPECTED_BUCKET_OWNER_HEADER;
 import static org.apache.hadoop.ozone.s3.util.S3Consts.STORAGE_CLASS_HEADER;
 import static org.apache.hadoop.ozone.s3.util.S3Consts.X_AMZ_CONTENT_SHA256;
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import java.io.IOException;
+import java.util.Collections;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
+import org.apache.hadoop.hdds.client.RatisReplicationConfig;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
 import org.apache.hadoop.ozone.OzoneConsts;
+import org.apache.hadoop.ozone.client.ObjectStore;
+import org.apache.hadoop.ozone.client.OzoneBucket;
 import org.apache.hadoop.ozone.client.OzoneClient;
 import org.apache.hadoop.ozone.client.OzoneClientStub;
+import org.apache.hadoop.ozone.client.OzoneMultipartUploadPartListParts;
+import org.apache.hadoop.ozone.client.OzoneVolume;
+import org.apache.hadoop.ozone.client.protocol.ClientProtocol;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes;
 import org.apache.hadoop.ozone.s3.exception.OS3Exception;
 import org.apache.hadoop.ozone.s3.exception.S3ErrorTable;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
 
 /**
  * This class test list parts request.
@@ -99,6 +117,96 @@ public void testListPartsWithUnknownUploadID() {
         () -> EndpointTestUtils.listParts(rest, OzoneConsts.S3_BUCKET, 
"no-such-key", "no-such-upload", 2, 0));
   }
 
+  @ParameterizedTest
+  @CsvSource({", 0", "'', 0", "owner, 1"})
+  public void testListPartsOnlyLooksUpBucketForOwner(String expectedOwner, int 
bucketLookups) throws Exception {
+    ClientProtocol proxy = mock(ClientProtocol.class);
+    HttpHeaders headers = mock(HttpHeaders.class);
+    
when(headers.getHeaderString(EXPECTED_BUCKET_OWNER_HEADER)).thenReturn(expectedOwner);
+    ObjectEndpoint endpoint = newEndpoint(proxy, headers);
+    OzoneMultipartUploadPartListParts parts = new 
OzoneMultipartUploadPartListParts(
+        
RatisReplicationConfig.getInstance(HddsProtos.ReplicationFactor.THREE), 4, 
true);
+    parts.addPart(new OzoneMultipartUploadPartListParts.PartInfo(4, "part4", 
0, 10, "etag4"));
+    when(proxy.listParts("volume1", "bucket1", "key1", "upload1", 3, 
2)).thenReturn(parts);
+
+    try (Response response = EndpointTestUtils.listParts(endpoint, "bucket1", 
"key1", "upload1", 2, 3)) {
+      assertThat(response.getStatus()).isEqualTo(200);
+      ListPartsResponse result = (ListPartsResponse) response.getEntity();
+      assertThat(result.getBucket()).isEqualTo("bucket1");
+      assertThat(result.getPartNumberMarker()).isEqualTo(3);
+      assertThat(result.getMaxParts()).isEqualTo(2);
+      assertThat(result.getNextPartNumberMarker()).isEqualTo(4);
+      assertThat(result.getTruncated()).isTrue();
+      assertThat(result.getPartList()).hasSize(1);
+      assertThat(result.getPartList().get(0).getETag()).isEqualTo("etag4");
+    }
+    verify(proxy, times(bucketLookups)).getBucketDetails("volume1", "bucket1");
+    verify(proxy).listParts("volume1", "bucket1", "key1", "upload1", 3, 2);
+  }
+
+  @Test
+  public void testListPartsRejectsIncorrectOwner() throws Exception {
+    ClientProtocol proxy = mock(ClientProtocol.class);
+    HttpHeaders headers = mock(HttpHeaders.class);
+    
when(headers.getHeaderString(EXPECTED_BUCKET_OWNER_HEADER)).thenReturn("other-owner");
+    ObjectEndpoint endpoint = newEndpoint(proxy, headers);
+
+    assertErrorResponse(S3ErrorTable.BUCKET_OWNER_MISMATCH,
+        () -> EndpointTestUtils.listParts(endpoint, "bucket1", "key1", 
"upload1", 2, 3));
+
+    verify(proxy, never()).listParts("volume1", "bucket1", "key1", "upload1", 
3, 2);
+  }
+
+  @ParameterizedTest
+  @CsvSource({
+      "NO_SUCH_MULTIPART_UPLOAD_ERROR, NO_SUCH_UPLOAD",
+      "BUCKET_NOT_FOUND, NO_SUCH_BUCKET",
+      "VOLUME_NOT_FOUND, NO_SUCH_BUCKET",
+      "PERMISSION_DENIED, ACCESS_DENIED"
+  })
+  public void testListPartsProtocolErrors(ResultCodes resultCode, S3ErrorTable 
expectedError) throws Exception {
+    ClientProtocol proxy = mock(ClientProtocol.class);
+    ObjectEndpoint endpoint = newEndpoint(proxy, mock(HttpHeaders.class));
+    when(proxy.listParts("volume1", "bucket1", "key1", "upload1", 3, 2))
+        .thenThrow(new OMException("List parts failed", resultCode));
+
+    assertErrorResponse(expectedError,
+        () -> EndpointTestUtils.listParts(endpoint, "bucket1", "key1", 
"upload1", 2, 3));
+
+    verify(proxy).listParts("volume1", "bucket1", "key1", "upload1", 3, 2);
+  }
+
+  @ParameterizedTest
+  @CsvSource({"PERMISSION_DENIED, ACCESS_DENIED", "TOKEN_EXPIRED, 
EXPIRED_TOKEN"})
+  public void testListPartsVolumeLookupErrors(ResultCodes resultCode, 
S3ErrorTable expectedError) throws Exception {
+    ClientProtocol proxy = mock(ClientProtocol.class);
+    ObjectEndpoint endpoint = newEndpoint(proxy, mock(HttpHeaders.class));
+    when(endpoint.getClient().getObjectStore().getS3Volume())
+        .thenThrow(new OMException("Volume lookup failed", resultCode));
+
+    OS3Exception error = assertErrorResponse(expectedError,
+        () -> EndpointTestUtils.listParts(endpoint, "bucket1", "key1", 
"upload1", 2, 3));
+
+    assertThat(error.getResource()).isEqualTo("key1");
+    verify(proxy, never()).listParts("volume1", "bucket1", "key1", "upload1", 
3, 2);
+  }
+
+  private ObjectEndpoint newEndpoint(ClientProtocol proxy, HttpHeaders 
headers) throws Exception {
+    OzoneConfiguration conf = new OzoneConfiguration();
+    OzoneVolume volume = OzoneVolume.newBuilder(conf, proxy)
+        .setName("volume1").setAcls(Collections.emptyList()).build();
+    OzoneBucket bucket = OzoneBucket.newBuilder(conf, proxy)
+        .setVolumeName("volume1").setName("bucket1").setOwner("owner").build();
+    when(proxy.getBucketDetails("volume1", "bucket1")).thenReturn(bucket);
+    ObjectStore objectStore = mock(ObjectStore.class);
+    when(objectStore.getS3Volume()).thenReturn(volume);
+    when(objectStore.getClientProxy()).thenReturn(proxy);
+    OzoneClient client = mock(OzoneClient.class);
+    when(client.getObjectStore()).thenReturn(objectStore);
+    when(client.getProxy()).thenReturn(proxy);
+    return 
EndpointBuilder.newObjectEndpointBuilder().setClient(client).setHeaders(headers).build();
+  }
+
   private ListPartsResponse listParts(int maxParts, int nextPart) throws 
IOException, OS3Exception {
     try (Response response = EndpointTestUtils.listParts(rest, 
OzoneConsts.S3_BUCKET, OzoneConsts.KEY,
         uploadID, maxParts, nextPart)) {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to