Skip to content
14 changes: 14 additions & 0 deletions hadoop-ozone/dist/src/main/smoketest/basic/links.robot
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment thread
sarvekshayr marked this conversation as resolved.
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,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;
Expand All @@ -104,6 +105,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;
Expand Down Expand Up @@ -5494,6 +5496,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()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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"));
}

Comment thread
sarvekshayr marked this conversation as resolved.
private OzoneManager createAclEnabledOmSpy(BucketManager bucketManager, OmMetadataReader metadataReader) {
OzoneManager omSpy = spy(omTestManagers.getOzoneManager());
HddsWhiteboxTestUtils.setInternalState(omSpy, "bucketManager", bucketManager);
Expand Down
Loading