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.security.access;
019
020import static org.apache.hadoop.hbase.HConstants.HBASE_CLIENT_RETRIES_NUMBER;
021import static org.junit.jupiter.api.Assertions.assertThrows;
022import static org.junit.jupiter.api.Assertions.assertTrue;
023
024import java.io.IOException;
025import java.util.concurrent.TimeUnit;
026import org.apache.hadoop.conf.Configuration;
027import org.apache.hadoop.hbase.HBaseTestingUtil;
028import org.apache.hadoop.hbase.HConstants;
029import org.apache.hadoop.hbase.SingleProcessHBaseCluster;
030import org.apache.hadoop.hbase.TableName;
031import org.apache.hadoop.hbase.client.Admin;
032import org.apache.hadoop.hbase.client.Connection;
033import org.apache.hadoop.hbase.client.ConnectionFactory;
034import org.apache.hadoop.hbase.client.Put;
035import org.apache.hadoop.hbase.client.Table;
036import org.apache.hadoop.hbase.master.HMaster;
037import org.apache.hadoop.hbase.regionserver.HRegionServer;
038import org.apache.hadoop.hbase.testclassification.LargeTests;
039import org.apache.hadoop.hbase.testclassification.SecurityTests;
040import org.apache.hadoop.hbase.util.Bytes;
041import org.junit.jupiter.api.AfterEach;
042import org.junit.jupiter.api.BeforeEach;
043import org.junit.jupiter.api.Tag;
044import org.junit.jupiter.api.Test;
045import org.junit.jupiter.api.Timeout;
046
047@Tag(SecurityTests.TAG)
048@Tag(LargeTests.TAG)
049@SuppressWarnings("deprecation")
050public class TestReadOnlyControllerFlush {
051
052  private final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
053  private static final TableName TEST_TABLE = TableName.valueOf("read_only_flush_test_table");
054  private static final byte[] TEST_FAMILY = Bytes.toBytes("read_only_flush_col_fam");
055  private static HRegionServer hRegionServer;
056  private static HMaster hMaster;
057  private static Configuration conf;
058  private static Connection connection;
059  private static SingleProcessHBaseCluster cluster;
060
061  private static Table testTable;
062
063  @BeforeEach
064  public void beforeClass() throws Exception {
065    conf = TEST_UTIL.getConfiguration();
066
067    // Shorten the run time of failed unit tests by limiting retries and the session timeout
068    // threshold
069    conf.setInt(HBASE_CLIENT_RETRIES_NUMBER, 1);
070    conf.setInt(HConstants.ZK_SESSION_TIMEOUT, 1000);
071
072    // Set up test class with Read-Only mode disabled so a table can be created
073    conf.setBoolean(HConstants.HBASE_GLOBAL_READONLY_ENABLED_KEY, false);
074
075    try {
076      // Start the test cluster
077      cluster = TEST_UTIL.startMiniCluster(1);
078
079      hMaster = cluster.getMaster();
080      hRegionServer = cluster.getRegionServerThreads().get(0).getRegionServer();
081      connection = ConnectionFactory.createConnection(conf);
082
083      // Create a test table and insert a row so the memstore has data to flush
084      testTable = TEST_UTIL.createTable(TEST_TABLE, TEST_FAMILY);
085      Put put = new Put(Bytes.toBytes("row1"));
086      put.addColumn(TEST_FAMILY, null, Bytes.toBytes("value1"));
087      testTable.put(put);
088    } catch (Exception e) {
089      disableReadOnlyMode();
090      TEST_UTIL.deleteTable(TEST_TABLE);
091      if (connection != null) {
092        connection.close();
093      }
094      TEST_UTIL.shutdownMiniCluster();
095      throw new RuntimeException(e);
096    }
097  }
098
099  @AfterEach
100  public void afterClass() throws Exception {
101    if (connection != null) {
102      connection.close();
103    }
104    TEST_UTIL.shutdownMiniCluster();
105  }
106
107  private static void enableReadOnlyMode() {
108    SecureTestUtil.enableReadOnlyMode(conf, hMaster, hRegionServer);
109  }
110
111  private static void disableReadOnlyMode() {
112    SecureTestUtil.disableReadOnlyMode(conf, hMaster, hRegionServer);
113  }
114
115  @Test
116  public void testFlushTableWithReadOnlyDisabled() throws IOException {
117    disableReadOnlyMode();
118    try (Admin admin = TEST_UTIL.getAdmin()) {
119      admin.flush(TEST_TABLE);
120    }
121  }
122
123  @Test
124  @Timeout(value = 60, unit = TimeUnit.SECONDS)
125  public void testCannotFlushTableWithReadOnlyEnabled() throws IOException {
126    enableReadOnlyMode();
127    try (Admin admin = TEST_UTIL.getAdmin()) {
128      IOException exception = assertThrows(IOException.class, () -> {
129        admin.flush(TEST_TABLE);
130      });
131      assertTrue(exception.getMessage().contains("Operation not allowed in Read-Only Mode"));
132    }
133  }
134}