diff --git a/hadoop-ozone/dist/src/main/compose/testlib.sh b/hadoop-ozone/dist/src/main/compose/testlib.sh index 9c3d6c49c111..d7604f3720f5 100755 --- a/hadoop-ozone/dist/src/main/compose/testlib.sh +++ b/hadoop-ozone/dist/src/main/compose/testlib.sh @@ -147,8 +147,8 @@ start_docker_env(){ create_results_dir export OZONE_SAFEMODE_MIN_DATANODES="${datanode_count}" - docker-compose --no-ansi down - if ! { docker-compose --no-ansi up -d --scale datanode="${datanode_count}" \ + docker-compose --ansi never down + if ! { docker-compose --ansi never up -d --scale datanode="${datanode_count}" \ && wait_for_safemode_exit \ && wait_for_om_leader ; }; then [[ -n "$OUTPUT_NAME" ]] || OUTPUT_NAME="$COMPOSE_ENV_NAME" @@ -235,7 +235,7 @@ execute_command_in_container(){ ## @param List of container names, eg datanode_1 datanode_2 stop_containers() { set -e - docker-compose --no-ansi stop $@ + docker-compose --ansi never stop $@ set +e } @@ -244,7 +244,7 @@ stop_containers() { ## @param List of container names, eg datanode_1 datanode_2 start_containers() { set -e - docker-compose --no-ansi start $@ + docker-compose --ansi never start $@ set +e } @@ -280,9 +280,9 @@ wait_for_port(){ ## @description Stops a docker-compose based test environment (with saving the logs) stop_docker_env(){ - docker-compose --no-ansi logs > "$RESULT_DIR/docker-$OUTPUT_NAME.log" + docker-compose --ansi never logs > "$RESULT_DIR/docker-$OUTPUT_NAME.log" if [ "${KEEP_RUNNING:-false}" = false ]; then - docker-compose --no-ansi down + docker-compose --ansi never down fi } diff --git a/hadoop-ozone/dist/src/main/compose/upgrade/compose/ha/docker-config b/hadoop-ozone/dist/src/main/compose/upgrade/compose/ha/docker-config index 9a2b70012089..8e01004d8d70 100644 --- a/hadoop-ozone/dist/src/main/compose/upgrade/compose/ha/docker-config +++ b/hadoop-ozone/dist/src/main/compose/upgrade/compose/ha/docker-config @@ -24,8 +24,8 @@ OZONE-SITE.XML_ozone.om.address.omservice.om1=om1 OZONE-SITE.XML_ozone.om.address.omservice.om2=om2 OZONE-SITE.XML_ozone.om.address.omservice.om3=om3 OZONE-SITE.XML_ozone.om.ratis.enable=true -// setting ozone.scm.ratis.enable to false for now, as scm ha upgrade is -// not supported yet. This is supposed to work without SCM HA configuration +# setting ozone.scm.ratis.enable to false for now, as scm ha upgrade is +# not supported yet. This is supposed to work without SCM HA configuration OZONE-SITE.XML_ozone.scm.ratis.enable=false OZONE-SITE.XML_ozone.scm.pipeline.creation.interval=30s OZONE-SITE.XML_ozone.scm.pipeline.owner.container.count=1 diff --git a/hadoop-ozone/dist/src/main/compose/upgrade/upgrades/non-rolling-upgrade/1.1.0-1.2.0/callback.sh b/hadoop-ozone/dist/src/main/compose/upgrade/upgrades/non-rolling-upgrade/1.1.0-1.2.0/callback.sh index b533e6c03da9..96528bbc849a 100755 --- a/hadoop-ozone/dist/src/main/compose/upgrade/upgrades/non-rolling-upgrade/1.1.0-1.2.0/callback.sh +++ b/hadoop-ozone/dist/src/main/compose/upgrade/upgrades/non-rolling-upgrade/1.1.0-1.2.0/callback.sh @@ -64,8 +64,7 @@ with_old_version_downgraded() { with_new_version_finalized() { _check_hdds_mlvs 2 - # OM currently only has one layout version. - _check_om_mlvs 0 + _check_om_mlvs 1 validate old1 validate new1 diff --git a/hadoop-ozone/dist/src/main/smoketest/upgrade/finalize.robot b/hadoop-ozone/dist/src/main/smoketest/upgrade/finalize.robot index 288f9c01507f..b70f3ca14781 100644 --- a/hadoop-ozone/dist/src/main/smoketest/upgrade/finalize.robot +++ b/hadoop-ozone/dist/src/main/smoketest/upgrade/finalize.robot @@ -19,7 +19,7 @@ Resource ../commonlib.robot Test Timeout 5 minutes Test Setup Run Keyword if '${SECURITY_ENABLED}' == 'true' Kinit test user testuser testuser.keytab -** Test Cases *** +*** Test Cases *** Finalize SCM ${result} = Execute ozone admin scm finalizeupgrade #Wait Until Keyword Succeeds 3min 10sec Should contain ${result} OM Preparation successful! diff --git a/hadoop-ozone/dist/src/main/smoketest/upgrade/generate.robot b/hadoop-ozone/dist/src/main/smoketest/upgrade/generate.robot index 0a146049861d..7493c65ea91b 100644 --- a/hadoop-ozone/dist/src/main/smoketest/upgrade/generate.robot +++ b/hadoop-ozone/dist/src/main/smoketest/upgrade/generate.robot @@ -18,6 +18,7 @@ Documentation Generate data Library OperatingSystem Library BuiltIn Resource ../commonlib.robot +Resource ../s3/commonawslib.robot Test Timeout 5 minutes *** Variables *** @@ -29,5 +30,30 @@ Create a volume, bucket and key Should not contain ${output} Failed ${output} = Execute ozone sh bucket create /${PREFIX}-volume/${PREFIX}-bucket Should not contain ${output} Failed - ${output} = Execute ozone sh key put /${PREFIX}-volume/${PREFIX}-bucket/${PREFIX}-key /opt/hadoop/NOTICE.txt + Execute and checkrc echo "${PREFIX}: key created using Ozone Shell" > /tmp/sourcekey 0 + ${output} = Execute ozone sh key put /${PREFIX}-volume/${PREFIX}-bucket/${PREFIX}-key /tmp/sourcekey Should not contain ${output} Failed + Execute and checkrc rm /tmp/sourcekey 0 + +Create a bucket and key in volume s3v + ${output} = Execute ozone sh bucket create /s3v/${PREFIX}-bucket + Should not contain ${output} Failed + Execute and checkrc echo "${PREFIX}: another key created using Ozone Shell" > /tmp/sourcekey 0 + ${output} = Execute ozone sh key put /s3v/${PREFIX}-bucket/key1-shell /tmp/sourcekey + Should not contain ${output} Failed + Execute and checkrc rm /tmp/sourcekey 0 + +Setup credentials for S3 + # TODO: Run "Setup secure v4 headers" instead when security is enabled + Run Keyword Setup dummy credentials for S3 + +Try to create a bucket using S3 API + # Note: S3 API does not return error if the bucket already exists + ${output} = Create bucket with name ${PREFIX}-bucket + Should Be Equal ${output} ${None} + +Create key using S3 API + Execute and checkrc echo "${PREFIX}: key created using S3 API" > /tmp/sourcekey 0 + ${output} = Execute AWSS3APICli and checkrc put-object --bucket ${PREFIX}-bucket --key key2-s3api --body /tmp/sourcekey 0 + Should not contain ${output} error + Execute and checkrc rm /tmp/sourcekey 0 diff --git a/hadoop-ozone/dist/src/main/smoketest/upgrade/prepare.robot b/hadoop-ozone/dist/src/main/smoketest/upgrade/prepare.robot index 0f6d7a097618..b1b4095d6eb2 100644 --- a/hadoop-ozone/dist/src/main/smoketest/upgrade/prepare.robot +++ b/hadoop-ozone/dist/src/main/smoketest/upgrade/prepare.robot @@ -19,7 +19,7 @@ Resource ../commonlib.robot Test Timeout 5 minutes Test Setup Run Keyword if '${SECURITY_ENABLED}' == 'true' Kinit test user testuser testuser.keytab -** Test Cases *** +*** Test Cases *** Prepare Ozone Manager ${result} = Execute ozone admin om prepare -id %{OM_SERVICE_ID} Wait Until Keyword Succeeds 3min 10sec Should contain ${result} OM Preparation successful! diff --git a/hadoop-ozone/dist/src/main/smoketest/upgrade/validate.robot b/hadoop-ozone/dist/src/main/smoketest/upgrade/validate.robot index 4461ddf23834..9f5b0a08bf5c 100644 --- a/hadoop-ozone/dist/src/main/smoketest/upgrade/validate.robot +++ b/hadoop-ozone/dist/src/main/smoketest/upgrade/validate.robot @@ -18,6 +18,7 @@ Documentation Smoketest ozone cluster startup Library OperatingSystem Library BuiltIn Resource ../commonlib.robot +Resource ../s3/commonawslib.robot Test Timeout 5 minutes *** Variables *** @@ -28,3 +29,24 @@ Read data from previously created key ${random} = Generate Random String 5 [NUMBERS] ${output} = Execute ozone sh key get /${PREFIX}-volume/${PREFIX}-bucket/${PREFIX}-key /tmp/key-${random} Should not contain ${output} Failed + ${output} = Execute and checkrc cat /tmp/key-${random} 0 + Should contain ${output} ${PREFIX}: key created using Ozone Shell + Execute and checkrc rm /tmp/key-${random} 0 + +Setup credentials for S3 + # TODO: Run "Setup secure v4 headers" instead when security is enabled + Run Keyword Setup dummy credentials for S3 + +Read key created with Ozone Shell using S3 API + ${output} = Execute AWSS3APICli and checkrc get-object --bucket ${PREFIX}-bucket --key key1-shell /tmp/get-result 0 + Should contain ${output} "ContentLength" + ${output} = Execute and checkrc cat /tmp/get-result 0 + Should contain ${output} ${PREFIX}: another key created using Ozone Shell + Execute and checkrc rm /tmp/get-result 0 + +Read key created with S3 API using S3 API + ${output} = Execute AWSS3APICli and checkrc get-object --bucket ${PREFIX}-bucket --key key2-s3api /tmp/get-result 0 + Should contain ${output} "ContentLength" + ${output} = Execute and checkrc cat /tmp/get-result 0 + Should contain ${output} ${PREFIX}: key created using S3 API + Execute and checkrc rm /tmp/get-result 0 diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/hdds/upgrade/TestHDDSUpgrade.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/hdds/upgrade/TestHDDSUpgrade.java index e7f6f34a5bab..6f356ac49c7e 100644 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/hdds/upgrade/TestHDDSUpgrade.java +++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/hdds/upgrade/TestHDDSUpgrade.java @@ -28,7 +28,6 @@ import static org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_DATANODE_PIPELINE_LIMIT; import static org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_HA_ENABLE_KEY; import static org.apache.hadoop.hdds.scm.pipeline.Pipeline.PipelineState.OPEN; -import static org.apache.hadoop.hdds.upgrade.HDDSLayoutFeature.INITIAL_VERSION; import static org.apache.hadoop.ozone.upgrade.InjectedUpgradeFinalizationExecutor.UpgradeTestInjectionPoints.AFTER_COMPLETE_FINALIZATION; import static org.apache.hadoop.ozone.upgrade.InjectedUpgradeFinalizationExecutor.UpgradeTestInjectionPoints.AFTER_POST_FINALIZE_UPGRADE; import static org.apache.hadoop.ozone.upgrade.InjectedUpgradeFinalizationExecutor.UpgradeTestInjectionPoints.AFTER_PRE_FINALIZE_UPGRADE; @@ -78,6 +77,7 @@ import org.apache.hadoop.ozone.client.io.OzoneOutputStream; import org.apache.hadoop.ozone.container.common.interfaces.Container; import org.apache.hadoop.ozone.container.common.statemachine.DatanodeStateMachine; +import org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature; import org.apache.hadoop.ozone.upgrade.BasicUpgradeFinalizer; import org.apache.hadoop.ozone.upgrade.InjectedUpgradeFinalizationExecutor; import org.apache.hadoop.ozone.upgrade.InjectedUpgradeFinalizationExecutor.UpgradeTestInjectionPoints; @@ -155,8 +155,9 @@ public static void initClass() { .setTotalPipelineNumLimit(NUM_DATA_NODES + 1) .setHbInterval(500) .setHbProcessorInterval(500) - .setScmLayoutVersion(INITIAL_VERSION.layoutVersion()) - .setDnLayoutVersion(INITIAL_VERSION.layoutVersion()); + .setOmLayoutVersion(OMLayoutFeature.INITIAL_VERSION.layoutVersion()) + .setScmLayoutVersion(HDDSLayoutFeature.INITIAL_VERSION.layoutVersion()) + .setDnLayoutVersion(HDDSLayoutFeature.INITIAL_VERSION.layoutVersion()); // Setting the provider to a max of 100 clusters. Some of the tests here // use multiple clusters, so its hard to know exactly how many will be @@ -219,7 +220,7 @@ private void createKey() throws IOException { * Helper function to test Pre-Upgrade conditions on the SCM */ private void testPreUpgradeConditionsSCM() { - Assert.assertEquals(INITIAL_VERSION.layoutVersion(), + Assert.assertEquals(HDDSLayoutFeature.INITIAL_VERSION.layoutVersion(), scmVersionManager.getMetadataLayoutVersion()); for (ContainerInfo ci : scmContainerManager.getContainers()) { Assert.assertEquals(HddsProtos.LifeCycleState.OPEN, ci.getState()); diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantVolume.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantVolume.java index ee2d9ab197be..0bb1d587ba53 100644 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantVolume.java +++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/multitenant/TestMultiTenantVolume.java @@ -26,13 +26,25 @@ import org.apache.hadoop.ozone.client.rpc.RpcClient; import org.apache.hadoop.ozone.om.OMMultiTenantManagerImpl; import org.apache.hadoop.ozone.om.exceptions.OMException; +import org.apache.hadoop.ozone.om.helpers.S3SecretValue; +import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol; import org.apache.hadoop.ozone.om.protocol.S3Auth; +import org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature; +import org.apache.hadoop.ozone.upgrade.UpgradeFinalizer; +import org.apache.ozone.test.GenericTestUtils; +import org.apache.ozone.test.LambdaTestUtils; +import org.apache.ozone.test.LambdaTestUtils.VoidCallable; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; +import java.io.IOException; import java.util.UUID; +import java.util.concurrent.TimeoutException; + +import static org.apache.hadoop.ozone.admin.scm.FinalizeUpgradeCommandUtil.isDone; +import static org.apache.hadoop.ozone.admin.scm.FinalizeUpgradeCommandUtil.isStarting; /** * Tests that S3 requests for a tenant are directed to that tenant's volume, @@ -43,15 +55,24 @@ public class TestMultiTenantVolume { private static MiniOzoneCluster cluster; private static String s3VolumeName; + private static final String TENANT_NAME = "tenant"; + private static final String USER_PRINCIPAL = "username"; + private static final String BUCKET_NAME = "bucket"; + private static final String ACCESS_ID = UUID.randomUUID().toString(); + @BeforeClass public static void initClusterProvider() throws Exception { OzoneConfiguration conf = new OzoneConfiguration(); conf.setBoolean( OMMultiTenantManagerImpl.OZONE_OM_TENANT_DEV_SKIP_RANGER, true); MiniOzoneCluster.Builder builder = MiniOzoneCluster.newBuilder(conf) - .withoutDatanodes(); + .withoutDatanodes() + .setOmLayoutVersion(OMLayoutFeature.INITIAL_VERSION.layoutVersion()); cluster = builder.build(); s3VolumeName = HddsClientUtils.getDefaultS3VolumeName(conf); + + preFinalizationChecks(getStoreForAccessID(ACCESS_ID)); + finalizeOMUpgrade(); } @AfterClass @@ -59,6 +80,82 @@ public static void shutdownClusterProvider() { cluster.shutdown(); } + private static void expectFailurePreFinalization(VoidCallable eval) + throws Exception { + LambdaTestUtils.intercept(OMException.class, + "cannot be invoked before finalization", eval); + } + + /** + * Perform sanity checks before triggering upgrade finalization. + */ + private static void preFinalizationChecks(ObjectStore store) + throws Exception { + + // None of the tenant APIs is usable before the upgrade finalization step + expectFailurePreFinalization( + store::listTenant); + expectFailurePreFinalization(() -> + store.listUsersInTenant(TENANT_NAME, "")); + expectFailurePreFinalization(() -> + store.tenantGetUserInfo(USER_PRINCIPAL)); + expectFailurePreFinalization(() -> + store.createTenant(TENANT_NAME)); + expectFailurePreFinalization(() -> + store.tenantAssignUserAccessId(USER_PRINCIPAL, TENANT_NAME, ACCESS_ID)); + expectFailurePreFinalization(() -> + store.tenantAssignAdmin(USER_PRINCIPAL, TENANT_NAME, true)); + expectFailurePreFinalization(() -> + store.tenantRevokeAdmin(ACCESS_ID, TENANT_NAME)); + expectFailurePreFinalization(() -> + store.tenantRevokeUserAccessId(ACCESS_ID)); + expectFailurePreFinalization(() -> + store.deleteTenant(TENANT_NAME)); + + // S3 get/set/revoke secret APIs still work before finalization + final String accessId = "testUser1accessId1"; + S3SecretValue s3SecretValue = store.getS3Secret(accessId); + Assert.assertEquals(accessId, s3SecretValue.getAwsAccessKey()); + final String setSecret = "testsecret"; + s3SecretValue = store.setS3Secret(accessId, setSecret); + Assert.assertEquals(accessId, s3SecretValue.getAwsAccessKey()); + Assert.assertEquals(setSecret, s3SecretValue.getAwsSecret()); + store.revokeS3Secret(accessId); + } + + /** + * Trigger OM upgrade finalization from the client and block until completion + * (status FINALIZATION_DONE). + */ + private static void finalizeOMUpgrade() + throws IOException, InterruptedException, TimeoutException { + + // Trigger OM upgrade finalization. Ref: FinalizeUpgradeSubCommand#call + final OzoneManagerProtocol client = cluster.getRpcClient().getObjectStore() + .getClientProxy().getOzoneManagerClient(); + final String upgradeClientID = "Test-Upgrade-Client-" + UUID.randomUUID(); + UpgradeFinalizer.StatusAndMessages finalizationResponse = + client.finalizeUpgrade(upgradeClientID); + + // The status should transition as soon as the client call above returns + Assert.assertTrue(isStarting(finalizationResponse.status())); + + // Wait for the finalization to be marked as done. + // 10s timeout should be plenty. + GenericTestUtils.waitFor(() -> { + try { + final UpgradeFinalizer.StatusAndMessages progress = + client.queryUpgradeFinalizationProgress( + upgradeClientID, false, false); + return isDone(progress.status()); + } catch (IOException e) { + Assert.fail("Unexpected exception while waiting for " + + "the OM upgrade to finalize: " + e.getMessage()); + } + return false; + }, 500, 10000); + } + @Test public void testDefaultS3Volume() throws Exception { final String bucketName = "bucket"; @@ -79,31 +176,31 @@ public void testDefaultS3Volume() throws Exception { @Test public void testS3TenantVolume() throws Exception { - final String tenant = "tenant"; - final String principal = "username"; - final String bucketName = "bucket"; - final String accessID = UUID.randomUUID().toString(); - ObjectStore store = getStoreForAccessID(accessID); - store.createTenant(tenant); - store.tenantAssignUserAccessId(principal, tenant, accessID); + ObjectStore store = getStoreForAccessID(ACCESS_ID); + + store.createTenant(TENANT_NAME); + store.tenantAssignUserAccessId(USER_PRINCIPAL, TENANT_NAME, ACCESS_ID); // S3 volume pointed to by the store should be for the tenant. - Assert.assertEquals(tenant, store.getS3Volume().getName()); + Assert.assertEquals(TENANT_NAME, store.getS3Volume().getName()); // Create bucket in the tenant volume. - store.createS3Bucket(bucketName); - OzoneBucket bucket = store.getS3Bucket(bucketName); - Assert.assertEquals(tenant, bucket.getVolumeName()); + store.createS3Bucket(BUCKET_NAME); + OzoneBucket bucket = store.getS3Bucket(BUCKET_NAME); + Assert.assertEquals(TENANT_NAME, bucket.getVolumeName()); // A different user should not see bucket, since they will be directed to // the s3 volume. ObjectStore store2 = getStoreForAccessID(UUID.randomUUID().toString()); - assertS3BucketNotFound(store2, bucketName); + assertS3BucketNotFound(store2, BUCKET_NAME); // Delete bucket. - store.deleteS3Bucket(bucketName); - assertS3BucketNotFound(store, bucketName); + store.deleteS3Bucket(BUCKET_NAME); + assertS3BucketNotFound(store, BUCKET_NAME); + + store.tenantRevokeUserAccessId(ACCESS_ID); + store.deleteTenant(TENANT_NAME); } /** @@ -112,7 +209,7 @@ public void testS3TenantVolume() throws Exception { * by the ObjectStore. */ private void assertS3BucketNotFound(ObjectStore store, String bucketName) - throws Exception { + throws IOException { try { store.getS3Bucket(bucketName); } catch(OMException ex) { @@ -131,7 +228,8 @@ private void assertS3BucketNotFound(ObjectStore store, String bucketName) } } - private ObjectStore getStoreForAccessID(String accessID) throws Exception { + private static ObjectStore getStoreForAccessID(String accessID) + throws IOException { // Cluster provider will modify our provided configuration. We must use // this version to build the client. OzoneConfiguration conf = cluster.getOzoneManager().getConfiguration(); diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMAssignUserToTenantRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMAssignUserToTenantRequest.java index 8d796e13bf2b..6e686ca53063 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMAssignUserToTenantRequest.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMAssignUserToTenantRequest.java @@ -37,6 +37,7 @@ import org.apache.hadoop.ozone.om.request.util.OmResponseUtil; import org.apache.hadoop.ozone.om.response.OMClientResponse; import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantAssignUserAccessIdResponse; +import org.apache.hadoop.ozone.om.upgrade.DisallowedUntilLayoutVersion; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.S3Secret; @@ -58,6 +59,7 @@ import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_LOCK; import static org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantRequestHelper.checkTenantAdmin; import static org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantRequestHelper.checkTenantExistence; +import static org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.MULTITENANCY_SCHEMA; /* Ratis execution flow for OMAssignUserToTenant request: @@ -112,6 +114,7 @@ public OMAssignUserToTenantRequest(OMRequest omRequest) { } @Override + @DisallowedUntilLayoutVersion(MULTITENANCY_SCHEMA) public OMRequest preExecute(OzoneManager ozoneManager) throws IOException { final TenantAssignUserAccessIdRequest request = getOmRequest().getTenantAssignUserAccessIdRequest(); diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantAssignAdminRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantAssignAdminRequest.java index 4c021d25af7c..d3fb7089fb5d 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantAssignAdminRequest.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantAssignAdminRequest.java @@ -34,6 +34,7 @@ import org.apache.hadoop.ozone.om.request.util.OmResponseUtil; import org.apache.hadoop.ozone.om.response.OMClientResponse; import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantAssignAdminResponse; +import org.apache.hadoop.ozone.om.upgrade.DisallowedUntilLayoutVersion; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantAssignAdminRequest; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantAssignAdminResponse; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest; @@ -46,6 +47,7 @@ import java.util.Map; import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_LOCK; +import static org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.MULTITENANCY_SCHEMA; /* Execution flow @@ -68,6 +70,7 @@ public OMTenantAssignAdminRequest(OMRequest omRequest) { } @Override + @DisallowedUntilLayoutVersion(MULTITENANCY_SCHEMA) public OMRequest preExecute(OzoneManager ozoneManager) throws IOException { final TenantAssignAdminRequest request = getOmRequest().getTenantAssignAdminRequest(); diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java index 1d4777f08839..73da4eea3e45 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java @@ -38,6 +38,7 @@ import org.apache.hadoop.ozone.om.request.volume.OMVolumeRequest; import org.apache.hadoop.ozone.om.response.OMClientResponse; import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantCreateResponse; +import org.apache.hadoop.ozone.om.upgrade.DisallowedUntilLayoutVersion; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantRequest; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantResponse; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateVolumeRequest; @@ -61,6 +62,7 @@ import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.VOLUME_ALREADY_EXISTS; import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.USER_LOCK; import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_LOCK; +import static org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.MULTITENANCY_SCHEMA; /* Ratis execution flow for OMTenantCreate @@ -110,6 +112,7 @@ public OMTenantCreateRequest(OMRequest omRequest) { } @Override + @DisallowedUntilLayoutVersion(MULTITENANCY_SCHEMA) public OMRequest preExecute(OzoneManager ozoneManager) throws IOException { // Check Ozone cluster admin privilege diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantDeleteRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantDeleteRequest.java index ff88449922d3..f9931cc39e52 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantDeleteRequest.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantDeleteRequest.java @@ -34,6 +34,7 @@ import org.apache.hadoop.ozone.om.request.volume.OMVolumeRequest; import org.apache.hadoop.ozone.om.response.OMClientResponse; import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantDeleteResponse; +import org.apache.hadoop.ozone.om.upgrade.DisallowedUntilLayoutVersion; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteTenantRequest; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteTenantResponse; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest; @@ -50,6 +51,7 @@ import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TENANT_NOT_EMPTY; import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TENANT_NOT_FOUND; import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_LOCK; +import static org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.MULTITENANCY_SCHEMA; /** * Handles OMTenantDelete request. @@ -63,6 +65,7 @@ public OMTenantDeleteRequest(OMRequest omRequest) { } @Override + @DisallowedUntilLayoutVersion(MULTITENANCY_SCHEMA) public OMRequest preExecute(OzoneManager ozoneManager) throws IOException { // Check Ozone cluster admin privilege diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRevokeAdminRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRevokeAdminRequest.java index 0987ab03e9cd..0f4a2d54c4c0 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRevokeAdminRequest.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRevokeAdminRequest.java @@ -34,6 +34,7 @@ import org.apache.hadoop.ozone.om.request.util.OmResponseUtil; import org.apache.hadoop.ozone.om.response.OMClientResponse; import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantRevokeAdminResponse; +import org.apache.hadoop.ozone.om.upgrade.DisallowedUntilLayoutVersion; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantRevokeAdminRequest; @@ -46,6 +47,7 @@ import java.util.Map; import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_LOCK; +import static org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.MULTITENANCY_SCHEMA; /* Execution flow @@ -68,6 +70,7 @@ public OMTenantRevokeAdminRequest(OMRequest omRequest) { } @Override + @DisallowedUntilLayoutVersion(MULTITENANCY_SCHEMA) public OMRequest preExecute(OzoneManager ozoneManager) throws IOException { final TenantRevokeAdminRequest request = getOmRequest().getTenantRevokeAdminRequest(); diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRevokeUserAccessIdRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRevokeUserAccessIdRequest.java index c9436fb05bfd..7b52cd2798a0 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRevokeUserAccessIdRequest.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantRevokeUserAccessIdRequest.java @@ -34,6 +34,7 @@ import org.apache.hadoop.ozone.om.request.util.OmResponseUtil; import org.apache.hadoop.ozone.om.response.OMClientResponse; import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantRevokeUserAccessIdResponse; +import org.apache.hadoop.ozone.om.upgrade.DisallowedUntilLayoutVersion; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantRevokeUserAccessIdRequest; @@ -47,6 +48,7 @@ import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.S3_SECRET_LOCK; import static org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_LOCK; +import static org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.MULTITENANCY_SCHEMA; /* Execution flow @@ -73,6 +75,7 @@ public OMTenantRevokeUserAccessIdRequest(OMRequest omRequest) { } @Override + @DisallowedUntilLayoutVersion(MULTITENANCY_SCHEMA) public OMRequest preExecute(OzoneManager ozoneManager) throws IOException { final TenantRevokeUserAccessIdRequest request = getOmRequest().getTenantRevokeUserAccessIdRequest(); diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/DisallowedUntilLayoutVersion.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/DisallowedUntilLayoutVersion.java index c437c1d7bfa0..ef44d474b70a 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/DisallowedUntilLayoutVersion.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/DisallowedUntilLayoutVersion.java @@ -28,7 +28,7 @@ * not include the associated layout feature. Helps to keep the method logic * and upgrade related cross cutting concern separate. */ -@Target(ElementType.METHOD) +@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface DisallowedUntilLayoutVersion { OMLayoutFeature value(); diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/OMLayoutFeature.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/OMLayoutFeature.java index 5338c168bcd1..a57d5409d86a 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/OMLayoutFeature.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/OMLayoutFeature.java @@ -29,8 +29,8 @@ */ public enum OMLayoutFeature implements LayoutFeature { ////////////////////////////// ////////////////////////////// - INITIAL_VERSION(0, "Initial Layout Version"); - + INITIAL_VERSION(0, "Initial Layout Version"), + MULTITENANCY_SCHEMA(1, "Multi-Tenancy Schema"); /////////////////////////////// ///////////////////////////// // Example OM Layout Feature with Actions diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/OMLayoutFeatureAspect.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/OMLayoutFeatureAspect.java index a6fe773a2463..36887180bc52 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/OMLayoutFeatureAspect.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/upgrade/OMLayoutFeatureAspect.java @@ -25,6 +25,7 @@ import org.apache.hadoop.ozone.om.OzoneManager; import org.apache.hadoop.ozone.om.exceptions.OMException; +import org.apache.hadoop.ozone.om.request.OMClientRequest; import org.apache.hadoop.ozone.protocolPB.OzoneManagerRequestHandler; import org.apache.hadoop.ozone.upgrade.LayoutFeature; import org.apache.hadoop.ozone.upgrade.LayoutVersionManager; @@ -54,11 +55,17 @@ public void checkLayoutFeature(JoinPoint joinPoint) throws IOException { String featureName = ((MethodSignature) joinPoint.getSignature()) .getMethod().getAnnotation(DisallowedUntilLayoutVersion.class) .value().name(); - LayoutVersionManager lvm = null; + LayoutVersionManager lvm; + final Object[] args = joinPoint.getArgs(); if (joinPoint.getTarget() instanceof OzoneManagerRequestHandler) { OzoneManager ozoneManager = ((OzoneManagerRequestHandler) joinPoint.getTarget()).getOzoneManager(); lvm = ozoneManager.getVersionManager(); + } else if (joinPoint.getTarget() instanceof OMClientRequest && + joinPoint.toShortString().endsWith(".preExecute(..))")) { + // Get OzoneManager instance from preExecute first argument + OzoneManager ozoneManager = (OzoneManager) args[0]; + lvm = ozoneManager.getVersionManager(); } else { try { Method method = joinPoint.getTarget().getClass() @@ -111,4 +118,12 @@ public void beforeRequestApplyTxn(final JoinPoint joinPoint) om.getVersionManager(), lf.name()); } + /** + * Note: Without this, it occasionally throws NoSuchMethodError when running + * the test. + */ + public static OMLayoutFeatureAspect aspectOf() { + return new OMLayoutFeatureAspect(); + } + } diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java index 820686b9eaf3..b6e5137fbee5 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java @@ -49,6 +49,7 @@ import org.apache.hadoop.ozone.om.request.OMClientRequest; import org.apache.hadoop.ozone.om.request.util.OmResponseUtil; import org.apache.hadoop.ozone.om.response.OMClientResponse; +import org.apache.hadoop.ozone.om.upgrade.DisallowedUntilLayoutVersion; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CheckVolumeAccessRequest; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CheckVolumeAccessResponse; @@ -91,6 +92,7 @@ import com.google.common.collect.Lists; +import static org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.MULTITENANCY_SCHEMA; import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DBUpdatesRequest; import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DBUpdatesResponse; import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetAclRequest; @@ -372,6 +374,7 @@ private InfoVolumeResponse infoVolume(InfoVolumeRequest request) return resp.build(); } + @DisallowedUntilLayoutVersion(MULTITENANCY_SCHEMA) private TenantGetUserInfoResponse tenantGetUserInfo( TenantGetUserInfoRequest request) throws IOException { @@ -390,6 +393,7 @@ private TenantGetUserInfoResponse tenantGetUserInfo( return resp.build(); } + @DisallowedUntilLayoutVersion(MULTITENANCY_SCHEMA) private TenantListUserResponse tenantListUsers( TenantListUserRequest request) throws IOException { TenantListUserResponse.Builder builder = @@ -406,6 +410,7 @@ private TenantListUserResponse tenantListUsers( return builder.build(); } + @DisallowedUntilLayoutVersion(MULTITENANCY_SCHEMA) private ListTenantResponse listTenant( ListTenantRequest request) throws IOException { diff --git a/hadoop-ozone/ozone-manager/src/main/resources/META-INF/aop.xml b/hadoop-ozone/ozone-manager/src/main/resources/META-INF/aop.xml new file mode 100644 index 000000000000..f92d820ad27a --- /dev/null +++ b/hadoop-ozone/ozone-manager/src/main/resources/META-INF/aop.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/s3/security/TestS3GetSecretRequest.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/s3/security/TestS3GetSecretRequest.java index 3ebb14ca1a0f..52803e26e5c4 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/s3/security/TestS3GetSecretRequest.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/s3/security/TestS3GetSecretRequest.java @@ -41,6 +41,7 @@ import org.apache.hadoop.ozone.om.response.s3.security.S3GetSecretResponse; import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantAssignUserAccessIdResponse; import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantCreateResponse; +import org.apache.hadoop.ozone.om.upgrade.OMLayoutVersionManager; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantRequest; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetS3SecretRequest; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.GetS3SecretResponse; @@ -328,6 +329,10 @@ public void testGetSecretWithTenant() throws IOException { // This effectively makes alice an admin. when(ozoneManager.isAdmin(ugiAlice)).thenReturn(true); + // Init LayoutVersionManager to prevent NPE in checkLayoutFeature + final OMLayoutVersionManager lvm = + new OMLayoutVersionManager(OMLayoutVersionManager.maxLayoutVersion()); + when(ozoneManager.getVersionManager()).thenReturn(lvm); // 1. CreateTenantRequest: Create tenant "finance". long txLogIndex = 1; diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/upgrade/TestOMLayoutFeatureAspect.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/upgrade/TestOMLayoutFeatureAspect.java index 842cf878fb3c..dd60d5a5a4dc 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/upgrade/TestOMLayoutFeatureAspect.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/upgrade/TestOMLayoutFeatureAspect.java @@ -69,6 +69,7 @@ public void testDisallowedUntilLayoutVersion() throws Throwable { MethodSignature methodSignature = mock(MethodSignature.class); when(methodSignature.getMethod()) .thenReturn(OMLayoutFeatureUtil.class.getMethod("ecMethod")); + when(methodSignature.toShortString()).thenReturn("ecMethod"); when(joinPoint.getSignature()).thenReturn(methodSignature); LambdaTestUtils.intercept(OMException.class,