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

jmckenzie pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 10103d3  Add required -f option to nodetool verify
10103d3 is described below

commit 10103d353c3d63505cb119cda0e38e692cdb1809
Author: Josh McKenzie <[email protected]>
AuthorDate: Thu Sep 30 12:10:46 2021 -0400

    Add required -f option to nodetool verify
    
    Patch by Josh McKenzie; reviewed by Marcus Eriksson, Berenguer Blasi, and 
Caleb Rackliffe for CASSANDRA-17017
    
    Co-authored by Josh McKenzie <[email protected]>
    Co-authored by Chris Lohfink <[email protected]>
---
 CHANGES.txt                                        |   1 +
 NEWS.txt                                           |  15 ++-
 .../pages/tools/sstable/sstableverify.adoc         |   6 +
 .../apache/cassandra/tools/StandaloneVerifier.java |  22 +++-
 .../apache/cassandra/tools/nodetool/Verify.java    |  11 ++
 .../cassandra/distributed/test/NodeToolTest.java   |   2 +-
 test/unit/org/apache/cassandra/db/VerifyTest.java  |  16 +--
 .../apache/cassandra/tools/OfflineToolUtils.java   |   1 +
 .../tools/StandaloneVerifierOnSSTablesTest.java    |  10 +-
 .../cassandra/tools/StandaloneVerifierTest.java    | 104 ++++++++++++-----
 .../cassandra/tools/nodetool/VerifyTest.java       | 128 +++++++++++++++++++++
 11 files changed, 259 insertions(+), 57 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index b363a96..e6bea07 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 4.1
+ * Add required -f option to use nodetool verify and standalone sstableverify 
(CASSANDRA-17017)
  * Add support for UUID based sstable generation identifiers (CASSANDRA-17048)
  * Log largest memtable flush at info instead of debug (CASSANDRA-17472)
  * Add native transport rate limiter options to example cassandra.yaml, and 
expose metric for dispatch rate (CASSANDRA-17423)
diff --git a/NEWS.txt b/NEWS.txt
index ccf5766..10a5e30 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -83,11 +83,11 @@ New features
       mechanism (see below) and with some minor modifications to applications 
using LWTs.
       The new implementation may be enabled at any time by setting 
paxos_variant: v2, and disabled by setting to v1,
       and this alone will reduce the number of WAN round-trips by between one 
and two for reads, and one for writes.
-    - A new Paxos Repair mechanism has been introduced as part of Repair, that 
permits further reducing the number of WAN 
-      round-trips for write LWTs. This process may be manually executed for v1 
and is run automatically alongside normal 
-      repairs for v2. Once users are running regular repairs that include 
paxos repairs they are encouraged to set 
-      paxos_state_purging: repaired. Once this has been set across the 
cluster, users are encouraged to set their 
-      applications to supply a Commit consistency level of ANY with their LWT 
write operations, saving one additional WAN 
+    - A new Paxos Repair mechanism has been introduced as part of Repair, that 
permits further reducing the number of WAN
+      round-trips for write LWTs. This process may be manually executed for v1 
and is run automatically alongside normal
+      repairs for v2. Once users are running regular repairs that include 
paxos repairs they are encouraged to set
+      paxos_state_purging: repaired. Once this has been set across the 
cluster, users are encouraged to set their
+      applications to supply a Commit consistency level of ANY with their LWT 
write operations, saving one additional WAN
       round-trip. See upgrade notes below.
     - Warn/abort thresholds added to read queries notifying clients when these 
thresholds trigger (by
       emitting a client warning or aborting the query).  This feature is 
disabled by default, scheduled
@@ -164,6 +164,11 @@ Upgrading
           restore default commit consistency to ensure correctness.
         - Applications may now safely be updated to use ANY commit consistency 
level (or LOCAL_QUORUM, as preferred).
           Uncontended writes should now take 2 round-trips, and uncontended 
reads should typically take one round-trip.
+    - A required [f|force] flag has been added to both "nodetool verify" and 
the standalone "sstableverify" tools.
+      These tools have some subtleties and should not be used unless the 
operator is familiar with what they do
+      and do not do, as well as the edge cases associated with their use.
+      NOTE: ANY SCRIPTS THAT RELY ON sstableverify OR nodetool verify WILL 
STOP WORKING UNTIL MODIFIED.
+      Please see CASSANDRA-17017 for details: 
https://issues.apache.org/jira/browse/CASSANDRA-17017
 
 Deprecation
 -----------
