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.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory.TRACKER_IMPL;
021import static org.junit.jupiter.api.Assertions.assertArrayEquals;
022import static org.junit.jupiter.api.Assertions.assertEquals;
023import static org.junit.jupiter.api.Assertions.assertFalse;
024import static org.junit.jupiter.api.Assertions.assertNull;
025import static org.junit.jupiter.api.Assertions.assertThrows;
026import static org.junit.jupiter.api.Assertions.assertTrue;
027import static org.junit.jupiter.api.Assertions.fail;
028
029import java.io.IOException;
030import java.util.List;
031import java.util.regex.Pattern;
032import org.apache.hadoop.hbase.DoNotRetryIOException;
033import org.apache.hadoop.hbase.HConstants;
034import org.apache.hadoop.hbase.HRegionLocation;
035import org.apache.hadoop.hbase.InvalidFamilyOperationException;
036import org.apache.hadoop.hbase.MetaTableAccessor;
037import org.apache.hadoop.hbase.TableName;
038import org.apache.hadoop.hbase.TableNotDisabledException;
039import org.apache.hadoop.hbase.TableNotEnabledException;
040import org.apache.hadoop.hbase.TableNotFoundException;
041import org.apache.hadoop.hbase.regionserver.NoSuchColumnFamilyException;
042import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory;
043import org.apache.hadoop.hbase.testclassification.ClientTests;
044import org.apache.hadoop.hbase.testclassification.LargeTests;
045import org.apache.hadoop.hbase.util.Bytes;
046import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
047import org.junit.jupiter.api.Tag;
048import org.junit.jupiter.api.Test;
049import org.slf4j.Logger;
050import org.slf4j.LoggerFactory;
051
052@Tag(LargeTests.TAG)
053@Tag(ClientTests.TAG)
054public class TestAdmin3 extends TestAdminBase {
055
056  private static final Logger LOG = LoggerFactory.getLogger(TestAdmin3.class);
057
058  @Test
059  public void testDisableAndEnableTable() throws IOException {
060    final byte[] row = Bytes.toBytes("row");
061    final byte[] qualifier = Bytes.toBytes("qualifier");
062    final byte[] value = Bytes.toBytes("value");
063    final TableName table = TableName.valueOf(methodName);
064    Table ht = TEST_UTIL.createTable(table, HConstants.CATALOG_FAMILY);
065    Put put = new Put(row);
066    put.addColumn(HConstants.CATALOG_FAMILY, qualifier, value);
067    ht.put(put);
068    Get get = new Get(row);
069    get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
070    ht.get(get);
071
072    ADMIN.disableTable(ht.getName());
073    assertTrue(TEST_UTIL.getHBaseCluster().getMaster().getTableStateManager()
074      .isTableState(ht.getName(), TableState.State.DISABLED), "Table must be disabled.");
075    assertEquals(TableState.State.DISABLED, getStateFromMeta(table));
076
077    // Test that table is disabled
078    get = new Get(row);
079    get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
080    boolean ok = false;
081    try {
082      ht.get(get);
083    } catch (TableNotEnabledException e) {
084      ok = true;
085    }
086    ok = false;
087    // verify that scan encounters correct exception
088    Scan scan = new Scan();
089    try {
090      ResultScanner scanner = ht.getScanner(scan);
091      Result res = null;
092      do {
093        res = scanner.next();
094      } while (res != null);
095    } catch (TableNotEnabledException e) {
096      ok = true;
097    }
098    assertTrue(ok);
099    ADMIN.enableTable(table);
100    assertTrue(TEST_UTIL.getHBaseCluster().getMaster().getTableStateManager()
101      .isTableState(ht.getName(), TableState.State.ENABLED), "Table must be enabled.");
102    assertEquals(TableState.State.ENABLED, getStateFromMeta(table));
103
104    // Test that table is enabled
105    try {
106      ht.get(get);
107    } catch (RetriesExhaustedException e) {
108      ok = false;
109    }
110    assertTrue(ok);
111    ht.close();
112  }
113
114  @Test
115  public void testDisableAndEnableTables() throws IOException {
116    final byte[] row = Bytes.toBytes("row");
117    final byte[] qualifier = Bytes.toBytes("qualifier");
118    final byte[] value = Bytes.toBytes("value");
119    final TableName table1 = TableName.valueOf(methodName + "1");
120    final TableName table2 = TableName.valueOf(methodName + "2");
121    Table ht1 = TEST_UTIL.createTable(table1, HConstants.CATALOG_FAMILY);
122    Table ht2 = TEST_UTIL.createTable(table2, HConstants.CATALOG_FAMILY);
123    Put put = new Put(row);
124    put.addColumn(HConstants.CATALOG_FAMILY, qualifier, value);
125    ht1.put(put);
126    ht2.put(put);
127    Get get = new Get(row);
128    get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
129    ht1.get(get);
130    ht2.get(get);
131
132    TableName[] tableNames = ADMIN.listTableNames(Pattern.compile("testDisableAndEnableTable.*"));
133    for (TableName tableName : tableNames) {
134      ADMIN.disableTable(tableName);
135    }
136
137    // Test that tables are disabled
138    get = new Get(row);
139    get.addColumn(HConstants.CATALOG_FAMILY, qualifier);
140    boolean ok = false;
141    try {
142      ht1.get(get);
143      ht2.get(get);
144    } catch (org.apache.hadoop.hbase.DoNotRetryIOException e) {
145      ok = true;
146    }
147
148    assertEquals(TableState.State.DISABLED, getStateFromMeta(table1));
149    assertEquals(TableState.State.DISABLED, getStateFromMeta(table2));
150
151    assertTrue(ok);
152    for (TableName tableName : tableNames) {
153      ADMIN.enableTable(tableName);
154    }
155
156    // Test that tables are enabled
157    try {
158      ht1.get(get);
159    } catch (IOException e) {
160      ok = false;
161    }
162    try {
163      ht2.get(get);
164    } catch (IOException e) {
165      ok = false;
166    }
167    assertTrue(ok);
168
169    ht1.close();
170    ht2.close();
171
172    assertEquals(TableState.State.ENABLED, getStateFromMeta(table1));
173    assertEquals(TableState.State.ENABLED, getStateFromMeta(table2));
174  }
175
176  /**
177   * Test retain assignment on enableTable.
178   */
179  @Test
180  public void testEnableTableRetainAssignment() throws IOException {
181    final TableName tableName = TableName.valueOf(methodName);
182    byte[][] splitKeys = { new byte[] { 1, 1, 1 }, new byte[] { 2, 2, 2 }, new byte[] { 3, 3, 3 },
183      new byte[] { 4, 4, 4 }, new byte[] { 5, 5, 5 }, new byte[] { 6, 6, 6 },
184      new byte[] { 7, 7, 7 }, new byte[] { 8, 8, 8 }, new byte[] { 9, 9, 9 } };
185    int expectedRegions = splitKeys.length + 1;
186    TableDescriptor desc = TableDescriptorBuilder.newBuilder(tableName)
187      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(HConstants.CATALOG_FAMILY)).build();
188    ADMIN.createTable(desc, splitKeys);
189
190    try (RegionLocator l = TEST_UTIL.getConnection().getRegionLocator(tableName)) {
191      List<HRegionLocation> regions = l.getAllRegionLocations();
192
193      assertEquals(expectedRegions, regions.size(),
194        "Tried to create " + expectedRegions + " regions " + "but only found " + regions.size());
195      // Disable table.
196      ADMIN.disableTable(tableName);
197      // Enable table, use retain assignment to assign regions.
198      ADMIN.enableTable(tableName);
199      List<HRegionLocation> regions2 = l.getAllRegionLocations();
200
201      // Check the assignment.
202      assertEquals(regions.size(), regions2.size());
203      assertTrue(regions2.containsAll(regions));
204    }
205  }
206
207  @Test
208  public void testEnableDisableAddColumnDeleteColumn() throws Exception {
209    final TableName tableName = TableName.valueOf(methodName);
210    TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
211    while (!ADMIN.isTableEnabled(TableName.valueOf(methodName))) {
212      Thread.sleep(10);
213    }
214    ADMIN.disableTable(tableName);
215    try {
216      TEST_UTIL.getConnection().getTable(tableName);
217    } catch (org.apache.hadoop.hbase.DoNotRetryIOException e) {
218      // expected
219    }
220
221    ADMIN.addColumnFamily(tableName, ColumnFamilyDescriptorBuilder.of("col2"));
222    ADMIN.enableTable(tableName);
223    try {
224      ADMIN.deleteColumnFamily(tableName, Bytes.toBytes("col2"));
225    } catch (TableNotDisabledException e) {
226      LOG.info(e.toString(), e);
227    }
228    ADMIN.disableTable(tableName);
229    ADMIN.deleteTable(tableName);
230  }
231
232  @Test
233  public void testGetTableDescriptor() throws IOException {
234    TableDescriptor htd = TableDescriptorBuilder.newBuilder(TableName.valueOf(methodName))
235      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("fam1"))
236      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("fam2"))
237      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("fam3")).build();
238    ADMIN.createTable(htd);
239    Table table = TEST_UTIL.getConnection().getTable(htd.getTableName());
240    TableDescriptor confirmedHtd = table.getDescriptor();
241    // HBASE-26246 introduced persist of store file tracker into table descriptor
242    htd = TableDescriptorBuilder.newBuilder(htd).setValue(TRACKER_IMPL,
243      StoreFileTrackerFactory.getStoreFileTrackerName(TEST_UTIL.getConfiguration())).build();
244    assertEquals(0, TableDescriptor.COMPARATOR.compare(htd, confirmedHtd));
245    MetaTableAccessor.fullScanMetaAndPrint(TEST_UTIL.getConnection());
246    table.close();
247  }
248
249  /**
250   * Verify schema change for read only table
251   */
252  @Test
253  public void testReadOnlyTableModify() throws IOException, InterruptedException {
254    final TableName tableName = TableName.valueOf(methodName);
255    TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
256
257    // Make table read only
258    TableDescriptor htd =
259      TableDescriptorBuilder.newBuilder(ADMIN.getDescriptor(tableName)).setReadOnly(true).build();
260    ADMIN.modifyTable(htd);
261
262    // try to modify the read only table now
263    htd = TableDescriptorBuilder.newBuilder(ADMIN.getDescriptor(tableName))
264      .setCompactionEnabled(false).build();
265    ADMIN.modifyTable(htd);
266    // Delete the table
267    ADMIN.disableTable(tableName);
268    ADMIN.deleteTable(tableName);
269    assertFalse(ADMIN.tableExists(tableName));
270  }
271
272  @Test
273  public void testDeleteLastColumnFamily() throws Exception {
274    final TableName tableName = TableName.valueOf(methodName);
275    TEST_UTIL.createTable(tableName, HConstants.CATALOG_FAMILY).close();
276    while (!ADMIN.isTableEnabled(TableName.valueOf(methodName))) {
277      Thread.sleep(10);
278    }
279
280    // test for enabled table
281    try {
282      ADMIN.deleteColumnFamily(tableName, HConstants.CATALOG_FAMILY);
283      fail("Should have failed to delete the only column family of a table");
284    } catch (InvalidFamilyOperationException ex) {
285      // expected
286    }
287
288    // test for disabled table
289    ADMIN.disableTable(tableName);
290
291    try {
292      ADMIN.deleteColumnFamily(tableName, HConstants.CATALOG_FAMILY);
293      fail("Should have failed to delete the only column family of a table");
294    } catch (InvalidFamilyOperationException ex) {
295      // expected
296    }
297
298    ADMIN.deleteTable(tableName);
299  }
300
301  @Test
302  public void testDeleteEditUnknownColumnFamilyAndOrTable() throws IOException {
303    // Test we get exception if we try to
304    final TableName nonexistentTable = TableName.valueOf("nonexistent");
305    final byte[] nonexistentColumn = Bytes.toBytes("nonexistent");
306    ColumnFamilyDescriptor nonexistentHcd = ColumnFamilyDescriptorBuilder.of(nonexistentColumn);
307    Exception exception = null;
308    try {
309      ADMIN.addColumnFamily(nonexistentTable, nonexistentHcd);
310    } catch (IOException e) {
311      exception = e;
312    }
313    assertTrue(exception instanceof TableNotFoundException);
314
315    exception = null;
316    try {
317      ADMIN.deleteTable(nonexistentTable);
318    } catch (IOException e) {
319      exception = e;
320    }
321    assertTrue(exception instanceof TableNotFoundException);
322
323    exception = null;
324    try {
325      ADMIN.deleteColumnFamily(nonexistentTable, nonexistentColumn);
326    } catch (IOException e) {
327      exception = e;
328    }
329    assertTrue(exception instanceof TableNotFoundException);
330
331    exception = null;
332    try {
333      ADMIN.disableTable(nonexistentTable);
334    } catch (IOException e) {
335      exception = e;
336    }
337    assertTrue(exception instanceof TableNotFoundException);
338
339    exception = null;
340    try {
341      ADMIN.enableTable(nonexistentTable);
342    } catch (IOException e) {
343      exception = e;
344    }
345    assertTrue(exception instanceof TableNotFoundException);
346
347    exception = null;
348    try {
349      ADMIN.modifyColumnFamily(nonexistentTable, nonexistentHcd);
350    } catch (IOException e) {
351      exception = e;
352    }
353    assertTrue(exception instanceof TableNotFoundException);
354
355    exception = null;
356    try {
357      TableDescriptor htd = TableDescriptorBuilder.newBuilder(nonexistentTable)
358        .setColumnFamily(ColumnFamilyDescriptorBuilder.of(HConstants.CATALOG_FAMILY)).build();
359      ADMIN.modifyTable(htd);
360    } catch (IOException e) {
361      exception = e;
362    }
363    assertTrue(exception instanceof TableNotFoundException);
364
365    // Now make it so at least the table exists and then do tests against a
366    // nonexistent column family -- see if we get right exceptions.
367    final TableName tableName =
368      TableName.valueOf(methodName + EnvironmentEdgeManager.currentTime());
369    TableDescriptor htd = TableDescriptorBuilder.newBuilder(tableName)
370      .setColumnFamily(ColumnFamilyDescriptorBuilder.of("cf")).build();
371    ADMIN.createTable(htd);
372    try {
373      exception = null;
374      try {
375        ADMIN.deleteColumnFamily(htd.getTableName(), nonexistentHcd.getName());
376      } catch (IOException e) {
377        exception = e;
378      }
379      assertTrue(exception instanceof InvalidFamilyOperationException,
380        "found=" + exception.getClass().getName());
381
382      exception = null;
383      try {
384        ADMIN.modifyColumnFamily(htd.getTableName(), nonexistentHcd);
385      } catch (IOException e) {
386        exception = e;
387      }
388      assertTrue(exception instanceof InvalidFamilyOperationException,
389        "found=" + exception.getClass().getName());
390    } finally {
391      ADMIN.disableTable(tableName);
392      ADMIN.deleteTable(tableName);
393    }
394  }
395
396  private static final String SRC_IMPL = "hbase.store.file-tracker.migration.src.impl";
397
398  private static final String DST_IMPL = "hbase.store.file-tracker.migration.dst.impl";
399
400  private void verifyModifyTableResult(TableName tableName, byte[] family, byte[] qual, byte[] row,
401    byte[] value, String sft) throws IOException {
402    TableDescriptor td = ADMIN.getDescriptor(tableName);
403    assertEquals(sft, td.getValue(StoreFileTrackerFactory.TRACKER_IMPL));
404    // no migration related configs
405    assertNull(td.getValue(SRC_IMPL));
406    assertNull(td.getValue(DST_IMPL));
407    try (Table table = TEST_UTIL.getConnection().getTable(tableName)) {
408      assertArrayEquals(value, table.get(new Get(row)).getValue(family, qual));
409    }
410  }
411
412  @Test
413  public void testModifyTableStoreFileTracker() throws IOException {
414    TableName tableName = TableName.valueOf(methodName);
415    byte[] family = Bytes.toBytes("info");
416    byte[] qual = Bytes.toBytes("q");
417    byte[] row = Bytes.toBytes(0);
418    byte[] value = Bytes.toBytes(1);
419    try (Table table = TEST_UTIL.createTable(tableName, family)) {
420      table.put(new Put(row).addColumn(family, qual, value));
421    }
422    // change to FILE
423    ADMIN.modifyTableStoreFileTracker(tableName, StoreFileTrackerFactory.Trackers.FILE.name());
424    verifyModifyTableResult(tableName, family, qual, row, value,
425      StoreFileTrackerFactory.Trackers.FILE.name());
426
427    // change to FILE again, should have no effect
428    ADMIN.modifyTableStoreFileTracker(tableName, StoreFileTrackerFactory.Trackers.FILE.name());
429    verifyModifyTableResult(tableName, family, qual, row, value,
430      StoreFileTrackerFactory.Trackers.FILE.name());
431
432    // change to MIGRATION, and then to FILE
433    ADMIN.modifyTable(TableDescriptorBuilder.newBuilder(ADMIN.getDescriptor(tableName))
434      .setValue(StoreFileTrackerFactory.TRACKER_IMPL,
435        StoreFileTrackerFactory.Trackers.MIGRATION.name())
436      .setValue(SRC_IMPL, StoreFileTrackerFactory.Trackers.FILE.name())
437      .setValue(DST_IMPL, StoreFileTrackerFactory.Trackers.DEFAULT.name()).build());
438    ADMIN.modifyTableStoreFileTracker(tableName, StoreFileTrackerFactory.Trackers.FILE.name());
439    verifyModifyTableResult(tableName, family, qual, row, value,
440      StoreFileTrackerFactory.Trackers.FILE.name());
441
442    // change to MIGRATION, and then to DEFAULT
443    ADMIN.modifyTable(TableDescriptorBuilder.newBuilder(ADMIN.getDescriptor(tableName))
444      .setValue(StoreFileTrackerFactory.TRACKER_IMPL,
445        StoreFileTrackerFactory.Trackers.MIGRATION.name())
446      .setValue(SRC_IMPL, StoreFileTrackerFactory.Trackers.FILE.name())
447      .setValue(DST_IMPL, StoreFileTrackerFactory.Trackers.DEFAULT.name()).build());
448    ADMIN.modifyTableStoreFileTracker(tableName, StoreFileTrackerFactory.Trackers.DEFAULT.name());
449    verifyModifyTableResult(tableName, family, qual, row, value,
450      StoreFileTrackerFactory.Trackers.DEFAULT.name());
451  }
452
453  private void verifyModifyColumnFamilyResult(TableName tableName, byte[] family, byte[] qual,
454    byte[] row, byte[] value, String sft) throws IOException {
455    TableDescriptor td = ADMIN.getDescriptor(tableName);
456    ColumnFamilyDescriptor cfd = td.getColumnFamily(family);
457    assertEquals(sft, cfd.getConfigurationValue(StoreFileTrackerFactory.TRACKER_IMPL));
458    // no migration related configs
459    assertNull(cfd.getConfigurationValue(SRC_IMPL));
460    assertNull(cfd.getConfigurationValue(DST_IMPL));
461    assertNull(cfd.getValue(SRC_IMPL));
462    assertNull(cfd.getValue(DST_IMPL));
463    try (Table table = TEST_UTIL.getConnection().getTable(tableName)) {
464      assertArrayEquals(value, table.get(new Get(row)).getValue(family, qual));
465    }
466  }
467
468  @Test
469  public void testModifyColumnFamilyStoreFileTracker() throws IOException {
470    TableName tableName = TableName.valueOf(methodName);
471    byte[] family = Bytes.toBytes("info");
472    byte[] qual = Bytes.toBytes("q");
473    byte[] row = Bytes.toBytes(0);
474    byte[] value = Bytes.toBytes(1);
475    try (Table table = TEST_UTIL.createTable(tableName, family)) {
476      table.put(new Put(row).addColumn(family, qual, value));
477    }
478    // change to FILE
479    ADMIN.modifyColumnFamilyStoreFileTracker(tableName, family,
480      StoreFileTrackerFactory.Trackers.FILE.name());
481    verifyModifyColumnFamilyResult(tableName, family, qual, row, value,
482      StoreFileTrackerFactory.Trackers.FILE.name());
483
484    // change to FILE again, should have no effect
485    ADMIN.modifyColumnFamilyStoreFileTracker(tableName, family,
486      StoreFileTrackerFactory.Trackers.FILE.name());
487    verifyModifyColumnFamilyResult(tableName, family, qual, row, value,
488      StoreFileTrackerFactory.Trackers.FILE.name());
489
490    // change to MIGRATION, and then to FILE
491    TableDescriptor current = ADMIN.getDescriptor(tableName);
492    ADMIN.modifyTable(TableDescriptorBuilder.newBuilder(current)
493      .modifyColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(current.getColumnFamily(family))
494        .setConfiguration(StoreFileTrackerFactory.TRACKER_IMPL,
495          StoreFileTrackerFactory.Trackers.MIGRATION.name())
496        .setConfiguration(SRC_IMPL, StoreFileTrackerFactory.Trackers.FILE.name())
497        .setConfiguration(DST_IMPL, StoreFileTrackerFactory.Trackers.DEFAULT.name()).build())
498      .build());
499    ADMIN.modifyColumnFamilyStoreFileTracker(tableName, family,
500      StoreFileTrackerFactory.Trackers.FILE.name());
501    verifyModifyColumnFamilyResult(tableName, family, qual, row, value,
502      StoreFileTrackerFactory.Trackers.FILE.name());
503
504    // change to MIGRATION, and then to DEFAULT
505    current = ADMIN.getDescriptor(tableName);
506    ADMIN.modifyTable(TableDescriptorBuilder.newBuilder(current)
507      .modifyColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(current.getColumnFamily(family))
508        .setConfiguration(StoreFileTrackerFactory.TRACKER_IMPL,
509          StoreFileTrackerFactory.Trackers.MIGRATION.name())
510        .setConfiguration(SRC_IMPL, StoreFileTrackerFactory.Trackers.FILE.name())
511        .setConfiguration(DST_IMPL, StoreFileTrackerFactory.Trackers.DEFAULT.name()).build())
512      .build());
513    ADMIN.modifyColumnFamilyStoreFileTracker(tableName, family,
514      StoreFileTrackerFactory.Trackers.DEFAULT.name());
515    verifyModifyColumnFamilyResult(tableName, family, qual, row, value,
516      StoreFileTrackerFactory.Trackers.DEFAULT.name());
517  }
518
519  @Test
520  public void testModifyStoreFileTrackerError() throws IOException {
521    TableName tableName = TableName.valueOf(methodName);
522    byte[] family = Bytes.toBytes("info");
523    TEST_UTIL.createTable(tableName, family).close();
524
525    // table not exists
526    assertThrows(TableNotFoundException.class,
527      () -> ADMIN.modifyTableStoreFileTracker(TableName.valueOf("whatever"),
528        StoreFileTrackerFactory.Trackers.FILE.name()));
529    // family not exists
530    assertThrows(NoSuchColumnFamilyException.class,
531      () -> ADMIN.modifyColumnFamilyStoreFileTracker(tableName, Bytes.toBytes("not_exists"),
532        StoreFileTrackerFactory.Trackers.FILE.name()));
533    // to migration
534    assertThrows(DoNotRetryIOException.class, () -> ADMIN.modifyTableStoreFileTracker(tableName,
535      StoreFileTrackerFactory.Trackers.MIGRATION.name()));
536    // disabled
537    ADMIN.disableTable(tableName);
538    assertThrows(TableNotEnabledException.class, () -> ADMIN.modifyTableStoreFileTracker(tableName,
539      StoreFileTrackerFactory.Trackers.FILE.name()));
540  }
541}