This is an automated email from the ASF dual-hosted git repository.
apolovtsev 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 6c5fadcaa1 IGNITE-22166 CLI for disaster recovery:
reset-lost-partitions (#3788)
6c5fadcaa1 is described below
commit 6c5fadcaa196e08028b0c65a923cb77d70b54b74
Author: Phillippko <[email protected]>
AuthorDate: Tue May 21 12:33:38 2024 +0400
IGNITE-22166 CLI for disaster recovery: reset-lost-partitions (#3788)
---
.../java/org/apache/ignite/lang/ErrorGroups.java | 2 +-
.../recovery/ItPartitionStatesCommandTest.java | 5 +-
...Test.java => ItResetPartitionsCommandTest.java} | 11 +-
.../ItResetPartitionsReplCommandTest.java} | 18 ++-
.../commands/recovery/ItResetPartitionsTest.java | 118 +++++++++++++++++++
.../call/recovery/PartitionStatesCallInput.java | 24 ++--
.../call/recovery/reset/ResetPartitionsCall.java | 56 +++++++++
.../recovery/reset/ResetPartitionsCallInput.java | 130 +++++++++++++++++++++
.../ignite/internal/cli/commands/Options.java | 14 ++-
.../cli/commands/recovery/RecoveryCommand.java | 4 +-
.../cli/commands/recovery/RecoveryReplCommand.java | 4 +-
.../recovery/reset/ResetPartitionsCommand.java | 48 ++++++++
.../recovery/reset/ResetPartitionsMixin.java | 65 +++++++++++
.../recovery/reset/ResetPartitionsReplCommand.java | 50 ++++++++
.../DistributionZoneNotFoundException.java | 6 +-
.../rest/api/recovery/DisasterRecoveryApi.java | 4 +-
.../recovery/ItDisasterRecoveryControllerTest.java | 2 +-
17 files changed, 522 insertions(+), 39 deletions(-)
diff --git a/modules/api/src/main/java/org/apache/ignite/lang/ErrorGroups.java
b/modules/api/src/main/java/org/apache/ignite/lang/ErrorGroups.java
index 6ef49bb962..960f6e7821 100755
--- a/modules/api/src/main/java/org/apache/ignite/lang/ErrorGroups.java
+++ b/modules/api/src/main/java/org/apache/ignite/lang/ErrorGroups.java
@@ -431,7 +431,7 @@ public class ErrorGroups {
/** Distribution zones group. */
public static final ErrorGroup DISTRIBUTION_ZONES_ERR_GROUP =
registerGroup("DISTRZONES", (short) 10);
- /** Distribution zone is not found. */
+ /** Distribution zone was not found. */
public static final int ZONE_NOT_FOUND_ERR =
DISTRIBUTION_ZONES_ERR_GROUP.registerErrorCode((short) 1);
}
diff --git
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/recovery/ItPartitionStatesCommandTest.java
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/recovery/ItPartitionStatesCommandTest.java
index 1b62ee971d..d512d0248e 100644
---
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/recovery/ItPartitionStatesCommandTest.java
+++
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/recovery/ItPartitionStatesCommandTest.java
@@ -17,16 +17,15 @@
package org.apache.ignite.internal.cli.commands.recovery;
-import java.util.List;
import
org.apache.ignite.internal.cli.commands.recovery.partitions.PartitionStatesCommand;
-import org.apache.ignite.internal.util.CollectionUtils;
+import org.apache.ignite.internal.util.ArrayUtils;
/** Test class for {@link PartitionStatesCommand}. */
public class ItPartitionStatesCommandTest extends ItPartitionStatesTest {
@Override
protected void execute(String... args) {
- String[] fullArgs = CollectionUtils.concat(List.of("recovery",
"partition-states"), List.of(args)).toArray(String[]::new);
+ String[] fullArgs = ArrayUtils.concat(new String[] {"recovery",
"partition-states"}, args);
super.execute(fullArgs);
}
diff --git
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/recovery/ItPartitionStatesCommandTest.java
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/recovery/ItResetPartitionsCommandTest.java
similarity index 69%
copy from
modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/recovery/ItPartitionStatesCommandTest.java
copy to
modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/recovery/ItResetPartitionsCommandTest.java
index 1b62ee971d..df483572a3 100644
---
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/recovery/ItPartitionStatesCommandTest.java
+++
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/recovery/ItResetPartitionsCommandTest.java
@@ -17,16 +17,15 @@
package org.apache.ignite.internal.cli.commands.recovery;
-import java.util.List;
-import
org.apache.ignite.internal.cli.commands.recovery.partitions.PartitionStatesCommand;
-import org.apache.ignite.internal.util.CollectionUtils;
+import
org.apache.ignite.internal.cli.commands.recovery.reset.ResetPartitionsCommand;
+import org.apache.ignite.internal.util.ArrayUtils;
-/** Test class for {@link PartitionStatesCommand}. */
-public class ItPartitionStatesCommandTest extends ItPartitionStatesTest {
+/** Test class for {@link ResetPartitionsCommand}. */
+public class ItResetPartitionsCommandTest extends ItResetPartitionsTest {
@Override
protected void execute(String... args) {
- String[] fullArgs = CollectionUtils.concat(List.of("recovery",
"partition-states"), List.of(args)).toArray(String[]::new);
+ String[] fullArgs = ArrayUtils.concat(new String[] {"recovery",
"reset-partitions"}, args);
super.execute(fullArgs);
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/RecoveryCommand.java
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/recovery/ItResetPartitionsReplCommandTest.java
similarity index 65%
copy from
modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/RecoveryCommand.java
copy to
modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/recovery/ItResetPartitionsReplCommandTest.java
index bba19e69cf..b0e476ab23 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/RecoveryCommand.java
+++
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/recovery/ItResetPartitionsReplCommandTest.java
@@ -17,15 +17,13 @@
package org.apache.ignite.internal.cli.commands.recovery;
-import org.apache.ignite.internal.cli.commands.BaseCommand;
-import
org.apache.ignite.internal.cli.commands.recovery.partitions.PartitionStatesCommand;
-import picocli.CommandLine.Command;
+import
org.apache.ignite.internal.cli.commands.recovery.reset.ResetPartitionsReplCommand;
-/** Disaster recovery command. */
-@Command(name = "recovery",
- subcommands = {
- PartitionStatesCommand.class
- },
- description = "Managers disaster recovery of Ignite cluster")
-public class RecoveryCommand extends BaseCommand {
+/** Test class for {@link ResetPartitionsReplCommand}. */
+public class ItResetPartitionsReplCommandTest extends ItResetPartitionsTest {
+
+ @Override
+ protected Class<?> getCommandClass() {
+ return ResetPartitionsReplCommand.class;
+ }
}
diff --git
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/recovery/ItResetPartitionsTest.java
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/recovery/ItResetPartitionsTest.java
new file mode 100644
index 0000000000..474de59819
--- /dev/null
+++
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/recovery/ItResetPartitionsTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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;
+
+import static
org.apache.ignite.internal.TestDefaultProfilesNames.DEFAULT_AIPERSIST_PROFILE_NAME;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.CLUSTER_URL_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_PARTITION_IDS_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_TABLE_NAME_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_ZONE_NAME_OPTION;
+
+import org.apache.ignite.internal.cli.CliIntegrationTest;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+/** Base test class for Recovery reset partitions commands. */
+public abstract class ItResetPartitionsTest extends CliIntegrationTest {
+ private static final String ZONE = "first_ZONE";
+
+ private static final String TABLE_NAME = "first_ZONE_table";
+
+ private static final String QUALIFIED_TABLE_NAME = "PUBLIC." + TABLE_NAME;
+
+ private static final int DEFAULT_PARTITION_COUNT = 25;
+
+ @BeforeAll
+ public void createTables() {
+ sql(String.format("CREATE ZONE \"%s\" WITH storage_profiles='%s'",
ZONE, DEFAULT_AIPERSIST_PROFILE_NAME));
+ sql(String.format("CREATE TABLE PUBLIC.\"%s\" (id INT PRIMARY KEY, val
INT) WITH PRIMARY_ZONE = '%s'", TABLE_NAME, ZONE));
+ }
+
+ @Test
+ public void testResetAllPartitions() {
+ execute(CLUSTER_URL_OPTION, NODE_URL,
+ RECOVERY_TABLE_NAME_OPTION, QUALIFIED_TABLE_NAME,
+ RECOVERY_ZONE_NAME_OPTION, ZONE);
+
+ assertErrOutputIsEmpty();
+ assertOutputContains("Successfully started resetting partitions.");
+ }
+
+ @Test
+ public void testResetSpecifiedPartitions() {
+ execute(CLUSTER_URL_OPTION, NODE_URL,
+ RECOVERY_TABLE_NAME_OPTION, QUALIFIED_TABLE_NAME,
+ RECOVERY_ZONE_NAME_OPTION, ZONE,
+ RECOVERY_PARTITION_IDS_OPTION, "1,2");
+
+ assertErrOutputIsEmpty();
+ assertOutputContains("Successfully started resetting partitions.");
+ }
+
+ @Test
+ public void testResetPartitionZoneNotFound() {
+ String unknownZone = "unknown_zone";
+
+ execute(CLUSTER_URL_OPTION, NODE_URL,
+ RECOVERY_TABLE_NAME_OPTION, QUALIFIED_TABLE_NAME,
+ RECOVERY_ZONE_NAME_OPTION, unknownZone,
+ RECOVERY_PARTITION_IDS_OPTION, "1,2");
+
+ assertErrOutputContains("Distribution zone was not found [zoneName=" +
unknownZone + "]");
+ assertOutputIsEmpty();
+ }
+
+ @Test
+ public void testResetPartitionTableNotFound() {
+ String unknownTable = "PUBLIC.unknown_table";
+
+ execute(CLUSTER_URL_OPTION, NODE_URL,
+ RECOVERY_TABLE_NAME_OPTION, unknownTable,
+ RECOVERY_ZONE_NAME_OPTION, ZONE);
+
+ assertErrOutputContains("The table does not exist [name=" +
unknownTable + "]");
+ assertOutputIsEmpty();
+ }
+
+ @Test
+ public void testResetPartitionsIllegalPartitionNegative() {
+ execute(CLUSTER_URL_OPTION, NODE_URL,
+ RECOVERY_TABLE_NAME_OPTION, QUALIFIED_TABLE_NAME,
+ RECOVERY_ZONE_NAME_OPTION, ZONE,
+ RECOVERY_PARTITION_IDS_OPTION, "0,5,-1,-10");
+
+ assertErrOutputContains("Partition ID can't be negative, found: -10");
+ assertOutputIsEmpty();
+ }
+
+ @Test
+ public void testResetPartitionsPartitionsOutOfRange() {
+ execute(CLUSTER_URL_OPTION, NODE_URL,
+ RECOVERY_TABLE_NAME_OPTION, QUALIFIED_TABLE_NAME,
+ RECOVERY_ZONE_NAME_OPTION, ZONE,
+ RECOVERY_PARTITION_IDS_OPTION,
String.valueOf(DEFAULT_PARTITION_COUNT));
+
+ assertErrOutputContains(String.format(
+ "Partition IDs should be in range [0, %d] for zone %s, found:
%d",
+ DEFAULT_PARTITION_COUNT - 1,
+ ZONE,
+ DEFAULT_PARTITION_COUNT
+ ));
+ assertOutputIsEmpty();
+ }
+}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/PartitionStatesCallInput.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/PartitionStatesCallInput.java
index 4da4275864..41d0749849 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/PartitionStatesCallInput.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/PartitionStatesCallInput.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.cli.call.recovery;
import java.util.List;
import
org.apache.ignite.internal.cli.commands.recovery.partitions.PartitionStatesMixin;
import org.apache.ignite.internal.cli.core.call.CallInput;
+import org.jetbrains.annotations.Nullable;
/** Input for the {@link PartitionStatesCall} call. */
public class PartitionStatesCallInput implements CallInput {
@@ -61,9 +62,9 @@ public class PartitionStatesCallInput implements CallInput {
private PartitionStatesCallInput(
String clusterUrl,
boolean local,
- List<String> nodeNames,
- List<String> zoneNames,
- List<Integer> partitionIds
+ @Nullable List<String> nodeNames,
+ @Nullable List<String> zoneNames,
+ @Nullable List<Integer> partitionIds
) {
this.clusterUrl = clusterUrl;
this.local = local;
@@ -102,10 +103,13 @@ public class PartitionStatesCallInput implements
CallInput {
private boolean local;
+ @Nullable
private List<String> nodeNames;
+ @Nullable
private List<String> zoneNames;
+ @Nullable
private List<Integer> partitionIds;
/** Set cluster URL. */
@@ -120,25 +124,25 @@ public class PartitionStatesCallInput implements
CallInput {
return this;
}
- /** Set names of zones to get partition states of. */
- PartitionStatesCallInputBuilder nodeNames(List<String> nodeNames) {
+ /** Set names of zones to get partition states of. All if empty or
null. */
+ PartitionStatesCallInputBuilder nodeNames(@Nullable List<String>
nodeNames) {
this.nodeNames = nodeNames;
return this;
}
- /** Set names of zones to get partition states of. */
- PartitionStatesCallInputBuilder zoneNames(List<String> zoneNames) {
+ /** Set names of zones to get partition states of. All if empty or
null. */
+ PartitionStatesCallInputBuilder zoneNames(@Nullable List<String>
zoneNames) {
this.zoneNames = zoneNames;
return this;
}
- /** Names of zones to get partition states of. */
- PartitionStatesCallInputBuilder partitionIds(List<Integer>
partitionIds) {
+ /** Names of zones to get partition states of. All if empty or null. */
+ PartitionStatesCallInputBuilder partitionIds(@Nullable List<Integer>
partitionIds) {
this.partitionIds = partitionIds;
return this;
}
- /** Set IDs of partitions to get states of. */
+ /** Build {@link PartitionStatesCallInput}. */
PartitionStatesCallInput build() {
return new PartitionStatesCallInput(clusterUrl, local, nodeNames,
zoneNames, partitionIds);
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/reset/ResetPartitionsCall.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/reset/ResetPartitionsCall.java
new file mode 100644
index 0000000000..4813ef9996
--- /dev/null
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/reset/ResetPartitionsCall.java
@@ -0,0 +1,56 @@
+/*
+ * 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.call.recovery.reset;
+
+import jakarta.inject.Singleton;
+import org.apache.ignite.internal.cli.core.call.Call;
+import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
+import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
+import org.apache.ignite.rest.client.api.RecoveryApi;
+import org.apache.ignite.rest.client.invoker.ApiException;
+import org.apache.ignite.rest.client.model.ResetPartitionsRequest;
+
+/** Call to reset partitions. */
+@Singleton
+public class ResetPartitionsCall implements Call<ResetPartitionsCallInput,
String> {
+ private final ApiClientFactory clientFactory;
+
+ public ResetPartitionsCall(ApiClientFactory clientFactory) {
+ this.clientFactory = clientFactory;
+ }
+
+ @Override
+ public DefaultCallOutput<String> execute(ResetPartitionsCallInput input) {
+ RecoveryApi client = new
RecoveryApi(clientFactory.getClient(input.clusterUrl()));
+
+ ResetPartitionsRequest command = new ResetPartitionsRequest();
+
+ command.setPartitionIds(input.partitionIds());
+ command.setTableName(input.tableName().trim());
+ command.setZoneName(input.zoneName().trim());
+
+ try {
+ client.resetPartitions(command);
+
+ return DefaultCallOutput.success("Successfully started resetting
partitions.");
+ } catch (ApiException e) {
+ return DefaultCallOutput.failure(new IgniteCliApiException(e,
input.clusterUrl()));
+ }
+ }
+}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/reset/ResetPartitionsCallInput.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/reset/ResetPartitionsCallInput.java
new file mode 100644
index 0000000000..e1a9b9f23c
--- /dev/null
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/recovery/reset/ResetPartitionsCallInput.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.call.recovery.reset;
+
+import java.util.List;
+import
org.apache.ignite.internal.cli.commands.recovery.reset.ResetPartitionsMixin;
+import org.apache.ignite.internal.cli.core.call.CallInput;
+import org.jetbrains.annotations.Nullable;
+
+/** Input for the {@link ResetPartitionsCall} call. */
+public class ResetPartitionsCallInput implements CallInput {
+ private final String clusterUrl;
+
+ private final String zoneName;
+
+ private final String tableName;
+
+ private final List<Integer> partitionIds;
+
+ /** Cluster url. */
+ public String clusterUrl() {
+ return clusterUrl;
+ }
+
+ /** Returns zone name to reset partitions of. */
+ public String zoneName() {
+ return zoneName;
+ }
+
+ /** Returns table name to reset partitions of. */
+ public String tableName() {
+ return tableName;
+ }
+
+ /** IDs of partitions to reset. */
+ public List<Integer> partitionIds() {
+ return partitionIds;
+ }
+
+ private ResetPartitionsCallInput(
+ String clusterUrl,
+ String zoneName,
+ String tableName,
+ @Nullable List<Integer> partitionIds
+ ) {
+ this.clusterUrl = clusterUrl;
+ this.zoneName = zoneName;
+ this.tableName = tableName;
+ this.partitionIds = partitionIds == null ? List.of() :
List.copyOf(partitionIds);
+ }
+
+ public static ResetPartitionsCallInput of(ResetPartitionsMixin statesArgs)
{
+ return of(statesArgs, statesArgs.clusterUrl());
+ }
+
+ /** Returns {@link ResetPartitionsCallInput} with specified arguments. */
+ public static ResetPartitionsCallInput of(ResetPartitionsMixin statesArgs,
String clusterUrl) {
+ return builder()
+ .zoneName(statesArgs.zoneName())
+ .tableName(statesArgs.tableName())
+ .partitionIds(statesArgs.partitionIds())
+ .clusterUrl(clusterUrl)
+ .build();
+ }
+
+ /**
+ * Builder method provider.
+ *
+ * @return new instance of {@link ResetPartitionsCallInput}.
+ */
+ private static ResetPartitionsCallInputBuilder builder() {
+ return new ResetPartitionsCallInputBuilder();
+ }
+
+ /** Builder for {@link ResetPartitionsCallInput}. */
+ private static class ResetPartitionsCallInputBuilder {
+ private String clusterUrl;
+
+ private String zoneName;
+
+ private String tableName;
+
+ @Nullable
+ private List<Integer> partitionIds;
+
+ /** Set cluster URL. */
+ ResetPartitionsCallInputBuilder clusterUrl(String clusterUrl) {
+ this.clusterUrl = clusterUrl;
+ return this;
+ }
+
+ /** Set name of zone to reset partitions of. */
+ ResetPartitionsCallInputBuilder zoneName(String zoneName) {
+ this.zoneName = zoneName;
+ return this;
+ }
+
+ /** Set name of table to reset partitions of. */
+ ResetPartitionsCallInputBuilder tableName(String tableName) {
+ this.tableName = tableName;
+ return this;
+ }
+
+ /** Names of zones to reset partitions of. */
+ ResetPartitionsCallInputBuilder partitionIds(@Nullable List<Integer>
partitionIds) {
+ this.partitionIds = partitionIds;
+ return this;
+ }
+
+ /** Build {@link ResetPartitionsCallInput}. */
+ ResetPartitionsCallInput build() {
+ return new ResetPartitionsCallInput(clusterUrl, zoneName,
tableName, partitionIds);
+ }
+ }
+}
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 1d3ad48685..6db9a0d744 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
@@ -298,11 +298,21 @@ public enum Options {
public static final String RECOVERY_ZONE_NAMES_OPTION = "--zones";
public static final String RECOVERY_ZONE_NAMES_OPTION_DESC = "Names
specifying zones to get partition states from. "
- + "Case-sensitive, all zones if not set";
+ + "Case-sensitive, without quotes, all zones if not set";
+
+ public static final String RECOVERY_ZONE_NAME_OPTION = "--zone";
+
+ public static final String RECOVERY_ZONE_NAME_OPTION_DESC = "Name of
the zone to reset partitions of. "
+ + "Case-sensitive, without quotes";
+
+ public static final String RECOVERY_TABLE_NAME_OPTION = "--table";
+
+ public static final String RECOVERY_TABLE_NAME_OPTION_DESC =
"Fully-qualified name of the table to reset partitions of. "
+ + "Case-sensitive, without quotes";
public static final String RECOVERY_NODE_NAMES_OPTION = "--nodes";
public static final String RECOVERY_NODE_NAMES_OPTION_DESC = "Names
specifying nodes to get partition states from. "
- + "Case-sensitive, all nodes if not set";
+ + "Case-sensitive, without quotes, all nodes if not set";
}
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/RecoveryCommand.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/RecoveryCommand.java
index bba19e69cf..c36e854bc4 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/RecoveryCommand.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/RecoveryCommand.java
@@ -19,12 +19,14 @@ package org.apache.ignite.internal.cli.commands.recovery;
import org.apache.ignite.internal.cli.commands.BaseCommand;
import
org.apache.ignite.internal.cli.commands.recovery.partitions.PartitionStatesCommand;
+import
org.apache.ignite.internal.cli.commands.recovery.reset.ResetPartitionsCommand;
import picocli.CommandLine.Command;
/** Disaster recovery command. */
@Command(name = "recovery",
subcommands = {
- PartitionStatesCommand.class
+ PartitionStatesCommand.class,
+ ResetPartitionsCommand.class
},
description = "Managers disaster recovery of Ignite cluster")
public class RecoveryCommand extends BaseCommand {
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/RecoveryReplCommand.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/RecoveryReplCommand.java
index dfc26a2bfa..120895bdfa 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/RecoveryReplCommand.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/RecoveryReplCommand.java
@@ -19,12 +19,14 @@ package org.apache.ignite.internal.cli.commands.recovery;
import org.apache.ignite.internal.cli.commands.BaseCommand;
import
org.apache.ignite.internal.cli.commands.recovery.partitions.PartitionStatesReplCommand;
+import
org.apache.ignite.internal.cli.commands.recovery.reset.ResetPartitionsReplCommand;
import picocli.CommandLine.Command;
/** Disaster recovery command. */
@Command(name = "recovery",
subcommands = {
- PartitionStatesReplCommand.class
+ PartitionStatesReplCommand.class,
+ ResetPartitionsReplCommand.class
},
description = "Managers disaster recovery of Ignite cluster")
public class RecoveryReplCommand extends BaseCommand {
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/reset/ResetPartitionsCommand.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/reset/ResetPartitionsCommand.java
new file mode 100644
index 0000000000..0eaf277ad3
--- /dev/null
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/reset/ResetPartitionsCommand.java
@@ -0,0 +1,48 @@
+/*
+ * 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.reset;
+
+import jakarta.inject.Inject;
+import java.util.concurrent.Callable;
+import org.apache.ignite.internal.cli.call.recovery.reset.ResetPartitionsCall;
+import
org.apache.ignite.internal.cli.call.recovery.reset.ResetPartitionsCallInput;
+import org.apache.ignite.internal.cli.commands.BaseCommand;
+import org.apache.ignite.internal.cli.core.call.CallExecutionPipeline;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Mixin;
+
+/** Command to reset partitions. */
+@Command(name = "reset-partitions", description = "Resets partitions.")
+public class ResetPartitionsCommand extends BaseCommand implements
Callable<Integer> {
+ @Mixin
+ private ResetPartitionsMixin options;
+
+ @Inject
+ private ResetPartitionsCall call;
+
+ @Override
+ public Integer call() {
+ return CallExecutionPipeline.builder(call)
+ .inputProvider(() -> ResetPartitionsCallInput.of(options))
+ .output(spec.commandLine().getOut())
+ .errOutput(spec.commandLine().getErr())
+ .verbose(verbose)
+ .build()
+ .runPipeline();
+ }
+}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/reset/ResetPartitionsMixin.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/reset/ResetPartitionsMixin.java
new file mode 100644
index 0000000000..3a58e5c118
--- /dev/null
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/reset/ResetPartitionsMixin.java
@@ -0,0 +1,65 @@
+/*
+ * 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.reset;
+
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_PARTITION_IDS_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_PARTITION_IDS_OPTION_DESC;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_TABLE_NAME_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_TABLE_NAME_OPTION_DESC;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_ZONE_NAME_OPTION;
+import static
org.apache.ignite.internal.cli.commands.Options.Constants.RECOVERY_ZONE_NAME_OPTION_DESC;
+
+import java.util.List;
+import org.apache.ignite.internal.cli.commands.cluster.ClusterUrlMixin;
+import picocli.CommandLine.Mixin;
+import picocli.CommandLine.Option;
+
+/** Arguments for recovery reset partitions command. */
+public class ResetPartitionsMixin {
+ @Mixin
+ private ClusterUrlMixin clusterUrl;
+
+ @Option(names = RECOVERY_PARTITION_IDS_OPTION, description =
RECOVERY_PARTITION_IDS_OPTION_DESC, split = ",")
+ private List<Integer> partitionIds;
+
+ @Option(names = RECOVERY_ZONE_NAME_OPTION, description =
RECOVERY_ZONE_NAME_OPTION_DESC, required = true)
+ private String zoneName;
+
+ @Option(names = RECOVERY_TABLE_NAME_OPTION, description =
RECOVERY_TABLE_NAME_OPTION_DESC, required = true)
+ private String tableName;
+
+ /** Returns name of the zone to reset partitions of. */
+ public String zoneName() {
+ return zoneName;
+ }
+
+ /** Returns name of the table to reset partitions of. */
+ public String tableName() {
+ return tableName;
+ }
+
+ /** Returns IDs of partitions to reset. */
+ public List<Integer> partitionIds() {
+ return partitionIds;
+ }
+
+ /** Returns cluster endpoint URL. */
+ public String clusterUrl() {
+ return clusterUrl.getClusterUrl();
+ }
+}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/reset/ResetPartitionsReplCommand.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/reset/ResetPartitionsReplCommand.java
new file mode 100644
index 0000000000..ce8d4fda8c
--- /dev/null
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/recovery/reset/ResetPartitionsReplCommand.java
@@ -0,0 +1,50 @@
+/*
+ * 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.reset;
+
+import jakarta.inject.Inject;
+import org.apache.ignite.internal.cli.call.recovery.reset.ResetPartitionsCall;
+import
org.apache.ignite.internal.cli.call.recovery.reset.ResetPartitionsCallInput;
+import org.apache.ignite.internal.cli.commands.BaseCommand;
+import
org.apache.ignite.internal.cli.commands.questions.ConnectToClusterQuestion;
+import org.apache.ignite.internal.cli.core.flow.builder.Flows;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Mixin;
+
+/** Command to reset partitions. */
+@Command(name = "reset-partitions", description = "Resets partitions.")
+public class ResetPartitionsReplCommand extends BaseCommand implements
Runnable {
+ @Mixin
+ private ResetPartitionsMixin options;
+
+ @Inject
+ private ConnectToClusterQuestion question;
+
+ @Inject
+ private ResetPartitionsCall call;
+
+ @Override
+ public void run() {
+ question.askQuestionIfNotConnected(options.clusterUrl())
+ .map(url -> ResetPartitionsCallInput.of(options, url))
+ .then(Flows.fromCall(call))
+ .verbose(verbose)
+ .print()
+ .start();
+ }
+}
diff --git
a/modules/distribution-zones/src/main/java/org/apache/ignite/internal/distributionzones/exception/DistributionZoneNotFoundException.java
b/modules/distribution-zones/src/main/java/org/apache/ignite/internal/distributionzones/exception/DistributionZoneNotFoundException.java
index 113753824a..5e148cf135 100644
---
a/modules/distribution-zones/src/main/java/org/apache/ignite/internal/distributionzones/exception/DistributionZoneNotFoundException.java
+++
b/modules/distribution-zones/src/main/java/org/apache/ignite/internal/distributionzones/exception/DistributionZoneNotFoundException.java
@@ -35,7 +35,7 @@ public class DistributionZoneNotFoundException extends
IgniteInternalException {
* @param zoneId Zone id.
*/
public DistributionZoneNotFoundException(int zoneId) {
- super(ZONE_NOT_FOUND_ERR, "Distribution zone is not found [zoneId=" +
zoneId + ']');
+ super(ZONE_NOT_FOUND_ERR, "Distribution zone was not found [zoneId=" +
zoneId + ']');
}
/**
@@ -44,7 +44,7 @@ public class DistributionZoneNotFoundException extends
IgniteInternalException {
* @param zoneName Zone name.
*/
public DistributionZoneNotFoundException(String zoneName) {
- super(ZONE_NOT_FOUND_ERR, "Distribution zone is not found [zoneName="
+ zoneName + ']');
+ super(ZONE_NOT_FOUND_ERR, "Distribution zone was not found [zoneName="
+ zoneName + ']');
}
/**
@@ -54,7 +54,7 @@ public class DistributionZoneNotFoundException extends
IgniteInternalException {
* @param cause Optional nested exception (can be {@code null}).
*/
public DistributionZoneNotFoundException(String zoneName, @Nullable
Throwable cause) {
- super(ZONE_NOT_FOUND_ERR, "Distribution zone is not found [zoneName="
+ zoneName + ']', cause);
+ super(ZONE_NOT_FOUND_ERR, "Distribution zone was not found [zoneName="
+ zoneName + ']', cause);
}
/**
diff --git
a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/recovery/DisasterRecoveryApi.java
b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/recovery/DisasterRecoveryApi.java
index 4cc26d2ac5..7d28f8d05f 100644
---
a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/recovery/DisasterRecoveryApi.java
+++
b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/recovery/DisasterRecoveryApi.java
@@ -18,6 +18,7 @@
package org.apache.ignite.internal.rest.api.recovery;
import io.micronaut.http.annotation.Body;
+import io.micronaut.http.annotation.Consumes;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Post;
@@ -87,6 +88,7 @@ public interface DisasterRecoveryApi {
content = @Content(mediaType = MediaType.PROBLEM_JSON, schema =
@Schema(implementation = Problem.class)))
@ApiResponse(responseCode = "400", description = "Bad request.",
content = @Content(mediaType = MediaType.PROBLEM_JSON, schema =
@Schema(implementation = Problem.class)))
- @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.PROBLEM_JSON)
CompletableFuture<Void> resetPartitions(@Body ResetPartitionsRequest
command);
}
diff --git
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/recovery/ItDisasterRecoveryControllerTest.java
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/recovery/ItDisasterRecoveryControllerTest.java
index fe6cbe693e..967728e8c2 100644
---
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/recovery/ItDisasterRecoveryControllerTest.java
+++
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/recovery/ItDisasterRecoveryControllerTest.java
@@ -374,7 +374,7 @@ public class ItDisasterRecoveryControllerTest extends
ClusterPerClassIntegration
assertThat(e.getResponse().code(), is(BAD_REQUEST.code()));
- assertThat(e.getMessage(), containsString("Distribution zone is not
found [zoneName=" + unknownZone + "]"));
+ assertThat(e.getMessage(), containsString("Distribution zone was not
found [zoneName=" + unknownZone + "]"));
}
@Test