diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterObserver.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterObserver.java index d005389..7dadf3b 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterObserver.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterObserver.java @@ -65,6 +65,11 @@ public class BaseMasterObserver implements MasterObserver { } @Override + public void preMerge(final ObserverContext ctx, + HRegionInfo regionA, HRegionInfo regionB) throws IOException { + } + + @Override public void preDeleteTable(ObserverContext ctx, TableName tableName) throws IOException { } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java index ede8cd4..0b8fbc0 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java @@ -1217,4 +1217,16 @@ public interface MasterObserver extends Coprocessor { */ void postSetNamespaceQuota(final ObserverContext ctx, final String namespace, final Quotas quotas) throws IOException; + + /** + * Called before sending region merge request. + * Call {@link org.apache.hadoop.hbase.coprocessor.ObserverContext#bypass()} to skip the merge. + * @throws IOException if an error occurred on the coprocessor + * @param ctx + * @param regionA + * @param regionB + * @throws IOException + */ + public void preMerge(final ObserverContext ctx, + HRegionInfo regionA, HRegionInfo regionB) throws IOException; } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java index 5fa92c6..6c1ab96 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java @@ -729,6 +729,17 @@ public class MasterCoprocessorHost }); } + public void preMerge(final HRegionInfo regionInfoA, final HRegionInfo regionInfoB) + throws IOException { + execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() { + @Override + public void call(MasterObserver oserver, ObserverContext ctx) + throws IOException { + oserver.preMerge(ctx, regionInfoA, regionInfoB); + } + }); + } + public boolean preBalance() throws IOException { return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() { @Override diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java index 141fa88..6912b58 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java @@ -19,7 +19,9 @@ package org.apache.hadoop.hbase.master; import java.io.IOException; +import java.io.InterruptedIOException; import java.net.InetAddress; +import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -47,6 +49,7 @@ import org.apache.hadoop.hbase.exceptions.MergeRegionException; import org.apache.hadoop.hbase.exceptions.UnknownProtocolException; import org.apache.hadoop.hbase.ipc.PriorityFunction; import org.apache.hadoop.hbase.ipc.QosPriority; +import org.apache.hadoop.hbase.ipc.RpcServer; import org.apache.hadoop.hbase.ipc.RpcServer.BlockingServiceAndInterface; import org.apache.hadoop.hbase.ipc.ServerRpcController; import org.apache.hadoop.hbase.mob.MobUtils; @@ -514,8 +517,8 @@ public class MasterRpcServices extends RSRpcServices "Unable to merge regions not online " + regionStateA + ", " + regionStateB)); } - HRegionInfo regionInfoA = regionStateA.getRegion(); - HRegionInfo regionInfoB = regionStateB.getRegion(); + final HRegionInfo regionInfoA = regionStateA.getRegion(); + final HRegionInfo regionInfoB = regionStateB.getRegion(); if (regionInfoA.getReplicaId() != HRegionInfo.DEFAULT_REPLICA_ID || regionInfoB.getReplicaId() != HRegionInfo.DEFAULT_REPLICA_ID) { throw new ServiceException(new MergeRegionException("Can't merge non-default replicas")); @@ -524,6 +527,24 @@ public class MasterRpcServices extends RSRpcServices throw new ServiceException(new MergeRegionException( "Unable to merge a region to itself " + regionInfoA + ", " + regionInfoB)); } + if (User.isHBaseSecurityEnabled(master.getConfiguration())) { + User user = RpcServer.getRequestUser(); + try { + user.getUGI().doAs(new PrivilegedExceptionAction() { + @Override + public Void run() throws Exception { + master.cpHost.preMerge(regionInfoA, regionInfoB); + return null; + } + }); + } catch (InterruptedException ie) { + InterruptedIOException iioe = new InterruptedIOException(); + iioe.initCause(ie); + throw new ServiceException(iioe); + } catch (IOException ioe) { + throw new ServiceException(ioe); + } + } if (!forcible && !HRegionInfo.areAdjacent(regionInfoA, regionInfoB)) { throw new ServiceException(new MergeRegionException( diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java index bb348a3..278dcdf 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java @@ -2530,6 +2530,13 @@ public class AccessController extends BaseMasterAndRegionObserver } @Override + public void preMerge(final ObserverContext ctx, + HRegionInfo regionA, HRegionInfo regionB) throws IOException { + requirePermission("mergeRegions", regionA.getTable(), null, null, + Action.ADMIN); + } + + @Override public void preMerge(ObserverContext ctx, Region regionA, Region regionB) throws IOException { requirePermission("mergeRegions", regionA.getTableDesc().getTableName(), null, null, diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityController.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityController.java index 347f3da..890c166 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityController.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityController.java @@ -43,6 +43,7 @@ import org.apache.hadoop.hbase.DoNotRetryIOException; import org.apache.hadoop.hbase.HBaseInterfaceAudience; import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.MetaTableAccessor; import org.apache.hadoop.hbase.TableName; @@ -223,6 +224,11 @@ public class VisibilityController extends BaseMasterAndRegionObserver implements } @Override + public void preMerge(final ObserverContext ctx, + HRegionInfo regionA, HRegionInfo regionB) throws IOException { + } + + @Override public void preAddColumnFamily(ObserverContext ctx, TableName tableName, HColumnDescriptor columnFamily) throws IOException { diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java index 638811a..3156a83 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java @@ -252,6 +252,11 @@ public class TestMasterObserver { } @Override + public void preMerge(final ObserverContext ctx, + HRegionInfo regionA, HRegionInfo regionB) throws IOException { + } + + @Override public void preCreateTable(ObserverContext env, HTableDescriptor desc, HRegionInfo[] regions) throws IOException { if (bypass) {