HBASE-15132 Master region merge RPC should authorize user request

This commit is contained in:
tedyu 2016-01-23 07:48:20 -08:00
parent 772f30fe2a
commit 6ed3c759d0
7 changed files with 219 additions and 108 deletions

View File

@ -53,6 +53,16 @@ public abstract class BaseMasterAndRegionObserver extends BaseRegionObserver
HTableDescriptor desc, HRegionInfo[] regions) throws IOException { HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
} }
@Override
public void preDispatchMerge(final ObserverContext<MasterCoprocessorEnvironment> ctx,
HRegionInfo regionA, HRegionInfo regionB) throws IOException {
}
@Override
public void postDispatchMerge(final ObserverContext<MasterCoprocessorEnvironment> ctx,
HRegionInfo regionA, HRegionInfo regionB) throws IOException {
}
@Override @Override
public void preCreateTableHandler( public void preCreateTableHandler(
final ObserverContext<MasterCoprocessorEnvironment> ctx, final ObserverContext<MasterCoprocessorEnvironment> ctx,

View File

@ -64,6 +64,16 @@ public class BaseMasterObserver implements MasterObserver {
HTableDescriptor desc, HRegionInfo[] regions) throws IOException { HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
} }
@Override
public void preDispatchMerge(final ObserverContext<MasterCoprocessorEnvironment> ctx,
HRegionInfo regionA, HRegionInfo regionB) throws IOException {
}
@Override
public void postDispatchMerge(final ObserverContext<MasterCoprocessorEnvironment> ctx,
HRegionInfo regionA, HRegionInfo regionB) throws IOException {
}
@Override @Override
public void preDeleteTable(ObserverContext<MasterCoprocessorEnvironment> ctx, public void preDeleteTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
TableName tableName) throws IOException { TableName tableName) throws IOException {

View File

@ -1217,4 +1217,25 @@ public interface MasterObserver extends Coprocessor {
*/ */
void postSetNamespaceQuota(final ObserverContext<MasterCoprocessorEnvironment> ctx, void postSetNamespaceQuota(final ObserverContext<MasterCoprocessorEnvironment> ctx,
final String namespace, final Quotas quotas) throws IOException; final String namespace, final Quotas quotas) throws IOException;
/**
* Called before dispatching region merge request.
* It can't bypass the default action, e.g., ctx.bypass() won't have effect.
* @param ctx coprocessor environment
* @param regionA first region to be merged
* @param regionB second region to be merged
* @throws IOException if an error occurred on the coprocessor
*/
public void preDispatchMerge(final ObserverContext<MasterCoprocessorEnvironment> ctx,
HRegionInfo regionA, HRegionInfo regionB) throws IOException;
/**
* called after dispatching the region merge request.
* @param c coprocessor environment
* @param regionA first region to be merged
* @param regionB second region to be merged
* @throws IOException if an error occurred on the coprocessor
*/
void postDispatchMerge(final ObserverContext<MasterCoprocessorEnvironment> c,
final HRegionInfo regionA, final HRegionInfo regionB) throws IOException;
} }

View File

@ -729,6 +729,28 @@ public class MasterCoprocessorHost
}); });
} }
public void preDispatchMerge(final HRegionInfo regionInfoA, final HRegionInfo regionInfoB)
throws IOException {
execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
@Override
public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
throws IOException {
oserver.preDispatchMerge(ctx, regionInfoA, regionInfoB);
}
});
}
public void postDispatchMerge(final HRegionInfo regionInfoA, final HRegionInfo regionInfoB)
throws IOException {
execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
@Override
public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
throws IOException {
oserver.postDispatchMerge(ctx, regionInfoA, regionInfoB);
}
});
}
public boolean preBalance() throws IOException { public boolean preBalance() throws IOException {
return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() { return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
@Override @Override

View File

@ -514,8 +514,8 @@ public class MasterRpcServices extends RSRpcServices
"Unable to merge regions not online " + regionStateA + ", " + regionStateB)); "Unable to merge regions not online " + regionStateA + ", " + regionStateB));
} }
HRegionInfo regionInfoA = regionStateA.getRegion(); final HRegionInfo regionInfoA = regionStateA.getRegion();
HRegionInfo regionInfoB = regionStateB.getRegion(); final HRegionInfo regionInfoB = regionStateB.getRegion();
if (regionInfoA.getReplicaId() != HRegionInfo.DEFAULT_REPLICA_ID || if (regionInfoA.getReplicaId() != HRegionInfo.DEFAULT_REPLICA_ID ||
regionInfoB.getReplicaId() != HRegionInfo.DEFAULT_REPLICA_ID) { regionInfoB.getReplicaId() != HRegionInfo.DEFAULT_REPLICA_ID) {
throw new ServiceException(new MergeRegionException("Can't merge non-default replicas")); throw new ServiceException(new MergeRegionException("Can't merge non-default replicas"));
@ -524,6 +524,11 @@ public class MasterRpcServices extends RSRpcServices
throw new ServiceException(new MergeRegionException( throw new ServiceException(new MergeRegionException(
"Unable to merge a region to itself " + regionInfoA + ", " + regionInfoB)); "Unable to merge a region to itself " + regionInfoA + ", " + regionInfoB));
} }
try {
master.cpHost.preDispatchMerge(regionInfoA, regionInfoB);
} catch (IOException ioe) {
throw new ServiceException(ioe);
}
if (!forcible && !HRegionInfo.areAdjacent(regionInfoA, regionInfoB)) { if (!forcible && !HRegionInfo.areAdjacent(regionInfoA, regionInfoB)) {
throw new ServiceException(new MergeRegionException( throw new ServiceException(new MergeRegionException(
@ -535,6 +540,7 @@ public class MasterRpcServices extends RSRpcServices
try { try {
master.dispatchMergingRegions(regionInfoA, regionInfoB, forcible); master.dispatchMergingRegions(regionInfoA, regionInfoB, forcible);
master.cpHost.postDispatchMerge(regionInfoA, regionInfoB);
} catch (IOException ioe) { } catch (IOException ioe) {
throw new ServiceException(ioe); throw new ServiceException(ioe);
} }

View File

@ -2529,6 +2529,13 @@ public class AccessController extends BaseMasterAndRegionObserver
} }
} }
@Override
public void preDispatchMerge(final ObserverContext<MasterCoprocessorEnvironment> ctx,
HRegionInfo regionA, HRegionInfo regionB) throws IOException {
requirePermission("mergeRegions", regionA.getTable(), null, null,
Action.ADMIN);
}
@Override @Override
public void preMerge(ObserverContext<RegionServerCoprocessorEnvironment> ctx, Region regionA, public void preMerge(ObserverContext<RegionServerCoprocessorEnvironment> ctx, Region regionA,
Region regionB) throws IOException { Region regionB) throws IOException {

View File

@ -25,6 +25,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
@ -44,6 +45,9 @@ import org.apache.hadoop.hbase.ProcedureInfo;
import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Admin; 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.client.HTable;
import org.apache.hadoop.hbase.client.RegionLocator; import org.apache.hadoop.hbase.client.RegionLocator;
import org.apache.hadoop.hbase.client.Table; import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.master.AssignmentManager; import org.apache.hadoop.hbase.master.AssignmentManager;
@ -167,6 +171,8 @@ public class TestMasterObserver {
private boolean postGetTableDescriptorsCalled; private boolean postGetTableDescriptorsCalled;
private boolean postGetTableNamesCalled; private boolean postGetTableNamesCalled;
private boolean preGetTableNamesCalled; private boolean preGetTableNamesCalled;
private boolean preDispatchMergeCalled;
private boolean postDispatchMergeCalled;
public void enableBypass(boolean bypass) { public void enableBypass(boolean bypass) {
this.bypass = bypass; this.bypass = bypass;
@ -249,6 +255,24 @@ public class TestMasterObserver {
postGetTableDescriptorsCalled = false; postGetTableDescriptorsCalled = false;
postGetTableNamesCalled = false; postGetTableNamesCalled = false;
preGetTableNamesCalled = false; preGetTableNamesCalled = false;
preDispatchMergeCalled = false;
postDispatchMergeCalled = false;
}
@Override
public void preDispatchMerge(final ObserverContext<MasterCoprocessorEnvironment> ctx,
HRegionInfo regionA, HRegionInfo regionB) throws IOException {
preDispatchMergeCalled = true;
}
@Override
public void postDispatchMerge(final ObserverContext<MasterCoprocessorEnvironment> ctx,
HRegionInfo regionA, HRegionInfo regionB) throws IOException {
postDispatchMergeCalled = true;
}
public boolean wasDispatchMergeCalled() {
return preDispatchMergeCalled && postDispatchMergeCalled;
} }
@Override @Override
@ -1347,10 +1371,12 @@ public class TestMasterObserver {
// create a table // create a table
HTableDescriptor htd = new HTableDescriptor(tableName); HTableDescriptor htd = new HTableDescriptor(tableName);
htd.addFamily(new HColumnDescriptor(TEST_FAMILY)); htd.addFamily(new HColumnDescriptor(TEST_FAMILY));
Admin admin = UTIL.getHBaseAdmin(); try(Connection connection = ConnectionFactory.createConnection(UTIL.getConfiguration());
Admin admin = connection.getAdmin()) {
tableCreationLatch = new CountDownLatch(1); tableCreationLatch = new CountDownLatch(1);
admin.createTable(htd); admin.createTable(htd, Arrays.copyOfRange(HBaseTestingUtility.KEYS,
1, HBaseTestingUtility.KEYS.length));
// preCreateTable can't bypass default action. // preCreateTable can't bypass default action.
assertTrue("Test table should be created", cp.wasCreateTableCalled()); assertTrue("Test table should be created", cp.wasCreateTableCalled());
tableCreationLatch.await(); tableCreationLatch.await();
@ -1359,6 +1385,14 @@ public class TestMasterObserver {
assertTrue("Table create handler should be called.", assertTrue("Table create handler should be called.",
cp.wasCreateTableHandlerCalled()); cp.wasCreateTableHandlerCalled());
RegionLocator regionLocator = connection.getRegionLocator(htd.getTableName());
List<HRegionLocation> regions = regionLocator.getAllRegionLocations();
admin.mergeRegions(regions.get(0).getRegionInfo().getEncodedNameAsBytes(),
regions.get(1).getRegionInfo().getEncodedNameAsBytes(), true);
assertTrue("Coprocessor should have been called on region merge",
cp.wasDispatchMergeCalled());
tableCreationLatch = new CountDownLatch(1); tableCreationLatch = new CountDownLatch(1);
admin.disableTable(tableName); admin.disableTable(tableName);
assertTrue(admin.isTableDisabled(tableName)); assertTrue(admin.isTableDisabled(tableName));
@ -1497,6 +1531,7 @@ public class TestMasterObserver {
assertTrue("Delete table handler should be called.", assertTrue("Delete table handler should be called.",
cp.wasDeleteTableHandlerCalled()); cp.wasDeleteTableHandlerCalled());
} }
}
@Test (timeout=180000) @Test (timeout=180000)
public void testSnapshotOperations() throws Exception { public void testSnapshotOperations() throws Exception {