浏览代码

HDFS-12910. Secure Datanode Starter should log the port when it fails to bind. Contributed by Stephen O'Donnell and Nanda kumar.

Xiao Chen 7 年之前
父节点
当前提交
e1cb278cd0

+ 25 - 4
hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/SecureDataNodeStarter.java

@@ -32,6 +32,7 @@ import org.apache.hadoop.security.UserGroupInformation;
 import java.net.InetSocketAddress;
 import java.net.ServerSocket;
 import java.nio.channels.ServerSocketChannel;
+import java.net.BindException;
 
 /**
  * Utility class to start a datanode in a secure cluster, first obtaining 
@@ -102,7 +103,13 @@ public class SecureDataNodeStarter implements Daemon {
 
     ServerSocket ss = (socketWriteTimeout > 0) ? 
         ServerSocketChannel.open().socket() : new ServerSocket();
-    ss.bind(streamingAddr, backlogLength);
+    try {
+      ss.bind(streamingAddr, backlogLength);
+    } catch (BindException e) {
+      BindException newBe = appendMessageToBindException(e,
+          streamingAddr.toString());
+      throw newBe;
+    }
 
     // Check that we got the port we need
     if (ss.getLocalPort() != streamingAddr.getPort()) {
@@ -126,13 +133,20 @@ public class SecureDataNodeStarter implements Daemon {
     if (policy.isHttpEnabled()) {
       httpChannel = ServerSocketChannel.open();
       InetSocketAddress infoSocAddr = DataNode.getInfoAddr(conf);
-      httpChannel.socket().bind(infoSocAddr);
+      try {
+        httpChannel.socket().bind(infoSocAddr);
+      } catch (BindException e) {
+        BindException newBe = appendMessageToBindException(e,
+            infoSocAddr.toString());
+        throw newBe;
+      }
       InetSocketAddress localAddr = (InetSocketAddress) httpChannel.socket()
         .getLocalSocketAddress();
 
       if (localAddr.getPort() != infoSocAddr.getPort()) {
-        throw new RuntimeException("Unable to bind on specified info port in secure " +
-            "context. Needed " + streamingAddr.getPort() + ", got " + ss.getLocalPort());
+        throw new RuntimeException("Unable to bind on specified info port in " +
+            "secure context. Needed " + infoSocAddr.getPort() + ", got " +
+             ss.getLocalPort());
       }
       System.err.println("Successfully obtained privileged resources (streaming port = "
           + ss + " ) (http listener port = " + localAddr.getPort() +")");
@@ -149,4 +163,11 @@ public class SecureDataNodeStarter implements Daemon {
     return new SecureResources(ss, httpChannel);
   }
 
+  private static BindException appendMessageToBindException(BindException e,
+      String msg) {
+    BindException newBe = new BindException(e.getMessage() + " " + msg);
+    newBe.initCause(e.getCause());
+    newBe.setStackTrace(e.getStackTrace());
+    return newBe;
+  }
 }

+ 61 - 3
hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestStartSecureDataNode.java

@@ -26,9 +26,14 @@ import org.apache.hadoop.hdfs.DFSConfigKeys;
 import org.apache.hadoop.hdfs.HdfsConfiguration;
 import org.apache.hadoop.hdfs.MiniDFSCluster;
 import static org.apache.hadoop.security.SecurityUtilTestHelper.isExternalKdcRunning;
+import org.apache.hadoop.net.NetUtils;
 import org.junit.Assume;
-import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import java.net.BindException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
 
 /**
  * This test starts a 1 NameNode 1 DataNode MiniDFSCluster with
@@ -48,16 +53,18 @@ import org.junit.Test;
  *   dfs.datanode.keytab.file
  */
 public class TestStartSecureDataNode {
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
   final static private int NUM_OF_DATANODES = 1;
 
-  @Before
-  public void testExternalKdcRunning() {
+  private void testExternalKdcRunning() {
     // Tests are skipped if external KDC is not running.
     Assume.assumeTrue(isExternalKdcRunning());
   }
 
   @Test
   public void testSecureNameNode() throws Exception {
+    testExternalKdcRunning();
     MiniDFSCluster cluster = null;
     try {
       String nnPrincipal =
@@ -104,4 +111,55 @@ public class TestStartSecureDataNode {
       }
     }
   }
+
+  /**
+   * This test doesn't require KDC or other security settings as it expects
+   * {@link java.net.BindException}. Testing is done with unprivileged port
+   * for {@code dfs.datanode.address}.
+   *
+   * @throws Exception
+   */
+  @Test
+  public void testStreamingAddrBindException() throws Exception {
+    ServerSocket ss = new ServerSocket();
+    try {
+      ss.bind(new InetSocketAddress("localhost", 0));
+      thrown.expect(BindException.class);
+      thrown.expectMessage("localhost/127.0.0.1:" + ss.getLocalPort());
+
+      Configuration conf = new HdfsConfiguration();
+      conf.set(DFSConfigKeys.DFS_DATANODE_ADDRESS_KEY,
+          "localhost:" + ss.getLocalPort());
+      SecureDataNodeStarter.getSecureResources(conf);
+    } finally {
+      ss.close();
+    }
+  }
+
+  /**
+   * This test doesn't require KDC or other security settings as it expects
+   * {@link java.net.BindException}. Testing is done with unprivileged port
+   * for {@code dfs.datanode.http.address}.
+   *
+   * @throws Exception
+   */
+  @Test
+  public void testWebServerAddrBindException() throws Exception {
+    ServerSocket ss = new ServerSocket();
+    try {
+      ss.bind(new InetSocketAddress("localhost", 0));
+      thrown.expect(BindException.class);
+      thrown.expectMessage("localhost/127.0.0.1:" + ss.getLocalPort());
+
+      Configuration conf = new HdfsConfiguration();
+      conf.set(DFSConfigKeys.DFS_DATANODE_ADDRESS_KEY,
+          "localhost:" + NetUtils.getFreeSocketPort());
+      conf.set(DFSConfigKeys.DFS_DATANODE_HTTP_ADDRESS_KEY,
+          "localhost:" + ss.getLocalPort());
+
+      SecureDataNodeStarter.getSecureResources(conf);
+    } finally {
+      ss.close();
+    }
+  }
 }