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