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