SLIDER-1121 fix slider AM port allocation race condition

Project: http://git-wip-us.apache.org/repos/asf/incubator-slider/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-slider/commit/55dd69dd
Tree: http://git-wip-us.apache.org/repos/asf/incubator-slider/tree/55dd69dd
Diff: http://git-wip-us.apache.org/repos/asf/incubator-slider/diff/55dd69dd

Branch: refs/heads/feature/SLIDER-1107_AM_config_generation
Commit: 55dd69dd36e743e645a67a53429c185dc4612307
Parents: 4b7d5c8
Author: Billie Rinaldi <billie.rina...@gmail.com>
Authored: Tue May 17 06:57:29 2016 -0700
Committer: Billie Rinaldi <billie.rina...@gmail.com>
Committed: Tue May 17 06:57:29 2016 -0700

----------------------------------------------------------------------
 .../org/apache/slider/common/SliderKeys.java    |  5 --
 .../apache/slider/common/tools/PortScanner.java | 16 +----
 .../apache/slider/common/tools/SliderUtils.java | 16 +++++
 .../server/appmaster/SliderAppMaster.java       | 63 ++++++++------------
 .../appmaster/web/rest/agent/AgentWebApp.java   | 35 ++++++++++-
 5 files changed, 79 insertions(+), 56 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/55dd69dd/slider-core/src/main/java/org/apache/slider/common/SliderKeys.java
----------------------------------------------------------------------
diff --git a/slider-core/src/main/java/org/apache/slider/common/SliderKeys.java 
b/slider-core/src/main/java/org/apache/slider/common/SliderKeys.java
index 1d2d5f8..05c7048 100644
--- a/slider-core/src/main/java/org/apache/slider/common/SliderKeys.java
+++ b/slider-core/src/main/java/org/apache/slider/common/SliderKeys.java
@@ -266,11 +266,6 @@ public interface SliderKeys extends SliderXmlConfKeys {
    * {@value}
    */
   String KEY_ALLOWED_PORT_RANGE = "site.global.slider.allowed.ports";
-  
-  /**
-   * Allowed port range
-   */
-  String KEY_AM_ALLOWED_PORT_RANGE = "slider.am.allowed.port.range";
 
   /**
    * env var for custom JVM options.

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/55dd69dd/slider-core/src/main/java/org/apache/slider/common/tools/PortScanner.java
----------------------------------------------------------------------
diff --git 
a/slider-core/src/main/java/org/apache/slider/common/tools/PortScanner.java 
b/slider-core/src/main/java/org/apache/slider/common/tools/PortScanner.java
index b5b21ce..5b80f9f 100644
--- a/slider-core/src/main/java/org/apache/slider/common/tools/PortScanner.java
+++ b/slider-core/src/main/java/org/apache/slider/common/tools/PortScanner.java
@@ -19,6 +19,7 @@ package org.apache.slider.common.tools;
 import org.apache.slider.common.SliderExitCodes;
 import org.apache.slider.core.exceptions.SliderException;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
@@ -39,8 +40,6 @@ public class PortScanner {
   public PortScanner() {
   }
 
-  int nextPort = 1024;
-  
   public void setPortRange(String input) {
     // first split based on commas
     Set<Integer> inputPorts= new TreeSet<Integer>();
@@ -68,23 +67,14 @@ public class PortScanner {
     return remainingPortsToCheck;
   }
 
-  public int getAvailablePort() throws SliderException {
+  public int getAvailablePort() throws SliderException, IOException {
     if (remainingPortsToCheck != null) {
       return getAvailablePortViaPortArray();
     } else {
-      return getAvailablePortViaCounter();
+      return SliderUtils.getOpenPort();
     }
   }
 
-  private int getAvailablePortViaCounter() throws SliderException {
-    int port;
-    do {
-      port = nextPort;
-      nextPort++;
-    } while (!SliderUtils.isPortAvailable(port));
-    return port;
-  }
-  
   private int getAvailablePortViaPortArray() throws SliderException {
     boolean found = false;
     int availablePort = -1;

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/55dd69dd/slider-core/src/main/java/org/apache/slider/common/tools/SliderUtils.java
----------------------------------------------------------------------
diff --git 
a/slider-core/src/main/java/org/apache/slider/common/tools/SliderUtils.java 
b/slider-core/src/main/java/org/apache/slider/common/tools/SliderUtils.java
index eae80f5..746e468 100644
--- a/slider-core/src/main/java/org/apache/slider/common/tools/SliderUtils.java
+++ b/slider-core/src/main/java/org/apache/slider/common/tools/SliderUtils.java
@@ -1114,6 +1114,22 @@ public final class SliderUtils {
   }
 
   /**
+   * Get a random open port
+   * @return true if the port was available for listening on
+   */
+  public static int getOpenPort() throws IOException {
+    ServerSocket socket = null;
+    try {
+      socket = new ServerSocket(0);
+      return socket.getLocalPort();
+    } finally {
+      if (socket != null) {
+        socket.close();
+      }
+    }
+  }
+
+  /**
    * See if a port is available for listening on by trying to listen
    * on it and seeing if that works or fails.
    * @param port port to listen to

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/55dd69dd/slider-core/src/main/java/org/apache/slider/server/appmaster/SliderAppMaster.java
----------------------------------------------------------------------
diff --git 
a/slider-core/src/main/java/org/apache/slider/server/appmaster/SliderAppMaster.java
 
b/slider-core/src/main/java/org/apache/slider/server/appmaster/SliderAppMaster.java
index 8d30da7..0776a6c 100644
--- 
a/slider-core/src/main/java/org/apache/slider/server/appmaster/SliderAppMaster.java
+++ 
b/slider-core/src/main/java/org/apache/slider/server/appmaster/SliderAppMaster.java
@@ -404,11 +404,6 @@ public class SliderAppMaster extends 
AbstractSliderLaunchedService
   private SecurityConfiguration securityConfiguration;
 
   /**
-   * The port for the web application
-   */
-  private int webAppPort;
-
-  /**
    * Is security enabled?
    * Set early on in the {@link #createAndRunCluster(String)} operation.
    */
@@ -776,11 +771,23 @@ public class SliderAppMaster extends 
AbstractSliderLaunchedService
         uploadServerCertForLocalization(clustername, fs);
       }
 
