Make cassandra-stress able to read all credentials from a file

As side-effect, this patch also introduces possibility to pass credentials to JMX.

patch by Stefan Miklosovic; reviewed by Brandon Williams for CASSANDRA-18544
This commit is contained in:
Stefan Miklosovic 2023-05-19 12:31:40 +02:00
parent ffe4d85df2
commit 4d2ee09ffa
No known key found for this signature in database
GPG Key ID: 32F35CB2F546D93E
12 changed files with 446 additions and 32 deletions

View File

@ -1,4 +1,5 @@
5.0
* Make cassandra-stress able to read all credentials from a file (CASSANDRA-18544)
* Add guardrail for partition size and deprecate compaction_large_partition_warning_threshold (CASSANDRA-18500)
* Add HISTORY command for CQLSH (CASSANDRA-15046)
* Fix sstable formats configuration (CASSANDRA-18441)

View File

@ -142,6 +142,8 @@ New features
Superusers have it by default, whereas regular users don't have it by default.
- Added support for using UDFs as masking functions attached to table columns on the schema.
- Added `sstablepartitions` offline tool to find large partitions in sstables.
- `cassandra-stress` has a new option called '-jmx' which enables a user to pass username and password to JMX (CASSANDRA-18544)
- It is possible to read all credentials for `cassandra-stress` from a file via option `-credentials-file` (CASSANDRA-18544)
Upgrading
---------

View File

@ -71,6 +71,10 @@ Primary Options:::
Graph recorded metrics
-tokenrange:;;
Token range settings
-jmx:;;
Username and password for JMX connection
-credentials-file <path>:;;
Credentials file to specify for CQL, JMX and transport
Suboptions:::
Every command and primary option has its own collection of suboptions.
These are too numerous to list here. For information on the suboptions

View File

@ -114,7 +114,8 @@ public class StressMetrics implements MeasurementSink
try
{
gcStatsCollector = new JmxCollector(toJmxNodes(settings.node.resolveAllPermitted(settings)),
settings.port.jmxPort);
settings.port.jmxPort,
settings.jmx);
}
catch (Throwable t)
{

View File

@ -37,8 +37,10 @@ public enum CliOption
LOG("Where to log progress to, and the interval at which to do it", SettingsLog.helpPrinter()),
TRANSPORT("Custom transport factories", SettingsTransport.helpPrinter()),
PORT("The port to connect to cassandra nodes on", SettingsPort.helpPrinter()),
JMX("JMX credentials", SettingsJMX.helpPrinter()),
GRAPH("-graph", "Graph recorded metrics", SettingsGraph.helpPrinter()),
TOKENRANGE("Token range settings", SettingsTokenRange.helpPrinter())
TOKENRANGE("Token range settings", SettingsTokenRange.helpPrinter()),
CREDENTIALS_FILE("Credentials file for CQL, JMX and transport", SettingsCredentials.helpPrinter());
;
private static final Map<String, CliOption> LOOKUP;
@ -63,11 +65,12 @@ public enum CliOption
public final String description;
private final Runnable helpPrinter;
private CliOption(String description, Runnable helpPrinter)
CliOption(String description, Runnable helpPrinter)
{
this(null, description, helpPrinter);
}
private CliOption(String extraName, String description, Runnable helpPrinter)
CliOption(String extraName, String description, Runnable helpPrinter)
{
this.extraName = extraName;
this.description = description;
@ -79,4 +82,8 @@ public enum CliOption
helpPrinter.run();
}
public String toString()
{
return name().replaceAll("_", "-");
}
}

View File

