This is an automated email from the ASF dual-hosted git repository.

smiklosovic pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 4d2ee09ffa Make cassandra-stress able to read all credentials from a 
file
4d2ee09ffa is described below

commit 4d2ee09ffabf7fb086fb9e2bd042fc946e72c3ec
Author: Stefan Miklosovic <[email protected]>
AuthorDate: Fri May 19 12:31:40 2023 +0200

    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
---
 CHANGES.txt                                        |   1 +
 NEWS.txt                                           |   2 +
 .../pages/managing/tools/cassandra_stress.adoc     |   4 +
 .../cassandra/stress/report/StressMetrics.java     |   3 +-
 .../cassandra/stress/settings/CliOption.java       |  13 ++-
 .../stress/settings/SettingsCredentials.java       | 129 +++++++++++++++++++++
 .../cassandra/stress/settings/SettingsJMX.java     |  98 ++++++++++++++++
 .../cassandra/stress/settings/SettingsMode.java    |  26 +++--
 .../stress/settings/SettingsTransport.java         |  46 ++++++--
 .../cassandra/stress/settings/StressSettings.java  |  18 ++-
 .../apache/cassandra/stress/util/JmxCollector.java |  12 +-
 .../stress/settings/SettingsCredentialsTest.java   | 126 ++++++++++++++++++++
 12 files changed, 446 insertions(+), 32 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index 168e5b2bed..b3e78b4dea 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -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)
diff --git a/NEWS.txt b/NEWS.txt
index 5c3267e1e0..b209e5adb0 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -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
 ---------
diff --git a/doc/modules/cassandra/pages/managing/tools/cassandra_stress.adoc 
b/doc/modules/cassandra/pages/managing/tools/cassandra_stress.adoc
index 7cf3548c48..e24367caca 100644
--- a/doc/modules/cassandra/pages/managing/tools/cassandra_stress.adoc
+++ b/doc/modules/cassandra/pages/managing/tools/cassandra_stress.adoc
@@ -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
diff --git 
a/tools/stress/src/org/apache/cassandra/stress/report/StressMetrics.java 
b/tools/stress/src/org/apache/cassandra/stress/report/StressMetrics.java
index b50dfd23f0..85bfc32121 100644
--- a/tools/stress/src/org/apache/cassandra/stress/report/StressMetrics.java
+++ b/tools/stress/src/org/apache/cassandra/stress/report/StressMetrics.java
@@ -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)
         {
diff --git 
a/tools/stress/src/org/apache/cassandra/stress/settings/CliOption.java 
b/tools/stress/src/org/apache/cassandra/stress/settings/CliOption.java
index eba276e241..3d721f3cc7 100644
--- a/tools/stress/src/org/apache/cassandra/stress/settings/CliOption.java
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/CliOption.java
@@ -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("_", "-");
+    }
 }
diff --git 
a/tools/stress/src/org/apache/cassandra/stress/settings/SettingsCredentials.java
 
b/tools/stress/src/org/apache/cassandra/stress/settings/SettingsCredentials.java
new file mode 100644
index 0000000000..76513b75f9
--- /dev/null
+++ 
b/tools/stress/src/org/apache/cassandra/stress/settings/SettingsCredentials.java
@@ -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;
+    }
+}
diff --git 
a/tools/stress/src/org/apache/cassandra/stress/settings/SettingsJMX.java 
b/tools/stress/src/org/apache/cassandra/stress/settings/SettingsJMX.java
new file mode 100644
index 0000000000..af202bef43
--- /dev/null
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/SettingsJMX.java
@@ -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;
+    }
+}
diff --git 
a/tools/stress/src/org/apache/cassandra/stress/settings/SettingsMode.java 
b/tools/stress/src/org/apache/cassandra/stress/settings/SettingsMode.java
index 7e53547e81..b009d04177 100644
--- a/tools/stress/src/org/apache/cassandra/stress/settings/SettingsMode.java
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/SettingsMode.java
@@ -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()
diff --git 
a/tools/stress/src/org/apache/cassandra/stress/settings/SettingsTransport.java 
b/tools/stress/src/org/apache/cassandra/stress/settings/SettingsTransport.java
index 5f22a7b7c2..cf62999822 100644
--- 
a/tools/stress/src/org/apache/cassandra/stress/settings/SettingsTransport.java
+++ 
b/tools/stress/src/org/apache/cassandra/stress/settings/SettingsTransport.java
@@ -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()
diff --git 
a/tools/stress/src/org/apache/cassandra/stress/settings/StressSettings.java 
b/tools/stress/src/org/apache/cassandra/stress/settings/StressSettings.java
index 2f76dfb0fd..9b831187f2 100644
--- a/tools/stress/src/org/apache/cassandra/stress/settings/StressSettings.java
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/StressSettings.java
@@ -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)
         {
diff --git 
a/tools/stress/src/org/apache/cassandra/stress/util/JmxCollector.java 
b/tools/stress/src/org/apache/cassandra/stress/util/JmxCollector.java
index 8cfbebbd65..24cb4c788b 100644
--- a/tools/stress/src/org/apache/cassandra/stress/util/JmxCollector.java
+++ b/tools/stress/src/org/apache/cassandra/stress/util/JmxCollector.java
@@ -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)
         {
diff --git 
a/tools/stress/test/unit/org/apache/cassandra/stress/settings/SettingsCredentialsTest.java
 
b/tools/stress/test/unit/org/apache/cassandra/stress/settings/SettingsCredentialsTest.java
new file mode 100644
index 0000000000..a1a07c4612
--- /dev/null
+++ 
b/tools/stress/test/unit/org/apache/cassandra/stress/settings/SettingsCredentialsTest.java
@@ -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);
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to