HBASE-17748 Include HBase snapshots in space quotas
[hbase.git] / hbase-server / src / test / java / org / apache / hadoop / hbase / quotas / TestSuperUserQuotaPermissions.java
blob3a60cbbd1961061db7ac3b95927849151655fbc3
1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to you under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 package org.apache.hadoop.hbase.quotas;
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertNull;
21 import static org.junit.Assert.fail;
23 import java.io.IOException;
24 import java.security.PrivilegedExceptionAction;
25 import java.util.Map;
26 import java.util.concurrent.Callable;
27 import java.util.concurrent.atomic.AtomicLong;
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.hadoop.conf.Configuration;
32 import org.apache.hadoop.hbase.DoNotRetryIOException;
33 import org.apache.hadoop.hbase.HBaseTestingUtility;
34 import org.apache.hadoop.hbase.TableName;
35 import org.apache.hadoop.hbase.Waiter;
36 import org.apache.hadoop.hbase.Waiter.Predicate;
37 import org.apache.hadoop.hbase.client.Admin;
38 import org.apache.hadoop.hbase.client.Connection;
39 import org.apache.hadoop.hbase.client.ConnectionFactory;
40 import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
41 import org.apache.hadoop.hbase.regionserver.HRegionServer;
42 import org.apache.hadoop.hbase.security.access.AccessControlClient;
43 import org.apache.hadoop.hbase.security.access.AccessController;
44 import org.apache.hadoop.hbase.security.access.Permission.Action;
45 import org.apache.hadoop.hbase.testclassification.MediumTests;
46 import org.apache.hadoop.security.UserGroupInformation;
47 import org.junit.AfterClass;
48 import org.junit.Before;
49 import org.junit.BeforeClass;
50 import org.junit.Rule;
51 import org.junit.Test;
52 import org.junit.experimental.categories.Category;
53 import org.junit.rules.TestName;
55 /**
56 * Test class to verify that the HBase superuser can override quotas.
58 @Category(MediumTests.class)
59 public class TestSuperUserQuotaPermissions {
60 private static final Log LOG = LogFactory.getLog(TestSuperUserQuotaPermissions.class);
61 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
62 // Default to the user running the tests
63 private static final String SUPERUSER_NAME = System.getProperty("user.name");
64 private static final UserGroupInformation SUPERUSER_UGI =
65 UserGroupInformation.createUserForTesting(SUPERUSER_NAME, new String[0]);
66 private static final String REGULARUSER_NAME = "quota_regularuser";
67 private static final UserGroupInformation REGULARUSER_UGI =
68 UserGroupInformation.createUserForTesting(REGULARUSER_NAME, new String[0]);
69 private static final AtomicLong COUNTER = new AtomicLong(0);
71 @Rule
72 public TestName testName = new TestName();
73 private SpaceQuotaHelperForTests helper;
75 @BeforeClass
76 public static void setupMiniCluster() throws Exception {
77 Configuration conf = TEST_UTIL.getConfiguration();
78 // Increase the frequency of some of the chores for responsiveness of the test
79 SpaceQuotaHelperForTests.updateConfigForQuotas(conf);
81 conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, AccessController.class.getName());
82 conf.set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, AccessController.class.getName());
83 conf.set(CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY, AccessController.class.getName());
84 conf.setBoolean("hbase.security.exec.permission.checks", true);
85 conf.setBoolean("hbase.security.authorization", true);
86 conf.set("hbase.superuser", SUPERUSER_NAME);
88 TEST_UTIL.startMiniCluster(1);
91 @AfterClass
92 public static void tearDown() throws Exception {
93 TEST_UTIL.shutdownMiniCluster();
96 @Before
97 public void removeAllQuotas() throws Exception {
98 final Connection conn = TEST_UTIL.getConnection();
99 if (helper == null) {
100 helper = new SpaceQuotaHelperForTests(TEST_UTIL, testName, COUNTER);
102 // Wait for the quota table to be created
103 if (!conn.getAdmin().tableExists(QuotaUtil.QUOTA_TABLE_NAME)) {
104 helper.waitForQuotaTable(conn);
105 } else {
106 // Or, clean up any quotas from previous test runs.
107 helper.removeAllQuotas(conn);
108 assertEquals(0, helper.listNumDefinedQuotas(conn));
112 @Test
113 public void testSuperUserCanStillCompact() throws Exception {
114 // Create a table and write enough data to push it into quota violation
115 final TableName tn = doAsSuperUser(new Callable<TableName>() {
116 @Override
117 public TableName call() throws Exception {
118 try (Connection conn = getConnection()) {
119 Admin admin = conn.getAdmin();
120 final TableName tn = helper.createTableWithRegions(admin, 5);
121 final long sizeLimit = 2L * SpaceQuotaHelperForTests.ONE_MEGABYTE;
122 QuotaSettings settings = QuotaSettingsFactory.limitTableSpace(
123 tn, sizeLimit, SpaceViolationPolicy.NO_WRITES_COMPACTIONS);
124 admin.setQuota(settings);
125 // Grant the normal user permissions
126 try {
127 AccessControlClient.grant(
128 conn, tn, REGULARUSER_NAME, null, null, Action.READ, Action.WRITE);
129 } catch (Throwable t) {
130 if (t instanceof Exception) {
131 throw (Exception) t;
133 throw new Exception(t);
135 return tn;
140 // Write a bunch of data as our end-user
141 doAsRegularUser(new Callable<Void>() {
142 @Override
143 public Void call() throws Exception {
144 try (Connection conn = getConnection()) {
145 helper.writeData(tn, 3L * SpaceQuotaHelperForTests.ONE_MEGABYTE);
146 return null;
151 waitForTableToEnterQuotaViolation(tn);
153 // Should throw an exception, unprivileged users cannot compact due to the quota
154 try {
155 doAsRegularUser(new Callable<Void>() {
156 @Override
157 public Void call() throws Exception {
158 try (Connection conn = getConnection()) {
159 conn.getAdmin().majorCompact(tn);
160 return null;
164 fail("Expected an exception trying to compact a table with a quota violation");
165 } catch (DoNotRetryIOException e) {
166 // Expected
169 // Should not throw an exception (superuser can do anything)
170 doAsSuperUser(new Callable<Void>() {
171 @Override
172 public Void call() throws Exception {
173 try (Connection conn = getConnection()) {
174 conn.getAdmin().majorCompact(tn);
175 return null;
181 @Test
182 public void testSuperuserCanRemoveQuota() throws Exception {
183 // Create a table and write enough data to push it into quota violation
184 final TableName tn = doAsSuperUser(new Callable<TableName>() {
185 @Override
186 public TableName call() throws Exception {
187 try (Connection conn = getConnection()) {
188 final Admin admin = conn.getAdmin();
189 final TableName tn = helper.createTableWithRegions(admin, 5);
190 final long sizeLimit = 2L * SpaceQuotaHelperForTests.ONE_MEGABYTE;
191 QuotaSettings settings = QuotaSettingsFactory.limitTableSpace(
192 tn, sizeLimit, SpaceViolationPolicy.NO_WRITES_COMPACTIONS);
193 admin.setQuota(settings);
194 // Grant the normal user permission to create a table and set a quota
195 try {
196 AccessControlClient.grant(
197 conn, tn, REGULARUSER_NAME, null, null, Action.READ, Action.WRITE);
198 } catch (Throwable t) {
199 if (t instanceof Exception) {
200 throw (Exception) t;
202 throw new Exception(t);
204 return tn;
209 // Write a bunch of data as our end-user
210 doAsRegularUser(new Callable<Void>() {
211 @Override
212 public Void call() throws Exception {
213 try (Connection conn = getConnection()) {
214 helper.writeData(tn, 3L * SpaceQuotaHelperForTests.ONE_MEGABYTE);
215 return null;
220 // Wait for the table to hit quota violation
221 waitForTableToEnterQuotaViolation(tn);
223 // Try to be "bad" and remove the quota as the end user (we want to write more data!)
224 doAsRegularUser(new Callable<Void>() {
225 @Override
226 public Void call() throws Exception {
227 try (Connection conn = getConnection()) {
228 final Admin admin = conn.getAdmin();
229 QuotaSettings qs = QuotaSettingsFactory.removeTableSpaceLimit(tn);
230 try {
231 admin.setQuota(qs);
232 fail("Expected that an unprivileged user should not be allowed to remove a quota");
233 } catch (Exception e) {
234 // pass
236 return null;
241 // Verify that the superuser can remove the quota
242 doAsSuperUser(new Callable<Void>() {
243 @Override
244 public Void call() throws Exception {
245 try (Connection conn = getConnection()) {
246 final Admin admin = conn.getAdmin();
247 QuotaSettings qs = QuotaSettingsFactory.removeTableSpaceLimit(tn);
248 admin.setQuota(qs);
249 assertNull(helper.getTableSpaceQuota(conn, tn));
250 return null;
256 private Connection getConnection() throws IOException {
257 return ConnectionFactory.createConnection(TEST_UTIL.getConfiguration());
260 private <T> T doAsSuperUser(Callable<T> task) throws Exception {
261 return doAsUser(SUPERUSER_UGI, task);
264 private <T> T doAsRegularUser(Callable<T> task) throws Exception {
265 return doAsUser(REGULARUSER_UGI, task);
268 private <T> T doAsUser(UserGroupInformation ugi, Callable<T> task) throws Exception {
269 return ugi.doAs(new PrivilegedExceptionAction<T>() {
270 public T run() throws Exception {
271 return task.call();
276 private void waitForTableToEnterQuotaViolation(TableName tn) throws Exception {
277 // Verify that the RegionServer has the quota in violation
278 final HRegionServer rs = TEST_UTIL.getHBaseCluster().getRegionServer(0);
279 Waiter.waitFor(TEST_UTIL.getConfiguration(), 30 * 1000, 1000, new Predicate<Exception>() {
280 @Override
281 public boolean evaluate() throws Exception {
282 Map<TableName,SpaceQuotaSnapshot> snapshots =
283 rs.getRegionServerSpaceQuotaManager().copyQuotaSnapshots();
284 SpaceQuotaSnapshot snapshot = snapshots.get(tn);
285 if (snapshot == null) {
286 LOG.info("Found no snapshot for " + tn);
287 return false;
289 LOG.info("Found snapshot " + snapshot);
290 return snapshot.getQuotaStatus().isInViolation();