diff --git a/doc/modules/cassandra/pages/tools/sstable/sstableverify.adoc 
b/doc/modules/cassandra/pages/tools/sstable/sstableverify.adoc
index 0af2f15..061edf4 100644
--- a/doc/modules/cassandra/pages/tools/sstable/sstableverify.adoc
+++ b/doc/modules/cassandra/pages/tools/sstable/sstableverify.adoc
@@ -8,6 +8,11 @@ Cassandra must be stopped before this tool is executed, or 
unexpected
 results will occur. Note: the script does not verify that Cassandra is
 stopped.
 
+== WARNING
+See CASSANDRA-9947 and CASSANDRA-17017 for discussion around risks with this 
tool. Specifically: "We mark sstables that fail verification as unrepaired, but 
that's not going to do what you think.  What it means is that the local node 
will use that sstable in the next repair, but other nodes will not. So all 
we'll end up doing is streaming whatever data we can read from it, to the other 
replicas.  If we could magically mark whatever sstables correspond on the 
remote nodes, to the data in  [...]
+
+This tool requires the use of a -f or --force flag to indicate that the user 
understands the risks and would like to attempt its usage anyway.
+
 == Usage
 
 sstableverify <options> <keyspace> <table>
@@ -18,6 +23,7 @@ sstableverify <options> <keyspace> <table>
 |-e, --extended |extended verification
 |-h, --help |display this help message
 |-v, --verbose |verbose output
+|-f, --force |allow use of tool (see CASSANDRA-17017 for risks)
 |===
 
 == Basic Verification
diff --git a/src/java/org/apache/cassandra/tools/StandaloneVerifier.java 
b/src/java/org/apache/cassandra/tools/StandaloneVerifier.java
index 17b93d1..d7554dd 100644
--- a/src/java/org/apache/cassandra/tools/StandaloneVerifier.java
+++ b/src/java/org/apache/cassandra/tools/StandaloneVerifier.java
@@ -62,11 +62,18 @@ public class StandaloneVerifier
     private static final String CHECK_VERSION = "check_version";
     private static final String MUTATE_REPAIR_STATUS = "mutate_repair_status";
     private static final String QUICK = "quick";