-      webAppPort = getPortToRequest();
-      if (webAppPort == 0) {
-        // failure to find a port
-        throw new BadConfigException("Failed to fix a web application port");
-      }
+      // Web service endpoints: initialize
+      WebAppApiImpl webAppApi =
+          new WebAppApiImpl(
+              stateForProviders,
+              providerService,
+              certificateManager,
+              registryOperations,
+              metricsAndMonitoring,
+              actionQueues,
+              this,
+              contentCache);
+      initAMFilterOptions(serviceConf);
+
+      // start the agent web app
+      startAgentWebApp(appInformation, serviceConf, webAppApi);
+      int webAppPort = deployWebApplication(webAppApi);
+
       String scheme = WebAppUtils.HTTP_PREFIX;
       appMasterTrackingUrl = scheme + appMasterHostname + ":" + webAppPort;
 
@@ -926,7 +933,7 @@ public class SliderAppMaster extends 
AbstractSliderLaunchedService
     Path tmpDirPath = new Path(amTmpDir);
     Path launcherTmpDirPath = new Path(tmpDirPath, rolesTmpSubdir);
     fs.getFileSystem().mkdirs(launcherTmpDirPath);
-    
+
     //launcher service
     launchService = new RoleLaunchService(actionQueues,
                                           providerService,
@@ -972,25 +979,6 @@ public class SliderAppMaster extends 
AbstractSliderLaunchedService
     scheduleEscalation(instanceDefinition.getInternal());
 
     try {
-
-      // Web service endpoints: initialize
-
-      WebAppApiImpl webAppApi =
-          new WebAppApiImpl(
-              stateForProviders,
-              providerService,
-              certificateManager,
-              registryOperations,
-              metricsAndMonitoring,
-              actionQueues,
-              this,
-              contentCache);
-      initAMFilterOptions(serviceConf);
-
-      // start the agent web app
-      startAgentWebApp(appInformation, serviceConf, webAppApi);
-      deployWebApplication(webAppPort, webAppApi);
-
       // schedule YARN Registry registration
       queue(new ActionRegisterServiceInstance(clustername, appid));
 
@@ -1051,7 +1039,7 @@ public class SliderAppMaster extends 
AbstractSliderLaunchedService
   }
 
   /**
-   * List the node reports: uses {@link #yarnClient} as the login user
+   * List the node reports: uses {@link SliderYarnClientImpl} as the login user
    * @param yarnClient client to the RM
    * @return the node reports
    * @throws IOException
@@ -1083,17 +1071,18 @@ public class SliderAppMaster extends 
AbstractSliderLaunchedService
    *   Creates and starts the web application, and adds a
    *   <code>WebAppService</code> service under the AM, to ensure
    *   a managed web application shutdown.
-   * @param port port to deploy the web application on
    * @param webAppApi web app API instance
+   * @return port the web application is deployed on
    * @throws IOException general problems starting the webapp (network, etc)
    * @throws WebAppException other issues
    */
-  private void deployWebApplication(int port, WebAppApiImpl webAppApi)
-    throws IOException {
+  private int deployWebApplication(WebAppApiImpl webAppApi)
+      throws IOException, SliderException {
 
     try {
       webApp = new SliderAMWebApp(webAppApi);
       HttpConfig.Policy policy = HttpConfig.Policy.HTTP_ONLY;
+      int port = getPortToRequest();
       log.info("Launching web application at port {} with policy {}", port, 
policy);
 
       WebApps.$for(SliderAMWebApp.BASE_PATH,
@@ -1101,7 +1090,7 @@ public class SliderAppMaster extends 
AbstractSliderLaunchedService
           webAppApi,
           RestPaths.WS_CONTEXT)
              .withHttpPolicy(getConfig(), policy)
-             .at(port)
+             .at("0.0.0.0", port, true)
              .inDevMode()
              .start(webApp);
 
@@ -1109,6 +1098,7 @@ public class SliderAppMaster extends 
AbstractSliderLaunchedService
         new WebAppService<>("slider", webApp);
 
       deployChildService(webAppService);
+      return webApp.port();
     } catch (WebAppException e) {
       if (e.getCause() instanceof IOException) {
         throw (IOException)e.getCause();
@@ -1167,8 +1157,7 @@ public class SliderAppMaster extends 
AbstractSliderLaunchedService
    * @return the port to request.
    * @throws SliderException
    */
-  private int getPortToRequest()
-      throws SliderException {
+  private int getPortToRequest() throws SliderException, IOException {
     return portScanner.getAvailablePort();
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/55dd69dd/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/agent/AgentWebApp.java
----------------------------------------------------------------------
diff --git 
a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/agent/AgentWebApp.java
 
b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/agent/AgentWebApp.java
index 200fbc2..3a3b0c0 100644
--- 
a/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/agent/AgentWebApp.java
+++ 
b/slider-core/src/main/java/org/apache/slider/server/appmaster/web/rest/agent/AgentWebApp.java
@@ -25,7 +25,6 @@ import 
com.sun.jersey.spi.inject.SingletonTypeInjectableProvider;
 import org.apache.slider.core.conf.MapOperations;
 import org.apache.slider.providers.agent.AgentKeys;
 import org.apache.slider.server.appmaster.web.WebAppApi;
-import org.apache.slider.server.appmaster.web.rest.RestPaths;
 import org.apache.slider.server.services.security.SecurityUtils;
 import org.mortbay.jetty.Connector;
 import org.mortbay.jetty.Server;
@@ -40,6 +39,7 @@ import javax.ws.rs.ext.Provider;
 import java.io.Closeable;
 import java.io.File;
 import java.io.IOException;
+import java.net.BindException;
 import java.util.Set;
 
 /**
@@ -91,6 +91,7 @@ public class AgentWebApp implements Closeable {
           new QueuedThreadPool(
               configsMap.getOptionInt("agent.threadpool.size.max", 25)));
       agentServer.setStopAtShutdown(true);
+      agentServer.setGracefulShutdown(1000);
 
       SslSelectChannelConnector ssl1WayConnector = createSSLConnector(false, 
port);
       SslSelectChannelConnector ssl2WayConnector =
@@ -115,6 +116,7 @@ public class AgentWebApp implements Closeable {
       agentRoot.addServlet(agent, "/*");
 
       try {
+        openListeners();
         agentServer.start();
       } catch (IOException e) {
         LOG.error("Unable to start agent server", e);
@@ -131,6 +133,37 @@ public class AgentWebApp implements Closeable {
 
     }
 
+    private void openListeners() throws Exception {
+      // from HttpServer2.openListeners()
+      for (Connector listener : agentServer.getConnectors()) {
+        if (listener.getLocalPort() != -1) {
+          // This listener is either started externally or has been bound
+          continue;
+        }
+        int port = listener.getPort();
+        while (true) {
+          // jetty has a bug where you can't reopen a listener that previously
+          // failed to open w/o issuing a close first, even if the port is 
changed
+          try {
+            listener.close();
+            listener.open();
+            LOG.info("Jetty bound to port " + listener.getLocalPort());
+            break;
+          } catch (BindException ex) {
+            if (port == 0) {
+              BindException be = new BindException("Port in use: "
+                  + listener.getHost() + ":" + listener.getPort());
+              be.initCause(ex);
+              throw be;
+            }
+          }
+          // try the next port number
+          listener.setPort(++port);
+          Thread.sleep(100);
+        }
+      }
+    }
+
     private SslSelectChannelConnector createSSLConnector(boolean 
needClientAuth, int port) {
       SslSelectChannelConnector sslConnector = new
           SslSelectChannelConnector();

Reply via email to