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