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