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.client;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertTrue;
023import static org.junit.Assert.fail;
024
025import java.io.IOException;
026import java.util.List;
027import java.util.regex.Pattern;
028import org.apache.hadoop.hbase.HBaseClassTestRule;
029import org.apache.hadoop.hbase.HConstants;
030import org.apache.hadoop.hbase.HRegionLocation;
031import org.apache.hadoop.hbase.InvalidFamilyOperationException;
032import org.apache.hadoop.hbase.MetaTableAccessor;
033import org.apache.hadoop.hbase.TableName;
034import org.apache.hadoop.hbase.TableNotDisabledException;
035import org.apache.hadoop.hbase.TableNotEnabledException;
036import org.apache.hadoop.hbase.TableNotFoundException;
037import org.apache.hadoop.hbase.testclassification.ClientTests;
038import org.apache.hadoop.hbase.testclassification.LargeTests;
039import org.apache.hadoop.hbase.util.Bytes;
040import org.junit.ClassRule;
041import org.junit.Test;
042import org.junit.experimental.categories.Category;
043import org.slf4j.Logger;
044import org.slf4j.LoggerFactory;
045
046@Category({ LargeTests.class, ClientTests.class })
047public class TestAdmin3 extends TestAdminBase {
048
049  @ClassRule
050  public static final HBaseClassTestRule CLASS_RULE = HBaseClassTestRule.forClass(TestAdmin3.class);
051
052  private static final Logger LOG = LoggerFactory.getLogger(TestAdmin3.class);
053
054  @Test
055  public void testDisableAndEnableTable() throws IOException {
056    final byte[] row = Bytes.toBytes("row");
057    final byte[] qualifier = Bytes.toBytes("qualifier");
058    final byte[] value = Bytes.toBytes("value");
059    final TableName table = TableName.valueOf(name.getMethodName());
060    Table ht = TEST_UTIL.createTable(table, HConstants.CATALOG_FAMILY);
061    Put put = new Put(row);
062    put.addColumn(HConstants.CATALOG_FAMILY, qualifier, value);
063    ht.put(put);
064    Get get = new Get(row);
065    get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
066    ht.get(get);
067
068    ADMIN.disableTable(ht.getName());
069    assertTrue("Table must be disabled.", TEST_UTIL.getHBaseCluster().getMaster()
070      .getTableStateManager().isTableState(ht.getName(), TableState.State.DISABLED));
071    assertEquals(TableState.State.DISABLED, getStateFromMeta(table));
072
073    // Test that table is disabled
074    get = new Get(row);
075    get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
076    boolean ok = false;
077    try {
078      ht.get(get);
079    } catch (TableNotEnabledException e) {
080      ok = true;
081    }
082    ok = false;
083    // verify that scan encounters correct exception
084    Scan scan = new Scan();
085    try {
086      ResultScanner scanner = ht.getScanner(scan);
087      Result res = null;
088      do {
089        res = scanner.next();
090      } while (res != null);
091    } catch (TableNotEnabledException e) {
092      ok = true;
093    }
094    assertTrue(ok);
095    ADMIN.enableTable(table);
096    assertTrue("Table must be enabled.", TEST_UTIL.getHBaseCluster().getMaster()
097      .getTableStateManager().isTableState(ht.getName(), TableState.State.ENABLED));
098    assertEquals(TableState.State.ENABLED, getStateFromMeta(table));
099
100    // Test that table is enabled
101    try {
102      ht.get(get);
103    } catch (RetriesExhaustedException e) {
104      ok = false;
105    }
106    assertTrue(ok);
107    ht.close();
108  }
109
110  @Test
111  public void testDisableAndEnableTables() throws IOException {
112    final byte[] row = Bytes.toBytes("row");
113    final byte[] qualifier = Bytes.toBytes("qualifier");
114    final byte[] value = Bytes.toBytes("value");
115    final TableName table1 = TableName.valueOf(name.getMethodName() + "1");
116    final TableName table2 = TableName.valueOf(name.getMethodName() + "2");
117    Table ht1 = TEST_UTIL.createTable(table1, HConstants.CATALOG_FAMILY);
118    Table ht2 = TEST_UTIL.createTable(table2, HConstants.CATALOG_FAMILY);
119    Put put = new Put(row);
120    put.addColumn(HConstants.CATALOG_FAMILY, qualifier, value);
121    ht1.put(put);
122    ht2.put(put);
123    Get get = new Get(row);
124    get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
125    ht1.get(get);
126    ht2.get(get);
127
128    TableName[] tableNames = ADMIN.listTableNames(Pattern.compile("testDisableAndEnableTable.*"));
129    for (TableName tableName : tableNames) {
130      ADMIN.disableTable(tableName);
131    }
132
133    // Test that tables are disabled
134    get = new Get(row);
135    get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
136    boolean ok = false;
137    try {
138      ht1.get(get);
139      ht2.get(get);
140    } catch (org.apache.hadoop.hbase.DoNotRetryIOException e) {
141      ok = true;
142    }
143
144    assertEquals(TableState.State.DISABLED, getStateFromMeta(table1));
145    assertEquals(TableState.State.DISABLED, getStateFromMeta(table2));
146
147    assertTrue(ok);
148    for (TableName tableName : tableNames) {
149      ADMIN.enableTable(tableName);
150    }
151
152    // Test that tables are enabled
153    try {
154      ht1.get(get);
155    } catch (IOException e) {
156      ok = false;
157    }
158    try {
159      ht2.get(get);
160    } catch (IOException e) {
161      ok = false;
162    }
163    assertTrue(ok);
164
165    ht1.close();
166    ht2.close();
167
168    assertEquals(TableState.State.ENABLED, getStateFromMeta(table1));
169    assertEquals(TableState.State.ENABLED, getStateFromMeta(table2));
170  }
171
172  /**
173   * Test retain assignment on enableTable.
174   */
175  @Test
176  public void testEnableTableRetainAssignment() throws IOException {
177    final TableName tableName = TableName.valueOf(name.getMethodName());
178    byte[][] splitKeys = { new byte[] { 1, 1, 1 }, new byte[] { 2, 2, 2 }, new byte[] { 3, 3, 3 },
179      new byte[] { 4, 4, 4 }, new byte[] { 5, 5, 5 }, new byte[] { 6, 6, 6 },
180      new byte[] { 7, 7, 7 }, new byte[] { 8, 8, 8 }, new byte[] { 9, 9, 9 } };
181    int expectedRegions = splitKeys.length + 1;
182    TableDescriptor desc = TableDescriptorBuilder.newBuilder(tableName)
183      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(HConstants.CATALOG_FAMILY)).build();
184    ADMIN.createTable(desc, splitKeys);
185
186    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
187      List<HRegionLocation> regions = l.getAllRegionLocations();
188
189      assertEquals(
190        "Tried to create " + expectedRegions + " regions " + "but only found " + regions.size(),
191        expectedRegions, regions.size());
192      // Disable table.
193      ADMIN.disableTable(tableName);
194      // Enable table, use retain assignment to assign regions.
195      ADMIN.enableTable(tableName);
196      List<HRegionLocation> regions2 = l.getAllRegionLocations();
197
198      // Check the assignment.
199      assertEquals(regions.size(), regions2.size());
200      assertTrue(regions2.containsAll(regions));
201    }
202  }
203
204  @Test
205  public void testEnableDisableAddColumnDeleteColumn() throws Exception {
206    final TableName tableName = TableName.valueOf(name.getMethodName());
207    TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
208    while (!ADMIN.isTableEnabled(TableName.valueOf(name.getMethodName()))) {
209      Thread.sleep(10);
210    }
211    ADMIN.disableTable(tableName);
212    try {
213      TEST_UTIL.getConnection().getTable(tableName);
214    } catch (org.apache.hadoop.hbase.DoNotRetryIOException e) {
215      // expected
216    }
217
218    ADMIN.addColumnFamily(tableName, ColumnFamilyDescriptorBuilder.of("col2"));
219    ADMIN.enableTable(tableName);
220    try {
221      ADMIN.deleteColumnFamily(tableName, Bytes.toBytes("col2"));
222    } catch (TableNotDisabledException e) {
223      LOG.info(e.toString(), e);
224    }
225    ADMIN.disableTable(tableName);
226    ADMIN.deleteTable(tableName);
227  }
228
229  @Test
230  public void testGetTableDescriptor() throws IOException {
231    TableDescriptor htd = TableDescriptorBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
232      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("fam1"))
233      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("fam2"))
234      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("fam3")).build();
235    ADMIN.createTable(htd);
236    Table table = TEST_UTIL.getConnection().getTable(htd.getTableName());
237    TableDescriptor confirmedHtd = table.getDescriptor();
238    assertEquals(0, TableDescriptor.COMPARATOR.compare(htd, confirmedHtd));
239    MetaTableAccessor.fullScanMetaAndPrint(TEST_UTIL.getConnection());
240    table.close();
241  }
242
243  /**
244   * Verify schema change for read only table
245   */
246  @Test
247  public void testReadOnlyTableModify() throws IOException, InterruptedException {
248    final TableName tableName = TableName.valueOf(name.getMethodName());
249    TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
250
251    // Make table read only
252    TableDescriptor htd =
253      TableDescriptorBuilder.newBuilder(ADMIN.getDescriptor(tableName)).setReadOnly(true).build();
254    ADMIN.modifyTable(htd);
255
256    // try to modify the read only table now
257    htd = TableDescriptorBuilder.newBuilder(ADMIN.getDescriptor(tableName))
258      .setCompactionEnabled(false).build();
259    ADMIN.modifyTable(htd);
260    // Delete the table
261    ADMIN.disableTable(tableName);
262    ADMIN.deleteTable(tableName);
263    assertFalse(ADMIN.tableExists(tableName));
264  }
265
266  @Test(expected = TableNotDisabledException.class)
267  public void testModifyRegionReplicasEnabledTable() throws Exception {
268    final TableName tableName = TableName.valueOf(name.getMethodName());
269    TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
270
271    // Modify region replication count
272    TableDescriptor htd = TableDescriptorBuilder.newBuilder(ADMIN.getDescriptor(tableName))
273      .setRegionReplication(3).build();
274    try {
275      // try to modify the region replication count without disabling the table
276      ADMIN.modifyTable(htd);
277      fail("Expected an exception");
278    } finally {
279      // Delete the table
280      ADMIN.disableTable(tableName);
281      ADMIN.deleteTable(tableName);
282      assertFalse(ADMIN.tableExists(tableName));
283    }
284  }
285
286  @Test
287  public void testDeleteLastColumnFamily() throws Exception {
288    final TableName tableName = TableName.valueOf(name.getMethodName());
289    TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
290    while (!ADMIN.isTableEnabled(TableName.valueOf(name.getMethodName()))) {
291      Thread.sleep(10);
292    }
293
294    // test for enabled table
295    try {
296      ADMIN.deleteColumnFamily(tableName, HConstants.CATALOG_FAMILY);
297      fail("Should have failed to delete the only column family of a table");
298    } catch (InvalidFamilyOperationException ex) {
299      // expected
300    }
301
302    // test for disabled table
303    ADMIN.disableTable(tableName);
304
305    try {
306      ADMIN.deleteColumnFamily(tableName, HConstants.CATALOG_FAMILY);
307      fail("Should have failed to delete the only column family of a table");
308    } catch (InvalidFamilyOperationException ex) {
309      // expected
310    }
311
312    ADMIN.deleteTable(tableName);
313  }
314
315  @Test
316  public void testDeleteEditUnknownColumnFamilyAndOrTable() throws IOException {
317    // Test we get exception if we try to
318    final TableName nonexistentTable = TableName.valueOf("nonexistent");
319    final byte[] nonexistentColumn = Bytes.toBytes("nonexistent");
320    ColumnFamilyDescriptor nonexistentHcd = ColumnFamilyDescriptorBuilder.of(nonexistentColumn);
321    Exception exception = null;
322    try {
323      ADMIN.addColumnFamily(nonexistentTable, nonexistentHcd);
324    } catch (IOException e) {
325      exception = e;
326    }
327    assertTrue(exception instanceof TableNotFoundException);
328
329    exception = null;
330    try {
331      ADMIN.deleteTable(nonexistentTable);
332    } catch (IOException e) {
333      exception = e;
334    }
335    assertTrue(exception instanceof TableNotFoundException);
336
337    exception = null;
338    try {
339      ADMIN.deleteColumnFamily(nonexistentTable, nonexistentColumn);
340    } catch (IOException e) {
341      exception = e;
342    }
343    assertTrue(exception instanceof TableNotFoundException);
344
345    exception = null;
346    try {
347      ADMIN.disableTable(nonexistentTable);
348    } catch (IOException e) {
349      exception = e;
350    }
351    assertTrue(exception instanceof TableNotFoundException);
352
353    exception = null;
354    try {
355      ADMIN.enableTable(nonexistentTable);
356    } catch (IOException e) {
357      exception = e;
358    }
359    assertTrue(exception instanceof TableNotFoundException);
360
361    exception = null;
362    try {
363      ADMIN.modifyColumnFamily(nonexistentTable, nonexistentHcd);
364    } catch (IOException e) {
365      exception = e;
366    }
367    assertTrue(exception instanceof TableNotFoundException);
368
369    exception = null;
370    try {
371      TableDescriptor htd = TableDescriptorBuilder.newBuilder(nonexistentTable)
372        .setColumnFamily(ColumnFamilyDescriptorBuilder.of(HConstants.CATALOG_FAMILY)).build();
373      ADMIN.modifyTable(htd);
374    } catch (IOException e) {
375      exception = e;
376    }
377    assertTrue(exception instanceof TableNotFoundException);
378
379    // Now make it so at least the table exists and then do tests against a
380    // nonexistent column family -- see if we get right exceptions.
381    final TableName tableName =
382      TableName.valueOf(name.getMethodName() + System.currentTimeMillis());
383    TableDescriptor htd = TableDescriptorBuilder.newBuilder(tableName)
384      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("cf")).build();
385    ADMIN.createTable(htd);
386    try {
387      exception = null;
388      try {
389        ADMIN.deleteColumnFamily(htd.getTableName(), nonexistentHcd.getName());
390      } catch (IOException e) {
391        exception = e;
392      }
393      assertTrue("found=" + exception.getClass().getName(),
394        exception instanceof InvalidFamilyOperationException);
395
396      exception = null;
397      try {
398        ADMIN.modifyColumnFamily(htd.getTableName(), nonexistentHcd);
399      } catch (IOException e) {
400        exception = e;
401      }
402      assertTrue("found=" + exception.getClass().getName(),
403        exception instanceof InvalidFamilyOperationException);
404    } finally {
405      ADMIN.disableTable(tableName);
406      ADMIN.deleteTable(tableName);
407    }
408  }
409}