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.master;
019
020import static org.junit.jupiter.api.Assertions.assertEquals;
021import static org.junit.jupiter.api.Assertions.assertFalse;
022import static org.junit.jupiter.api.Assertions.assertNotNull;
023import static org.junit.jupiter.api.Assertions.assertNull;
024import static org.junit.jupiter.api.Assertions.assertThrows;
025import static org.junit.jupiter.api.Assertions.assertTrue;
026
027import java.util.Arrays;
028import java.util.Map;
029import java.util.concurrent.TimeUnit;
030import java.util.stream.Stream;
031import java.util.stream.StreamSupport;
032import org.apache.hadoop.conf.Configuration;
033import org.apache.hadoop.hbase.CompatibilityFactory;
034import org.apache.hadoop.hbase.HBaseTestingUtil;
035import org.apache.hadoop.hbase.HConstants;
036import org.apache.hadoop.hbase.StartTestingClusterOption;
037import org.apache.hadoop.hbase.TableName;
038import org.apache.hadoop.hbase.client.AsyncTable;
039import org.apache.hadoop.hbase.client.Connection;
040import org.apache.hadoop.hbase.client.Put;
041import org.apache.hadoop.hbase.client.Result;
042import org.apache.hadoop.hbase.client.ResultScanner;
043import org.apache.hadoop.hbase.client.Scan;
044import org.apache.hadoop.hbase.client.Table;
045import org.apache.hadoop.hbase.quotas.QuotaUtil;
046import org.apache.hadoop.hbase.test.MetricsAssertHelper;
047import org.apache.hadoop.hbase.testclassification.LargeTests;
048import org.apache.hadoop.hbase.testclassification.MasterTests;
049import org.apache.hadoop.hbase.util.Bytes;
050import org.junit.jupiter.api.AfterEach;
051import org.junit.jupiter.api.BeforeEach;
052import org.junit.jupiter.api.Tag;
053import org.junit.jupiter.api.Test;
054import org.junit.jupiter.api.TestInfo;
055import org.slf4j.Logger;
056import org.slf4j.LoggerFactory;
057
058@Tag(MasterTests.TAG)
059@Tag(LargeTests.TAG)
060public class TestMasterRepairMode {
061  private String testMethodName;
062
063  @BeforeEach
064  public void setTestMethod(TestInfo testInfo) {
065    testMethodName = testInfo.getTestMethod().get().getName();
066  }
067
068  private static final Logger LOG = LoggerFactory.getLogger(TestMasterRepairMode.class);
069
070  private static final byte[] FAMILYNAME = Bytes.toBytes("fam");
071
072  private static HBaseTestingUtil TEST_UTIL;
073
074  @BeforeEach
075  public void setUp() throws Exception {
076    TEST_UTIL = new HBaseTestingUtil();
077  }
078
079  @AfterEach
080  public void tearDown() throws Exception {
081    TEST_UTIL.shutdownMiniCluster();
082  }
083
084  private void enableMaintenanceMode() {
085    Configuration c = TEST_UTIL.getConfiguration();
086    c.setBoolean(HMaster.MAINTENANCE_MODE, true);
087    c.setInt("hbase.master.init.timeout.localHBaseCluster", 30000);
088  }
089
090  @Test
091  public void testNewCluster() throws Exception {
092    enableMaintenanceMode();
093
094    TEST_UTIL.startMiniCluster(
095      StartTestingClusterOption.builder().numRegionServers(0).numDataNodes(3).build());
096
097    Connection conn = TEST_UTIL.getConnection();
098    assertTrue(conn.getAdmin().isMasterInMaintenanceMode());
099
100    try (Table table = conn.getTable(TableName.META_TABLE_NAME);
101      ResultScanner scanner = table.getScanner(new Scan())) {
102      assertNotNull(scanner.next(), "Could not read meta.");
103    }
104  }
105
106  @Test
107  public void testSwitchMetricsWithQuotasEnabled() throws Exception {
108    enableMaintenanceMode();
109    TEST_UTIL.getConfiguration().setBoolean(QuotaUtil.QUOTA_CONF_KEY, true);
110    TEST_UTIL.startMiniCluster(
111      StartTestingClusterOption.builder().numRegionServers(0).numDataNodes(1).build());
112
113    HMaster master = TEST_UTIL.getMiniHBaseCluster().getMaster();
114    assertTrue(master.isInMaintenanceMode());
115    assertNull(master.getMasterQuotaManager());
116    MetricsMasterSource source = master.getMasterMetrics().getMetricsSource();
117    MetricsAssertHelper metricsHelper = CompatibilityFactory.getInstance(MetricsAssertHelper.class);
118    Map<String, Integer> states = new MetricsMasterWrapperImpl(master).getSwitchStates();
119    assertEquals(8, states.size());
120    assertFalse(states.containsKey(MetricsMasterSource.RPC_THROTTLE_ENABLED_NAME));
121    assertFalse(
122      metricsHelper.checkGaugeExists(MetricsMasterSource.RPC_THROTTLE_ENABLED_NAME, source));
123    states.forEach((name, state) -> {
124      assertTrue(state == 0 || state == 1);
125      metricsHelper.assertGauge(name, state.longValue(), source);
126    });
127    metricsHelper.assertGauge(MetricsMasterSource.BALANCER_ENABLED_NAME, 0L, source);
128    metricsHelper.assertGauge(MetricsMasterSource.NORMALIZER_ENABLED_NAME, 0L, source);
129    metricsHelper.assertGauge(MetricsMasterSource.SPLIT_ENABLED_NAME, 0L, source);
130    metricsHelper.assertGauge(MetricsMasterSource.MERGE_ENABLED_NAME, 0L, source);
131  }
132
133  @Test
134  public void testExistingCluster() throws Exception {
135    TableName testRepairMode = TableName.valueOf(testMethodName);
136
137    TEST_UTIL.startMiniCluster();
138    Table t = TEST_UTIL.createTable(testRepairMode, FAMILYNAME);
139    Put p = new Put(Bytes.toBytes("r"));
140    p.addColumn(FAMILYNAME, Bytes.toBytes("c"), new byte[0]);
141    t.put(p);
142
143    TEST_UTIL.shutdownMiniHBaseCluster();
144
145    LOG.info("Starting master-only");
146
147    enableMaintenanceMode();
148    TEST_UTIL.startMiniHBaseCluster(
149      StartTestingClusterOption.builder().numRegionServers(0).createRootDir(false).build());
150
151    Connection conn = TEST_UTIL.getConnection();
152    assertTrue(conn.getAdmin().isMasterInMaintenanceMode());
153
154    try (Table table = conn.getTable(TableName.META_TABLE_NAME);
155      ResultScanner scanner = table.getScanner(HConstants.TABLE_FAMILY);
156      Stream<Result> results = StreamSupport.stream(scanner.spliterator(), false)) {
157      assertTrue(results.anyMatch(r -> Arrays.equals(r.getRow(), testRepairMode.getName())),
158        "Did not find user table records while reading hbase:meta");
159    }
160    // use async table so we can set the timeout and retry value to let the operation fail fast
161    AsyncTable<?> table = conn.toAsyncConnection().getTableBuilder(testRepairMode)
162      .setScanTimeout(5, TimeUnit.SECONDS).setMaxRetries(2).build();
163    assertThrows(Exception.class, () -> {
164      try (ResultScanner scanner = table.getScanner(new Scan())) {
165        scanner.next();
166      }
167    }, "Should not be able to access user-space tables in repair mode.");
168  }
169}