Repository: eclipse-vertx/vert.x

Problem statement:
Configure cluster public host and port from command line
Currently, it's only possible to configure cluster public host and port with sysprops.

This will help having a consistent user experience (cluster host and port can be configured via the command line).


Hints:

Patch:
diff --git a/src/main/java/io/vertx/core/impl/launcher/commands/BareCommand.java b/src/main/java/io/vertx/core/impl/launcher/commands/BareCommand.java
--- a/src/main/java/io/vertx/core/impl/launcher/commands/BareCommand.java
+++ b/src/main/java/io/vertx/core/impl/launcher/commands/BareCommand.java
@@ -29,6 +29,7 @@
 import java.net.NetworkInterface;
 import java.net.SocketException;
 import java.util.Enumeration;
+import java.util.Objects;
 import java.util.Properties;
 import java.util.Scanner;
 import java.util.concurrent.CountDownLatch;
@@ -51,11 +52,13 @@ public class BareCommand extends ClasspathHandler {
   public static final String METRICS_OPTIONS_PROP_PREFIX = "vertx.metrics.options.";
 
   protected Vertx vertx;
-  protected int clusterPort;
 
+  protected int clusterPort;
   protected String clusterHost;
-  protected int quorum;
+  protected int clusterPublicPort;
+  protected String clusterPublicHost;
 
+  protected int quorum;
   protected String haGroup;
 
   protected String vertxOptions;
@@ -113,6 +116,29 @@ public void setClusterHost(String host) {
     this.clusterHost = host;
   }
 
+  /**
+   * Sets the cluster public port.
+   *
+   * @param port the port
+   */
+  @Option(longName = "cluster-public-port", argName = "public-port")
+  @Description("Public port to use for cluster communication. Default is -1 which means same as cluster port.")
+  @DefaultValue("-1")
+  public void setClusterPublicPort(int port) {
+    this.clusterPublicPort = port;
+  }
+
+  /**
+   * Sets the cluster public host.
+   *
+   * @param host the host
+   */
+  @Option(longName = "cluster-public-host", argName = "public-host")
+  @Description("Public host to bind to for cluster communication. If not specified, Vert.x will use the same as cluster host.")
+  public void setClusterPublicHost(String host) {
+    this.clusterPublicHost = host;
+  }
+
   /**
    * The Vert.x options, it can be a json file or a json string.
    *
@@ -186,12 +212,18 @@ protected Vertx startVertx() {
     Vertx instance;
     if (isClustered()) {
       log.info("Starting clustering...");
-      if (!options.getClusterHost().equals(VertxOptions.DEFAULT_CLUSTER_HOST)) {
+      if (!Objects.equals(options.getClusterHost(), VertxOptions.DEFAULT_CLUSTER_HOST)) {
         clusterHost = options.getClusterHost();
       }
       if (options.getClusterPort() != VertxOptions.DEFAULT_CLUSTER_PORT) {
         clusterPort = options.getClusterPort();
       }
+      if (!Objects.equals(options.getClusterPublicHost(), VertxOptions.DEFAULT_CLUSTER_PUBLIC_HOST)) {
+        clusterPublicHost = options.getClusterPublicHost();
+      }
+      if (options.getClusterPublicPort() != VertxOptions.DEFAULT_CLUSTER_PUBLIC_PORT) {
+        clusterPublicPort = options.getClusterPublicPort();
+      }
       if (clusterHost == null) {
         clusterHost = getDefaultAddress();
         if (clusterHost == null) {
@@ -204,7 +236,12 @@ protected Vertx startVertx() {
       CountDownLatch latch = new CountDownLatch(1);
       AtomicReference<AsyncResult<Vertx>> result = new AtomicReference<>();
 
-      options.setClusterHost(clusterHost).setClusterPort(clusterPort).setClustered(true);
+      options.setClustered(true)
+        .setClusterHost(clusterHost).setClusterPort(clusterPort)
+        .setClusterPublicHost(clusterPublicHost);
+      if (clusterPublicPort != -1) {
+        options.setClusterPublicPort(clusterPublicPort);
+      }
       if (getHA()) {
         options.setHAEnabled(true);
         if (haGroup != null) {
diff --git a/src/main/java/io/vertx/core/impl/launcher/commands/RunCommand.java b/src/main/java/io/vertx/core/impl/launcher/commands/RunCommand.java
--- a/src/main/java/io/vertx/core/impl/launcher/commands/RunCommand.java
+++ b/src/main/java/io/vertx/core/impl/launcher/commands/RunCommand.java
@@ -201,14 +201,14 @@ public void setRedeployStopWaitingTime(long period) {
   public void setUp(ExecutionContext context) throws CLIException {
     super.setUp(context);
 
-    // If cluster-host and / or port is set, cluster need to have been explicitly set
-    io.vertx.core.cli.Option clusterHostOption = executionContext.cli().getOption("cluster-host");
-    io.vertx.core.cli.Option clusterPortOption = executionContext.cli().getOption("cluster-port");
     CommandLine commandLine = executionContext.commandLine();
-    if ((!isClustered()) &&
-        (commandLine.isOptionAssigned(clusterHostOption)
-            || commandLine.isOptionAssigned(clusterPortOption))) {
-      throw new CLIException("The option -cluster-host and -cluster-port requires -cluster to be enabled");
+    if (!isClustered() && (
+      commandLine.isOptionAssigned(executionContext.cli().getOption("cluster-host"))
+        || commandLine.isOptionAssigned(executionContext.cli().getOption("cluster-port"))
+        || commandLine.isOptionAssigned(executionContext.cli().getOption("cluster-public-host"))
+        || commandLine.isOptionAssigned(executionContext.cli().getOption("cluster-public-port"))
+    )) {
+      throw new CLIException("The -cluster-xxx options require -cluster to be enabled");
     }
 
     // If quorum and / or ha-group, ha need to have been explicitly set
@@ -357,6 +357,12 @@ protected void startAsBackgroundApplication(Handler<Void> onCompletion) {
     if (clusterPort != 0) {
       args.add("--cluster-port=" + clusterPort);
     }
+    if (clusterPublicHost != null) {
+      args.add("--cluster-public-host=" + clusterPublicHost);
+    }
+    if (clusterPublicPort != -1) {
+      args.add("--cluster-public-port=" + clusterPublicPort);
+    }
     if (ha) {
       args.add("--ha");
     }


Test patch:
diff --git a/src/test/java/io/vertx/core/LauncherTest.java b/src/test/java/io/vertx/core/LauncherTest.java
--- a/src/test/java/io/vertx/core/LauncherTest.java
+++ b/src/test/java/io/vertx/core/LauncherTest.java
@@ -651,6 +651,17 @@ public void testConfigureClusterHostPortFromCommandLine() throws Exception {
     assertEquals(-1, launcher.options.getClusterPublicPort());
   }
 
+  @Test
+  public void testConfigureClusterPublicHostPortFromCommandLine() throws Exception {
+    int clusterPublicPort = TestUtils.randomHighPortInt();
+    MyLauncher launcher = new MyLauncher();
+    String[] args = {"run", "java:" + TestVerticle.class.getCanonicalName(), "-cluster", "--cluster-public-host", "127.0.0.1", "--cluster-public-port", Integer.toString(clusterPublicPort)};
+    launcher.dispatch(args);
+    assertWaitUntil(() -> TestVerticle.instanceCount.get() == 1);
+    assertEquals("127.0.0.1", launcher.options.getClusterPublicHost());
+    assertEquals(clusterPublicPort, launcher.options.getClusterPublicPort());
+  }
+
   @Test
   public void testOverrideClusterHostPortFromProperties() throws Exception {
     int clusterPort = TestUtils.randomHighPortInt();
@@ -675,6 +686,7 @@ public void testOverrideClusterHostPortFromProperties() throws Exception {
   @Test
   public void testOverrideClusterHostPortFromCommandLine() throws Exception {
     int clusterPort = TestUtils.randomHighPortInt();
+    int clusterPublicPort = TestUtils.randomHighPortInt();
     int newClusterPort = TestUtils.randomHighPortInt();
     int newClusterPublicPort = TestUtils.randomHighPortInt();
     MyLauncher launcher = new MyLauncher();
@@ -682,7 +694,12 @@ public void testOverrideClusterHostPortFromCommandLine() throws Exception {
     launcher.clusterPort = newClusterPort;
     launcher.clusterPublicHost = "127.0.0.3";
     launcher.clusterPublicPort = newClusterPublicPort;
-    String[] args = {"run", "java:" + TestVerticle.class.getCanonicalName(), "-cluster", "--cluster-host", "127.0.0.2", "--cluster-port", Integer.toString(clusterPort)};
+    String[] args = {
+      "run", "java:" + TestVerticle.class.getCanonicalName(),
+      "-cluster",
+      "--cluster-host", "127.0.0.2", "--cluster-port", Integer.toString(clusterPort),
+      "--cluster-public-host", "127.0.0.4", "--cluster-public-port", Integer.toString(clusterPublicPort)
+    };
     launcher.dispatch(args);
     assertWaitUntil(() -> TestVerticle.instanceCount.get() == 1);
     assertEquals("127.0.0.1", launcher.options.getClusterHost());