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