This is an automated email from the ASF dual-hosted git repository.
mpetrov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new 2332899b1a3 IGNITE-23459 Added support for interactive input of
sensitive command arguments in control utility (#11604)
2332899b1a3 is described below
commit 2332899b1a36b10cd6c769611d1f76ed1089d82e
Author: Denis <[email protected]>
AuthorDate: Thu Feb 6 20:34:31 2025 +1000
IGNITE-23459 Added support for interactive input of sensitive command
arguments in control utility (#11604)
---
.../internal/commandline/ArgumentParser.java | 156 ++++++++--------
.../internal/commandline/CommandHandler.java | 42 +----
.../commandline/ConnectionAndSslParameters.java | 13 +-
.../commandline/argument/parser/CLIArgument.java | 199 ++++++++++++++++-----
.../argument/parser/CLIArgumentParser.java | 82 ++++++---
.../commandline/indexreader/IgniteIndexReader.java | 41 +++--
.../commandline/CommandHandlerParsingTest.java | 6 +-
.../GridCommandHandlerSslWithSecurityTest.java | 122 +++++++++++++
.../ignite/util/CacheMetricsCommandTest.java | 2 +-
.../org/apache/ignite/util/CdcCommandTest.java | 4 +-
.../util/GridCommandHandlerClusterByClassTest.java | 9 +-
.../GridCommandHandlerFactoryAbstractTest.java | 2 +-
.../apache/ignite/util/GridCommandHandlerTest.java | 2 +-
.../ignite/internal/management/api/Argument.java | 6 +
14 files changed, 477 insertions(+), 209 deletions(-)
diff --git
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/ArgumentParser.java
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/ArgumentParser.java
index 5392ae5db51..1f09aeb3b4b 100644
---
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/ArgumentParser.java
+++
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/ArgumentParser.java
@@ -15,16 +15,15 @@
* limitations under the License.
*/
-
package org.apache.ignite.internal.commandline;
import java.lang.reflect.Field;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.ListIterator;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
@@ -42,15 +41,17 @@ import org.apache.ignite.internal.management.api.Command;
import org.apache.ignite.internal.management.api.CommandUtils;
import org.apache.ignite.internal.management.api.CommandsRegistry;
import org.apache.ignite.internal.management.api.Positional;
+import org.apache.ignite.internal.util.typedef.internal.SB;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteExperimental;
-import org.apache.ignite.ssl.SslContextFactory;
import static
org.apache.ignite.IgniteSystemProperties.IGNITE_ENABLE_EXPERIMENTAL_COMMAND;
import static org.apache.ignite.internal.commandline.CommandHandler.DFLT_HOST;
import static org.apache.ignite.internal.commandline.CommandHandler.DFLT_PORT;
import static
org.apache.ignite.internal.commandline.CommandHandler.UTILITY_NAME;
-import static
org.apache.ignite.internal.commandline.argument.parser.CLIArgument.optionalArg;
+import static
org.apache.ignite.internal.commandline.argument.parser.CLIArgument.CLIArgumentBuilder.argument;
+import static
org.apache.ignite.internal.commandline.argument.parser.CLIArgument.CLIArgumentBuilder.optionalArgument;
+import static
org.apache.ignite.internal.commandline.argument.parser.CLIArgumentParser.readNextValueToken;
import static
org.apache.ignite.internal.management.api.CommandUtils.CMD_WORDS_DELIM;
import static
org.apache.ignite.internal.management.api.CommandUtils.NAME_PREFIX;
import static
org.apache.ignite.internal.management.api.CommandUtils.PARAM_WORDS_DELIM;
@@ -62,7 +63,9 @@ import static
org.apache.ignite.internal.management.api.CommandUtils.isBoolean;
import static
org.apache.ignite.internal.management.api.CommandUtils.toFormattedCommandName;
import static
org.apache.ignite.internal.management.api.CommandUtils.toFormattedFieldName;
import static
org.apache.ignite.internal.management.api.CommandUtils.visitCommandParams;
+import static org.apache.ignite.ssl.SslContextFactory.DFLT_KEY_ALGORITHM;
import static org.apache.ignite.ssl.SslContextFactory.DFLT_SSL_PROTOCOL;
+import static org.apache.ignite.ssl.SslContextFactory.DFLT_STORE_TYPE;
/**
* Argument parser.
@@ -131,9 +134,6 @@ public class ArgumentParser {
/** */
static final String CMD_SSL_FACTORY = "--ssl-factory";
- /** Set of sensitive arguments */
- private static final Set<String> SENSITIVE_ARGUMENTS = new HashSet<>();
-
/** */
private static final BiConsumer<String, Integer> PORT_VALIDATOR = (name,
val) -> {
if (val <= 0 || val > 65535)
@@ -143,64 +143,46 @@ public class ArgumentParser {
/** */
private final List<CLIArgument<?>> common = new ArrayList<>();
- static {
- SENSITIVE_ARGUMENTS.add(CMD_PASSWORD);
- SENSITIVE_ARGUMENTS.add(CMD_KEYSTORE_PASSWORD);
- SENSITIVE_ARGUMENTS.add(CMD_TRUSTSTORE_PASSWORD);
- }
-
- /**
- * @param arg To check.
- * @return True if provided argument is among sensitive one and not should
be displayed.
- */
- public static boolean isSensitiveArgument(String arg) {
- return SENSITIVE_ARGUMENTS.contains(arg);
- }
+ /** Console instance. */
+ protected final GridConsole console;
/**
* @param log Logger.
* @param registry Supported commands.
+ * @param console Console.
*/
- public ArgumentParser(IgniteLogger log, IgniteCommandRegistry registry) {
+ public ArgumentParser(IgniteLogger log, IgniteCommandRegistry registry,
GridConsole console) {
this.log = log;
this.registry = registry;
-
- BiConsumer<String, ?> securityWarn = (name, val) ->
log.info(String.format("Warning: %s is insecure. " +
- "Whenever possible, use interactive prompt for password (just
discard %s option).", name, name));
-
- arg(CMD_HOST, "HOST_OR_IP", String.class, DFLT_HOST);
- arg(CMD_PORT, "PORT", Integer.class, DFLT_PORT, PORT_VALIDATOR);
- arg(CMD_USER, "USER", String.class, null);
- arg(CMD_PASSWORD, "PASSWORD", String.class, null, (BiConsumer<String,
String>)securityWarn);
- arg(CMD_VERBOSE, CMD_VERBOSE, boolean.class, false);
- arg(CMD_SSL_PROTOCOL, "SSL_PROTOCOL[, SSL_PROTOCOL_2, ...,
SSL_PROTOCOL_N]", String[].class, new String[] {DFLT_SSL_PROTOCOL});
- arg(CMD_SSL_CIPHER_SUITES, "SSL_CIPHER_1[, SSL_CIPHER_2, ...,
SSL_CIPHER_N]", String[].class, null);
- arg(CMD_SSL_KEY_ALGORITHM, "SSL_KEY_ALGORITHM", String.class,
SslContextFactory.DFLT_KEY_ALGORITHM);
- arg(CMD_SSL_FACTORY, "SSL_FACTORY_PATH", String.class, null);
- arg(CMD_KEYSTORE_TYPE, "KEYSTORE_TYPE", String.class,
SslContextFactory.DFLT_STORE_TYPE);
- arg(CMD_KEYSTORE, "KEYSTORE_PATH", String.class, null);
- arg(CMD_KEYSTORE_PASSWORD, "KEYSTORE_PASSWORD", char[].class, null,
(BiConsumer<String, char[]>)securityWarn);
- arg(CMD_TRUSTSTORE_TYPE, "TRUSTSTORE_TYPE", String.class,
SslContextFactory.DFLT_STORE_TYPE);
- arg(CMD_TRUSTSTORE, "TRUSTSTORE_PATH", String.class, null);
- arg(CMD_TRUSTSTORE_PASSWORD, "TRUSTSTORE_PASSWORD", char[].class,
null, (BiConsumer<String, char[]>)securityWarn);
- arg(CMD_AUTO_CONFIRMATION, CMD_AUTO_CONFIRMATION, boolean.class,
false);
- arg(
- CMD_ENABLE_EXPERIMENTAL,
- CMD_ENABLE_EXPERIMENTAL, Boolean.class,
-
IgniteSystemProperties.getBoolean(IGNITE_ENABLE_EXPERIMENTAL_COMMAND)
+ this.console = console;
+
+ common.addAll(List.of(
+ optionalArgument(CMD_HOST,
String.class).withUsage("HOST_OR_IP").withDefault(DFLT_HOST).build(),
+ optionalArgument(CMD_PORT,
Integer.class).withUsage("PORT").withDefault(DFLT_PORT).withValidator(PORT_VALIDATOR).build(),
+ optionalArgument(CMD_USER, String.class).withUsage("USER").build(),
+ optionalArgument(CMD_PASSWORD,
String.class).withUsage("PASSWORD").markSensitive().build(),
+ optionalArgument(CMD_VERBOSE,
boolean.class).withUsage(CMD_VERBOSE).withDefault(false).build(),
+ optionalArgument(CMD_SSL_PROTOCOL, String[].class)
+ .withUsage("SSL_PROTOCOL[, SSL_PROTOCOL_2, ...,
SSL_PROTOCOL_N]")
+ .withDefault(t -> new String[] {DFLT_SSL_PROTOCOL})
+ .build(),
+ optionalArgument(CMD_SSL_CIPHER_SUITES,
String[].class).withUsage("SSL_CIPHER_1[, SSL_CIPHER_2, ...,
SSL_CIPHER_N]").build(),
+ optionalArgument(CMD_SSL_KEY_ALGORITHM,
String.class).withUsage("SSL_KEY_ALGORITHM").withDefault(DFLT_KEY_ALGORITHM).build(),
+ optionalArgument(CMD_SSL_FACTORY,
String.class).withUsage("SSL_FACTORY_PATH").build(),
+ optionalArgument(CMD_KEYSTORE_TYPE,
String.class).withUsage("KEYSTORE_TYPE").withDefault(DFLT_STORE_TYPE).build(),
+ optionalArgument(CMD_KEYSTORE,
String.class).withUsage("KEYSTORE_PATH").build(),
+ optionalArgument(CMD_KEYSTORE_PASSWORD,
char[].class).withUsage("KEYSTORE_PASSWORD").markSensitive().build(),
+ optionalArgument(CMD_TRUSTSTORE_TYPE,
String.class).withUsage("TRUSTSTORE_TYPE").withDefault(DFLT_STORE_TYPE).build(),
+ optionalArgument(CMD_TRUSTSTORE,
String.class).withUsage("TRUSTSTORE_PATH").build(),
+ optionalArgument(CMD_TRUSTSTORE_PASSWORD,
char[].class).withUsage("TRUSTSTORE_PASSWORD").markSensitive().build(),
+ optionalArgument(CMD_AUTO_CONFIRMATION,
boolean.class).withUsage(CMD_AUTO_CONFIRMATION).withDefault(false).build(),
+ optionalArgument(CMD_ENABLE_EXPERIMENTAL, Boolean.class)
+ .withUsage(CMD_ENABLE_EXPERIMENTAL)
+ .withDefault(t ->
IgniteSystemProperties.getBoolean(IGNITE_ENABLE_EXPERIMENTAL_COMMAND))
+ .build())
);
}
- /** */
- private <T> void arg(String name, String usage, Class<T> type, T dflt,
BiConsumer<String, T> validator) {
- common.add(optionalArg(name, usage, type, t -> dflt, validator));
- }
-
- /** */
- private <T> void arg(String name, String usage, Class<T> type, T dflt) {
- common.add(optionalArg(name, usage, type, () -> dflt));
- }
-
/**
* Creates list of common utility options.
*
@@ -236,7 +218,9 @@ public class ArgumentParser {
CLIArgumentParser parser = createArgumentParser();
- parser.parse(args.iterator());
+ parser.parse(args.listIterator());
+
+ String safeCmd = buildSafeCommandString(raw.listIterator(), parser);
A arg = (A)argument(
cmdPath.peek().argClass(),
@@ -253,7 +237,7 @@ public class ArgumentParser {
throw new IllegalArgumentException("Experimental commands
disabled");
}
- return new ConnectionAndSslParameters<>(cmdPath, arg, parser);
+ return new ConnectionAndSslParameters<>(cmdPath, arg, parser, safeCmd);
}
/**
@@ -323,14 +307,11 @@ public class ArgumentParser {
List<CLIArgument<?>> positionalArgs = new ArrayList<>();
List<CLIArgument<?>> namedArgs = new ArrayList<>();
- BiFunction<Field, Boolean, CLIArgument<?>> toArg = (fld, optional) ->
new CLIArgument<>(
- toFormattedFieldName(fld).toLowerCase(),
- null,
- optional,
- fld.getType(),
- null,
- (name, val) -> {}
- );
+ BiFunction<Field, Boolean, CLIArgument<?>> toArg =
+ (fld, optional) ->
argument(toFormattedFieldName(fld).toLowerCase(), fld.getType())
+ .withOptional(optional)
+ .withSensitive(fld.getAnnotation(Argument.class).sensitive())
+ .build();
List<Set<String>> grpdFlds =
CommandUtils.argumentGroupsValues(cmdPath.peek().argClass());
@@ -339,14 +320,10 @@ public class ArgumentParser {
|| fld.getAnnotation(Argument.class).optional())
);
- Consumer<Field> positionalArgCb = fld -> positionalArgs.add(new
CLIArgument<>(
- fld.getName().toLowerCase(),
- null,
- fld.getAnnotation(Argument.class).optional(),
- fld.getType(),
- null,
- (name, val) -> {}
- ));
+ Consumer<Field> positionalArgCb = fld ->
positionalArgs.add(argument(fld.getName().toLowerCase(), fld.getType())
+ .withOptional(fld.getAnnotation(Argument.class).optional())
+ .build()
+ );
BiConsumer<ArgumentGroup, List<Field>> argGrpCb = (argGrp0, flds) ->
flds.forEach(fld -> {
if (fld.isAnnotationPresent(Positional.class))
@@ -359,6 +336,37 @@ public class ArgumentParser {
namedArgs.addAll(common);
- return new CLIArgumentParser(positionalArgs, namedArgs);
+ return new CLIArgumentParser(positionalArgs, namedArgs, console);
+ }
+
+ /** @return String representation of command with hidden values of
sensitive arguments. */
+ private String buildSafeCommandString(ListIterator<String> rawIter,
CLIArgumentParser parser) {
+ SB res = new SB();
+
+ while (rawIter.hasNext()) {
+ String arg = rawIter.next();
+
+ CLIArgument<?> argDesc =
parser.getArgumentDescriptor(arg.toLowerCase());
+
+ res.a(arg).a(' ');
+
+ if (argDesc == null || argDesc.isFlag())
+ continue;
+
+ String argVal = readNextValueToken(rawIter);
+
+ if (argVal != null) {
+ if (!argDesc.isSensitive())
+ res.a(argVal).a(' ');
+ else {
+ res.a("***** ");
+
+ log.info(String.format("Warning: %s is insecure. Whenever
possible, use interactive prompt for " +
+ "password (just omit the argument value).",
argDesc.name()));
+ }
+ }
+ }
+
+ return res.toString();
}
}
diff --git
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandHandler.java
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandHandler.java
index 4c733407f62..2b45198eeb7 100644
---
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandHandler.java
+++
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandHandler.java
@@ -60,7 +60,6 @@ import org.apache.ignite.internal.util.IgniteUtils;
import org.apache.ignite.internal.util.spring.IgniteSpringHelperImpl;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.X;
-import org.apache.ignite.internal.util.typedef.internal.SB;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteExperimental;
import org.apache.ignite.ssl.SslContextFactory;
@@ -247,7 +246,7 @@ public class CommandHandler {
verbose = F.exist(rawArgs, CMD_VERBOSE::equalsIgnoreCase);
- ConnectionAndSslParameters<A> args = new ArgumentParser(logger,
registry).parseAndValidate(rawArgs);
+ ConnectionAndSslParameters<A> args = new ArgumentParser(logger,
registry, console).parseAndValidate(rawArgs);
cmdName =
toFormattedCommandName(args.cmdPath().peekLast().getClass()).toUpperCase();
@@ -272,7 +271,7 @@ public class CommandHandler {
}
logger.info("Command [" + cmdName + "] started");
- logger.info("Arguments: " + argumentsToString(rawArgs));
+ logger.info("Arguments: " + args.safeCommandString());
logger.info(U.DELIM);
String deprecationMsg =
args.command().deprecationMessage(args.commandArg());
@@ -445,36 +444,6 @@ public class CommandHandler {
return e instanceof ClientConnectionException &&
e.getMessage().startsWith("Channel is closed");
}
- /**
- * Joins user's arguments and hides sensitive information.
- *
- * @param rawArgs Arguments which user has provided.
- * @return String which could be shown in console and pritned to log.
- */
- private String argumentsToString(List<String> rawArgs) {
- boolean hide = false;
-
- SB sb = new SB();
-
- for (int i = 0; i < rawArgs.size(); i++) {
- if (hide) {
- sb.a("***** ");
-
- hide = false;
-
- continue;
- }
-
- String arg = rawArgs.get(i);
-
- sb.a(arg).a(' ');
-
- hide = ArgumentParser.isSensitiveArgument(arg);
- }
-
- return sb.toString();
- }
-
/**
* @param args Common arguments.
* @return Thin client configuration to connect to cluster.
@@ -651,7 +620,8 @@ public class CommandHandler {
"The command has the following syntax:");
logger.info("");
- logger.info(INDENT + join(" ", join(" ", UTILITY_NAME, join(" ", new
ArgumentParser(logger, registry).getCommonOptions())),
+ logger.info(INDENT + join(" ",
+ join(" ", UTILITY_NAME, join(" ", new ArgumentParser(logger,
registry, null).getCommonOptions())),
asOptional("command", true), "<command_parameters>"));
logger.info("");
logger.info("");
@@ -717,8 +687,8 @@ public class CommandHandler {
logger.info(INDENT + "The '--cache subcommand' is used to get
information about and perform actions" +
" with caches. The command has the following syntax:");
logger.info("");
- logger.info(INDENT + join(" ", UTILITY_NAME, join(" ", new
ArgumentParser(logger, null).getCommonOptions())) + " " +
- "--cache [subcommand] <subcommand_parameters>");
+ logger.info(INDENT + join(" ", UTILITY_NAME, join(" ", new
ArgumentParser(logger, null, null).getCommonOptions())) +
+ " --cache [subcommand] <subcommand_parameters>");
logger.info("");
logger.info(INDENT + "The subcommands that take [nodeId] as an
argument ('list', 'find_garbage', " +
"'contention' and 'validate_indexes') will be executed on the
given node or on all server nodes" +
diff --git
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/ConnectionAndSslParameters.java
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/ConnectionAndSslParameters.java
index 1d001a4c19e..d1a5bc28248 100644
---
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/ConnectionAndSslParameters.java
+++
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/ConnectionAndSslParameters.java
@@ -65,19 +65,25 @@ public class ConnectionAndSslParameters<A extends
IgniteDataTransferObject> {
/** */
private final CLIArgumentParser parser;
+ /** String representation of command with hidden values of sensitive
arguments. */
+ private final String safeCmd;
+
/**
* @param cmdPath Path to the command in {@link CommandsRegistry}
hierarchy.
* @param arg Command argument.
* @param parser CLI arguments parser.
+ * @param safeCmd String safe representation of command.
*/
public ConnectionAndSslParameters(
Deque<Command<?, ?>> cmdPath,
A arg,
- CLIArgumentParser parser
+ CLIArgumentParser parser,
+ String safeCmd
) {
this.cmdPath = cmdPath;
this.arg = arg;
this.parser = parser;
+ this.safeCmd = safeCmd;
this.user = parser.get(CMD_USER);
this.pwd = parser.get(CMD_PASSWORD);
@@ -249,4 +255,9 @@ public class ConnectionAndSslParameters<A extends
IgniteDataTransferObject> {
public boolean verbose() {
return parser.get(CMD_VERBOSE);
}
+
+ /** @return String representation of command with hidden values of
sensitive arguments. */
+ public String safeCommandString() {
+ return safeCmd;
+ }
}
diff --git
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/argument/parser/CLIArgument.java
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/argument/parser/CLIArgument.java
index 94ce189a7a7..60064256b2a 100644
---
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/argument/parser/CLIArgument.java
+++
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/argument/parser/CLIArgument.java
@@ -19,27 +19,21 @@ package
org.apache.ignite.internal.commandline.argument.parser;
import java.util.function.BiConsumer;
import java.util.function.Function;
-import java.util.function.Supplier;
+import org.apache.ignite.internal.util.typedef.internal.A;
/**
* Command line argument.
* @param <T> Value type.
*/
public class CLIArgument<T> {
- /** */
- private final BiConsumer<String, T> EMPTY = (name, val) -> {};
-
/** */
private final String name;
/** */
- private final String usage;
-
- /** */
- private final boolean isOptional;
+ private final Class<T> type;
/** */
- private final Class<T> type;
+ private final String usage;
/** */
private final Function<CLIArgumentParser, T> dfltValSupplier;
@@ -48,48 +42,28 @@ public class CLIArgument<T> {
private final BiConsumer<String, T> validator;
/** */
- public static <T> CLIArgument<T> optionalArg(String name, String usage,
Class<T> type) {
- return new CLIArgument<>(name, usage, true, type, null, null);
- }
+ private final boolean isOptional;
/** */
- public static <T> CLIArgument<T> optionalArg(String name, String usage,
Class<T> type, Supplier<T> dfltValSupplier) {
- return new CLIArgument<>(name, usage, true, type, p ->
dfltValSupplier.get(), null);
- }
+ private final boolean isSensitive;
/** */
- public static <T> CLIArgument<T> optionalArg(
+ private CLIArgument(
String name,
- String usage,
Class<T> type,
- Function<CLIArgumentParser, T> dfltValSupplier,
- BiConsumer<String, T> validator
- ) {
- return new CLIArgument<>(name, usage, true, type, dfltValSupplier,
validator);
- }
-
- /** */
- public static <T> CLIArgument<T> mandatoryArg(String name, String usage,
Class<T> type) {
- return new CLIArgument<>(name, usage, false, type, null, null);
- }
-
- /** */
- public CLIArgument(
- String name,
String usage,
- boolean isOptional,
- Class<T> type,
Function<CLIArgumentParser, T> dfltValSupplier,
- BiConsumer<String, T> validator
+ BiConsumer<String, T> validator,
+ boolean isOptional,
+ boolean isSensitive
) {
this.name = name;
- this.usage = usage;
- this.isOptional = isOptional;
this.type = type;
- this.dfltValSupplier = dfltValSupplier == null
- ? (type.equals(Boolean.class) ? p -> (T)Boolean.FALSE : p -> null)
- : dfltValSupplier;
+ this.usage = usage;
+ this.dfltValSupplier = dfltValSupplier;
this.validator = validator;
+ this.isOptional = isOptional;
+ this.isSensitive = isSensitive;
}
/** */
@@ -97,28 +71,161 @@ public class CLIArgument<T> {
return name;
}
+ /** */
+ public Class<T> type() {
+ return type;
+ }
+
/** */
public String usage() {
return usage;
}
+ /** */
+ public Function<CLIArgumentParser, T> defaultValueSupplier() {
+ return dfltValSupplier;
+ }
+
+ /** */
+ public BiConsumer<String, T> validator() {
+ return validator;
+ }
+
/** */
public boolean optional() {
return isOptional;
}
/** */
- public Class<T> type() {
- return type;
+ public boolean isSensitive() {
+ return isSensitive;
}
/** */
- public Function<CLIArgumentParser, T> defaultValueSupplier() {
- return dfltValSupplier;
+ public boolean isFlag() {
+ return type.equals(Boolean.class) || type.equals(boolean.class);
}
- /** */
- public BiConsumer<String, T> validator() {
- return validator == null ? EMPTY : validator;
+ /**
+ * Command line argument builder.
+ * @param <T> Value type.
+ */
+ public static class CLIArgumentBuilder<T> {
+ /** */
+ private final String name;
+
+ /** */
+ private final Class<T> type;
+
+ /** */
+ private String usage;
+
+ /** */
+ private Function<CLIArgumentParser, T> dfltValSupplier;
+
+ /** */
+ private BiConsumer<String, T> validator;
+
+ /** */
+ private boolean isOptional;
+
+ /** */
+ private boolean isSensitive;
+
+ /** */
+ private CLIArgumentBuilder(String name, Class<T> type, boolean
isOptional) {
+ this.name = name;
+ this.type = type;
+ this.isOptional = isOptional;
+ }
+
+ /** */
+ public CLIArgumentBuilder<T> withUsage(String usage) {
+ this.usage = usage;
+
+ return this;
+ }
+
+ /** */
+ public CLIArgumentBuilder<T> withDefault(T dflt) {
+ dfltValSupplier = t -> dflt;
+
+ return this;
+ }
+
+ /** */
+ public CLIArgumentBuilder<T> withDefault(Function<CLIArgumentParser,
T> dfltValSupplier) {
+ this.dfltValSupplier = dfltValSupplier;
+
+ return this;
+ }
+
+ /** */
+ public CLIArgumentBuilder<T> withValidator(BiConsumer<String, T>
validator) {
+ this.validator = validator;
+
+ return this;
+ }
+
+ /** */
+ public CLIArgumentBuilder<T> markOptional() {
+ isOptional = true;
+
+ return this;
+ }
+
+ /** */
+ public CLIArgumentBuilder<T> withOptional(boolean optional) {
+ isOptional = optional;
+
+ return this;
+ }
+
+ /** */
+ public CLIArgumentBuilder<T> markSensitive() {
+ isSensitive = true;
+
+ return this;
+ }
+
+ /** */
+ public CLIArgumentBuilder<T> withSensitive(boolean sensitive) {
+ isSensitive = sensitive;
+
+ return this;
+ }
+
+ /** */
+ public static <T> CLIArgumentBuilder<T> argument(String name, Class<T>
type) {
+ return new CLIArgumentBuilder<>(name, type, false);
+ }
+
+ /** */
+ public static <T> CLIArgumentBuilder<T> optionalArgument(String name,
Class<T> type) {
+ return new CLIArgumentBuilder<>(name, type, true);
+ }
+
+ /** */
+ public CLIArgument<T> build() {
+ A.notNull(name, "name");
+ A.notNull(type, "type");
+
+ boolean isFlag = type.equals(Boolean.class) ||
type.equals(boolean.class);
+
+ if (isFlag && isSensitive)
+ throw new IllegalArgumentException("Flag argument can't be
sensitive");
+
+ Function<CLIArgumentParser, T> dfltValSupplier =
this.dfltValSupplier;
+
+ if (dfltValSupplier == null)
+ dfltValSupplier = isFlag ? p -> (T)Boolean.FALSE : p -> null;
+
+ BiConsumer<String, T> validator = this.validator;
+
+ if (validator == null)
+ validator = (name, val) -> {};
+
+ return new CLIArgument<>(name, type, usage, dfltValSupplier,
validator, isOptional, isSensitive);
+ }
}
}
diff --git
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/argument/parser/CLIArgumentParser.java
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/argument/parser/CLIArgumentParser.java
index ff7f2f84a79..9d7769cac80 100644
---
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/argument/parser/CLIArgumentParser.java
+++
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/argument/parser/CLIArgumentParser.java
@@ -19,14 +19,14 @@ package
org.apache.ignite.internal.commandline.argument.parser;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
+import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.commandline.GridConsole;
import org.apache.ignite.internal.util.GridStringBuilder;
import static java.util.stream.Collectors.toSet;
@@ -49,20 +49,21 @@ public class CLIArgumentParser {
/** */
private final Map<String, Object> parsedArgs = new HashMap<>();
- /** */
- public CLIArgumentParser(List<CLIArgument<?>> argConfiguration) {
- this(Collections.emptyList(), argConfiguration);
- }
+ /** Console instance. */
+ protected final GridConsole console;
/** */
public CLIArgumentParser(
- List<CLIArgument<?>> positionalArgConfig,
- List<CLIArgument<?>> argConfiguration
+ List<CLIArgument<?>> positionalArgCfg,
+ List<CLIArgument<?>> argConfiguration,
+ GridConsole console
) {
- this.positionalArgCfg = positionalArgConfig;
+ this.positionalArgCfg = positionalArgCfg;
for (CLIArgument<?> cliArg : argConfiguration)
this.argConfiguration.put(cliArg.name(), cliArg);
+
+ this.console = console;
}
/**
@@ -71,7 +72,7 @@ public class CLIArgumentParser {
*
* @param argsIter Iterator.
*/
- public void parse(Iterator<String> argsIter) {
+ public void parse(ListIterator<String> argsIter) {
Set<String> obligatoryArgs =
argConfiguration.values().stream().filter(a ->
!a.optional()).map(CLIArgument::name).collect(toSet());
@@ -102,18 +103,10 @@ public class CLIArgumentParser {
else if (parsedArgs.get(cliArg.name()) != null)
throw new IllegalArgumentException(cliArg.name() + " argument
specified twice");
- boolean bool = cliArg.type().equals(Boolean.class) ||
cliArg.type().equals(boolean.class);
-
- if (!bool && !argsIter.hasNext())
- throw new IllegalArgumentException("Please specify a value for
argument: " + arg);
-
- String strVal = bool ? "true" : argsIter.next();
-
- if (strVal != null && strVal.startsWith(NAME_PREFIX))
- throw new IllegalArgumentException("Unexpected value: " +
strVal);
+ String argVal = readArgumentValue(cliArg, argsIter);
try {
- Object val = parseVal(strVal, cliArg.type());
+ Object val = parseVal(argVal, cliArg.type());
((CLIArgument<Object>)cliArg).validator().accept(cliArg.name(), val);
@@ -177,6 +170,14 @@ public class CLIArgumentParser {
return (T)parsedPositionalArgs.get(position);
}
+ /**
+ * @param name Argument name.
+ * @return Argument descriptor.
+ */
+ public CLIArgument<?> getArgumentDescriptor(String name) {
+ return argConfiguration.get(name);
+ }
+
/**
* Returns usage description.
*
@@ -214,4 +215,45 @@ public class CLIArgumentParser {
else
return arg.name();
}
+
+ /** */
+ private String readArgumentValue(CLIArgument<?> arg, ListIterator<String>
argsIter) {
+ if (arg.isFlag())
+ return "true";
+
+ String argVal = readNextValueToken(argsIter);
+
+ if (argVal == null) {
+ if (console != null && arg.isSensitive())
+ argVal = new
String(requestPasswordFromConsole(arg.name().substring(NAME_PREFIX.length()) +
": "));
+ else
+ throw new IllegalArgumentException("Please specify a value for
argument: " + arg.name());
+ }
+
+ return argVal;
+ }
+
+ /** */
+ public static String readNextValueToken(ListIterator<String> argsIter) {
+ if (!argsIter.hasNext())
+ return null;
+
+ String val = argsIter.next();
+
+ if (val.startsWith(NAME_PREFIX)) {
+ argsIter.previous();
+
+ return null;
+ }
+
+ return val;
+ }
+
+ /**
+ * @param msg Message.
+ * @return Password.
+ */
+ private char[] requestPasswordFromConsole(String msg) {
+ return console.readPassword(msg);
+ }
}
diff --git
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/indexreader/IgniteIndexReader.java
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/indexreader/IgniteIndexReader.java
index 94af0f28227..4ffd28a6c6d 100644
---
a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/indexreader/IgniteIndexReader.java
+++
b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/indexreader/IgniteIndexReader.java
@@ -65,7 +65,6 @@ import
org.apache.ignite.internal.processors.cache.persistence.StorageException;
import
org.apache.ignite.internal.processors.cache.persistence.file.AsyncFileIOFactory;
import
org.apache.ignite.internal.processors.cache.persistence.file.FilePageStore;
import
org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreManager;
-import
org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreV2;
import
org.apache.ignite.internal.processors.cache.persistence.file.FileVersionCheckingFactory;
import
org.apache.ignite.internal.processors.cache.persistence.freelist.io.PagesListMetaIO;
import
org.apache.ignite.internal.processors.cache.persistence.freelist.io.PagesListNodeIO;
@@ -104,8 +103,8 @@ import static java.util.Objects.nonNull;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import static
org.apache.ignite.configuration.DataStorageConfiguration.DFLT_PAGE_SIZE;
-import static
org.apache.ignite.internal.commandline.argument.parser.CLIArgument.mandatoryArg;
-import static
org.apache.ignite.internal.commandline.argument.parser.CLIArgument.optionalArg;
+import static
org.apache.ignite.internal.commandline.argument.parser.CLIArgument.CLIArgumentBuilder.argument;
+import static
org.apache.ignite.internal.commandline.argument.parser.CLIArgument.CLIArgumentBuilder.optionalArgument;
import static
org.apache.ignite.internal.management.SystemViewTask.SimpleType.NUMBER;
import static
org.apache.ignite.internal.management.SystemViewTask.SimpleType.STRING;
import static org.apache.ignite.internal.pagemem.PageIdAllocator.FLAG_DATA;
@@ -118,6 +117,7 @@ import static
org.apache.ignite.internal.pagemem.PageIdUtils.pageIndex;
import static org.apache.ignite.internal.pagemem.PageIdUtils.partId;
import static
org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreManager.INDEX_FILE_NAME;
import static
org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreManager.PART_FILE_TEMPLATE;
+import static
org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreV2.VERSION;
import static org.apache.ignite.internal.util.GridUnsafe.allocateBuffer;
import static org.apache.ignite.internal.util.GridUnsafe.bufferAddress;
import static org.apache.ignite.internal.util.GridUnsafe.freeBuffer;
@@ -300,20 +300,27 @@ public class IgniteIndexReader implements AutoCloseable {
public static void main(String[] args) {
System.out.println("THIS UTILITY MUST BE LAUNCHED ON PERSISTENT STORE
WHICH IS NOT UNDER RUNNING GRID!");
- CLIArgumentParser p = new CLIArgumentParser(asList(
- mandatoryArg(
- DIR_ARG,
- "partition directory, where " + INDEX_FILE_NAME + " and
(optionally) partition files are located.",
- String.class
+ CLIArgumentParser p = new CLIArgumentParser(
+ Collections.emptyList(),
+ asList(
+ argument(DIR_ARG, String.class)
+ .withUsage("partition directory, where " + INDEX_FILE_NAME
+ " and (optionally) partition files are located.")
+ .build(),
+ optionalArgument(PART_CNT_ARG, Integer.class).withUsage("full
partitions count in cache group.").withDefault(0).build(),
+ optionalArgument(PAGE_SIZE_ARG, Integer.class).withUsage("page
size.").withDefault(DFLT_PAGE_SIZE).build(),
+ optionalArgument(PAGE_STORE_VER_ARG,
Integer.class).withUsage("page store version.").withDefault(VERSION).build(),
+ optionalArgument(INDEXES_ARG, String[].class)
+ .withUsage("you can specify index tree names that will be
processed, separated by comma without " +
+ "spaces, other index trees will be skipped.")
+ .withDefault(U.EMPTY_STRS)
+ .build(),
+ optionalArgument(CHECK_PARTS_ARG, Boolean.class)
+ .withUsage("check cache data tree in partition files and
it's consistency with indexes.")
+ .withDefault(false)
+ .build()
),
- optionalArg(PART_CNT_ARG, "full partitions count in cache group.",
Integer.class, () -> 0),
- optionalArg(PAGE_SIZE_ARG, "page size.", Integer.class, () ->
DFLT_PAGE_SIZE),
- optionalArg(PAGE_STORE_VER_ARG, "page store version.",
Integer.class, () -> FilePageStoreV2.VERSION),
- optionalArg(INDEXES_ARG, "you can specify index tree names that
will be processed, separated by comma " +
- "without spaces, other index trees will be skipped.",
String[].class, () -> U.EMPTY_STRS),
- optionalArg(CHECK_PARTS_ARG,
- "check cache data tree in partition files and it's consistency
with indexes.", Boolean.class, () -> false)
- ));
+ null
+ );
if (args.length == 0) {
System.out.println(p.usage());
@@ -321,7 +328,7 @@ public class IgniteIndexReader implements AutoCloseable {
return;
}
- p.parse(asList(args).iterator());
+ p.parse(asList(args).listIterator());
Set<String> idxs = new HashSet<>(asList(p.get(INDEXES_ARG)));
diff --git
a/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java
b/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java
index 8a816611c24..ada5db81577 100644
---
a/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java
+++
b/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java
@@ -968,7 +968,7 @@ public class CommandHandlerParsingTest {
"--group-names", "--some-other-arg"
)),
IllegalArgumentException.class,
- "Unexpected value: --some-other-arg"
+ "Please specify a value for argument: --group-names"
);
GridTestUtils.assertThrows(
@@ -979,7 +979,7 @@ public class CommandHandlerParsingTest {
"--cache-names", "--some-other-arg"
)),
IllegalArgumentException.class,
- "Unexpected value: --some-other-arg"
+ "Please specify a value for argument: --cache-names"
);
GridTestUtils.assertThrows(
@@ -1339,7 +1339,7 @@ public class CommandHandlerParsingTest {
* @return Common parameters container object.
*/
private <A extends IgniteDataTransferObject> ConnectionAndSslParameters<A>
parseArgs(List<String> args) {
- return new ArgumentParser(setupTestLogger(), new
IgniteCommandRegistry()).parseAndValidate(args);
+ return new ArgumentParser(setupTestLogger(), new
IgniteCommandRegistry(), null).parseAndValidate(args);
}
/**
diff --git
a/modules/control-utility/src/test/java/org/apache/ignite/internal/processors/security/GridCommandHandlerSslWithSecurityTest.java
b/modules/control-utility/src/test/java/org/apache/ignite/internal/processors/security/GridCommandHandlerSslWithSecurityTest.java
index b3fe7c8e38a..2a5ac1cd27f 100644
---
a/modules/control-utility/src/test/java/org/apache/ignite/internal/processors/security/GridCommandHandlerSslWithSecurityTest.java
+++
b/modules/control-utility/src/test/java/org/apache/ignite/internal/processors/security/GridCommandHandlerSslWithSecurityTest.java
@@ -200,4 +200,126 @@ public class GridCommandHandlerSslWithSecurityTest
extends GridCommandHandlerFac
assertContains(log, testOutput, "--keystore-password *****");
assertContains(log, testOutput, "--truststore-password *****");
}
+
+ /**
+ * Verify that the command work correctly when request starts with the
--password argument
+ * without value that invoke console password input for user, and that it
is requested only once.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testInputKeyUserPwdOnlyOncePwdArgStart() throws Exception {
+ doPasswordInteractiveInputTest(Arrays.asList(
+ "--password",
+ "--state",
+ "--user", login,
+ "--keystore", keyStorePath(CLI_CMD_HND.equals(commandHandler) ?
"thinClient" : "connectorServer"),
+ "--keystore-password", keyStorePassword(),
+ "--truststore", keyStorePath(CLI_CMD_HND.equals(commandHandler) ?
"trusttwo" : "trustthree"),
+ "--truststore-password", keyStorePassword()));
+ }
+
+ /**
+ * Verify that the command work correctly when request contains the
--password argument inside
+ * without value that invoke console password input for user, and that it
is requested only once.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testInputKeyUserPwdOnlyOncePwdArgMiddle() throws Exception {
+ doPasswordInteractiveInputTest(Arrays.asList(
+ "--state",
+ "--user", login,
+ "--password",
+ "--keystore", keyStorePath(CLI_CMD_HND.equals(commandHandler) ?
"thinClient" : "connectorServer"),
+ "--keystore-password", keyStorePassword(),
+ "--truststore", keyStorePath(CLI_CMD_HND.equals(commandHandler) ?
"trusttwo" : "trustthree"),
+ "--truststore-password", keyStorePassword()));
+ }
+
+ /**
+ * Verify that the command work correctly when request ends with the
--password argument
+ * without value that invoke console password input for user, and that it
is requested only once.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testInputKeyUserPwdOnlyOncePwdArgEnd() throws Exception {
+ doPasswordInteractiveInputTest(Arrays.asList(
+ "--state",
+ "--user", login,
+ "--keystore", keyStorePath(CLI_CMD_HND.equals(commandHandler) ?
"thinClient" : "connectorServer"),
+ "--keystore-password", keyStorePassword(),
+ "--truststore", keyStorePath(CLI_CMD_HND.equals(commandHandler) ?
"trusttwo" : "trustthree"),
+ "--truststore-password", keyStorePassword(),
+ "--password"));
+ }
+
+ /**
+ * Verify that the command work correctly when request few arguments
+ * without value that invoke console input.
+ *
+ * @throws Exception If failed.
+ */
+ @Test
+ public void testInputKeyForFewRequestedArguments() throws Exception {
+ IgniteEx crd = startGrid();
+
+ crd.cluster().state(ACTIVE);
+
+ TestCommandHandler hnd = newCommandHandler();
+
+ AtomicInteger reqCnt = new AtomicInteger();
+
+ ((CommandHandler)GridTestUtils.getFieldValue(hnd, "hnd")).console =
new NoopConsole() {
+ @Override public char[] readPassword(String fmt, Object... args) {
+ if (reqCnt.incrementAndGet() == 1)
+ return keyStorePassword().toCharArray();
+ else
+ return pwd.toCharArray();
+ }
+ };
+
+ int exitCode = hnd.execute(Arrays.asList(
+ "--state",
+ "--user", login,
+ "--verbose",
+ "--keystore", keyStorePath(CLI_CMD_HND.equals(commandHandler) ?
"thinClient" : "connectorServer"),
+ "--keystore-password",
+ "--truststore", keyStorePath(CLI_CMD_HND.equals(commandHandler) ?
"trusttwo" : "trustthree"),
+ "--truststore-password", keyStorePassword(),
+ "--password"));
+
+ assertEquals(EXIT_CODE_OK, exitCode);
+ assertEquals(2, reqCnt.get());
+ }
+
+ /**
+ * Perform the test with prepared List arguments.
+ *
+ * @param args List of query arguments.
+ * @throws Exception If failed.
+ */
+ private void doPasswordInteractiveInputTest(List<String> args) throws
Exception {
+ IgniteEx crd = startGrid();
+
+ crd.cluster().state(ACTIVE);
+
+ TestCommandHandler hnd = newCommandHandler();
+
+ AtomicInteger pwdCnt = new AtomicInteger();
+
+ ((CommandHandler)GridTestUtils.getFieldValue(hnd, "hnd")).console =
new NoopConsole() {
+ @Override public char[] readPassword(String fmt, Object... args) {
+ pwdCnt.incrementAndGet();
+
+ return pwd.toCharArray();
+ }
+ };
+
+ int exitCode = hnd.execute(args);
+
+ assertEquals(EXIT_CODE_OK, exitCode);
+ assertEquals(1, pwdCnt.get());
+ }
}
diff --git
a/modules/control-utility/src/test/java/org/apache/ignite/util/CacheMetricsCommandTest.java
b/modules/control-utility/src/test/java/org/apache/ignite/util/CacheMetricsCommandTest.java
index 5f36276a957..ab5f6581c00 100644
---
a/modules/control-utility/src/test/java/org/apache/ignite/util/CacheMetricsCommandTest.java
+++
b/modules/control-utility/src/test/java/org/apache/ignite/util/CacheMetricsCommandTest.java
@@ -184,7 +184,7 @@ public class CacheMetricsCommandTest extends
GridCommandHandlerAbstractTest {
CACHE_ONE);
// Check when after --caches argument extra argument is passed instead
of list of caches
- checkInvalidArguments("Unexpected value: --all-caches", ENABLE,
CACHES_ARGUMENT, ALL_CACHES_ARGUMENT);
+ checkInvalidArguments("Please specify a value for argument: --caches",
ENABLE, CACHES_ARGUMENT, ALL_CACHES_ARGUMENT);
}
/**
diff --git
a/modules/control-utility/src/test/java/org/apache/ignite/util/CdcCommandTest.java
b/modules/control-utility/src/test/java/org/apache/ignite/util/CdcCommandTest.java
index 95870fc3f2c..2b3f8f5a0e6 100644
---
a/modules/control-utility/src/test/java/org/apache/ignite/util/CdcCommandTest.java
+++
b/modules/control-utility/src/test/java/org/apache/ignite/util/CdcCommandTest.java
@@ -177,7 +177,7 @@ public class CdcCommandTest extends
GridCommandHandlerAbstractTest {
assertContains(log, executeCommand(EXIT_CODE_INVALID_ARGUMENTS,
CDC, DELETE_LOST_SEGMENT_LINKS, NODE_ID),
- "Unexpected value: --yes");
+ "Please specify a value for argument: --node-id");
assertContains(log, executeCommand(EXIT_CODE_INVALID_ARGUMENTS,
CDC, DELETE_LOST_SEGMENT_LINKS, NODE_ID, "10"),
@@ -298,7 +298,7 @@ public class CdcCommandTest extends
GridCommandHandlerAbstractTest {
assertContains(log, executeCommand(EXIT_CODE_INVALID_ARGUMENTS,
CDC, RESEND, CACHES),
- "Unexpected value: --yes");
+ "Please specify a value for argument: --caches");
}
/** */
diff --git
a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerClusterByClassTest.java
b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerClusterByClassTest.java
index c6dceefd994..efc11f3cd5b 100644
---
a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerClusterByClassTest.java
+++
b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerClusterByClassTest.java
@@ -1362,9 +1362,7 @@ public class GridCommandHandlerClusterByClassTest extends
GridCommandHandlerClus
assertContains(
log,
executeCommand(EXIT_CODE_INVALID_ARGUMENTS, "--cache", CREATE,
SPRING_XML_CONFIG),
- !sslEnabled()
- ? "Please specify a value for argument: --springXmlConfig"
- : "Unexpected value: --keystore"
+ "Please specify a value for argument: --springxmlconfig"
);
autoConfirmation = true;
@@ -1651,10 +1649,7 @@ public class GridCommandHandlerClusterByClassTest
extends GridCommandHandlerClus
assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--cache", SCAN,
"cache", "--limit"));
- if (sslEnabled()) // Extra arguments at the end added.
- assertContains(log, testOut.toString(), "Unexpected value:
--keystore");
- else
- assertContains(log, testOut.toString(), "Please specify a value
for argument: --limit");
+ assertContains(log, testOut.toString(), "Please specify a value for
argument: --limit");
assertEquals(EXIT_CODE_INVALID_ARGUMENTS, execute("--cache", SCAN,
"cache", "--limit", "test"));
diff --git
a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerFactoryAbstractTest.java
b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerFactoryAbstractTest.java
index 5c84a7030e9..07a98d279ec 100644
---
a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerFactoryAbstractTest.java
+++
b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerFactoryAbstractTest.java
@@ -206,7 +206,7 @@ public class GridCommandHandlerFactoryAbstractTest extends
GridCommonAbstractTes
String cmdName = null;
try {
- ArgumentParser parser = new ArgumentParser(log, new
IgniteCommandRegistry());
+ ArgumentParser parser = new ArgumentParser(log, new
IgniteCommandRegistry(), null);
ConnectionAndSslParameters<IgniteDataTransferObject> p =
parser.parseAndValidate(value);
diff --git
a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java
b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java
index e79a6e0704c..8371fa7b0cf 100644
---
a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java
+++
b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java
@@ -3787,7 +3787,7 @@ public class GridCommandHandlerTest extends
GridCommandHandlerClusterPerMethodAb
assertContains(
log,
testOut.toString(),
- !sslEnabled() ? "Please specify a value for argument:
--increment" : "Unexpected value: "
+ "Please specify a value for argument: --increment"
);
// Wrong params.
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/management/api/Argument.java
b/modules/core/src/main/java/org/apache/ignite/internal/management/api/Argument.java
index 3ca12071ba1..67e424d9621 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/management/api/Argument.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/management/api/Argument.java
@@ -52,4 +52,10 @@ public @interface Argument {
* @return {@code True} if parameter name expected without "--" prefix.
*/
public boolean withoutPrefix() default false;
+
+ /**
+ * @return {@code True} if the argument value is sensitive and needs value
to be requested from console,
+ * in case this value is not specified, {@code false} if not needed.
+ */
+ public boolean sensitive() default false;
}