+    private static final String FORCE = "force";
     private static final String TOKEN_RANGE = "token_range";
 
     public static void main(String args[])
     {
         Options options = Options.parseArgs(args);
+        if (!options.force)
+        {
+            System.err.println("verify will not run without -f or --force. See 
CASSANDRA-17017 for details.");
+            Options.printUsage(Options.getCmdLineOptions());
+            System.exit(1);
+        }
         initDatabaseDescriptorForTool();
 
         System.out.println("sstableverify using the following options: " + 
options);
@@ -165,6 +172,7 @@ public class StandaloneVerifier
         public boolean checkVersion;
         public boolean mutateRepairStatus;
         public boolean quick;
+        public boolean force;
         public Collection<Range<Token>> tokens;
 
         private Options(String keyspaceName, String cfName)
@@ -190,8 +198,8 @@ public class StandaloneVerifier
                 String[] args = cmd.getArgs();
                 if (args.length != 2)
                 {
-                    String msg = args.length < 2 ? "Missing arguments" : "Too 
many arguments";
-                    System.err.println(msg);
+                    String prefix = args.length < 2 ? "Missing" : "Too many";
+                    System.err.println(prefix + " arguments");
                     printUsage(options);
                     System.exit(1);
                 }
@@ -207,6 +215,7 @@ public class StandaloneVerifier
                 opts.checkVersion = cmd.hasOption(CHECK_VERSION);
                 opts.mutateRepairStatus = cmd.hasOption(MUTATE_REPAIR_STATUS);
                 opts.quick = cmd.hasOption(QUICK);
+                opts.force = cmd.hasOption(FORCE);
 
                 if (cmd.hasOption(TOKEN_RANGE))
                 {
@@ -260,17 +269,24 @@ public class StandaloneVerifier
             options.addOption("c",  CHECK_VERSION,         "make sure sstables 
are the latest version");
             options.addOption("r",  MUTATE_REPAIR_STATUS,  "don't mutate 
repair status");
             options.addOption("q",  QUICK,                 "do a quick check, 
don't read all data");
+            options.addOption("f",  FORCE,                 "force verify - see 
CASSANDRA-17017");
             options.addOptionList("t", TOKEN_RANGE, "range", "long token range 
of the format left,right. This may be provided multiple times to define 
multiple different ranges");
             return options;
         }
 
         public static void printUsage(CmdLineOptions options)
         {
-            String usage = String.format("%s [options] <keyspace> 
<column_family>", TOOL_NAME);
+            String usage = String.format("%s [options] <keyspace> 
<column_family> force", TOOL_NAME);
             StringBuilder header = new StringBuilder();
             header.append("--\n");
             header.append("Verify the sstable for the provided table." );
             header.append("\n--\n");
+            header.append("NOTE: There are significant risks associated with 
using this tool; it likely doesn't do what " +
+                          "you expect and there are known edge cases. You must 
provide a -f or --force argument in " +
+                          "order to allow usage of the tool -> see 
CASSANDRA-9947 and CASSANDRA-17017 for known risks.\n");
+            
header.append("https://issues.apache.org/jira/browse/CASSANDRA-9947\n";);
+            
header.append("https://issues.apache.org/jira/browse/CASSANDRA-17017";);
+            header.append("\n--\n");
             header.append("Options are:");
             new HelpFormatter().printHelp(usage, header.toString(), options, 
"");
         }
diff --git a/src/java/org/apache/cassandra/tools/nodetool/Verify.java 
b/src/java/org/apache/cassandra/tools/nodetool/Verify.java
index 872a124..0a610b3 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/Verify.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/Verify.java
@@ -44,6 +44,11 @@ public class Verify extends NodeToolCmd
             description = "Also check that all sstables are the latest 
version")
     private boolean checkVersion = false;
 
+    @Option(title = "override-disable",
+    name = {"-f", "--force"},
+    description = "Override disabling of verify tool - see CASSANDRA-9947 for 
caveats")
+    private boolean overrideDisable = false;
+
     @Option(title = "dfp",
             name = {"-d", "--dfp"},
             description = "Invoke the disk failure policy if a corrupt sstable 
is found")
@@ -68,6 +73,12 @@ public class Verify extends NodeToolCmd
     public void execute(NodeProbe probe)
     {
         PrintStream out = probe.output().out;
+        if (!overrideDisable)
+        {
+            out.println("verify is disabled unless a [-f|--force] override 
flag is provided. See CASSANDRA-9947 and CASSANDRA-17017 for details.");
+            System.exit(1);
+        }
+
         List<String> keyspaces = parseOptionalKeyspace(args, probe);
         String[] tableNames = parseOptionalTables(args);
 
diff --git 
a/test/distributed/org/apache/cassandra/distributed/test/NodeToolTest.java 
b/test/distributed/org/apache/cassandra/distributed/test/NodeToolTest.java
index ea45bd1..9087f96 100644
--- a/test/distributed/org/apache/cassandra/distributed/test/NodeToolTest.java
+++ b/test/distributed/org/apache/cassandra/distributed/test/NodeToolTest.java
@@ -73,7 +73,7 @@ public class NodeToolTest extends TestBaseImpl
     public void testNodetoolSystemExit()
     {
         // Verify currently calls System.exit, this test uses that knowlege to 
test System.exit behavior in jvm-dtest
-        NODE.nodetoolResult("verify", "--check-tokens")
+        NODE.nodetoolResult("verify", "--check-tokens", "--force")
             .asserts()
             .failure()
             .stdoutContains("Token verification requires --extended-verify");
diff --git a/test/unit/org/apache/cassandra/db/VerifyTest.java 
b/test/unit/org/apache/cassandra/db/VerifyTest.java
index 15478c4..fe73989 100644
--- a/test/unit/org/apache/cassandra/db/VerifyTest.java
+++ b/test/unit/org/apache/cassandra/db/VerifyTest.java
@@ -26,7 +26,6 @@ import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.UUID;
 import java.util.concurrent.ExecutionException;
 import java.util.zip.CRC32;
 import java.util.zip.CheckedInputStream;
@@ -54,6 +53,7 @@ import org.apache.cassandra.io.FSWriteError;
 import org.apache.cassandra.io.sstable.Component;
 import org.apache.cassandra.io.sstable.CorruptSSTableException;
 import org.apache.cassandra.io.sstable.format.SSTableReader;
+import org.apache.cassandra.io.util.File;
 import org.apache.cassandra.io.util.FileInputStreamPlus;
 import org.apache.cassandra.io.util.FileUtils;
 import org.apache.cassandra.io.util.RandomAccessReader;
@@ -63,21 +63,7 @@ import org.apache.cassandra.schema.CompressionParams;
 import org.apache.cassandra.schema.KeyspaceParams;
 import org.apache.cassandra.service.StorageService;
 import org.apache.cassandra.utils.ByteBufferUtil;
-import org.apache.commons.lang3.StringUtils;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import java.io.*;
-import java.net.UnknownHostException;
-import java.nio.file.Files;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.ExecutionException;
-import java.util.zip.CRC32;
-import java.util.zip.CheckedInputStream;
 
-import org.apache.cassandra.io.util.File;
 import static org.apache.cassandra.SchemaLoader.counterCFMD;
 import static org.apache.cassandra.SchemaLoader.createKeyspace;
 import static org.apache.cassandra.SchemaLoader.loadSchema;
diff --git a/test/unit/org/apache/cassandra/tools/OfflineToolUtils.java 
b/test/unit/org/apache/cassandra/tools/OfflineToolUtils.java
index 7cabef5..3bb2825 100644
--- a/test/unit/org/apache/cassandra/tools/OfflineToolUtils.java
+++ b/test/unit/org/apache/cassandra/tools/OfflineToolUtils.java
@@ -71,6 +71,7 @@ public abstract class OfflineToolUtils
     "ObjectCleanerThread",
     "process reaper",  // spawned by the jvm when executing external processes
                        // and may still be active when we check
+    "Attach Listener", // spawned in intellij IDEA
     };
 
     public void assertNoUnexpectedThreadsStarted(String[] optionalThreadNames)
diff --git 
a/test/unit/org/apache/cassandra/tools/StandaloneVerifierOnSSTablesTest.java 
b/test/unit/org/apache/cassandra/tools/StandaloneVerifierOnSSTablesTest.java
index 4a77a1c..2741ca4 100644
--- a/test/unit/org/apache/cassandra/tools/StandaloneVerifierOnSSTablesTest.java
+++ b/test/unit/org/apache/cassandra/tools/StandaloneVerifierOnSSTablesTest.java
@@ -83,7 +83,7 @@ public class StandaloneVerifierOnSSTablesTest extends 
OfflineToolUtils
 
         createAndPopulateTable(keyspaceName, workingTable, x -> {});
 
-        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
keyspaceName, workingTable, "-c");
+        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
keyspaceName, workingTable, "--force", "-c");
         assertEquals(0, tool.getExitCode());
         tool.assertOnCleanExit();
     }
@@ -103,7 +103,7 @@ public class StandaloneVerifierOnSSTablesTest extends 
OfflineToolUtils
             }
         });
 
-        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
keyspace, tableName, "-c");
+        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
keyspace, tableName, "-f", "-c");
 
         assertEquals(1, tool.getExitCode());
         Assertions.assertThat(tool.getStdout()).contains("is not the latest 
version, run upgradesstables");
@@ -117,7 +117,7 @@ public class StandaloneVerifierOnSSTablesTest extends 
OfflineToolUtils
 
         createAndPopulateTable(keyspaceName, workingTable, x -> {});
 
-        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
keyspaceName, workingTable);
+        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
keyspaceName, workingTable, "--force");
         assertEquals(0, tool.getExitCode());
         tool.assertOnCleanExit();
     }
@@ -136,7 +136,7 @@ public class StandaloneVerifierOnSSTablesTest extends 
OfflineToolUtils
             }
         });
 
-        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
keyspaceName, corruptStatsTable);
+        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
keyspaceName, corruptStatsTable, "-f");
 
         assertEquals(1, tool.getExitCode());
         Assertions.assertThat(tool.getStderr()).contains("Error Loading", 
corruptStatsTable);
@@ -161,7 +161,7 @@ public class StandaloneVerifierOnSSTablesTest extends 
OfflineToolUtils
             }
         });
 
