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.assertTrue; 022 023import java.io.IOException; 024import java.util.Set; 025import org.apache.hadoop.fs.Path; 026import org.apache.hadoop.hbase.HBaseClassTestRule; 027import org.apache.hadoop.hbase.HBaseTestingUtility; 028import org.apache.hadoop.hbase.HColumnDescriptor; 029import org.apache.hadoop.hbase.HTableDescriptor; 030import org.apache.hadoop.hbase.InvalidFamilyOperationException; 031import org.apache.hadoop.hbase.TableName; 032import org.apache.hadoop.hbase.client.Admin; 033import org.apache.hadoop.hbase.client.TableDescriptor; 034import org.apache.hadoop.hbase.master.MasterFileSystem; 035import org.apache.hadoop.hbase.testclassification.MasterTests; 036import org.apache.hadoop.hbase.testclassification.MediumTests; 037import org.apache.hadoop.hbase.util.Bytes; 038import org.apache.hadoop.hbase.util.CommonFSUtils; 039import org.apache.hadoop.hbase.util.FSTableDescriptors; 040import org.junit.AfterClass; 041import org.junit.Assert; 042import org.junit.Before; 043import org.junit.BeforeClass; 044import org.junit.ClassRule; 045import org.junit.Rule; 046import org.junit.Test; 047import org.junit.experimental.categories.Category; 048import org.junit.rules.TestName; 049 050/** 051 * Verify that the HTableDescriptor is updated after addColumn(), deleteColumn() and modifyTable() 052 * operations. 053 */ 054@Category({ MasterTests.class, MediumTests.class }) 055public class TestTableDescriptorModificationFromClient { 056 057 @ClassRule 058 public static final HBaseClassTestRule CLASS_RULE = 059 HBaseClassTestRule.forClass(TestTableDescriptorModificationFromClient.class); 060 061 @Rule 062 public TestName name = new TestName(); 063 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 064 private static TableName TABLE_NAME = null; 065 private static final byte[] FAMILY_0 = Bytes.toBytes("cf0"); 066 private static final byte[] FAMILY_1 = Bytes.toBytes("cf1"); 067 068 /** 069 * Start up a mini cluster and put a small table of empty regions into it. 070 */ 071 @BeforeClass 072 public static void beforeAllTests() throws Exception { 073 TEST_UTIL.startMiniCluster(1); 074 } 075 076 @Before 077 public void setup() { 078 TABLE_NAME = TableName.valueOf(name.getMethodName()); 079 080 } 081 082 @AfterClass 083 public static void afterAllTests() throws Exception { 084 TEST_UTIL.shutdownMiniCluster(); 085 } 086 087 @Test 088 public void testModifyTable() throws IOException { 089 Admin admin = TEST_UTIL.getAdmin(); 090 // Create a table with one family 091 HTableDescriptor baseHtd = new HTableDescriptor(TABLE_NAME); 092 baseHtd.addFamily(new HColumnDescriptor(FAMILY_0)); 093 admin.createTable(baseHtd); 094 admin.disableTable(TABLE_NAME); 095 try { 096 // Verify the table descriptor 097 verifyTableDescriptor(TABLE_NAME, FAMILY_0); 098 099 // Modify the table adding another family and verify the descriptor 100 HTableDescriptor modifiedHtd = new HTableDescriptor(TABLE_NAME); 101 modifiedHtd.addFamily(new HColumnDescriptor(FAMILY_0)); 102 modifiedHtd.addFamily(new HColumnDescriptor(FAMILY_1)); 103 admin.modifyTable(TABLE_NAME, modifiedHtd); 104 verifyTableDescriptor(TABLE_NAME, FAMILY_0, FAMILY_1); 105 } finally { 106 admin.deleteTable(TABLE_NAME); 107 } 108 } 109 110 @Test 111 public void testAddColumn() throws IOException { 112 Admin admin = TEST_UTIL.getAdmin(); 113 // Create a table with two families 114 HTableDescriptor baseHtd = new HTableDescriptor(TABLE_NAME); 115 baseHtd.addFamily(new HColumnDescriptor(FAMILY_0)); 116 admin.createTable(baseHtd); 117 admin.disableTable(TABLE_NAME); 118 try { 119 // Verify the table descriptor 120 verifyTableDescriptor(TABLE_NAME, FAMILY_0); 121 122 // Modify the table removing one family and verify the descriptor 123 admin.addColumnFamily(TABLE_NAME, new HColumnDescriptor(FAMILY_1)); 124 verifyTableDescriptor(TABLE_NAME, FAMILY_0, FAMILY_1); 125 } finally { 126 admin.deleteTable(TABLE_NAME); 127 } 128 } 129 130 @Test 131 public void testAddSameColumnFamilyTwice() throws IOException { 132 Admin admin = TEST_UTIL.getAdmin(); 133 // Create a table with one families 134 HTableDescriptor baseHtd = new HTableDescriptor(TABLE_NAME); 135 baseHtd.addFamily(new HColumnDescriptor(FAMILY_0)); 136 admin.createTable(baseHtd); 137 admin.disableTable(TABLE_NAME); 138 try { 139 // Verify the table descriptor 140 verifyTableDescriptor(TABLE_NAME, FAMILY_0); 141 142 // Modify the table removing one family and verify the descriptor 143 admin.addColumnFamily(TABLE_NAME, new HColumnDescriptor(FAMILY_1)); 144 verifyTableDescriptor(TABLE_NAME, FAMILY_0, FAMILY_1); 145 146 try { 147 // Add same column family again - expect failure 148 admin.addColumnFamily(TABLE_NAME, new HColumnDescriptor(FAMILY_1)); 149 Assert.fail("Delete a non-exist column family should fail"); 150 } catch (InvalidFamilyOperationException e) { 151 // Expected. 152 } 153 154 } finally { 155 admin.deleteTable(TABLE_NAME); 156 } 157 } 158 159 @Test 160 public void testModifyColumnFamily() throws IOException { 161 Admin admin = TEST_UTIL.getAdmin(); 162 163 HColumnDescriptor cfDescriptor = new HColumnDescriptor(FAMILY_0); 164 int blockSize = cfDescriptor.getBlocksize(); 165 // Create a table with one families 166 HTableDescriptor baseHtd = new HTableDescriptor(TABLE_NAME); 167 baseHtd.addFamily(cfDescriptor); 168 admin.createTable(baseHtd); 169 admin.disableTable(TABLE_NAME); 170 try { 171 // Verify the table descriptor 172 verifyTableDescriptor(TABLE_NAME, FAMILY_0); 173 174 int newBlockSize = 2 * blockSize; 175 cfDescriptor.setBlocksize(newBlockSize); 176 177 // Modify colymn family 178 admin.modifyColumnFamily(TABLE_NAME, cfDescriptor); 179 180 HTableDescriptor htd = admin.getTableDescriptor(TABLE_NAME); 181 HColumnDescriptor hcfd = htd.getFamily(FAMILY_0); 182 assertTrue(hcfd.getBlocksize() == newBlockSize); 183 } finally { 184 admin.deleteTable(TABLE_NAME); 185 } 186 } 187 188 @Test 189 public void testModifyNonExistingColumnFamily() throws IOException { 190 Admin admin = TEST_UTIL.getAdmin(); 191 192 HColumnDescriptor cfDescriptor = new HColumnDescriptor(FAMILY_1); 193 int blockSize = cfDescriptor.getBlocksize(); 194 // Create a table with one families 195 HTableDescriptor baseHtd = new HTableDescriptor(TABLE_NAME); 196 baseHtd.addFamily(new HColumnDescriptor(FAMILY_0)); 197 admin.createTable(baseHtd); 198 admin.disableTable(TABLE_NAME); 199 try { 200 // Verify the table descriptor 201 verifyTableDescriptor(TABLE_NAME, FAMILY_0); 202 203 int newBlockSize = 2 * blockSize; 204 cfDescriptor.setBlocksize(newBlockSize); 205 206 // Modify a column family that is not in the table. 207 try { 208 admin.modifyColumnFamily(TABLE_NAME, cfDescriptor); 209 Assert.fail("Modify a non-exist column family should fail"); 210 } catch (InvalidFamilyOperationException e) { 211 // Expected. 212 } 213 214 } finally { 215 admin.deleteTable(TABLE_NAME); 216 } 217 } 218 219 @Test 220 public void testDeleteColumn() throws IOException { 221 Admin admin = TEST_UTIL.getAdmin(); 222 // Create a table with two families 223 HTableDescriptor baseHtd = new HTableDescriptor(TABLE_NAME); 224 baseHtd.addFamily(new HColumnDescriptor(FAMILY_0)); 225 baseHtd.addFamily(new HColumnDescriptor(FAMILY_1)); 226 admin.createTable(baseHtd); 227 admin.disableTable(TABLE_NAME); 228 try { 229 // Verify the table descriptor 230 verifyTableDescriptor(TABLE_NAME, FAMILY_0, FAMILY_1); 231 232 // Modify the table removing one family and verify the descriptor 233 admin.deleteColumnFamily(TABLE_NAME, FAMILY_1); 234 verifyTableDescriptor(TABLE_NAME, FAMILY_0); 235 } finally { 236 admin.deleteTable(TABLE_NAME); 237 } 238 } 239 240 @Test 241 public void testDeleteSameColumnFamilyTwice() throws IOException { 242 Admin admin = TEST_UTIL.getAdmin(); 243 // Create a table with two families 244 HTableDescriptor baseHtd = new HTableDescriptor(TABLE_NAME); 245 baseHtd.addFamily(new HColumnDescriptor(FAMILY_0)); 246 baseHtd.addFamily(new HColumnDescriptor(FAMILY_1)); 247 admin.createTable(baseHtd); 248 admin.disableTable(TABLE_NAME); 249 try { 250 // Verify the table descriptor 251 verifyTableDescriptor(TABLE_NAME, FAMILY_0, FAMILY_1); 252 253 // Modify the table removing one family and verify the descriptor 254 admin.deleteColumnFamily(TABLE_NAME, FAMILY_1); 255 verifyTableDescriptor(TABLE_NAME, FAMILY_0); 256 257 try { 258 // Delete again - expect failure 259 admin.deleteColumnFamily(TABLE_NAME, FAMILY_1); 260 Assert.fail("Delete a non-exist column family should fail"); 261 } catch (Exception e) { 262 // Expected. 263 } 264 } finally { 265 admin.deleteTable(TABLE_NAME); 266 } 267 } 268 269 private void verifyTableDescriptor(final TableName tableName, final byte[]... families) 270 throws IOException { 271 Admin admin = TEST_UTIL.getAdmin(); 272 273 // Verify descriptor from master 274 HTableDescriptor htd = admin.getTableDescriptor(tableName); 275 verifyTableDescriptor(htd, tableName, families); 276 277 // Verify descriptor from HDFS 278 MasterFileSystem mfs = TEST_UTIL.getMiniHBaseCluster().getMaster().getMasterFileSystem(); 279 Path tableDir = CommonFSUtils.getTableDir(mfs.getRootDir(), tableName); 280 TableDescriptor td = FSTableDescriptors.getTableDescriptorFromFs(mfs.getFileSystem(), tableDir); 281 verifyTableDescriptor(td, tableName, families); 282 } 283 284 private void verifyTableDescriptor(final TableDescriptor htd, final TableName tableName, 285 final byte[]... families) { 286 Set<byte[]> htdFamilies = htd.getColumnFamilyNames(); 287 assertEquals(tableName, htd.getTableName()); 288 assertEquals(families.length, htdFamilies.size()); 289 for (byte[] familyName : families) { 290 assertTrue("Expected family " + Bytes.toString(familyName), htdFamilies.contains(familyName)); 291 } 292 } 293}