diff --git a/CHANGES.txt b/CHANGES.txt index e2575ff828..22f4759f69 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -108,6 +108,7 @@ * GossiperTest.testHasVersion3Nodes didn't take into account trunk version changes, fixed to rely on latest version (CASSANDRA-16651) * Update JNA library to 5.9.0 and snappy-java to version 1.1.8.4 (CASSANDRA-17040) Merged from 4.0: + * Reject snapshot names with special character (CASSANDRA-15297) * Fix ObjectSizes implementation and usages (CASSANDRA-17402) * Fix race condition bug during local session repair (CASSANDRA-17335) * Fix ignored streaming encryption settings in sstableloader (CASSANDRA-17367) diff --git a/src/java/org/apache/cassandra/tools/nodetool/Snapshot.java b/src/java/org/apache/cassandra/tools/nodetool/Snapshot.java index 1d899d318f..a4b432f686 100644 --- a/src/java/org/apache/cassandra/tools/nodetool/Snapshot.java +++ b/src/java/org/apache/cassandra/tools/nodetool/Snapshot.java @@ -17,13 +17,6 @@ */ package org.apache.cassandra.tools.nodetool; -import static com.google.common.collect.Iterables.toArray; -import static org.apache.cassandra.utils.Clock.Global.currentTimeMillis; -import static org.apache.commons.lang3.StringUtils.join; -import io.airlift.airline.Arguments; -import io.airlift.airline.Command; -import io.airlift.airline.Option; - import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; @@ -31,10 +24,18 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import io.airlift.airline.Arguments; +import io.airlift.airline.Command; +import io.airlift.airline.Option; import org.apache.cassandra.config.DurationSpec; +import org.apache.cassandra.io.util.File; import org.apache.cassandra.tools.NodeProbe; import org.apache.cassandra.tools.NodeTool.NodeToolCmd; +import static com.google.common.collect.Iterables.toArray; +import static org.apache.cassandra.utils.Clock.Global.currentTimeMillis; +import static org.apache.commons.lang3.StringUtils.join; + @Command(name = "snapshot", description = "Take a snapshot of specified keyspaces or a snapshot of the specified table") public class Snapshot extends NodeToolCmd { @@ -73,6 +74,10 @@ public class Snapshot extends NodeToolCmd options.put("ttl", d.toString()); } + if (!snapshotName.isEmpty() && snapshotName.contains(File.pathSeparator())) + { + throw new IOException("Snapshot name cannot contain " + File.pathSeparator()); + } // Create a separate path for kclist to avoid breaking of already existing scripts if (null != ktList && !ktList.isEmpty()) { diff --git a/test/unit/org/apache/cassandra/tools/nodetool/SnapshotTest.java b/test/unit/org/apache/cassandra/tools/nodetool/SnapshotTest.java new file mode 100644 index 0000000000..56d8422c8b --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/SnapshotTest.java @@ -0,0 +1,169 @@ +/* + * 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.cql3.CQLTester; +import org.apache.cassandra.io.util.File; +import org.apache.cassandra.tools.ToolRunner; + +import static org.apache.cassandra.tools.ToolRunner.invokeNodetool; +import static org.assertj.core.api.Assertions.assertThat; + +/**o + * Tests for the {@code nodetool snapshot} command + */ +public class SnapshotTest extends CQLTester +{ + @BeforeClass + public static void setup() throws Exception + { + startJMXServer(); + } + + @Test + public void testSnapshotAllKeyspaces() + { + ToolRunner.ToolResult tool = invokeNodetool("snapshot"); + tool.assertOnCleanExit(); + + assertThat(tool.getExitCode()).isEqualTo(0); + assertThat(tool.getStdout()).contains("Requested creating snapshot(s) for [all keyspaces]"); + } + + @Test + public void testSnapshotSpecifySingleKeyspace() + { + ToolRunner.ToolResult tool = invokeNodetool("snapshot", "system_schema"); + tool.assertOnCleanExit(); + + assertThat(tool.getExitCode()).isEqualTo(0); + assertThat(tool.getStdout()).contains("Requested creating snapshot(s) for [system_schema]"); + } + + @Test + public void testSnapshotSpecifyMultipleKeyspaces() + { + ToolRunner.ToolResult tool = invokeNodetool("snapshot", "system", "system_schema"); + tool.assertOnCleanExit(); + + assertThat(tool.getExitCode()).isEqualTo(0); + assertThat(tool.getStdout()).contains("Requested creating snapshot(s) for [system, system_schema]"); + } + + @Test + public void testSnapshotWithName() + { + ToolRunner.ToolResult tool = invokeNodetool("snapshot", "-t", "custom_snapshot_name"); + tool.assertOnCleanExit(); + + assertThat(tool.getExitCode()).isEqualTo(0); + assertThat(tool.getStdout()).contains("Requested creating snapshot(s) for [all keyspaces] with snapshot name [custom_snapshot_name] and options {skipFlush=false}"); + } + + @Test + public void testInvalidSnapshotName() + { + ToolRunner.ToolResult tool = invokeNodetool("snapshot", "-t", "invalid" + File.pathSeparator() + "name"); + assertThat(tool.getExitCode()).isEqualTo(2); + assertThat(tool.getStderr()).contains("Snapshot name cannot contain " + File.pathSeparator()); + } + + @Test + public void testSkipFlushOption() + { + ToolRunner.ToolResult tool = invokeNodetool("snapshot", "-t", "skip_flush", "-sf"); + tool.assertOnCleanExit(); + + assertThat(tool.getExitCode()).isEqualTo(0); + assertThat(tool.getStdout()).contains("Requested creating snapshot(s) for [all keyspaces] with snapshot name [skip_flush] and options {skipFlush=true}"); + } + + @Test + public void testTTLOption() + { + ToolRunner.ToolResult tool = invokeNodetool("snapshot", "-t", "ttl", "--ttl", "5h"); + tool.assertOnCleanExit(); + + assertThat(tool.getExitCode()).isEqualTo(0); + assertThat(tool.getStdout()).contains("Requested creating snapshot(s) for [all keyspaces] with snapshot name [ttl] and options {skipFlush=false, ttl=5h}"); + } + + @Test + public void testInvalidTTLOption() + { + ToolRunner.ToolResult tool = invokeNodetool("snapshot", "-t", "ttl", "--ttl", "infinity"); + assertThat(tool.getExitCode()).isEqualTo(2); + assertThat(tool.getStderr()).contains("Invalid duration: infinity"); + } + + @Test + public void testTableOption() + { + ToolRunner.ToolResult tool = invokeNodetool("snapshot", "-t", "table", "--table", "keyspaces", "system_schema"); + tool.assertOnCleanExit(); + + assertThat(tool.getExitCode()).isEqualTo(0); + assertThat(tool.getStdout()).contains("Requested creating snapshot(s) for [system_schema] with snapshot name [table] and options {skipFlush=false}"); + } + + @Test + public void testInvalidTableWithMultipleKeyspacesOption() + { + ToolRunner.ToolResult tool = invokeNodetool("snapshot", "-t", "table", "--table", "keyspaces", "system", "system_schema"); + assertThat(tool.getExitCode()).isEqualTo(2); + assertThat(tool.getStderr()).contains("When specifying the table for a snapshot, you must specify one and only one keyspace"); + } + + @Test + public void testInvalidTableWithKeyspaceTableListOption() + { + ToolRunner.ToolResult tool = invokeNodetool("snapshot", "-t", "table", "--table", "keyspaces", "-kt", "ks.table"); + assertThat(tool.getExitCode()).isEqualTo(2); + assertThat(tool.getStderr()).contains("When specifying the Keyspace table list (using -kt,--kt-list,-kc,--kc.list), you must not also specify keyspaces to snapshot"); + } + + @Test + public void testInvalidTableWithoutKeyspaceOption() + { + ToolRunner.ToolResult tool = invokeNodetool("snapshot", "-t", "table", "--table", "keyspaces"); + assertThat(tool.getExitCode()).isEqualTo(2); + assertThat(tool.getStderr()).contains("When specifying the table for a snapshot, you must specify one and only one keyspace"); + } + + @Test + public void testKeyspaceTableListOption() + { + ToolRunner.ToolResult tool = invokeNodetool("snapshot", "-t", "kt_option", "-kt", "system_schema.keyspaces,system_schema.aggregates"); + tool.assertOnCleanExit(); + + assertThat(tool.getExitCode()).isEqualTo(0); + assertThat(tool.getStdout()).contains("Requested creating snapshot(s) for [system_schema.keyspaces,system_schema.aggregates] with snapshot name [kt_option] and options {skipFlush=false}"); + } + + @Test + public void testInvalidKeyspacesWithKeyspaceTableListOptions() + { + ToolRunner.ToolResult tool = invokeNodetool("snapshot", "-kt", "ks.table", "ks"); + assertThat(tool.getExitCode()).isEqualTo(2); + assertThat(tool.getStderr()).contains("When specifying the Keyspace table list (using -kt,--kt-list,-kc,--kc.list), you must not also specify keyspaces to snapshot"); + } +}