Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2993,8 +2993,13 @@ public List<OmBucketInfo> listBuckets(String volumeName, String startKey,
volumeName, null, null);
}
metrics.incNumBucketLists();
return bucketManager.listBuckets(volumeName,
List<OmBucketInfo> buckets = bucketManager.listBuckets(volumeName,
startKey, prefix, maxNumOfBuckets, hasSnapshot);
List<OmBucketInfo> enrichedBuckets = new ArrayList<>(buckets.size());
for (OmBucketInfo bucket : buckets) {
enrichedBuckets.add(enrichLinkBucketInfo(bucket));
}
return enrichedBuckets;
} catch (IOException ex) {
metrics.incNumBucketListFails();
auditSuccess = false;
Expand All @@ -3009,6 +3014,50 @@ public List<OmBucketInfo> listBuckets(String volumeName, String startKey,
}
}

/**
* For link buckets, follows the link chain and overlays the source bucket's
* operational properties onto the link's {@link OmBucketInfo}. Non-link
* buckets and dangling links are returned unchanged.
*/
private OmBucketInfo enrichLinkBucketInfo(OmBucketInfo bucketInfo)
throws IOException {
if (!bucketInfo.isLink()) {
return bucketInfo;
}
// We already know that `bucketInfo` is a linked one,
// so we skip one `getBucketInfo` and start with the known link.
ResolvedBucket resolvedBucket =
resolveBucketLink(Pair.of(
bucketInfo.getSourceVolume(),
bucketInfo.getSourceBucket()),
true);

// If it is a dangling link it means no real bucket exists,
// for example, it could have been deleted, but the links still present.
if (resolvedBucket.isDangling()) {
return bucketInfo;
}
OmBucketInfo realBucket =
bucketManager.getBucketInfo(
resolvedBucket.realVolume(),
resolvedBucket.realBucket());
// Pass the real bucket metadata in the link bucket info.
return bucketInfo.toBuilder()
.setDefaultReplicationConfig(
realBucket.getDefaultReplicationConfig())
.setIsVersionEnabled(realBucket.getIsVersionEnabled())
.setStorageType(realBucket.getStorageType())
.setQuotaInBytes(realBucket.getQuotaInBytes())
.setQuotaInNamespace(realBucket.getQuotaInNamespace())
.setUsedBytes(realBucket.getUsedBytes())
.setSnapshotUsedBytes(realBucket.getSnapshotUsedBytes())
.setSnapshotUsedNamespace(realBucket.getSnapshotUsedNamespace())
.setUsedNamespace(realBucket.getUsedNamespace())
.addAllMetadata(realBucket.getMetadata())
.setBucketLayout(realBucket.getBucketLayout())
.build();
}