-        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
keyspaceName, corruptDataTable);
+        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
keyspaceName, corruptDataTable, "--force");
         assertEquals(1, tool.getExitCode());
         Assertions.assertThat(tool.getStdout()).contains("Invalid SSTable", 
corruptDataTable);
     }
diff --git a/test/unit/org/apache/cassandra/tools/StandaloneVerifierTest.java 
b/test/unit/org/apache/cassandra/tools/StandaloneVerifierTest.java
index b76ceae..9d8f797 100644
--- a/test/unit/org/apache/cassandra/tools/StandaloneVerifierTest.java
+++ b/test/unit/org/apache/cassandra/tools/StandaloneVerifierTest.java
@@ -42,15 +42,23 @@ public class StandaloneVerifierTest extends OfflineToolUtils
     {
         // If you added, modified options or help, please update docs if 
necessary
         ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
"-h");
-        String help = "usage: sstableverify [options] <keyspace> 
<column_family>\n" + 
+        String help = "usage: sstableverify [options] <keyspace> 
<column_family> force\n" +
                        "--\n" + 
                        "Verify the sstable for the provided table.\n" + 
-                       "--\n" + 
-                       "Options are:\n" + 
+                       "--\n" +
+                       "NOTE: There are significant risks associated with 
using this tool; it\n" +
+                       "likely doesn't do what you expect and there are known 
edge cases. You must\n" +
+                       "provide a -f or --force argument in order to allow 
usage of the tool ->\n" +
+                       "see CASSANDRA-9947 and CASSANDRA-17017 for known 
risks.\n" +
+                       
"https://issues.apache.org/jira/browse/CASSANDRA-9947\n"; +
+                       
"https://issues.apache.org/jira/browse/CASSANDRA-17017\n"; +
+                       "--\n" +
+                       "Options are:\n" +
                        " -c,--check_version          make sure sstables are 
