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]