Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to you under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hbase.quotas; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import java.io.IOException; import java.security.PrivilegedExceptionAction; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicLong; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.DoNotRetryIOException; import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.Waiter; import org.apache.hadoop.hbase.Waiter.Predicate; import org.apache.hadoop.hbase.client.Admin; import org.apache.hadoop.hbase.client.Connection; import org.apache.hadoop.hbase.client.ConnectionFactory; import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; import org.apache.hadoop.hbase.regionserver.HRegionServer; import org.apache.hadoop.hbase.security.access.AccessControlClient; import org.apache.hadoop.hbase.security.access.AccessController; import org.apache.hadoop.hbase.security.access.Permission.Action; import org.apache.hadoop.hbase.testclassification.MediumTests; import org.apache.hadoop.security.UserGroupInformation; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.rules.TestName; /** * Test class to verify that the HBase superuser can override quotas. */ @Category(MediumTests.class) public class TestSuperUserQuotaPermissions { private static final Log LOG = LogFactory.getLog(TestSuperUserQuotaPermissions.class); private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); // Default to the user running the tests private static final String SUPERUSER_NAME = System.getProperty("user.name"); private static final UserGroupInformation SUPERUSER_UGI = UserGroupInformation .createUserForTesting(SUPERUSER_NAME, new String[0]); private static final String REGULARUSER_NAME = "quota_regularuser"; private static final UserGroupInformation REGULARUSER_UGI = UserGroupInformation .createUserForTesting(REGULARUSER_NAME, new String[0]); private static final AtomicLong COUNTER = new AtomicLong(0); @Rule public TestName testName = new TestName(); private SpaceQuotaHelperForTests helper; @BeforeClass public static void setupMiniCluster() throws Exception { Configuration conf = TEST_UTIL.getConfiguration(); // Increase the frequency of some of the chores for responsiveness of the test SpaceQuotaHelperForTests.updateConfigForQuotas(conf); conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, AccessController.class.getName()); conf.set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, AccessController.class.getName()); conf.set(CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY, AccessController.class.getName()); conf.setBoolean("hbase.security.exec.permission.checks", true); conf.setBoolean("hbase.security.authorization", true); conf.set("hbase.superuser", SUPERUSER_NAME); TEST_UTIL.startMiniCluster(1); } @AfterClass public static void tearDown() throws Exception { TEST_UTIL.shutdownMiniCluster(); } @Before public void removeAllQuotas() throws Exception { final Connection conn = TEST_UTIL.getConnection(); if (helper == null) { helper = new SpaceQuotaHelperForTests(TEST_UTIL, testName, COUNTER); } // Wait for the quota table to be created if (!conn.getAdmin().tableExists(QuotaUtil.QUOTA_TABLE_NAME)) { helper.waitForQuotaTable(conn); } else { // Or, clean up any quotas from previous test runs. helper.removeAllQuotas(conn); assertEquals(0, helper.listNumDefinedQuotas(conn)); } } @Test public void testSuperUserCanStillCompact() throws Exception { // Create a table and write enough data to push it into quota violation final TableName tn = doAsSuperUser(new Callable<TableName>() { @Override public TableName call() throws Exception { try (Connection conn = getConnection()) { Admin admin = conn.getAdmin(); final TableName tn = helper.createTableWithRegions(admin, 5); final long sizeLimit = 2L * SpaceQuotaHelperForTests.ONE_MEGABYTE; QuotaSettings settings = QuotaSettingsFactory.limitTableSpace(tn, sizeLimit, SpaceViolationPolicy.NO_WRITES_COMPACTIONS); admin.setQuota(settings); // Grant the normal user permissions try { AccessControlClient.grant(conn, tn, REGULARUSER_NAME, null, null, Action.READ, Action.WRITE); } catch (Throwable t) { if (t instanceof Exception) { throw (Exception) t; } throw new Exception(t); } return tn; } } }); // Write a bunch of data as our end-user doAsRegularUser(new Callable<Void>() { @Override public Void call() throws Exception { try (Connection conn = getConnection()) { helper.writeData(tn, 3L * SpaceQuotaHelperForTests.ONE_MEGABYTE); return null; } } }); waitForTableToEnterQuotaViolation(tn); // Should throw an exception, unprivileged users cannot compact due to the quota try { doAsRegularUser(new Callable<Void>() { @Override public Void call() throws Exception { try (Connection conn = getConnection()) { conn.getAdmin().majorCompact(tn); return null; } } }); fail("Expected an exception trying to compact a table with a quota violation"); } catch (DoNotRetryIOException e) { // Expected } // Should not throw an exception (superuser can do anything) doAsSuperUser(new Callable<Void>() { @Override public Void call() throws Exception { try (Connection conn = getConnection()) { conn.getAdmin().majorCompact(tn); return null; } } }); } @Test public void testSuperuserCanRemoveQuota() throws Exception { // Create a table and write enough data to push it into quota violation final TableName tn = doAsSuperUser(new Callable<TableName>() { @Override public TableName call() throws Exception { try (Connection conn = getConnection()) { final Admin admin = conn.getAdmin(); final TableName tn = helper.createTableWithRegions(admin, 5); final long sizeLimit = 2L * SpaceQuotaHelperForTests.ONE_MEGABYTE; QuotaSettings settings = QuotaSettingsFactory.limitTableSpace(tn, sizeLimit, SpaceViolationPolicy.NO_WRITES_COMPACTIONS); admin.setQuota(settings); // Grant the normal user permission to create a table and set a quota try { AccessControlClient.grant(conn, tn, REGULARUSER_NAME, null, null, Action.READ, Action.WRITE); } catch (Throwable t) { if (t instanceof Exception) { throw (Exception) t; } throw new Exception(t); } return tn; } } }); // Write a bunch of data as our end-user doAsRegularUser(new Callable<Void>() { @Override public Void call() throws Exception { try (Connection conn = getConnection()) { helper.writeData(tn, 3L * SpaceQuotaHelperForTests.ONE_MEGABYTE); return null; } } }); // Wait for the table to hit quota violation waitForTableToEnterQuotaViolation(tn); // Try to be "bad" and remove the quota as the end user (we want to write more data!) doAsRegularUser(new Callable<Void>() { @Override public Void call() throws Exception { try (Connection conn = getConnection()) { final Admin admin = conn.getAdmin(); QuotaSettings qs = QuotaSettingsFactory.removeTableSpaceLimit(tn); try { admin.setQuota(qs); fail("Expected that an unprivileged user should not be allowed to remove a quota"); } catch (Exception e) { // pass } return null; } } }); // Verify that the superuser can remove the quota doAsSuperUser(new Callable<Void>() { @Override public Void call() throws Exception { try (Connection conn = getConnection()) { final Admin admin = conn.getAdmin(); QuotaSettings qs = QuotaSettingsFactory.removeTableSpaceLimit(tn); admin.setQuota(qs); assertNull(helper.getTableSpaceQuota(conn, tn)); return null; } } }); } private Connection getConnection() throws IOException { return ConnectionFactory.createConnection(TEST_UTIL.getConfiguration()); } private <T> T doAsSuperUser(Callable<T> task) throws Exception { return doAsUser(SUPERUSER_UGI, task); } private <T> T doAsRegularUser(Callable<T> task) throws Exception { return doAsUser(REGULARUSER_UGI, task); } private <T> T doAsUser(UserGroupInformation ugi, Callable<T> task) throws Exception { return ugi.doAs(new PrivilegedExceptionAction<T>() { public T run() throws Exception { return task.call(); } }); } private void waitForTableToEnterQuotaViolation(TableName tn) throws Exception { // Verify that the RegionServer has the quota in violation final HRegionServer rs = TEST_UTIL.getHBaseCluster().getRegionServer(0); Waiter.waitFor(TEST_UTIL.getConfiguration(), 30 * 1000, 1000, new Predicate<Exception>() { @Override public boolean evaluate() throws Exception { Map<TableName, SpaceQuotaSnapshot> snapshots = rs.getRegionServerSpaceQuotaManager() .copyQuotaSnapshots(); SpaceQuotaSnapshot snapshot = snapshots.get(tn); if (snapshot == null) { LOG.info("Found no snapshot for " + tn); return false; } LOG.info("Found snapshot " + snapshot); return snapshot.getQuotaStatus().isInViolation(); } }); } }