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;
019
020import static org.junit.jupiter.api.Assertions.assertEquals;
021import static org.junit.jupiter.api.Assertions.assertNotNull;
022import static org.junit.jupiter.api.Assertions.assertTrue;
023import static org.junit.jupiter.api.Assertions.fail;
024
025import java.io.IOException;
026import java.util.List;
027import java.util.Set;
028import java.util.concurrent.Callable;
029import java.util.regex.Pattern;
030import org.apache.hadoop.fs.FileSystem;
031import org.apache.hadoop.fs.Path;
032import org.apache.hadoop.hbase.client.Admin;
033import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
034import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
035import org.apache.hadoop.hbase.client.Get;
036import org.apache.hadoop.hbase.client.Put;
037import org.apache.hadoop.hbase.client.Table;
038import org.apache.hadoop.hbase.client.TableDescriptor;
039import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
040import org.apache.hadoop.hbase.master.HMaster;
041import org.apache.hadoop.hbase.testclassification.MediumTests;
042import org.apache.hadoop.hbase.testclassification.MiscTests;
043import org.apache.hadoop.hbase.util.Bytes;
044import org.junit.jupiter.api.AfterAll;
045import org.junit.jupiter.api.BeforeAll;
046import org.junit.jupiter.api.BeforeEach;
047import org.junit.jupiter.api.Tag;
048import org.junit.jupiter.api.Test;
049import org.junit.jupiter.api.TestInfo;
050import org.slf4j.Logger;
051import org.slf4j.LoggerFactory;
052
053import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
054
055@Tag(MiscTests.TAG)
056@Tag(MediumTests.TAG)
057public class TestNamespace {
058
059  private static final Logger LOG = LoggerFactory.getLogger(TestNamespace.class);
060  private static HMaster master;
061  protected final static int NUM_SLAVES_BASE = 4;
062  private static HBaseTestingUtil TEST_UTIL;
063  protected static Admin admin;
064  protected static HBaseClusterInterface cluster;
065  private String prefix = "TestNamespace";
066
067  @BeforeAll
068  public static void setUp() throws Exception {
069    TEST_UTIL = new HBaseTestingUtil();
070    TEST_UTIL.startMiniCluster(NUM_SLAVES_BASE);
071    admin = TEST_UTIL.getAdmin();
072    cluster = TEST_UTIL.getHBaseCluster();
073    master = ((SingleProcessHBaseCluster) cluster).getMaster();
074    LOG.info("Done initializing cluster");
075  }
076
077  @AfterAll
078  public static void tearDown() throws Exception {
079    TEST_UTIL.shutdownMiniCluster();
080  }
081
082  @BeforeEach
083  public void beforeMethod() throws IOException {
084    for (TableDescriptor desc : admin.listTableDescriptors(Pattern.compile(prefix + ".*"))) {
085      admin.disableTable(desc.getTableName());
086      admin.deleteTable(desc.getTableName());
087    }
088    for (NamespaceDescriptor ns : admin.listNamespaceDescriptors()) {
089      if (ns.getName().startsWith(prefix)) {
090        admin.deleteNamespace(ns.getName());
091      }
092    }
093  }
094
095  @Test
096  public void verifyReservedNS() throws IOException {
097    // verify existence of reserved namespaces
098    NamespaceDescriptor ns =
099      admin.getNamespaceDescriptor(NamespaceDescriptor.DEFAULT_NAMESPACE.getName());
100    assertNotNull(ns);
101    assertEquals(ns.getName(), NamespaceDescriptor.DEFAULT_NAMESPACE.getName());
102
103    ns = admin.getNamespaceDescriptor(NamespaceDescriptor.SYSTEM_NAMESPACE.getName());
104    assertNotNull(ns);
105    assertEquals(ns.getName(), NamespaceDescriptor.SYSTEM_NAMESPACE.getName());
106
107    assertEquals(2, admin.listNamespaces().length);
108    assertEquals(2, admin.listNamespaceDescriptors().length);
109
110    // verify existence of system tables
111    Set<TableName> systemTables = Sets.newHashSet(TableName.META_TABLE_NAME);
112    List<TableDescriptor> descs = admin.listTableDescriptorsByNamespace(
113      Bytes.toBytes(NamespaceDescriptor.SYSTEM_NAMESPACE.getName()));
114    assertEquals(systemTables.size(), descs.size());
115    for (TableDescriptor desc : descs) {
116      assertTrue(systemTables.contains(desc.getTableName()));
117    }
118    // verify system tables aren't listed
119    assertEquals(0, admin.listTableDescriptors().size());
120
121    // Try creating default and system namespaces.
122    boolean exceptionCaught = false;
123    try {
124      admin.createNamespace(NamespaceDescriptor.DEFAULT_NAMESPACE);
125    } catch (IOException exp) {
126      LOG.warn(exp.toString(), exp);
127      exceptionCaught = true;
128    } finally {
129      assertTrue(exceptionCaught);
130    }
131
132    exceptionCaught = false;
133    try {
134      admin.createNamespace(NamespaceDescriptor.SYSTEM_NAMESPACE);
135    } catch (IOException exp) {
136      LOG.warn(exp.toString(), exp);
137      exceptionCaught = true;
138    } finally {
139      assertTrue(exceptionCaught);
140    }
141  }
142
143  @Test
144  public void testDeleteReservedNS() throws Exception {
145    boolean exceptionCaught = false;
146    try {
147      admin.deleteNamespace(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR);
148    } catch (IOException exp) {
149      LOG.warn(exp.toString(), exp);
150      exceptionCaught = true;
151    } finally {
152      assertTrue(exceptionCaught);
153    }
154
155    try {
156      admin.deleteNamespace(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR);
157    } catch (IOException exp) {
158      LOG.warn(exp.toString(), exp);
159      exceptionCaught = true;
160    } finally {
161      assertTrue(exceptionCaught);
162    }
163  }
164
165  @Test
166  public void createRemoveTest(TestInfo testInfo) throws Exception {
167    String nsName = prefix + "_" + testInfo.getTestMethod().get().getName();
168    LOG.info(testInfo.getTestMethod().get().getName());
169
170    // create namespace and verify
171    admin.createNamespace(NamespaceDescriptor.create(nsName).build());
172    assertEquals(3, admin.listNamespaces().length);
173    assertEquals(3, admin.listNamespaceDescriptors().length);
174    // remove namespace and verify
175    admin.deleteNamespace(nsName);
176    assertEquals(2, admin.listNamespaces().length);
177    assertEquals(2, admin.listNamespaceDescriptors().length);
178  }
179
180  @Test
181  public void createDoubleTest(TestInfo testInfo) throws IOException, InterruptedException {
182    String nsName = prefix + "_" + testInfo.getTestMethod().get().getName();
183    LOG.info(testInfo.getTestMethod().get().getName());
184
185    final TableName tableName = TableName.valueOf(testInfo.getTestMethod().get().getName());
186    final TableName tableNameFoo =
187      TableName.valueOf(nsName + ":" + testInfo.getTestMethod().get().getName());
188    // create namespace and verify
189    admin.createNamespace(NamespaceDescriptor.create(nsName).build());
190    TEST_UTIL.createTable(tableName, Bytes.toBytes(nsName));
191    TEST_UTIL.createTable(tableNameFoo, Bytes.toBytes(nsName));
192    assertEquals(2, admin.listTableDescriptors().size());
193    assertNotNull(admin.getDescriptor(tableName));
194    assertNotNull(admin.getDescriptor(tableNameFoo));
195    // remove namespace and verify
196    admin.disableTable(tableName);
197    admin.deleteTable(tableName);
198    assertEquals(1, admin.listTableDescriptors().size());
199  }
200
201  @Test
202  public void createTableTest(TestInfo testInfo) throws IOException, InterruptedException {
203    String nsName = prefix + "_" + testInfo.getTestMethod().get().getName();
204    LOG.info(testInfo.getTestMethod().get().getName());
205
206    TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder
207      .newBuilder(TableName.valueOf(nsName + ":" + testInfo.getTestMethod().get().getName()));
208    ColumnFamilyDescriptor columnFamilyDescriptor =
209      ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("my_cf")).build();
210    tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptor);
211    TableDescriptor tableDescriptor = tableDescriptorBuilder.build();
212    try {
213      admin.createTable(tableDescriptor);
214      fail("Expected no namespace exists exception");
215    } catch (NamespaceNotFoundException ex) {
216    }
217    // create table and in new namespace
218    admin.createNamespace(NamespaceDescriptor.create(nsName).build());
219    admin.createTable(tableDescriptor);
220    TEST_UTIL.waitTableAvailable(tableDescriptor.getTableName().getName(), 10000);
221    FileSystem fs = FileSystem.get(TEST_UTIL.getConfiguration());
222    assertTrue(fs.exists(
223      new Path(master.getMasterFileSystem().getRootDir(), new Path(HConstants.BASE_NAMESPACE_DIR,
224        new Path(nsName, tableDescriptor.getTableName().getQualifierAsString())))));
225    assertEquals(1, admin.listTableDescriptors().size());
226
227    // verify non-empty namespace can't be removed
228    try {
229      admin.deleteNamespace(nsName);
230      fail("Expected non-empty namespace constraint exception");
231    } catch (Exception ex) {
232      LOG.info("Caught expected exception: " + ex);
233    }
234
235    // sanity check try to write and read from table
236    Table table = TEST_UTIL.getConnection().getTable(tableDescriptor.getTableName());
237    Put p = new Put(Bytes.toBytes("row1"));
238    p.addColumn(Bytes.toBytes("my_cf"), Bytes.toBytes("my_col"), Bytes.toBytes("value1"));
239    table.put(p);
240    // flush and read from disk to make sure directory changes are working
241    admin.flush(tableDescriptor.getTableName());
242    Get g = new Get(Bytes.toBytes("row1"));
243    assertTrue(table.exists(g));
244
245    // normal case of removing namespace
246    TEST_UTIL.deleteTable(tableDescriptor.getTableName());
247    admin.deleteNamespace(nsName);
248  }
249
250  @Test
251  public void createTableInDefaultNamespace(TestInfo testInfo) throws Exception {
252    TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder
253      .newBuilder(TableName.valueOf(testInfo.getTestMethod().get().getName()));
254    ColumnFamilyDescriptor columnFamilyDescriptor =
255      ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("cf1")).build();
256    tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptor);
257    TableDescriptor tableDescriptor = tableDescriptorBuilder.build();
258    admin.createTable(tableDescriptor);
259    assertTrue(admin.listTableDescriptors().size() == 1);
260    admin.disableTable(tableDescriptor.getTableName());
261    admin.deleteTable(tableDescriptor.getTableName());
262  }
263
264  @Test
265  public void createTableInSystemNamespace(TestInfo testInfo) throws Exception {
266    final TableName tableName =
267      TableName.valueOf("hbase:" + testInfo.getTestMethod().get().getName());
268    TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder.newBuilder(tableName);
269    ColumnFamilyDescriptor columnFamilyDescriptor =
270      ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("cf1")).build();
271    tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptor);
272    TableDescriptor tableDescriptor = tableDescriptorBuilder.build();
273    admin.createTable(tableDescriptor);
274    assertEquals(0, admin.listTableDescriptors().size());
275    assertTrue(admin.tableExists(tableName));
276    admin.disableTable(tableDescriptor.getTableName());
277    admin.deleteTable(tableDescriptor.getTableName());
278  }
279
280  @Test
281  public void testNamespaceOperations(TestInfo testInfo) throws IOException {
282    admin.createNamespace(NamespaceDescriptor.create(prefix + "ns1").build());
283    admin.createNamespace(NamespaceDescriptor.create(prefix + "ns2").build());
284
285    // create namespace that already exists
286    runWithExpectedException(new Callable<Void>() {
287      @Override
288      public Void call() throws Exception {
289        admin.createNamespace(NamespaceDescriptor.create(prefix + "ns1").build());
290        return null;
291      }
292    }, NamespaceExistException.class);
293
294    // create a table in non-existing namespace
295    runWithExpectedException(new Callable<Void>() {
296      @Override
297      public Void call() throws Exception {
298        TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder.newBuilder(
299          TableName.valueOf("non_existing_namespace", testInfo.getTestMethod().get().getName()));
300        ColumnFamilyDescriptor columnFamilyDescriptor =
301          ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("family1")).build();
302        tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptor);
303        admin.createTable(tableDescriptorBuilder.build());
304        return null;
305      }
306    }, NamespaceNotFoundException.class);
307
308    // get descriptor for existing namespace
309    admin.getNamespaceDescriptor(prefix + "ns1");
310
311    // get descriptor for non-existing namespace
312    runWithExpectedException(new Callable<NamespaceDescriptor>() {
313      @Override
314      public NamespaceDescriptor call() throws Exception {
315        return admin.getNamespaceDescriptor("non_existing_namespace");
316      }
317    }, NamespaceNotFoundException.class);
318
319    // delete descriptor for existing namespace
320    admin.deleteNamespace(prefix + "ns2");
321
322    // delete descriptor for non-existing namespace
323    runWithExpectedException(new Callable<Void>() {
324      @Override
325      public Void call() throws Exception {
326        admin.deleteNamespace("non_existing_namespace");
327        return null;
328      }
329    }, NamespaceNotFoundException.class);
330
331    // modify namespace descriptor for existing namespace
332    NamespaceDescriptor ns1 = admin.getNamespaceDescriptor(prefix + "ns1");
333    ns1.setConfiguration("foo", "bar");
334    admin.modifyNamespace(ns1);
335
336    // modify namespace descriptor for non-existing namespace
337    runWithExpectedException(new Callable<Void>() {
338      @Override
339      public Void call() throws Exception {
340        admin.modifyNamespace(NamespaceDescriptor.create("non_existing_namespace").build());
341        return null;
342      }
343    }, NamespaceNotFoundException.class);
344
345    // get table descriptors for existing namespace
346    TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder
347      .newBuilder(TableName.valueOf(prefix + "ns1", testInfo.getTestMethod().get().getName()));
348    ColumnFamilyDescriptor columnFamilyDescriptor =
349      ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("family1")).build();
350    tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptor);
351    admin.createTable(tableDescriptorBuilder.build());
352    List<TableDescriptor> htds =
353      admin.listTableDescriptorsByNamespace(Bytes.toBytes(prefix + "ns1"));
354    assertNotNull(htds, "Should have not returned null");
355    assertEquals(1, htds.size(), "Should have returned non-empty array");
356
357    // get table descriptors for non-existing namespace
358    runWithExpectedException(new Callable<Void>() {
359      @Override
360      public Void call() throws Exception {
361        admin.listTableDescriptorsByNamespace(Bytes.toBytes("non_existant_namespace"));
362        return null;
363      }
364    }, NamespaceNotFoundException.class);
365
366    // get table names for existing namespace
367    TableName[] tableNames = admin.listTableNamesByNamespace(prefix + "ns1");
368    assertNotNull(tableNames, "Should have not returned null");
369    assertEquals(1, tableNames.length, "Should have returned non-empty array");
370
371    // get table names for non-existing namespace
372    runWithExpectedException(new Callable<Void>() {
373      @Override
374      public Void call() throws Exception {
375        admin.listTableNamesByNamespace("non_existing_namespace");
376        return null;
377      }
378    }, NamespaceNotFoundException.class);
379
380  }
381
382  private static <V, E> void runWithExpectedException(Callable<V> callable,
383    Class<E> exceptionClass) {
384    try {
385      callable.call();
386    } catch (Exception ex) {
387      assertEquals(exceptionClass, ex.getClass());
388      return;
389    }
390    fail("Should have thrown exception " + exceptionClass);
391  }
392}