From e1cb278cd0287ce47f923941147d17540b199a99 Mon Sep 17 00:00:00 2001 From: Xiao Chen Date: Thu, 14 Dec 2017 19:20:57 -0800 Subject: [PATCH] HDFS-12910. Secure Datanode Starter should log the port when it fails to bind. Contributed by Stephen O'Donnell and Nanda kumar. --- .../datanode/SecureDataNodeStarter.java | 29 +++++++-- .../datanode/TestStartSecureDataNode.java | 64 ++++++++++++++++++- 2 files changed, 86 insertions(+), 7 deletions(-) diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/SecureDataNodeStarter.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/SecureDataNodeStarter.java index cea78163cb..accf2bd2ad 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/SecureDataNodeStarter.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/SecureDataNodeStarter.java @@ -32,6 +32,7 @@ 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 static SecureResources getSecureResources(Configuration conf) 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 static SecureResources getSecureResources(Configuration conf) 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 static SecureResources getSecureResources(Configuration conf) 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; + } } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestStartSecureDataNode.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestStartSecureDataNode.java index 540c05f69c..51a843bd42 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestStartSecureDataNode.java +++ b/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.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 @@ * 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 void testSecureNameNode() throws Exception { } } } + + /** + * 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(); + } + } }