Skip to content

Commit 2b3258f

Browse files
committed
Merge branch 'cassandra-6.0' into trunk
2 parents 8f8c4ec + 26d6b4f commit 2b3258f

6 files changed

Lines changed: 83 additions & 25 deletions

File tree

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* Allow nodetool garbagecollect to take a user defined list of SSTables (CASSANDRA-16767)
55
* Add a guardrail for misprepared statements (CASSANDRA-21139)
66
Merged from 6.0:
7+
* Offline nodetool commands should not print network options in help (CASSANDRA-20876)
78
* Defer creation of the system_cluster_metadata keyspace until CMS initialization (CASSANDRA-21477)
89
* Support direct I/O for background SSTable writes (CASSANDRA-21134)
910
* Relax assertion on partitioner instances in SinglePartitionReadCommand (CASSANDRA-21251)

src/java/org/apache/cassandra/tools/nodetool/History.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import picocli.CommandLine.Option;
3131

3232
@Command(name = "history", description = "Print previously executed nodetool commands")
33-
public class History extends AbstractCommand
33+
public class History extends AbstractCommand implements LocalCommand
3434
{
3535
@Option(paramLabel = "commands",
3636
names = { "-n", "--num", "--number-of-commands" },
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.cassandra.tools.nodetool;
20+
21+
/**
22+
* Marker interface for nodetool commands that are known to run entirely locally,
23+
* without requiring a JMX connection to the node.
24+
* <p>
25+
* Commands that implement this interface will not display JMX connection options
26+
* (host, port, username, password, etc.) in their help output, since those options
27+
* are not applicable to local commands.
28+
* <p>
29+
* Note: this is different from commands whose locality is determined at runtime
30+
* (e.g. {@link Sjk}), which override {@link AbstractCommand#shouldConnect()} instead.
31+
*/
32+
public interface LocalCommand
33+
{
34+
}

src/java/org/apache/cassandra/tools/nodetool/layout/CassandraCliHelpLayout.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535

3636
import org.apache.cassandra.tools.nodetool.CommandUtils;
3737
import org.apache.cassandra.tools.nodetool.JmxConnect;
38+
import org.apache.cassandra.tools.nodetool.LocalCommand;
3839
import org.apache.cassandra.tools.nodetool.NodetoolCommand;
3940
import org.apache.cassandra.utils.Pair;
4041

@@ -65,9 +66,11 @@
6566
* Help factory for the Cassandra nodetool to generate the help output in the format
6667
* of the airline help output, which is used as the default layout for the Cassandra nodetool.
6768
* <p>
68-
* Note, that JMX connect options are always shown in the help output and are not hidden. The
69-
* {@link JmxConnect} class is used to connect to a C* node via JMX, but the opttions are not
70-
* part of the command hierarchy to allow reusage of the commands in other contexts.
69+
* JMX connect options (host, port, username, password, etc.) are shown in the help output for
70+
* all commands that require a JMX connection. Commands that implement {@link org.apache.cassandra.tools.nodetool.LocalCommand}
71+
* are known at the metadata level to run locally, and their help output suppresses the JMX options.
72+
* Commands whose locality is only determined at runtime (e.g. {@link org.apache.cassandra.tools.nodetool.Sjk})
73+
* continue to show JMX options since the decision cannot be made statically.
7174
*/
7275
public class CassandraCliHelpLayout extends CommandLine.Help
7376
{
@@ -257,6 +260,10 @@ private static List<CommandLine.Model.OptionSpec> parentCommandOptionsWithJmxOpt
257260
if (commandSpec.helpCommand())
258261
return Collections.emptyList();
259262

263+
// If the command is a LocalCommand, it runs locally and does not need JMX connection options
264+
// in its help output. This is a static, metadata-level check based on the class hierarchy.
265+
boolean shouldShowJmx = !(commandSpec.userObject() instanceof LocalCommand);
266+
260267
List<CommandLine.Model.CommandSpec> hierarhy = new LinkedList<>();
261268
CommandLine.Model.CommandSpec curr;
262269
CommandLine.Model.CommandSpec command = commandSpec;
@@ -271,11 +278,16 @@ private static List<CommandLine.Model.OptionSpec> parentCommandOptionsWithJmxOpt
271278
{
272279
for (CommandLine.Model.OptionSpec option : spec.options())
273280
{
274-
// JmxConnect and NodetoolCommand options are always shown in the help output for backwards compatibility.
281+
// JmxConnect options are shown for online commands only; NodetoolCommand options are always shown for backwards compatibility.
275282
if (option.userObject() instanceof Field &&
276283
(((Field) option.userObject()).getDeclaringClass().equals(JmxConnect.class) ||
277284
((Field) option.userObject()).getDeclaringClass().equals(NodetoolCommand.class)))
285+
{
286+
if (((Field) option.userObject()).getDeclaringClass().equals(JmxConnect.class) && !shouldShowJmx)
287+
continue;
288+
278289
options.add(option);
290+
}
279291
else
280292
{
281293
if (option.hidden() || option.scopeType() == CommandLine.ScopeType.LOCAL)

test/resources/nodetool/help/history

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,8 @@ NAME
22
nodetool history - Print previously executed nodetool commands
33

44
SYNOPSIS
5-
nodetool [(-h <host> | --host <host>)] [(-p <port> | --port <port>)]
6-
[(-pw <password> | --password <password>)]
7-
[(-pwf <passwordFilePath> | --password-file <passwordFilePath>)]
8-
[(-u <username> | --username <username>)] history
9-
[(-n <commands> | --number-of-commands <commands>)]
5+
nodetool history [(-n <commands> | --number-of-commands <commands>)]
106

117
OPTIONS
12-
-h <host>, --host <host>
13-
Node hostname or ip address
14-
158
-n <commands>, --num <commands>, --number-of-commands <commands>
169
Number of commands to print, defaults to 1000.
17-
18-
-p <port>, --port <port>
19-
Remote jmx agent port number
20-
21-
-pw <password>, --password <password>
22-
Remote jmx agent password
23-
24-
-pwf <passwordFilePath>, --password-file <passwordFilePath>
25-
Path to the JMX password file
26-
27-
-u <username>, --username <username>
28-
Remote jmx agent username

test/unit/org/apache/cassandra/tools/nodetool/NodetoolClassHierarchyTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,36 @@ public void testNodetoolCommandsMustBeSelfContained()
113113
affected.isEmpty());
114114
}
115115

116+
/**
117+
* Ensures that all commands implementing {@code LocalCommand} do not establish a JMX connection.
118+
*/
119+
@Test
120+
public void testLocalCommandsShouldNotConnect() throws Exception
121+
{
122+
CommandLine root = NodeTool.createCommandLine(CommandLine.defaultFactory());
123+
List<String> failedCommands = new ArrayList<>();
124+
125+
commandTreeWalker(root, cmd -> {
126+
Object userObject = cmd.getCommandSpec().userObject();
127+
if (userObject instanceof LocalCommand && userObject instanceof AbstractCommand)
128+
{
129+
AbstractCommand abstractCommand = (AbstractCommand) userObject;
130+
try
131+
{
132+
if (abstractCommand.shouldConnect())
133+
failedCommands.add(fullCommandName(cmd));
134+
}
135+
catch (Exception e)
136+
{
137+
failedCommands.add(fullCommandName(cmd) + " (" + e.getMessage() + ")");
138+
}
139+
}
140+
});
141+
142+
assertTrue("The following commands implement LocalCommand but shouldConnect() does not return false: " + failedCommands,
143+
failedCommands.isEmpty());
144+
}
145+
116146
/**
117147
* For a given command, follows the {@code @ParentCommand} chain and collects all
118148
* options and parameters declared on each parent.

0 commit comments

Comments
 (0)