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.assertFalse;
022import static org.junit.jupiter.api.Assertions.assertTrue;
023import static org.junit.jupiter.api.Assertions.fail;
024
025import java.io.IOException;
026import java.util.Collections;
027import org.apache.hadoop.hbase.client.Admin;
028import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
029import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
030import org.apache.hadoop.hbase.client.RegionInfoBuilder;
031import org.apache.hadoop.hbase.client.TableDescriptor;
032import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
033import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
034import org.apache.hadoop.hbase.regionserver.Region;
035import org.apache.hadoop.hbase.testclassification.MediumTests;
036import org.apache.hadoop.hbase.testclassification.MiscTests;
037import org.apache.hadoop.hbase.util.Bytes;
038import org.junit.jupiter.api.AfterEach;
039import org.junit.jupiter.api.BeforeEach;
040import org.junit.jupiter.api.Tag;
041import org.junit.jupiter.api.Test;
042
043/**
044 * Test being able to edit hbase:meta.
045 */
046@Tag(MiscTests.TAG)
047@Tag(MediumTests.TAG)
048public class TestHBaseMetaEdit {
049
050  private final static HBaseTestingUtil UTIL = new HBaseTestingUtil();
051
052  @BeforeEach
053  public void before() throws Exception {
054    UTIL.startMiniCluster();
055  }
056
057  @AfterEach
058  public void after() throws Exception {
059    UTIL.shutdownMiniCluster();
060  }
061
062  // make sure that with every possible way, we get the same meta table descriptor.
063  private TableDescriptor getMetaDescriptor() throws TableNotFoundException, IOException {
064    Admin admin = UTIL.getAdmin();
065    TableDescriptor get = admin.getDescriptor(TableName.META_TABLE_NAME);
066    TableDescriptor list =
067      admin.listTableDescriptors(true).stream().filter(td -> td.isMetaTable()).findAny().get();
068    TableDescriptor listByName =
069      admin.listTableDescriptors(Collections.singletonList(TableName.META_TABLE_NAME)).get(0);
070    TableDescriptor listByNs =
071      admin.listTableDescriptorsByNamespace(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME).stream()
072        .filter(td -> td.isMetaTable()).findAny().get();
073    assertEquals(get, list);
074    assertEquals(get, listByName);
075    assertEquals(get, listByNs);
076    return get;
077  }
078
079  /**
080   * Set versions, set HBASE-16213 indexed block encoding, and add a column family. Delete the
081   * column family. Then try to delete a core hbase:meta family (should fail). Verify they are all
082   * in place by looking at TableDescriptor AND by checking what the RegionServer sees after opening
083   * Region.
084   */
085  @Test
086  public void testEditMeta() throws IOException {
087    Admin admin = UTIL.getAdmin();
088    admin.tableExists(TableName.META_TABLE_NAME);
089    TableDescriptor originalDescriptor = getMetaDescriptor();
090    ColumnFamilyDescriptor cfd = originalDescriptor.getColumnFamily(HConstants.CATALOG_FAMILY);
091    int oldVersions = cfd.getMaxVersions();
092    // Add '1' to current versions count. Set encoding too.
093    cfd = ColumnFamilyDescriptorBuilder.newBuilder(cfd).setMaxVersions(oldVersions + 1)
094      .setConfiguration(ColumnFamilyDescriptorBuilder.DATA_BLOCK_ENCODING,
095        DataBlockEncoding.ROW_INDEX_V1.toString())
096      .build();
097    admin.modifyColumnFamily(TableName.META_TABLE_NAME, cfd);
098    byte[] extraColumnFamilyName = Bytes.toBytes("xtra");
099    ColumnFamilyDescriptor newCfd =
100      ColumnFamilyDescriptorBuilder.newBuilder(extraColumnFamilyName).build();
101    admin.addColumnFamily(TableName.META_TABLE_NAME, newCfd);
102    TableDescriptor descriptor = getMetaDescriptor();
103    // Assert new max versions is == old versions plus 1.
104    assertEquals(oldVersions + 1,
105      descriptor.getColumnFamily(HConstants.CATALOG_FAMILY).getMaxVersions());
106    descriptor = getMetaDescriptor();
107    // Assert new max versions is == old versions plus 1.
108    assertEquals(oldVersions + 1,
109      descriptor.getColumnFamily(HConstants.CATALOG_FAMILY).getMaxVersions());
110    assertTrue(descriptor.getColumnFamily(newCfd.getName()) != null);
111    String encoding = descriptor.getColumnFamily(HConstants.CATALOG_FAMILY).getConfiguration()
112      .get(ColumnFamilyDescriptorBuilder.DATA_BLOCK_ENCODING);
113    assertEquals(encoding, DataBlockEncoding.ROW_INDEX_V1.toString());
114    Region r = UTIL.getHBaseCluster().getRegionServer(0)
115      .getRegion(RegionInfoBuilder.FIRST_META_REGIONINFO.getEncodedName());
116    assertEquals(oldVersions + 1,
117      r.getStore(HConstants.CATALOG_FAMILY).getColumnFamilyDescriptor().getMaxVersions());
118    encoding = r.getStore(HConstants.CATALOG_FAMILY).getColumnFamilyDescriptor()
119      .getConfigurationValue(ColumnFamilyDescriptorBuilder.DATA_BLOCK_ENCODING);
120    assertEquals(encoding, DataBlockEncoding.ROW_INDEX_V1.toString());
121    assertTrue(r.getStore(extraColumnFamilyName) != null);
122    // Assert we can't drop critical hbase:meta column family but we can drop any other.
123    admin.deleteColumnFamily(TableName.META_TABLE_NAME, newCfd.getName());
124    descriptor = getMetaDescriptor();
125    assertTrue(descriptor.getColumnFamily(newCfd.getName()) == null);
126    try {
127      admin.deleteColumnFamily(TableName.META_TABLE_NAME, HConstants.CATALOG_FAMILY);
128      fail("Should not reach here");
129    } catch (HBaseIOException hioe) {
130      assertTrue(hioe.getMessage().contains("Delete of hbase:meta"));
131    }
132  }
133
134  /**
135   * Validate whether meta table can be altered as READ only, shouldn't be allowed otherwise it will
136   * break assignment functionalities. See HBASE-24977.
137   */
138  @Test
139  public void testAlterMetaWithReadOnly() throws IOException {
140    Admin admin = UTIL.getAdmin();
141    TableDescriptor origMetaTableDesc = admin.getDescriptor(TableName.META_TABLE_NAME);
142    assertFalse(origMetaTableDesc.isReadOnly());
143    TableDescriptor newTD =
144      TableDescriptorBuilder.newBuilder(origMetaTableDesc).setReadOnly(true).build();
145    try {
146      admin.modifyTable(newTD);
147      fail("Meta table can't be set as read only");
148    } catch (Exception e) {
149      assertFalse(admin.getDescriptor(TableName.META_TABLE_NAME).isReadOnly());
150    }
151
152    // Create a table to check region assignment & meta operation
153    TableName tableName = TableName.valueOf("tempTable");
154    TableDescriptor td = TableDescriptorBuilder.newBuilder(tableName).setReadOnly(true)
155      .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("f1")).build())
156      .build();
157    UTIL.getAdmin().createTable(td);
158    UTIL.deleteTable(tableName);
159  }
160}