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 were 079 * missing the hregioninfo because of error were being left behind messing up any subsequent table 080 * made with the same name. HBASE-12980 nn 081 */ 082 @Test 083 public void testDeleteForSureClearsAllTableRowsFromMeta() 084 throws IOException, InterruptedException { 085 final TableName tableName = TableName.valueOf(name.getMethodName()); 086 final Admin admin = TEST_UTIL.getAdmin(); 087 final HTableDescriptor desc = new HTableDescriptor(tableName); 088 desc.addFamily(new HColumnDescriptor(FAMILYNAME)); 089 try { 090 createTable(TEST_UTIL, desc, HBaseTestingUtility.KEYS_FOR_HBA_CREATE_TABLE); 091 } catch (Exception e) { 092 e.printStackTrace(); 093 fail("Got an exception while creating " + tableName); 094 } 095 // Now I have a nice table, mangle it by removing the HConstants.REGIONINFO_QUALIFIER_STR 096 // content from a few of the rows. 097 try (Table metaTable = TEST_UTIL.getConnection().getTable(TableName.META_TABLE_NAME)) { 098 try (ResultScanner scanner = metaTable.getScanner( 099 MetaTableAccessor.getScanForTableName(TEST_UTIL.getConfiguration(), tableName))) { 100 for (Result result : scanner) { 101 // Just delete one row. 102 Delete d = new Delete(result.getRow()); 103 d.addColumn(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER); 104 LOG.info("Mangled: " + d); 105 metaTable.delete(d); 106 break; 107 } 108 } 109 admin.disableTable(tableName); 110 TEST_UTIL.waitTableDisabled(tableName.getName()); 111 // Rely on the coprocessor based latch to make the operation synchronous. 112 try { 113 deleteTable(TEST_UTIL, tableName); 114 } catch (Exception e) { 115 e.printStackTrace(); 116 fail("Got an exception while deleting " + tableName); 117 } 118 int rowCount = 0; 119 try (ResultScanner scanner = metaTable.getScanner( 120 MetaTableAccessor.getScanForTableName(TEST_UTIL.getConfiguration(), tableName))) { 121 for (Result result : scanner) { 122 LOG.info("Found when none expected: " + result); 123 rowCount++; 124 } 125 } 126 assertEquals(0, rowCount); 127 } 128 } 129 130 public static class MasterSyncObserver implements MasterCoprocessor, MasterObserver { 131 volatile CountDownLatch tableCreationLatch = null; 132 volatile CountDownLatch tableDeletionLatch = null; 133 134 @Override 135 public Optional<MasterObserver> getMasterObserver() { 136 return Optional.of(this); 137 } 138 139 @Override 140 public void postCompletedCreateTableAction( 141 final ObserverContext<MasterCoprocessorEnvironment> ctx, final TableDescriptor desc, 142 final RegionInfo[] regions) throws IOException { 143 // the AccessController test, some times calls only and directly the 144 // postCompletedCreateTableAction() 145 if (tableCreationLatch != null) { 146 tableCreationLatch.countDown(); 147 } 148 } 149 150 @Override 151 public void postCompletedDeleteTableAction( 152 final ObserverContext<MasterCoprocessorEnvironment> ctx, final TableName tableName) 153 throws IOException { 154 // the AccessController test, some times calls only and directly the postDeleteTableHandler() 155 if (tableDeletionLatch != null) { 156 tableDeletionLatch.countDown(); 157 } 158 } 159 } 160 161 public static void createTable(HBaseTestingUtility testUtil, HTableDescriptor htd, 162 byte[][] splitKeys) throws Exception { 163 // NOTE: We need a latch because admin is not sync, 164 // so the postOp coprocessor method may be called after the admin operation returned. 165 MasterSyncObserver observer = testUtil.getHBaseCluster().getMaster().getMasterCoprocessorHost() 166 .findCoprocessor(MasterSyncObserver.class); 167 observer.tableCreationLatch = new CountDownLatch(1); 168 Admin admin = testUtil.getAdmin(); 169 if (splitKeys != null) { 170 admin.createTable(htd, splitKeys); 171 } else { 172 admin.createTable(htd); 173 } 174 observer.tableCreationLatch.await(); 175 observer.tableCreationLatch = null; 176 testUtil.waitUntilAllRegionsAssigned(htd.getTableName()); 177 } 178 179 public static void deleteTable(HBaseTestingUtility testUtil, TableName tableName) 180 throws Exception { 181 // NOTE: We need a latch because admin is not sync, 182 // so the postOp coprocessor method may be called after the admin operation returned. 183 MasterSyncObserver observer = testUtil.getHBaseCluster().getMaster().getMasterCoprocessorHost() 184 .findCoprocessor(MasterSyncObserver.class); 185 observer.tableDeletionLatch = new CountDownLatch(1); 186 Admin admin = testUtil.getAdmin(); 187 try { 188 admin.disableTable(tableName); 189 } catch (Exception e) { 190 LOG.debug("Table: " + tableName + " already disabled, so just deleting it."); 191 } 192 admin.deleteTable(tableName); 193 observer.tableDeletionLatch.await(); 194 observer.tableDeletionLatch = null; 195 } 196}