This is an automated email from the ASF dual-hosted git repository.

prashantpogde pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new 7f5277fabb HDDS-8994. [Multi-Tenant] Add CLI option to allow tenant 
creation on top of existing volumes (#5045)
7f5277fabb is described below

commit 7f5277fabb09118860ae6e24124b4183b4b96e14
Author: Siyao Meng <[email protected]>
AuthorDate: Thu Jul 13 13:36:05 2023 -0700

    HDDS-8994. [Multi-Tenant] Add CLI option to allow tenant creation on top of 
existing volumes (#5045)
---
 .../org/apache/hadoop/ozone/client/TenantArgs.java |  25 ++++-
 .../apache/hadoop/ozone/client/rpc/RpcClient.java  |  15 ++-
 .../hadoop/ozone/om/helpers/OmTenantArgs.java      |  26 ++++-
 ...OzoneManagerProtocolClientSideTranslatorPB.java |   4 +-
 .../hadoop/ozone/shell/TestOzoneTenantShell.java   |  18 +++-
 .../src/main/proto/OmClientProtocol.proto          |   1 +
 .../request/s3/tenant/OMTenantCreateRequest.java   |  96 ++++++++++++------
 .../response/s3/tenant/OMTenantCreateResponse.java |  19 ++--
 .../hadoop/ozone/om/TestOMMultiTenantManager.java  |   2 +-
 .../hadoop/ozone/om/TestOMTenantCreateRequest.java | 111 +++++++++++++++++++--
 .../ozone/om/request/OMRequestTestUtils.java       |   6 +-
 .../ozone/shell/tenant/TenantCreateHandler.java    |  15 ++-
 12 files changed, 277 insertions(+), 61 deletions(-)

diff --git 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/TenantArgs.java
 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/TenantArgs.java
index f1b67a5773..eed7a6829c 100644
--- 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/TenantArgs.java
+++ 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/TenantArgs.java
@@ -30,18 +30,30 @@ public final class TenantArgs {
    */
   private final String volumeName;
 
+  /**
+   * Force tenant creation when volume exists.
+   */
+  private final boolean forceCreationWhenVolumeExists;
+
   /**
    * Private constructor, constructed via builder.
-   * @param volumeName Volume name.
+   *
+   * @param volumeName                    Volume name.
+   * @param forceCreationWhenVolumeExists Force creation when volume exists.
    */
-  private TenantArgs(String volumeName) {
+  private TenantArgs(String volumeName, boolean forceCreationWhenVolumeExists) 
{
     this.volumeName = volumeName;
+    this.forceCreationWhenVolumeExists = forceCreationWhenVolumeExists;
   }
 
   public String getVolumeName() {
     return volumeName;
   }
 
+  public boolean getForceCreationWhenVolumeExists() {
+    return forceCreationWhenVolumeExists;
+  }
+
   /**
    * Returns new builder class that builds a TenantArgs.
    *
@@ -57,6 +69,7 @@ public final class TenantArgs {
   @SuppressWarnings("checkstyle:hiddenfield")
   public static class Builder {
     private String volumeName;
+    private boolean forceCreationWhenVolumeExists;
 
     /**
      * Constructs a builder.
@@ -69,13 +82,19 @@ public final class TenantArgs {
       return this;
     }
 
+    public TenantArgs.Builder setForceCreationWhenVolumeExists(
+        boolean forceCreationWhenVolumeExists) {
+      this.forceCreationWhenVolumeExists = forceCreationWhenVolumeExists;
+      return this;
+    }
+
     /**
      * Constructs a TenantArgs.
      * @return TenantArgs.
      */
     public TenantArgs build() {
       Preconditions.checkNotNull(volumeName);
-      return new TenantArgs(volumeName);
+      return new TenantArgs(volumeName, forceCreationWhenVolumeExists);
     }
   }
 
diff --git 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
index f6e9b2a328..8bf0527b64 100644
--- 
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
+++ 
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/rpc/RpcClient.java
@@ -885,14 +885,21 @@ public class RpcClient implements ClientProtocol {
     final String volumeName = tenantArgs.getVolumeName();
     verifyVolumeName(volumeName);
 
+    final boolean forceCreationWhenVolumeExists =
+        tenantArgs.getForceCreationWhenVolumeExists();
+
     OmTenantArgs.Builder builder = OmTenantArgs.newBuilder();
     builder.setTenantId(tenantId);
     builder.setVolumeName(volumeName);
-    // TODO: Add more fields
-    // TODO: Include OmVolumeArgs in (Om)TenantArgs as well for volume 
creation?
+    builder.setForceCreationWhenVolumeExists(
+        tenantArgs.getForceCreationWhenVolumeExists());
+
+    // TODO: Add more fields. e.g. include OmVolumeArgs in (Om)TenantArgs
+    //  as well for customized volume creation.
 
-    LOG.info("Creating Tenant: '{}', with new volume: '{}'",
-        tenantId, volumeName);
+    LOG.info("Creating Tenant: '{}', with volume: '{}', "
+            + "forceCreationWhenVolumeExists: {}",
+        tenantId, volumeName, forceCreationWhenVolumeExists);
 
     ozoneManagerClient.createTenant(builder.build());
   }
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmTenantArgs.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmTenantArgs.java
index 518a08bacb..bf331c48a1 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmTenantArgs.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmTenantArgs.java
@@ -36,6 +36,11 @@ public class OmTenantArgs {
    */
   private final String volumeName;
 
+  /**
+   * Force tenant creation when volume exists.
+   */
+  private boolean forceCreationWhenVolumeExists;
+
   public OmTenantArgs(String tenantId) {
     this.tenantId = tenantId;
     this.volumeName = this.tenantId;
@@ -46,6 +51,13 @@ public class OmTenantArgs {
     this.volumeName = volumeName;
   }
 
+  public OmTenantArgs(String tenantId, String volumeName,
+      boolean forceCreationWhenVolumeExists) {
+    this.tenantId = tenantId;
+    this.volumeName = volumeName;
+    this.forceCreationWhenVolumeExists = forceCreationWhenVolumeExists;
+  }
+
   public String getTenantId() {
     return tenantId;
   }
@@ -54,6 +66,10 @@ public class OmTenantArgs {
     return volumeName;
   }
 
+  public boolean getForceCreationWhenVolumeExists() {
+    return forceCreationWhenVolumeExists;
+  }
+
   public static OmTenantArgs.Builder newBuilder() {
     return new OmTenantArgs.Builder();
   }
@@ -65,6 +81,7 @@ public class OmTenantArgs {
   public static class Builder {
     private String tenantId;
     private String volumeName;
+    private boolean forceCreationWhenVolumeExists;
 
     /**
      * Constructs a builder.
@@ -82,10 +99,17 @@ public class OmTenantArgs {
       return this;
     }
 
+    public Builder setForceCreationWhenVolumeExists(
+        boolean forceCreationWhenVolumeExists) {
+      this.forceCreationWhenVolumeExists = forceCreationWhenVolumeExists;
+      return this;
+    }
+
     public OmTenantArgs build() {
       Preconditions.checkNotNull(tenantId);
       Preconditions.checkNotNull(volumeName);
-      return new OmTenantArgs(tenantId, volumeName);
+      return new OmTenantArgs(tenantId, volumeName,
+          forceCreationWhenVolumeExists);
     }
   }
 
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
index 348e364a42..e8b1bd2254 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/OzoneManagerProtocolClientSideTranslatorPB.java
@@ -1074,7 +1074,9 @@ public final class 
OzoneManagerProtocolClientSideTranslatorPB
     final CreateTenantRequest.Builder requestBuilder =
         CreateTenantRequest.newBuilder()
             .setTenantId(omTenantArgs.getTenantId())
-            .setVolumeName(omTenantArgs.getVolumeName());
+            .setVolumeName(omTenantArgs.getVolumeName())
+            .setForceCreationWhenVolumeExists(
+                omTenantArgs.getForceCreationWhenVolumeExists());
             // Can add more args (like policy names) later if needed
     final OMRequest omRequest = createOMRequest(Type.CreateTenant)
         .setCreateTenantRequest(requestBuilder)
diff --git 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneTenantShell.java
 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneTenantShell.java
index c2f113743f..b6b8d295d7 100644
--- 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneTenantShell.java
+++ 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneTenantShell.java
@@ -1077,7 +1077,7 @@ public class TestOzoneTenantShell {
   }
 
   @Test
-  public void testCreateTenantOnExistingVolumeShouldFail() throws IOException {
+  public void testCreateTenantOnExistingVolume() throws IOException {
     final String testVolume = "existing-volume-1";
     int exitC = execute(ozoneSh, new String[] {"volume", "create", 
testVolume});
     // Volume create should succeed
@@ -1085,12 +1085,26 @@ public class TestOzoneTenantShell {
     checkOutput(out, "", true);
     checkOutput(err, "", true);
 
-    // Try to create tenant on the same volume, should fail
+    // Try to create tenant on the same volume, should fail by default
     executeHA(tenantShell, new String[] {"create", testVolume});
     checkOutput(out, "", true);
     checkOutput(err, "Volume already exists\n", true);
 
+    // Try to create tenant on the same volume with --force, should work
+    executeHA(tenantShell, new String[] {"create", testVolume, "--force"});
+    checkOutput(out, "", true);
+    checkOutput(err, "", true);
+
+    // Try to create the same tenant one more time, should fail even
+    // with --force because the tenant already exists.
+    executeHA(tenantShell, new String[] {"create", testVolume, "--force"});
+    checkOutput(out, "", true);
+    checkOutput(err, "Tenant '" + testVolume + "' already exists\n", true);
+
     // Clean up
+    executeHA(tenantShell, new String[] {"delete", testVolume});
+    checkOutput(out, "", true);
+    checkOutput(err, "Deleted tenant '" + testVolume + "'.\n", false);
     deleteVolume(testVolume);
   }
 }
diff --git 
a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto 
b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
index 2655c30c11..760d68557a 100644
--- a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
+++ b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
@@ -1724,6 +1724,7 @@ message CreateTenantRequest {
     optional string volumeName = 2;
     optional string userRoleName = 3;
     optional string adminRoleName = 4;
+    optional bool forceCreationWhenVolumeExists = 5;
 }
 
 message SetRangerServiceVersionRequest {
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java
index 2c7158c4f4..d61ad27d27 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantCreateRequest.java
@@ -147,9 +147,16 @@ public class OMTenantCreateRequest extends OMVolumeRequest 
{
     final String dbVolumeKey = ozoneManager.getMetadataManager()
         .getVolumeKey(volumeName);
 
+    // Backwards compatibility with older Ozone clients that don't have this
+    // field. Defaults to false.
+    boolean forceCreationWhenVolumeExists =
+        request.hasForceCreationWhenVolumeExists()
+            && request.getForceCreationWhenVolumeExists();
+
     // Check volume existence
-    if (ozoneManager.getMetadataManager().getVolumeTable()
-        .isExist(dbVolumeKey)) {
+    if (!forceCreationWhenVolumeExists &&
+        ozoneManager.getMetadataManager().getVolumeTable().isExist(
+            dbVolumeKey)) {
       LOG.debug("volume: '{}' already exists", volumeName);
       throw new OMException("Volume already exists", VOLUME_ALREADY_EXISTS);
     }
@@ -192,7 +199,9 @@ public class OMTenantCreateRequest extends OMVolumeRequest {
                 .setTenantId(tenantId)
                 .setVolumeName(volumeName)
                 .setUserRoleName(userRoleName)
-                .setAdminRoleName(adminRoleName))
+                .setAdminRoleName(adminRoleName)
+                .setForceCreationWhenVolumeExists(
+                    forceCreationWhenVolumeExists))
         .setCreateVolumeRequest(
             CreateVolumeRequest.newBuilder()
                 .setVolumeInfo(updatedVolumeInfo));
@@ -216,7 +225,7 @@ public class OMTenantCreateRequest extends OMVolumeRequest {
     OMClientResponse omClientResponse = null;
     final OMResponse.Builder omResponse =
         OmResponseUtil.getOMResponseBuilder(getOmRequest());
-    OmVolumeArgs omVolumeArgs;
+    OmVolumeArgs omVolumeArgs = null;
     boolean acquiredVolumeLock = false;
     boolean acquiredUserLock = false;
     final String owner = getOmRequest().getUserInfo().getUserName();
@@ -227,6 +236,8 @@ public class OMTenantCreateRequest extends OMVolumeRequest {
     final String tenantId = request.getTenantId();
     final String userRoleName = request.getUserRoleName();
     final String adminRoleName = request.getAdminRoleName();
+    final boolean forceCreationWhenVolumeExists =
+        request.getForceCreationWhenVolumeExists();
 
     final VolumeInfo volumeInfo =
         getOmRequest().getCreateVolumeRequest().getVolumeInfo();
@@ -249,41 +260,64 @@ public class OMTenantCreateRequest extends 
OMVolumeRequest {
       acquiredVolumeLock = omMetadataManager.getLock().acquireWriteLock(
           VOLUME_LOCK, volumeName);
 
+      boolean skipVolumeCreation = false;
       // Check volume existence
       if (omMetadataManager.getVolumeTable().isExist(dbVolumeKey)) {
         LOG.debug("volume: '{}' already exists", volumeName);
-        throw new OMException("Volume already exists", VOLUME_ALREADY_EXISTS);
+        if (forceCreationWhenVolumeExists) {
+          LOG.warn("forceCreationWhenVolumeExists = true. Resuming "
+              + "tenant creation despite volume '{}' existence", volumeName);
+          skipVolumeCreation = true;
+        } else {
+          // forceCreationWhenVolumeExists is false, throw
+          throw new OMException("Volume already exists", 
VOLUME_ALREADY_EXISTS);
+        }
+      }
+
+      acquiredUserLock = omMetadataManager.getLock().acquireWriteLock(
+          USER_LOCK, owner);
+
+      PersistedUserVolumeInfo volumeList = null;
+      if (!skipVolumeCreation) {
+        // Create volume. TODO: dedup OMVolumeCreateRequest
+        omVolumeArgs = OmVolumeArgs.getFromProtobuf(volumeInfo);
+        omVolumeArgs.setQuotaInBytes(OzoneConsts.QUOTA_RESET);
+        omVolumeArgs.setQuotaInNamespace(OzoneConsts.QUOTA_RESET);
+        omVolumeArgs.setObjectID(
+            ozoneManager.getObjectIdFromTxId(transactionLogIndex));
+        omVolumeArgs.setUpdateID(transactionLogIndex,
+            ozoneManager.isRatisEnabled());
+
+        omVolumeArgs.incRefCount();
+        // Remove this check when vol ref count is also used by other features
+        Preconditions.checkState(omVolumeArgs.getRefCount() == 1L,
+            "refCount should have been set to 1");
+
+        final String dbUserKey = omMetadataManager.getUserKey(owner);
+        volumeList = omMetadataManager.getUserTable().get(dbUserKey);
+        volumeList = addVolumeToOwnerList(volumeList, volumeName, owner,
+            ozoneManager.getMaxUserVolumeCount(), transactionLogIndex);
+        createVolume(omMetadataManager, omVolumeArgs, volumeList, dbVolumeKey,
+            dbUserKey, transactionLogIndex);
+        LOG.debug("volume: '{}' successfully created", dbVolumeKey);
+      } else {
+        LOG.info("Skipped volume '{}' creation. "
+            + "Will only increment volume refCount", volumeName);
+        omVolumeArgs = getVolumeInfo(omMetadataManager, volumeName);
+
+        omVolumeArgs.incRefCount();
+        // Remove this check when vol ref count is also used by other features
+        Preconditions.checkState(omVolumeArgs.getRefCount() == 1L,
+            "refCount should have been set to 1");
+
+        omMetadataManager.getVolumeTable().addCacheEntry(
+            new CacheKey<>(omMetadataManager.getVolumeKey(volumeName)),
+            CacheValue.get(transactionLogIndex, omVolumeArgs));
       }
 
-      // Create volume
-      acquiredUserLock = 
omMetadataManager.getLock().acquireWriteLock(USER_LOCK,
-          owner);
-
-      // TODO: dedup OMVolumeCreateRequest
-      omVolumeArgs = OmVolumeArgs.getFromProtobuf(volumeInfo);
-      omVolumeArgs.setQuotaInBytes(OzoneConsts.QUOTA_RESET);
-      omVolumeArgs.setQuotaInNamespace(OzoneConsts.QUOTA_RESET);
-      omVolumeArgs.setObjectID(
-          ozoneManager.getObjectIdFromTxId(transactionLogIndex));
-      omVolumeArgs.setUpdateID(transactionLogIndex,
-          ozoneManager.isRatisEnabled());
-      // Set volume reference count to 1
-      omVolumeArgs.incRefCount();
-      Preconditions.checkState(omVolumeArgs.getRefCount() == 1,
-          "refCount should have been set to 1");
       // Audit
       auditMap = omVolumeArgs.toAuditMap();
 
-      PersistedUserVolumeInfo volumeList;
-      final String dbUserKey = omMetadataManager.getUserKey(owner);
-      volumeList = omMetadataManager.getUserTable().get(dbUserKey);
-      volumeList = addVolumeToOwnerList(volumeList, volumeName, owner,
-          ozoneManager.getMaxUserVolumeCount(), transactionLogIndex);
-      createVolume(omMetadataManager, omVolumeArgs, volumeList, dbVolumeKey,
-          dbUserKey, transactionLogIndex);
-      LOG.debug("volume: '{}' successfully created", dbVolumeKey);
-
-
       // Check tenant existence in tenantStateTable
       if (omMetadataManager.getTenantStateTable().isExist(tenantId)) {
         LOG.debug("tenant: '{}' already exists", tenantId);
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantCreateResponse.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantCreateResponse.java
index 11172cbcd1..a845734dd5 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantCreateResponse.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/tenant/OMTenantCreateResponse.java
@@ -26,7 +26,7 @@ import org.apache.hadoop.ozone.om.helpers.OmVolumeArgs;
 import org.apache.hadoop.ozone.om.response.CleanupTableInfo;
 import org.apache.hadoop.ozone.om.response.OMClientResponse;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
-import org.apache.hadoop.ozone.storage.proto.OzoneManagerStorageProtos;
+import 
org.apache.hadoop.ozone.storage.proto.OzoneManagerStorageProtos.PersistedUserVolumeInfo;
 
 import javax.annotation.Nonnull;
 import java.io.IOException;
@@ -43,13 +43,13 @@ import static 
org.apache.hadoop.ozone.om.OmMetadataManagerImpl.VOLUME_TABLE;
 })
 public class OMTenantCreateResponse extends OMClientResponse {
 
-  private OzoneManagerStorageProtos.PersistedUserVolumeInfo userVolumeInfo;
+  private PersistedUserVolumeInfo userVolumeInfo;
   private OmVolumeArgs omVolumeArgs;
   private OmDBTenantState omTenantState;
 
   public OMTenantCreateResponse(@Nonnull OMResponse omResponse,
       @Nonnull OmVolumeArgs omVolumeArgs,
-      @Nonnull OzoneManagerStorageProtos.PersistedUserVolumeInfo 
userVolumeInfo,
+      PersistedUserVolumeInfo userVolumeInfo,
       @Nonnull OmDBTenantState omTenantState
   ) {
     super(omResponse);
@@ -78,13 +78,16 @@ public class OMTenantCreateResponse extends 
OMClientResponse {
     // From OMVolumeCreateResponse
     String dbVolumeKey =
         omMetadataManager.getVolumeKey(omVolumeArgs.getVolume());
-    String dbUserKey =
-        omMetadataManager.getUserKey(omVolumeArgs.getOwnerName());
-
     omMetadataManager.getVolumeTable().putWithBatch(batchOperation,
         dbVolumeKey, omVolumeArgs);
-    omMetadataManager.getUserTable().putWithBatch(batchOperation, dbUserKey,
-        userVolumeInfo);
+
+    // userVolumeInfo can be null when skipped volume creation
+    if (userVolumeInfo != null) {
+      String dbUserKey =
+          omMetadataManager.getUserKey(omVolumeArgs.getOwnerName());
+      omMetadataManager.getUserTable().putWithBatch(batchOperation, dbUserKey,
+          userVolumeInfo);
+    }
   }
 
   @VisibleForTesting
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOMMultiTenantManager.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOMMultiTenantManager.java
index 39062472c8..41ab197c4f 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOMMultiTenantManager.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOMMultiTenantManager.java
@@ -139,7 +139,7 @@ public class TestOMMultiTenantManager {
 
     // Check that Multi-Tenancy write requests are blocked when not enabled
     expectWriteRequestToFail(ozoneManager,
-        OMRequestTestUtils.createTenantRequest(tenantId));
+        OMRequestTestUtils.createTenantRequest(tenantId, false));
     expectWriteRequestToFail(ozoneManager,
         OMRequestTestUtils.deleteTenantRequest(tenantId));
     expectWriteRequestToFail(ozoneManager,
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOMTenantCreateRequest.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOMTenantCreateRequest.java
index ff7407e3db..5b8762ed4f 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOMTenantCreateRequest.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOMTenantCreateRequest.java
@@ -28,8 +28,10 @@ import org.apache.hadoop.ozone.om.request.OMRequestTestUtils;
 import org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantCreateRequest;
 import org.apache.hadoop.ozone.om.response.OMClientResponse;
 import org.apache.hadoop.ozone.om.upgrade.OMLayoutVersionManager;
-import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CreateTenantRequest;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
+import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status;
 import org.apache.ozone.test.LambdaTestUtils;
 import org.junit.After;
 import org.junit.Assert;
@@ -39,6 +41,7 @@ import org.junit.rules.TemporaryFolder;
 import org.junit.Test;
 import org.mockito.Mockito;
 
+import java.io.IOException;
 import java.util.UUID;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -102,6 +105,102 @@ public class TestOMTenantCreateRequest {
     Mockito.framework().clearInlineMocks();
   }
 
+  @Test
+  public void testValidateAndUpdateCache() throws IOException {
+    // Happy path
+
+    final String tenantId = UUID.randomUUID().toString();
+
+    OMRequest originalRequest =
+        OMRequestTestUtils.createTenantRequest(tenantId, false);
+    OMTenantCreateRequest omTenantCreateRequest =
+        Mockito.spy(new OMTenantCreateRequest(originalRequest));
+    Mockito.doReturn("username").when(omTenantCreateRequest).getUserName();
+
+    // First creation should be successful
+    OMRequest modifiedRequest = omTenantCreateRequest.preExecute(ozoneManager);
+    omTenantCreateRequest = new OMTenantCreateRequest(modifiedRequest);
+
+    long txLogIndex = 1L;
+    OMClientResponse omClientResponse =
+        omTenantCreateRequest.validateAndUpdateCache(ozoneManager, txLogIndex,
+            ozoneManagerDoubleBufferHelper);
+    OMResponse omResponse = omClientResponse.getOMResponse();
+
+    Assert.assertNotNull(omResponse.getCreateTenantResponse());
+    Assert.assertEquals(Status.OK, omResponse.getStatus());
+    Assert.assertNotNull(omMetadataManager.getVolumeTable().get(
+        omMetadataManager.getVolumeKey(tenantId)));
+  }
+
+  @Test
+  public void testValidateAndUpdateCacheWhenVolumeExists() throws Exception {
+    // Check that forceCreationWhenVolumeExists flag behaves as expected
+
+    final String tenantId = UUID.randomUUID().toString();
+    final String ownerName = "username";
+
+    // Deliberately put volume entry in VolumeTable, to simulate the case where
+    //  the volume already exists.
+    OMRequestTestUtils.addVolumeToDB(tenantId, ownerName, omMetadataManager);
+
+    // First with forceCreationWhenVolumeExists = false
+    OMRequest originalRequest =
+        OMRequestTestUtils.createTenantRequest(tenantId, false);
+    OMTenantCreateRequest omTenantCreateRequest1 =
+        Mockito.spy(new OMTenantCreateRequest(originalRequest));
+    Mockito.doReturn(ownerName).when(omTenantCreateRequest1).getUserName();
+
+    // Should throw in preExecute
+    LambdaTestUtils.intercept(OMException.class, "VOLUME_ALREADY_EXISTS",
+        () -> omTenantCreateRequest1.preExecute(ozoneManager));
+
+    // Now with forceCreationWhenVolumeExists = true
+    originalRequest =
+        OMRequestTestUtils.createTenantRequest(tenantId, true);
+    OMTenantCreateRequest omTenantCreateRequest2 =
+        Mockito.spy(new OMTenantCreateRequest(originalRequest));
+    Mockito.doReturn(ownerName).when(omTenantCreateRequest2).getUserName();
+
+    // Should not throw now that forceCreationWhenVolumeExists = true
+    OMRequest modifiedRequest = 
omTenantCreateRequest2.preExecute(ozoneManager);
+    omTenantCreateRequest2 = new OMTenantCreateRequest(modifiedRequest);
+
+    // Craft a request that sets forceCreationWhenVolumeExists to false to test
+    //  validateAndUpdateCache.
+    CreateTenantRequest reqPostPreExecute =
+        omTenantCreateRequest2.getOmRequest().getCreateTenantRequest();
+    OMRequest modReqPostPreExecute =
+        omTenantCreateRequest2.getOmRequest().toBuilder()
+            .setCreateTenantRequest(
+                CreateTenantRequest.newBuilder()
+                    .setTenantId(tenantId)
+                    .setVolumeName(reqPostPreExecute.getVolumeName())
+                    .setUserRoleName(reqPostPreExecute.getUserRoleName())
+                    .setAdminRoleName(reqPostPreExecute.getAdminRoleName())
+                    .setForceCreationWhenVolumeExists(false)).build();
+    OMTenantCreateRequest modTenantCreateRequest =
+        new OMTenantCreateRequest(modReqPostPreExecute);
+    // OMResponse should have status VOLUME_ALREADY_EXISTS in this crafted case
+    OMClientResponse modOMClientResponse =
+        modTenantCreateRequest.validateAndUpdateCache(ozoneManager, 2L,
+            ozoneManagerDoubleBufferHelper);
+    Assert.assertEquals(Status.VOLUME_ALREADY_EXISTS,
+        modOMClientResponse.getOMResponse().getStatus());
+    Assert.assertEquals("Volume already exists",
+        modOMClientResponse.getOMResponse().getMessage());
+
+    // validateAndUpdateCache with forceCreationWhenVolumeExists = true
+    OMClientResponse omClientResponse =
+        omTenantCreateRequest2.validateAndUpdateCache(ozoneManager, 2L,
+            ozoneManagerDoubleBufferHelper);
+    OMResponse omResponse = omClientResponse.getOMResponse();
+
+    Assert.assertNotNull(omResponse.getCreateTenantResponse());
+    Assert.assertEquals(Status.OK, omResponse.getStatus());
+    Assert.assertNotNull(omMetadataManager.getVolumeTable().get(
+        omMetadataManager.getVolumeKey(tenantId)));
+  }
 
   @Test
   public void
@@ -140,7 +239,7 @@ public class TestOMTenantCreateRequest {
   private void acceptTenantIdCreationHelper(String tenantId)
       throws Exception {
     OMRequest originalRequest =
-        OMRequestTestUtils.createTenantRequest(tenantId);
+        OMRequestTestUtils.createTenantRequest(tenantId, false);
     OMTenantCreateRequest omTenantCreateRequest =
         Mockito.spy(new OMTenantCreateRequest(originalRequest));
     Mockito.doReturn("username").when(omTenantCreateRequest).getUserName();
@@ -151,12 +250,10 @@ public class TestOMTenantCreateRequest {
     OMClientResponse omClientResponse =
         omTenantCreateRequest.validateAndUpdateCache(ozoneManager, txLogIndex,
             ozoneManagerDoubleBufferHelper);
-    OzoneManagerProtocolProtos.OMResponse omResponse =
-        omClientResponse.getOMResponse();
+    OMResponse omResponse = omClientResponse.getOMResponse();
 
     Assert.assertNotNull(omResponse.getCreateTenantResponse());
-    Assert.assertEquals(OzoneManagerProtocolProtos.Status.OK,
-        omResponse.getStatus());
+    Assert.assertEquals(Status.OK, omResponse.getStatus());
     Assert.assertNotNull(omMetadataManager.getVolumeTable().get(
         omMetadataManager.getVolumeKey(tenantId)));
   }
@@ -171,7 +268,7 @@ public class TestOMTenantCreateRequest {
 
   private void doPreExecute(String tenantId) throws Exception {
     OMRequest originalRequest =
-        OMRequestTestUtils.createTenantRequest(tenantId);
+        OMRequestTestUtils.createTenantRequest(tenantId, false);
 
     OMTenantCreateRequest omTenantCreateRequest =
         Mockito.spy(new OMTenantCreateRequest(originalRequest));
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/OMRequestTestUtils.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/OMRequestTestUtils.java
index 1be049ef78..c1fd0f3069 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/OMRequestTestUtils.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/OMRequestTestUtils.java
@@ -971,12 +971,14 @@ public final class OMRequestTestUtils {
         .setClientId(UUID.randomUUID().toString()).build();
   }
 
-  public static OMRequest createTenantRequest(String tenantId) {
+  public static OMRequest createTenantRequest(String tenantId,
+      boolean forceCreationWhenVolumeExists) {
 
     final CreateTenantRequest.Builder requestBuilder =
         CreateTenantRequest.newBuilder()
             .setTenantId(tenantId)
-            .setVolumeName(tenantId);
+            .setVolumeName(tenantId)
+            .setForceCreationWhenVolumeExists(forceCreationWhenVolumeExists);
 
     return OMRequest.newBuilder()
         .setCreateTenantRequest(requestBuilder)
diff --git 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantCreateHandler.java
 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantCreateHandler.java
index 0789124412..fd6c410960 100644
--- 
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantCreateHandler.java
+++ 
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantCreateHandler.java
@@ -21,6 +21,7 @@ import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import com.google.gson.JsonObject;
 import org.apache.hadoop.ozone.client.OzoneClient;
+import org.apache.hadoop.ozone.client.TenantArgs;
 import org.apache.hadoop.ozone.shell.OzoneAddress;
 import picocli.CommandLine;
 
@@ -37,11 +38,23 @@ public class TenantCreateHandler extends TenantHandler {
   @CommandLine.Parameters(description = "Tenant name", arity = "1..1")
   private String tenantId;
 
+  @CommandLine.Option(names = {"-f", "--force"},
+      description = "(Optional) Force tenant creation even when volume exists. 
"
+          + "This does NOT override other errors like Ranger failure.",
+      hidden = true)
+  // This option is intentionally hidden to avoid abuse.
+  private boolean forceCreationWhenVolumeExists;
+
   @Override
   protected void execute(OzoneClient client, OzoneAddress address)
       throws IOException {
 
-    client.getObjectStore().createTenant(tenantId);
+    final TenantArgs tenantArgs = TenantArgs.newBuilder()
+        .setVolumeName(tenantId)
+        .setForceCreationWhenVolumeExists(forceCreationWhenVolumeExists)
+        .build();
+
+    client.getObjectStore().createTenant(tenantId, tenantArgs);
     // RpcClient#createTenant prints INFO level log of tenant and volume name
 
     if (isVerbose()) {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to