e2e-tests: Add JAVA_OPTS support to the framework

Unhardcode hostname and port values to start with, which typically vary
across test execution environments or deployments. This allows for less
json scenario data file duplication and proliferation.

Base this JAVA_OPTS solution on [1] below. If no JAVA_OPTS property|ies
gets defined in the scenario execution shell environment, use the
well-known default value(s) coded in. Document how to use JAVA_OPTS.

Adapt the showcase core CloneUsingBothProtocols and aggregated project
scenarios accordingly. Make them reuse core's amended GerritSimulation.

[1] https://gatling.io/docs/current/cookbook/passing_parameters

Change-Id: I45cf4b7ccf5cc51c8bd31fedbac99ae4bed053bb
diff --git a/Documentation/dev-e2e-tests.txt b/Documentation/dev-e2e-tests.txt
index 17334e3..56668c7 100644
--- a/Documentation/dev-e2e-tests.txt
+++ b/Documentation/dev-e2e-tests.txt
@@ -105,16 +105,16 @@
 file contains the commands and repository used during the e2e test. That file currently looks like
 below. This scenario serves as a simple example with no actual load in it. It can be used to test
 or validate the local setup. More complex scenarios can be further developed, under the
-`com.google.gerrit.scenarios` package.
+`com.google.gerrit.scenarios` package. The uppercase keywords are discussed further below.
 
 ----
 [
   {
-    "url": "ssh://admin@localhost:29418/loadtest-repo",
+    "url": "ssh://admin@HOSTNAME:SSH_PORT/loadtest-repo",
     "cmd": "clone"
   },
   {
-    "url": "http://localhost:8080/loadtest-repo",
+    "url": "http://HOSTNAME:HTTP_PORT/loadtest-repo",
     "cmd": "clone"
   }
 ]
@@ -141,6 +141,21 @@
 Executing the `CloneUsingBothProtocols` scenario, as is, does require setting the http credentials.
 That is because of the aforementioned create/delete project (http) scenarios composed within it.
 
+=== Environment properties
+
+The `JAVA_OPTS` environment variable
+link:https://gatling.io/docs/current/cookbook/passing_parameters[can optionally be used] to define
+non-default values for keys found in scenario `json` data files. That variable can currently be set
+with either one or many of these supported properties, from the core framework:
+
+* `-Dcom.google.gerrit.scenarios.hostname=localhost`
+* `-Dcom.google.gerrit.scenarios.ssh_port=29418`
+* `-Dcom.google.gerrit.scenarios.http_port=8080`
+
+Above, the properties can be set with values matching specific deployment topologies under test.
+The example values shown above are the currently coded default ones. The framework could support
+differing or more properties over time. Plugin (non-core) scenarios may do so just as well.
+
 == How to run tests
 
 Run all tests:
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CloneUsingBothProtocols.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CloneUsingBothProtocols.json
index 0335b2f..1125687 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CloneUsingBothProtocols.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CloneUsingBothProtocols.json
@@ -1,10 +1,10 @@
 [
   {
-    "url": "ssh://admin@localhost:29418/loadtest-repo",
+    "url": "ssh://admin@HOSTNAME:SSH_PORT/loadtest-repo",
     "cmd": "clone"
   },
   {
-    "url": "http://localhost:8080/loadtest-repo",
+    "url": "http://HOSTNAME:HTTP_PORT/loadtest-repo",
     "cmd": "clone"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject.json
index 2e54de5..f1a38ae 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateProject.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "http://localhost:8080/a/projects/loadtest-repo"
+    "url": "http://HOSTNAME:HTTP_PORT/a/projects/loadtest-repo"
   }
 ]
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteProject.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteProject.json
index 9312fb4..e5167b5 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteProject.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/DeleteProject.json
@@ -1,5 +1,5 @@
 [
   {
-    "url": "http://localhost:8080/a/projects/loadtest-repo/delete-project~delete"
+    "url": "http://HOSTNAME:HTTP_PORT/a/projects/loadtest-repo/delete-project~delete"
   }
 ]
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CloneUsingBothProtocols.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CloneUsingBothProtocols.scala
index 19fbf1b..182ac48 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CloneUsingBothProtocols.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CloneUsingBothProtocols.scala
@@ -21,7 +21,7 @@
 import scala.concurrent.duration._
 
 class CloneUsingBothProtocols extends GitSimulation {
-  private val data: FileBasedFeederBuilder[Any]#F = jsonFile(resource).queue
+  private val data: FileBasedFeederBuilder[Any]#F#F = jsonFile(resource).convert(url).queue
 
   private val test: ScenarioBuilder = scenario(name)
       .feed(data)
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateProject.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateProject.scala
index 58c8994..13d3519 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateProject.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateProject.scala
@@ -19,7 +19,7 @@
 import io.gatling.core.structure.ScenarioBuilder
 
 class CreateProject extends GerritSimulation {
-  private val data: FileBasedFeederBuilder[Any]#F = jsonFile(resource).queue
+  private val data: FileBasedFeederBuilder[Any]#F#F = jsonFile(resource).convert(url).queue
 
   val test: ScenarioBuilder = scenario(name)
       .feed(data)
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteProject.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteProject.scala
index 4b723cb..70b901d 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteProject.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/DeleteProject.scala
@@ -19,7 +19,7 @@
 import io.gatling.core.structure.ScenarioBuilder
 
 class DeleteProject extends GerritSimulation {
-  private val data: FileBasedFeederBuilder[Any]#F = jsonFile(resource).queue
+  private val data: FileBasedFeederBuilder[Any]#F#F = jsonFile(resource).convert(url).queue
 
   val test: ScenarioBuilder = scenario(name)
       .feed(data)
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala
index b628bc7..a159977 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/GerritSimulation.scala
@@ -23,7 +23,8 @@
 class GerritSimulation extends Simulation {
   implicit val conf: GatlingGitConfiguration = GatlingGitConfiguration()
 
-  private val path: String = this.getClass.getPackage.getName.replaceAllLiterally(".", "/")
+  private val pack: String = this.getClass.getPackage.getName
+  private val path: String = pack.replaceAllLiterally(".", "/")
   protected val name: String = this.getClass.getSimpleName
   protected val resource: String = s"data/$path/$name.json"
 
@@ -31,4 +32,27 @@
   protected val httpProtocol: HttpProtocolBuilder = http.basicAuth(
     conf.httpConfiguration.userName,
     conf.httpConfiguration.password)
+
+  protected val url: PartialFunction[(String, Any), Any] = {
+    case ("url", url) =>
+      var in = replaceProperty("hostname", "localhost", url.toString)
+      in = replaceProperty("http_port", 8080, in)
+      replaceProperty("ssh_port", 29418, in)
+  }
+
+  private def replaceProperty(term: String, default: Any, in: String): String = {
+    val key: String = term.toUpperCase
+    val property = pack + "." + term
+    var value = default
+    default match {
+      case _: String =>
+        val propertyValue = Option(System.getProperty(property))
+        if (propertyValue.nonEmpty) {
+          value = propertyValue.get
+        }
+      case _: Integer =>
+        value = Integer.getInteger(property, default.asInstanceOf[Integer])
+    }
+    in.replaceAllLiterally(key, value.toString)
+  }
 }