diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/bucket/OMBucketCreateRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/bucket/OMBucketCreateRequest.java index 23cbac4704a..ac308b800bc 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/bucket/OMBucketCreateRequest.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/bucket/OMBucketCreateRequest.java @@ -93,9 +93,14 @@ public OMRequest preExecute(OzoneManager ozoneManager) throws IOException { CreateBucketRequest createBucketRequest = getOmRequest().getCreateBucketRequest(); BucketInfo bucketInfo = createBucketRequest.getBucketInfo(); - // Verify resource name - OmUtils.validateBucketName(bucketInfo.getBucketName(), - ozoneManager.isStrictS3()); + + BucketLayout bucketLayout = BucketLayout.fromProto(bucketInfo.getBucketLayout()); + boolean strict = ozoneManager.isStrictS3() + || bucketLayout == BucketLayout.OBJECT_STORE; + + // OBS (Object Store) buckets must follow strict S3 bucket naming rules. + // FSO and LEGACY buckets are not strictly bound to S3 naming semantics. + OmUtils.validateBucketName(bucketInfo.getBucketName(), strict); // ACL check during preExecute if (ozoneManager.getAclsEnabled()) { diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/TestOMClientRequestWithUserInfo.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/TestOMClientRequestWithUserInfo.java index d8618b8ed8f..145ffe9854f 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/TestOMClientRequestWithUserInfo.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/TestOMClientRequestWithUserInfo.java @@ -38,6 +38,7 @@ import org.apache.hadoop.ozone.om.OMConfigKeys; import org.apache.hadoop.ozone.om.OMMetadataManager; import org.apache.hadoop.ozone.om.OMMetrics; +import org.apache.hadoop.ozone.om.OmConfig; import org.apache.hadoop.ozone.om.OmMetadataManagerImpl; import org.apache.hadoop.ozone.om.OzoneManager; import org.apache.hadoop.ozone.om.helpers.BucketLayout; @@ -79,6 +80,11 @@ public void setup() throws Exception { when(ozoneManager.getMetadataManager()).thenReturn(omMetadataManager); when(ozoneManager.getConfiguration()).thenReturn(ozoneConfiguration); + // mock OmConfig to avoid NPE in OMBucketCreateRequest.preExecute + OmConfig omConfig = mock(OmConfig.class); + when(omConfig.isFileSystemPathEnabled()).thenReturn(false); + when(ozoneManager.getConfig()).thenReturn(omConfig); + // Mock version manager to avoid NPE in preExecute OMLayoutVersionManager versionManager = mock(OMLayoutVersionManager.class); when(versionManager.getMetadataLayoutVersion()).thenReturn(0); diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/bucket/TestOMBucketCreateRequest.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/bucket/TestOMBucketCreateRequest.java index 09f3e0b9d60..49bf00b1f8f 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/bucket/TestOMBucketCreateRequest.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/bucket/TestOMBucketCreateRequest.java @@ -322,10 +322,38 @@ public void testAcceptNonS3CompliantBucketNameCreationWithStrictS3False() {"bucket_underscore", "_bucket___multi_underscore_", "bucket_"}; when(ozoneManager.isStrictS3()).thenReturn(false); for (String bucketName : nonS3CompliantBucketName) { - acceptBucketCreationHelper(volumeName, bucketName); + acceptFSOBucketCreationHelper(volumeName, bucketName); } } + @Test + public void testNonS3BucketNameRejectedForObjectStoreWhenStrictDisabled() + throws Exception { + + // strict mode disabled + ozoneManager.getConfiguration().setBoolean( + OMConfigKeys.OZONE_OM_NAMESPACE_STRICT_S3, false); + + String volumeName = UUID.randomUUID().toString(); + String bucketName = "bucket_with_underscore"; // non-S3-compliant + addCreateVolumeToTable(volumeName, omMetadataManager); + + // Explicitly set bucket layout to OBJECT_STORE so the test doesn't depend on + // defaults or mocked OM behavior. + OzoneManagerProtocolProtos.BucketInfo.Builder bucketInfo = + newBucketInfoBuilder(bucketName, volumeName) + .setBucketLayout( + OzoneManagerProtocolProtos.BucketLayoutProto.OBJECT_STORE); + + OMRequest originalRequest = newCreateBucketRequest(bucketInfo).build(); + OMBucketCreateRequest req = new OMBucketCreateRequest(originalRequest); + + OMException ex = assertThrows(OMException.class, + () -> req.preExecute(ozoneManager)); + + assertEquals(OMException.ResultCodes.INVALID_BUCKET_NAME, ex.getResult()); + } + @ParameterizedTest @ValueSource(booleans = {true, false}) public void testIgnoreClientACL(boolean ignoreClientACLs) throws Exception { @@ -478,4 +506,14 @@ public static void addCreateVolumeToTable(String volumeName, .setOwnerName(UUID.randomUUID().toString()).build(); OMRequestTestUtils.addVolumeToOM(omMetadataManager, omVolumeArgs); } + + private void acceptFSOBucketCreationHelper(String volumeName, String bucketName) + throws Exception { + OzoneManagerProtocolProtos.BucketInfo.Builder bucketInfo = + newBucketInfoBuilder(bucketName, volumeName) + .setBucketLayout(OzoneManagerProtocolProtos.BucketLayoutProto.FILE_SYSTEM_OPTIMIZED); + OMBucketCreateRequest omBucketCreateRequest = doPreExecute(bucketInfo); + doValidateAndUpdateCache(volumeName, bucketName, + omBucketCreateRequest.getOmRequest()); + } } diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/bucket/TestOMBucketCreateRequestWithFSO.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/bucket/TestOMBucketCreateRequestWithFSO.java index b9bd4029a62..107e697971f 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/bucket/TestOMBucketCreateRequestWithFSO.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/bucket/TestOMBucketCreateRequestWithFSO.java @@ -20,6 +20,7 @@ import static org.apache.hadoop.ozone.om.request.OMRequestTestUtils.newBucketInfoBuilder; import static org.apache.hadoop.ozone.om.request.OMRequestTestUtils.newCreateBucketRequest; import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.BucketLayoutProto.FILE_SYSTEM_OPTIMIZED; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -182,4 +183,36 @@ protected void doValidateAndUpdateCache(String volumeName, String bucketName, verifySuccessCreateBucketResponse(omClientResponse.getOMResponse()); } + + @Test + public void testNonS3BucketNameAllowedForFSOWhenStrictDisabled() throws Exception { + // Arrange + ozoneManager.getConfiguration().setBoolean( + OMConfigKeys.OZONE_OM_NAMESPACE_STRICT_S3, false); + + when(ozoneManager.getOMDefaultBucketLayout()).thenReturn( + BucketLayout.FILE_SYSTEM_OPTIMIZED); + + String volumeName = UUID.randomUUID().toString(); + String bucketName = "bucket_with_underscore"; // non-S3-compliant name + addCreateVolumeToTable(volumeName, omMetadataManager); + + OzoneManagerProtocolProtos.BucketInfo.Builder bucketInfo = + newBucketInfoBuilder(bucketName, volumeName) + .setBucketLayout(FILE_SYSTEM_OPTIMIZED) + .addMetadata(OMRequestTestUtils.fsoMetadata()); + + OMRequest originalRequest = newCreateBucketRequest(bucketInfo).build(); + OMBucketCreateRequest req = new OMBucketCreateRequest(originalRequest); + + // Act + OMRequest modifiedRequest = req.preExecute(ozoneManager); + + // Assert: validateAndUpdateCache should succeed + assertDoesNotThrow(() -> { + OMBucketCreateRequest omReq = new OMBucketCreateRequest(modifiedRequest); + omReq.setUGI(UserGroupInformation.getCurrentUser()); + omReq.validateAndUpdateCache(ozoneManager, 1); + }); + } }