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

This commit is contained in:
Xiao Chen 2017-12-14 19:20:57 -08:00
parent 95d4ec7fc0
commit e1cb278cd0
2 changed files with 86 additions and 7 deletions

View File

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

View File

@ -26,9 +26,14 @@ import org.apache.hadoop.hdfs.DFSConfigKeys;
import org.apache.hadoop.hdfs.HdfsConfiguration; import org.apache.hadoop.hdfs.HdfsConfiguration;
import org.apache.hadoop.hdfs.MiniDFSCluster; import org.apache.hadoop.hdfs.MiniDFSCluster;
import static org.apache.hadoop.security.SecurityUtilTestHelper.isExternalKdcRunning; import static org.apache.hadoop.security.SecurityUtilTestHelper.isExternalKdcRunning;
import org.apache.hadoop.net.NetUtils;
import org.junit.Assume; import org.junit.Assume;
import org.junit.Before; import org.junit.Rule;
import org.junit.Test; 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 * This test starts a 1 NameNode 1 DataNode MiniDFSCluster with
@ -48,16 +53,18 @@ import org.junit.Test;
* dfs.datanode.keytab.file * dfs.datanode.keytab.file
*/ */
public class TestStartSecureDataNode { public class TestStartSecureDataNode {
@Rule
public ExpectedException thrown = ExpectedException.none();
final static private int NUM_OF_DATANODES = 1; final static private int NUM_OF_DATANODES = 1;
@Before private void testExternalKdcRunning() {
public void testExternalKdcRunning() {
// Tests are skipped if external KDC is not running. // Tests are skipped if external KDC is not running.
Assume.assumeTrue(isExternalKdcRunning()); Assume.assumeTrue(isExternalKdcRunning());
} }
@Test @Test
public void testSecureNameNode() throws Exception { public void testSecureNameNode() throws Exception {
testExternalKdcRunning();
MiniDFSCluster cluster = null; MiniDFSCluster cluster = null;
try { try {
String nnPrincipal = 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();
}
}
} }