This is an automated email from the ASF dual-hosted git repository. weichiu pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push: new cc0a7bae7f2 HDDS-12000. Add unit test for RatisContainerSafeModeRule and ECContainerSafeModeRule (#8801) cc0a7bae7f2 is described below commit cc0a7bae7f2c0fa03633687cdf651dcf3b6202c6 Author: Anastasia Kostryukova <63952085+kosta...@users.noreply.github.com> AuthorDate: Tue Jul 22 01:48:57 2025 +0300 HDDS-12000. Add unit test for RatisContainerSafeModeRule and ECContainerSafeModeRule (#8801) Generated-by: GitHub Copilot --- .../scm/safemode/TestECContainerSafeModeRule.java | 219 +++++++++++++++++++++ .../safemode/TestRatisContainerSafeModeRule.java | 207 +++++++++++++++++++ 2 files changed, 426 insertions(+) diff --git a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestECContainerSafeModeRule.java b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestECContainerSafeModeRule.java new file mode 100644 index 00000000000..aceff644ec3 --- /dev/null +++ b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestECContainerSafeModeRule.java @@ -0,0 +1,219 @@ +/* + * 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.hdds.scm.safemode; + +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 static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import org.apache.hadoop.hdds.conf.ConfigurationSource; +import org.apache.hadoop.hdds.protocol.DatanodeDetails; +import org.apache.hadoop.hdds.protocol.proto.HddsProtos.LifeCycleState; +import org.apache.hadoop.hdds.protocol.proto.HddsProtos.ReplicationType; +import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.ContainerReplicaProto; +import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.ContainerReportsProto; +import org.apache.hadoop.hdds.scm.container.ContainerID; +import org.apache.hadoop.hdds.scm.container.ContainerInfo; +import org.apache.hadoop.hdds.scm.container.ContainerManager; +import org.apache.hadoop.hdds.scm.server.SCMDatanodeProtocolServer.NodeRegistrationContainerReport; +import org.apache.hadoop.hdds.server.events.EventQueue; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +/** + * This class tests ECContainerSafeModeRule. + */ +public class TestECContainerSafeModeRule { + private ContainerManager containerManager; + private ConfigurationSource conf; + private EventQueue eventQueue; + private SCMSafeModeManager safeModeManager; + private SafeModeMetrics metrics; + + private ECContainerSafeModeRule rule; + + @BeforeEach + public void setup() { + containerManager = mock(ContainerManager.class); + conf = mock(ConfigurationSource.class); + eventQueue = mock(EventQueue.class); + safeModeManager = mock(SCMSafeModeManager.class); + metrics = mock(SafeModeMetrics.class); + + when(safeModeManager.getSafeModeMetrics()).thenReturn(metrics); + + rule = new ECContainerSafeModeRule(eventQueue, conf, containerManager, safeModeManager); + rule.setValidateBasedOnReportProcessing(false); + } + + @Test + public void testRefreshInitializeECContainers() { + List<ContainerInfo> containers = Arrays.asList( + mockECContainer(LifeCycleState.CLOSED, 1L), + mockECContainer(LifeCycleState.OPEN, 2L) + ); + + when(containerManager.getContainers(ReplicationType.EC)).thenReturn(containers); + + rule.refresh(false); + + assertEquals(0.0, rule.getCurrentContainerThreshold()); + } + + @ParameterizedTest + @EnumSource(value = LifeCycleState.class, + names = {"OPEN", "CLOSING", "QUASI_CLOSED", "CLOSED", "DELETING", "DELETED", "RECOVERING"}) + public void testValidateReturnsTrueAndFalse(LifeCycleState state) { + ContainerInfo container = mockECContainer(state, 1L); + + when(containerManager.getContainers(ReplicationType.EC)).thenReturn(Collections.singletonList(container)); + + boolean expected = state != LifeCycleState.QUASI_CLOSED && state != LifeCycleState.CLOSED; + assertEquals(expected, rule.validate()); + } + + @Test + public void testProcessECContainer() { + long containerId = 123L; + ContainerInfo container = mockECContainer(LifeCycleState.CLOSED, containerId); + + when(containerManager.getContainers(ReplicationType.EC)).thenReturn(Collections.singletonList(container)); + rule.refresh(true); + + assertEquals(0.0, rule.getCurrentContainerThreshold()); + + ContainerReplicaProto replica = mock(ContainerReplicaProto.class); + List<ContainerReplicaProto> replicas = new ArrayList<>(); + replicas.add(replica); + ContainerReportsProto containerReport = mock(ContainerReportsProto.class); + NodeRegistrationContainerReport report = mock(NodeRegistrationContainerReport.class); + DatanodeDetails datanodeDetails = mock(DatanodeDetails.class); + + when(report.getDatanodeDetails()).thenReturn(datanodeDetails); + when(datanodeDetails.getUuid()).thenReturn(UUID.randomUUID()); + when(replica.getContainerID()).thenReturn(containerId); + when(containerReport.getReportsList()).thenReturn(replicas); + when(report.getReport()).thenReturn(containerReport); + + rule.process(report); + + assertEquals(1.0, rule.getCurrentContainerThreshold()); + } + + @Test + public void testAllContainersClosed() { + List<ContainerInfo> closedContainers = Arrays.asList( + mockECContainer(LifeCycleState.CLOSED, 11L), + mockECContainer(LifeCycleState.CLOSED, 32L) + ); + + when(containerManager.getContainers(ReplicationType.EC)).thenReturn(closedContainers); + + rule.refresh(false); + + assertEquals(0.0, rule.getCurrentContainerThreshold(), "Threshold should be 0.0 when all containers are closed"); + assertFalse(rule.validate(), "Validate should return false when all containers are closed"); + } + + @Test + public void testAllContainersOpen() { + List<ContainerInfo> openContainers = Arrays.asList( + mockECContainer(LifeCycleState.OPEN, 11L), + mockECContainer(LifeCycleState.OPEN, 32L) + ); + + when(containerManager.getContainers(ReplicationType.EC)).thenReturn(openContainers); + + rule.refresh(false); + + assertEquals(1.0, rule.getCurrentContainerThreshold(), "Threshold should be 1.0 when all containers are open"); + assertTrue(rule.validate(), "Validate should return true when all containers are open"); + } + + @Test + public void testDuplicateContainerIdsInReports() { + long containerId = 42L; + ContainerInfo container = mockECContainer(LifeCycleState.OPEN, containerId); + + when(containerManager.getContainers(ReplicationType.EC)).thenReturn(Collections.singletonList(container)); + + rule.refresh(false); + + ContainerReplicaProto replica = mock(ContainerReplicaProto.class); + ContainerReportsProto containerReport = mock(ContainerReportsProto.class); + NodeRegistrationContainerReport report = mock(NodeRegistrationContainerReport.class); + DatanodeDetails datanodeDetails = mock(DatanodeDetails.class); + + when(replica.getContainerID()).thenReturn(containerId); + when(containerReport.getReportsList()).thenReturn(Collections.singletonList(replica)); + when(report.getReport()).thenReturn(containerReport); + when(report.getDatanodeDetails()).thenReturn(datanodeDetails); + when(datanodeDetails.getUuid()).thenReturn(UUID.randomUUID()); + + rule.process(report); + rule.process(report); + + assertEquals(1.0, rule.getCurrentContainerThreshold(), "Duplicated containers should be counted only once"); + } + + @Test + public void testValidateBasedOnReportProcessingTrue() throws Exception { + rule.setValidateBasedOnReportProcessing(true); + long containerId = 1L; + ContainerInfo container = mockECContainer(LifeCycleState.OPEN, containerId); + + when(containerManager.getContainers(ReplicationType.EC)).thenReturn(Collections.singletonList(container)); + + rule.refresh(false); + + ContainerReplicaProto replica = mock(ContainerReplicaProto.class); + ContainerReportsProto reportsProto = mock(ContainerReportsProto.class); + NodeRegistrationContainerReport report = mock(NodeRegistrationContainerReport.class); + DatanodeDetails datanodeDetails = mock(DatanodeDetails.class); + + when(replica.getContainerID()).thenReturn(containerId); + when(reportsProto.getReportsList()).thenReturn(Collections.singletonList(replica)); + when(report.getReport()).thenReturn(reportsProto); + when(report.getDatanodeDetails()).thenReturn(datanodeDetails); + when(datanodeDetails.getUuid()).thenReturn(UUID.randomUUID()); + + + rule.process(report); + + assertTrue(rule.validate(), "Should validate based on reported containers"); + } + + private static ContainerInfo mockECContainer(LifeCycleState state, long containerID) { + ContainerInfo container = mock(ContainerInfo.class); + when(container.getReplicationType()).thenReturn(ReplicationType.EC); + when(container.getState()).thenReturn(state); + when(container.getContainerID()).thenReturn(containerID); + when(container.containerID()).thenReturn(ContainerID.valueOf(containerID)); + when(container.getNumberOfKeys()).thenReturn(1L); + return container; + } +} diff --git a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestRatisContainerSafeModeRule.java b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestRatisContainerSafeModeRule.java new file mode 100644 index 00000000000..56a1e1e8323 --- /dev/null +++ b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/safemode/TestRatisContainerSafeModeRule.java @@ -0,0 +1,207 @@ +/* + * 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.hdds.scm.safemode; + +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 static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.apache.hadoop.hdds.conf.ConfigurationSource; +import org.apache.hadoop.hdds.protocol.proto.HddsProtos.LifeCycleState; +import org.apache.hadoop.hdds.protocol.proto.HddsProtos.ReplicationType; +import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.ContainerReplicaProto; +import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.ContainerReportsProto; +import org.apache.hadoop.hdds.scm.container.ContainerInfo; +import org.apache.hadoop.hdds.scm.container.ContainerManager; +import org.apache.hadoop.hdds.scm.server.SCMDatanodeProtocolServer.NodeRegistrationContainerReport; +import org.apache.hadoop.hdds.server.events.EventQueue; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +/** + * This class tests RatisContainerSafeModeRule. + */ +public class TestRatisContainerSafeModeRule { + + private ContainerManager containerManager; + private ConfigurationSource conf; + private EventQueue eventQueue; + private SCMSafeModeManager safeModeManager; + private SafeModeMetrics metrics; + + private RatisContainerSafeModeRule rule; + + @BeforeEach + public void setup() { + containerManager = mock(ContainerManager.class); + conf = mock(ConfigurationSource.class); + eventQueue = mock(EventQueue.class); + safeModeManager = mock(SCMSafeModeManager.class); + metrics = mock(SafeModeMetrics.class); + + when(safeModeManager.getSafeModeMetrics()).thenReturn(metrics); + + rule = new RatisContainerSafeModeRule(eventQueue, conf, containerManager, safeModeManager); + rule.setValidateBasedOnReportProcessing(false); + } + + @Test + public void testRefreshInitializeRatisContainers() { + List<ContainerInfo> containers = Arrays.asList( + mockRatisContainer(LifeCycleState.CLOSED, 1L), + mockRatisContainer(LifeCycleState.OPEN, 2L) + ); + + when(containerManager.getContainers(ReplicationType.RATIS)).thenReturn(containers); + + rule.refresh(false); + + assertEquals(0.0, rule.getCurrentContainerThreshold()); + } + + @ParameterizedTest + @EnumSource(value = LifeCycleState.class, + names = {"OPEN", "CLOSING", "QUASI_CLOSED", "CLOSED", "DELETING", "DELETED", "RECOVERING"}) + public void testValidateReturnsTrueAndFalse(LifeCycleState state) { + ContainerInfo container = mockRatisContainer(state, 1L); + + when(containerManager.getContainers(ReplicationType.RATIS)).thenReturn(Collections.singletonList(container)); + + boolean expected = state != LifeCycleState.QUASI_CLOSED && state != LifeCycleState.CLOSED; + assertEquals(expected, rule.validate()); + } + + @Test + public void testProcessRatisContainer() { + long containerId = 123L; + ContainerInfo container = mockRatisContainer(LifeCycleState.CLOSED, containerId); + + when(containerManager.getContainers(ReplicationType.RATIS)).thenReturn(Collections.singletonList(container)); + rule.refresh(true); + + assertEquals(0.0, rule.getCurrentContainerThreshold()); + + ContainerReplicaProto replica = mock(ContainerReplicaProto.class); + List<ContainerReplicaProto> replicas = new ArrayList<>(); + replicas.add(replica); + ContainerReportsProto containerReport = mock(ContainerReportsProto.class); + NodeRegistrationContainerReport report = mock(NodeRegistrationContainerReport.class); + + when(replica.getContainerID()).thenReturn(containerId); + when(containerReport.getReportsList()).thenReturn(replicas); + when(report.getReport()).thenReturn(containerReport); + + rule.process(report); + + assertEquals(1.0, rule.getCurrentContainerThreshold()); + } + + @Test + public void testAllContainersClosed() { + List<ContainerInfo> closedContainers = Arrays.asList( + mockRatisContainer(LifeCycleState.CLOSED, 11L), + mockRatisContainer(LifeCycleState.CLOSED, 32L) + ); + + when(containerManager.getContainers(ReplicationType.RATIS)).thenReturn(closedContainers); + + rule.refresh(false); + + assertEquals(0.0, rule.getCurrentContainerThreshold(), "Threshold should be 0.0 when all containers are closed"); + assertFalse(rule.validate(), "Validate should return false when all containers are closed"); + } + + @Test + public void testAllContainersOpen() { + List<ContainerInfo> openContainers = Arrays.asList( + mockRatisContainer(LifeCycleState.OPEN, 11L), + mockRatisContainer(LifeCycleState.OPEN, 32L) + ); + + when(containerManager.getContainers(ReplicationType.RATIS)).thenReturn(openContainers); + + rule.refresh(false); + + assertEquals(1.0, rule.getCurrentContainerThreshold(), "Threshold should be 1.0 when all containers are open"); + assertTrue(rule.validate(), "Validate should return true when all containers are open"); + } + + @Test + public void testDuplicateContainerIdsInReports() { + long containerId = 42L; + ContainerInfo container = mockRatisContainer(LifeCycleState.OPEN, containerId); + + when(containerManager.getContainers(ReplicationType.RATIS)).thenReturn(Collections.singletonList(container)); + + rule.refresh(false); + + ContainerReplicaProto replica = mock(ContainerReplicaProto.class); + ContainerReportsProto containerReport = mock(ContainerReportsProto.class); + NodeRegistrationContainerReport report = mock(NodeRegistrationContainerReport.class); + + when(replica.getContainerID()).thenReturn(containerId); + when(containerReport.getReportsList()).thenReturn(Collections.singletonList(replica)); + when(report.getReport()).thenReturn(containerReport); + + rule.process(report); + rule.process(report); + + assertEquals(1.0, rule.getCurrentContainerThreshold(), "Duplicated containers should be counted only once"); + } + + @Test + public void testValidateBasedOnReportProcessingTrue() throws Exception { + rule.setValidateBasedOnReportProcessing(true); + long containerId = 1L; + ContainerInfo container = mockRatisContainer(LifeCycleState.OPEN, containerId); + + when(containerManager.getContainers(ReplicationType.RATIS)).thenReturn(Collections.singletonList(container)); + + rule.refresh(false); + + ContainerReplicaProto replica = mock(ContainerReplicaProto.class); + ContainerReportsProto reportsProto = mock(ContainerReportsProto.class); + NodeRegistrationContainerReport report = mock(NodeRegistrationContainerReport.class); + + when(replica.getContainerID()).thenReturn(containerId); + when(reportsProto.getReportsList()).thenReturn(Collections.singletonList(replica)); + when(report.getReport()).thenReturn(reportsProto); + + rule.process(report); + + assertTrue(rule.validate(), "Should validate based on reported containers"); + } + + private static ContainerInfo mockRatisContainer(LifeCycleState state, long containerID) { + ContainerInfo container = mock(ContainerInfo.class); + when(container.getReplicationType()).thenReturn(ReplicationType.RATIS); + when(container.getState()).thenReturn(state); + when(container.getContainerID()).thenReturn(containerID); + when(container.getNumberOfKeys()).thenReturn(1L); + return container; + } + +} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@ozone.apache.org For additional commands, e-mail: commits-h...@ozone.apache.org