zstan commented on code in PR #13577:
URL: https://github.com/apache/ignite/pull/13577#discussion_r4294110418


##########
modules/control-utility/src/test/java/org/apache/ignite/testsuites/IgniteControlUtilityTestSuite.java:
##########
@@ -74,6 +75,7 @@
     GridCommandHandlerCheckIndexesInlineSizeTest.class,
     GridCommandHandlerMetadataTest.class,
     GridCommandHandlerCheckIncrementalSnapshotTest.class,
+    GridCommandHandlerDeleteSnapshotTest.class,

Review Comment:
   Seems you need to remove it from here ?



##########
modules/core/src/main/java/org/apache/ignite/internal/management/snapshot/SnapshotDeleteCommand.java:
##########
@@ -0,0 +1,123 @@
+/*
+ * 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.snapshot;
+
+import java.util.Map;
+import java.util.UUID;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import 
org.apache.ignite.internal.processors.cache.persistence.snapshot.SnapshotDeleteProcess;
+import 
org.apache.ignite.internal.processors.cache.persistence.snapshot.SnapshotDeleteProcessResult;
+import 
org.apache.ignite.internal.processors.rollingupgrade.feature.CoreFeatureRegistry;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+/**
+ * Snapshot deletion command.
+ *
+ * @see CoreFeatureRegistry#SNAPSHOT_DELETE_FEATURE
+ * @see SnapshotDeleteProcess
+ */
+public class SnapshotDeleteCommand extends 
AbstractSnapshotCommand<SnapshotDeleteCommandArg, SnapshotDeleteProcessResult> {
+    /** */
+    public static final String DESC = "Deletes the snapshot and all its 
incremental snapshots from all online server nodes";
+
+    /** */
+    public static final String UNSURE_DELETION_PREF = "WARNING: the following 
nodes found snapshot data but might not " +
+        "remove it completely: ";
+
+    /** */
+    public static final String REMOVED_PREF = "Snapshot removal is completed 
on: ";
+
+    /** */
+    public static final String NODE_NOT_FOUND_PREF = "NOTE: the following 
nodes can't find any snapshot data, " +
+        "operation skipped: ";
+
+    /** */
+    public static final String NOT_FOUND = "Snapshot not found on available 
server nodes.";
+
+    /** */
+    public static final String MISSING_BASELINES = "WARNING: the snapshot's 
baseline nodes with the following consistent " +
+        "ids are missing in current cluster: ";

Review Comment:
   ```suggestion
           "IDs are missing in current cluster: ";
   ```



##########
modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerDeleteSnapshotTest.java:
##########
@@ -0,0 +1,240 @@
+/*
+ * 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 java.io.File;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collection;
+import org.apache.ignite.IgniteDataStreamer;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.management.snapshot.SnapshotDeleteCommand;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.junit.Test;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import static java.nio.file.Files.newDirectoryStream;
+import static org.apache.ignite.cluster.ClusterState.ACTIVE;
+import static 
org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_OK;
+import static 
org.apache.ignite.internal.processors.cache.persistence.snapshot.AbstractSnapshotSelfTest.snp;
+import static org.apache.ignite.testframework.GridTestUtils.waitForCondition;
+import static org.junit.Assume.assumeTrue;
+
+/** Test for the command '--snapshot delete'. */
+public class GridCommandHandlerDeleteSnapshotTest extends 
GridCommandHandlerAbstractTest {
+    /** Flag to add an extra server node after the cluster start and the 
snapshot creation. */
+    @Parameter(1)
+    public boolean extraServerNode;
+
+    /** */
+    @Parameter(2)
+    public boolean incremental;
+
+    /** */
+    @Parameter(3)
+    public boolean changeBaseline;
+
+    /** */
+    @Parameter(4)
+    public boolean customPath;
+
+    /** */
+    @Parameter(5)
+    public boolean separatedWorkDir;
+
+    /** */
+    @Parameters(name = 
"client={0},useExtraNode={1},inc={2},chBaseln={3},cstSnpPath={4},ownWorkDir={5}")

Review Comment:
   Appropriate param called :
   ```
       @Parameter(5)
       public boolean separatedWorkDir;
   ```
   but you name it as : `ownWorkDir` it hard to understand



##########
modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerDeleteSnapshotTest.java:
##########
@@ -0,0 +1,240 @@
+/*
+ * 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 java.io.File;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collection;
+import org.apache.ignite.IgniteDataStreamer;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.management.snapshot.SnapshotDeleteCommand;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.junit.Test;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import static java.nio.file.Files.newDirectoryStream;
+import static org.apache.ignite.cluster.ClusterState.ACTIVE;
+import static 
org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_OK;
+import static 
org.apache.ignite.internal.processors.cache.persistence.snapshot.AbstractSnapshotSelfTest.snp;
+import static org.apache.ignite.testframework.GridTestUtils.waitForCondition;
+import static org.junit.Assume.assumeTrue;
+
+/** Test for the command '--snapshot delete'. */
+public class GridCommandHandlerDeleteSnapshotTest extends 
GridCommandHandlerAbstractTest {
+    /** Flag to add an extra server node after the cluster start and the 
snapshot creation. */
+    @Parameter(1)
+    public boolean extraServerNode;
+
+    /** */
+    @Parameter(2)
+    public boolean incremental;
+
+    /** */
+    @Parameter(3)
+    public boolean changeBaseline;
+
+    /** */
+    @Parameter(4)
+    public boolean customPath;
+
+    /** */
+    @Parameter(5)
+    public boolean separatedWorkDir;
+
+    /** */
+    @Parameters(name = 
"client={0},useExtraNode={1},inc={2},chBaseln={3},cstSnpPath={4},ownWorkDir={5}")
+    public static Collection<?> parameters() {
+        return GridTestUtils.cartesianProduct(
+            commandHandlers(),
+            F.asList(false, true), // Use or not extra server node after the 
cluster start and the snapshot creation;

Review Comment:
   Common practise is to set a "." not ";" at the comment end, for example : 
   
org.apache.ignite.spi.communication.tcp.CommunicationConnectionPoolMetricsTest#params



##########
modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output:
##########
@@ -269,6 +269,13 @@ This utility can do the following commands:
   Get the status of the current snapshot operation:
     control.(sh|bat) --snapshot status
 
+  Deletes the snapshot and all its incremental snapshots from all online 
server nodes:
+    control.(sh|bat) --snapshot delete snapshot_name [--src path/to/snapshots]

Review Comment:
   change to common style : `[--src path]`



##########
modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output:
##########
@@ -269,6 +269,13 @@ This utility can do the following commands:
   Get the status of the current snapshot operation:
     control.(sh|bat) --snapshot status
 
+  Deletes the snapshot and all its incremental snapshots from all online 
server nodes:
+    control.(sh|bat) --snapshot delete snapshot_name [--src path/to/snapshots]

Review Comment:
   Change it consistently to common style : `[--src path]`



##########
modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotCheckProcess.java:
##########
@@ -487,12 +489,17 @@ private IgniteInternalFuture<SnapshotCheckResponse> 
prepareAndCheckMetas(UUID ig
             if (nodeStopping)
                 return new GridFinishedFuture<>(new NodeStoppingException("The 
node is stopping: " + kctx.localNodeId()));
 
-            ctx = contexts.computeIfAbsent(req.snapshotName(), snpName -> new 
SnapshotCheckContext(req));
+            ctx = 
contexts.computeIfAbsent(req.snapshotName().toLowerCase(Locale.ROOT), snpName 
-> new SnapshotCheckContext(req));

Review Comment:
   If we have no appropriate tests for such a case - no need to change it and 
just reject Copilot comment. If i`m right I suggest to revert this change and 
fill appropriate issue.



##########
modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/AbstractSnapshotSelfTest.java:
##########
@@ -814,6 +862,86 @@ public static void doSnapshotCancellationTest(
         assertEquals("Snapshot directory must be empty due to snapshot 
cancelled", 0, snpDir.list().length);
     }
 
+    /** Tests concurrent snapshot operation when snapshot deletion with the 
same name is active. */
+    protected void doConcurrentSnapshotOperationWhenDeletionIsActive(
+        ExRunnable prepareCluster,
+        ExRunnable concurrentOp,
+        @Nullable Function<Exception, Boolean> errValidator,
+        boolean rerunAtTheEnd
+    ) throws Exception {
+        CountDownLatch delProcInitLatch = new CountDownLatch(1);
+        CountDownLatch delProcProceedLatch = new CountDownLatch(1);
+
+        pluginProvider = new AbstractTestPluginProvider() {
+            @Override public String name() {
+                return "TestSnpMgrProvider";
+            }
+
+            @Override public <T> T createComponent(PluginContext ctx, Class<T> 
cls) {
+                if (IgniteSnapshotManager.class.isAssignableFrom(cls)) {
+                    return (T)new 
IgniteSnapshotManager(((IgniteEx)ctx.grid()).context()) {
+                        @Override public T2<Boolean, Boolean> 
deleteLocalSnapshot(
+                            SnapshotFileTree sft,
+                            boolean scoped,
+                            boolean ignoreErrs
+                        ) {
+                            delProcInitLatch.countDown();
+
+                            try {
+                                
assertTrue(delProcProceedLatch.await(getTestTimeout(), TimeUnit.MILLISECONDS));
+                            }
+                            catch (InterruptedException e) {
+                                throw new RuntimeException("Interrupted.", e);
+                            }
+
+                            return super.deleteLocalSnapshot(sft, scoped, 
ignoreErrs);
+                        }
+                    };
+                }
+
+                return super.createComponent(ctx, cls);
+            }
+        };
+
+        prepareCluster.run();
+
+        IgniteInternalFuture<SnapshotDeleteProcessResult> delFut = 
snp(grid(0)).deleteSnapshot(SNAPSHOT_NAME, null);

Review Comment:
   no take into account FS sensitivity, thus, as i say before - global flag 
looks very strange



##########
modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerDeleteSnapshotTest.java:
##########
@@ -0,0 +1,240 @@
+/*
+ * 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 java.io.File;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collection;
+import org.apache.ignite.IgniteDataStreamer;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.management.snapshot.SnapshotDeleteCommand;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.junit.Test;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import static java.nio.file.Files.newDirectoryStream;
+import static org.apache.ignite.cluster.ClusterState.ACTIVE;
+import static 
org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_OK;
+import static 
org.apache.ignite.internal.processors.cache.persistence.snapshot.AbstractSnapshotSelfTest.snp;
+import static org.apache.ignite.testframework.GridTestUtils.waitForCondition;
+import static org.junit.Assume.assumeTrue;
+
+/** Test for the command '--snapshot delete'. */
+public class GridCommandHandlerDeleteSnapshotTest extends 
GridCommandHandlerAbstractTest {
+    /** Flag to add an extra server node after the cluster start and the 
snapshot creation. */
+    @Parameter(1)
+    public boolean extraServerNode;
+
+    /** */
+    @Parameter(2)
+    public boolean incremental;
+
+    /** */
+    @Parameter(3)
+    public boolean changeBaseline;
+
+    /** */
+    @Parameter(4)
+    public boolean customPath;
+
+    /** */
+    @Parameter(5)
+    public boolean separatedWorkDir;
+
+    /** */
+    @Parameters(name = 
"client={0},useExtraNode={1},inc={2},chBaseln={3},cstSnpPath={4},ownWorkDir={5}")

Review Comment:
   Common style is to use spase between enumeration i.e. : `"client={0}, 
useExtraNode={1}`  and so on



##########
modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotRestoreProcess.java:
##########
@@ -659,6 +659,11 @@ private 
IgniteInternalFuture<SnapshotRestoreOperationResponse> prepare(UUID igno
             if (snpMgr.isSnapshotCreating())
                 throw new IgniteCheckedException(OP_REJECT_MSG + "A cluster 
snapshot operation is in progress.");
 
+            if (snpMgr.isSnapshotDeleting(req.snapshotName(), 
req.snapshotPath())) {
+                throw new IgniteCheckedException(OP_REJECT_MSG + "A snapshot 
'" + req.snapshotName() +

Review Comment:
   Message and exception if different, is it ok ? 
   
   from 
   `SnapshotCheckProcess#prepareAndCheckMetas`
   
   ```
           if 
(kctx.cache().context().snapshotMgr().isSnapshotDeleting(req.snapshotName(), 
req.snapshotPath())) {
               return new GridFinishedFuture<>(new 
IgniteIllegalStateException("Snapshot '" + req.snapshotName()
                   + "' is being deleted [req=" + req + ']'));
           }
   ```



##########
modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteClusterSnapshotSelfTest.java:
##########
@@ -615,6 +615,43 @@ public void testSnapshotExistsException() throws Exception 
{
         waitForEvents(EVT_CLUSTER_SNAPSHOT_STARTED, 
EVT_CLUSTER_SNAPSHOT_FAILED);
     }
 
+    /**
+     * Tests that snapshot create detects concurrent deletion, or detects 
still existing snapshot or successfully
+     * proceeds if snapshot already deleted.
+     */
+    @Test
+    public void testConcurrentSnapshotDeleteOperation() throws Exception {

Review Comment:
   This is not about concurrent snp deletion.
   
   Look in `doConcurrentSnapshotOperationWhenDeletionIsActive`
   u call : 
   `prepareCluster.run();`
   
   then call 
   
   ```
           IgniteInternalFuture<SnapshotDeleteProcessResult> delFut = 
snp(grid(0)).deleteSnapshot(SNAPSHOT_NAME, null);
   
           assertTrue(delProcInitLatch.await(getTestTimeout(), 
TimeUnit.MILLISECONDS));
   ```
   
   but this deletion is blocked on latch: 
   `assertTrue(delProcProceedLatch.await(getTestTimeout(), 
TimeUnit.MILLISECONDS));`
   
   after u call :
   `concurrentOp.run();`
   
   and where here we can found a concurrent deletion ?



##########
modules/core/src/test/java/org/apache/ignite/testsuites/IgniteSnapshotTestSuite8.java:
##########
@@ -48,6 +50,8 @@ public static List<Class<?>> suite(Collection<Class> 
ignoredTests) {
 
         GridTestUtils.addTestIfNeeded(suite, 
IgniteClusterSnapshotCheckTest.class, ignoredTests);
         GridTestUtils.addTestIfNeeded(suite, 
IgniteClusterSnapshotHandlerTest.class, ignoredTests);
+        GridTestUtils.addTestIfNeeded(suite, 
IgniteClusterSnapshotDeleteRollingUpgradeTest.class, ignoredTests);
+        GridTestUtils.addTestIfNeeded(suite, 
IgniteClusterSnapshotDeleteTest.class, ignoredTests);

Review Comment:
   Seems you need to remove both of them ?



##########
modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerDeleteSnapshotTest.java:
##########
@@ -0,0 +1,240 @@
+/*
+ * 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 java.io.File;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collection;
+import org.apache.ignite.IgniteDataStreamer;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.management.snapshot.SnapshotDeleteCommand;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.junit.Test;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import static java.nio.file.Files.newDirectoryStream;
+import static org.apache.ignite.cluster.ClusterState.ACTIVE;
+import static 
org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_OK;
+import static 
org.apache.ignite.internal.processors.cache.persistence.snapshot.AbstractSnapshotSelfTest.snp;
+import static org.apache.ignite.testframework.GridTestUtils.waitForCondition;
+import static org.junit.Assume.assumeTrue;
+
+/** Test for the command '--snapshot delete'. */
+public class GridCommandHandlerDeleteSnapshotTest extends 
GridCommandHandlerAbstractTest {
+    /** Flag to add an extra server node after the cluster start and the 
snapshot creation. */
+    @Parameter(1)
+    public boolean extraServerNode;
+
+    /** */
+    @Parameter(2)
+    public boolean incremental;
+
+    /** */
+    @Parameter(3)
+    public boolean changeBaseline;
+
+    /** */
+    @Parameter(4)
+    public boolean customPath;
+
+    /** */
+    @Parameter(5)
+    public boolean separatedWorkDir;
+
+    /** */
+    @Parameters(name = 
"client={0},useExtraNode={1},inc={2},chBaseln={3},cstSnpPath={4},ownWorkDir={5}")
+    public static Collection<?> parameters() {
+        return GridTestUtils.cartesianProduct(
+            commandHandlers(),
+            F.asList(false, true), // Use or not extra server node after the 
cluster start and the snapshot creation;
+            F.asList(false, true), // Add incremental snapshot;
+            F.asList(false, true), // Change baseline;
+            F.asList(false, true), // Use custom snapshot path;
+            F.asList(false, true) // Separated (own) work directory.
+        );
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        super.afterTest();
+
+        stopAllGrids();
+
+        cleanPersistenceDir();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        super.beforeTest();
+
+        /** Handy if test running is interrupted and {@link #afterTest()} 
isn't invoked. */
+        cleanPersistenceDir();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void cleanPersistenceDir() throws Exception {
+        super.cleanPersistenceDir();
+
+        // Also cleans separated snapshot working directories and custom 
snapshot paths.
+        try (DirectoryStream<Path> files = 
newDirectoryStream(Paths.get(U.defaultWorkDirectory()))) {
+            for (Path path : files)
+                U.delete(path);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String 
igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        if (separatedWorkDir)
+            cfg.setWorkDirectory(new File(U.defaultWorkDirectory(), 
igniteInstanceName).getAbsolutePath());
+
+        return cfg;
+    }
+
+    /** */
+    @Test
+    public void testSnapshotDelete() throws Exception {
+        // A custom snapshot path actually puts snapshots in a shared 
directory. This skews the results when dedicated
+        // work directories are set.
+        assumeTrue(!customPath || !separatedWorkDir);
+
+        int entriesCnt = 100;
+        int initNodes = 3;
+
+        walCompactionEnabled(incremental);
+
+        IgniteEx ig = (IgniteEx)startGridsMultiThreaded(initNodes);
+
+        if (changeBaseline) {
+            ig.cluster().baselineAutoAdjustEnabled(false);
+
+            ig.cluster().setBaselineTopology(ig.cluster().topologyVersion());
+        }
+
+        ig.cluster().state(ACTIVE);
+
+        createCacheAndPreload(ig, entriesCnt);
+
+        File cstSnpsRoot = customPath
+            ? new 
File(grid(0).context().pdsFolderResolver().fileTree().snapshotsRoot(), 
"ex_snapshots")
+            : null;
+        File snpDir = new File(customPath ? cstSnpsRoot : 
ig.context().pdsFolderResolver().fileTree().snapshotsRoot(), "testSnapshot");
+
+        snp(ig).createSnapshot("testSnapshot", customPath ? 
cstSnpsRoot.getAbsolutePath() : null, false, false)
+            .get(getTestTimeout());
+
+        if (incremental) {
+            for (int i = 0; i < 3; ++i) {
+                int dataIdx = entriesCnt + entriesCnt / 4 * i;
+
+                try (IgniteDataStreamer<Object, Object> streamer = 
ig.dataStreamer(DEFAULT_CACHE_NAME)) {
+                    for (int d = dataIdx; d < dataIdx + entriesCnt / 4; ++d)
+                        streamer.addData(d, d);
+                }
+
+                snp(ig).createSnapshot("testSnapshot", customPath ? 
cstSnpsRoot.getAbsolutePath() : null, true, false)
+                    .get(getTestTimeout());
+            }
+        }
+
+        String baselineGone = null;
+
+        // Optionally restarts with the same servers number, but changed 
baseline. The snapshot is kept on the same
+        // previous nodes independently of the baseline.
+        if (changeBaseline) {
+            baselineGone = grid(initNodes - 1).name();
+
+            ig.destroyCache(DEFAULT_CACHE_NAME);
+            awaitPartitionMapExchange();
+
+            stopAllGrids();
+
+            ig = (IgniteEx)startGridsMultiThreaded(initNodes - 1);
+
+            ig.cluster().setBaselineTopology(ig.cluster().topologyVersion());
+
+            startGrid(initNodes - 1);
+
+            assertEquals(initNodes - 1, 
ig.cluster().currentBaselineTopology().size());
+            assertEquals(initNodes, ig.cluster().nodes().size());
+        }
+
+        // Optionally adds extra server.
+        if (extraServerNode)
+            startGrid(initNodes);
+
+        injectTestSystemOut();
+
+        // Tests missing snapshot deletion.
+        if (customPath) {
+            assertEquals(EXIT_CODE_OK, 
execute(newCommandHandler(createTestLogger()), "--snapshot", "delete", "--src",
+                cstSnpsRoot.getAbsolutePath(), "wrongSnapshot"));
+
+            assertEquals(EXIT_CODE_OK, 
execute(newCommandHandler(createTestLogger()), "--snapshot", "delete", "--src",
+                cstSnpsRoot.getAbsolutePath() + "_wrongPath", "testSnapshot"));
+        }
+        else
+            assertEquals(EXIT_CODE_OK, 
execute(newCommandHandler(createTestLogger()), "--snapshot", "delete", 
"wrongSnapshot"));
+
+        String out = testOut.toString();
+
+        assertFalse(out.contains(SnapshotDeleteCommand.REMOVED_PREF));
+        assertFalse(out.contains(SnapshotDeleteCommand.NODE_NOT_FOUND_PREF));
+        assertTrue(out.contains(SnapshotDeleteCommand.NOT_FOUND));
+
+        testOut.reset();
+        assertTrue(testOut.toString().isEmpty());
+
+        if (customPath) {
+            assertEquals(EXIT_CODE_OK, 
execute(newCommandHandler(createTestLogger()), "--snapshot", "delete", "--src",
+                cstSnpsRoot.getAbsolutePath(), "testSnapshot"));
+        }
+        else
+            assertEquals(EXIT_CODE_OK, 
execute(newCommandHandler(createTestLogger()), "--snapshot", "delete", 
"testSnapshot"));
+
+        out = testOut.toString();
+
+        if (changeBaseline)
+            assertTrue(out.contains(SnapshotDeleteCommand.MISSING_BASELINES + 
baselineGone));
+
+        if (separatedWorkDir) {
+            // When the nodes use own separated work directory, we expect a 
strict result.
+            assertTrue(out.contains(SnapshotDeleteCommand.REMOVED_PREF));
+
+            if (extraServerNode)
+                
assertTrue(out.contains(SnapshotDeleteCommand.NODE_NOT_FOUND_PREF));
+        }
+        else {
+            // When nodes use a shared work directory, there is a race for the 
delete operation. One node can get faster
+            // than others and remove snapshot completely quickly. The others 
might not find snapshot files. We can be

Review Comment:
   completely quickly - errorneous form, change it



##########
modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerDeleteSnapshotTest.java:
##########
@@ -0,0 +1,240 @@
+/*
+ * 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 java.io.File;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collection;
+import org.apache.ignite.IgniteDataStreamer;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.management.snapshot.SnapshotDeleteCommand;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.junit.Test;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import static java.nio.file.Files.newDirectoryStream;
+import static org.apache.ignite.cluster.ClusterState.ACTIVE;
+import static 
org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_OK;
+import static 
org.apache.ignite.internal.processors.cache.persistence.snapshot.AbstractSnapshotSelfTest.snp;
+import static org.apache.ignite.testframework.GridTestUtils.waitForCondition;
+import static org.junit.Assume.assumeTrue;
+
+/** Test for the command '--snapshot delete'. */
+public class GridCommandHandlerDeleteSnapshotTest extends 
GridCommandHandlerAbstractTest {
+    /** Flag to add an extra server node after the cluster start and the 
snapshot creation. */
+    @Parameter(1)
+    public boolean extraServerNode;
+
+    /** */
+    @Parameter(2)
+    public boolean incremental;
+
+    /** */
+    @Parameter(3)
+    public boolean changeBaseline;
+
+    /** */
+    @Parameter(4)
+    public boolean customPath;
+
+    /** */
+    @Parameter(5)
+    public boolean separatedWorkDir;
+
+    /** */
+    @Parameters(name = 
"client={0},useExtraNode={1},inc={2},chBaseln={3},cstSnpPath={4},ownWorkDir={5}")
+    public static Collection<?> parameters() {
+        return GridTestUtils.cartesianProduct(
+            commandHandlers(),
+            F.asList(false, true), // Use or not extra server node after the 
cluster start and the snapshot creation;
+            F.asList(false, true), // Add incremental snapshot;
+            F.asList(false, true), // Change baseline;
+            F.asList(false, true), // Use custom snapshot path;
+            F.asList(false, true) // Separated (own) work directory.
+        );
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        super.afterTest();
+
+        stopAllGrids();
+
+        cleanPersistenceDir();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        super.beforeTest();
+
+        /** Handy if test running is interrupted and {@link #afterTest()} 
isn't invoked. */
+        cleanPersistenceDir();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void cleanPersistenceDir() throws Exception {
+        super.cleanPersistenceDir();
+
+        // Also cleans separated snapshot working directories and custom 
snapshot paths.
+        try (DirectoryStream<Path> files = 
newDirectoryStream(Paths.get(U.defaultWorkDirectory()))) {
+            for (Path path : files)
+                U.delete(path);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String 
igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        if (separatedWorkDir)
+            cfg.setWorkDirectory(new File(U.defaultWorkDirectory(), 
igniteInstanceName).getAbsolutePath());
+
+        return cfg;
+    }
+
+    /** */
+    @Test
+    public void testSnapshotDelete() throws Exception {
+        // A custom snapshot path actually puts snapshots in a shared 
directory. This skews the results when dedicated
+        // work directories are set.
+        assumeTrue(!customPath || !separatedWorkDir);
+
+        int entriesCnt = 100;
+        int initNodes = 3;
+
+        walCompactionEnabled(incremental);
+
+        IgniteEx ig = (IgniteEx)startGridsMultiThreaded(initNodes);
+
+        if (changeBaseline) {
+            ig.cluster().baselineAutoAdjustEnabled(false);
+
+            ig.cluster().setBaselineTopology(ig.cluster().topologyVersion());
+        }
+
+        ig.cluster().state(ACTIVE);
+
+        createCacheAndPreload(ig, entriesCnt);
+
+        File cstSnpsRoot = customPath
+            ? new 
File(grid(0).context().pdsFolderResolver().fileTree().snapshotsRoot(), 
"ex_snapshots")
+            : null;
+        File snpDir = new File(customPath ? cstSnpsRoot : 
ig.context().pdsFolderResolver().fileTree().snapshotsRoot(), "testSnapshot");
+
+        snp(ig).createSnapshot("testSnapshot", customPath ? 
cstSnpsRoot.getAbsolutePath() : null, false, false)
+            .get(getTestTimeout());
+
+        if (incremental) {
+            for (int i = 0; i < 3; ++i) {

Review Comment:
   ```suggestion
               for (int i = 0; i < initNodes; ++i) {
   ```



##########
modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotCheckProcess.java:
##########
@@ -131,7 +133,7 @@ private IgniteInternalFuture<?> 
reduceValidatePartsAndFinish(
             return new GridFinishedFuture<>();
 
         try {
-            contexts.remove(ctx.req.snapshotName());
+            contexts.remove(ctx.req.snapshotName().toLowerCase(Locale.ROOT));

Review Comment:
   
   If we have no appropriate tests for such a case - no need to change it and 
just reject Copilot comment. If i`m right I suggest to revert this change and 
fill appropriate issue.



##########
modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/EncryptedSnapshotTest.java:
##########
@@ -232,6 +232,8 @@ public void testValidatingSnapshotFailsWithNoEncryption() 
throws Exception {
 
             assertTrue(snpDir.renameTo(tmpSnpDir));
 
+            removeAllWorkingDirectories = false;

Review Comment:
   global abstract test flag for only one redefinition looks weird



##########
modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/AbstractSnapshotSelfTest.java:
##########
@@ -148,6 +153,9 @@ public abstract class AbstractSnapshotSelfTest extends 
GridCommonAbstractTest {
     protected static final int PAGE_SIZE =
         
IgniteSystemProperties.getInteger(IGNITE_DEFAULT_DATA_STORAGE_PAGE_SIZE, 
DFLT_PAGE_SIZE);
 
+    /** */
+    protected static boolean caseInsensitiveFs;

Review Comment:
   I don\`t understand why we need this flag globally ? 
   Look, in my vision all we need for FS sensitivity tests (in pseudo code) is 
one test like : 
   1. create 'snap'
   2. create 'SNAP' (in case insensitive FS - expect exception ?)
   3. delete 'snap'
   4. delete 'SNAP' (in case insensitive FS - expect exception ?)
   
   all other tests no need to know about such a flag in my opinion.



##########
modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/EncryptedSnapshotTest.java:
##########
@@ -232,6 +232,8 @@ public void testValidatingSnapshotFailsWithNoEncryption() 
throws Exception {
 
             assertTrue(snpDir.renameTo(tmpSnpDir));
 
+            removeAllWorkingDirectories = false;

Review Comment:
   I suggest to remove this flag and just call : `cleanPersistenceDir(false);`



##########
modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IncrementalSnapshotProcessor.java:
##########
@@ -138,7 +138,8 @@ void process(
 
                 WALRecord rec = walRec.getValue();
 
-                if (rec.type() == CLUSTER_SNAPSHOT && 
((ClusterSnapshotRecord)rec).clusterSnapshotName().equals(sft.name())) {
+                // A filesystem might not support the character case of 
directory or file name.
+                if (rec.type() == CLUSTER_SNAPSHOT && 
((ClusterSnapshotRecord)rec).clusterSnapshotName().equalsIgnoreCase(sft.name()))
 {

Review Comment:
   If we have no appropriate tests for such a case - no need to change it and 
just reject Copilot comment. If i\`m right I suggest to revert this change and 
fill appropriate issue.



##########
modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteClusterSnapshotRestoreSelfTest.java:
##########
@@ -333,6 +333,47 @@ public void testCreateSnapshotDuringRestore() throws 
Exception {
         assertCacheKeys(ignite.cache(DEFAULT_CACHE_NAME), CACHE_KEYS_RANGE);
     }
 
+    /** Tests that snapshot restore is declined when the same snapshot is 
being deleted. */
+    @Test
+    public void testConcurrentSnapshotDeleteAndRestoreOperations() throws 
Exception {
+        String snapshotName = caseInsensitiveFs ? SNAPSHOT_NAME.toLowerCase() 
: SNAPSHOT_NAME;
+
+        doConcurrentSnapshotOperationWhenDeletionIsActive(
+            () -> startGridsWithSnapshot(3, CACHE_KEYS_RANGE),
+            () -> snp(grid(2)).restoreSnapshot(snapshotName, null).get(),
+            e -> e.getMessage().contains("Snapshot '%s' is being 
deleted".formatted(snapshotName)),
+            true
+        );
+    }
+
+    /** */
+    @Test
+    public void 
testConcurrentSnapshotDeleteAndRestoreOperationsWithDifferentPath() throws 
Exception {
+        String snpPath = new File(U.defaultWorkDirectory(), 
"ex_snapshots").getAbsolutePath();
+
+        doConcurrentSnapshotOperationWhenDeletionIsActive(
+            () -> {
+                startGridsWithSnapshot(3, CACHE_KEYS_RANGE);
+
+                grid(0).createCache(DEFAULT_CACHE_NAME);
+
+                try (var ds = grid(0).dataStreamer(DEFAULT_CACHE_NAME)) {

Review Comment:
   **var** usage not acceptable



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to