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