/**
* Gets the bucket information.
*
Expand All @@ -3030,46 +3079,8 @@ public OmBucketInfo getBucketInfo(String volume, String bucket)
}
metrics.incNumBucketInfos();

OmBucketInfo bucketInfo = bucketManager.getBucketInfo(volume, bucket);

// No links - return the bucket info right away.
if (!bucketInfo.isLink()) {
return bucketInfo;
}
// Otherwise follow the links to find the real bucket.
// We already know that `bucketInfo` is a linked one,
// so we skip one `getBucketInfo` and start with the known link.
ResolvedBucket resolvedBucket =
resolveBucketLink(Pair.of(
bucketInfo.getSourceVolume(),
bucketInfo.getSourceBucket()),
true);

// If it is a dangling link it means no real bucket exists,
// for example, it could have been deleted, but the links still present.
if (!resolvedBucket.isDangling()) {
OmBucketInfo realBucket =
bucketManager.getBucketInfo(
resolvedBucket.realVolume(),
resolvedBucket.realBucket());
// Pass the real bucket metadata in the link bucket info.
return bucketInfo.toBuilder()
.setDefaultReplicationConfig(
realBucket.getDefaultReplicationConfig())
.setIsVersionEnabled(realBucket.getIsVersionEnabled())
.setStorageType(realBucket.getStorageType())
.setQuotaInBytes(realBucket.getQuotaInBytes())
.setQuotaInNamespace(realBucket.getQuotaInNamespace())
.setUsedBytes(realBucket.getUsedBytes())
.setSnapshotUsedBytes(realBucket.getSnapshotUsedBytes())
.setSnapshotUsedNamespace(realBucket.getSnapshotUsedNamespace())
.setUsedNamespace(realBucket.getUsedNamespace())
.addAllMetadata(realBucket.getMetadata())
.setBucketLayout(realBucket.getBucketLayout())
.build();
}
// If no real bucket exists, return the requested one's info.
return bucketInfo;
return enrichLinkBucketInfo(
bucketManager.getBucketInfo(volume, bucket));
} catch (Exception ex) {
metrics.incNumBucketInfoFails();
auditSuccess = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.hadoop.crypto.key.KeyProvider;
import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension;
import org.apache.hadoop.hdds.client.DefaultReplicationConfig;
import org.apache.hadoop.hdds.client.ECReplicationConfig;
import org.apache.hadoop.hdds.client.RatisReplicationConfig;
import org.apache.hadoop.hdds.client.StandaloneReplicationConfig;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.protocol.StorageType;
Expand Down Expand Up @@ -460,4 +462,82 @@ public void testLinkedBucketResolution() throws Exception {
bucketInfo.getIsVersionEnabled(),
storedLinkBucket.getIsVersionEnabled());
}

@Test
public void testListBucketsResolvesFsoAndObsLinkLayouts() throws Exception {
String volume = volumeName();
createSampleVol(volume);

ECReplicationConfig fsoReplication = new ECReplicationConfig(3, 2);
OmBucketInfo fsoSource = OmBucketInfo.newBuilder()
.setVolumeName(volume)
.setBucketName("fso-source")
.setBucketLayout(BucketLayout.FILE_SYSTEM_OPTIMIZED)
.setDefaultReplicationConfig(new DefaultReplicationConfig(fsoReplication))
.build();
writeClient.createBucket(fsoSource);

RatisReplicationConfig obsReplication =
RatisReplicationConfig.getInstance(ReplicationFactor.THREE);
OmBucketInfo obsSource = OmBucketInfo.newBuilder()
.setVolumeName(volume)
.setBucketName("obs-source")
.setBucketLayout(BucketLayout.OBJECT_STORE)
.setDefaultReplicationConfig(new DefaultReplicationConfig(obsReplication))
.build();
writeClient.createBucket(obsSource);

OmBucketInfo fsoLink = OmBucketInfo.newBuilder()
.setVolumeName(volume)
.setBucketName("link-fso")
.setSourceVolume(volume)
.setSourceBucket("fso-source")
.build();
writeClient.createBucket(fsoLink);

OmBucketInfo obsLink = OmBucketInfo.newBuilder()
.setVolumeName(volume)
.setBucketName("link-obs")
.setSourceVolume(volume)
.setSourceBucket("obs-source")
.build();
writeClient.createBucket(obsLink);

OmBucketInfo fsoLinkInfo = writeClient.getBucketInfo(volume, "link-fso");
assertEquals(BucketLayout.FILE_SYSTEM_OPTIMIZED, fsoLinkInfo.getBucketLayout());
assertEquals(fsoReplication,
fsoLinkInfo.getDefaultReplicationConfig().getReplicationConfig());

OmBucketInfo obsLinkInfo = writeClient.getBucketInfo(volume, "link-obs");
assertEquals(BucketLayout.OBJECT_STORE, obsLinkInfo.getBucketLayout());
assertEquals(obsReplication,
obsLinkInfo.getDefaultReplicationConfig().getReplicationConfig());

List<OmBucketInfo> listedBuckets =
writeClient.listBuckets(volume, "", "", 100, false);

OmBucketInfo listedFsoLink = null;
OmBucketInfo listedObsLink = null;
for (OmBucketInfo listedBucket : listedBuckets) {
if ("link-fso".equals(listedBucket.getBucketName())) {
listedFsoLink = listedBucket;
} else if ("link-obs".equals(listedBucket.getBucketName())) {
listedObsLink = listedBucket;
}
}
assertNotNull(listedFsoLink, "link-fso not found in listBuckets response");
assertNotNull(listedObsLink, "link-obs not found in listBuckets response");

assertEquals(BucketLayout.FILE_SYSTEM_OPTIMIZED, listedFsoLink.getBucketLayout());
assertEquals(fsoReplication,
listedFsoLink.getDefaultReplicationConfig().getReplicationConfig());
assertEquals(volume, listedFsoLink.getSourceVolume());
assertEquals("fso-source", listedFsoLink.getSourceBucket());

assertEquals(BucketLayout.OBJECT_STORE, listedObsLink.getBucketLayout());
assertEquals(obsReplication,
listedObsLink.getDefaultReplicationConfig().getReplicationConfig());
assertEquals(volume, listedObsLink.getSourceVolume());
assertEquals("obs-source", listedObsLink.getSourceBucket());
}
}