This is an automated email from the ASF dual-hosted git repository. tkalkirill pushed a commit to branch ignite-26665 in repository https://gitbox.apache.org/repos/asf/ignite-3.git
commit 5553753fcb3fdd65b8c483c88d89636977e5a592 Author: Kirill Tkalenko <[email protected]> AuthorDate: Thu Oct 9 15:05:36 2025 +0300 IGNITE-26665 wip --- modules/cluster-management/build.gradle | 1 + .../commands/CmgCommandsCompatibilityTest.java | 169 +++++++++++++++++++++ 2 files changed, 170 insertions(+) diff --git a/modules/cluster-management/build.gradle b/modules/cluster-management/build.gradle index 267f9452c43..af06255f148 100644 --- a/modules/cluster-management/build.gradle +++ b/modules/cluster-management/build.gradle @@ -51,6 +51,7 @@ dependencies { testImplementation project(':ignite-configuration') testImplementation project(':ignite-network') testImplementation project(':ignite-system-disaster-recovery') + testImplementation project(':ignite-raft') testImplementation testFixtures(project(':ignite-core')) testImplementation testFixtures(project(':ignite-configuration')) testImplementation testFixtures(project(':ignite-network')) diff --git a/modules/cluster-management/src/test/java/org/apache/ignite/internal/cluster/management/raft/commands/CmgCommandsCompatibilityTest.java b/modules/cluster-management/src/test/java/org/apache/ignite/internal/cluster/management/raft/commands/CmgCommandsCompatibilityTest.java new file mode 100644 index 00000000000..291d96b559e --- /dev/null +++ b/modules/cluster-management/src/test/java/org/apache/ignite/internal/cluster/management/raft/commands/CmgCommandsCompatibilityTest.java @@ -0,0 +1,169 @@ +/* + * 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.ignite.internal.cluster.management.raft.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +import java.util.Base64; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import org.apache.ignite.internal.cluster.management.ClusterState; +import org.apache.ignite.internal.cluster.management.ClusterTag; +import org.apache.ignite.internal.cluster.management.network.messages.CmgMessagesFactory; +import org.apache.ignite.internal.cluster.management.network.messages.CmgMessagesSerializationRegistryInitializer; +import org.apache.ignite.internal.network.MessageSerializationRegistryImpl; +import org.apache.ignite.internal.network.serialization.MessageSerializationRegistry; +import org.apache.ignite.internal.raft.Command; +import org.apache.ignite.internal.raft.Marshaller; +import org.apache.ignite.internal.raft.util.ThreadLocalOptimizedMarshaller; +import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +/** + * Compatibility testing for serialization/deserialization of CMG raft commands. It is verified that deserialization of commands that were + * created on earlier versions of the product will be error-free. + */ +public class CmgCommandsCompatibilityTest extends BaseIgniteAbstractTest { + private static final MessageSerializationRegistry REGISTRY = new MessageSerializationRegistryImpl(); + + private static final Marshaller MARSHALLER = new ThreadLocalOptimizedMarshaller(REGISTRY); + + private static final CmgMessagesFactory FACTORY = new CmgMessagesFactory(); + + @BeforeAll + static void beforeAll() { + new CmgMessagesSerializationRegistryInitializer().registerFactories(REGISTRY); + } + + @Test + void testChangeMetaStorageInfoCommand() { + ChangeMetaStorageInfoCommand command = decodeCommand("CDADCG1zTm9kZTIIbXNOb2RlMQEr"); + + assertEquals(Set.of("msNode1", "msNode2"), command.metaStorageNodes()); + assertEquals(42, command.metastorageRepairingConfigIndex()); + } + + @Test + void testInitCmgStateCommand() { + InitCmgStateCommand command = decodeCommand( + "CCkIPgg/ACoAAAAAAAAARQAAAAAAAAANY2x1c3Rlck5hbWUxAwljbWdOb2RlMgljbWdOb2RlMQIAKgAAAAAAAABFAAAAAAAAAAxpbml0Q29uZmlnMQMIbXNOb" + + "2RlMghtc05vZGUxCXZlcnNpb24xCD0GaG9zdDEAKgAAAAAAAABFAAAAAAAAAAZuYW1lMekHAglwcm9maWxlMQIIc3lzS2V5MQhzeXNWYWwxAgl1" + + "c2VyS2V5MQl1c2VyVmFsMQ==" + ); + + assertEquals(createClusterNodeMessage(), command.node()); + assertEquals(createClusterState(), command.clusterState()); + } + + @Test + void testJoinReadyCommand() { + JoinReadyCommand command = decodeCommand( + "CC0IPQZob3N0MQAqAAAAAAAAAEUAAAAAAAAABm5hbWUx6QcCCXByb2ZpbGUxAghzeXNLZXkxCHN5c1ZhbDECCXVzZXJLZXkxCXVzZXJWYWwx" + ); + + assertEquals(createClusterNodeMessage(), command.node()); + } + + @Test + void testJoinRequestCommand() { + JoinRequestCommand command = decodeCommand( + "CCwIPwAqAAAAAAAAAEUAAAAAAAAADWNsdXN0ZXJOYW1lMQg9Bmhvc3QxACoAAAAAAAAARQAAAAAAAAAGbmFtZTHpBwIJcHJvZmlsZTECCHN5c0tleTEIc3lzV" + + "mFsMQIJdXNlcktleTEJdXNlclZhbDEJdmVyc2lvbjE=" + ); + + assertEquals(createClusterNodeMessage(), command.node()); + assertEquals("version1", command.version()); + assertEquals(ClusterTag.clusterTag(FACTORY, "clusterName1", uuid()), command.clusterTag()); + } + + @Test + void testNodesLeaveCommand() { + NodesLeaveCommand command = decodeCommand( + "CC4CCD0GaG9zdDEAKgAAAAAAAABFAAAAAAAAAAZuYW1lMekHAglwcm9maWxlMQIIc3lzS2V5MQhzeXNWYWwxAgl1c2VyS2V5MQl1c2VyVmFsMQ==" + ); + + assertEquals(Set.of(createClusterNodeMessage()), command.nodes()); + } + + @Test + void testReadLogicalTopologyCommand() { + Command command = decodeCommand("CCs="); + + assertInstanceOf(ReadLogicalTopologyCommand.class, command); + } + + @Test + void testReadMetaStorageInfoCommand() { + Command command = decodeCommand("CEM="); + + assertInstanceOf(ReadMetaStorageInfoCommand.class, command); + } + + @Test + void testReadStateCommand() { + Command command = decodeCommand("CCo="); + + assertInstanceOf(ReadStateCommand.class, command); + } + + @Test + void testReadValidatedNodesCommand() { + Command command = decodeCommand("CC8="); + + assertInstanceOf(ReadValidatedNodesCommand.class, command); + } + + private static UUID uuid() { + return new UUID(42, 69); + } + + private static ClusterNodeMessage createClusterNodeMessage() { + return FACTORY.clusterNodeMessage() + .id(uuid()) + .name("name1") + .host("host1") + .port(1000) + .userAttributes(Map.of("userKey1", "userVal1")) + .systemAttributes(Map.of("sysKey1", "sysVal1")) + .storageProfiles(List.of("profile1")) + .build(); + } + + private static ClusterState createClusterState() { + return FACTORY.clusterState() + .cmgNodes(Set.of("cmgNode1", "cmgNode2")) + .metaStorageNodes(Set.of("msNode1", "msNode2")) + .version("version1") + .clusterTag(ClusterTag.clusterTag(FACTORY, "clusterName1", uuid())) + .initialClusterConfiguration("initConfig1") + .formerClusterIds(List.of(uuid())) + .build(); + } + + private static <T extends Command> T deserializeCommand(byte[] bytes) { + return MARSHALLER.unmarshall(bytes); + } + + private static <T extends Command> T decodeCommand(String base64) { + return deserializeCommand(Base64.getDecoder().decode(base64)); + } +}
