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

sarvekshayr 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 19996bd2015 HDDS-13066. Return a clear bucket-link error when the 
linked source does not exist (#11368)
19996bd2015 is described below

commit 19996bd2015c912fb383525527523147ef604167
Author: rajeshkumarchandolu <[email protected]>
AuthorDate: Mon Oct 12 15:01:36 2026 +0530

    HDDS-13066. Return a clear bucket-link error when the linked source does 
not exist (#11368)
---
 .../dist/src/main/smoketest/basic/links.robot      | 14 ++++
 .../org/apache/hadoop/ozone/om/OzoneManager.java   |  6 ++
 .../hadoop/ozone/om/TestBucketManagerImpl.java     | 80 ++++++++++++++++++++++
 3 files changed, 100 insertions(+)

diff --git a/hadoop-ozone/dist/src/main/smoketest/basic/links.robot 
b/hadoop-ozone/dist/src/main/smoketest/basic/links.robot
index 41da993bae1..0bae80e5d52 100644
--- a/hadoop-ozone/dist/src/main/smoketest/basic/links.robot
+++ b/hadoop-ozone/dist/src/main/smoketest/basic/links.robot
@@ -93,6 +93,20 @@ Link to non-existent bucket
                         Execute                     ozone sh bucket link 
${source}/no-such-bucket ${target}/dangling-link
     ${result} =         Execute And Ignore Error    ozone sh key list 
${target}/dangling-link
                         Should Contain              ${result}         
BUCKET_NOT_FOUND
+                        Should Contain              ${result}         Cannot 
follow bucket link
+
+Link to non-existent source volume
+                        Execute                     ozone sh bucket link 
no-such-volume/no-such-bucket ${target}/dangling-missing-vol
+    ${result} =         Execute And Ignore Error    ozone sh key list 
${target}/dangling-missing-vol
+                        Should Contain              ${result}         
VOLUME_NOT_FOUND
+                        Should Contain              ${result}         Cannot 
follow bucket link
+
+Link chain to broken source
+                        Execute                     ozone sh bucket link 
no-such-volume/no-such-bucket ${target}/link-bucket2
+                        Execute                     ozone sh bucket link 
${target}/link-bucket2 ${target}/link-bucket1
+    ${result} =         Execute And Ignore Error    ozone sh key list 
${target}/link-bucket1
+                        Should Contain              ${result}         
VOLUME_NOT_FOUND
+                        Should Contain              ${result}         Cannot 
follow bucket link
 
 Key create passthrough
                         Ozone Shell Batch           bucket link 
${source}/bucket1 ${target}/link1
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
index 5c3e54ebbae..efb1f0d6201 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
@@ -93,6 +93,7 @@
 import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_SERVER_DEFAULT_REPLICATION_TYPE_KEY;
 import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_SNAPSHOT_SST_FILTERING_SERVICE_INTERVAL;
 import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_THREAD_NUMBER_DIR_DELETION;
+import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.BUCKET_NOT_FOUND;
 import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.DETECTED_LOOP_IN_BUCKET_LINKS;
 import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.FEATURE_NOT_ENABLED;
 import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.INTERNAL_ERROR;
@@ -102,6 +103,7 @@
 import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.NOT_SUPPORTED_OPERATION;
 import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.PERMISSION_DENIED;
 import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TOKEN_ERROR_OTHER;
+import static 
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.VOLUME_NOT_FOUND;
 import static 
org.apache.hadoop.ozone.om.lock.OzoneManagerLock.LeveledResource.BUCKET_LOCK;
 import static 
org.apache.hadoop.ozone.om.lock.OzoneManagerLock.LeveledResource.VOLUME_LOCK;
 import static 
org.apache.hadoop.ozone.om.ratis.OzoneManagerRatisServer.RaftServerStatus.LEADER_AND_NOT_READY;
@@ -5525,6 +5527,10 @@ private OmBucketInfo resolveBucketLink(
       if (allowDanglingBuckets) {
         return null;
       }
+
+      if (!visited.isEmpty() && (e.getResult() == VOLUME_NOT_FOUND || 
e.getResult() == BUCKET_NOT_FOUND)) {
+        throw new OMException("Cannot follow bucket link: linked source bucket 
does not exist", e, e.getResult());
+      }
       throw e;
     }
     if (!info.isLink()) {
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java
index 6252836b57d..d507eeae8dd 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestBucketManagerImpl.java
@@ -30,6 +30,7 @@
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyMap;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.lenient;
 import static org.mockito.Mockito.mock;
@@ -44,6 +45,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.hadoop.crypto.key.KeyProvider;
 import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension;
 import org.apache.hadoop.hdds.client.DefaultReplicationConfig;
@@ -660,6 +662,84 @@ private static void denySourceRead(OmMetadataReader 
metadataReader, String sourc
     }).when(metadataReader).checkAcls(any(), any(), any(), any(), any(), 
any());
   }
 
+  @Test
+  void testResolveBucketLinkMissingSourceVolume() throws Exception {
+    String targetVolume = volumeName();
+    String missingSourceVolume = volumeName();
+    OmBucketInfo danglingLink = OmBucketInfo.newBuilder()
+        .setVolumeName(targetVolume)
+        .setBucketName("dangling-link")
+        .setSourceVolume(missingSourceVolume)
+        .setSourceBucket("any-bucket")
+        .build();
+    BucketManager bucketManager = mock(BucketManager.class);
+    when(bucketManager.getBucketInfo(targetVolume, 
"dangling-link")).thenReturn(danglingLink);
+    when(bucketManager.getBucketInfo(missingSourceVolume, "any-bucket"))
+        .thenThrow(new OMException("Volume doesn't exist", 
ResultCodes.VOLUME_NOT_FOUND));
+    OzoneManager omSpy = spy(omTestManagers.getOzoneManager());
+    HddsWhiteboxTestUtils.setInternalState(omSpy, "bucketManager", 
bucketManager);
+    when(omSpy.getAclsEnabled()).thenReturn(false);
+
+    OMException omEx = assertThrows(OMException.class,
+        () -> omSpy.resolveBucketLink(Pair.of(targetVolume, "dangling-link")));
+    assertEquals(ResultCodes.VOLUME_NOT_FOUND, omEx.getResult());
+    assertThat(omEx.getMessage()).contains("Cannot follow bucket link");
+  }
+
+  @Test
+  void testResolveBucketLinkMissingSourceBucket() throws Exception {
+    String targetVolume = volumeName();
+    String sourceVolume = volumeName();
+    OmBucketInfo danglingLink = OmBucketInfo.newBuilder()
+        .setVolumeName(targetVolume)
+        .setBucketName("dangling-link-bucket")
+        .setSourceVolume(sourceVolume)
+        .setSourceBucket("no-such-bucket")
+        .build();
+    BucketManager bucketManager = mock(BucketManager.class);
+    when(bucketManager.getBucketInfo(targetVolume, 
"dangling-link-bucket")).thenReturn(danglingLink);
+    when(bucketManager.getBucketInfo(sourceVolume, "no-such-bucket"))
+        .thenThrow(new OMException("Bucket not found", 
ResultCodes.BUCKET_NOT_FOUND));
+    OzoneManager omSpy = spy(omTestManagers.getOzoneManager());
+    HddsWhiteboxTestUtils.setInternalState(omSpy, "bucketManager", 
bucketManager);
+    when(omSpy.getAclsEnabled()).thenReturn(false);
+
+    OMException omEx = assertThrows(OMException.class,
+        () -> omSpy.resolveBucketLink(Pair.of(targetVolume, 
"dangling-link-bucket")));
+    assertEquals(ResultCodes.BUCKET_NOT_FOUND, omEx.getResult());
+    assertThat(omEx.getMessage()).contains("Cannot follow bucket link");
+  }
+
+  @Test
+  void testListKeysOnLinkWithMissingSourceVolume() throws Exception {
+    String targetVolume = volumeName();
+    String missingSourceVolume = volumeName();
+    OmBucketInfo danglingLink = OmBucketInfo.newBuilder()
+        .setVolumeName(targetVolume)
+        .setBucketName("dangling-link-list")
+        .setSourceVolume(missingSourceVolume)
+        .setSourceBucket("any-bucket")
+        .build();
+    BucketManager bucketManager = mock(BucketManager.class);
+    when(bucketManager.getBucketInfo(targetVolume, 
"dangling-link-list")).thenReturn(danglingLink);
+    when(bucketManager.getBucketInfo(missingSourceVolume, "any-bucket"))
+        .thenThrow(new OMException("Volume doesn't exist", 
ResultCodes.VOLUME_NOT_FOUND));
+    OzoneManager om = omTestManagers.getOzoneManager();
+    OzoneManager omSpy = spy(om);
+    HddsWhiteboxTestUtils.setInternalState(omSpy, "bucketManager", 
bucketManager);
+    when(omSpy.getAclsEnabled()).thenReturn(false);
+    OmMetadataReader metadataReader = (OmMetadataReader) 
HddsWhiteboxTestUtils.getInternalState(om,
+        "omMetadataReader");
+    HddsWhiteboxTestUtils.setInternalState(metadataReader, "ozoneManager", 
omSpy);
+
+    OMException omEx = assertThrows(OMException.class,
+        () -> omSpy.listKeys(targetVolume, "dangling-link-list", null, null, 
100));
+    assertEquals(ResultCodes.VOLUME_NOT_FOUND, omEx.getResult());
+    assertThat(omEx.getMessage()).contains("Cannot follow bucket link");
+    verify(bucketManager).getBucketInfo(eq(targetVolume), 
eq("dangling-link-list"));
+    verify(bucketManager).getBucketInfo(eq(missingSourceVolume), 
eq("any-bucket"));
+  }
+
   private OzoneManager createAclEnabledOmSpy(BucketManager bucketManager, 
OmMetadataReader metadataReader) {
     OzoneManager omSpy = spy(omTestManagers.getOzoneManager());
     HddsWhiteboxTestUtils.setInternalState(omSpy, "bucketManager", 
bucketManager);


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

Reply via email to