diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/LocalHBaseCluster.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/LocalHBaseCluster.java index 06199f72d63..e19e53bfc51 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/LocalHBaseCluster.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/LocalHBaseCluster.java @@ -66,6 +66,8 @@ public class LocalHBaseCluster { public static final String LOCAL = "local"; /** 'local:' */ public static final String LOCAL_COLON = LOCAL + ":"; + public static final String ASSIGN_RANDOM_PORTS = "hbase.localcluster.assign.random.ports"; + private final Configuration conf; private final Class masterClass; private final Class regionServerClass; @@ -139,10 +141,15 @@ public class LocalHBaseCluster { // Always have masters and regionservers come up on port '0' so we don't // clash over default ports. - conf.set(HConstants.MASTER_PORT, "0"); - conf.set(HConstants.REGIONSERVER_PORT, "0"); - if (conf.getInt(HConstants.REGIONSERVER_INFO_PORT, 0) != -1) { - conf.set(HConstants.REGIONSERVER_INFO_PORT, "0"); + if (conf.getBoolean(ASSIGN_RANDOM_PORTS, true)) { + conf.set(HConstants.MASTER_PORT, "0"); + conf.set(HConstants.REGIONSERVER_PORT, "0"); + if (conf.getInt(HConstants.REGIONSERVER_INFO_PORT, 0) != -1) { + conf.set(HConstants.REGIONSERVER_INFO_PORT, "0"); + } + if (conf.getInt(HConstants.MASTER_INFO_PORT, 0) != -1) { + conf.set(HConstants.MASTER_INFO_PORT, "0"); + } } this.masterClass = (Class) diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/MiniHBaseCluster.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/MiniHBaseCluster.java index 9959e318a50..a6522847276 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/MiniHBaseCluster.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/MiniHBaseCluster.java @@ -108,9 +108,11 @@ public class MiniHBaseCluster extends HBaseCluster { Class regionserverClass) throws IOException, InterruptedException { super(conf); - conf.set(HConstants.MASTER_PORT, "0"); - if (conf.getInt(HConstants.MASTER_INFO_PORT, 0) != -1) { - conf.set(HConstants.MASTER_INFO_PORT, "0"); + if (conf.getBoolean(LocalHBaseCluster.ASSIGN_RANDOM_PORTS, true)) { + conf.set(HConstants.MASTER_PORT, "0"); + if (conf.getInt(HConstants.MASTER_INFO_PORT, 0) != -1) { + conf.set(HConstants.MASTER_INFO_PORT, "0"); + } } // Hadoop 2 diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/TestClusterPortAssignment.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/TestClusterPortAssignment.java new file mode 100644 index 00000000000..0c8247f362a --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/TestClusterPortAssignment.java @@ -0,0 +1,80 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.net.BindException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.testclassification.MediumTests; + +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +@Category(MediumTests.class) +public class TestClusterPortAssignment { + @ClassRule + public static final HBaseClassTestRule CLASS_RULE = + HBaseClassTestRule.forClass(TestClusterPortAssignment.class); + + private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); + private static final Log LOG = LogFactory.getLog(TestClusterPortAssignment.class); + + /** + * Check that we can start an HBase cluster specifying a custom set of + * RPC and infoserver ports. + */ + @Test + public void testClusterPortAssignment() throws Exception { + boolean retry = false; + do { + int masterPort = HBaseTestingUtility.randomFreePort(); + int masterInfoPort = HBaseTestingUtility.randomFreePort(); + int rsPort = HBaseTestingUtility.randomFreePort(); + int rsInfoPort = HBaseTestingUtility.randomFreePort(); + TEST_UTIL.getConfiguration().setBoolean(LocalHBaseCluster.ASSIGN_RANDOM_PORTS, false); + TEST_UTIL.getConfiguration().setInt(HConstants.MASTER_PORT, masterPort); + TEST_UTIL.getConfiguration().setInt(HConstants.MASTER_INFO_PORT, masterInfoPort); + TEST_UTIL.getConfiguration().setInt(HConstants.REGIONSERVER_PORT, rsPort); + TEST_UTIL.getConfiguration().setInt(HConstants.REGIONSERVER_INFO_PORT, rsInfoPort); + try { + MiniHBaseCluster cluster = TEST_UTIL.startMiniCluster(); + assertTrue("Cluster failed to come up", cluster.waitForActiveAndReadyMaster(30000)); + retry = false; + assertEquals("Master RPC port is incorrect", masterPort, + cluster.getMaster().getRpcServer().getListenerAddress().getPort()); + assertEquals("Master info port is incorrect", masterInfoPort, + cluster.getMaster().getInfoServer().getPort()); + assertEquals("RS RPC port is incorrect", rsPort, + cluster.getRegionServer(0).getRpcServer().getListenerAddress().getPort()); + assertEquals("RS info port is incorrect", rsInfoPort, + cluster.getRegionServer(0).getInfoServer().getPort()); + } catch (BindException e) { + LOG.info("Failed to bind, need to retry", e); + retry = true; + } finally { + TEST_UTIL.shutdownMiniCluster(); + } + } while (retry); + } +}