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