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