the latest version\n" + 
                        "    --debug                  display stack traces\n" + 
-                       " -e,--extended               extended verification\n" 
+ 
-                       " -h,--help                   display this help 
message\n" + 
+                       " -e,--extended               extended verification\n" +
+                       " -f,--force                  force verify - see 
CASSANDRA-17017\n" +
+                       " -h,--help                   display this help 
message\n" +
                        " -q,--quick                  do a quick check, don't 
read all data\n" + 
                        " -r,--mutate_repair_status   don't mutate repair 
status\n" + 
                        " -t,--token_range <range>    long token range of the 
format left,right.\n" + 
@@ -62,7 +70,7 @@ public class StandaloneVerifierTest extends OfflineToolUtils
     @Test
     public void testWrongArgFailsAndPrintsHelp()
     {
-        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
"--debugwrong", "system_schema", "tables");
+        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
"--debugwrong", "system_schema", "tables", "-f");
         assertThat(tool.getStdout(), 
CoreMatchers.containsStringIgnoringCase("usage:"));
         assertThat(tool.getCleanedStderr(), 
CoreMatchers.containsStringIgnoringCase("Unrecognized option"));
         assertEquals(1, tool.getExitCode());
@@ -71,24 +79,24 @@ public class StandaloneVerifierTest extends OfflineToolUtils
     @Test
     public void testDefaultCall()
     {
-        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
"system_schema", "tables");
-        assertThat(tool.getStdout(), 
CoreMatchers.containsStringIgnoringCase("using the following options"));
-        Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
-        assertEquals(0, tool.getExitCode());
-        assertCorrectEnvPostTest();
-        tool.assertOnCleanExit();
-
+        Arrays.asList("-f", "--force").forEach(arg -> {
+            ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
"system_schema", "tables", arg);
+            assertThat(tool.getStdout(), 
CoreMatchers.containsStringIgnoringCase("using the following options"));
+            Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
+            assertEquals(0, tool.getExitCode());
+            assertCorrectEnvPostTest();
+            tool.assertOnCleanExit();
+        });
     }
 
     @Test
     public void testDebugArg()
     {
-        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
"--debug", "system_schema", "tables");
+        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
"--debug", "system_schema", "tables", "-f");
         assertThat(tool.getStdout(), 
CoreMatchers.containsStringIgnoringCase("debug=true"));
         Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
         assertCorrectEnvPostTest();
         tool.assertOnCleanExit();
-
     }
 
     @Test
