diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/CancelPrepareSubCommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/CancelPrepareSubCommand.java index e208c8cc9273..7e859c604e6f 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/CancelPrepareSubCommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/CancelPrepareSubCommand.java @@ -27,7 +27,9 @@ */ @CommandLine.Command( name = "cancelprepare", - description = "Cancel prepare state in the OMs.", + description = "Deprecated. This command is no longer required, but is kept for backward compatibility with older" + + " Ozone Managers.", + hidden = true, mixinStandardHelpOptions = true, versionProvider = HddsVersionProvider.class ) diff --git a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/PrepareSubCommand.java b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/PrepareSubCommand.java index f1e0c92e691b..a1826d0210a9 100644 --- a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/PrepareSubCommand.java +++ b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/ozone/admin/om/PrepareSubCommand.java @@ -17,7 +17,6 @@ package org.apache.hadoop.ozone.admin.om; -import static org.apache.hadoop.ozone.OmUtils.getOmHostsFromConfig; import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus.PREPARE_COMPLETED; import java.io.IOException; @@ -25,10 +24,12 @@ import java.time.temporal.ChronoUnit; import java.util.HashMap; import java.util.Map; -import java.util.Set; import java.util.concurrent.Callable; import java.util.stream.Collectors; import org.apache.hadoop.hdds.cli.HddsVersionProvider; +import org.apache.hadoop.hdds.conf.OzoneConfiguration; +import org.apache.hadoop.ozone.OmUtils; +import org.apache.hadoop.ozone.om.helpers.OMNodeDetails; import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus; @@ -40,11 +41,9 @@ */ @CommandLine.Command( name = "prepare", - description = "Prepares Ozone Manager for upgrade/downgrade, by applying " + - "all pending transactions, taking a Ratis snapshot at the last " + - "transaction and purging all logs on each OM instance. The returned " + - "transaction #ID corresponds to the last transaction in the quorum in" + - " which the snapshot is taken.", + description = "Deprecated. This command is no longer required, but is kept for backward compatibility with older" + + " Ozone Managers.", + hidden = true, mixinStandardHelpOptions = true, versionProvider = HddsVersionProvider.class ) @@ -106,9 +105,11 @@ private void execute(OzoneManagerProtocol client) throws Exception { "with Transaction Id : [" + prepareTxnId + "]."); Map omPreparedStatusMap = new HashMap<>(); - Set omHosts = getOmHostsFromConfig( - parent.getParent().getOzoneConf(), omServiceOption.getServiceID()); - omHosts.forEach(h -> omPreparedStatusMap.put(h, false)); + OzoneConfiguration conf = parent.getParent().getOzoneConf(); + OmUtils.getAllOMHAAddresses(conf, omServiceOption.getServiceID(), false) + .stream() + .map(OMNodeDetails::getRpcAddressString) + .forEach(addr -> omPreparedStatusMap.put(addr, false)); Duration pTimeout = Duration.of(prepareTimeOut, ChronoUnit.SECONDS); Duration pInterval = Duration.of(prepareCheckInterval, ChronoUnit.SECONDS); @@ -123,10 +124,9 @@ private void execute(OzoneManagerProtocol client) throws Exception { for (Map.Entry e : omPreparedStatusMap.entrySet()) { if (!e.getValue()) { String omHost = e.getKey(); - try (OzoneManagerProtocol singleOmClient = - parent.createOmClient(omServiceOption.getServiceID(), omHost, false)) { - PrepareStatusResponse response = - singleOmClient.getOzoneManagerPrepareStatus(prepareTxnId); + try (OzoneManagerProtocol singleOmClient = parent.createOmClient(omServiceOption.getServiceID(), omHost, + false)) { + PrepareStatusResponse response = singleOmClient.getOzoneManagerPrepareStatus(prepareTxnId); PrepareStatus status = response.getStatus(); System.out.println("OM : [" + omHost + "], Prepare " + "Status : [" + status.name() + "], Current Transaction Id : [" + diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java index 03ea287572a1..9a59da439d97 100644 --- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java +++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java @@ -263,6 +263,9 @@ public static boolean isReadOnly(OMRequest omRequest) { return true; case GetQuotaRepairStatus: case StartQuotaRepair: + // Prepare and CancelPrepare are now no-ops, but still produce responses for compatability. + case Prepare: + case CancelPrepare: case QueryUpgradeStatus: return true; case CreateVolume: @@ -299,8 +302,6 @@ public static boolean isReadOnly(OMRequest omRequest) { // as deprecated case FinalizeUpgrade: case StartFinalizeUpgrade: - case Prepare: - case CancelPrepare: case DeleteOpenKeys: case SetS3Secret: case RevokeS3Secret: diff --git a/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/.env b/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/.env deleted file mode 100644 index dd37cf578302..000000000000 --- a/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/.env +++ /dev/null @@ -1,25 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -HDDS_VERSION=${hdds.version} -OZONE_RUNNER_VERSION=${docker.ozone-runner.version} -OZONE_RUNNER_IMAGE=apache/ozone-runner -OZONE_DIR=/opt/hadoop -OZONE_VOLUME=. -# Indicates no arguments to the OM. -# This variable must be set to some non-empty value, or docker compose will -# expand it to an empty string and pass that to the OM as an argument. -OM_HA_ARGS=-- diff --git a/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/docker-compose.yaml b/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/docker-compose.yaml deleted file mode 100644 index 228f78c968be..000000000000 --- a/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/docker-compose.yaml +++ /dev/null @@ -1,126 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# reusable fragments (see https://docs.docker.com/compose/compose-file/#extension-fields) -x-common-config: - &common-config - env_file: - - docker-config - image: ${OZONE_RUNNER_IMAGE}:${OZONE_RUNNER_VERSION} - -x-replication: - &replication - OZONE-SITE.XML_ozone.server.default.replication: ${OZONE_REPLICATION_FACTOR:-1} - -x-datanode: - &datanode - command: ["ozone","datanode"] - <<: *common-config - environment: - <<: *replication - ports: - - 19864 - - 9882 - -x-om: - &om - command: ["ozone","om","${OM_HA_ARGS}"] - <<: *common-config - environment: - ENSURE_OM_INITIALIZED: /data/metadata/om/current/VERSION - <<: *replication - ports: - - 9862 - - 9872 - -services: - dn1: - <<: *datanode - hostname: dn1 - networks: - net: - ipv4_address: 10.9.0.11 - volumes: - - ${OZONE_VOLUME}/dn1:/data - - ../..:${OZONE_DIR} - dn2: - <<: *datanode - hostname: dn2 - networks: - net: - ipv4_address: 10.9.0.12 - volumes: - - ${OZONE_VOLUME}/dn2:/data - - ../..:${OZONE_DIR} - dn3: - <<: *datanode - hostname: dn3 - networks: - net: - ipv4_address: 10.9.0.13 - volumes: - - ${OZONE_VOLUME}/dn3:/data - - ../..:${OZONE_DIR} - - om1: - <<: *om - networks: - net: - ipv4_address: 10.9.0.14 - volumes: - - ${OZONE_VOLUME}/om1:/data - - ../..:${OZONE_DIR} - - om2: - <<: *om - networks: - net: - ipv4_address: 10.9.0.15 - volumes: - - ${OZONE_VOLUME}/om2:/data - - ../..:${OZONE_DIR} - - om3: - <<: *om - networks: - net: - ipv4_address: 10.9.0.16 - volumes: - - ${OZONE_VOLUME}/om3:/data - - ../..:${OZONE_DIR} - - scm: - command: ["ozone","scm"] - <<: *common-config - networks: - net: - ipv4_address: 10.9.0.17 - environment: - ENSURE_SCM_INITIALIZED: /data/metadata/scm/current/VERSION - OZONE-SITE.XML_hdds.scm.safemode.min.datanode: ${OZONE_SAFEMODE_MIN_DATANODES:-1} - <<: *replication - ports: - - 9876:9876 - volumes: - - ${OZONE_VOLUME}/scm:/data - - ../..:${OZONE_DIR} - -networks: - net: - driver: bridge - ipam: - config: - - subnet: 10.9.0.0/16 diff --git a/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/docker-config b/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/docker-config deleted file mode 100644 index b197e9a7f7e4..000000000000 --- a/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/docker-config +++ /dev/null @@ -1,37 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -CORE-SITE.XML_fs.defaultFS=ofs://omservice/ - -OZONE-SITE.XML_ozone.om.service.ids=omservice -OZONE-SITE.XML_ozone.om.nodes.omservice=om1,om2,om3 -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.scm.names=scm -OZONE-SITE.XML_ozone.scm.datanode.id.dir=/data/metadata -OZONE-SITE.XML_ozone.scm.block.client.address=scm -OZONE-SITE.XML_ozone.scm.container.size=1GB -OZONE-SITE.XML_ozone.metadata.dirs=/data/metadata -OZONE-SITE.XML_ozone.scm.client.address=scm -OZONE-SITE.XML_ozone.client.failover.max.attempts=6 -OZONE-SITE.XML_hdds.datanode.dir=/data/hdds -OZONE-SITE.XML_hdds.datanode.volume.min.free.space=100MB -OZONE-SITE.XML_hdds.datanode.volume.min.free.space.percent=0 -OZONE-SITE.XML_ozone.http.basedir=/tmp/ozone_http - -no_proxy=om1,om2,om3,scm,s3g,recon,kdc,localhost,127.0.0.1 diff --git a/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/test.sh b/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/test.sh deleted file mode 100755 index 1e4ef919b4a7..000000000000 --- a/hadoop-ozone/dist/src/main/compose/ozone-om-prepare/test.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bash -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -u -o pipefail - -COMPOSE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -export COMPOSE_DIR - -: "${OZONE_REPLICATION_FACTOR:=3}" -: "${OZONE_VOLUME:="${COMPOSE_DIR}/data"}" - -export OZONE_VOLUME - -export OZONE_DIR=/opt/hadoop -export OM_SERVICE_ID=omservice - -# shellcheck source=/dev/null -source "${COMPOSE_DIR}/../testlib.sh" - -create_data_dirs dn{1..3} om{1..3} scm - -start_docker_env - -# Write data and prepare cluster. -execute_robot_test scm omha/om-prepare.robot - -# Cancel preparation. -execute_robot_test scm omha/om-cancel-prepare.robot - -# Prepare cluster again. -execute_robot_test scm omha/om-prepare.robot -execute_robot_test scm omha/om-prepared.robot - -# re-start cluster and check that it remains prepared. -KEEP_RUNNING=false stop_docker_env -export OZONE_KEEP_RESULTS=true -start_docker_env - -execute_robot_test scm omha/om-prepared.robot - -# re-start cluster with --upgrade flag to take it out of prepare. -KEEP_RUNNING=false stop_docker_env -export OM_HA_ARGS='--upgrade' -start_docker_env - -# Writes should now succeed. -execute_robot_test scm topology/loaddata.robot -execute_robot_test scm topology/readdata.robot diff --git a/hadoop-ozone/dist/src/main/smoketest/omha/om-cancel-prepare.robot b/hadoop-ozone/dist/src/main/smoketest/omha/om-cancel-prepare.robot deleted file mode 100644 index 92dc1a25a048..000000000000 --- a/hadoop-ozone/dist/src/main/smoketest/omha/om-cancel-prepare.robot +++ /dev/null @@ -1,44 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -*** Settings *** -Documentation Smoke test for ozone manager cancel prepare -Library OperatingSystem -Library String -Library BuiltIn -Resource ../commonlib.robot -Test Timeout 5 minutes -Suite Setup Run Keywords Generate volume and bucket names -... AND Get Security Enabled From Config -Test Setup Run Keyword if '${SECURITY_ENABLED}' == 'true' Kinit test user testuser testuser.keytab - -** Keywords *** -Generate volume and bucket names - ${random} = Generate Random String 5 [NUMBERS] - Set Suite Variable ${volume_name} ${random}-volume-for-cancel - Set Suite Variable ${bucket_name} ${random}-bucket-for-cancel - -** Test Cases *** -Cancel Ozone Manager Prepare - ${result} = Execute ozone admin om cancelprepare -id=omservice - Wait Until Keyword Succeeds 3min 5sec Should contain ${result} Cancel prepare succeeded - -Test write operations - Execute ozone sh volume create /${volume_name} - Execute ozone sh bucket create /${volume_name}/${bucket_name} - ${result} = Execute ozone sh key put /${volume_name}/${bucket_name}/cancel-key /opt/hadoop/NOTICE.txt - ${result} = Execute ozone sh key info /${volume_name}/${bucket_name}/cancel-key - Should contain ${result} \"name\" : \"cancel-key\" - diff --git a/hadoop-ozone/dist/src/main/smoketest/omha/om-prepare.robot b/hadoop-ozone/dist/src/main/smoketest/omha/om-prepare.robot deleted file mode 100644 index b483fd0b925a..000000000000 --- a/hadoop-ozone/dist/src/main/smoketest/omha/om-prepare.robot +++ /dev/null @@ -1,51 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -*** Settings *** -Documentation Smoke test to test preparing OMs in an OM HA cluster. -Library OperatingSystem -Library String -Library BuiltIn -Resource ../commonlib.robot -Resource ../ozone-lib/freon.robot -Test Timeout 5 minutes -Suite Setup Run Keywords Get Security Enabled From Config -... AND Create Specific OM data for prepare -Test Setup Run Keyword if '${SECURITY_ENABLED}' == 'true' Kinit test user testuser testuser.keytab - -*** Keywords *** -Create Specific OM data for prepare - # Freon data to make sure there are a reasonable number of transactions in the system. - Freon OCKG prefix=om-prepare n=100 - ${random} = Generate Random String 5 [NUMBERS] - Set Suite Variable ${volume_name} ${random}-volume-for-prepare - Set Suite Variable ${bucket_name} ${random}-bucket-for-prepare - Execute ozone sh volume create /${volume_name} - Execute ozone sh bucket create /${volume_name}/${bucket_name} - Execute ozone sh key put /${volume_name}/${bucket_name}/prepare-key /opt/hadoop/NOTICE.txt - -** Test Cases *** -Prepare Ozone Manager - ${result} = Execute ozone admin om prepare -id=omservice - Wait Until Keyword Succeeds 3min 10sec Should contain ${result} OM Preparation successful! - -Checks if the expected data is present in OM - ${result} = Execute ozone sh key info /${volume_name}/${bucket_name}/prepare-key - Should contain ${result} \"name\" : \"prepare-key\" - -Test write operation fails - ${result} = Execute and checkrc ozone sh key put /${volume_name}/${bucket_name}/prepare-key2 /opt/hadoop/NOTICE.txt 255 - Should contain ${result} OM is in prepare mode - Execute and checkrc ozone sh key info /${volume_name}/${bucket_name}/prepare-key2 255 diff --git a/hadoop-ozone/dist/src/main/smoketest/omha/om-prepared.robot b/hadoop-ozone/dist/src/main/smoketest/omha/om-prepared.robot deleted file mode 100644 index bf745f670728..000000000000 --- a/hadoop-ozone/dist/src/main/smoketest/omha/om-prepared.robot +++ /dev/null @@ -1,37 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -*** Settings *** -Documentation Smoke test to test that OMs are prepared in an OM HA cluster. -Library OperatingSystem -Library String -Library BuiltIn -Resource ../commonlib.robot -Test Timeout 5 minutes -Suite Setup Get Security Enabled From Config -Test Setup Run Keyword if '${SECURITY_ENABLED}' == 'true' Kinit test user testuser testuser.keytab - -** Test Cases *** -Test create volume fails - ${random} = Generate Random String 5 [NUMBERS] - Set Suite Variable ${volume_name} ${random}-volume-for-prepare - ${result} = Execute and checkrc ozone sh volume create /${volume_name} 255 - Should contain ${result} OM is in prepare mode - ${result} = Execute and checkrc ozone sh volume info /${volume_name} 255 - Should contain ${result} VOLUME_NOT_FOUND - -Test list volumes succeeds - ${result} = Execute ozone sh volume list - diff --git a/hadoop-ozone/dist/src/main/smoketest/upgrade/prepare.robot b/hadoop-ozone/dist/src/main/smoketest/upgrade/prepare.robot deleted file mode 100644 index 021515ebd06d..000000000000 --- a/hadoop-ozone/dist/src/main/smoketest/upgrade/prepare.robot +++ /dev/null @@ -1,29 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -*** Settings *** -Documentation Prepares OMs -Resource ../commonlib.robot -Resource lib.robot -Test Timeout 5 minutes -Suite Setup Get Security Enabled From Config -Test Setup Run Keyword if '${SECURITY_ENABLED}' == 'true' Kinit test user testuser testuser.keytab - -*** Test Cases *** -Prepare Ozone Manager - Pass Execution If '%{OZONE_UPGRADE_FROM}' == '1.1.0' OM prepare is skipped for version %{OZONE_UPGRADE_FROM} - ${service_id} = Get OM Service ID - Pass Execution If '${service_id}' == '' OM prepare skipped in non-HA - Wait Until Keyword Succeeds 3min 10sec Prepare OM diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/OMUpgradeTestUtils.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/OMUpgradeTestUtils.java index 8f1fc41544a7..cd2d704eb462 100644 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/OMUpgradeTestUtils.java +++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/OMUpgradeTestUtils.java @@ -17,17 +17,14 @@ package org.apache.hadoop.ozone.om; -import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus.PREPARE_COMPLETED; import static org.apache.hadoop.ozone.upgrade.UpgradeFinalization.Status.FINALIZATION_DONE; import static org.apache.ozone.test.GenericTestUtils.waitFor; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; -import java.util.List; import java.util.concurrent.TimeoutException; import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol; import org.apache.hadoop.ozone.upgrade.UpgradeFinalization; -import org.apache.ozone.test.LambdaTestUtils; /** * Utility class to help test OM upgrade scenarios. @@ -38,35 +35,6 @@ private OMUpgradeTestUtils() { // Utility class. } - public static void assertClusterPrepared( - long preparedIndex, List ozoneManagers) throws Exception { - for (OzoneManager om : ozoneManagers) { - LambdaTestUtils.await(120000, - 1000, () -> { - if (!om.isRunning()) { - return false; - } else { - boolean preparedAtIndex = false; - OzoneManagerPrepareState.State state = - om.getPrepareState().getState(); - - if (state.getStatus() == PREPARE_COMPLETED) { - if (state.getIndex() == preparedIndex) { - preparedAtIndex = true; - } else { - // State will not change if we are prepared at the wrong - // index. Break out of wait. - throw new Exception("OM " + om.getOMNodeId() + " prepared " + - "but prepare index " + state.getIndex() + " does not " + - "match expected prepare index " + preparedIndex); - } - } - return preparedAtIndex; - } - }); - } - } - public static void waitForFinalization(OzoneManagerProtocol omClient) throws TimeoutException, InterruptedException { waitFor(() -> { diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMUpgradeFinalization.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMUpgradeFinalization.java deleted file mode 100644 index 36fd6c7ac030..000000000000 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOMUpgradeFinalization.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hadoop.ozone.om; - -import static org.apache.hadoop.ozone.OzoneConsts.APPARENT_VERSION_KEY; -import static org.apache.hadoop.ozone.om.OMUpgradeTestUtils.assertClusterPrepared; -import static org.apache.hadoop.ozone.om.OMUpgradeTestUtils.waitForFinalization; -import static org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.INITIAL_VERSION; -import static org.apache.ozone.test.GenericTestUtils.waitFor; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; - -import java.io.IOException; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.TimeoutException; -import org.apache.hadoop.hdds.conf.OzoneConfiguration; -import org.apache.hadoop.ozone.MiniOzoneCluster; -import org.apache.hadoop.ozone.MiniOzoneHAClusterImpl; -import org.apache.hadoop.ozone.OzoneConsts; -import org.apache.hadoop.ozone.OzoneManagerVersion; -import org.apache.hadoop.ozone.audit.AuditEventStatus; -import org.apache.hadoop.ozone.audit.AuditLogTestUtils; -import org.apache.hadoop.ozone.audit.OMAction; -import org.apache.hadoop.ozone.client.OzoneClient; -import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol; -import org.apache.hadoop.ozone.om.ratis.OzoneManagerStateMachine; -import org.apache.ratis.util.LifeCycle; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -/** - * Tests for OM upgrade finalization. - * TODO: can be merged into class with other OM tests with per-method cluster - */ -class TestOMUpgradeFinalization { - static { - AuditLogTestUtils.enableAuditLog(); - } - - @BeforeEach - public void setup() throws Exception { - AuditLogTestUtils.truncateAuditLogFile(); - } - - @AfterAll - public static void shutdown() { - AuditLogTestUtils.deleteAuditLogFile(); - AuditLogTestUtils.deleteSystemAuditLogFile(); - } - - @Test - void testOMUpgradeFinalizationWithOneOMDown() throws Exception { - OzoneConfiguration conf = new OzoneConfiguration(); - conf.setInt(OMStorage.TESTING_INIT_APPARENT_VERSION_KEY, INITIAL_VERSION.serialize()); - conf.set(OMConfigKeys.OZONE_OM_UPGRADE_FINALIZATION_CHECK_INTERVAL, "10ms"); - try (MiniOzoneHAClusterImpl cluster = newCluster(conf)) { - cluster.waitForClusterToBeReady(); - - try (OzoneClient client = cluster.newClient()) { - List runningOms = cluster.getOzoneManagersList(); - for (OzoneManager om : runningOms) { - assertEquals(INITIAL_VERSION, om.getVersionManager().getApparentVersion()); - // The OMs have not been finalized yet, so no version has been written to the DB. - assertNull(om.getMetadataManager().getMetaTable().get(APPARENT_VERSION_KEY)); - } - - final int shutdownOMIndex = 2; - OzoneManager downedOM = cluster.getOzoneManager(shutdownOMIndex); - cluster.stopOzoneManager(shutdownOMIndex); - assertFalse(downedOM.isRunning()); - assertEquals(runningOms.remove(shutdownOMIndex), downedOM); - - OzoneManagerProtocol omClient = client.getObjectStore().getClientProxy() - .getOzoneManagerClient(); - // Have to do a "prepare" operation to get rid of the logs in the active - // OMs. - long prepareIndex = omClient.prepareOzoneManager(120L, 5L); - assertClusterPrepared(prepareIndex, runningOms); - AuditLogTestUtils.verifyAuditLog(OMAction.UPGRADE_PREPARE, AuditEventStatus.SUCCESS); - omClient.cancelOzoneManagerPrepare(); - AuditLogTestUtils.verifyAuditLog(OMAction.UPGRADE_CANCEL, AuditEventStatus.SUCCESS); - omClient.finalizeUpgrade(); - waitForFinalization(omClient); - AuditLogTestUtils.verifySystemAuditLog(OMAction.UPGRADE_FINALIZE, AuditEventStatus.SUCCESS); - // Ensure the finalization in progress key has been removed. - assertNull(cluster.getOzoneManager().getMetadataManager() - .getMetaTable().get(OzoneConsts.FINALIZATION_IN_PROGRESS_KEY)); - cluster.restartOzoneManager(downedOM, true); - - OzoneManagerStateMachine omStateMachine = downedOM.getOmRatisServer() - .getOmStateMachine(); - try { - waitFor(() -> omStateMachine.getLifeCycleState().isPausingOrPaused(), - 1000, 60000); - } catch (TimeoutException timeEx) { - assertEquals(LifeCycle.State.RUNNING, - omStateMachine.getLifeCycle().getCurrentState()); - } - - waitFor(() -> !omStateMachine.getLifeCycle().getCurrentState() - .isPausingOrPaused(), 1000, 60000); - - assertEquals(OzoneManagerVersion.SOFTWARE_VERSION, downedOM.getVersionManager().getApparentVersion()); - String dbVersionString = downedOM.getMetadataManager().getMetaTable().get(APPARENT_VERSION_KEY); - assertNotNull(dbVersionString); - assertEquals(OzoneManagerVersion.SOFTWARE_VERSION.serialize(), Integer.parseInt(dbVersionString)); - } - } - } - - private static MiniOzoneHAClusterImpl newCluster(OzoneConfiguration conf) - throws IOException { - conf.setInt(OMStorage.TESTING_INIT_APPARENT_VERSION_KEY, INITIAL_VERSION.serialize()); - MiniOzoneHAClusterImpl.Builder builder = MiniOzoneCluster.newHABuilder(conf); - builder.setOMServiceId(UUID.randomUUID().toString()) - .setNumOfOzoneManagers(3) - .setNumDatanodes(1); - return builder.build(); - } - -} diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerPrepare.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerPrepare.java deleted file mode 100644 index edc9b569b2a5..000000000000 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerPrepare.java +++ /dev/null @@ -1,529 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hadoop.ozone.om; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.NOT_SUPPORTED_OPERATION_WHEN_PREPARED; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import org.apache.hadoop.ozone.MiniOzoneHAClusterImpl; -import org.apache.hadoop.ozone.TestDataUtil; -import org.apache.hadoop.ozone.client.ObjectStore; -import org.apache.hadoop.ozone.client.OzoneVolume; -import org.apache.hadoop.ozone.client.protocol.ClientProtocol; -import org.apache.hadoop.ozone.container.ContainerTestHelper; -import org.apache.hadoop.ozone.om.exceptions.OMException; -import org.apache.hadoop.ozone.om.helpers.OmKeyInfo; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus; -import org.apache.ozone.test.LambdaTestUtils; -import org.apache.ozone.test.tag.Flaky; -import org.apache.ozone.test.tag.Slow; -import org.apache.ozone.test.tag.Unhealthy; -import org.apache.ratis.server.RaftServer; -import org.apache.ratis.util.ExitUtils; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Test OM prepare against actual mini cluster. - */ -@Flaky("HDDS-5990") -public class TestOzoneManagerPrepare extends TestOzoneManagerHA { - private static final String BUCKET = "bucket"; - private static final String VOLUME = "volume"; - private static final String KEY_PREFIX = "key"; - - // Maximum time to wait for conditions involving Ratis logs. - private static final int WAIT_TIMEOUT_MILLIS = 120000; - private static final long PREPARE_FLUSH_WAIT_TIMEOUT_SECONDS = 120L; - private static final long PREPARE_FLUSH_INTERVAL_SECONDS = 5L; - - private MiniOzoneHAClusterImpl cluster; - private ClientProtocol clientProtocol; - private ObjectStore store; - - private static final Logger LOG = - LoggerFactory.getLogger(TestOzoneManagerPrepare.class); - - private void initInstanceVariables() { - cluster = getCluster(); - store = getObjectStore(); - clientProtocol = store.getClientProxy(); - } - - /** - * Make sure OM is out of Prepare state before executing individual tests. - */ - @BeforeEach - void setup() throws Exception { - initInstanceVariables(); - - LOG.info("Waiting for OM leader election"); - waitForLeaderToBeReady(); - submitCancelPrepareRequest(); - assertClusterNotPrepared(); - } - - /** - * Reset cluster between tests. - */ - @AfterEach - void resetCluster() throws Exception { - if (cluster != null) { - cluster.restartOzoneManager(); - } - } - - /** - * Writes data to the cluster via the leader OM, and then prepares it. - * Checks that every OM is prepared successfully. - */ - @Test - public void testPrepareWithTransactions() throws Exception { - long prepareIndex = submitPrepareRequest(); - assertClusterPrepared(prepareIndex); - assertRatisLogsCleared(); - - submitCancelPrepareRequest(); - assertClusterNotPrepared(); - - String volumeName = VOLUME + UUID.randomUUID().toString(); - Set writtenKeys = writeKeysAndWaitForLogs(volumeName, 50, - cluster.getOzoneManagersList()); - prepareIndex = submitPrepareRequest(); - - // Make sure all OMs are prepared and all OMs still have their data. - assertClusterPrepared(prepareIndex); - assertRatisLogsCleared(); - assertKeysWritten(volumeName, writtenKeys); - - // Should be able to "prepare" the OM group again. - assertShouldBeAbleToPrepare(); - } - - /** - * Writes data to the cluster. - * Shuts down one OM. - * Writes more data to the cluster. - * Submits prepare as ratis request. - * Checks that two live OMs are prepared. - * Revives the third OM - * Checks that third OM received all transactions and is prepared. - * @throws Exception - */ - @Test - @Unhealthy("RATIS-1481") // until upgrade to Ratis 2.3.0 - public void testPrepareDownedOM() throws Exception { - // Index of the OM that will be shut down during this test. - final int shutdownOMIndex = 2; - List runningOms = cluster.getOzoneManagersList(); - - String volumeName1 = VOLUME + UUID.randomUUID().toString(); - // Create keys with all 3 OMs up. - Set writtenKeysBeforeOmShutDown = writeKeysAndWaitForLogs( - volumeName1, 10, runningOms); - - // Shut down one OM. - cluster.stopOzoneManager(shutdownOMIndex); - OzoneManager downedOM = cluster.getOzoneManager(shutdownOMIndex); - assertFalse(downedOM.isRunning()); - assertEquals(runningOms.remove(shutdownOMIndex), downedOM); - - // Write keys with the remaining OMs up. - String volumeName2 = VOLUME + UUID.randomUUID().toString(); - Set writtenKeysAfterOmShutDown = - writeKeysAndWaitForLogs(volumeName2, 10, runningOms); - - long prepareIndex = submitPrepareRequest(); - - // Check that the two live OMs are prepared. - assertClusterPrepared(prepareIndex, runningOms); - - // Restart the downed OM and wait for it to catch up. - // Since prepare was the last Ratis transaction, it should have all data - // it missed once it receives the prepare transaction. - cluster.restartOzoneManager(downedOM, true); - runningOms.add(shutdownOMIndex, downedOM); - ExitUtils.assertNotTerminated(); - - // Make sure all OMs are prepared and still have data. - assertClusterPrepared(prepareIndex, runningOms); - assertKeysWritten(volumeName1, writtenKeysBeforeOmShutDown, runningOms); - assertKeysWritten(volumeName2, writtenKeysAfterOmShutDown, runningOms); - - // Cancelling prepare state of the cluster to try out an operation. - submitCancelPrepareRequest(); - assertClusterNotPrepared(); - - // Should be able to write data to all 3 OMs. - String volumeName3 = VOLUME + UUID.randomUUID().toString(); - store.createVolume(volumeName3); - for (OzoneManager om : runningOms) { - LambdaTestUtils.await(WAIT_TIMEOUT_MILLIS, 1000, () -> { - OMMetadataManager metadataManager = om.getMetadataManager(); - String volumeKey = metadataManager.getVolumeKey(volumeName3); - return metadataManager.getVolumeTable().get(volumeKey) != null; - }); - } - } - - @Test - public void testPrepareWithRestart() throws Exception { - // Create fresh cluster for this test to prevent timeout from restarting - // modified cluster. - shutdown(); - init(); - initInstanceVariables(); - - String volumeName = VOLUME + UUID.randomUUID().toString(); - writeKeysAndWaitForLogs(volumeName, 10); - - long prepareIndex = submitPrepareRequest(); - assertClusterPrepared(prepareIndex); - - // Restart all ozone managers. - cluster.restartOzoneManager(); - - // No check for cleared logs, since Ratis meta transactions may slip in - // on restart. - assertClusterPrepared(prepareIndex); - } - - @Slow("Saving on CI time since this is a pessimistic test. We should not " + - "be able to do anything with 2 OMs down.") - @Test - public void testPrepareFailsWhenTwoOmsAreDown() throws Exception { - // Shut down 2 OMs. - for (int i : Arrays.asList(1, 2)) { - cluster.stopOzoneManager(i); - OzoneManager downedOM = cluster.getOzoneManager(i); - assertFalse(downedOM.isRunning()); - } - - assertThrows(IOException.class, - () -> clientProtocol.getOzoneManagerClient().prepareOzoneManager( - PREPARE_FLUSH_WAIT_TIMEOUT_SECONDS, - PREPARE_FLUSH_INTERVAL_SECONDS)); - } - - /** - * Issues requests on ten different threads, for which one is a prepare and - * the rest are create volume. We cannot be sure of the exact order that - * the requests will execute, so this test checks that the cluster ends in - * a prepared state, and that create volume requests either succeed, or fail - * indicating the cluster was prepared before they were encountered. - * - * @throws Exception - */ - @Test - public void testPrepareWithMultipleThreads() throws Exception { - final int numThreads = 10; - final int prepareTaskIndex = 5; - - ExecutorService executorService = Executors.newFixedThreadPool(numThreads); - // For the prepare task, the future will return a log index. - // For the create volume tasks, 0 (dummy value) will be returned. - List> tasks = new ArrayList<>(); - - for (int i = 0; i < numThreads; i++) { - Callable task; - if (i == prepareTaskIndex) { - task = this::submitPrepareRequest; - } else { - String volumeName = VOLUME + i; - task = () -> { - clientProtocol.createVolume(volumeName); - return 0L; - }; - } - tasks.add(executorService.submit(task)); - } - - // For each task, wait for it to complete and check its result. - for (int i = 0; i < numThreads; i++) { - Future future = tasks.get(i); - - if (i == prepareTaskIndex) { - assertClusterPrepared(future.get()); - assertRatisLogsCleared(); - } else { - try { - // If this throws an exception, it should be an OMException - // indicating failure because the cluster was already prepared. - // If no exception is thrown, the volume should be created. - future.get(); - String volumeName = VOLUME + i; - assertTrue(clientProtocol.listVolumes(volumeName, "", 1) - .stream() - .anyMatch((vol) -> vol.getName().equals(volumeName))); - } catch (ExecutionException ex) { - OMException cause = assertInstanceOf(OMException.class, ex.getCause()); - assertEquals(NOT_SUPPORTED_OPERATION_WHEN_PREPARED, cause.getResult()); - } - } - } - - // In the above loop, we have waited for all threads to terminate. - executorService.shutdown(); - } - - @Test - public void testCancelPrepare() throws Exception { - String volumeName = VOLUME + UUID.randomUUID().toString(); - Set writtenKeys = writeKeysAndWaitForLogs(volumeName, 10); - long prepareIndex = submitPrepareRequest(); - - // Make sure all OMs are prepared and all OMs still have their data. - assertClusterPrepared(prepareIndex); - assertRatisLogsCleared(); - assertKeysWritten(volumeName, writtenKeys); - - // Cancel prepare and check that data is still present. - submitCancelPrepareRequest(); - assertClusterNotPrepared(); - assertKeysWritten(volumeName, writtenKeys); - - // Cancelling prepare again should have no effect. - submitCancelPrepareRequest(); - assertClusterNotPrepared(); - - // Write more data after cancelling prepare. - String volumeNameNew = VOLUME + UUID.randomUUID().toString(); - writtenKeys = writeKeysAndWaitForLogs(volumeNameNew, 10); - - // Cancelling prepare again should have no effect and new data should be - // preserved. - submitCancelPrepareRequest(); - assertClusterNotPrepared(); - assertKeysWritten(volumeNameNew, writtenKeys); - } - - private boolean logFilesPresentInRatisPeer(OzoneManager om) { - final RaftServer.Division server = om.getOmRatisServer().getServerDivision(); - final String ratisDir = server.getRaftServer().getProperties().get("raft.server.storage.dir"); - final String groupIdDirName = server.getGroup().getGroupId().getUuid().toString(); - File logDir = Paths.get(ratisDir, groupIdDirName, "current") - .toFile(); - - File[] files = logDir.listFiles(); - if (files != null) { - for (File file : files) { - if (file.getName().startsWith("log")) { - return true; - } - } - } - return false; - } - - private Set writeKeysAndWaitForLogs(String volumeName, - int numKeys) throws Exception { - return writeKeysAndWaitForLogs(volumeName, numKeys, - cluster.getOzoneManagersList()); - } - - private Set writeKeysAndWaitForLogs(String volumeName, int numKeys, - List ozoneManagers) throws Exception { - - store.createVolume(volumeName); - OzoneVolume volume = store.getVolume(volumeName); - volume.createBucket(BUCKET); - - Set writtenKeys = new HashSet<>(); - for (int i = 1; i <= numKeys; i++) { - String keyName = KEY_PREFIX + i; - writeTestData(volumeName, BUCKET, keyName); - writtenKeys.add(keyName); - } - - // Make sure all OMs have logs from writing data, so we can check that - // they are purged after prepare. - for (OzoneManager om : ozoneManagers) { - LambdaTestUtils.await(WAIT_TIMEOUT_MILLIS, 1000, - () -> logFilesPresentInRatisPeer(om)); - } - - return writtenKeys; - } - - private void writeTestData(String volumeName, - String bucketName, String keyName) throws Exception { - - String keyString = UUID.randomUUID().toString(); - byte[] data = ContainerTestHelper.getFixedLengthString( - keyString, 100).getBytes(UTF_8); - TestDataUtil.createKey(store.getVolume(volumeName). - getBucket(bucketName), keyName, data); - } - - private void assertKeysWritten(String volumeName, - Set expectedKeys) throws Exception { - assertKeysWritten(volumeName, expectedKeys, cluster.getOzoneManagersList()); - } - - /** - * Checks that all provided OMs have {@code expectedKeys} in the volume - * {@code volumeName} and retries checking until the test timeout. - * All provided OMs are checked, not just a majority, so that we can - * test that downed OMs are able to make a full recovery after preparation, - * even though the cluster could appear healthy with just 2 OMs. - */ - private void assertKeysWritten(String volumeName, Set expectedKeys, - List ozoneManagers) throws Exception { - for (OzoneManager om: ozoneManagers) { - // Wait for a potentially slow follower to apply all key writes. - LambdaTestUtils.await(WAIT_TIMEOUT_MILLIS, 1000, () -> { - List keys = om.getMetadataManager().listKeys(volumeName, - BUCKET, null, KEY_PREFIX, 100).getKeys(); - - boolean allKeysFound = (expectedKeys.size() == keys.size()); - if (!allKeysFound) { - LOG.info("In {} waiting for number of keys {} to equal " + - "expected number of keys {}.", om.getOMNodeId(), - keys.size(), expectedKeys.size()); - } else { - for (OmKeyInfo keyInfo : keys) { - if (!expectedKeys.contains(keyInfo.getKeyName())) { - allKeysFound = false; - LOG.info("In {} expected keys did not contain key {}", - om.getOMNodeId(), keyInfo.getKeyName()); - break; - } - } - } - - return allKeysFound; - }); - } - } - - private long submitPrepareRequest() throws Exception { - return clientProtocol.getOzoneManagerClient() - .prepareOzoneManager(PREPARE_FLUSH_WAIT_TIMEOUT_SECONDS, - PREPARE_FLUSH_INTERVAL_SECONDS); - } - - private void submitCancelPrepareRequest() throws Exception { - clientProtocol.getOzoneManagerClient().cancelOzoneManagerPrepare(); - } - - private void assertClusterPrepared(long expectedPreparedIndex) - throws Exception { - assertClusterPrepared(expectedPreparedIndex, - cluster.getOzoneManagersList()); - } - - private void assertClusterPrepared(long expectedPreparedIndex, - List ozoneManagers) throws Exception { - - for (OzoneManager om : ozoneManagers) { - // Wait for each OM to be running and transaction info to match to know - // it is prepared. - LambdaTestUtils.await(WAIT_TIMEOUT_MILLIS, - 1000, () -> { - if (!om.isRunning()) { - LOG.info("{} is not yet started.", om.getOMNodeId()); - return false; - } else { - OzoneManagerPrepareState.State state = - om.getPrepareState().getState(); - - LOG.info("{} has prepare status: {} prepare index: {}.", - om.getOMNodeId(), state.getStatus(), state.getIndex()); - - return (state.getStatus() == PrepareStatus.PREPARE_COMPLETED) && - (state.getIndex() >= expectedPreparedIndex); - } - }); - } - - // Submitting a read request should pass. - clientProtocol.listVolumes(VOLUME, "", 100); - - // Submitting write request should fail. - OMException omException = assertThrows(OMException.class, - () -> clientProtocol.createVolume("vol")); - assertEquals(NOT_SUPPORTED_OPERATION_WHEN_PREPARED, - omException.getResult()); - } - - private void assertClusterNotPrepared() throws Exception { - assertClusterNotPrepared(cluster.getOzoneManagersList()); - } - - private void assertClusterNotPrepared(List ozoneManagers) - throws Exception { - for (OzoneManager om : ozoneManagers) { - LambdaTestUtils.await(WAIT_TIMEOUT_MILLIS, - 1000, () -> { - if (!om.isRunning()) { - return false; - } else { - return om.getPrepareState().getState().getStatus() == - PrepareStatus.NOT_PREPARED; - } - }); - } - - // Submitting a read request should pass. - clientProtocol.listVolumes(VOLUME, "", 100); - - // Submitting write request should also pass. - clientProtocol.createVolume("vol"); - clientProtocol.deleteVolume("vol"); - } - - private void assertRatisLogsCleared() throws Exception { - assertRatisLogsCleared(cluster.getOzoneManagersList()); - } - - private void assertRatisLogsCleared(List ozoneManagers) - throws Exception { - for (OzoneManager om: ozoneManagers) { - LambdaTestUtils.await(WAIT_TIMEOUT_MILLIS, 1000, - () -> !logFilesPresentInRatisPeer(om)); - } - } - - private void assertShouldBeAbleToPrepare() throws Exception { - long prepareIndex = submitPrepareRequest(); - assertClusterPrepared(prepareIndex); - assertRatisLogsCleared(); - } -} diff --git a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java index 743214d72f4d..340f9d160e52 100644 --- a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java +++ b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java @@ -100,6 +100,7 @@ import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.util.ToolRunner; import org.apache.ozone.test.GenericTestUtils; +import org.apache.ozone.test.tag.Unhealthy; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; @@ -564,6 +565,7 @@ public void testOzoneAdminCmdList() throws UnsupportedEncodingException { execute(ozoneAdminShell, args); } + @Unhealthy("HDDS-14686") @Test public void testAdminCmdListOpenFiles() throws IOException, InterruptedException, TimeoutException { @@ -684,6 +686,7 @@ public void testAdminCmdListOpenFiles() } + @Unhealthy("HDDS-14686") @Test public void testAdminCmdListOpenFilesWithDeletedKeys() throws Exception { @@ -2478,6 +2481,54 @@ public void testKeyDeleteLegacyWithEnableFileSystemPath() throws IOException { execute(ozoneShell, args); } + /** + * Test that even though prepare commands are deprecated, they still succeed without blocking writes. + */ + @Test + public void testPrepareCommandNoOp() throws IOException { + String volumeName = "vol-prepare-test"; + String bucketName = "bucket-prepare-test"; + + // Execute prepare command via CLI + String[] args = new String[] {"om", "prepare", "--service-id", omServiceId}; + execute(ozoneAdminShell, args); + out.reset(); + + // Verify write operations still work (prepare should not block them) + // Create volume + args = new String[] {"volume", "create", "o3://" + omServiceId + OZONE_URI_DELIMITER + volumeName}; + execute(ozoneShell, args); + out.reset(); + + // Create bucket + args = new String[] {"bucket", "create", "o3://" + omServiceId + OZONE_URI_DELIMITER + volumeName + + OZONE_URI_DELIMITER + bucketName}; + execute(ozoneShell, args); + out.reset(); + + // Create key + String keyName = OZONE_URI_DELIMITER + volumeName + OZONE_URI_DELIMITER + + bucketName + OZONE_URI_DELIMITER + "testkey"; + args = new String[] {"key", "put", "o3://" + omServiceId + keyName, testFile.getPath()}; + execute(ozoneShell, args); + out.reset(); + + // Verify the key was created successfully + OzoneVolume volume = client.getObjectStore().getVolume(volumeName); + OzoneBucket bucket = volume.getBucket(bucketName); + assertNotNull(bucket.getKey("testkey"), "Key should be created successfully after prepare"); + + // Execute cancelprepare command via CLI + args = new String[] {"om", "cancelprepare", "--service-id", omServiceId}; + execute(ozoneAdminShell, args); + out.reset(); + + // Cleanup + args = new String[] {"volume", "delete", volumeName, "-r", "--yes"}; + execute(ozoneShell, args); + out.reset(); + } + private static String getKeyProviderURI(MiniKMS kms) { if (kms == null) { return ""; diff --git a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto index 72c226b62b30..9c46e8add17c 100644 --- a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto +++ b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto @@ -75,9 +75,9 @@ enum Type { DBUpdates = 53; FinalizeUpgrade = 54; // [deprecated = true] FinalizeUpgradeProgress = 55; - Prepare = 56; - PrepareStatus = 57; - CancelPrepare = 58; + Prepare = 56; // Deprecated + PrepareStatus = 57; // Deprecated + CancelPrepare = 58; // Deprecated GetDelegationToken = 61; RenewDelegationToken = 62; @@ -223,9 +223,9 @@ message OMRequest { optional DBUpdatesRequest dbUpdatesRequest = 53; optional FinalizeUpgradeRequest finalizeUpgradeRequest = 54 [deprecated = true]; optional FinalizeUpgradeProgressRequest finalizeUpgradeProgressRequest = 55 [deprecated = true]; - optional PrepareRequest prepareRequest = 56; - optional PrepareStatusRequest prepareStatusRequest = 57; - optional CancelPrepareRequest cancelPrepareRequest = 58; + optional PrepareRequest prepareRequest = 56 [deprecated = true]; + optional PrepareStatusRequest prepareStatusRequest = 57 [deprecated = true]; + optional CancelPrepareRequest cancelPrepareRequest = 58 [deprecated = true]; optional hadoop.common.GetDelegationTokenRequestProto getDelegationTokenRequest = 61; optional hadoop.common.RenewDelegationTokenRequestProto renewDelegationTokenRequest= 62; @@ -374,9 +374,9 @@ message OMResponse { optional DBUpdatesResponse dbUpdatesResponse = 52; optional FinalizeUpgradeResponse finalizeUpgradeResponse = 54 [deprecated = true]; optional FinalizeUpgradeProgressResponse finalizeUpgradeProgressResponse = 55 [deprecated = true]; - optional PrepareResponse prepareResponse = 56; - optional PrepareStatusResponse prepareStatusResponse = 57; - optional CancelPrepareResponse cancelPrepareResponse = 58; + optional PrepareResponse prepareResponse = 56 [deprecated = true]; + optional PrepareStatusResponse prepareStatusResponse = 57 [deprecated = true]; + optional CancelPrepareResponse cancelPrepareResponse = 58 [deprecated = true]; optional GetDelegationTokenResponseProto getDelegationTokenResponse = 61; optional RenewDelegationTokenResponseProto renewDelegationTokenResponse = 62; @@ -558,9 +558,9 @@ enum Status { REMOVE_UPGRADE_TO_LAYOUT_VERSION_FAILED = 70; UPDATE_LAYOUT_VERSION_FAILED = 71; LAYOUT_FEATURE_FINALIZATION_FAILED = 72; - PREPARE_FAILED = 73; - NOT_SUPPORTED_OPERATION_WHEN_PREPARED = 74; - NOT_SUPPORTED_OPERATION_PRIOR_FINALIZATION = 75; + PREPARE_FAILED = 73; // Deprecated + NOT_SUPPORTED_OPERATION_WHEN_PREPARED = 74; // Deprecated + NOT_SUPPORTED_OPERATION_PRIOR_FINALIZATION = 75; // Deprecated TENANT_NOT_FOUND = 76; TENANT_ALREADY_EXISTS = 77; @@ -1678,23 +1678,38 @@ message FinalizeUpgradeProgressResponse { required hadoop.hdds.UpgradeFinalizationStatus status = 1; } +/** + * Deprecated. Prepare functionality has been removed. + */ message PrepareRequest { - required PrepareRequestArgs args = 1; + required PrepareRequestArgs args = 1 [deprecated = true]; } +/** + * Deprecated. Prepare functionality has been removed and is now a no-op. + */ message PrepareRequestArgs { - optional uint64 txnApplyWaitTimeoutSeconds = 1 [default = 300]; - optional uint64 txnApplyCheckIntervalSeconds = 2 [default = 5]; + optional uint64 txnApplyWaitTimeoutSeconds = 1 [default = 300, deprecated = true]; + optional uint64 txnApplyCheckIntervalSeconds = 2 [default = 5, deprecated = true]; } +/** + * Deprecated. Prepare functionality has been removed and is now a no-op. + */ message PrepareResponse { - required uint64 txnID = 1; + required uint64 txnID = 1 [deprecated = true]; } +/** + * Deprecated. Prepare functionality has been removed and is now a no-op. + */ message PrepareStatusRequest { - required uint64 txnID = 1; + required uint64 txnID = 1 [deprecated = true]; } +/** + * Deprecated. Prepare functionality has been removed and server status will always be NOT_PREPARED. + */ message PrepareStatusResponse { enum PrepareStatus { NOT_PREPARED = 1; @@ -1702,13 +1717,19 @@ message PrepareStatusResponse { PREPARE_COMPLETED = 3; } required PrepareStatus status = 1; - optional uint64 currentTxnIndex = 2; + optional uint64 currentTxnIndex = 2 [deprecated = true]; } +/** + * Deprecated. Prepare functionality has been removed and is now a no-op. + */ message CancelPrepareRequest { } +/** + * Deprecated. Prepare functionality has been removed and is now a no-op. + */ message CancelPrepareResponse { } diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java index 85c4545fceae..c02ff4457bff 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java @@ -105,8 +105,6 @@ public enum OMAction implements AuditAction { ABORT_EXPIRED_MULTIPART_UPLOAD, - UPGRADE_PREPARE, - UPGRADE_CANCEL, UPGRADE_FINALIZE, LIST_OPEN_FILES, diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMStarterInterface.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMStarterInterface.java index 94be4139bc2a..0942fd33b058 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMStarterInterface.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMStarterInterface.java @@ -34,7 +34,4 @@ boolean init(OzoneConfiguration conf) throws IOException, void bootstrap(OzoneConfiguration conf, boolean force) throws IOException, AuthenticationException; - - void startAndCancelPrepare(OzoneConfiguration conf) throws IOException, - AuthenticationException; } diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java index 423a879e0dd5..f76545184417 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java @@ -50,10 +50,8 @@ import static org.apache.hadoop.ozone.OzoneConsts.OM_METRICS_FILE; import static org.apache.hadoop.ozone.OzoneConsts.OM_METRICS_TEMP_FILE; import static org.apache.hadoop.ozone.OzoneConsts.OZONE_RATIS_SNAPSHOT_DIR; -import static org.apache.hadoop.ozone.OzoneConsts.PREPARE_MARKER_KEY; import static org.apache.hadoop.ozone.OzoneConsts.RPC_PORT; import static org.apache.hadoop.ozone.OzoneConsts.SCM_CA_CERT_STORAGE_DIR; -import static org.apache.hadoop.ozone.OzoneConsts.TRANSACTION_INFO_KEY; import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_DEFAULT_BUCKET_LAYOUT; import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_DEFAULT_BUCKET_LAYOUT_DEFAULT; import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_DIR_DELETING_SERVICE_INTERVAL; @@ -103,7 +101,6 @@ import static org.apache.hadoop.ozone.om.s3.S3SecretStoreConfigurationKeys.S3_SECRET_STORAGE_TYPE; import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerInterServiceProtocolProtos.OzoneManagerInterService; import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OzoneManagerService; -import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus; import static org.apache.hadoop.ozone.upgrade.UpgradeFinalization.FINALIZATION_DONE_MSG; import static org.apache.hadoop.ozone.upgrade.UpgradeFinalization.FINALIZATION_REQUIRED_MSG; import static org.apache.hadoop.ozone.upgrade.UpgradeFinalization.FINALIZED_MSG; @@ -485,8 +482,6 @@ public final class OzoneManager extends ServiceRuntimeInfoImpl private final boolean isStrictS3; private ExitManager exitManager; - private OzoneManagerPrepareState prepareState; - private boolean isBootstrapping = false; private boolean isForcedBootstrapping = false; @@ -1051,11 +1046,6 @@ public void close() { if (withNewSnapshot) { versionManager.finalizeFromSnapshotIfRequired(metadataManager.getMetaTable()); - instantiatePrepareStateAfterSnapshot(); - } else { - // Prepare state depends on the transaction ID of metadataManager after a - // restart. - instantiatePrepareStateOnStartup(); } } @@ -5043,102 +5033,6 @@ public OMVersionManager getVersionManager() { return versionManager; } - public OzoneManagerPrepareState getPrepareState() { - return prepareState; - } - - /** - * Determines if the prepare gate should be enabled on this OM after OM - * is restarted. - * This must be done after metadataManager is instantiated - * and before the RPC server is started. - */ - private void instantiatePrepareStateOnStartup() - throws IOException { - TransactionInfo txnInfo = metadataManager.getTransactionInfoTable() - .get(TRANSACTION_INFO_KEY); - if (txnInfo == null) { - // No prepare request could be received if there are not transactions. - prepareState = new OzoneManagerPrepareState(configuration); - } else { - prepareState = new OzoneManagerPrepareState(configuration, - txnInfo.getTransactionIndex()); - TransactionInfo dbPrepareValue = - metadataManager.getTransactionInfoTable().get(PREPARE_MARKER_KEY); - - boolean hasMarkerFile = - (prepareState.getState().getStatus() == - PrepareStatus.PREPARE_COMPLETED); - boolean hasDBMarker = (dbPrepareValue != null); - - if (hasDBMarker) { - long dbPrepareIndex = dbPrepareValue.getTransactionIndex(); - - if (hasMarkerFile) { - long prepareFileIndex = prepareState.getState().getIndex(); - // If marker and DB prepare index do not match, use the DB value - // since this is synced through Ratis, to avoid divergence. - if (prepareFileIndex != dbPrepareIndex) { - LOG.warn("Prepare marker file index {} does not match DB prepare " + - "index {}. Writing DB index to prepare file and maintaining " + - "prepared state.", prepareFileIndex, dbPrepareIndex); - prepareState.finishPrepare(dbPrepareIndex); - } - // Else, marker and DB are present and match, so OM is prepared. - } else { - // Prepare cancelled with startup flag to remove marker file. - // Persist this to the DB. - // If the startup flag is used it should be used on all OMs to avoid - // divergence. - metadataManager.getTransactionInfoTable().delete(PREPARE_MARKER_KEY); - } - } else if (hasMarkerFile) { - // Marker file present but no DB entry present. - // This should never happen. If a prepare request fails partway - // through, OM should replay it so both the DB and marker file exist. - throw new OMException("Prepare marker file found on startup without " + - "a corresponding database entry. Corrupt prepare state.", - ResultCodes.PREPARE_FAILED); - } - // Else, no DB or marker file, OM is not prepared. - } - } - - /** - * Determines if the prepare gate should be enabled on this OM after OM - * receives a snapshot. - */ - private void instantiatePrepareStateAfterSnapshot() - throws IOException { - TransactionInfo txnInfo = metadataManager.getTransactionInfoTable() - .get(TRANSACTION_INFO_KEY); - if (txnInfo == null) { - // No prepare request could be received if there are not transactions. - prepareState = new OzoneManagerPrepareState(configuration); - } else { - prepareState = new OzoneManagerPrepareState(configuration, - txnInfo.getTransactionIndex()); - TransactionInfo dbPrepareValue = - metadataManager.getTransactionInfoTable().get(PREPARE_MARKER_KEY); - - boolean hasDBMarker = (dbPrepareValue != null); - - if (hasDBMarker) { - // Snapshot contained a prepare request to apply. - // Update the in memory prepare gate and marker file index. - // If we have already done this, the operation is idempotent. - long dbPrepareIndex = dbPrepareValue.getTransactionIndex(); - prepareState.restorePrepareFromIndex(dbPrepareIndex, - txnInfo.getTransactionIndex()); - } else { - // No DB marker. - // Deletes marker file if exists, otherwise does nothing if we were not - // already prepared. - prepareState.cancelPrepare(); - } - } - } - public int getMinMultipartUploadPartSize() { return minMultipartUploadPartSize; } diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerPrepareState.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerPrepareState.java deleted file mode 100644 index 076c13766719..000000000000 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerPrepareState.java +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hadoop.ozone.om; - -import com.google.common.annotations.VisibleForTesting; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import org.apache.hadoop.hdds.conf.ConfigurationSource; -import org.apache.hadoop.hdds.server.ServerUtils; -import org.apache.hadoop.ozone.OzoneConsts; -import org.apache.hadoop.ozone.om.exceptions.OMException; -import org.apache.hadoop.ozone.om.ratis.OzoneManagerRatisServer; -import org.apache.hadoop.ozone.om.ratis.OzoneManagerStateMachine; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Controls the prepare state of the {@link OzoneManager} containing the - * instance. When prepared, an ozone manager should have no Ratis logs - * remaining, disallow all write requests except prepare and cancel prepare, - * and have a marker file present on disk that will cause it to remain prepared - * on restart. - */ -public final class OzoneManagerPrepareState { - public static final long NO_PREPARE_INDEX = -1; - - private static final Logger LOG = - LoggerFactory.getLogger(OzoneManagerPrepareState.class); - - private boolean prepareGateEnabled; - private long prepareIndex; - private PrepareStatus status; - private final ConfigurationSource conf; - - /** - * Sets prepare state to {@link PrepareStatus#NOT_PREPARED}, - * ignoring any marker file that may or may not exist. - */ - public OzoneManagerPrepareState(ConfigurationSource conf) { - this.conf = conf; - prepareIndex = NO_PREPARE_INDEX; - prepareGateEnabled = false; - status = PrepareStatus.NOT_PREPARED; - } - - /** - * Restores prepare state from the marker file if it exists, otherwise sets - * prepare state to {@link PrepareStatus#NOT_PREPARED}. - * - * @param conf Configuration used to determine marker file location. - * @param currentIndex The OM's current log index to verify the prepare - * index against. Prepare index should not be larger than the current - * index. - * @throws IOException On error restoring prepare state from marker file. - */ - public OzoneManagerPrepareState(ConfigurationSource conf, long currentIndex) - throws IOException { - this(conf); - - if (getPrepareMarkerFile().exists()) { - restorePrepareFromFile(currentIndex); - } - } - - /** - * Turns on the prepare gate flag, clears the prepare index, and moves the - * prepare status to {@link PrepareStatus#PREPARE_GATE_ENABLED}. - * - * Turning on the prepare gate flag will enable a gate in the - * {@link OzoneManagerStateMachine#preAppendTransaction} (called on leader - * OM only) and {@link OzoneManagerRatisServer#submitRequest} - * (called on all OMs) that block write requests from reaching the OM and - * fail them with error responses to the client. - */ - public synchronized void enablePrepareGate() { - prepareGateEnabled = true; - prepareIndex = NO_PREPARE_INDEX; - status = PrepareStatus.PREPARE_GATE_ENABLED; - } - - /** - * Removes the prepare marker file, clears the prepare index, turns off - * the prepare gate, and moves the prepare status to - * {@link PrepareStatus#NOT_PREPARED}. - * This can be called from any state to clear the current prepare state. - * - * @throws IOException If the prepare marker file exists but cannot be - * deleted. - */ - public synchronized void cancelPrepare() throws IOException { - prepareIndex = NO_PREPARE_INDEX; - prepareGateEnabled = false; - status = PrepareStatus.NOT_PREPARED; - deletePrepareMarkerFile(); - } - - /** - * Enables the prepare gate, writes the prepare marker file, sets the in - * memory prepare index, and - * moves the prepare status to {@link PrepareStatus#PREPARE_COMPLETED}. - * This can be called from any state to move the OM into prepare mode. - * - * @param index The log index to prepare the OM on. - * @throws IOException If the marker file cannot be written. - */ - public synchronized void finishPrepare(long index) throws IOException { - restorePrepareFromIndex(index, index, true); - } - - /** - * Finishes preparation the same way as - * {@link OzoneManagerPrepareState#finishPrepare(long)}, but only if {@code - * currentIndex} is at least as large as {@code minIndex}. This is useful - * if the current log index needs to be checked against a prepare index - * saved to disk for validity. - */ - public synchronized void restorePrepareFromIndex(long restoredPrepareIndex, - long currentIndex) throws IOException { - restorePrepareFromIndex(restoredPrepareIndex, currentIndex, true); - } - - private void restorePrepareFromIndex(long restoredPrepareIndex, - long currentIndex, boolean writeFile) throws IOException { - if (restoredPrepareIndex <= currentIndex) { - // Enabling the prepare gate is idempotent, and may have already been - // performed if we are the leader. If we are a follower, we must ensure - // this is run now in case we become the leader. - enablePrepareGate(); - - if (writeFile) { - writePrepareMarkerFile(restoredPrepareIndex); - } - prepareIndex = currentIndex; - status = PrepareStatus.PREPARE_COMPLETED; - } else { - throwPrepareException("Failed to restore OM prepare " + - "state, because the existing prepare index %d is larger than" + - "the current index %d.", restoredPrepareIndex, currentIndex); - } - } - - /** - * Uses the on disk marker file to determine the OM's prepare state. - * If the marker file exists and contains an index matching {@code - * currentIndex}, the necessary steps will be taken to finish - * preparation and the state will be moved to - * {@link PrepareStatus#PREPARE_COMPLETED}. - * Else, the status will be moved to - * {@link PrepareStatus#NOT_PREPARED} and any preparation steps will - * be cancelled. - * - * @throws IOException If the marker file cannot be read, and it cannot be - * deleted as part of moving to the - * {@link PrepareStatus#NOT_PREPARED} state. - */ - public synchronized void restorePrepareFromFile(long currentIndex) - throws IOException { - long prepareMarkerIndex = NO_PREPARE_INDEX; - - File prepareMarkerFile = getPrepareMarkerFile(); - if (prepareMarkerFile.exists()) { - byte[] data = new byte[(int) prepareMarkerFile.length()]; - try (InputStream stream = Files.newInputStream(prepareMarkerFile.toPath())) { - stream.read(data); - } catch (IOException e) { - throwPrepareException(e, "Failed to read prepare marker " + - "file %s while restoring OM.", prepareMarkerFile.getAbsolutePath()); - } - - try { - prepareMarkerIndex = Long.parseLong( - new String(data, StandardCharsets.UTF_8)); - } catch (NumberFormatException e) { - throwPrepareException("Failed to parse log index from " + - "prepare marker file %s while restoring OM.", - prepareMarkerFile.getAbsolutePath()); - } - } else { - // No marker file found. - throwPrepareException("Unable to find prepare marker file to restore" + - " from. Expected %s: ", prepareMarkerFile.getAbsolutePath()); - } - - restorePrepareFromIndex(prepareMarkerIndex, currentIndex, false); - } - - private void throwPrepareException(Throwable cause, String format, - Object... args) throws OMException { - throw new OMException(String.format(format, args), cause, - OMException.ResultCodes.PREPARE_FAILED); - } - - private void throwPrepareException(String format, - Object... args) throws OMException { - throw new OMException(String.format(format, args), - OMException.ResultCodes.PREPARE_FAILED); - } - - /** - * If the prepare gate is enabled, always returns true. - * If the prepare gate is disabled, returns true only if {@code - * requestType} is {@code Prepare} or {@code CancelPrepare}. Returns false - * otherwise. - */ - public synchronized boolean requestAllowed(Type requestType) { - boolean requestAllowed = true; - - if (prepareGateEnabled) { - requestAllowed = - (requestType == Type.Prepare) || (requestType == Type.CancelPrepare); - } - - return requestAllowed; - } - - /** - * @return the current log index and status of preparation. - * Both fields are returned together to provide a consistent view of the - * state, which would not be guaranteed if they had to be retrieved through - * separate getters. - */ - public synchronized State getState() { - return new State(prepareIndex, status); - } - - /** - * Creates a prepare marker file inside the OM metadata directory which - * contains the log index {@code index}. - * If a marker file already exists, it will be overwritten. - */ - private void writePrepareMarkerFile(long index) throws IOException { - File markerFile = getPrepareMarkerFile(); - File parentDir = markerFile.getParentFile(); - Files.createDirectories(parentDir.toPath()); - - try (OutputStream stream = Files.newOutputStream(markerFile.toPath())) { - stream.write(Long.toString(index).getBytes(StandardCharsets.UTF_8)); - } - - LOG.info("Prepare marker file written with log index {} to file {}", index, - markerFile.getAbsolutePath()); - } - - private void deletePrepareMarkerFile() - throws IOException { - File markerFile = getPrepareMarkerFile(); - if (markerFile.exists()) { - Files.delete(markerFile.toPath()); - LOG.info("Deleted prepare marker file: {}", markerFile.getAbsolutePath()); - } else { - LOG.debug("Request to delete prepare marker file that does not exist: {}", - markerFile.getAbsolutePath()); - } - } - - /** - * Returns a {@link File} object representing the prepare marker file, - * which may or may not actually exist on disk. - * This method should be used for testing only. - */ - @VisibleForTesting - public File getPrepareMarkerFile() { - File markerFileDir = new File(ServerUtils.getOzoneMetaDirPath(conf), - OMStorage.STORAGE_DIR_CURRENT); - return new File(markerFileDir, OzoneConsts.PREPARE_MARKER); - } - - /** - * The current state of preparation is defined by the status and the - * prepare index that are currently set. - */ - public static class State { - private final long index; - private final PrepareStatus status; - - public State(long index, PrepareStatus status) { - this.index = index; - this.status = status; - } - - public long getIndex() { - return index; - } - - public PrepareStatus getStatus() { - return status; - } - } -} diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerStarter.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerStarter.java index af4d62b92db9..f24c584c9af6 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerStarter.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManagerStarter.java @@ -104,22 +104,25 @@ public void initOm() } /** - * This function implements a sub-command to allow the OM to be + * This function previously implemented a sub-command to allow the OM to be * Removed from prepare mode after an upgrade or downgrade. + * Since the Ozone Manager no longer requires prepare for upgrade, these flags are a no-op. */ @CommandLine.Command(name = "--upgrade", aliases = "--downgrade", customSynopsis = "ozone om [global options] --upgrade", - description = "Cancels prepare state in this OM on startup", + description = "Deprecated. Starts OM normally. Kept for backward compatibility.", + hidden = true, mixinStandardHelpOptions = true, versionProvider = HddsVersionProvider.class) public void startOmUpgrade() throws Exception { try { commonInit(); - receiver.startAndCancelPrepare(conf); + // These flags are deprecated and no longer have special behavior. + // Just start OM normally for backward compatibility. + receiver.start(conf); } catch (Exception ex) { - LOG.error("Cancelling prepare to start OM in upgrade mode failed " + - "with exception", ex); + LOG.error("Starting OM failed with exception", ex); throw ex; } } @@ -222,15 +225,5 @@ public void bootstrap(OzoneConfiguration conf, boolean force) om.join(); } } - - @Override - public void startAndCancelPrepare(OzoneConfiguration conf) - throws IOException, AuthenticationException { - try (OzoneManager om = OzoneManager.createOm(conf)) { - om.getPrepareState().cancelPrepare(); - om.start(); - om.join(); - } - } } } diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/helpers/OMAuditLogger.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/helpers/OMAuditLogger.java index ed742bad1649..8a4eba27573c 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/helpers/OMAuditLogger.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/helpers/OMAuditLogger.java @@ -89,8 +89,6 @@ private static void init() { CMD_AUDIT_ACTION_MAP.put(Type.AbortExpiredMultiPartUploads, OMAction.ABORT_EXPIRED_MULTIPART_UPLOAD); CMD_AUDIT_ACTION_MAP.put(Type.SetVolumeProperty, OMAction.SET_OWNER); CMD_AUDIT_ACTION_MAP.put(Type.SetBucketProperty, OMAction.UPDATE_BUCKET); - CMD_AUDIT_ACTION_MAP.put(Type.Prepare, OMAction.UPGRADE_PREPARE); - CMD_AUDIT_ACTION_MAP.put(Type.CancelPrepare, OMAction.UPGRADE_CANCEL); CMD_AUDIT_ACTION_MAP.put(Type.FinalizeUpgrade, OMAction.UPGRADE_FINALIZE); CMD_AUDIT_ACTION_MAP.put(Type.GetObjectTagging, OMAction.GET_OBJECT_TAGGING); CMD_AUDIT_ACTION_MAP.put(Type.PutObjectTagging, OMAction.PUT_OBJECT_TAGGING); diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerRatisServer.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerRatisServer.java index 18defcf808a2..5cec7ce5405b 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerRatisServer.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerRatisServer.java @@ -70,7 +70,6 @@ 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.OMResponse; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status; import org.apache.ratis.conf.Parameters; import org.apache.ratis.conf.RaftProperties; import org.apache.ratis.grpc.GrpcConfigKeys; @@ -247,27 +246,9 @@ public static OzoneManagerRatisServer newOMRatisServer( * @throws ServiceException */ public OMResponse submitRequest(OMRequest omRequest, boolean isWrite) throws ServiceException { - // In prepare mode, only prepare and cancel requests are allowed to go - // through. - if (ozoneManager.getPrepareState().requestAllowed(omRequest.getCmdType())) { - RaftClientRequest raftClientRequest = createRaftRequest(omRequest, isWrite); - RaftClientReply raftClientReply = submitRequestToRatis(raftClientRequest); - return createOmResponse(omRequest, raftClientReply); - } else { - LOG.info("Rejecting write request on OM {} because it is in prepare " + - "mode: {}", ozoneManager.getOMNodeId(), - omRequest.getCmdType().name()); - - String message = "Cannot apply write request " + - omRequest.getCmdType().name() + " when OM is in prepare mode."; - OMResponse.Builder omResponse = OMResponse.newBuilder() - .setMessage(message) - .setStatus(Status.NOT_SUPPORTED_OPERATION_WHEN_PREPARED) - .setCmdType(omRequest.getCmdType()) - .setTraceID(omRequest.getTraceID()) - .setSuccess(false); - return omResponse.build(); - } + RaftClientRequest raftClientRequest = createRaftRequest(omRequest, isWrite); + RaftClientReply raftClientReply = submitRequestToRatis(raftClientRequest); + return createOmResponse(omRequest, raftClientReply); } private OMResponse createOmResponse(OMRequest omRequest, diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerStateMachine.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerStateMachine.java index feeda4ca72be..815bd8454fb7 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerStateMachine.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/OzoneManagerStateMachine.java @@ -44,7 +44,6 @@ import org.apache.hadoop.ozone.om.OMConfigKeys; import org.apache.hadoop.ozone.om.OMMetrics; import org.apache.hadoop.ozone.om.OzoneManager; -import org.apache.hadoop.ozone.om.OzoneManagerPrepareState; import org.apache.hadoop.ozone.om.exceptions.OMException; import org.apache.hadoop.ozone.om.execution.flowcontrol.ExecutionContext; import org.apache.hadoop.ozone.om.helpers.OMRatisHelper; @@ -52,12 +51,10 @@ import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerRatisUtils; import org.apache.hadoop.ozone.om.response.DummyOMClientResponse; import org.apache.hadoop.ozone.om.response.OMClientResponse; -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.OMResponse; import org.apache.hadoop.ozone.protocolPB.OzoneManagerRequestHandler; import org.apache.hadoop.ozone.protocolPB.RequestHandler; -import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.util.Time; import org.apache.hadoop.util.concurrent.HadoopExecutors; import org.apache.ratis.proto.RaftProtos; @@ -68,7 +65,6 @@ import org.apache.ratis.protocol.RaftGroupMemberId; import org.apache.ratis.protocol.RaftPeer; import org.apache.ratis.protocol.RaftPeerId; -import org.apache.ratis.protocol.exceptions.StateMachineException; import org.apache.ratis.server.RaftServer; import org.apache.ratis.server.protocol.TermIndex; import org.apache.ratis.server.raftlog.RaftLog; @@ -394,52 +390,6 @@ public TransactionContext startTransaction( .build(); } - @Override - public TransactionContext preAppendTransaction(TransactionContext trx) - throws IOException { - final OMRequest request = (OMRequest) trx.getStateMachineContext(); - OzoneManagerProtocolProtos.Type cmdType = request.getCmdType(); - - OzoneManagerPrepareState prepareState = ozoneManager.getPrepareState(); - - if (LOG.isDebugEnabled()) { - LOG.debug("{}: preAppendTransaction {}", getId(), TermIndex.valueOf(trx.getLogEntry())); - } - - if (cmdType == OzoneManagerProtocolProtos.Type.Prepare) { - // Must authenticate prepare requests here, since we must determine - // whether or not to apply the prepare gate before proceeding with the - // prepare request. - UserGroupInformation userGroupInformation = - UserGroupInformation.createRemoteUser( - request.getUserInfo().getUserName()); - if (ozoneManager.isAdminAuthorizationEnabled() - && !ozoneManager.isAdmin(userGroupInformation)) { - String message = "Access denied for user " + userGroupInformation - + ". Superuser privilege is required to prepare upgrade/downgrade."; - OMException cause = - new OMException(message, OMException.ResultCodes.ACCESS_DENIED); - // Leader should not step down because of this failure. - throw new StateMachineException(message, cause, false); - } else { - prepareState.enablePrepareGate(); - } - } - - // In prepare mode, only prepare and cancel requests are allowed to go - // through. - if (prepareState.requestAllowed(cmdType)) { - return trx; - } else { - String message = "Cannot apply write request " + - request.getCmdType().name() + " when OM is in prepare mode."; - OMException cause = new OMException(message, - OMException.ResultCodes.NOT_SUPPORTED_OPERATION_WHEN_PREPARED); - // Indicate that the leader should not step down because of this failure. - throw new StateMachineException(message, cause, false); - } - } - /* * Apply a committed log entry to the state machine. */ diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java index 5d064558831d..ddfc1f153e95 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java @@ -86,9 +86,7 @@ import org.apache.hadoop.ozone.om.request.snapshot.OMSnapshotPurgeRequest; import org.apache.hadoop.ozone.om.request.snapshot.OMSnapshotRenameRequest; import org.apache.hadoop.ozone.om.request.snapshot.OMSnapshotSetPropertyRequest; -import org.apache.hadoop.ozone.om.request.upgrade.OMCancelPrepareRequest; import org.apache.hadoop.ozone.om.request.upgrade.OMFinalizeUpgradeRequest; -import org.apache.hadoop.ozone.om.request.upgrade.OMPrepareRequest; import org.apache.hadoop.ozone.om.request.upgrade.OMStartFinalizeUpgradeRequest; import org.apache.hadoop.ozone.om.request.util.OMEchoRPCWriteRequest; import org.apache.hadoop.ozone.om.request.volume.OMQuotaRepairRequest; @@ -190,10 +188,6 @@ public static OMClientRequest createClientRequest(OMRequest omRequest, return new OMFinalizeUpgradeRequest(omRequest); case StartFinalizeUpgrade: return new OMStartFinalizeUpgradeRequest(omRequest); - case Prepare: - return new OMPrepareRequest(omRequest); - case CancelPrepare: - return new OMCancelPrepareRequest(omRequest); case SetS3Secret: return new OMSetSecretRequest(omRequest); case RevokeS3Secret: diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMCancelPrepareRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMCancelPrepareRequest.java deleted file mode 100644 index 2a9d36940e64..000000000000 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMCancelPrepareRequest.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hadoop.ozone.om.request.upgrade; - -import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type; - -import java.io.IOException; -import java.util.HashMap; -import org.apache.hadoop.ozone.audit.AuditLogger; -import org.apache.hadoop.ozone.audit.OMAction; -import org.apache.hadoop.ozone.om.OzoneManager; -import org.apache.hadoop.ozone.om.exceptions.OMException; -import org.apache.hadoop.ozone.om.execution.flowcontrol.ExecutionContext; -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.response.upgrade.OMCancelPrepareResponse; -import org.apache.hadoop.ozone.om.response.upgrade.OMPrepareResponse; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CancelPrepareResponse; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse; -import org.apache.hadoop.security.UserGroupInformation; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * OM request class to cancel preparation. - */ -public class OMCancelPrepareRequest extends OMClientRequest { - private static final Logger LOG = - LoggerFactory.getLogger(OMCancelPrepareRequest.class); - - public OMCancelPrepareRequest(OMRequest omRequest) { - super(omRequest); - } - - @Override - public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, ExecutionContext context) { - - LOG.info("OM {} Received cancel prepare request with log {}", ozoneManager.getOMNodeId(), context.getTermIndex()); - - OMRequest omRequest = getOmRequest(); - AuditLogger auditLogger = ozoneManager.getAuditLogger(); - OzoneManagerProtocolProtos.UserInfo userInfo = omRequest.getUserInfo(); - OMResponse.Builder responseBuilder = - OmResponseUtil.getOMResponseBuilder(omRequest); - responseBuilder.setCmdType(Type.CancelPrepare); - OMClientResponse response = null; - Exception exception = null; - - try { - UserGroupInformation ugi = createUGIForApi(); - if (ozoneManager.isAdminAuthorizationEnabled() && !ozoneManager.isAdmin(ugi)) { - throw new OMException("Access denied for user " - + ugi + ". " + - "Superuser privilege is required to cancel ozone manager " + - "preparation.", - OMException.ResultCodes.ACCESS_DENIED); - } - - // Create response. - CancelPrepareResponse omResponse = CancelPrepareResponse.newBuilder() - .build(); - responseBuilder.setCancelPrepareResponse(omResponse); - response = new OMCancelPrepareResponse(responseBuilder.build()); - - // Deletes on disk marker file, does not update DB and therefore does - // not update cache. - ozoneManager.getPrepareState().cancelPrepare(); - - LOG.info("OM {} prepare state cancelled at log {}. Returning response {}", - ozoneManager.getOMNodeId(), context.getTermIndex(), omResponse); - } catch (IOException e) { - exception = e; - LOG.error("Cancel Prepare Request apply failed in {}. ", - ozoneManager.getOMNodeId(), e); - response = new OMPrepareResponse( - createErrorOMResponse(responseBuilder, e)); - } - - markForAudit(auditLogger, buildAuditMessage(OMAction.UPGRADE_CANCEL, - new HashMap<>(), exception, userInfo)); - return response; - } - -} diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMPrepareRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMPrepareRequest.java deleted file mode 100644 index f349e74e5417..000000000000 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/upgrade/OMPrepareRequest.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hadoop.ozone.om.request.upgrade; - -import static org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type; - -import java.io.IOException; -import java.time.Duration; -import java.time.temporal.ChronoUnit; -import java.util.HashMap; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import org.apache.hadoop.ozone.audit.AuditLogger; -import org.apache.hadoop.ozone.audit.OMAction; -import org.apache.hadoop.ozone.om.OzoneManager; -import org.apache.hadoop.ozone.om.exceptions.OMException; -import org.apache.hadoop.ozone.om.execution.flowcontrol.ExecutionContext; -import org.apache.hadoop.ozone.om.ratis.OzoneManagerDoubleBuffer; -import org.apache.hadoop.ozone.om.ratis.OzoneManagerRatisServer; -import org.apache.hadoop.ozone.om.ratis.OzoneManagerStateMachine; -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.response.upgrade.OMPrepareResponse; -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.OMResponse; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareResponse; -import org.apache.hadoop.util.Time; -import org.apache.ratis.server.RaftServer; -import org.apache.ratis.server.raftlog.RaftLog; -import org.apache.ratis.statemachine.StateMachine; -import org.apache.ratis.util.Preconditions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * OM Request used to flush all transactions to disk, take a DB snapshot, and - * purge the logs, leaving Ratis in a clean state without unapplied log - * entries. This prepares the OM for upgrades/downgrades so that no request - * in the log is applied to the database in the old version of the code in one - * OM, and the new version of the code on another OM. - */ -public class OMPrepareRequest extends OMClientRequest { - private static final Logger LOG = - LoggerFactory.getLogger(OMPrepareRequest.class); - - public OMPrepareRequest(OMRequest omRequest) { - super(omRequest); - } - - @Override - public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, ExecutionContext context) { - final long transactionLogIndex = context.getIndex(); - - LOG.info("OM {} Received prepare request with log {}", ozoneManager.getOMNodeId(), context.getTermIndex()); - - OMRequest omRequest = getOmRequest(); - AuditLogger auditLogger = ozoneManager.getAuditLogger(); - OzoneManagerProtocolProtos.UserInfo userInfo = omRequest.getUserInfo(); - OzoneManagerProtocolProtos.PrepareRequestArgs args = - omRequest.getPrepareRequest().getArgs(); - OMResponse.Builder responseBuilder = - OmResponseUtil.getOMResponseBuilder(omRequest); - responseBuilder.setCmdType(Type.Prepare); - OMClientResponse response = null; - Exception exception = null; - - // Allow double buffer this many seconds to flush all transactions before - // returning an error to the caller. - Duration flushTimeout = - Duration.of(args.getTxnApplyWaitTimeoutSeconds(), ChronoUnit.SECONDS); - // Time between checks to see if double buffer finished flushing. - Duration flushCheckInterval = - Duration.of(args.getTxnApplyCheckIntervalSeconds(), ChronoUnit.SECONDS); - - try { - // Create response. - PrepareResponse omResponse = PrepareResponse.newBuilder() - .setTxnID(transactionLogIndex) - .build(); - responseBuilder.setPrepareResponse(omResponse); - response = new OMPrepareResponse(responseBuilder.build(), - transactionLogIndex); - - // Add response to double buffer before clearing logs. - // This guarantees the log index of this request will be the same as - // the snapshot index in the prepared state. - OzoneManagerDoubleBuffer doubleBuffer = - ozoneManager.getOmRatisServer().getOmStateMachine().getOzoneManagerDoubleBuffer(); - doubleBuffer.add(response, context.getTermIndex()); - - OzoneManagerRatisServer omRatisServer = ozoneManager.getOmRatisServer(); - final RaftServer.Division division = omRatisServer.getServerDivision(); - final OzoneManagerStateMachine stateMachine = (OzoneManagerStateMachine) division.getStateMachine(); - - // Wait for outstanding double buffer entries - // - to be flushed to db, and - // - to be notified by Ratis. - // The log index returned, will be used as the prepare index, is the last Ratis commit index - // which can be higher than the transactionLogIndex of this request. - final long prepareIndex = waitForLogIndex(transactionLogIndex, ozoneManager, stateMachine, - flushTimeout, flushCheckInterval); - Preconditions.assertTrue(prepareIndex >= transactionLogIndex); - takeSnapshotAndPurgeLogs(prepareIndex, division); - - // Save prepare index to a marker file, so if the OM restarts, - // it will remain in prepare mode as long as the file exists and its - // log indices are >= the one in the file. - ozoneManager.getPrepareState().finishPrepare(transactionLogIndex); - - LOG.info("OM {} prepared at log index {}. Returning response {} with " + - "log index {}", ozoneManager.getOMNodeId(), transactionLogIndex, - omResponse, omResponse.getTxnID()); - } catch (OMException e) { - exception = e; - LOG.error("Prepare Request Apply failed in {}. ", - ozoneManager.getOMNodeId(), e); - response = new OMPrepareResponse( - createErrorOMResponse(responseBuilder, e)); - } catch (InterruptedException | IOException e) { - // Set error code so that prepare failure does not cause the OM to - // terminate. - exception = e; - LOG.error("Prepare Request Apply failed in {}. ", - ozoneManager.getOMNodeId(), e); - response = new OMPrepareResponse( - createErrorOMResponse(responseBuilder, new OMException(e, - OMException.ResultCodes.PREPARE_FAILED))); - - // Disable prepare gate and attempt to delete prepare marker file. - // Whether marker file delete fails or succeeds, we will return the - // above error response to the caller. - try { - ozoneManager.getPrepareState().cancelPrepare(); - } catch (IOException ex) { - LOG.error("Failed to delete prepare marker file.", ex); - } - if (e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } - } - - markForAudit(auditLogger, buildAuditMessage(OMAction.UPGRADE_PREPARE, - new HashMap<>(), exception, userInfo)); - return response; - } - - /** - * Waits for the specified index to be applied to {@link OzoneManagerStateMachine}. - * Note that - * - the applied index is updated after the transaction is flushed to db. - * - after a transaction (i) is committed, ratis will append another ratis-metadata transaction (i+1). - * - * @return the last Ratis applied index - */ - private static long waitForLogIndex(long minOMDBFlushIndex, - OzoneManager om, OzoneManagerStateMachine stateMachine, - Duration flushTimeout, Duration flushCheckInterval) - throws InterruptedException, IOException { - - long endTime = Time.monotonicNow() + flushTimeout.toMillis(); - - boolean omDBFlushed = false; - boolean ratisStateMachineApplied = false; - - // Wait for the given Ratis commit index to be applied to Ratis' - // state machine. This index will not appear in the OM DB until a - // snapshot is taken. - // If we purge logs without waiting for this index, it may not make it to - // the RocksDB snapshot, and then the log entry is lost on this OM. - - // Wait OM state machine to apply the given index. - long lastOMDBFlushIndex = RaftLog.INVALID_LOG_INDEX; - long lastRatisAppliedIndex = RaftLog.INVALID_LOG_INDEX; - - LOG.info("{} waiting for index {} to flush to OM DB and flush" + - " to Ratis state machine.", om.getOMNodeId(), minOMDBFlushIndex); - while (!(omDBFlushed && ratisStateMachineApplied) && - Time.monotonicNow() < endTime) { - // Check OM DB. - lastOMDBFlushIndex = om.getRatisSnapshotIndex(); - omDBFlushed = (lastOMDBFlushIndex >= minOMDBFlushIndex); - LOG.debug("{} Current DB transaction index {}.", om.getOMNodeId(), - lastOMDBFlushIndex); - - // Check ratis state machine. - lastRatisAppliedIndex = stateMachine.getLastAppliedTermIndex().getIndex(); - ratisStateMachineApplied = lastRatisAppliedIndex >= minOMDBFlushIndex; - LOG.debug("{} Current Ratis state machine transaction index {}.", - om.getOMNodeId(), lastRatisAppliedIndex); - - if (!(omDBFlushed && ratisStateMachineApplied)) { - Thread.sleep(flushCheckInterval.toMillis()); - } - } - - // If the timeout waiting for all transactions to reach the state machine - // is exceeded, the exception is propagated, resulting in an error response - // to the client. They can retry the prepare request. - if (!omDBFlushed) { - throw new IOException(String.format("After waiting for %d seconds, " + - "OM database flushed index %d which is less than the minimum " + - "required index %d.", - flushTimeout.getSeconds(), lastOMDBFlushIndex, minOMDBFlushIndex)); - } else if (!ratisStateMachineApplied) { - throw new IOException(String.format("After waiting for %d seconds, " + - "Ratis state machine applied index %d which is less than" + - " the minimum required index %d.", - flushTimeout.getSeconds(), lastRatisAppliedIndex, - minOMDBFlushIndex)); - } - return lastRatisAppliedIndex; - } - - /** - * Take a snapshot of the state machine at the last index, and purge at - * least all log with indices less than or equal to the prepare index. - * If there is another prepare request or cancel prepare request, - * this one will end up purging that request since it was allowed through - * the pre-append prepare gate. - * This means that an OM cannot support 2 prepare requests in the - * transaction pipeline (un-applied) at the same time. - */ - public static void takeSnapshotAndPurgeLogs(long prepareIndex, - RaftServer.Division division) throws IOException { - StateMachine stateMachine = division.getStateMachine(); - long snapshotIndex = stateMachine.takeSnapshot(); - LOG.info("takeSnapshot at {} for prepareIndex {}", snapshotIndex, prepareIndex); - - if (snapshotIndex < prepareIndex) { - throw new IOException(String.format("OM DB snapshot index %d is less " + - "than prepare index %d. Some required logs may not have" + - "been persisted to the state machine.", snapshotIndex, - prepareIndex)); - } - - CompletableFuture purgeFuture = - division.getRaftLog().onSnapshotInstalled(snapshotIndex); - - try { - long actualPurgeIndex = purgeFuture.get(); - - if (actualPurgeIndex != snapshotIndex) { - LOG.warn("Actual purge index {} does not " + - "match specified purge index {}. ", actualPurgeIndex, - snapshotIndex); - } - } catch (ExecutionException e) { - // Ozone manager error handler does not respect exception chaining and - // only displays the message of the top level exception. - throw new IOException("Unable to purge logs: " + e.getMessage()); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new IOException("Unable to purge logs: " + e.getMessage()); - } - } -} diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/upgrade/OMCancelPrepareResponse.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/upgrade/OMCancelPrepareResponse.java deleted file mode 100644 index 8e150d696bd6..000000000000 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/upgrade/OMCancelPrepareResponse.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hadoop.ozone.om.response.upgrade; - -import static org.apache.hadoop.ozone.om.codec.OMDBDefinition.TRANSACTION_INFO_TABLE; - -import java.io.IOException; -import org.apache.hadoop.hdds.utils.db.BatchOperation; -import org.apache.hadoop.ozone.om.OMMetadataManager; -import org.apache.hadoop.ozone.om.response.CleanupTableInfo; -import org.apache.hadoop.ozone.om.response.OMClientResponse; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos; - -/** - * Response for cancel prepare. - */ -@CleanupTableInfo(cleanupTables = {TRANSACTION_INFO_TABLE}) -public class OMCancelPrepareResponse extends OMClientResponse { - public OMCancelPrepareResponse( - OzoneManagerProtocolProtos.OMResponse omResponse) { - super(omResponse); - } - - @Override - protected void addToDBBatch(OMMetadataManager omMetadataManager, - BatchOperation batchOperation) throws IOException { - // The cancel prepare request will delete the prepare marker file and - // update in memory state, so this response does not need to perform DB - // update. - } -} diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/upgrade/OMPrepareResponse.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/upgrade/OMPrepareResponse.java deleted file mode 100644 index d9e8901554b0..000000000000 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/upgrade/OMPrepareResponse.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hadoop.ozone.om.response.upgrade; - -import static org.apache.hadoop.ozone.OzoneConsts.PREPARE_MARKER_KEY; -import static org.apache.hadoop.ozone.om.codec.OMDBDefinition.TRANSACTION_INFO_TABLE; - -import java.io.IOException; -import org.apache.hadoop.hdds.utils.TransactionInfo; -import org.apache.hadoop.hdds.utils.db.BatchOperation; -import org.apache.hadoop.ozone.om.OMMetadataManager; -import org.apache.hadoop.ozone.om.response.CleanupTableInfo; -import org.apache.hadoop.ozone.om.response.OMClientResponse; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos; - -/** - * Response for prepare request. - */ -@CleanupTableInfo(cleanupTables = {TRANSACTION_INFO_TABLE}) -public class OMPrepareResponse extends OMClientResponse { - - private long prepareIndex = -1; - - public OMPrepareResponse(OzoneManagerProtocolProtos.OMResponse omResponse, - long prepareIndex) { - super(omResponse); - this.prepareIndex = prepareIndex; - } - - public OMPrepareResponse(OzoneManagerProtocolProtos.OMResponse omResponse) { - super(omResponse); - } - - @Override - protected void addToDBBatch(OMMetadataManager omMetadataManager, - BatchOperation batchOperation) throws IOException { - if (prepareIndex != -1) { - omMetadataManager.getTransactionInfoTable().putWithBatch(batchOperation, - PREPARE_MARKER_KEY, - TransactionInfo.valueOf(TransactionInfo.DEFAULT_VALUE.getTerm(), prepareIndex)); - } - } -} 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 017e840ae576..fb1faf2e680c 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 @@ -17,6 +17,8 @@ package org.apache.hadoop.ozone.protocolPB; +import static org.apache.hadoop.hdds.utils.HddsServerUtil.getRemoteUser; +import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.PERMISSION_DENIED; import static org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.FILESYSTEM_SNAPSHOT; import static org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.HBASE_SUPPORT; import static org.apache.hadoop.ozone.om.upgrade.OMLayoutFeature.MULTITENANCY_SCHEMA; @@ -62,7 +64,6 @@ import org.apache.hadoop.hdds.utils.FaultInjector; import org.apache.hadoop.ozone.OzoneAcl; import org.apache.hadoop.ozone.om.OzoneManager; -import org.apache.hadoop.ozone.om.OzoneManagerPrepareState; import org.apache.hadoop.ozone.om.exceptions.OMException; import org.apache.hadoop.ozone.om.execution.flowcontrol.ExecutionContext; import org.apache.hadoop.ozone.om.helpers.BasicOmKeyInfo; @@ -102,6 +103,7 @@ import org.apache.hadoop.ozone.om.upgrade.DisallowedUntilLayoutVersion; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.BucketArgs; +import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CancelPrepareResponse; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CancelSnapshotDiffRequest; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CancelSnapshotDiffResponse; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CheckVolumeAccessRequest; @@ -145,6 +147,7 @@ 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.OzoneFileStatusProto; +import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareResponse; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrintCompactionLogDagRequest; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrintCompactionLogDagResponse; @@ -172,6 +175,7 @@ import org.apache.hadoop.ozone.upgrade.UpgradeFinalization.StatusAndMessages; import org.apache.hadoop.ozone.util.PayloadUtils; import org.apache.hadoop.ozone.util.ProtobufUtils; +import org.apache.hadoop.security.UserGroupInformation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -307,6 +311,16 @@ public OMResponse handleReadRequest(OMRequest request) { PrepareStatusResponse prepareStatusResponse = getPrepareStatus(); responseBuilder.setPrepareStatusResponse(prepareStatusResponse); break; + case Prepare: + PrepareResponse prepareResponse = prepare(); + responseBuilder.setPrepareResponse(prepareResponse); + responseBuilder.setMessage("Prepare is no longer required in this version"); + break; + case CancelPrepare: + CancelPrepareResponse cancelPrepareResponse = cancelPrepare(); + responseBuilder.setCancelPrepareResponse(cancelPrepareResponse); + responseBuilder.setMessage("Cancel Prepare is no longer required in this version"); + break; case GetS3VolumeContext: GetS3VolumeContextResponse s3VolumeContextResponse = getS3VolumeContext(); @@ -1391,12 +1405,43 @@ private FinalizeUpgradeProgressResponse reportUpgradeProgress( .build(); } - private PrepareStatusResponse getPrepareStatus() { - OzoneManagerPrepareState.State prepareState = - impl.getPrepareState().getState(); + private PrepareStatusResponse getPrepareStatus() throws IOException { + UserGroupInformation ugi = getRemoteUser(); + if (!impl.isAdmin(ugi)) { + throw new OMException("Access denied for user " + ugi + + ". Superuser privilege is required to get prepare status for OM.", PERMISSION_DENIED); + } + + // Prepare is no longer used, always return PREPARE_COMPLETED for backward compatibility return PrepareStatusResponse.newBuilder() - .setStatus(prepareState.getStatus()) - .setCurrentTxnIndex(prepareState.getIndex()).build(); + .setStatus(PrepareStatusResponse.PrepareStatus.PREPARE_COMPLETED) + .setCurrentTxnIndex(0).build(); + } + + private PrepareResponse prepare() throws IOException { + // Prepare functionality is no longer supported. An empty successful transaction is returned for compatibility + // with older clients and upgrade processes. + UserGroupInformation ugi = getRemoteUser(); + if (!impl.isAdmin(ugi)) { + throw new OMException("Access denied for user " + ugi + + ". Superuser privilege is required to prepare OM.", PERMISSION_DENIED); + } + + return PrepareResponse.newBuilder() + .setTxnID(0) // Dummy transaction ID + .build(); + } + + private CancelPrepareResponse cancelPrepare() throws IOException { + // Cancel Prepare functionality is no longer supported. An empty successful transaction is returned for + // compatibility with older clients and upgrade processes. + UserGroupInformation ugi = getRemoteUser(); + if (!impl.isAdmin(ugi)) { + throw new OMException("Access denied for user " + ugi + + ". Superuser privilege is required to cancel prepare for OM.", PERMISSION_DENIED); + } + + return CancelPrepareResponse.newBuilder().build(); } private GetS3VolumeContextResponse getS3VolumeContext() diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerStarter.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerStarter.java index 5ad57731728f..381e0de11b13 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerStarter.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerStarter.java @@ -108,18 +108,6 @@ public void testInitThatReturnsFalseThrowsException() { assertEquals(EXECUTION_ERROR_EXIT_CODE, executeCommand("--init")); } - @Test - public void testCallsStartAndCancelPrepareWithUpgradeFlag() { - assertEquals(OK, executeCommand("--upgrade")); - assertTrue(mock.startAndCancelPrepareCalled); - } - - @Test - public void testUnsuccessfulUpgradeThrowsException() { - mock.throwOnStartAndCancelPrepare = true; - assertEquals(EXECUTION_ERROR_EXIT_CODE, executeCommand("--upgrade")); - } - @Test public void testUsagePrintedOnInvalidInput() throws UnsupportedEncodingException { @@ -140,8 +128,6 @@ static class MockOMStarter implements OMStarterInterface { private boolean initStatus = true; private boolean throwOnStart = false; private boolean throwOnInit = false; - private boolean startAndCancelPrepareCalled = false; - private boolean throwOnStartAndCancelPrepare = false; @Override public void start(OzoneConfiguration conf) throws IOException, @@ -167,14 +153,5 @@ public void bootstrap(OzoneConfiguration conf, boolean force) throws IOException, AuthenticationException { //TODO: Add test for bootstrap } - - @Override - public void startAndCancelPrepare(OzoneConfiguration conf) - throws IOException, AuthenticationException { - startAndCancelPrepareCalled = true; - if (throwOnStartAndCancelPrepare) { - throw new IOException("Simulated Exception"); - } - } } } diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/ratis/TestOzoneManagerStateMachine.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/ratis/TestOzoneManagerStateMachine.java index b9fb82786e13..adcf39045030 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/ratis/TestOzoneManagerStateMachine.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/ratis/TestOzoneManagerStateMachine.java @@ -22,7 +22,6 @@ import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -59,7 +58,6 @@ import org.apache.hadoop.ozone.om.OmMetadataManagerImpl; import org.apache.hadoop.ozone.om.OmSnapshotManager; import org.apache.hadoop.ozone.om.OzoneManager; -import org.apache.hadoop.ozone.om.OzoneManagerPrepareState; import org.apache.hadoop.ozone.om.exceptions.OMException; import org.apache.hadoop.ozone.om.ha.OMServiceManager; import org.apache.hadoop.ozone.om.helpers.OMRatisHelper; @@ -71,14 +69,10 @@ import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.KeyArgs; 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.PrepareRequest; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareRequestArgs; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.UserInfo; import org.apache.hadoop.ozone.protocolPB.RequestHandler; -import org.apache.hadoop.security.UserGroupInformation; import org.apache.ratis.proto.RaftProtos; import org.apache.ratis.protocol.ClientId; import org.apache.ratis.protocol.Message; @@ -87,7 +81,6 @@ import org.apache.ratis.protocol.RaftGroupMemberId; import org.apache.ratis.protocol.RaftPeer; import org.apache.ratis.protocol.RaftPeerId; -import org.apache.ratis.protocol.exceptions.StateMachineException; import org.apache.ratis.server.RaftServer; import org.apache.ratis.server.protocol.TermIndex; import org.apache.ratis.server.raftlog.LogProtoUtils; @@ -177,81 +170,6 @@ public void testStartTransactionGroupIdMismatchThrows() throws Exception { () -> sm.startTransaction(clientRequest)); } - // --- preAppendTransaction tests --- - - @Test - public void testPreAppendTransactionPrepareGate() throws Exception { - OzoneConfiguration conf = new OzoneConfiguration(); - OzoneManagerPrepareState ps = new OzoneManagerPrepareState(conf); - when(om.getPrepareState()).thenReturn(ps); - when(om.isAdmin(any(UserGroupInformation.class))).thenReturn(true); - - OMRequest writeRequest = sampleWriteRequest(); - OMRequest prepareRequest = OMRequest.newBuilder() - .setPrepareRequest(PrepareRequest.newBuilder() - .setArgs(PrepareRequestArgs.getDefaultInstance())) - .setCmdType(Type.Prepare) - .setClientId("123") - .setUserInfo(UserInfo.newBuilder() - .setUserName("user") - .setHostName("localhost") - .setRemoteAddress("127.0.0.1")) - .build(); - - // Write request passes before prepare - TransactionContext writeTrx = mockTrx(writeRequest, 1, 1); - assertSame(writeTrx, sm.preAppendTransaction(writeTrx)); - assertEquals(PrepareStatus.NOT_PREPARED, ps.getState().getStatus()); - - // Prepare enables gate - TransactionContext prepareTrx = mockTrx(prepareRequest, 1, 2); - assertSame(prepareTrx, sm.preAppendTransaction(prepareTrx)); - assertEquals(PrepareStatus.PREPARE_GATE_ENABLED, - ps.getState().getStatus()); - - // Write request now blocked - TransactionContext writeTrx2 = mockTrx(writeRequest, 1, 3); - StateMachineException ex = assertThrows(StateMachineException.class, - () -> sm.preAppendTransaction(writeTrx2)); - assertFalse(ex.leaderShouldStepDown()); - assertInstanceOf(OMException.class, ex.getCause()); - assertEquals(OMException.ResultCodes.NOT_SUPPORTED_OPERATION_WHEN_PREPARED, - ((OMException) ex.getCause()).getResult()); - - // Can prepare again - TransactionContext prepareTrx2 = mockTrx(prepareRequest, 1, 4); - assertSame(prepareTrx2, sm.preAppendTransaction(prepareTrx2)); - } - - @Test - public void testPreAppendTransactionAclDenied() { - OzoneConfiguration conf = new OzoneConfiguration(); - OzoneManagerPrepareState ps = new OzoneManagerPrepareState(conf); - when(om.getPrepareState()).thenReturn(ps); - when(om.isAdminAuthorizationEnabled()).thenReturn(true); - when(om.isAdmin(any(UserGroupInformation.class))).thenReturn(false); - - OMRequest prepareRequest = OMRequest.newBuilder() - .setPrepareRequest(PrepareRequest.newBuilder() - .setArgs(PrepareRequestArgs.getDefaultInstance())) - .setCmdType(Type.Prepare) - .setClientId("123") - .setUserInfo(UserInfo.newBuilder() - .setUserName("nonAdminUser") - .setHostName("localhost") - .setRemoteAddress("127.0.0.1")) - .build(); - - TransactionContext trx = mockTrx(prepareRequest, 1, 1); - - StateMachineException ex = assertThrows(StateMachineException.class, - () -> sm.preAppendTransaction(trx)); - assertFalse(ex.leaderShouldStepDown()); - assertInstanceOf(OMException.class, ex.getCause()); - assertEquals(OMException.ResultCodes.ACCESS_DENIED, - ((OMException) ex.getCause()).getResult()); - } - // --- applyTransaction tests --- @Test diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/OMKeyRequestTests.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/OMKeyRequestTests.java index 06352cea7092..ec23ea64b693 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/OMKeyRequestTests.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/OMKeyRequestTests.java @@ -71,7 +71,6 @@ import org.apache.hadoop.ozone.om.OmMetadataReader; import org.apache.hadoop.ozone.om.OmSnapshotManager; import org.apache.hadoop.ozone.om.OzoneManager; -import org.apache.hadoop.ozone.om.OzoneManagerPrepareState; import org.apache.hadoop.ozone.om.ResolvedBucket; import org.apache.hadoop.ozone.om.ScmClient; import org.apache.hadoop.ozone.om.helpers.BucketLayout; @@ -111,7 +110,6 @@ public class OMKeyRequestTests { protected OMMetrics omMetrics; protected OMMetadataManager omMetadataManager; protected AuditLogger auditLogger; - protected OzoneManagerPrepareState prepareState; protected ScmClient scmClient; protected OzoneBlockTokenSecretManager ozoneBlockTokenSecretManager; @@ -204,9 +202,6 @@ public void setup() throws Exception { when(omMetadataReader.isNativeAuthorizerEnabled()).thenReturn(true); when(rcOmMetadataReader.get()).thenReturn(omMetadataReader); - prepareState = new OzoneManagerPrepareState(ozoneConfiguration); - when(ozoneManager.getPrepareState()).thenReturn(prepareState); - Pipeline pipeline = Pipeline.newBuilder() .setState(Pipeline.PipelineState.OPEN) .setId(PipelineID.randomId()) diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/upgrade/TestOMCancelPrepareRequest.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/upgrade/TestOMCancelPrepareRequest.java deleted file mode 100644 index 91d802e5dd5d..000000000000 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/upgrade/TestOMCancelPrepareRequest.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hadoop.ozone.om.request.upgrade; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.UUID; -import org.apache.hadoop.ozone.om.OzoneManagerPrepareState; -import org.apache.hadoop.ozone.om.request.key.OMKeyRequestTests; -import org.apache.hadoop.ozone.om.request.key.OMOpenKeysDeleteRequest; -import org.apache.hadoop.ozone.om.response.OMClientResponse; -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.PrepareStatusResponse.PrepareStatus; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type; -import org.junit.jupiter.api.Test; - -/** - * Unit testing of cancel prepare request. Cancel prepare response does not - * perform an action, so it has no unit testing. - */ -public class TestOMCancelPrepareRequest extends OMKeyRequestTests { - private static final long LOG_INDEX = 1; - - @Test - public void testCancelPrepare() throws Exception { - assertNotPrepared(); - ozoneManager.getPrepareState().finishPrepare(LOG_INDEX); - assertPrepared(LOG_INDEX); - submitCancelPrepareRequest(); - assertNotPrepared(); - - // Another cancel prepare should be able to be submitted without error. - submitCancelPrepareRequest(); - assertNotPrepared(); - } - - private void assertPrepared(long logIndex) { - OzoneManagerPrepareState prepareState = ozoneManager.getPrepareState(); - OzoneManagerPrepareState.State state = prepareState.getState(); - - assertEquals(state.getStatus(), PrepareStatus.PREPARE_COMPLETED); - assertEquals(state.getIndex(), logIndex); - assertTrue(prepareState.getPrepareMarkerFile().exists()); - assertFalse(prepareState.requestAllowed(Type.CreateVolume)); - } - - private void assertNotPrepared() { - OzoneManagerPrepareState prepareState = ozoneManager.getPrepareState(); - OzoneManagerPrepareState.State state = prepareState.getState(); - - assertEquals(state.getStatus(), PrepareStatus.NOT_PREPARED); - assertEquals(state.getIndex(), - OzoneManagerPrepareState.NO_PREPARE_INDEX); - assertFalse(prepareState.getPrepareMarkerFile().exists()); - assertTrue(prepareState.requestAllowed(Type.CreateVolume)); - } - - private void submitCancelPrepareRequest() throws Exception { - OMRequest omRequest = - doPreExecute(createCancelPrepareRequest()); - - OMCancelPrepareRequest cancelPrepareRequest = - new OMCancelPrepareRequest(omRequest); - - OMClientResponse omClientResponse = - cancelPrepareRequest.validateAndUpdateCache(ozoneManager, LOG_INDEX); - - assertEquals(OzoneManagerProtocolProtos.Status.OK, - omClientResponse.getOMResponse().getStatus()); - } - - private OMRequest doPreExecute(OMRequest originalOmRequest) throws Exception { - OMOpenKeysDeleteRequest omOpenKeysDeleteRequest = - new OMOpenKeysDeleteRequest(originalOmRequest, getBucketLayout()); - - OMRequest modifiedOmRequest = - omOpenKeysDeleteRequest.preExecute(ozoneManager); - - // Will not be equal, as UserInfo will be set. - assertNotEquals(originalOmRequest, modifiedOmRequest); - - return modifiedOmRequest; - } - - private OMRequest createCancelPrepareRequest() { - OzoneManagerProtocolProtos.CancelPrepareRequest cancelPrepareRequest = - OzoneManagerProtocolProtos.CancelPrepareRequest.newBuilder().build(); - - OzoneManagerProtocolProtos.UserInfo userInfo = - OzoneManagerProtocolProtos.UserInfo.newBuilder() - .setUserName("user") - .setHostName("host") - .setRemoteAddress("0.0.0.0") - .build(); - - return OMRequest.newBuilder() - .setCancelPrepareRequest(cancelPrepareRequest) - .setCmdType(OzoneManagerProtocolProtos.Type.CancelPrepare) - .setClientId(UUID.randomUUID().toString()) - .setUserInfo(userInfo) - .build(); - } -} diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/upgrade/TestOzoneManagerPrepareState.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/upgrade/TestOzoneManagerPrepareState.java deleted file mode 100644 index 800549ad9182..000000000000 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/upgrade/TestOzoneManagerPrepareState.java +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.hadoop.ozone.om.upgrade; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Random; -import org.apache.hadoop.hdds.HddsConfigKeys; -import org.apache.hadoop.hdds.conf.OzoneConfiguration; -import org.apache.hadoop.ozone.om.OzoneManagerPrepareState; -import org.apache.hadoop.ozone.om.exceptions.OMException; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus; -import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type; -import org.apache.ozone.test.LambdaTestUtils; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -/** - * Class to test Ozone Manager prepare state maintenance. - */ -public class TestOzoneManagerPrepareState { - @TempDir - private Path folder; - - private static final int TEST_INDEX = 5; - private OzoneManagerPrepareState prepareState; - private static final Random RANDOM = new Random(); - - @BeforeEach - public void setup() throws Exception { - OzoneConfiguration conf = new OzoneConfiguration(); - conf.set(HddsConfigKeys.OZONE_METADATA_DIRS, - folder.toAbsolutePath().toString()); - - prepareState = new OzoneManagerPrepareState(conf); - } - - @AfterEach - public void tearDown() throws Exception { - // Clean up marker file from previous runs. - prepareState.cancelPrepare(); - } - - @Test - public void testStartPrepare() { - assertPrepareNotStarted(); - prepareState.enablePrepareGate(); - assertPrepareInProgress(); - } - - @Test - public void testFinishPrepare() throws Exception { - // Test finish as a follower OM, where it would be called without setting - // the prepare gate first. - prepareState.finishPrepare(TEST_INDEX); - assertPrepareCompleted(TEST_INDEX); - prepareState.cancelPrepare(); - - // Test finish as a leader OM, where the prepare gate would be put up - // before prepare is finished. - prepareState.enablePrepareGate(); - prepareState.finishPrepare(TEST_INDEX); - assertPrepareCompleted(TEST_INDEX); - - // A following prepare operation should not raise errors. - prepareState.finishPrepare(TEST_INDEX); - assertPrepareCompleted(TEST_INDEX); - } - - @Test - public void testCancelPrepare() throws Exception { - // Test cancel after start. - prepareState.enablePrepareGate(); - prepareState.cancelPrepare(); - assertPrepareNotStarted(); - - // Test cancel after finish. - prepareState.enablePrepareGate(); - prepareState.finishPrepare(TEST_INDEX); - prepareState.cancelPrepare(); - assertPrepareNotStarted(); - - // Test cancel after cancel. - prepareState.cancelPrepare(); - prepareState.cancelPrepare(); - assertPrepareNotStarted(); - - } - - @Test - public void testRequestAllowed() { - assertPrepareGateDown(); - prepareState.enablePrepareGate(); - assertPrepareGateUp(); - } - - @Test - public void testRestoreCorrectIndex() throws Exception { - writePrepareMarkerFile(TEST_INDEX); - prepareState.restorePrepareFromFile(TEST_INDEX); - assertPrepareCompleted(TEST_INDEX); - } - - @Test - public void testRestoreIncorrectIndex() throws Exception { - writePrepareMarkerFile(TEST_INDEX); - // Ratis is allowed to apply transactions after prepare, like commit and - // conf entries, but OM write requests are not allowed. - // Therefore prepare restoration should only fail if the marker file index - // is less than the OM's txn index. - assertPrepareFailedException(() -> - prepareState.restorePrepareFromFile(TEST_INDEX - 1)); - } - - @Test - public void testRestoreGarbageMarkerFile() throws Exception { - byte[] randomBytes = new byte[10]; - RANDOM.nextBytes(randomBytes); - writePrepareMarkerFile(randomBytes); - - assertPrepareFailedException(() -> - prepareState.restorePrepareFromFile(TEST_INDEX)); - } - - @Test - public void testRestoreEmptyMarkerFile() throws Exception { - writePrepareMarkerFile(new byte[]{}); - - assertPrepareFailedException(() -> - prepareState.restorePrepareFromFile(TEST_INDEX)); - } - - @Test - public void testRestoreNoMarkerFile() throws Exception { - assertPrepareFailedException(() -> - prepareState.restorePrepareFromFile(TEST_INDEX)); - assertPrepareNotStarted(); - } - - @Test - public void testRestoreWithGateOnly() throws Exception { - prepareState.enablePrepareGate(); - - // If prepare is started but never finished, (gate is up but no marker - // file written), then restoring the prepare state from the file should - // fail, but leave the in memory state the same. - assertPrepareFailedException(() -> - prepareState.restorePrepareFromFile(TEST_INDEX)); - assertPrepareInProgress(); - } - - @Test - public void testMultipleRestores() throws Exception { - assertPrepareFailedException(() -> - prepareState.restorePrepareFromFile(TEST_INDEX)); - assertPrepareNotStarted(); - - assertPrepareFailedException(() -> - prepareState.restorePrepareFromFile(TEST_INDEX)); - assertPrepareNotStarted(); - - prepareState.enablePrepareGate(); - prepareState.finishPrepare(TEST_INDEX); - - prepareState.restorePrepareFromFile(TEST_INDEX); - assertPrepareCompleted(TEST_INDEX); - - prepareState.restorePrepareFromFile(TEST_INDEX); - assertPrepareCompleted(TEST_INDEX); - } - - private void writePrepareMarkerFile(long index) throws IOException { - writePrepareMarkerFile(Long.toString(index).getBytes( - Charset.defaultCharset())); - } - - private void writePrepareMarkerFile(byte[] bytes) throws IOException { - File markerFile = prepareState.getPrepareMarkerFile(); - boolean mkdirs = markerFile.getParentFile().mkdirs(); - if (!mkdirs) { - throw new IOException("Unable to create marker file directory."); - } - try (OutputStream stream = - Files.newOutputStream(markerFile.toPath())) { - stream.write(bytes); - } - } - - private long readPrepareMarkerFile() throws Exception { - long index = 0; - File prepareMarkerFile = prepareState.getPrepareMarkerFile(); - byte[] data = new byte[(int) prepareMarkerFile.length()]; - - try (InputStream stream = Files.newInputStream(prepareMarkerFile.toPath())) { - stream.read(data); - index = Long.parseLong(new String(data, Charset.defaultCharset())); - } - - return index; - } - - private void assertPrepareNotStarted() { - OzoneManagerPrepareState.State state = prepareState.getState(); - assertEquals(PrepareStatus.NOT_PREPARED, state.getStatus()); - assertEquals(OzoneManagerPrepareState.NO_PREPARE_INDEX, - state.getIndex()); - assertFalse(prepareState.getPrepareMarkerFile().exists()); - - assertPrepareGateDown(); - } - - private void assertPrepareInProgress() { - OzoneManagerPrepareState.State state = prepareState.getState(); - assertEquals(PrepareStatus.PREPARE_GATE_ENABLED, - state.getStatus()); - assertEquals(OzoneManagerPrepareState.NO_PREPARE_INDEX, - state.getIndex()); - assertFalse(prepareState.getPrepareMarkerFile().exists()); - - assertPrepareGateUp(); - } - - private void assertPrepareCompleted(long index) throws Exception { - OzoneManagerPrepareState.State state = prepareState.getState(); - assertEquals(PrepareStatus.PREPARE_COMPLETED, - state.getStatus()); - assertEquals(index, state.getIndex()); - assertEquals(index, readPrepareMarkerFile()); - - assertPrepareGateUp(); - } - - private void assertPrepareGateUp() { - // Once preparation has begun, only prepare and cancel prepare should be - // allowed. - for (Type cmdType: Type.values()) { - if (cmdType == Type.Prepare || cmdType == Type.CancelPrepare) { - assertTrue(prepareState.requestAllowed(cmdType)); - } else { - assertFalse(prepareState.requestAllowed(cmdType)); - } - } - } - - private void assertPrepareGateDown() { - for (Type cmdType: Type.values()) { - assertTrue(prepareState.requestAllowed(cmdType)); - } - } - - private void assertPrepareFailedException(LambdaTestUtils.VoidCallable call) - throws Exception { - try { - call.call(); - } catch (OMException ex) { - if (ex.getResult() != OMException.ResultCodes.PREPARE_FAILED) { - throw ex; - } - } - } -} diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java index 6e99f949c154..3b6cfe9b679a 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/protocolPB/TestOzoneManagerRequestHandler.java @@ -49,6 +49,7 @@ import org.apache.hadoop.ozone.om.upgrade.OMVersionManager; import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos; import org.apache.hadoop.ozone.snapshot.SnapshotDiffResponse; +import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.util.Time; import org.apache.ratis.server.protocol.TermIndex; import org.junit.jupiter.api.Assertions; @@ -68,6 +69,8 @@ private OzoneManagerRequestHandler getRequestHandler(int limitListKeySize) { config.setMaxListSize(limitListKeySize); OzoneManager ozoneManager = Mockito.mock(OzoneManager.class); Mockito.when(ozoneManager.getConfig()).thenReturn(config); + Mockito.when(ozoneManager.isAdmin(Mockito.any(UserGroupInformation.class))) + .thenReturn(true); return new OzoneManagerRequestHandler(ozoneManager); } @@ -255,6 +258,75 @@ public void testListKeysLightEncryptionFromOmKeyInfo() throws IOException { Assertions.assertFalse(basicKeyInfoList.get(1).getIsEncrypted(), "normal-key should have isEncrypted=false"); } + /** + * Test to verify prepare-related requests return success as no-ops, since the Ozone Manager no longer supports this + * operation. + */ + @Test + public void testPrepareRequestsAreNoOps() { + OzoneManagerRequestHandler requestHandler = getRequestHandler(10); + + // Prepare command should be a no-op. + OzoneManagerProtocolProtos.OMRequest prepareRequest = + OzoneManagerProtocolProtos.OMRequest.newBuilder() + .setCmdType(OzoneManagerProtocolProtos.Type.Prepare) + .setClientId("test-client") + .setPrepareRequest(OzoneManagerProtocolProtos.PrepareRequest.newBuilder() + .setArgs(OzoneManagerProtocolProtos.PrepareRequestArgs.newBuilder().build()) + .build()) + .build(); + + OzoneManagerProtocolProtos.OMResponse prepareResponse = + requestHandler.handleReadRequest(prepareRequest); + + Assertions.assertTrue(prepareResponse.getSuccess(), "Prepare should return success"); + Assertions.assertTrue(prepareResponse.hasPrepareResponse(), "Prepare response should be present"); + Assertions.assertEquals(0, prepareResponse.getPrepareResponse().getTxnID(), + "Prepare should return empty txnID of 0"); + Assertions.assertEquals("Prepare is no longer required in this version", + prepareResponse.getMessage(), "Prepare should return deprecation message"); + + // PrepareStatus should always return PREPARE_COMPLETED. + OzoneManagerProtocolProtos.OMRequest prepareStatusRequest = + OzoneManagerProtocolProtos.OMRequest.newBuilder() + .setCmdType(OzoneManagerProtocolProtos.Type.PrepareStatus) + .setClientId("test-client") + .setPrepareStatusRequest(OzoneManagerProtocolProtos.PrepareStatusRequest.newBuilder() + .setTxnID(0) + .build()) + .build(); + + OzoneManagerProtocolProtos.OMResponse prepareStatusResponse = + requestHandler.handleReadRequest(prepareStatusRequest); + + Assertions.assertTrue(prepareStatusResponse.getSuccess(), "PrepareStatus should return success"); + Assertions.assertTrue(prepareStatusResponse.hasPrepareStatusResponse(), + "PrepareStatus response should be present"); + Assertions.assertEquals( + OzoneManagerProtocolProtos.PrepareStatusResponse.PrepareStatus.PREPARE_COMPLETED, + prepareStatusResponse.getPrepareStatusResponse().getStatus(), + "PrepareStatus should always return PREPARE_COMPLETED for backward compatibility"); + Assertions.assertEquals(0, prepareStatusResponse.getPrepareStatusResponse().getCurrentTxnIndex(), + "PrepareStatus should return currentTxnIndex of 0"); + + // CancelPrepare command should be a no-op. + OzoneManagerProtocolProtos.OMRequest cancelPrepareRequest = + OzoneManagerProtocolProtos.OMRequest.newBuilder() + .setCmdType(OzoneManagerProtocolProtos.Type.CancelPrepare) + .setClientId("test-client") + .setCancelPrepareRequest(OzoneManagerProtocolProtos.CancelPrepareRequest.newBuilder().build()) + .build(); + + OzoneManagerProtocolProtos.OMResponse cancelPrepareResponse = + requestHandler.handleReadRequest(cancelPrepareRequest); + + Assertions.assertTrue(cancelPrepareResponse.getSuccess(), "CancelPrepare should return success"); + Assertions.assertTrue(cancelPrepareResponse.hasCancelPrepareResponse(), + "CancelPrepare response should be present"); + Assertions.assertEquals("Cancel Prepare is no longer required in this version", + cancelPrepareResponse.getMessage(), "CancelPrepare should return deprecation message"); + } + /** * Verify that when handleWriteRequestImpl encounters an exception during * validateAndUpdateCache, the audit logger is called with the correct