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

timoninmaxim pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new b090ee565ca IGNITE-26748 Add rolling upgrade commands for control.sh 
(#12494)
b090ee565ca is described below

commit b090ee565ca7d3eada325e75585b0717bb1de6ca
Author: Aleksandr Chesnokov <[email protected]>
AuthorDate: Fri Nov 7 15:02:10 2025 +0300

    IGNITE-26748 Add rolling upgrade commands for control.sh (#12494)
---
 .../commandline/CommandHandlerParsingTest.java     |   5 +
 .../testsuites/IgniteControlUtilityTestSuite2.java |   2 +
 .../ignite/util/RollingUpgradeCommandTest.java     | 130 +++++++++++++++++++++
 .../internal/management/IgniteCommandRegistry.java |   2 +
 .../rollingupgrade/RollingUpgradeCommand.java      |  31 +++++
 .../RollingUpgradeDisableCommand.java              |  67 +++++++++++
 .../rollingupgrade/RollingUpgradeDisableTask.java  |  75 ++++++++++++
 .../RollingUpgradeEnableCommand.java               |  74 ++++++++++++
 .../RollingUpgradeEnableCommandArg.java            |  58 +++++++++
 .../rollingupgrade/RollingUpgradeEnableTask.java   |  77 ++++++++++++
 .../rollingupgrade/RollingUpgradeTaskResult.java   | 102 ++++++++++++++++
 ...ridCommandHandlerClusterByClassTest_help.output |  11 ++
 ...andHandlerClusterByClassWithSSLTest_help.output |  11 ++
 13 files changed, 645 insertions(+)

diff --git 
a/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java
 
b/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java
index ada5db81577..507d3bed706 100644
--- 
a/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java
+++ 
b/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java
@@ -76,6 +76,8 @@ import 
org.apache.ignite.internal.management.meta.MetaUpdateCommand;
 import org.apache.ignite.internal.management.metric.MetricCommand;
 import 
org.apache.ignite.internal.management.performancestatistics.PerformanceStatisticsCommand;
 import org.apache.ignite.internal.management.property.PropertyCommand;
+import 
org.apache.ignite.internal.management.rollingupgrade.RollingUpgradeCommand;
+import 
org.apache.ignite.internal.management.rollingupgrade.RollingUpgradeEnableCommand;
 import org.apache.ignite.internal.management.snapshot.SnapshotCommand;
 import org.apache.ignite.internal.management.snapshot.SnapshotRestoreCommand;
 import org.apache.ignite.internal.management.tx.TxCommand;
@@ -516,6 +518,8 @@ public class CommandHandlerParsingTest {
             cmdText = F.concat(cmdText, "masterKeyName1");
         else if (cmd.getClass() == EncryptionChangeCacheKeyCommand.class)
             cmdText = F.concat(cmdText, "cacheGroup1");
+        else if (cmd.getClass() == RollingUpgradeEnableCommand.class)
+            cmdText = F.concat(cmdText, "2.18.0");
         else if (cmd.getClass() == SnapshotRestoreCommand.class)
             cmdText = F.concat(cmdText, "snp1");
         else if (cmd.getClass() == MetaUpdateCommand.class)
@@ -1391,6 +1395,7 @@ public class CommandHandlerParsingTest {
             cmd == MetaCommand.class ||
             cmd == WarmUpCommand.class ||
             cmd == PropertyCommand.class ||
+            cmd == RollingUpgradeCommand.class ||
             cmd == SystemViewCommand.class ||
             cmd == MetricCommand.class ||
             cmd == DefragmentationCommand.class ||
diff --git 
a/modules/control-utility/src/test/java/org/apache/ignite/testsuites/IgniteControlUtilityTestSuite2.java
 
b/modules/control-utility/src/test/java/org/apache/ignite/testsuites/IgniteControlUtilityTestSuite2.java
index 4d6feb57851..d7ed2b51246 100644
--- 
a/modules/control-utility/src/test/java/org/apache/ignite/testsuites/IgniteControlUtilityTestSuite2.java
+++ 
b/modules/control-utility/src/test/java/org/apache/ignite/testsuites/IgniteControlUtilityTestSuite2.java
@@ -37,6 +37,7 @@ import 
org.apache.ignite.util.GridCommandHandlerTracingConfigurationTest;
 import org.apache.ignite.util.IdleVerifyDumpTest;
 import org.apache.ignite.util.MetricCommandTest;
 import org.apache.ignite.util.PerformanceStatisticsCommandTest;
+import org.apache.ignite.util.RollingUpgradeCommandTest;
 import org.apache.ignite.util.SystemViewCommandTest;
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
@@ -63,6 +64,7 @@ import org.junit.runners.Suite;
     GridCommandHandlerConsistencySensitiveTest.class,
     GridCommandHandlerConsistencyRepairCorrectnessAtomicTest.class,
 
+    RollingUpgradeCommandTest.class,
     SystemViewCommandTest.class,
     MetricCommandTest.class,
     PerformanceStatisticsCommandTest.class,
diff --git 
a/modules/control-utility/src/test/java/org/apache/ignite/util/RollingUpgradeCommandTest.java
 
b/modules/control-utility/src/test/java/org/apache/ignite/util/RollingUpgradeCommandTest.java
new file mode 100644
index 00000000000..6737103dbd4
--- /dev/null
+++ 
b/modules/control-utility/src/test/java/org/apache/ignite/util/RollingUpgradeCommandTest.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.util;
+
+import 
org.apache.ignite.internal.management.rollingupgrade.RollingUpgradeCommand;
+import 
org.apache.ignite.internal.management.rollingupgrade.RollingUpgradeTaskResult;
+import org.apache.ignite.lang.IgniteProductVersion;
+import org.junit.Test;
+
+import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_BUILD_VER;
+import static 
org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_OK;
+
+/** Tests {@link RollingUpgradeCommand} command. */
+public class RollingUpgradeCommandTest extends 
GridCommandHandlerClusterByClassAbstractTest {
+    /** */
+    public static final String ENABLE = "enable";
+
+    /** */
+    public static final String DISABLE = "disable";
+
+    /** */
+    public static final String ROLLING_UPGRADE = "--rolling-upgrade";
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        autoConfirmation = true;
+    }
+
+    /** */
+    @Test
+    public void testEnableAndDisable() {
+        IgniteProductVersion curVer = 
IgniteProductVersion.fromString(crd.localNode().attribute(ATTR_BUILD_VER));
+
+        String targetVerStr = curVer.major() + "." + (curVer.minor() + 1) + 
".0";
+        IgniteProductVersion targetVer = 
IgniteProductVersion.fromString(targetVerStr);
+
+        int res = execute(ROLLING_UPGRADE, ENABLE, targetVerStr);
+
+        assertEquals(EXIT_CODE_OK, res);
+
+        RollingUpgradeTaskResult taskRes = 
(RollingUpgradeTaskResult)lastOperationResult;
+
+        assertNull(taskRes.errorMessage());
+        assertEquals(curVer, taskRes.currentVersion());
+        assertEquals(targetVer, taskRes.targetVersion());
+
+        assertTrue(crd.context().rollingUpgrade().enabled());
+
+        res = execute(ROLLING_UPGRADE, DISABLE);
+
+        assertEquals(EXIT_CODE_OK, res);
+
+        taskRes = (RollingUpgradeTaskResult)lastOperationResult;
+
+        assertNull(taskRes.errorMessage());
+        assertEquals(curVer, taskRes.currentVersion());
+        assertNull(taskRes.targetVersion());
+
+        assertFalse(crd.context().rollingUpgrade().enabled());
+    }
+
+    /** */
+    @Test
+    public void testDoubleDisable() {
+        IgniteProductVersion curVer = 
IgniteProductVersion.fromString(crd.localNode().attribute(ATTR_BUILD_VER));
+
+        int res = execute(ROLLING_UPGRADE, DISABLE);
+
+        assertEquals(EXIT_CODE_OK, res);
+        RollingUpgradeTaskResult taskRes = 
(RollingUpgradeTaskResult)lastOperationResult;
+
+        assertEquals(curVer, taskRes.currentVersion());
+        assertNull(taskRes.targetVersion());
+        assertNull(taskRes.errorMessage());
+
+        res = execute(ROLLING_UPGRADE, DISABLE);
+
+        assertEquals(EXIT_CODE_OK, res);
+        taskRes = (RollingUpgradeTaskResult)lastOperationResult;
+
+        assertNull(taskRes.errorMessage());
+        assertEquals(curVer, taskRes.currentVersion());
+        assertNull(taskRes.targetVersion());
+
+        assertFalse(crd.context().rollingUpgrade().enabled());
+    }
+
+    /** */
+    @Test
+    public void testEnableWithDifferentTargetVersions() {
+        IgniteProductVersion curVer = 
IgniteProductVersion.fromString(crd.localNode().attribute(ATTR_BUILD_VER));
+
+        String targetVerStr = curVer.major() + "." + (curVer.minor() + 1) + 
".0";
+        IgniteProductVersion targetVer = 
IgniteProductVersion.fromString(targetVerStr);
+
+        execute(ROLLING_UPGRADE, ENABLE, targetVerStr);
+
+        String anotherTargetVerStr = curVer.major() + "." + curVer.minor() + 
"." + (curVer.maintenance() + 1);
+
+        int res = execute(ROLLING_UPGRADE, ENABLE, anotherTargetVerStr);
+
+        assertEquals(EXIT_CODE_OK, res);
+        RollingUpgradeTaskResult taskRes = 
(RollingUpgradeTaskResult)lastOperationResult;
+
+        assertNotNull(taskRes.errorMessage());
+        assertTrue(taskRes.errorMessage().contains("Rolling upgrade is already 
enabled with a different current and target version"));
+
+        assertEquals(curVer, taskRes.currentVersion());
+        assertEquals(targetVer, taskRes.targetVersion());
+
+        assertTrue(crd.context().rollingUpgrade().enabled());
+    }
+}
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/management/IgniteCommandRegistry.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/management/IgniteCommandRegistry.java
index ae53c3e142e..4388486148a 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/management/IgniteCommandRegistry.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/management/IgniteCommandRegistry.java
@@ -34,6 +34,7 @@ import 
org.apache.ignite.internal.management.metric.MetricCommand;
 import 
org.apache.ignite.internal.management.performancestatistics.PerformanceStatisticsCommand;
 import org.apache.ignite.internal.management.persistence.PersistenceCommand;
 import org.apache.ignite.internal.management.property.PropertyCommand;
+import 
org.apache.ignite.internal.management.rollingupgrade.RollingUpgradeCommand;
 import org.apache.ignite.internal.management.snapshot.SnapshotCommand;
 import 
org.apache.ignite.internal.management.tracing.TracingConfigurationCommand;
 import org.apache.ignite.internal.management.tx.TxCommand;
@@ -67,6 +68,7 @@ public class IgniteCommandRegistry extends 
CommandRegistryImpl<NoArg, Void> {
             new TracingConfigurationCommand(),
             new WarmUpCommand(),
             new PropertyCommand(),
+            new RollingUpgradeCommand(),
             new SystemViewCommand(),
             new MetricCommand(),
             new PersistenceCommand(),
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/management/rollingupgrade/RollingUpgradeCommand.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/management/rollingupgrade/RollingUpgradeCommand.java
new file mode 100644
index 00000000000..c68f0378998
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/management/rollingupgrade/RollingUpgradeCommand.java
@@ -0,0 +1,31 @@
+/*
+ * 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.management.rollingupgrade;
+
+import org.apache.ignite.internal.management.api.CommandRegistryImpl;
+
+/** Rolling upgrade commands. */
+public class RollingUpgradeCommand extends CommandRegistryImpl {
+    /** */
+    public RollingUpgradeCommand() {
+        super(
+            new RollingUpgradeEnableCommand(),
+            new RollingUpgradeDisableCommand()
+        );
+    }
+}
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/management/rollingupgrade/RollingUpgradeDisableCommand.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/management/rollingupgrade/RollingUpgradeDisableCommand.java
new file mode 100644
index 00000000000..8bf3b948f07
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/management/rollingupgrade/RollingUpgradeDisableCommand.java
@@ -0,0 +1,67 @@
+/*
+ * 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.management.rollingupgrade;
+
+import java.util.Collection;
+import java.util.function.Consumer;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.management.api.ComputeCommand;
+import org.apache.ignite.internal.management.api.NoArg;
+import org.apache.ignite.lang.IgniteExperimental;
+
+import static 
org.apache.ignite.internal.management.api.CommandUtils.coordinatorOrNull;
+
+/** Command to disable rolling upgrade mode. */
+@IgniteExperimental
+public class RollingUpgradeDisableCommand implements ComputeCommand<NoArg, 
RollingUpgradeTaskResult> {
+    /** {@inheritDoc} */
+    @Override public String description() {
+        return "Disable rolling upgrade mode. All nodes in the cluster must be 
running the same version";
+    }
+
+    /** {@inheritDoc} */
+    @Override public Class<NoArg> argClass() {
+        return NoArg.class;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Class<RollingUpgradeDisableTask> taskClass() {
+        return RollingUpgradeDisableTask.class;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void printResult(NoArg arg, RollingUpgradeTaskResult res, 
Consumer<String> printer) {
+        if (res.errorMessage() != null) {
+            printer.accept("Failed to disable rolling upgrade: " + 
res.errorMessage());
+            return;
+        }
+
+        printer.accept("Rolling upgrade disabled [currentVersion=" + 
res.currentVersion() + ']');
+    }
+
+    /** {@inheritDoc} */
+    @Override public Collection<ClusterNode> nodes(Collection<ClusterNode> 
nodes, NoArg arg) {
+        Collection<ClusterNode> coordinator = coordinatorOrNull(nodes);
+
+        if (coordinator == null)
+            throw new IgniteException("Could not find coordinator among nodes: 
" + nodes);
+
+        return coordinator;
+    }
+}
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/management/rollingupgrade/RollingUpgradeDisableTask.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/management/rollingupgrade/RollingUpgradeDisableTask.java
new file mode 100644
index 00000000000..8c913de58ab
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/management/rollingupgrade/RollingUpgradeDisableTask.java
@@ -0,0 +1,75 @@
+/*
+ * 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.management.rollingupgrade;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.management.api.NoArg;
+import 
org.apache.ignite.internal.processors.rollingupgrade.RollingUpgradeProcessor;
+import org.apache.ignite.internal.processors.task.GridInternal;
+import org.apache.ignite.internal.util.lang.IgnitePair;
+import org.apache.ignite.internal.visor.VisorJob;
+import org.apache.ignite.internal.visor.VisorOneNodeTask;
+import org.apache.ignite.lang.IgniteProductVersion;
+
+import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_BUILD_VER;
+
+/** Task to disable rolling upgrade. */
+@GridInternal
+public class RollingUpgradeDisableTask extends VisorOneNodeTask<NoArg, 
RollingUpgradeTaskResult> {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** {@inheritDoc} */
+    @Override protected VisorJob<NoArg, RollingUpgradeTaskResult> job(NoArg 
arg) {
+        return new RollingUpgradeDisableJob(arg, debug);
+    }
+
+    /** */
+    private static class RollingUpgradeDisableJob extends VisorJob<NoArg, 
RollingUpgradeTaskResult> {
+        /** */
+        private static final long serialVersionUID = 0L;
+
+        /** */
+        protected RollingUpgradeDisableJob(NoArg arg, boolean debug) {
+            super(arg, debug);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected RollingUpgradeTaskResult run(NoArg arg) throws 
IgniteException {
+            RollingUpgradeProcessor proc = ignite.context().rollingUpgrade();
+
+            try {
+                proc.disable();
+
+                String buildVer = ignite.localNode().attribute(ATTR_BUILD_VER);
+
+                return new 
RollingUpgradeTaskResult(IgniteProductVersion.fromString(buildVer), null, null);
+            }
+            catch (IgniteCheckedException e) {
+                IgnitePair<IgniteProductVersion> rollUpVers = proc.versions();
+
+                return new RollingUpgradeTaskResult(
+                    rollUpVers == null ? null : rollUpVers.get1(),
+                    rollUpVers == null ? null : rollUpVers.get2(),
+                    e.getMessage()
+                );
+            }
+        }
+    }
+}
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/management/rollingupgrade/RollingUpgradeEnableCommand.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/management/rollingupgrade/RollingUpgradeEnableCommand.java
new file mode 100644
index 00000000000..94cfd7be2ab
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/management/rollingupgrade/RollingUpgradeEnableCommand.java
@@ -0,0 +1,74 @@
+/*
+ * 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.management.rollingupgrade;
+
+import java.util.Collection;
+import java.util.function.Consumer;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.cluster.ClusterNode;
+import org.apache.ignite.internal.management.api.ComputeCommand;
+import org.apache.ignite.lang.IgniteExperimental;
+
+import static 
org.apache.ignite.internal.management.api.CommandUtils.coordinatorOrNull;
+
+/** Command to enable rolling upgrade mode. */
+@IgniteExperimental
+public class RollingUpgradeEnableCommand implements 
ComputeCommand<RollingUpgradeEnableCommandArg, RollingUpgradeTaskResult> {
+    /** {@inheritDoc} */
+    @Override public String description() {
+        return "Enable rolling upgrade mode. It allows cluster with 
mixed-version nodes";
+    }
+
+    /** {@inheritDoc} */
+    @Override public String confirmationPrompt(RollingUpgradeEnableCommandArg 
arg) {
+        return "Warning: the command will enable rolling upgrade mode. "
+            + "It allows cluster with mixed-version nodes. You are responsible 
for upgrading nodes manually. "
+            + "This mode can be disabled only when all nodes (including client 
nodes) run the same version.";
+    }
+
+    /** {@inheritDoc} */
+    @Override public Class<RollingUpgradeEnableCommandArg> argClass() {
+        return RollingUpgradeEnableCommandArg.class;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Class<RollingUpgradeEnableTask> taskClass() {
+        return RollingUpgradeEnableTask.class;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void printResult(RollingUpgradeEnableCommandArg arg, 
RollingUpgradeTaskResult res, Consumer<String> printer) {
+        if (res.errorMessage() != null) {
+            printer.accept("Failed to enable rolling upgrade: " + 
res.errorMessage());
+            return;
+        }
+
+        printer.accept("Rolling upgrade enabled "
+                + "[currentVersion=" + res.currentVersion() + ", 
targetVersion=" + res.targetVersion() + ']');
+    }
+
+    /** {@inheritDoc} */
+    @Override public Collection<ClusterNode> nodes(Collection<ClusterNode> 
nodes, RollingUpgradeEnableCommandArg arg) {
+        Collection<ClusterNode> coordinator = coordinatorOrNull(nodes);
+
+        if (coordinator == null)
+            throw new IgniteException("Could not find coordinator among nodes: 
" + nodes);
+
+        return coordinator;
+    }
+}
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/management/rollingupgrade/RollingUpgradeEnableCommandArg.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/management/rollingupgrade/RollingUpgradeEnableCommandArg.java
new file mode 100644
index 00000000000..180e7e39d76
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/management/rollingupgrade/RollingUpgradeEnableCommandArg.java
@@ -0,0 +1,58 @@
+/*
+ * 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.management.rollingupgrade;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.apache.ignite.internal.dto.IgniteDataTransferObject;
+import org.apache.ignite.internal.management.api.Argument;
+import org.apache.ignite.internal.management.api.Positional;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+/** Rolling upgrade enable command argument. */
+public class RollingUpgradeEnableCommandArg extends IgniteDataTransferObject {
+    /** */
+    private static final long serialVersionUID = 0;
+
+    /** Target version. */
+    @Positional
+    @Argument(description = "Target Ignite version. The target version can be 
one minor higher if its maintenance version is zero, "
+        + "or one maintenance version higher (e.g. 2.18.0 -> 2.18.1 or 2.18.1 
-> 2.19.0)")
+    private String targetVersion;
+
+    /** */
+    public String targetVersion() {
+        return targetVersion;
+    }
+
+    /** */
+    public void targetVersion(String targetVersion) {
+        this.targetVersion = targetVersion;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void writeExternalData(ObjectOutput out) throws 
IOException {
+        U.writeString(out, targetVersion);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void readExternalData(ObjectInput in) throws 
IOException, ClassNotFoundException {
+        targetVersion = U.readString(in);
+    }
+}
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/management/rollingupgrade/RollingUpgradeEnableTask.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/management/rollingupgrade/RollingUpgradeEnableTask.java
new file mode 100644
index 00000000000..70b279f01de
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/management/rollingupgrade/RollingUpgradeEnableTask.java
@@ -0,0 +1,77 @@
+/*
+ * 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.management.rollingupgrade;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
+import 
org.apache.ignite.internal.processors.rollingupgrade.RollingUpgradeProcessor;
+import org.apache.ignite.internal.processors.task.GridInternal;
+import org.apache.ignite.internal.util.lang.IgnitePair;
+import org.apache.ignite.internal.visor.VisorJob;
+import org.apache.ignite.internal.visor.VisorOneNodeTask;
+import org.apache.ignite.lang.IgniteProductVersion;
+
+/** Task to enable rolling upgrade. */
+@GridInternal
+public class RollingUpgradeEnableTask extends 
VisorOneNodeTask<RollingUpgradeEnableCommandArg, RollingUpgradeTaskResult> {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** {@inheritDoc} */
+    @Override protected VisorJob<RollingUpgradeEnableCommandArg, 
RollingUpgradeTaskResult> job(RollingUpgradeEnableCommandArg arg) {
+        return new RollingUpgradeEnableJob(arg, debug);
+    }
+
+    /** */
+    private static class RollingUpgradeEnableJob extends 
VisorJob<RollingUpgradeEnableCommandArg, RollingUpgradeTaskResult> {
+        /** */
+        private static final long serialVersionUID = 0L;
+
+        /** */
+        protected RollingUpgradeEnableJob(RollingUpgradeEnableCommandArg arg, 
boolean debug) {
+            super(arg, debug);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected RollingUpgradeTaskResult 
run(RollingUpgradeEnableCommandArg arg) throws IgniteException {
+            RollingUpgradeProcessor proc = ignite.context().rollingUpgrade();
+
+            try {
+                
proc.enable(IgniteProductVersion.fromString(arg.targetVersion()));
+
+                IgnitePair<IgniteProductVersion> rollUpVers = proc.versions();
+
+                return new RollingUpgradeTaskResult(
+                    rollUpVers == null ? null : rollUpVers.get1(),
+                    rollUpVers == null ? null : rollUpVers.get2(),
+                    null
+                );
+            }
+            catch (IgniteCheckedException e) {
+                IgnitePair<IgniteProductVersion> rollUpVers = proc.versions();
+
+                return new RollingUpgradeTaskResult(
+                    rollUpVers == null ? null : rollUpVers.get1(),
+                    rollUpVers == null ? null : rollUpVers.get2(),
+                    e.getMessage()
+                );
+            }
+        }
+
+    }
+}
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/management/rollingupgrade/RollingUpgradeTaskResult.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/management/rollingupgrade/RollingUpgradeTaskResult.java
new file mode 100644
index 00000000000..178120654c2
--- /dev/null
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/management/rollingupgrade/RollingUpgradeTaskResult.java
@@ -0,0 +1,102 @@
+/*
+ * 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.management.rollingupgrade;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.apache.ignite.internal.dto.IgniteDataTransferObject;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteProductVersion;
+
+/** */
+public class RollingUpgradeTaskResult extends IgniteDataTransferObject {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** */
+    private IgniteProductVersion curVer;
+
+    /** */
+    private IgniteProductVersion targetVer;
+
+    /** */
+    private String errMsg;
+
+    /** */
+    public RollingUpgradeTaskResult(IgniteProductVersion curVer, 
IgniteProductVersion targetVer, String errMsg) {
+        this.curVer = curVer;
+        this.targetVer = targetVer;
+        this.errMsg = errMsg;
+    }
+
+    /** */
+    public RollingUpgradeTaskResult() {
+        // No-op.
+    }
+
+    /** */
+    public IgniteProductVersion currentVersion() {
+        return curVer;
+    }
+
+    /** */
+    public void currentVersion(IgniteProductVersion curVer) {
+        this.curVer = curVer;
+    }
+
+    /** */
+    public IgniteProductVersion targetVersion() {
+        return targetVer;
+    }
+
+    /** */
+    public void targetVersion(IgniteProductVersion targetVer) {
+        this.targetVer = targetVer;
+    }
+
+    /** */
+    public String errorMessage() {
+        return errMsg;
+    }
+
+    /** */
+    public void errorMessage(String errMsg) {
+        this.errMsg = errMsg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void writeExternalData(ObjectOutput out) throws 
IOException {
+        out.writeObject(curVer);
+        out.writeObject(targetVer);
+        U.writeString(out, errMsg);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void readExternalData(ObjectInput in) throws 
IOException, ClassNotFoundException {
+        this.curVer = (IgniteProductVersion)in.readObject();
+        this.targetVer = (IgniteProductVersion)in.readObject();
+        this.errMsg = U.readString(in);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(RollingUpgradeTaskResult.class, this);
+    }
+}
diff --git 
a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
 
b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
index 35a0878fec5..06a5226d291 100644
--- 
a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
+++ 
b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
@@ -371,6 +371,17 @@ If the file name isn't specified the output file name is: 
'<typeId>.bin':
   Set the property value:
     control.(sh|bat) --property set --name <property_name> --val 
<property_value>
 
+  [EXPERIMENTAL]
+  Enable rolling upgrade mode. It allows cluster with mixed-version nodes:
+      control.(sh|bat) --rolling-upgrade enable target_version
+
+      Parameters:
+        target_version  - Target Ignite version. The target version can be one 
minor higher if its maintenance version is zero, or one maintenance version 
higher (e.g. 2.18.0 -> 2.18.1 or 2.18.1 -> 2.19.0).
+
+  [EXPERIMENTAL]
+  Disable rolling upgrade mode. All nodes in the cluster must be running the 
same version:
+    control.(sh|bat) --rolling-upgrade disable
+
   Print system view content:
     control.(sh|bat) --system-view system_view_name [--node-id 
node_id|--node-ids nodeId1,nodeId2,..|--all-nodes]
 
diff --git 
a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
 
b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
index bd8712d6147..a3a7da449d8 100644
--- 
a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
+++ 
b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
@@ -371,6 +371,17 @@ If the file name isn't specified the output file name is: 
'<typeId>.bin':
   Set the property value:
     control.(sh|bat) --property set --name <property_name> --val 
<property_value>
 
+  [EXPERIMENTAL]
+  Enable rolling upgrade mode. It allows cluster with mixed-version nodes:
+      control.(sh|bat) --rolling-upgrade enable target_version
+
+      Parameters:
+        target_version  - Target Ignite version. The target version can be one 
minor higher if its maintenance version is zero, or one maintenance version 
higher (e.g. 2.18.0 -> 2.18.1 or 2.18.1 -> 2.19.0).
+
+  [EXPERIMENTAL]
+  Disable rolling upgrade mode. All nodes in the cluster must be running the 
same version:
+    control.(sh|bat) --rolling-upgrade disable
+
   Print system view content:
     control.(sh|bat) --system-view system_view_name [--node-id 
node_id|--node-ids nodeId1,nodeId2,..|--all-nodes]
 


Reply via email to