Add version command to cassandra stress

Patch by Chris Batey; reviewed by tjake for CASSANDRA-12258
diff --git a/CHANGES.txt b/CHANGES.txt
index f759b7e..f9ff43d 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.10
+ * Add version command to cassandra-stress (CASSANDRA-12258)
  * Create compaction-stress tool (CASSANDRA-11844)
  * Garbage-collecting compaction operation and schema option (CASSANDRA-7019)
  * Add schema to snapshot manifest, add USING TIMESTAMP clause to ALTER TABLE statements (CASSANDRA-7190)
diff --git a/tools/stress/src/org/apache/cassandra/stress/settings/Command.java b/tools/stress/src/org/apache/cassandra/stress/settings/Command.java
index c47c5d2..d8ac5d1 100644
--- a/tools/stress/src/org/apache/cassandra/stress/settings/Command.java
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/Command.java
@@ -61,7 +61,8 @@
 
     HELP(false, null, "-?", "Print help for a command or option", null),
     PRINT(false, null, "Inspect the output of a distribution definition", null),
-    LEGACY(false, null, "Legacy support mode", null)
+    LEGACY(false, null, "Legacy support mode", null),
+    VERSION(false, null, "Print the version of cassandra stress", null)
     ;
 
     private static final Map<String, Command> LOOKUP;
diff --git a/tools/stress/src/org/apache/cassandra/stress/settings/SettingsMisc.java b/tools/stress/src/org/apache/cassandra/stress/settings/SettingsMisc.java
index 5735f9d..7f2335f 100644
--- a/tools/stress/src/org/apache/cassandra/stress/settings/SettingsMisc.java
+++ b/tools/stress/src/org/apache/cassandra/stress/settings/SettingsMisc.java
@@ -21,15 +21,22 @@
  */
 
 
+import java.io.IOException;
 import java.io.PrintStream;
 import java.io.Serializable;
+import java.net.URL;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Resources;
 
 import org.apache.cassandra.stress.generate.Distribution;
 
-public class SettingsMisc implements Serializable
+class SettingsMisc implements Serializable
 {
 
     static boolean maybeDoSpecial(Map<String, String[]> clArgs)
@@ -38,10 +45,12 @@
             return true;
         if (maybePrintDistribution(clArgs))
             return true;
+        if (maybePrintVersion(clArgs))
+            return true;
         return false;
     }
 
-    static final class PrintDistribution extends GroupedOptions
+    private static final class PrintDistribution extends GroupedOptions
     {
         final OptionDistribution dist = new OptionDistribution("dist=", null, "A mathematical distribution");
 
@@ -52,7 +61,8 @@
         }
     }
 
-    static boolean maybePrintDistribution(Map<String, String[]> clArgs)
+
+    private static boolean maybePrintDistribution(Map<String, String[]> clArgs)
     {
         final String[] args = clArgs.get("print");
         if (args == null)
@@ -68,17 +78,17 @@
         return true;
     }
 
-    static void printDistribution(Distribution dist)
+    private static void printDistribution(Distribution dist)
     {
         PrintStream out = System.out;
         out.println("% of samples    Range       % of total");
         String format = "%-16.1f%-12d%12.1f";
         double rangemax = dist.inverseCumProb(1d) / 100d;
-        for (double d : new double[] { 0.1d, 0.2d, 0.3d, 0.4d, 0.5d, 0.6d, 0.7d, 0.8d, 0.9d, 0.95d, 0.99d, 1d })
+        for (double d : new double[]{ 0.1d, 0.2d, 0.3d, 0.4d, 0.5d, 0.6d, 0.7d, 0.8d, 0.9d, 0.95d, 0.99d, 1d })
         {
             double sampleperc = d * 100;
             long max = dist.inverseCumProb(d);
-            double rangeperc = max/ rangemax;
+            double rangeperc = max / rangemax;
             out.println(String.format(format, sampleperc, max, rangeperc));
         }
     }
@@ -98,7 +108,7 @@
                 {
                     String p = clArgs.keySet().iterator().next();
                     if (clArgs.get(p).length == 0)
-                        params = new String[] {p};
+                        params = new String[]{ p };
                 }
             }
             else
@@ -115,6 +125,37 @@
         throw new IllegalArgumentException("Invalid command/option provided to help");
     }
 
+    private static boolean maybePrintVersion(Map<String, String[]> clArgs)
+    {
+        if (clArgs.containsKey("version"))
+        {
+            try
+            {
+                URL url = Resources.getResource("org/apache/cassandra/config/version.properties");
+                System.out.println(parseVersionFile(Resources.toString(url, Charsets.UTF_8)));
+            }
+            catch (IOException e)
+            {
+                e.printStackTrace(System.err);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    static String parseVersionFile(String versionFileContents)
+    {
+        Matcher matcher = Pattern.compile(".*?CassandraVersion=(.*?)$").matcher(versionFileContents);
+        if (matcher.find())
+        {
+            return "Version: " + matcher.group(1);
+        }
+        else
+        {
+            return "Unable to find version information";
+        }
+    }
+
     public static void printHelp()
     {
         System.out.println("Usage:      cassandra-stress <command> [options]");
@@ -151,7 +192,7 @@
         throw new IllegalArgumentException("Invalid command or option provided to command help");
     }
 
-    public static Runnable helpHelpPrinter()
+    static Runnable helpHelpPrinter()
     {
         return new Runnable()
         {
@@ -169,7 +210,7 @@
         };
     }
 
-    public static Runnable printHelpPrinter()
+    static Runnable printHelpPrinter()
     {
         return new Runnable()
         {
@@ -188,7 +229,7 @@
         };
     }
 
-    public static Runnable sendToDaemonHelpPrinter()
+    static Runnable sendToDaemonHelpPrinter()
     {
         return new Runnable()
         {
@@ -202,7 +243,7 @@
         };
     }
 
-    public static String getSendToDaemon(Map<String, String[]> clArgs)
+    static String getSendToDaemon(Map<String, String[]> clArgs)
     {
         String[] params = clArgs.remove("-send-to");
         if (params == null)
@@ -216,7 +257,5 @@
             System.exit(1);
         }
         return params[0];
-
     }
-
 }
diff --git a/tools/stress/test/unit/org/apache/cassandra/stress/settings/SettingsMiscTest.java b/tools/stress/test/unit/org/apache/cassandra/stress/settings/SettingsMiscTest.java
new file mode 100644
index 0000000..78d9817
--- /dev/null
+++ b/tools/stress/test/unit/org/apache/cassandra/stress/settings/SettingsMiscTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.util.Collections;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class SettingsMiscTest
+{
+    @Test
+    public void versionTriggersSpecialOption() throws Exception
+    {
+        assertTrue(SettingsMisc.maybeDoSpecial(ImmutableMap.of("version", new String[] {})));
+    }
+
+    @Test
+    public void noSpecialOptions() throws Exception
+    {
+        assertFalse(SettingsMisc.maybeDoSpecial(Collections.emptyMap()));
+    }
+
+    @Test
+    public void parsesVersionMatch() throws Exception
+    {
+        String versionString = SettingsMisc.parseVersionFile("CassandraVersion=TheBestVersion\n");
+        assertEquals("Version: TheBestVersion", versionString);
+    }
+
+    @Test
+    public void parsesVersionNoMatch() throws Exception
+    {
+        String versionString = SettingsMisc.parseVersionFile("VersionFileChangedFormat :(");
+        assertEquals("Unable to find version information", versionString);
+    }
+}