This is an automated email from the ASF dual-hosted git repository.

rpuch pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 6935ca8cc4 IGNITE-22897 Add CLI for initiating MG repair (#4611)
6935ca8cc4 is described below

commit 6935ca8cc4751595be067dcae91789835198fd7f
Author: Phillippko <[email protected]>
AuthorDate: Sat Oct 26 01:26:57 2024 +0900

    IGNITE-22897 Add CLI for initiating MG repair (#4611)
---
 .../call/recovery/cluster/ResetClusterCall.java    |   1 +
 .../recovery/cluster/ResetClusterCallInput.java    |  33 ++++--
 .../ignite/internal/cli/commands/Options.java      |   5 +
 .../recovery/cluster/reset/ResetClusterMixin.java  |  25 +++-
 .../cluster/reset/ResetClusterCommandTest.java     | 130 +++++++++++++++++++++
 .../disaster/system/ItCmgDisasterRecoveryTest.java |   4 +-
 .../ItMetastorageGroupDisasterRecoveryTest.java    |  26 ++---
 .../system/SystemDisasterRecoveryClient.java       |  34 ++++--
 8 files changed, 220 insertions(+), 38 deletions(-)

diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/cluster/ResetClusterCall.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/cluster/ResetClusterCall.java
index c6566044f4..c8d7da5e90 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/cluster/ResetClusterCall.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/cluster/ResetClusterCall.java
@@ -43,6 +43,7 @@ public class ResetClusterCall implements 
Call<ResetClusterCallInput, String> {
         ResetClusterRequest command = new ResetClusterRequest();
 
         command.setCmgNodeNames(input.cmgNodeNames());
+        
command.setMetastorageReplicationFactor(input.metastorageReplicationFactor());
 
         try {
             client.resetCluster(command);
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/cluster/ResetClusterCallInput.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/cluster/ResetClusterCallInput.java
index b5767c1690..8f4d562350 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/cluster/ResetClusterCallInput.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/cluster/ResetClusterCallInput.java
@@ -18,37 +18,46 @@
 package org.apache.ignite.internal.cli.call.recovery.cluster;
 
 import java.util.List;
-import java.util.Objects;
 import 
org.apache.ignite.internal.cli.commands.recovery.cluster.reset.ResetClusterMixin;
 import org.apache.ignite.internal.cli.core.call.CallInput;
+import org.jetbrains.annotations.Nullable;
 
 /** Input for the {@link ResetClusterCall} call. */
 public class ResetClusterCallInput implements CallInput {
     private final String clusterUrl;
 
+    @Nullable
     private final List<String> cmgNodeNames;
 
+    @Nullable
+    private final Integer metastorageReplicationFactor;
+
     /** Cluster url. */
     public String clusterUrl() {
         return clusterUrl;
     }
 
     /** Returns names of the proposed CMG nodes. */
-    public List<String> cmgNodeNames() {
+    public @Nullable List<String> cmgNodeNames() {
         return cmgNodeNames;
     }
 
-    private ResetClusterCallInput(String clusterUrl, List<String> 
cmgNodeNames) {
-        Objects.requireNonNull(cmgNodeNames);
+    /** Returns metastorage replication factor. */
+    public @Nullable Integer metastorageReplicationFactor() {
+        return metastorageReplicationFactor;
+    }
 
+    private ResetClusterCallInput(String clusterUrl, @Nullable List<String> 
cmgNodeNames, @Nullable Integer metastorageReplicationFactor) {
         this.clusterUrl = clusterUrl;
-        this.cmgNodeNames = List.copyOf(cmgNodeNames);
+        this.cmgNodeNames = cmgNodeNames == null ? null : 
List.copyOf(cmgNodeNames);
+        this.metastorageReplicationFactor = metastorageReplicationFactor;
     }
 
     /** Returns {@link ResetClusterCallInput} with specified arguments. */
     public static ResetClusterCallInput of(ResetClusterMixin statesArgs, 
String clusterUrl) {
         return builder()
                 .cmgNodeNames(statesArgs.cmgNodeNames())
+                
.metastorageReplicationFactor(statesArgs.metastorageReplicationFactor())
                 .clusterUrl(clusterUrl)
                 .build();
     }
@@ -66,8 +75,12 @@ public class ResetClusterCallInput implements CallInput {
     private static class ResetClusterCallInputBuilder {
         private String clusterUrl;
 
+        @Nullable
         private List<String> cmgNodeNames;
 
+        @Nullable
+        private Integer metastorageReplicationFactor;
+
         /** Set cluster URL. */
         ResetClusterCallInputBuilder clusterUrl(String clusterUrl) {
             this.clusterUrl = clusterUrl;
@@ -75,14 +88,20 @@ public class ResetClusterCallInput implements CallInput {
         }
 
         /** Names of the proposed CMG nodes. */
-        ResetClusterCallInputBuilder cmgNodeNames(List<String> cmgNodeNames) {
+        ResetClusterCallInputBuilder cmgNodeNames(@Nullable List<String> 
cmgNodeNames) {
             this.cmgNodeNames = cmgNodeNames;
             return this;
         }
 
+        /** Metastorage replication factor. */
+        ResetClusterCallInputBuilder metastorageReplicationFactor(@Nullable 
Integer metastorageReplicationFactor) {
+            this.metastorageReplicationFactor = metastorageReplicationFactor;
+            return this;
+        }
+
         /** Build {@link ResetClusterCallInput}. */
         ResetClusterCallInput build() {
-            return new ResetClusterCallInput(clusterUrl, cmgNodeNames);
+            return new ResetClusterCallInput(clusterUrl, cmgNodeNames, 
metastorageReplicationFactor);
         }
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java
index 68b27cf92f..6f76b70ff7 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java
@@ -292,6 +292,11 @@ public enum Options {
         public static final String RECOVERY_NODE_NAMES_OPTION_DESC = "Names 
specifying nodes to get partition states from. "
                 + "Case-sensitive, without quotes, all nodes if not set";
 
+        public static final String RECOVERY_METASTORAGE_REPLICATION_OPTION = 
"--metastorage-replication-factor";
+
+        public static final String RECOVERY_METASTORAGE_REPLICATION_DESC = 
"Number of nodes in the voting member set of the Metastorage "
+                + "RAFT group.";
+
         public static final String RECOVERY_CMG_NODES_OPTION = 
"--cluster-management-group";
 
         public static final String RECOVERY_CMG_NODES_OPTION_DESC = "Names of 
nodes (use comma-separated list of node names "
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/cluster/reset/ResetClusterMixin.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/cluster/reset/ResetClusterMixin.java
index 65dd687c1b..caca6b84ce 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/cluster/reset/ResetClusterMixin.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/cluster/reset/ResetClusterMixin.java
@@ -19,17 +19,34 @@ package 
org.apache.ignite.internal.cli.commands.recovery.cluster.reset;
 
 import static 
org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_CMG_NODES_OPTION;
 import static 
org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_CMG_NODES_OPTION_DESC;
+import static 
org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_METASTORAGE_REPLICATION_DESC;
+import static 
org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_METASTORAGE_REPLICATION_OPTION;
 
 import java.util.List;
+import org.jetbrains.annotations.Nullable;
+import picocli.CommandLine.ArgGroup;
 import picocli.CommandLine.Option;
 
 /** Arguments for 'recovery cluster reset' command. */
 public class ResetClusterMixin {
-    @Option(names = RECOVERY_CMG_NODES_OPTION, description = 
RECOVERY_CMG_NODES_OPTION_DESC, split = ",", required = true)
-    private List<String> cmgNodeNames;
+    @ArgGroup(multiplicity = "1", exclusive = false)
+    private Options options;
 
     /** Returns names of the proposed CMG nodes. */
-    public List<String> cmgNodeNames() {
-        return cmgNodeNames;
+    public @Nullable List<String> cmgNodeNames() {
+        return options.cmgNodeNames;
+    }
+
+    /** Returns metastorage replication factor. */
+    public @Nullable Integer metastorageReplicationFactor() {
+        return options.metastorageReplicationFactor;
+    }
+
+    private static class Options {
+        @Option(names = RECOVERY_CMG_NODES_OPTION, description = 
RECOVERY_CMG_NODES_OPTION_DESC, split = ",")
+        private List<String> cmgNodeNames;
+
+        @Option(names = RECOVERY_METASTORAGE_REPLICATION_OPTION, description = 
RECOVERY_METASTORAGE_REPLICATION_DESC)
+        private Integer metastorageReplicationFactor;
     }
 }
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/recovery/cluster/reset/ResetClusterCommandTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/recovery/cluster/reset/ResetClusterCommandTest.java
new file mode 100644
index 0000000000..24a1a81891
--- /dev/null
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/commands/recovery/cluster/reset/ResetClusterCommandTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.cli.commands.recovery.cluster.reset;
+
+import static 
org.apache.ignite.internal.cli.commands.Options.Constants.CLUSTER_URL_OPTION;
+import static 
org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_CMG_NODES_OPTION;
+import static 
org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_METASTORAGE_REPLICATION_OPTION;
+import static org.mockserver.model.HttpRequest.request;
+import static org.mockserver.model.HttpResponse.response;
+import static org.mockserver.model.JsonBody.json;
+
+import org.apache.ignite.internal.cli.commands.IgniteCliInterfaceTestBase;
+import org.apache.ignite.internal.util.ArrayUtils;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockserver.model.MediaType;
+
+/** Unit tests for {@link ResetClusterCommand}. */
+public class ResetClusterCommandTest extends IgniteCliInterfaceTestBase {
+    @Test
+    @DisplayName("Reset cluster with CMG nodes specified")
+    void resetWithCmgNodesSpecified() {
+        String nodeNames = "node1,node2";
+
+        String expectedSentContent = "{"
+                + "    \"cmgNodeNames\": [\"node1\", \"node2\"],"
+                + "}";
+
+        clientAndServer
+                .when(request()
+                        .withMethod("POST")
+                        .withPath("/management/v1/recovery/cluster/reset")
+                        .withBody(json(expectedSentContent))
+                        .withContentType(MediaType.APPLICATION_JSON_UTF_8)
+                )
+                .respond(response(null));
+
+        execute(CLUSTER_URL_OPTION, mockUrl,
+                RECOVERY_CMG_NODES_OPTION, nodeNames
+        );
+
+        assertErrOutputIsEmpty();
+        assertOutputIs("Successfully initiated cluster repair.");
+    }
+
+    @Test
+    @DisplayName("Reset cluster with replication factor specified")
+    void resetWithReplicationFactorSpecified() {
+        String replicationFactor = "5";
+
+        String expectedSentContent = "{"
+                + "     \"metastorageReplicationFactor\" : 5"
+                + "}";
+
+        clientAndServer
+                .when(request()
+                        .withMethod("POST")
+                        .withPath("/management/v1/recovery/cluster/reset")
+                        .withBody(json(expectedSentContent))
+                        .withContentType(MediaType.APPLICATION_JSON_UTF_8)
+                )
+                .respond(response(null));
+
+        execute(CLUSTER_URL_OPTION, mockUrl,
+                RECOVERY_METASTORAGE_REPLICATION_OPTION, replicationFactor
+        );
+
+        assertErrOutputIsEmpty();
+        assertOutputIs("Successfully initiated cluster repair.");
+    }
+
+    @Test
+    @DisplayName("Reset cluster with cmg node names and replication factor 
specified")
+    void resetWithCmgNodeNamesAndReplicationFactorSpecified() {
+        String replicationFactor = "5";
+        String nodeNames = "node1,node2";
+
+        String expectedSentContent = "{"
+                + "    \"cmgNodeNames\": [\"node1\", \"node2\"],"
+                + "     \"metastorageReplicationFactor\" : 5"
+                + "}";
+
+        clientAndServer
+                .when(request()
+                        .withMethod("POST")
+                        .withPath("/management/v1/recovery/cluster/reset")
+                        .withBody(json(expectedSentContent))
+                        .withContentType(MediaType.APPLICATION_JSON_UTF_8)
+                )
+                .respond(response(null));
+
+        execute(CLUSTER_URL_OPTION, mockUrl,
+                RECOVERY_CMG_NODES_OPTION, nodeNames,
+                RECOVERY_METASTORAGE_REPLICATION_OPTION, replicationFactor
+        );
+
+        assertErrOutputIsEmpty();
+        assertOutputIs("Successfully initiated cluster repair.");
+    }
+
+    @Test
+    @DisplayName("Reset cluster fails with args not specified")
+    void resetFailsArgsNotSpecified() {
+        execute(CLUSTER_URL_OPTION, mockUrl);
+
+        assertErrOutputContains("Missing required argument(s): ");
+    }
+
+    @Override
+    protected void execute(String... args) {
+        String[] fullArgs = ArrayUtils.concat(new String[] {"recovery", 
"cluster", "reset"}, args);
+
+        super.execute(fullArgs);
+    }
+}
diff --git 
a/modules/system-disaster-recovery/src/integrationTest/java/org/apache/ignite/internal/disaster/system/ItCmgDisasterRecoveryTest.java
 
b/modules/system-disaster-recovery/src/integrationTest/java/org/apache/ignite/internal/disaster/system/ItCmgDisasterRecoveryTest.java
index 3365bf12e3..a22c15f97a 100644
--- 
a/modules/system-disaster-recovery/src/integrationTest/java/org/apache/ignite/internal/disaster/system/ItCmgDisasterRecoveryTest.java
+++ 
b/modules/system-disaster-recovery/src/integrationTest/java/org/apache/ignite/internal/disaster/system/ItCmgDisasterRecoveryTest.java
@@ -80,8 +80,8 @@ class ItCmgDisasterRecoveryTest extends 
ItSystemGroupDisasterRecoveryTest {
         assertThat(ignite.logicalTopologyService().logicalTopologyOnLeader(), 
willCompleteSuccessfully());
     }
 
-    private void initiateCmgRepairVia(int condictorIndex, int... 
newCmgIndexes) throws InterruptedException {
-        recoveryClient.initiateCmgRepair("localhost", 
cluster.httpPort(condictorIndex), nodeNames(newCmgIndexes));
+    private void initiateCmgRepairVia(int conductorIndex, int... 
newCmgIndexes) throws InterruptedException {
+        recoveryClient.initiateClusterReset("localhost", 
cluster.httpPort(conductorIndex), null, nodeNames(newCmgIndexes));
     }
 
     @Test
diff --git 
a/modules/system-disaster-recovery/src/integrationTest/java/org/apache/ignite/internal/disaster/system/ItMetastorageGroupDisasterRecoveryTest.java
 
b/modules/system-disaster-recovery/src/integrationTest/java/org/apache/ignite/internal/disaster/system/ItMetastorageGroupDisasterRecoveryTest.java
index 77dd33c3c6..2226b4123a 100644
--- 
a/modules/system-disaster-recovery/src/integrationTest/java/org/apache/ignite/internal/disaster/system/ItMetastorageGroupDisasterRecoveryTest.java
+++ 
b/modules/system-disaster-recovery/src/integrationTest/java/org/apache/ignite/internal/disaster/system/ItMetastorageGroupDisasterRecoveryTest.java
@@ -29,14 +29,12 @@ import static org.hamcrest.Matchers.notNullValue;
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
-import java.util.List;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
 import java.util.stream.IntStream;
 import org.apache.ignite.internal.app.IgniteImpl;
-import org.apache.ignite.internal.app.IgniteServerImpl;
 import org.apache.ignite.internal.hlc.HybridTimestamp;
 import org.apache.ignite.internal.lang.ByteArray;
 import org.apache.ignite.internal.lang.NodeStoppingException;
@@ -63,7 +61,7 @@ class ItMetastorageGroupDisasterRecoveryTest extends 
ItSystemGroupDisasterRecove
 
         assertThatMgHasNoMajority(node0BeforeRestart);
 
-        initiateMgRepairVia(node0BeforeRestart, 1, 0);
+        initiateMgRepairVia(0, 1, 0);
 
         IgniteImpl restartedNode0 = waitTillNodeRestartsInternally(0);
         waitTillMgHasMajority(restartedNode0);
@@ -83,12 +81,8 @@ class ItMetastorageGroupDisasterRecoveryTest extends 
ItSystemGroupDisasterRecove
         assertThat(ignite.metaStorageManager().get(new ByteArray("abc")), 
willCompleteSuccessfully());
     }
 
-    private void initiateMgRepairVia(IgniteImpl conductor, int 
mgReplicationFactor, int... newCmgIndexes) {
-        // TODO: IGNITE-22897 - initiate repair via CLI.
-
-        CompletableFuture<Void> initiationFuture = 
conductor.systemDisasterRecoveryManager()
-                
.resetClusterRepairingMetastorage(List.of(nodeNames(newCmgIndexes)), 
mgReplicationFactor);
-        assertThat(initiationFuture, willCompleteSuccessfully());
+    private void initiateMgRepairVia(int conductorIndex, int 
mgReplicationFactor, int... newCmgIndexes) throws InterruptedException {
+        recoveryClient.initiateClusterReset("localhost", 
cluster.httpPort(conductorIndex), mgReplicationFactor, 
nodeNames(newCmgIndexes));
     }
 
     @Test
@@ -99,9 +93,7 @@ class ItMetastorageGroupDisasterRecoveryTest extends 
ItSystemGroupDisasterRecove
         // This makes the MG majority go away.
         cluster.stopNode(1);
 
-        IgniteImpl igniteImpl0BeforeRestart = igniteImpl(0);
-
-        initiateMgRepairVia(igniteImpl0BeforeRestart, 1, 0);
+        initiateMgRepairVia(0, 1, 0);
 
         IgniteImpl restartedIgniteImpl0 = waitTillNodeRestartsInternally(0);
         waitTillMgHasMajority(restartedIgniteImpl0);
@@ -125,7 +117,7 @@ class ItMetastorageGroupDisasterRecoveryTest extends 
ItSystemGroupDisasterRecove
 
         assertThatMgHasNoMajority(igniteImpl2BeforeRestart);
 
-        initiateMgRepairVia(igniteImpl2BeforeRestart, 3, 0, 1, 2);
+        initiateMgRepairVia(2, 3, 0, 1, 2);
 
         IgniteImpl restartedIgniteImpl2 = waitTillNodeRestartsInternally(2);
         waitTillMgHasMajority(restartedIgniteImpl2);
@@ -146,7 +138,7 @@ class ItMetastorageGroupDisasterRecoveryTest extends 
ItSystemGroupDisasterRecove
 
         cluster.stopNode(1);
 
-        initiateMgRepairVia(igniteImpl(0), 1, 0);
+        initiateMgRepairVia(0, 1, 0);
 
         // Doing this wait to make sure that blank node will be able to 
connect at least someone. If we don't do this, the new node
         // will still be able to connect, but this will happen on Scalecube's 
initial sync retry, and we don't want to wait for it
@@ -179,7 +171,7 @@ class ItMetastorageGroupDisasterRecoveryTest extends 
ItSystemGroupDisasterRecove
 
         IntStream.of(0, 2).parallel().forEach(this::restartPartially);
 
-        initiateMgRepairVia(((IgniteServerImpl) 
cluster.server(0)).igniteImpl(), 1, 0);
+        initiateMgRepairVia(0, 1, 0);
 
         IgniteImpl restartedIgniteImpl0 = waitTillNodeRestartsInternally(0);
         waitTillMgHasMajority(restartedIgniteImpl0);
@@ -196,7 +188,7 @@ class ItMetastorageGroupDisasterRecoveryTest extends 
ItSystemGroupDisasterRecove
 
         IgniteImpl igniteImpl0BeforeRestart = igniteImpl(0);
 
-        initiateMgRepairVia(igniteImpl0BeforeRestart, 1, 0);
+        initiateMgRepairVia(0, 1, 0);
 
         IgniteImpl restartedIgniteImpl0 = waitTillNodeRestartsInternally(0);
         waitTillMgHasMajority(restartedIgniteImpl0);
@@ -217,7 +209,7 @@ class ItMetastorageGroupDisasterRecoveryTest extends 
ItSystemGroupDisasterRecove
 
         assertThatMgHasNoMajority(igniteImpl0BeforeRestart);
 
-        initiateMgRepairVia(igniteImpl0BeforeRestart, 1, 0);
+        initiateMgRepairVia(0, 1, 0);
 
         IgniteImpl restartedIgniteImpl0 = waitTillNodeRestartsInternally(0);
         waitTillMgHasMajority(restartedIgniteImpl0);
diff --git 
a/modules/system-disaster-recovery/src/integrationTest/java/org/apache/ignite/internal/disaster/system/SystemDisasterRecoveryClient.java
 
b/modules/system-disaster-recovery/src/integrationTest/java/org/apache/ignite/internal/disaster/system/SystemDisasterRecoveryClient.java
index 3041882eb5..7d0f49ef4d 100644
--- 
a/modules/system-disaster-recovery/src/integrationTest/java/org/apache/ignite/internal/disaster/system/SystemDisasterRecoveryClient.java
+++ 
b/modules/system-disaster-recovery/src/integrationTest/java/org/apache/ignite/internal/disaster/system/SystemDisasterRecoveryClient.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.disaster.system;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.apache.ignite.internal.util.ArrayUtils.concat;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -29,6 +30,7 @@ import java.util.stream.Stream;
 import org.apache.ignite.internal.cli.Main;
 import org.apache.ignite.internal.logger.IgniteLogger;
 import org.apache.ignite.internal.logger.Loggers;
+import org.jetbrains.annotations.Nullable;
 
 /**
  * Used to run system disaster recovery CLI commands.
@@ -37,15 +39,31 @@ import org.apache.ignite.internal.logger.Loggers;
 class SystemDisasterRecoveryClient {
     private static final IgniteLogger LOG = 
Loggers.forClass(SystemDisasterRecoveryClient.class);
 
-    void initiateCmgRepair(String httpHost, int httpPort, String... 
newCmgNodeNames) throws InterruptedException {
-        LOG.info("Initiating CMG repair via {}:{}, new CMG {}", httpHost, 
httpPort, List.of(newCmgNodeNames));
-
-        executeWithSameJavaBinaryAndClasspath(
-                Main.class.getName(),
-                "recovery", "cluster", "reset",
-                "--url", "http://"; + httpHost + ":" + httpPort,
-                "--cluster-management-group", String.join(",", newCmgNodeNames)
+    void initiateClusterReset(
+            String httpHost,
+            int httpPort,
+            @Nullable Integer metastorageReplicationFactor,
+            String... newCmgNodeNames
+    ) throws InterruptedException {
+        LOG.info(
+                "Initiating cluster reset via {}:{}, new CMG {}, metastorage 
replication Factor {}",
+                httpHost,
+                httpPort,
+                List.of(newCmgNodeNames),
+                metastorageReplicationFactor
         );
+
+        String[] args = {Main.class.getName(), "recovery", "cluster", "reset", 
"--url", "http://"; + httpHost + ":" + httpPort};
+
+        if (newCmgNodeNames.length > 0) {
+            args = concat(args, "--cluster-management-group", String.join(",", 
newCmgNodeNames));
+        }
+
+        if (metastorageReplicationFactor != null) {
+            args = concat(args, "--metastorage-replication-factor", 
String.valueOf(metastorageReplicationFactor));
+        }
+
+        executeWithSameJavaBinaryAndClasspath(args);
     }
 
     private static void executeWithSameJavaBinaryAndClasspath(String... args) 
throws InterruptedException {

Reply via email to