001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertNotEquals;
023import static org.junit.Assert.assertTrue;
024import static org.mockito.ArgumentMatchers.anyInt;
025import static org.mockito.Mockito.mock;
026import static org.mockito.Mockito.when;
027
028import java.io.File;
029import java.util.List;
030import java.util.Random;
031import org.apache.hadoop.conf.Configuration;
032import org.apache.hadoop.fs.FileSystem;
033import org.apache.hadoop.fs.FileUtil;
034import org.apache.hadoop.fs.Path;
035import org.apache.hadoop.hbase.client.Get;
036import org.apache.hadoop.hbase.client.Put;
037import org.apache.hadoop.hbase.client.Result;
038import org.apache.hadoop.hbase.client.Table;
039import org.apache.hadoop.hbase.http.ssl.KeyStoreTestUtil;
040import org.apache.hadoop.hbase.testclassification.LargeTests;
041import org.apache.hadoop.hbase.testclassification.MiscTests;
042import org.apache.hadoop.hbase.util.Bytes;
043import org.apache.hadoop.hbase.zookeeper.MiniZooKeeperCluster;
044import org.apache.hadoop.hdfs.MiniDFSCluster;
045import org.junit.ClassRule;
046import org.junit.Rule;
047import org.junit.Test;
048import org.junit.experimental.categories.Category;
049import org.junit.rules.TestName;
050import org.mockito.Mockito;
051import org.mockito.invocation.InvocationOnMock;
052import org.mockito.stubbing.Answer;
053import org.slf4j.Logger;
054import org.slf4j.LoggerFactory;
055
056/**
057 * Test our testing utility class
058 */
059@Category({MiscTests.class, LargeTests.class})
060public class TestHBaseTestingUtility {
061  private static final int NUMTABLES = 1;
062  private static final int NUMROWS = 100;
063  private static final int NUMREGIONS = 10;
064
065  @ClassRule
066  public static final HBaseClassTestRule CLASS_RULE =
067      HBaseClassTestRule.forClass(TestHBaseTestingUtility.class);
068
069  private static final Logger LOG = LoggerFactory.getLogger(TestHBaseTestingUtility.class);
070
071  @Rule
072  public TestName name = new TestName();
073
074  /**
075   * Basic sanity test that spins up multiple HDFS and HBase clusters that share
076   * the same ZK ensemble. We then create the same table in both and make sure
077   * that what we insert in one place doesn't end up in the other.
078   * @throws Exception
079   */
080  @Test
081  public void testMultiClusters() throws Exception {
082    // Create three clusters
083
084    // Cluster 1.
085    HBaseTestingUtility htu1 = new HBaseTestingUtility();
086    // Set a different zk path for each cluster
087    htu1.getConfiguration().set(HConstants.ZOOKEEPER_ZNODE_PARENT, "/1");
088    htu1.startMiniZKCluster();
089
090    // Cluster 2
091    HBaseTestingUtility htu2 = new HBaseTestingUtility();
092    htu2.getConfiguration().set(HConstants.ZOOKEEPER_ZNODE_PARENT, "/2");
093    htu2.getConfiguration().set(HConstants.ZOOKEEPER_CLIENT_PORT,
094      htu1.getConfiguration().get(HConstants.ZOOKEEPER_CLIENT_PORT, "-1"));
095    htu2.setZkCluster(htu1.getZkCluster());
096
097    // Cluster 3; seed it with the conf from htu1 so we pickup the 'right'
098    // zk cluster config; it is set back into the config. as part of the
099    // start of minizkcluster.
100    HBaseTestingUtility htu3 = new HBaseTestingUtility();
101    htu3.getConfiguration().set(HConstants.ZOOKEEPER_ZNODE_PARENT, "/3");
102    htu3.getConfiguration().set(HConstants.ZOOKEEPER_CLIENT_PORT,
103      htu1.getConfiguration().get(HConstants.ZOOKEEPER_CLIENT_PORT, "-1"));
104    htu3.setZkCluster(htu1.getZkCluster());
105
106    try {
107      htu1.startMiniCluster();
108      htu2.startMiniCluster();
109      htu3.startMiniCluster();
110
111      final TableName tableName = TableName.valueOf(name.getMethodName());
112      final byte[] FAM_NAME = Bytes.toBytes("fam");
113      final byte[] ROW = Bytes.toBytes("row");
114      final byte[] QUAL_NAME = Bytes.toBytes("qual");
115      final byte[] VALUE = Bytes.toBytes("value");
116
117      Table table1 = htu1.createTable(tableName, FAM_NAME);
118      Table table2 = htu2.createTable(tableName, FAM_NAME);
119
120      Put put = new Put(ROW);
121      put.addColumn(FAM_NAME, QUAL_NAME, VALUE);
122      table1.put(put);
123
124      Get get = new Get(ROW);
125      get.addColumn(FAM_NAME, QUAL_NAME);
126      Result res = table1.get(get);
127      assertEquals(1, res.size());
128
129      res = table2.get(get);
130      assertEquals(0, res.size());
131
132      table1.close();
133      table2.close();
134
135    } finally {
136      htu3.shutdownMiniCluster();
137      htu2.shutdownMiniCluster();
138      htu1.shutdownMiniCluster();
139    }
140  }
141
142  @Test public void testMiniCluster() throws Exception {
143    HBaseTestingUtility hbt = new HBaseTestingUtility();
144
145    MiniHBaseCluster cluster = hbt.startMiniCluster();
146    try {
147      assertEquals(1, cluster.getLiveRegionServerThreads().size());
148    } finally {
149      hbt.shutdownMiniCluster();
150    }
151  }
152
153  @Test
154  public void testMiniClusterBindToWildcard() throws Exception {
155    HBaseTestingUtility hbt = new HBaseTestingUtility();
156    hbt.getConfiguration().set("hbase.regionserver.ipc.address", "0.0.0.0");
157    MiniHBaseCluster cluster = hbt.startMiniCluster();
158    try {
159      assertEquals(1, cluster.getLiveRegionServerThreads().size());
160    } finally {
161      hbt.shutdownMiniCluster();
162    }
163  }
164
165  @Test
166  public void testMiniClusterWithSSLOn() throws Exception {
167    final String BASEDIR = System.getProperty("test.build.dir",
168        "target/test-dir") + "/" + TestHBaseTestingUtility.class.getSimpleName();
169    String sslConfDir = KeyStoreTestUtil.getClasspathDir(TestHBaseTestingUtility.class);
170    String keystoresDir = new File(BASEDIR).getAbsolutePath();
171
172    HBaseTestingUtility hbt = new HBaseTestingUtility();
173    File base = new File(BASEDIR);
174    FileUtil.fullyDelete(base);
175    base.mkdirs();
176
177    KeyStoreTestUtil.setupSSLConfig(keystoresDir, sslConfDir, hbt.getConfiguration(), false);
178
179    hbt.getConfiguration().set("hbase.ssl.enabled", "true");
180    hbt.getConfiguration().addResource("ssl-server.xml");
181    hbt.getConfiguration().addResource("ssl-client.xml");
182
183    MiniHBaseCluster cluster = hbt.startMiniCluster();
184    try {
185      assertEquals(1, cluster.getLiveRegionServerThreads().size());
186    } finally {
187      hbt.shutdownMiniCluster();
188    }
189  }
190
191  /**
192   *  Test that we can start and stop multiple time a cluster
193   *   with the same HBaseTestingUtility.
194   */
195  @Test public void testMultipleStartStop() throws Exception{
196    HBaseTestingUtility htu1 = new HBaseTestingUtility();
197    Path foo = new Path("foo");
198
199    htu1.startMiniCluster();
200    htu1.getDFSCluster().getFileSystem().create(foo);
201    assertTrue( htu1.getDFSCluster().getFileSystem().exists(foo));
202    htu1.shutdownMiniCluster();
203
204    htu1.startMiniCluster();
205    assertFalse( htu1.getDFSCluster().getFileSystem().exists(foo));
206    htu1.getDFSCluster().getFileSystem().create(foo);
207    assertTrue( htu1.getDFSCluster().getFileSystem().exists(foo));
208    htu1.shutdownMiniCluster();
209  }
210
211  @Test
212  public void testMiniZooKeeperWithOneServer() throws Exception {
213    HBaseTestingUtility hbt = new HBaseTestingUtility();
214    MiniZooKeeperCluster cluster1 = hbt.startMiniZKCluster();
215    try {
216      assertEquals(0, cluster1.getBackupZooKeeperServerNum());
217      assertTrue((cluster1.killCurrentActiveZooKeeperServer() == -1));
218    } finally {
219      hbt.shutdownMiniZKCluster();
220    }
221  }
222
223  @Test
224  public void testMiniZooKeeperWithMultipleServers() throws Exception {
225    HBaseTestingUtility hbt = new HBaseTestingUtility();
226    // set up zookeeper cluster with 5 zk servers
227    MiniZooKeeperCluster cluster2 = hbt.startMiniZKCluster(5);
228    int defaultClientPort = 21818;
229    cluster2.setDefaultClientPort(defaultClientPort);
230    try {
231      assertEquals(4, cluster2.getBackupZooKeeperServerNum());
232
233      // killing the current active zk server
234      int currentActivePort = cluster2.killCurrentActiveZooKeeperServer();
235      assertTrue(currentActivePort >= defaultClientPort);
236      // Check if the client port is returning a proper value
237      assertTrue(cluster2.getClientPort() == currentActivePort);
238
239      // kill another active zk server
240      currentActivePort = cluster2.killCurrentActiveZooKeeperServer();
241      assertTrue(currentActivePort >= defaultClientPort);
242      assertTrue(cluster2.getClientPort() == currentActivePort);
243      assertEquals(2, cluster2.getBackupZooKeeperServerNum());
244      assertEquals(3, cluster2.getZooKeeperServerNum());
245
246      // killing the backup zk servers
247      cluster2.killOneBackupZooKeeperServer();
248      cluster2.killOneBackupZooKeeperServer();
249      assertEquals(0, cluster2.getBackupZooKeeperServerNum());
250      assertEquals(1, cluster2.getZooKeeperServerNum());
251
252      // killing the last zk server
253      currentActivePort = cluster2.killCurrentActiveZooKeeperServer();
254      assertTrue(currentActivePort == -1);
255      assertTrue(cluster2.getClientPort() == currentActivePort);
256      // this should do nothing.
257      cluster2.killOneBackupZooKeeperServer();
258      assertEquals(-1, cluster2.getBackupZooKeeperServerNum());
259      assertEquals(0, cluster2.getZooKeeperServerNum());
260    } finally {
261      hbt.shutdownMiniZKCluster();
262    }
263  }
264
265  @Test
266  public void testMiniZooKeeperWithMultipleClientPorts() throws Exception {
267    int defaultClientPort = 8888;
268    int i, j;
269    HBaseTestingUtility hbt = new HBaseTestingUtility();
270
271    // Test 1 - set up zookeeper cluster with same number of ZK servers and specified client ports
272    int [] clientPortList1 = {1111, 1112, 1113};
273    MiniZooKeeperCluster cluster1 = hbt.startMiniZKCluster(clientPortList1.length, clientPortList1);
274    try {
275      List<Integer> clientPortListInCluster = cluster1.getClientPortList();
276
277      for (i = 0; i < clientPortListInCluster.size(); i++) {
278        // cannot assert the specific port due to the port conflict in which situation
279        // it always chooses a bigger port by +1. The below is the same.
280        assertTrue(clientPortListInCluster.get(i).intValue() >= clientPortList1[i]);
281      }
282    } finally {
283      hbt.shutdownMiniZKCluster();
284    }
285
286    // Test 2 - set up zookeeper cluster with more ZK servers than specified client ports
287    hbt.getConfiguration().setInt("test.hbase.zookeeper.property.clientPort", defaultClientPort);
288    int [] clientPortList2 = {2222, 2223};
289    MiniZooKeeperCluster cluster2 =
290        hbt.startMiniZKCluster(clientPortList2.length + 2, clientPortList2);
291
292    try {
293      List<Integer> clientPortListInCluster = cluster2.getClientPortList();
294
295      for (i = 0, j = 0; i < clientPortListInCluster.size(); i++) {
296        if (i < clientPortList2.length) {
297          assertTrue(clientPortListInCluster.get(i).intValue() >= clientPortList2[i]);
298        } else {
299          // servers with no specified client port will use defaultClientPort or some other ports
300          // based on defaultClientPort
301          assertTrue(clientPortListInCluster.get(i).intValue() >= defaultClientPort + j);
302          j++;
303        }
304      }
305    } finally {
306      hbt.shutdownMiniZKCluster();
307    }
308
309    // Test 3 - set up zookeeper cluster with invalid client ports
310    hbt.getConfiguration().setInt("test.hbase.zookeeper.property.clientPort", defaultClientPort);
311    int [] clientPortList3 = {3333, -3334, 3335, 0};
312    MiniZooKeeperCluster cluster3 =
313        hbt.startMiniZKCluster(clientPortList3.length + 1, clientPortList3);
314
315    try {
316      List<Integer> clientPortListInCluster = cluster3.getClientPortList();
317
318      for (i = 0, j = 0; i < clientPortListInCluster.size(); i++) {
319        // Servers will only use valid client ports; if ports are not specified or invalid,
320        // the default port or a port based on default port will be used.
321        if (i < clientPortList3.length && clientPortList3[i] > 0) {
322          assertTrue(clientPortListInCluster.get(i).intValue() >= clientPortList3[i]);
323        } else {
324          assertTrue(clientPortListInCluster.get(i).intValue() >= defaultClientPort + j);
325          j++;
326        }
327      }
328    } finally {
329      hbt.shutdownMiniZKCluster();
330    }
331
332    // Test 4 - set up zookeeper cluster with default port and some other ports used
333    // This test tests that the defaultClientPort and defaultClientPort+2 are used, so
334    // the algorithm should choice defaultClientPort+1 and defaultClientPort+3 to fill
335    // out the ports for servers without ports specified.
336    hbt.getConfiguration().setInt("test.hbase.zookeeper.property.clientPort", defaultClientPort);
337    int [] clientPortList4 = {-4444, defaultClientPort+2, 4446, defaultClientPort};
338    MiniZooKeeperCluster cluster4 =
339        hbt.startMiniZKCluster(clientPortList4.length + 1, clientPortList4);
340
341    try {
342      List<Integer> clientPortListInCluster = cluster4.getClientPortList();
343
344      for (i = 0, j = 1; i < clientPortListInCluster.size(); i++) {
345        // Servers will only use valid client ports; if ports are not specified or invalid,
346        // the default port or a port based on default port will be used.
347        if (i < clientPortList4.length && clientPortList4[i] > 0) {
348          assertTrue(clientPortListInCluster.get(i).intValue() >= clientPortList4[i]);
349        } else {
350          assertTrue(clientPortListInCluster.get(i).intValue() >= defaultClientPort + j);
351          j +=2;
352        }
353      }
354    } finally {
355      hbt.shutdownMiniZKCluster();
356    }
357
358    // Test 5 - set up zookeeper cluster with same ports specified - fail is expected.
359    int [] clientPortList5 = {5555, 5556, 5556};
360
361    try {
362      MiniZooKeeperCluster cluster5 =
363          hbt.startMiniZKCluster(clientPortList5.length, clientPortList5);
364      assertTrue(cluster5.getClientPort() == -1); // expected failure
365    } catch (Exception e) {
366      // exception is acceptable
367    } finally {
368      hbt.shutdownMiniZKCluster();
369    }
370  }
371
372  @Test public void testMiniDFSCluster() throws Exception {
373    HBaseTestingUtility hbt = new HBaseTestingUtility();
374    MiniDFSCluster cluster = hbt.startMiniDFSCluster(null);
375    FileSystem dfs = cluster.getFileSystem();
376    Path dir = new Path("dir");
377    Path qualifiedDir = dfs.makeQualified(dir);
378    LOG.info("dir=" + dir + ", qualifiedDir=" + qualifiedDir);
379    assertFalse(dfs.exists(qualifiedDir));
380    assertTrue(dfs.mkdirs(qualifiedDir));
381    assertTrue(dfs.delete(qualifiedDir, true));
382    hbt.shutdownMiniCluster();
383  }
384
385  @Test public void testSetupClusterTestBuildDir() throws Exception {
386    HBaseTestingUtility hbt = new HBaseTestingUtility();
387    Path testdir = hbt.getClusterTestDir();
388    LOG.info("uuid-subdir=" + testdir);
389    FileSystem fs = hbt.getTestFileSystem();
390
391    assertFalse(fs.exists(testdir));
392
393    hbt.startMiniDFSCluster(null);
394    assertTrue(fs.exists(testdir));
395
396    hbt.shutdownMiniCluster();
397    assertFalse(fs.exists(testdir));
398  }
399
400  @Test public void testTestDir() throws Exception {
401    HBaseTestingUtility hbt = new HBaseTestingUtility();
402    Path testdir = hbt.getDataTestDir();
403    LOG.info("testdir=" + testdir);
404    FileSystem fs = hbt.getTestFileSystem();
405    assertTrue(!fs.exists(testdir));
406    assertTrue(fs.mkdirs(testdir));
407    assertTrue(hbt.cleanupTestDir());
408  }
409
410  @Test public void testResolvePortConflict() throws Exception {
411    // raises port conflict between 1st call and 2nd call of randomPort() by mocking Random object
412    Random random = mock(Random.class);
413    when(random.nextInt(anyInt()))
414      .thenAnswer(new Answer<Integer>() {
415        int[] numbers = { 1, 1, 2 };
416        int count = 0;
417
418        @Override
419        public Integer answer(InvocationOnMock invocation) {
420          int ret = numbers[count];
421          count++;
422          return ret;
423        }
424      });
425
426    HBaseTestingUtility.PortAllocator.AvailablePortChecker portChecker =
427      mock(HBaseTestingUtility.PortAllocator.AvailablePortChecker.class);
428    when(portChecker.available(anyInt())).thenReturn(true);
429
430    HBaseTestingUtility.PortAllocator portAllocator =
431      new HBaseTestingUtility.PortAllocator(random, portChecker);
432
433    int port1 = portAllocator.randomFreePort();
434    int port2 = portAllocator.randomFreePort();
435    assertNotEquals(port1, port2);
436    Mockito.verify(random, Mockito.times(3)).nextInt(anyInt());
437  }
438
439  @Test
440  public void testOverridingOfDefaultPorts() throws Exception {
441
442    // confirm that default port properties being overridden to random
443    Configuration defaultConfig = HBaseConfiguration.create();
444    defaultConfig.setInt(HConstants.MASTER_INFO_PORT, HConstants.DEFAULT_MASTER_INFOPORT);
445    defaultConfig.setInt(HConstants.REGIONSERVER_INFO_PORT,
446        HConstants.DEFAULT_REGIONSERVER_INFOPORT);
447    HBaseTestingUtility htu = new HBaseTestingUtility(defaultConfig);
448    try {
449      MiniHBaseCluster defaultCluster = htu.startMiniCluster();
450      assertNotEquals(HConstants.DEFAULT_MASTER_INFOPORT,
451          defaultCluster.getConfiguration().getInt(HConstants.MASTER_INFO_PORT, 0));
452      assertNotEquals(HConstants.DEFAULT_REGIONSERVER_INFOPORT,
453          defaultCluster.getConfiguration().getInt(HConstants.REGIONSERVER_INFO_PORT, 0));
454    } finally {
455      htu.shutdownMiniCluster();
456    }
457
458    // confirm that nonDefault (custom) port settings are NOT overridden
459    Configuration altConfig = HBaseConfiguration.create();
460    final int nonDefaultMasterInfoPort = 3333;
461    final int nonDefaultRegionServerPort = 4444;
462    altConfig.setInt(HConstants.MASTER_INFO_PORT, nonDefaultMasterInfoPort);
463    altConfig.setInt(HConstants.REGIONSERVER_INFO_PORT, nonDefaultRegionServerPort);
464    htu = new HBaseTestingUtility(altConfig);
465    try {
466      MiniHBaseCluster customCluster = htu.startMiniCluster();
467      assertEquals(nonDefaultMasterInfoPort,
468              customCluster.getConfiguration().getInt(HConstants.MASTER_INFO_PORT, 0));
469      assertEquals(nonDefaultRegionServerPort,
470          customCluster.getConfiguration().getInt(HConstants.REGIONSERVER_INFO_PORT, 0));
471    } finally {
472      htu.shutdownMiniCluster();
473    }
474  }
475
476  // This test demonstrates how long killHBTU takes vs. shutdownHBTU takes
477  // for realistic results, adjust NUMROWS, NUMTABLES to much larger number.
478  @Test
479  public void testKillMiniHBaseCluster() throws Exception {
480
481    HBaseTestingUtility htu = new HBaseTestingUtility();
482    htu.startMiniZKCluster();
483
484    try {
485      htu.startMiniHBaseCluster();
486
487      TableName tableName;
488      byte[] FAM_NAME;
489
490      for(int i = 0; i < NUMTABLES; i++) {
491        tableName = TableName.valueOf(name.getMethodName() + i);
492        FAM_NAME = Bytes.toBytes("fam" + i);
493
494        try (Table table = htu.createMultiRegionTable(tableName, FAM_NAME, NUMREGIONS)) {
495          htu.loadRandomRows(table, FAM_NAME, 100, NUMROWS);
496        }
497      }
498    } finally {
499      htu.killMiniHBaseCluster();
500      htu.shutdownMiniZKCluster();
501    }
502  }
503}