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.client; 019 020import static org.junit.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.fail; 022 023import java.io.IOException; 024import java.util.Optional; 025import java.util.concurrent.CountDownLatch; 026import org.apache.hadoop.hbase.HBaseTestingUtil; 027import org.apache.hadoop.hbase.HConstants; 028import org.apache.hadoop.hbase.MetaTableAccessor; 029import org.apache.hadoop.hbase.TableName; 030import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; 031import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor; 032import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; 033import org.apache.hadoop.hbase.coprocessor.MasterObserver; 034import org.apache.hadoop.hbase.coprocessor.ObserverContext; 035import org.apache.hadoop.hbase.testclassification.MasterTests; 036import org.apache.hadoop.hbase.testclassification.MediumTests; 037import org.apache.hadoop.hbase.util.Bytes; 038import org.junit.jupiter.api.AfterEach; 039import org.junit.jupiter.api.BeforeEach; 040import org.junit.jupiter.api.Tag; 041import org.junit.jupiter.api.Test; 042import org.junit.jupiter.api.TestInfo; 043import org.slf4j.Logger; 044import org.slf4j.LoggerFactory; 045 046@Tag(MasterTests.TAG) 047@Tag(MediumTests.TAG) 048public class TestEnableTable { 049 050 private static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil(); 051 private static final Logger LOG = LoggerFactory.getLogger(TestEnableTable.class); 052 private static final byte[] FAMILYNAME = Bytes.toBytes("fam"); 053 054 @BeforeEach 055 public void setUp() throws Exception { 056 TEST_UTIL.getConfiguration().set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, 057 MasterSyncObserver.class.getName()); 058 TEST_UTIL.startMiniCluster(1); 059 } 060 061 @AfterEach 062 public void tearDown() throws Exception { 063 TEST_UTIL.shutdownMiniCluster(); 064 } 065 066 /** 067 * We were only clearing rows that had a hregioninfo column in hbase:meta. Mangled rows that were 068 * missing the hregioninfo because of error were being left behind messing up any subsequent table 069 * made with the same name. HBASE-12980 070 */ 071 @Test 072 public void testDeleteForSureClearsAllTableRowsFromMeta(TestInfo testInfo) 073 throws IOException, InterruptedException { 074 final TableName tableName = TableName.valueOf(testInfo.getTestMethod().get().getName()); 075 final Admin admin = TEST_UTIL.getAdmin(); 076 TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(tableName) 077 .setColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILYNAME)).build(); 078 try { 079 createTable(TEST_UTIL, tableDescriptor, HBaseTestingUtil.KEYS_FOR_HBA_CREATE_TABLE); 080 } catch (Exception e) { 081 LOG.error("", e); 082 fail("Got an exception while creating " + tableName); 083 } 084 // Now I have a nice table, mangle it by removing the HConstants.REGIONINFO_QUALIFIER_STR 085 // content from a few of the rows. 086 try (Table metaTable = TEST_UTIL.getConnection().getTable(TableName.META_TABLE_NAME)) { 087 try (ResultScanner scanner = metaTable.getScanner( 088 MetaTableAccessor.getScanForTableName(TEST_UTIL.getConfiguration(), tableName))) { 089 for (Result result : scanner) { 090 // Just delete one row. 091 Delete d = new Delete(result.getRow()); 092 d.addColumn(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER); 093 LOG.info("Mangled: " + d); 094 metaTable.delete(d); 095 break; 096 } 097 } 098 admin.disableTable(tableName); 099 TEST_UTIL.waitTableDisabled(tableName.getName()); 100 // Rely on the coprocessor based latch to make the operation synchronous. 101 try { 102 deleteTable(TEST_UTIL, tableName); 103 } catch (Exception e) { 104 LOG.error("", e); 105 fail("Got an exception while deleting " + tableName); 106 } 107 int rowCount = 0; 108 try (ResultScanner scanner = metaTable.getScanner( 109 MetaTableAccessor.getScanForTableName(TEST_UTIL.getConfiguration(), tableName))) { 110 for (Result result : scanner) { 111 LOG.info("Found when none expected: " + result); 112 rowCount++; 113 } 114 } 115 assertEquals(0, rowCount); 116 } 117 } 118 119 public static class MasterSyncObserver implements MasterCoprocessor, MasterObserver { 120 volatile CountDownLatch tableCreationLatch = null; 121 volatile CountDownLatch tableDeletionLatch = null; 122 123 @Override 124 public Optional<MasterObserver> getMasterObserver() { 125 return Optional.of(this); 126 } 127 128 @Override 129 public void postCompletedCreateTableAction( 130 final ObserverContext<MasterCoprocessorEnvironment> ctx, final TableDescriptor desc, 131 final RegionInfo[] regions) throws IOException { 132 // the AccessController test, some times calls only and directly the 133 // postCompletedCreateTableAction() 134 if (tableCreationLatch != null) { 135 tableCreationLatch.countDown(); 136 } 137 } 138 139 @Override 140 public void postCompletedDeleteTableAction( 141 final ObserverContext<MasterCoprocessorEnvironment> ctx, final TableName tableName) 142 throws IOException { 143 // the AccessController test, some times calls only and directly the postDeleteTableHandler() 144 if (tableDeletionLatch != null) { 145 tableDeletionLatch.countDown(); 146 } 147 } 148 } 149 150 public static void createTable(HBaseTestingUtil testUtil, TableDescriptor tableDescriptor, 151 byte[][] splitKeys) throws Exception { 152 // NOTE: We need a latch because admin is not sync, 153 // so the postOp coprocessor method may be called after the admin operation returned. 154 MasterSyncObserver observer = testUtil.getHBaseCluster().getMaster().getMasterCoprocessorHost() 155 .findCoprocessor(MasterSyncObserver.class); 156 observer.tableCreationLatch = new CountDownLatch(1); 157 Admin admin = testUtil.getAdmin(); 158 if (splitKeys != null) { 159 admin.createTable(tableDescriptor, splitKeys); 160 } else { 161 admin.createTable(tableDescriptor); 162 } 163 observer.tableCreationLatch.await(); 164 observer.tableCreationLatch = null; 165 testUtil.waitUntilAllRegionsAssigned(tableDescriptor.getTableName()); 166 } 167 168 public static void deleteTable(HBaseTestingUtil testUtil, TableName tableName) throws Exception { 169 // NOTE: We need a latch because admin is not sync, 170 // so the postOp coprocessor method may be called after the admin operation returned. 171 MasterSyncObserver observer = testUtil.getHBaseCluster().getMaster().getMasterCoprocessorHost() 172 .findCoprocessor(MasterSyncObserver.class); 173 observer.tableDeletionLatch = new CountDownLatch(1); 174 Admin admin = testUtil.getAdmin(); 175 try { 176 admin.disableTable(tableName); 177 } catch (Exception e) { 178 LOG.debug("Table: " + tableName + " already disabled, so just deleting it."); 179 } 180 admin.deleteTable(tableName); 181 observer.tableDeletionLatch.await(); 182 observer.tableDeletionLatch = null; 183 } 184}