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.procedure; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertTrue; 023 024import java.io.IOException; 025import org.apache.hadoop.fs.FileStatus; 026import org.apache.hadoop.fs.FileSystem; 027import org.apache.hadoop.fs.Path; 028import org.apache.hadoop.fs.PathFilter; 029import org.apache.hadoop.hbase.HBaseClassTestRule; 030import org.apache.hadoop.hbase.HBaseTestingUtility; 031import org.apache.hadoop.hbase.HColumnDescriptor; 032import org.apache.hadoop.hbase.HConstants; 033import org.apache.hadoop.hbase.HTableDescriptor; 034import org.apache.hadoop.hbase.InvalidFamilyOperationException; 035import org.apache.hadoop.hbase.TableName; 036import org.apache.hadoop.hbase.client.Admin; 037import org.apache.hadoop.hbase.client.Table; 038import org.apache.hadoop.hbase.testclassification.LargeTests; 039import org.apache.hadoop.hbase.testclassification.MasterTests; 040import org.apache.hadoop.hbase.util.Bytes; 041import org.apache.hadoop.hbase.util.FSUtils; 042import org.apache.hadoop.hbase.wal.WALSplitter; 043import org.junit.After; 044import org.junit.AfterClass; 045import org.junit.Assert; 046import org.junit.Before; 047import org.junit.BeforeClass; 048import org.junit.ClassRule; 049import org.junit.Test; 050import org.junit.experimental.categories.Category; 051 052@Category({MasterTests.class, LargeTests.class}) 053public class TestDeleteColumnFamilyProcedureFromClient { 054 055 @ClassRule 056 public static final HBaseClassTestRule CLASS_RULE = 057 HBaseClassTestRule.forClass(TestDeleteColumnFamilyProcedureFromClient.class); 058 059 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 060 061 private static final TableName TABLENAME = 062 TableName.valueOf("column_family_handlers"); 063 private static final byte[][] FAMILIES = new byte[][] { Bytes.toBytes("cf1"), 064 Bytes.toBytes("cf2"), Bytes.toBytes("cf3") }; 065 066 /** 067 * Start up a mini cluster and put a small table of empty regions into it. 068 * 069 * @throws Exception 070 */ 071 @BeforeClass 072 public static void beforeAllTests() throws Exception { 073 TEST_UTIL.startMiniCluster(2); 074 } 075 076 @AfterClass 077 public static void afterAllTests() throws Exception { 078 TEST_UTIL.shutdownMiniCluster(); 079 } 080 081 @Before 082 public void setup() throws IOException, InterruptedException { 083 // Create a table of three families. This will assign a region. 084 TEST_UTIL.createTable(TABLENAME, FAMILIES); 085 Table t = TEST_UTIL.getConnection().getTable(TABLENAME); 086 TEST_UTIL.waitUntilNoRegionsInTransition(); 087 088 // Load the table with data for all families 089 TEST_UTIL.loadTable(t, FAMILIES); 090 091 TEST_UTIL.flush(); 092 093 t.close(); 094 095 TEST_UTIL.ensureSomeRegionServersAvailable(2); 096 } 097 098 @After 099 public void cleanup() throws Exception { 100 TEST_UTIL.deleteTable(TABLENAME); 101 } 102 103 @Test 104 public void deleteColumnFamilyWithMultipleRegions() throws Exception { 105 Admin admin = TEST_UTIL.getAdmin(); 106 HTableDescriptor beforehtd = admin.getTableDescriptor(TABLENAME); 107 108 FileSystem fs = TEST_UTIL.getDFSCluster().getFileSystem(); 109 110 // 1 - Check if table exists in descriptor 111 assertTrue(admin.isTableAvailable(TABLENAME)); 112 113 // 2 - Check if all three families exist in descriptor 114 assertEquals(3, beforehtd.getColumnFamilyCount()); 115 HColumnDescriptor[] families = beforehtd.getColumnFamilies(); 116 for (int i = 0; i < families.length; i++) { 117 assertTrue(families[i].getNameAsString().equals("cf" + (i + 1))); 118 } 119 120 // 3 - Check if table exists in FS 121 Path tableDir = FSUtils.getTableDir(TEST_UTIL.getDefaultRootDirPath(), TABLENAME); 122 assertTrue(fs.exists(tableDir)); 123 124 // 4 - Check if all the 3 column families exist in FS 125 FileStatus[] fileStatus = fs.listStatus(tableDir); 126 for (int i = 0; i < fileStatus.length; i++) { 127 if (fileStatus[i].isDirectory() == true) { 128 FileStatus[] cf = fs.listStatus(fileStatus[i].getPath(), new PathFilter() { 129 @Override 130 public boolean accept(Path p) { 131 if (p.getName().contains(HConstants.RECOVERED_EDITS_DIR)) { 132 return false; 133 } 134 return true; 135 } 136 }); 137 int k = 1; 138 for (int j = 0; j < cf.length; j++) { 139 if (cf[j].isDirectory() == true 140 && cf[j].getPath().getName().startsWith(".") == false) { 141 assertEquals(cf[j].getPath().getName(), "cf" + k); 142 k++; 143 } 144 } 145 } 146 } 147 148 // TEST - Disable and delete the column family 149 admin.disableTable(TABLENAME); 150 admin.deleteColumnFamily(TABLENAME, Bytes.toBytes("cf2")); 151 152 // 5 - Check if only 2 column families exist in the descriptor 153 HTableDescriptor afterhtd = admin.getTableDescriptor(TABLENAME); 154 assertEquals(2, afterhtd.getColumnFamilyCount()); 155 HColumnDescriptor[] newFamilies = afterhtd.getColumnFamilies(); 156 assertTrue(newFamilies[0].getNameAsString().equals("cf1")); 157 assertTrue(newFamilies[1].getNameAsString().equals("cf3")); 158 159 // 6 - Check if the second column family is gone from the FS 160 fileStatus = fs.listStatus(tableDir); 161 for (int i = 0; i < fileStatus.length; i++) { 162 if (fileStatus[i].isDirectory() == true) { 163 FileStatus[] cf = fs.listStatus(fileStatus[i].getPath(), new PathFilter() { 164 @Override 165 public boolean accept(Path p) { 166 if (WALSplitter.isSequenceIdFile(p)) { 167 return false; 168 } 169 return true; 170 } 171 }); 172 for (int j = 0; j < cf.length; j++) { 173 if (cf[j].isDirectory() == true) { 174 assertFalse(cf[j].getPath().getName().equals("cf2")); 175 } 176 } 177 } 178 } 179 } 180 181 @Test 182 public void deleteColumnFamilyTwice() throws Exception { 183 Admin admin = TEST_UTIL.getAdmin(); 184 HTableDescriptor beforehtd = admin.getTableDescriptor(TABLENAME); 185 String cfToDelete = "cf1"; 186 187 FileSystem fs = TEST_UTIL.getDFSCluster().getFileSystem(); 188 189 // 1 - Check if table exists in descriptor 190 assertTrue(admin.isTableAvailable(TABLENAME)); 191 192 // 2 - Check if all the target column family exist in descriptor 193 HColumnDescriptor[] families = beforehtd.getColumnFamilies(); 194 Boolean foundCF = false; 195 for (int i = 0; i < families.length; i++) { 196 if (families[i].getNameAsString().equals(cfToDelete)) { 197 foundCF = true; 198 break; 199 } 200 } 201 assertTrue(foundCF); 202 203 // 3 - Check if table exists in FS 204 Path tableDir = FSUtils.getTableDir(TEST_UTIL.getDefaultRootDirPath(), TABLENAME); 205 assertTrue(fs.exists(tableDir)); 206 207 // 4 - Check if all the target column family exist in FS 208 FileStatus[] fileStatus = fs.listStatus(tableDir); 209 foundCF = false; 210 for (int i = 0; i < fileStatus.length; i++) { 211 if (fileStatus[i].isDirectory() == true) { 212 FileStatus[] cf = fs.listStatus(fileStatus[i].getPath(), new PathFilter() { 213 @Override 214 public boolean accept(Path p) { 215 if (p.getName().contains(HConstants.RECOVERED_EDITS_DIR)) { 216 return false; 217 } 218 return true; 219 } 220 }); 221 for (int j = 0; j < cf.length; j++) { 222 if (cf[j].isDirectory() == true && cf[j].getPath().getName().equals(cfToDelete)) { 223 foundCF = true; 224 break; 225 } 226 } 227 } 228 if (foundCF) { 229 break; 230 } 231 } 232 assertTrue(foundCF); 233 234 // TEST - Disable and delete the column family 235 if (admin.isTableEnabled(TABLENAME)) { 236 admin.disableTable(TABLENAME); 237 } 238 admin.deleteColumnFamily(TABLENAME, Bytes.toBytes(cfToDelete)); 239 240 // 5 - Check if the target column family is gone from the FS 241 fileStatus = fs.listStatus(tableDir); 242 for (int i = 0; i < fileStatus.length; i++) { 243 if (fileStatus[i].isDirectory() == true) { 244 FileStatus[] cf = fs.listStatus(fileStatus[i].getPath(), new PathFilter() { 245 @Override 246 public boolean accept(Path p) { 247 if (WALSplitter.isSequenceIdFile(p)) { 248 return false; 249 } 250 return true; 251 } 252 }); 253 for (int j = 0; j < cf.length; j++) { 254 if (cf[j].isDirectory() == true) { 255 assertFalse(cf[j].getPath().getName().equals(cfToDelete)); 256 } 257 } 258 } 259 } 260 261 try { 262 // Test: delete again 263 admin.deleteColumnFamily(TABLENAME, Bytes.toBytes(cfToDelete)); 264 Assert.fail("Delete a non-exist column family should fail"); 265 } catch (InvalidFamilyOperationException e) { 266 // Expected. 267 } 268 } 269}