@@ -96,9 +104,10 @@ public class StandaloneVerifierTest extends OfflineToolUtils
     {
         Arrays.asList("-e", "--extended").forEach(arg -> {
             ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
-                                                       arg,
-                                                       "system_schema",
-                                                       "tables");
+                                                     arg,
+                                                     "system_schema",
+                                                     "tables",
+                                                     "--force");
             assertThat(tool.getStdout(), 
CoreMatchers.containsStringIgnoringCase("extended=true"));
             Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
             assertCorrectEnvPostTest();
@@ -111,9 +120,10 @@ public class StandaloneVerifierTest extends 
OfflineToolUtils
     {
         Arrays.asList("-q", "--quick").forEach(arg -> {
             ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
-                                                       arg,
-                                                       "system_schema",
-                                                       "tables");
+                                                     arg,
+                                                     "system_schema",
+                                                     "tables",
+                                                     "-f");
             assertThat(tool.getStdout(), 
CoreMatchers.containsStringIgnoringCase("quick=true"));
             Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
             assertCorrectEnvPostTest();
@@ -126,9 +136,10 @@ public class StandaloneVerifierTest extends 
OfflineToolUtils
     {
         Arrays.asList("-r", "--mutate_repair_status").forEach(arg -> {
             ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
-                                                       arg,
-                                                       "system_schema",
-                                                       "tables");
+                                                     arg,
+                                                     "system_schema",
+                                                     "tables",
+                                                     "--force");
             assertThat(tool.getStdout(), 
CoreMatchers.containsStringIgnoringCase("mutateRepairStatus=true"));
             Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
             assertCorrectEnvPostTest();
@@ -153,9 +164,10 @@ public class StandaloneVerifierTest extends 
OfflineToolUtils
     {
         Arrays.asList("-v", "--verbose").forEach(arg -> {
             ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
-                                                       arg,
-                                                       "system_schema",
-                                                       "tables");
+                                                     arg,
+                                                     "system_schema",
+                                                     "tables",
+                                                     "-f");
             assertThat(tool.getStdout(), 
CoreMatchers.containsStringIgnoringCase("verbose=true"));
             Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
             assertCorrectEnvPostTest();
@@ -164,11 +176,47 @@ public class StandaloneVerifierTest extends 
OfflineToolUtils
     }
 
     @Test
+    public void testTooFewArgs()
+    {
+        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
"one_arg");
+        assertThat(tool.getStdout(), 
CoreMatchers.containsStringIgnoringCase("usage:"));
+        assertThat(tool.getCleanedStderr(), 
CoreMatchers.containsStringIgnoringCase("Missing arguments"));
+        assertEquals(1, tool.getExitCode());
+    }
+
+    @Test
     public void testTooManyArgs()
     {
-        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
"another arg", "system_schema", "tables");
+        ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class, 
"one_arg", "two_arg", "toomany_arg");
         assertThat(tool.getStdout(), 
CoreMatchers.containsStringIgnoringCase("usage:"));
         assertThat(tool.getCleanedStderr(), 
CoreMatchers.containsStringIgnoringCase("Too many arguments"));
         assertEquals(1, tool.getExitCode());
     }
+
+    @Test
+    public void testFailsWithoutForce()
+    {
+        Arrays.asList("-r", "--mutate_repair_status").forEach(arg -> {
+            ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
+                                                     arg,
+                                                     "system_schema",
+                                                     "tables",
+                                                     "debug");
+            assertThat(tool.getStdout(), 
CoreMatchers.containsStringIgnoringCase("usage:"));
+            assertEquals(1, tool.getExitCode());
+        });
+    }
+
+    @Test
+    public void testBadForceArgument()
+    {
+        Arrays.asList("bf", "badforce", "garbage", "forrce").forEach(arg -> {
+            ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
+                                                     "system_schema",
+                                                     "tables",
+                                                     arg);
+            assertThat(tool.getStdout(), 
CoreMatchers.containsStringIgnoringCase("usage"));
+            assertEquals(1, tool.getExitCode());
+        });
+    }
 }