@ -0,0 +1,129 @@
/*
* 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.stress.settings;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.Map;
import java.util.Properties;
import org.apache.cassandra.io.util.File;
import org.apache.cassandra.stress.util.ResultLogger;
public class SettingsCredentials implements Serializable
{
public static final String CQL_USERNAME_PROPERTY_KEY = "cql.username";
public static final String CQL_PASSWORD_PROPERTY_KEY = "cql.password";
public static final String JMX_USERNAME_PROPERTY_KEY = "jmx.username";
public static final String JMX_PASSWORD_PROPERTY_KEY = "jmx.password";
public static final String TRANSPORT_TRUSTSTORE_PASSWORD_PROPERTY_KEY = "transport.truststore.password";
public static final String TRANSPORT_KEYSTORE_PASSWORD_PROPERTY_KEY = "transport.keystore.password";
private final String file;
public final String cqlUsername;
public final String cqlPassword;
public final String jmxUsername;
public final String jmxPassword;
public final String transportTruststorePassword;
public final String transportKeystorePassword;
public SettingsCredentials(String file)
{
this.file = file;
if (file == null)
{
cqlUsername = null;
cqlPassword = null;
jmxUsername = null;
jmxPassword = null;
transportTruststorePassword = null;
transportKeystorePassword = null;
return;
}
try
{
Properties properties = new Properties();
try (InputStream is = new FileInputStream(new File(file).toJavaIOFile()))
{
properties.load(is);
cqlUsername = properties.getProperty(CQL_USERNAME_PROPERTY_KEY);
cqlPassword = properties.getProperty(CQL_PASSWORD_PROPERTY_KEY);
jmxUsername = properties.getProperty(JMX_USERNAME_PROPERTY_KEY);
jmxPassword = properties.getProperty(JMX_PASSWORD_PROPERTY_KEY);
transportTruststorePassword = properties.getProperty(TRANSPORT_TRUSTSTORE_PASSWORD_PROPERTY_KEY);
transportKeystorePassword = properties.getProperty(TRANSPORT_KEYSTORE_PASSWORD_PROPERTY_KEY);
}
}
catch (IOException ioe)
{
throw new RuntimeException(ioe);
}
}
// CLI Utility Methods
public void printSettings(ResultLogger out)
{
out.printf(" File: %s%n", file == null ? "*not set*" : file);
out.printf(" CQL username: %s%n", cqlUsername == null ? "*not set*" : cqlUsername);
out.printf(" CQL password: %s%n", cqlPassword == null ? "*not set*" : "*suppressed*");
out.printf(" JMX username: %s%n", jmxUsername == null ? "*not set*" : jmxUsername);
out.printf(" JMX password: %s%n", jmxPassword == null ? "*not set*" : "*suppressed*");
out.printf(" Transport truststore password: %s%n", transportTruststorePassword == null ? "*not set*" : "*suppressed*");
out.printf(" Transport keystore password: %s%n", transportKeystorePassword == null ? "*not set*" : "*suppressed*");
}
public static SettingsCredentials get(Map<String, String[]> clArgs)
{
String[] params = clArgs.remove("-credentials-file");
if (params == null)
return new SettingsCredentials(null);
if (params.length != 1)
{
printHelp();
System.out.println("Invalid -credentials-file option provided, see output for valid options");
System.exit(1);
}
return new SettingsCredentials(params[0]);
}
public static void printHelp()
{
System.out.println("Usage: -credentials-file <file> ");
System.out.printf("File is supposed to be a standard property file with '%s', '%s', '%s', '%s', '%s', and '%s' as keys. " +
"The values for these keys will be overriden by their command-line counterparts when specified.%n",
CQL_USERNAME_PROPERTY_KEY,
CQL_PASSWORD_PROPERTY_KEY,
JMX_USERNAME_PROPERTY_KEY,
JMX_PASSWORD_PROPERTY_KEY,
TRANSPORT_KEYSTORE_PASSWORD_PROPERTY_KEY,
TRANSPORT_TRUSTSTORE_PASSWORD_PROPERTY_KEY);
}
public static Runnable helpPrinter()
{
return SettingsCredentials::printHelp;
}
}

View File

@ -0,0 +1,98 @@
/*
* 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.stress.settings;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.apache.cassandra.stress.util.ResultLogger;
import static java.lang.String.format;
import static org.apache.cassandra.stress.settings.SettingsCredentials.JMX_PASSWORD_PROPERTY_KEY;
import static org.apache.cassandra.stress.settings.SettingsCredentials.JMX_USERNAME_PROPERTY_KEY;
public class SettingsJMX implements Serializable
{
public final String user;
public final String password;
public SettingsJMX(Options options, SettingsCredentials credentials)
{
this.user = options.user.setByUser() ? options.user.value() : credentials.jmxUsername;
this.password = options.password.setByUser() ? options.password.value() : credentials.jmxPassword;
}
// Option Declarations
public static final class Options extends GroupedOptions
{
final OptionSimple user = new OptionSimple("user=",
".*",
null,
format("Username for JMX connection, when specified, it will override the value in credentials file for key '%s'", JMX_USERNAME_PROPERTY_KEY),
false);
final OptionSimple password = new OptionSimple("password=",
".*",
null,
format("Password for JMX connection, when specified, it will override the value in credentials file for key '%s'", JMX_PASSWORD_PROPERTY_KEY),
false);
@Override
public List<? extends Option> options()
{
return Arrays.asList(user, password);
}
}
// CLI Utility Methods
public void printSettings(ResultLogger out)
{
out.printf(" Username: %s%n", user);
out.printf(" Password: %s%n", (password == null ? "*not set*" : "*suppressed*"));
}
public static SettingsJMX get(Map<String, String[]> clArgs, SettingsCredentials credentials)
{
String[] params = clArgs.remove("-jmx");
if (params == null)
return new SettingsJMX(new SettingsJMX.Options(), credentials);
GroupedOptions options = GroupedOptions.select(params, new SettingsJMX.Options());
if (options == null)
{
printHelp();
System.out.println("Invalid -jmx options provided, see output for valid options");
System.exit(1);
}
return new SettingsJMX((SettingsJMX.Options) options, credentials);
}
public static void printHelp()
{
GroupedOptions.printOptions(System.out, "-jmx", new SettingsJMX.Options());
}
public static Runnable helpPrinter()
{
return SettingsJMX::printHelp;
}
}

View File

@ -32,6 +32,10 @@ import com.datastax.driver.core.ProtocolOptions;
import com.datastax.driver.core.ProtocolVersion;
import org.apache.cassandra.stress.util.ResultLogger;
import static java.lang.String.format;
import static org.apache.cassandra.stress.settings.SettingsCredentials.CQL_PASSWORD_PROPERTY_KEY;
import static org.apache.cassandra.stress.settings.SettingsCredentials.CQL_USERNAME_PROPERTY_KEY;
public class SettingsMode implements Serializable
{
@ -51,7 +55,7 @@ public class SettingsMode implements Serializable
private final String compression;
public SettingsMode(GroupedOptions options)
public SettingsMode(GroupedOptions options, SettingsCredentials credentials)
{
if (options instanceof Cql3Options)
{
@ -63,8 +67,8 @@ public class SettingsMode implements Serializable
api = ConnectionAPI.JAVA_DRIVER_NATIVE;
style = opts.useUnPrepared.setByUser() ? ConnectionStyle.CQL : ConnectionStyle.CQL_PREPARED;
compression = ProtocolOptions.Compression.valueOf(opts.useCompression.value().toUpperCase()).name();
username = opts.user.value();
password = opts.password.value();
username = opts.user.setByUser() ? opts.user.value() : credentials.cqlUsername;
password = opts.password.setByUser() ? opts.password.value() : credentials.cqlPassword;
maxPendingPerConnection = opts.maxPendingPerConnection.value().isEmpty() ? null : Integer.valueOf(opts.maxPendingPerConnection.value());
connectionsPerHost = opts.connectionsPerHost.value().isEmpty() ? null : Integer.valueOf(opts.connectionsPerHost.value());
authProviderClassname = opts.authProvider.value();
@ -137,8 +141,12 @@ public class SettingsMode implements Serializable
final OptionSimple useUnPrepared = new OptionSimple("unprepared", "", null, "force use of unprepared statements", false);
final OptionSimple useCompression = new OptionSimple("compression=", "none|lz4|snappy", "none", "", false);
final OptionSimple port = new OptionSimple("port=", "[0-9]+", "9046", "", false);
final OptionSimple user = new OptionSimple("user=", ".+", null, "username", false);
final OptionSimple password = new OptionSimple("password=", ".+", null, "password", false);
final OptionSimple user = new OptionSimple("user=", ".+", null,
format("CQL user, when specified, it will override the value in credentials file for key '%s'", CQL_USERNAME_PROPERTY_KEY),
false);
final OptionSimple password = new OptionSimple("password=", ".+", null,
format("CQL password, when specified, it will override the value in credentials file for key '%s'", CQL_PASSWORD_PROPERTY_KEY),
false);
final OptionSimple authProvider = new OptionSimple("auth-provider=", ".*", null, "Fully qualified implementation of com.datastax.driver.core.AuthProvider", false);
final OptionSimple maxPendingPerConnection = new OptionSimple("maxPending=", "[0-9]+", "128", "Maximum pending requests per connection", false);
final OptionSimple connectionsPerHost = new OptionSimple("connectionsPerHost=", "[0-9]+", "8", "Number of connections per host", false);
@ -179,11 +187,9 @@ public class SettingsMode implements Serializable
out.printf(" Max Pending Per Connection: %d%n", maxPendingPerConnection);
out.printf(" Connections Per Host: %d%n", connectionsPerHost);
out.printf(" Compression: %s%n", compression);
}
public static SettingsMode get(Map<String, String[]> clArgs)
public static SettingsMode get(Map<String, String[]> clArgs, SettingsCredentials credentials)
{
String[] params = clArgs.remove("-mode");
if (params == null)
@ -192,7 +198,7 @@ public class SettingsMode implements Serializable
opts.accept("cql3");
opts.accept("native");
opts.accept("prepared");
return new SettingsMode(opts);
return new SettingsMode(opts, credentials);
}
GroupedOptions options = GroupedOptions.select(params, new Cql3NativeOptions(), new Cql3SimpleNativeOptions());
@ -202,7 +208,7 @@ public class SettingsMode implements Serializable
System.out.println("Invalid -mode options provided, see output for valid options");
System.exit(1);
}
return new SettingsMode(options);
return new SettingsMode(options, credentials);
}
public static void printHelp()

View File

@ -29,13 +29,19 @@ import java.util.Map;
import org.apache.cassandra.config.EncryptionOptions;
import org.apache.cassandra.stress.util.ResultLogger;
import static java.lang.String.format;
import static org.apache.cassandra.stress.settings.SettingsCredentials.TRANSPORT_KEYSTORE_PASSWORD_PROPERTY_KEY;
import static org.apache.cassandra.stress.settings.SettingsCredentials.TRANSPORT_TRUSTSTORE_PASSWORD_PROPERTY_KEY;
public class SettingsTransport implements Serializable
{
private final TOptions options;
private final SettingsCredentials credentials;
public SettingsTransport(TOptions options)
public SettingsTransport(TOptions options, SettingsCredentials credentials)
{
this.options = options;
this.credentials = credentials;
}
public EncryptionOptions getEncryptionOptions()
@ -46,7 +52,7 @@ public class SettingsTransport implements Serializable
encOptions = encOptions
.withEnabled(true)
.withTrustStore(options.trustStore.value())
.withTrustStorePassword(options.trustStorePw.value())
.withTrustStorePassword(options.trustStorePw.setByUser() ? options.trustStorePw.value() : credentials.transportTruststorePassword)
.withAlgorithm(options.alg.value())
.withProtocol(options.protocol.value())
.withCipherSuites(options.ciphers.value().split(","));
@ -54,14 +60,14 @@ public class SettingsTransport implements Serializable
{
encOptions = encOptions
.withKeyStore(options.keyStore.value())
.withKeyStorePassword(options.keyStorePw.value());
.withKeyStorePassword(options.keyStorePw.setByUser() ? options.keyStorePw.value() : credentials.transportKeystorePassword);
}
else
{
// mandatory for SSLFactory.createSSLContext(), see CASSANDRA-9325
encOptions = encOptions
.withKeyStore(encOptions.truststore)
.withKeyStorePassword(encOptions.truststore_password);
.withKeyStorePassword(encOptions.truststore_password != null ? encOptions.truststore_password : credentials.transportTruststorePassword);
}
}
return encOptions;
@ -72,12 +78,18 @@ public class SettingsTransport implements Serializable
static class TOptions extends GroupedOptions implements Serializable
{
final OptionSimple trustStore = new OptionSimple("truststore=", ".*", null, "SSL: full path to truststore", false);
final OptionSimple trustStorePw = new OptionSimple("truststore-password=", ".*", null, "SSL: truststore password", false);
final OptionSimple trustStorePw = new OptionSimple("truststore-password=", ".*", null,
format("SSL: truststore password, when specified, it will override the value in credentials file of key '%s'",
TRANSPORT_TRUSTSTORE_PASSWORD_PROPERTY_KEY), false);
final OptionSimple keyStore = new OptionSimple("keystore=", ".*", null, "SSL: full path to keystore", false);
final OptionSimple keyStorePw = new OptionSimple("keystore-password=", ".*", null, "SSL: keystore password", false);
final OptionSimple keyStorePw = new OptionSimple("keystore-password=", ".*", null,
format("SSL: keystore password, when specified, it will override the value in credentials file for key '%s'",
TRANSPORT_KEYSTORE_PASSWORD_PROPERTY_KEY), false);
final OptionSimple protocol = new OptionSimple("ssl-protocol=", ".*", "TLS", "SSL: connection protocol to use", false);
final OptionSimple alg = new OptionSimple("ssl-alg=", ".*", null, "SSL: algorithm", false);
final OptionSimple ciphers = new OptionSimple("ssl-ciphers=", ".*", "TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA", "SSL: comma delimited list of encryption suites to use", false);
final OptionSimple ciphers = new OptionSimple("ssl-ciphers=", ".*",
"TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA",
"SSL: comma delimited list of encryption suites to use", false);
@Override
public List<? extends Option> options()
@ -89,14 +101,26 @@ public class SettingsTransport implements Serializable
// CLI Utility Methods
public void printSettings(ResultLogger out)
{
out.println(" " + options.getOptionAsString());
String tPassword = options.trustStorePw.setByUser() ? options.trustStorePw.value() : credentials.transportTruststorePassword;
tPassword = tPassword != null ? "*suppressed*" : tPassword;
String kPassword = options.keyStorePw.setByUser() ? options.keyStore.value() : credentials.transportKeystorePassword;
kPassword = kPassword != null ? "*suppressed*" : kPassword;
out.printf(" Truststore: %s%n", options.trustStore.value());
out.printf(" Truststore Password: %s%n", tPassword);
out.printf(" Keystore: %s%n", options.keyStore.value());
out.printf(" Keystore Password: %s%n", kPassword);
out.printf(" SSL Protocol: %s%n", options.protocol.value());
out.printf(" SSL Algorithm: %s%n", options.alg.value());
out.printf(" SSL Ciphers: %s%n", options.ciphers.value());
}
public static SettingsTransport get(Map<String, String[]> clArgs)
public static SettingsTransport get(Map<String, String[]> clArgs, SettingsCredentials credentials)
{
String[] params = clArgs.remove("-transport");
if (params == null)
return new SettingsTransport(new TOptions());
return new SettingsTransport(new TOptions(), credentials);
GroupedOptions options = GroupedOptions.select(params, new TOptions());
if (options == null)
@ -105,7 +129,7 @@ public class SettingsTransport implements Serializable
System.out.println("Invalid -transport options provided, see output for valid options");
System.exit(1);
}
return new SettingsTransport((TOptions) options);
return new SettingsTransport((TOptions) options, credentials);
}
public static void printHelp()

View File

@ -38,11 +38,13 @@ public class StressSettings implements Serializable
public final SettingsColumn columns;
public final SettingsErrors errors;
public final SettingsLog log;
public final SettingsCredentials credentials;
public final SettingsMode mode;
public final SettingsNode node;
public final SettingsSchema schema;
public final SettingsTransport transport;
public final SettingsPort port;
public final SettingsJMX jmx;
public final SettingsGraph graph;
public final SettingsTokenRange tokenRange;
@ -53,11 +55,13 @@ public class StressSettings implements Serializable
SettingsColumn columns,
SettingsErrors errors,
SettingsLog log,
SettingsCredentials credentials,
SettingsMode mode,
SettingsNode node,
SettingsSchema schema,
SettingsTransport transport,
SettingsPort port,
SettingsJMX jmx,
SettingsGraph graph,
SettingsTokenRange tokenRange)
{
@ -68,11 +72,13 @@ public class StressSettings implements Serializable
this.columns = columns;
this.errors = errors;
this.log = log;
this.credentials = credentials;
this.mode = mode;
this.node = node;
this.schema = schema;
this.transport = transport;
this.port = port;
this.jmx = jmx;
this.graph = graph;
this.tokenRange = tokenRange;
}
@ -194,10 +200,12 @@ public class StressSettings implements Serializable
SettingsColumn columns = SettingsColumn.get(clArgs);
SettingsErrors errors = SettingsErrors.get(clArgs);
SettingsLog log = SettingsLog.get(clArgs);
SettingsMode mode = SettingsMode.get(clArgs);
SettingsCredentials credentials = SettingsCredentials.get(clArgs);
SettingsMode mode = SettingsMode.get(clArgs, credentials);
SettingsNode node = SettingsNode.get(clArgs);
SettingsSchema schema = SettingsSchema.get(clArgs, command);
SettingsTransport transport = SettingsTransport.get(clArgs);
SettingsTransport transport = SettingsTransport.get(clArgs, credentials);
SettingsJMX jmx = SettingsJMX.get(clArgs, credentials);
SettingsGraph graph = SettingsGraph.get(clArgs, command);
if (!clArgs.isEmpty())
{
@ -216,7 +224,7 @@ public class StressSettings implements Serializable
System.exit(1);
}
return new StressSettings(command, rate, generate, insert, columns, errors, log, mode, node, schema, transport, port, graph, tokenRange);
return new StressSettings(command, rate, generate, insert, columns, errors, log, credentials, mode, node, schema, transport, port, jmx, graph, tokenRange);
}
private static Map<String, String[]> parseMap(String[] args)
@ -290,10 +298,14 @@ public class StressSettings implements Serializable
transport.printSettings(out);
out.println("Port:");
port.printSettings(out);
out.println("JMX:");
jmx.printSettings(out);
out.println("Graph:");
graph.printSettings(out);
out.println("TokenRange:");
tokenRange.printSettings(out);
out.println("Credentials file:");
credentials.printSettings(out);
if (command.type == Command.USER)
{

View File

@ -28,6 +28,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.apache.cassandra.concurrent.NamedThreadFactory;
import org.apache.cassandra.stress.settings.SettingsJMX;
import org.apache.cassandra.tools.NodeProbe;
public class JmxCollector implements Callable<JmxCollector.GcStats>
@ -76,23 +77,26 @@ public class JmxCollector implements Callable<JmxCollector.GcStats>
final NodeProbe[] probes;
// TODO: should expand to whole cluster
public JmxCollector(Collection<String> hosts, int port)
public JmxCollector(Collection<String> hosts, int port, SettingsJMX jmx)
{
probes = new NodeProbe[hosts.size()];
int i = 0;
for (String host : hosts)
{
probes[i] = connect(host, port);
probes[i] = connect(host, port, jmx);
probes[i].getAndResetGCStats();
i++;
}
}
private static NodeProbe connect(String host, int port)
private static NodeProbe connect(String host, int port, SettingsJMX jmx)
{
try
{
return new NodeProbe(host, port);
if (jmx.user != null && jmx.password != null)
return new NodeProbe(host, port, jmx.user, jmx.password);
else
return new NodeProbe(host, port);
}
catch (IOException e)
{

View File

@ -0,0 +1,126 @@
/*
* 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.stress.settings;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.junit.Test;
import org.apache.cassandra.io.util.File;
import org.apache.cassandra.io.util.FileUtils;
import static org.apache.cassandra.io.util.File.WriteMode.OVERWRITE;
import static org.apache.cassandra.stress.settings.SettingsCredentials.CQL_PASSWORD_PROPERTY_KEY;
import static org.apache.cassandra.stress.settings.SettingsCredentials.CQL_USERNAME_PROPERTY_KEY;
import static org.apache.cassandra.stress.settings.SettingsCredentials.JMX_PASSWORD_PROPERTY_KEY;
import static org.apache.cassandra.stress.settings.SettingsCredentials.JMX_USERNAME_PROPERTY_KEY;
import static org.apache.cassandra.stress.settings.SettingsCredentials.TRANSPORT_KEYSTORE_PASSWORD_PROPERTY_KEY;
import static org.apache.cassandra.stress.settings.SettingsCredentials.TRANSPORT_TRUSTSTORE_PASSWORD_PROPERTY_KEY;
import static org.junit.Assert.assertEquals;
public class SettingsCredentialsTest
{
@Test
public void testReadCredentialsFromFileMixed() throws Exception
{
Properties properties = new Properties();
properties.setProperty(CQL_USERNAME_PROPERTY_KEY, "cqluserfromfile");
properties.setProperty(CQL_PASSWORD_PROPERTY_KEY, "cqlpasswordfromfile");
properties.setProperty(JMX_USERNAME_PROPERTY_KEY, "jmxuserfromfile");
properties.setProperty(JMX_PASSWORD_PROPERTY_KEY, "jmxpasswordfromfile");
properties.setProperty(TRANSPORT_KEYSTORE_PASSWORD_PROPERTY_KEY, "keystorestorepasswordfromfile");
properties.setProperty(TRANSPORT_TRUSTSTORE_PASSWORD_PROPERTY_KEY, "truststorepasswordfromfile");
File tempFile = FileUtils.createTempFile("cassandra-stress-credentials-test", "properties");
try (Writer w = tempFile.newWriter(OVERWRITE))
{
properties.store(w, null);
}
Map<String, String[]> args = new HashMap<>();
args.put("write", new String[]{});
args.put("-mode", new String[]{ "cql3", "native", "password=cqlpasswordoncommandline" });
args.put("-transport", new String[]{ "truststore=sometruststore", "keystore=somekeystore" });
args.put("-jmx", new String[]{ "password=jmxpasswordoncommandline" });
args.put("-credentials-file", new String[]{ tempFile.absolutePath() });
StressSettings settings = StressSettings.get(args);
assertEquals("cqluserfromfile", settings.credentials.cqlUsername);
assertEquals("cqlpasswordfromfile", settings.credentials.cqlPassword);
assertEquals("jmxuserfromfile", settings.credentials.jmxUsername);
assertEquals("jmxpasswordfromfile", settings.credentials.jmxPassword);
assertEquals("keystorestorepasswordfromfile", settings.credentials.transportKeystorePassword);
assertEquals("truststorepasswordfromfile", settings.credentials.transportTruststorePassword);
assertEquals("cqluserfromfile", settings.mode.username);
assertEquals("cqlpasswordoncommandline", settings.mode.password);
assertEquals("jmxuserfromfile", settings.jmx.user);
assertEquals("jmxpasswordoncommandline", settings.jmx.password);
assertEquals("keystorestorepasswordfromfile", settings.transport.getEncryptionOptions().keystore_password);
assertEquals("truststorepasswordfromfile", settings.transport.getEncryptionOptions().truststore_password);
}
@Test
public void testReadCredentialsFromFileOverridenByCommandLine() throws Exception
{
Properties properties = new Properties();
properties.setProperty(CQL_USERNAME_PROPERTY_KEY, "cqluserfromfile");
properties.setProperty(CQL_PASSWORD_PROPERTY_KEY, "cqlpasswordfromfile");
properties.setProperty(JMX_USERNAME_PROPERTY_KEY, "jmxuserfromfile");
properties.setProperty(JMX_PASSWORD_PROPERTY_KEY, "jmxpasswordfromfile");
properties.setProperty(TRANSPORT_KEYSTORE_PASSWORD_PROPERTY_KEY, "keystorestorepasswordfromfile");
properties.setProperty(TRANSPORT_TRUSTSTORE_PASSWORD_PROPERTY_KEY, "truststorepasswordfromfile");
File tempFile = FileUtils.createTempFile("cassandra-stress-credentials-test", "properties");
try (Writer w = tempFile.newWriter(OVERWRITE))
{
properties.store(w, null);
}
Map<String, String[]> args = new HashMap<>();
args.put("write", new String[]{});
args.put("-mode", new String[]{ "cql3", "native", "password=cqlpasswordoncommandline", "user=cqluseroncommandline" });
args.put("-jmx", new String[]{ "password=jmxpasswordoncommandline", "user=jmxuseroncommandline" });
args.put("-transport", new String[]{ "truststore=sometruststore",
"keystore=somekeystore",
"truststore-password=truststorepasswordfromcommandline",
"keystore-password=keystorepasswordfromcommandline" });
args.put("-credentials-file", new String[]{ tempFile.absolutePath() });
StressSettings settings = StressSettings.get(args);
assertEquals("cqluserfromfile", settings.credentials.cqlUsername);
assertEquals("cqlpasswordfromfile", settings.credentials.cqlPassword);
assertEquals("jmxuserfromfile", settings.credentials.jmxUsername);
assertEquals("jmxpasswordfromfile", settings.credentials.jmxPassword);
assertEquals("keystorestorepasswordfromfile", settings.credentials.transportKeystorePassword);
assertEquals("truststorepasswordfromfile", settings.credentials.transportTruststorePassword);
assertEquals("cqluseroncommandline", settings.mode.username);
assertEquals("cqlpasswordoncommandline", settings.mode.password);
assertEquals("jmxuseroncommandline", settings.jmx.user);
assertEquals("jmxpasswordoncommandline", settings.jmx.password);
assertEquals("truststorepasswordfromcommandline", settings.transport.getEncryptionOptions().truststore_password);
assertEquals("keystorepasswordfromcommandline", settings.transport.getEncryptionOptions().keystore_password);
}
}