Skip to content
Merged
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 @@ -61,7 +61,12 @@ public enum OzoneManagerVersion implements ComponentVersion {
S3_BUCKET_TAGGING_API(13,
"OzoneManager version that supports S3 bucket tagging APIs, such as "
+ "PutBucketTagging, GetBucketTagging, and DeleteBucketTagging"),


GET_FILE_STATUS_REJECTS_OBS(14,
"OzoneManager version that rejects getFileStatus on OBJECT_STORE "
+ "buckets server-side, so file system clients no longer need the "
+ "client-side InfoBucket layout check"),

FUTURE_VERSION(-1, "Used internally in the client when the server side is "
+ " newer and an unknown server version has arrived to the client.");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.apache.hadoop.io.Text;
import org.apache.hadoop.ozone.OzoneAcl;
import org.apache.hadoop.ozone.OzoneFsServerDefaults;
import org.apache.hadoop.ozone.OzoneManagerVersion;
import org.apache.hadoop.ozone.client.BucketArgs;
import org.apache.hadoop.ozone.client.OzoneBucket;
import org.apache.hadoop.ozone.client.OzoneKey;
Expand Down Expand Up @@ -1063,6 +1064,14 @@ TenantUserList listUsersInTenant(String tenantId, String prefix)
*/
OzoneFsServerDefaults getServerDefaults() throws IOException;

/**
* Returns the negotiated Ozone Manager version for the connected cluster.
* In an HA cluster this is the minimum version across all OMs, so callers
* can safely gate client behavior on new server-side features.
* @return the effective Ozone Manager version.
*/
OzoneManagerVersion getOmVersion();

/**
* Get KMS client provider.
* @return KMS client provider.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2845,6 +2845,11 @@ public KeyProvider call() throws Exception {
}
}

@Override
public OzoneManagerVersion getOmVersion() {
return omVersion;
}

@Override
public OzoneFsServerDefaults getServerDefaults() throws IOException {
long now = Time.monotonicNow();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1835,16 +1835,74 @@ void testBucketDefaultsShouldBeInheritedToFileForEC()
assertEquals(ReplicationType.EC.name(), key.getReplicationConfig().getReplicationType().name());
}

@Test
void testGetFileStatusUsesSingleOmRpc() throws Exception {
String keyName = "single-rpc-" + RandomStringUtils.secure().nextAlphabetic(5);
Path filePath = new Path(bucketPath, keyName);
ContractTestUtils.touch(fs, filePath);

OMMetrics metrics = getOMMetrics();
long bucketInfosBefore = metrics.getNumBucketInfos();
long getFileStatusBefore = metrics.getNumGetFileStatus();

FileStatus status = fs.getFileStatus(filePath);
assertTrue(status.isFile());

assertEquals(bucketInfosBefore, metrics.getNumBucketInfos(),
"getFileStatus must not trigger InfoBucket");
assertEquals(getFileStatusBefore + 1, metrics.getNumGetFileStatus());

long getFileStatusAfterFirst = metrics.getNumGetFileStatus();
fs.getFileStatus(filePath);
assertEquals(bucketInfosBefore, metrics.getNumBucketInfos());
assertEquals(getFileStatusAfterFirst + 1, metrics.getNumGetFileStatus());
}

@Test
void testGetFileStatusRejectsObsBucket() throws Exception {
OzoneBucket obsBucket =
DataTestUtil.createVolumeAndBucket(client, BucketLayout.OBJECT_STORE);
Path obsBucketPath = new Path(
new Path(OZONE_URI_DELIMITER, obsBucket.getVolumeName()),
obsBucket.getName());
String keyName = "obs-key-" + RandomStringUtils.secure().nextAlphabetic(5);
DataTestUtil.createKey(obsBucket, keyName,
"data".getBytes(StandardCharsets.UTF_8));
Path keyPath = new Path(obsBucketPath, keyName);

OMMetrics metrics = getOMMetrics();
long bucketInfosBefore = metrics.getNumBucketInfos();

IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
() -> fs.getFileStatus(keyPath));
assertThat(exception.getMessage()).contains(obsBucket.getName());
assertThat(exception.getMessage()).contains("OBJECT_STORE");
assertEquals(bucketInfosBefore, metrics.getNumBucketInfos(),
"getFileStatus must not trigger InfoBucket");
}

@Test
void testGetFileStatus() throws Exception {
String volumeNameLocal = getRandomNonExistVolumeName();
String bucketNameLocal = RandomStringUtils.secure().nextNumeric(5);
Path volume = new Path("/" + volumeNameLocal);
fs.mkdirs(volume);
assertThrows(OMException.class,
() -> fs.getFileStatus(new Path(volume, bucketNameLocal)));
// Cleanup
fs.delete(volume, false);
try {
FileNotFoundException exception = assertThrows(FileNotFoundException.class,
() -> fs.getFileStatus(new Path(volume, bucketNameLocal)));
assertThat(exception.getMessage()).contains("Bucket doesn't exist");
} finally {
fs.delete(volume, false);
}
}

@Test
void testGetFileStatusMissingFile() throws Exception {
Path missingFile = new Path(bucketPath, "missing-file-" +
RandomStringUtils.secure().nextAlphanumeric(5));
FileNotFoundException exception = assertThrows(FileNotFoundException.class,
() -> fs.getFileStatus(missingFile));
assertThat(exception.getMessage()).contains("No such file or directory");
}

@Test
Expand Down
Loading
Loading