HBASE-11275 [AccessController] postCreateTable hook fails when another CP creates table on their startup (Anoop Sam John)
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java
index 9ec1a01..5ec5bfb 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java
@@ -1188,6 +1188,8 @@
    // We depend on there being only one instance of this executor running
    // at a time.  To do concurrency, would need fencing of enable/disable of
    // tables.
+   // Any time changing this maxThreads to > 1, pls see the comment at
+   // AccessController#postCreateTableHandler
    this.executorService.startExecutorService(ExecutorType.MASTER_TABLE_OPERATIONS, 1);
 
    // Start log cleaner thread
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessControlLists.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessControlLists.java
index af35c8d..8baa34b 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessControlLists.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessControlLists.java
@@ -43,7 +43,6 @@
 import org.apache.hadoop.hbase.TableName;
 import org.apache.hadoop.hbase.Tag;
 import org.apache.hadoop.hbase.TagType;
-import org.apache.hadoop.hbase.catalog.MetaReader;
 import org.apache.hadoop.hbase.client.Delete;
 import org.apache.hadoop.hbase.client.Get;
 import org.apache.hadoop.hbase.client.HTable;
@@ -135,9 +134,7 @@
    * @param master reference to HMaster
    */
   static void init(MasterServices master) throws IOException {
-    if (!MetaReader.tableExists(master.getCatalogTracker(), ACL_TABLE_NAME)) {
-      master.createTable(ACL_TABLEDESC, null);
-    }
+    master.createTable(ACL_TABLEDESC, null);
   }
 
   /**
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 537b0d2..95e28fc 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
@@ -48,6 +48,7 @@
 import org.apache.hadoop.hbase.TableNotDisabledException;
 import org.apache.hadoop.hbase.TableNotFoundException;
 import org.apache.hadoop.hbase.Tag;
+import org.apache.hadoop.hbase.catalog.MetaReader;
 import org.apache.hadoop.hbase.client.Append;
 import org.apache.hadoop.hbase.client.Delete;
 import org.apache.hadoop.hbase.client.Durability;
@@ -178,6 +179,9 @@
 
   private volatile boolean initialized = false;
 
+  // This boolean having relevance only in the Master.
+  private volatile boolean aclTabAvailable = false;
+
   public HRegion getRegion() {
     return regionEnv != null ? regionEnv.getRegion() : null;
   }
@@ -838,20 +842,44 @@
 
   @Override
   public void postCreateTable(ObserverContext<MasterCoprocessorEnvironment> c,
-      HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
-    if (!AccessControlLists.isAclTable(desc)) {
-      String owner = desc.getOwnerString();
-      // default the table owner to current user, if not specified.
-      if (owner == null) owner = getActiveUser().getShortName();
-      UserPermission userperm = new UserPermission(Bytes.toBytes(owner), desc.getTableName(), null,
-          Action.values());
-      AccessControlLists.addUserPermission(c.getEnvironment().getConfiguration(), userperm);
-    }
-  }
+      HTableDescriptor desc, HRegionInfo[] regions) throws IOException {}
 
   @Override
   public void postCreateTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
-      HTableDescriptor desc, HRegionInfo[] regions) throws IOException {}
+      HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
+    // When AC is used, it should be configured as the 1st CP.
+    // In Master, the table operations like create, are handled by a Thread pool but the max size
+    // for this pool is 1. So if multiple CPs create tables on startup, these creations will happen
+    // sequentially only.
+    // Related code in HMaster#startServiceThreads
+    // {code}
+    //   // We depend on there being only one instance of this executor running
+    //   // at a time. To do concurrency, would need fencing of enable/disable of
+    //   // tables.
+    //   this.service.startExecutorService(ExecutorType.MASTER_TABLE_OPERATIONS, 1);
+    // {code}
+    // In future if we change this pool to have more threads, then there is a chance for thread,
+    // creating acl table, getting delayed and by that time another table creation got over and
+    // this hook is getting called. In such a case, we will need a wait logic here which will
+    // wait till the acl table is created.
+    if (AccessControlLists.isAclTable(desc)) {
+      this.aclTabAvailable = true;
+    } else if (!(TableName.NAMESPACE_TABLE_NAME.equals(desc.getTableName()))) {
+      if (!aclTabAvailable) {
+        LOG.warn("Not adding owner permission for table " + desc.getTableName() + ". "
+            + AccessControlLists.ACL_TABLE_NAME + " is not yet created. "
+            + getClass().getSimpleName() + " should be configured as the first Coprocessor");
+      } else {
+        String owner = desc.getOwnerString();
+        // default the table owner to current user, if not specified.
+        if (owner == null)
+          owner = getActiveUser().getShortName();
+        UserPermission userperm = new UserPermission(Bytes.toBytes(owner), desc.getTableName(),
+            null, Action.values());
+        AccessControlLists.addUserPermission(c.getEnvironment().getConfiguration(), userperm);
+      }
+    }
+  }
 
   @Override
   public void preDeleteTable(ObserverContext<MasterCoprocessorEnvironment> c, TableName tableName)
@@ -1073,8 +1101,13 @@
   @Override
   public void postStartMaster(ObserverContext<MasterCoprocessorEnvironment> ctx)
       throws IOException {
-    // initialize the ACL storage table
-    AccessControlLists.init(ctx.getEnvironment().getMasterServices());
+    if (!MetaReader.tableExists(ctx.getEnvironment().getMasterServices().getCatalogTracker(),
+        AccessControlLists.ACL_TABLE_NAME)) {
+      // initialize the ACL storage table
+      AccessControlLists.init(ctx.getEnvironment().getMasterServices());
+    } else {
+      aclTabAvailable = true;
+    }
   }
 
   @Override