diff --git a/test/unit/org/apache/cassandra/tools/nodetool/VerifyTest.java 
b/test/unit/org/apache/cassandra/tools/nodetool/VerifyTest.java
new file mode 100644
index 0000000..47ca5fe
--- /dev/null
+++ b/test/unit/org/apache/cassandra/tools/nodetool/VerifyTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.cassandra.tools.nodetool;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import org.apache.cassandra.SchemaLoader;
+import org.apache.cassandra.auth.AuthTestUtils;
+import org.apache.cassandra.auth.AuthenticatedUser;
+import org.apache.cassandra.cql3.CQLTester;
+import org.apache.cassandra.tools.ToolRunner;
+
+import static org.apache.cassandra.auth.AuthTestUtils.ROLE_A;
+import static org.apache.cassandra.auth.AuthTestUtils.ROLE_B;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class VerifyTest extends CQLTester
+{
+    @BeforeClass
+    public static void setup() throws Exception
+    {
+        SchemaLoader.prepareServer();
+        AuthTestUtils.LocalCassandraRoleManager roleManager = new 
AuthTestUtils.LocalCassandraRoleManager();
+        SchemaLoader.setupAuth(roleManager,
+                               new AuthTestUtils.LocalPasswordAuthenticator(),
+                               new AuthTestUtils.LocalCassandraAuthorizer(),
+                               new 
AuthTestUtils.LocalCassandraNetworkAuthorizer());
+
+        roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_A, 
AuthTestUtils.getLoginRoleOptions());
+        roleManager.createRole(AuthenticatedUser.SYSTEM_USER, ROLE_B, 
AuthTestUtils.getLoginRoleOptions());
+
+        startJMXServer();
+    }
+
+    /**
+     * We calcify the help file as last seen as a "trigger" to notify a 
developer that, upon addition of a new flag or
+     * functionality to this tool option, they will need to update help output 
and/or documentation as necessary.
+     */
+    @Test
+    public void testMaybeChangeDocs()
+    {
+        // If you added, modified options or help, please update docs if 
necessary
+        ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", 
"verify");
+        tool.assertOnCleanExit();
+
+        String help =
+        "NAME\n" +
+        "        nodetool verify - Verify (check data checksum for) one or 
more tables\n" +
+        "\n" +
+        "SYNOPSIS\n" +
+        "        nodetool [(-h <host> | --host <host>)] [(-p <port> | --port 
<port>)]\n" +
+        "                [(-pp | --print-port)] [(-pw <password> | --password 
<password>)]\n" +
+        "                [(-pwf <passwordFilePath> | --password-file 
<passwordFilePath>)]\n" +
+        "                [(-u <username> | --username <username>)] verify\n" +
+        "                [(-c | --check-version)] [(-d | --dfp)] [(-e | 
--extended-verify)]\n" +
+        "                [(-f | --force)] [(-q | --quick)] [(-r | --rsc)] [(-t 
| --check-tokens)]\n" +
+        "                [--] [<keyspace> <tables>...]\n" +
+        "\n" +
+
+        "OPTIONS\n" +
+        "        -c, --check-version\n" +
+        "            Also check that all sstables are the latest version\n" +
+        "\n" +
+        "        -d, --dfp\n" +
+        "            Invoke the disk failure policy if a corrupt sstable is 
found\n" +
+        "\n" +
+        "        -e, --extended-verify\n" +
+        "            Verify each cell data, beyond simply checking sstable 
checksums\n" +
+        "\n" +
+        "        -f, --force\n" +
+        "            Override disabling of verify tool - see CASSANDRA-9947 
for caveats\n" +
+        "\n" +
+        "        -h <host>, --host <host>\n" +
+        "            Node hostname or ip address\n" +
+        "\n" +
+        "        -p <port>, --port <port>\n" +
+        "            Remote jmx agent port number\n" +
+        "\n" +
+        "        -pp, --print-port\n" +
+        "            Operate in 4.0 mode with hosts disambiguated by port 
number\n" +
+        "\n" +
+        "        -pw <password>, --password <password>\n" +
+        "            Remote jmx agent password\n" +
+        "\n" +
+        "        -pwf <passwordFilePath>, --password-file 
<passwordFilePath>\n" +
+        "            Path to the JMX password file\n" +
+        "\n" +
+        "        -q, --quick\n" +
+        "            Do a quick check - avoid reading all data to verify 
checksums\n" +
+        "\n" +
+        "        -r, --rsc\n" +
+        "            Mutate the repair status on corrupt sstables\n" +
+        "\n" +
+        "        -t, --check-tokens\n" +
+        "            Verify that all tokens in sstables are owned by this 
node\n" +
+        "\n" +
+        "        -u <username>, --username <username>\n" +
+        "            Remote jmx agent username\n" +
+        "\n" +
+        "        --\n" +
+        "            This option can be used to separate command-line options 
from the\n" +
+        "            list of argument, (useful when arguments might be 
mistaken for\n" +
+        "            command-line options\n" +
+        "\n" +
+        "        [<keyspace> <tables>...]\n" +
+        "            The keyspace followed by one or many tables\n" +
+        "\n\n";
+
+        assertThat(tool.getStdout()).isEqualTo(help);
+    }
+}

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to