diff --git a/CHANGES.txt b/CHANGES.txt index b363a96e7c..e6bea07a14 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 ccf5766745..10a5e30356 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 0af2f150db..061edf4978 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 the local sstable, that would work, but we can't." + +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 @@ -18,6 +23,7 @@ sstableverify
|-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 17b93d1aed..d7554dd5d3 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> 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] ", TOOL_NAME); + String usage = String.format("%s [options] 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 872a124b1b..0a610b3266 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 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 ea45bd10ee..9087f96296 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 15478c4233..fe739895db 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 7cabef5b44..3bb2825527 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 4a77a1c7df..2741ca46c0 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 b76ceae2fc..9d8f797982 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] \n" + + String help = "usage: sstableverify [options] 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 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(); @@ -163,12 +175,48 @@ 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 0000000000..47ca5febf2 --- /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 )] [(-p | --port )]\n" + + " [(-pp | --print-port)] [(-pw | --password )]\n" + + " [(-pwf | --password-file )]\n" + + " [(-u | --username )] verify\n" + + " [(-c | --check-version)] [(-d | --dfp)] [(-e | --extended-verify)]\n" + + " [(-f | --force)] [(-q | --quick)] [(-r | --rsc)] [(-t | --check-tokens)]\n" + + " [--] [ ...]\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 \n" + + " Node hostname or ip address\n" + + "\n" + + " -p , --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 \n" + + " Remote jmx agent password\n" + + "\n" + + " -pwf , --password-file \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 \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" + + " [ ...]\n" + + " The keyspace followed by one or many tables\n" + + "\n\n"; + + assertThat(tool.getStdout()).isEqualTo(help); + } +}