This is an automated email from the ASF dual-hosted git repository.
siyao pushed a commit to branch HDDS-4944
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/HDDS-4944 by this push:
new 9fc34dc HDDS-5754. [Multi-Tenant] Implement GetUserInfo (#2692)
9fc34dc is described below
commit 9fc34dc7ff0631038516db69a4e2bdeca91f2a15
Author: Siyao Meng <[email protected]>
AuthorDate: Mon Oct 4 11:28:39 2021 -0700
HDDS-5754. [Multi-Tenant] Implement GetUserInfo (#2692)
---
.../apache/hadoop/ozone/client/ObjectStore.java | 12 +-
.../ozone/client/protocol/ClientProtocol.java | 10 +-
.../apache/hadoop/ozone/client/rpc/RpcClient.java | 15 +-
.../main/java/org/apache/hadoop/ozone/OmUtils.java | 4 +-
.../org/apache/hadoop/ozone/audit/OMAction.java | 4 +-
.../om/helpers/OmDBKerberosPrincipalInfo.java | 5 +
.../ozone/om/helpers/TenantUserInfoValue.java | 87 +++++
.../MultiTenantAccessAuthorizerDummyPlugin.java | 131 +++++++
.../ozone/om/protocol/OzoneManagerProtocol.java | 10 +-
...OzoneManagerProtocolClientSideTranslatorPB.java | 22 +-
.../smoketest/security/ozone-secure-tenant.robot | 51 +++
hadoop-ozone/dist/src/shell/ozone/ozone | 5 +
.../hadoop/ozone/shell/TestOzoneShellHA.java | 17 -
.../hadoop/ozone/shell/TestOzoneTenantShell.java | 383 +++++++++++++++++++++
.../src/main/proto/OmClientProtocol.proto | 55 +--
.../hadoop/ozone/om/OMMultiTenantManagerImpl.java | 18 +-
.../hadoop/ozone/om/OmMetadataManagerImpl.java | 12 +-
.../org/apache/hadoop/ozone/om/OzoneManager.java | 54 ++-
.../om/ratis/utils/OzoneManagerRatisUtils.java | 9 +-
.../s3/tenant/OMAssignUserToTenantRequest.java | 16 +-
...java => OMRevokeUserAccessToTenantRequest.java} | 4 +-
.../request/s3/tenant/OMTenantCreateRequest.java | 85 +++--
.../s3/tenant/OMTenantUserModifyRequest.java | 50 ---
.../protocolPB/OzoneManagerRequestHandler.java | 25 ++
.../s3/security/TestS3GetSecretRequest.java | 4 +
.../org/apache/hadoop/ozone/shell/s3/S3Shell.java | 4 +-
.../hadoop/ozone/shell/s3/TenantCommands.java | 71 ----
.../{s3 => tenant}/AssignUserToTenantHandler.java | 13 +-
.../ozone/shell/tenant/GetUserInfoHandler.java | 83 +++++
.../RevokeUserAccessToTenantHandler.java} | 10 +-
.../shell/{s3 => tenant}/TenantCreateHandler.java | 10 +-
.../shell/{s3 => tenant}/TenantDeleteHandler.java | 8 +-
.../ozone/shell/{s3 => tenant}/TenantHandler.java | 29 +-
.../shell/{s3 => tenant}/TenantModifyHandler.java | 4 +-
.../{s3/S3Shell.java => tenant/TenantShell.java} | 29 +-
.../shell/{s3 => tenant}/TenantUserCommands.java | 10 +-
.../package-info.java} | 18 +-
37 files changed, 1083 insertions(+), 294 deletions(-)
diff --git
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/ObjectStore.java
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/ObjectStore.java
index 72b82d6..81b5101 100644
---
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/ObjectStore.java
+++
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/ObjectStore.java
@@ -34,6 +34,7 @@ import org.apache.hadoop.ozone.OzoneAcl;
import org.apache.hadoop.ozone.client.protocol.ClientProtocol;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
+import org.apache.hadoop.ozone.om.helpers.TenantUserInfoValue;
import org.apache.hadoop.ozone.security.OzoneTokenIdentifier;
import org.apache.hadoop.ozone.security.acl.OzoneObj;
import org.apache.hadoop.security.UserGroupInformation;
@@ -199,7 +200,16 @@ public class ObjectStore {
return proxy.assignUserToTenant(username, tenantName, accessId);
}
- // TODO: modify, delete
+ /**
+ * Get tenant info for a user.
+ * @param userPrincipal Kerberos principal of a user.
+ * @return TenantUserInfo
+ * @throws IOException
+ */
+ public TenantUserInfoValue tenantGetUserInfo(String userPrincipal)
+ throws IOException {
+ return proxy.tenantGetUserInfo(userPrincipal);
+ }
/**
* Returns Iterator to iterate over all the volumes in object store.
diff --git
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
index 2676710..33dfa3b 100644
---
a/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
+++
b/hadoop-ozone/client/src/main/java/org/apache/hadoop/ozone/client/protocol/ClientProtocol.java
@@ -47,6 +47,7 @@ import
org.apache.hadoop.ozone.om.helpers.OmMultipartUploadCompleteInfo;
import org.apache.hadoop.ozone.om.helpers.OzoneFileStatus;
import org.apache.hadoop.ozone.om.helpers.RepeatedOmKeyInfo;
import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
+import org.apache.hadoop.ozone.om.helpers.TenantUserInfoValue;
import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRoleInfo;
import org.apache.hadoop.ozone.security.OzoneTokenIdentifier;
@@ -582,7 +583,14 @@ public interface ClientProtocol {
S3SecretValue assignUserToTenant(String username, String tenantName,
String accessId) throws IOException;
- // TODO: modify, delete
+ /**
+ * Get tenant info for a user.
+ * @param userPrincipal Kerberos principal of a user.
+ * @return TenantUserInfo
+ * @throws IOException
+ */
+ TenantUserInfoValue tenantGetUserInfo(String userPrincipal)
+ throws IOException;
/**
* Get KMS client provider.
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 32aa18e..3b2e572 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
@@ -104,6 +104,7 @@ import org.apache.hadoop.ozone.om.helpers.RepeatedOmKeyInfo;
import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
import org.apache.hadoop.ozone.om.helpers.ServiceInfo;
import org.apache.hadoop.ozone.om.helpers.ServiceInfoEx;
+import org.apache.hadoop.ozone.om.helpers.TenantUserInfoValue;
import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol;
import org.apache.hadoop.ozone.om.protocolPB.OmTransport;
import org.apache.hadoop.ozone.om.protocolPB.OmTransportFactory;
@@ -655,7 +656,19 @@ public class RpcClient implements ClientProtocol {
username, tenantName, accessId);
}
- // TODO: modify, delete
+ /**
+ * Get tenant info for a user.
+ * @param userPrincipal Kerberos principal of a user.
+ * @return TenantUserInfo
+ * @throws IOException
+ */
+ @Override
+ public TenantUserInfoValue tenantGetUserInfo(String userPrincipal)
+ throws IOException {
+ Preconditions.checkArgument(Strings.isNotBlank(userPrincipal),
+ "userPrincipal can't be null or empty.");
+ return ozoneManagerClient.tenantGetUserInfo(userPrincipal);
+ }
@Override
public void setBucketVersioning(
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
index 8dc4bfc..8502744 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
@@ -265,6 +265,7 @@ public final class OmUtils {
case ListMultipartUploads:
case FinalizeUpgradeProgress:
case PrepareStatus:
+ case TenantGetUserInfo:
return true;
case CreateVolume:
case SetVolumeProperty:
@@ -304,8 +305,7 @@ public final class OmUtils {
case ModifyTenant:
case DeleteTenant:
case AssignUserToTenant:
- case ModifyTenantUser:
- case DeleteTenantUser:
+ case RevokeUserAccessToTenant:
return false;
default:
LOG.error("CmdType {} is not categorized as readOnly or not.", cmdType);
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java
index 390bbb5..961aacb 100644
---
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/audit/OMAction.java
@@ -77,9 +77,9 @@ public enum OMAction implements AuditAction {
MODIFY_TENANT,
DELETE_TENANT,
+ TENANT_GET_USER_INFO,
ASSIGN_USER_TO_TENANT,
- MODIFY_TENANT_USER,
- DELETE_TENANT_USER;
+ REVOKE_USER_ACCESS_TO_TENANT;
@Override
public String getAction() {
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmDBKerberosPrincipalInfo.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmDBKerberosPrincipalInfo.java
index e9beec2..a231ad4 100644
---
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmDBKerberosPrincipalInfo.java
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/OmDBKerberosPrincipalInfo.java
@@ -30,6 +30,7 @@ import java.util.Set;
* principal, but can be extended to store more fields later.
*/
public final class OmDBKerberosPrincipalInfo {
+
/**
* A set of accessIds.
*/
@@ -48,6 +49,10 @@ public final class OmDBKerberosPrincipalInfo {
Arrays.asList(serialized.split(SERIALIZATION_SPLIT_KEY)));
}
+ public Set<String> getAccessIds() {
+ return accessIds;
+ }
+
public boolean addAccessId(String accessId) {
return accessIds.add(accessId);
}
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/TenantUserInfoValue.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/TenantUserInfoValue.java
new file mode 100644
index 0000000..a0ac396
--- /dev/null
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/TenantUserInfoValue.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.ozone.om.helpers;
+
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantAccessIdInfo;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantUserInfo;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Utility class to handle protobuf message TenantUserInfo conversion.
+ */
+public class TenantUserInfoValue {
+
+ // Usually this is the Kerberos principal of a user.
+ private final String userPrincipal;
+
+ // A list of TenantAccessIdInfo from protobuf.
+ private final List<TenantAccessIdInfo> accessIdInfoList;
+
+ public String getUserPrincipal() {
+ return userPrincipal;
+ }
+
+ public List<TenantAccessIdInfo> getAccessIdInfoList() {
+ return accessIdInfoList;
+ }
+
+ public TenantUserInfoValue(String kerberosID,
+ List<TenantAccessIdInfo> accessIdInfoList) {
+ this.userPrincipal = kerberosID;
+ this.accessIdInfoList = accessIdInfoList;
+ }
+
+ public static TenantUserInfoValue fromProtobuf(
+ TenantUserInfo tenantUserInfo) {
+ return new TenantUserInfoValue(tenantUserInfo.getUserPrincipal(),
+ tenantUserInfo.getAccessIdInfoList());
+ }
+
+ public TenantUserInfo getProtobuf() {
+ final TenantUserInfo.Builder builder = TenantUserInfo.newBuilder();
+ builder.setUserPrincipal(this.userPrincipal);
+ accessIdInfoList.forEach(builder::addAccessIdInfo);
+ return builder.build();
+ }
+
+ @Override
+ public String toString() {
+ return "userPrincipal=" + userPrincipal +
+ "\naccessIdInfoList=" + accessIdInfoList;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ TenantUserInfoValue that = (TenantUserInfoValue) o;
+ return userPrincipal.equals(that.userPrincipal) &&
+ accessIdInfoList.equals(that.accessIdInfoList);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(userPrincipal, accessIdInfoList);
+ }
+}
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/MultiTenantAccessAuthorizerDummyPlugin.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/MultiTenantAccessAuthorizerDummyPlugin.java
new file mode 100644
index 0000000..8ab532d
--- /dev/null
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/multitenant/MultiTenantAccessAuthorizerDummyPlugin.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.ozone.om.multitenant;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.ozone.om.exceptions.OMException;
+import org.apache.hadoop.ozone.security.acl.IOzoneObj;
+import org.apache.hadoop.ozone.security.acl.RequestContext;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Dummy implementation of MultiTenantAccessAuthorizer when some parts of
+ * testing don't need to deal with Ranger.
+ */
+public class MultiTenantAccessAuthorizerDummyPlugin implements
+ MultiTenantAccessAuthorizer {
+
+ @Override
+ public void init(Configuration configuration) throws IOException {
+ }
+
+ @Override
+ public void shutdown() throws Exception {
+ }
+
+ @Override
+ public void grantAccess(BucketNameSpace bucketNameSpace,
+ OzoneMultiTenantPrincipal user, ACLType aclType) {
+ }
+
+ @Override
+ public void revokeAccess(BucketNameSpace bucketNameSpace,
+ OzoneMultiTenantPrincipal user, ACLType aclType) {
+ }
+
+ @Override
+ public void grantAccess(AccountNameSpace accountNameSpace,
+ OzoneMultiTenantPrincipal user, ACLType aclType) {
+ }
+
+ @Override
+ public void revokeAccess(AccountNameSpace accountNameSpace,
+ OzoneMultiTenantPrincipal user, ACLType aclType) {
+ }
+
+ public List<Pair<BucketNameSpace, ACLType>>
+ getAllBucketNameSpaceAccesses(OzoneMultiTenantPrincipal user) {
+ return null;
+ }
+
+ @Override
+ public boolean checkAccess(BucketNameSpace bucketNameSpace,
+ OzoneMultiTenantPrincipal user) {
+ return true;
+ }
+
+ @Override
+ public boolean checkAccess(AccountNameSpace accountNameSpace,
+ OzoneMultiTenantPrincipal user) {
+ return true;
+ }
+
+ @Override
+ public boolean checkAccess(IOzoneObj ozoneObject, RequestContext context)
+ throws OMException {
+ return true;
+ }
+
+ @Override
+ public String getGroupId(OzoneMultiTenantPrincipal principal)
+ throws Exception {
+ return "dummyGroupId";
+ }
+
+ @Override
+ public String getUserId(OzoneMultiTenantPrincipal principal)
+ throws Exception {
+ return "dummyUserId";
+ }
+
+ public String createUser(OzoneMultiTenantPrincipal principal,
+ List<String> groupIDs)
+ throws Exception {
+ return "dummyCreateUser";
+ }
+
+ public String createGroup(OzoneMultiTenantPrincipal group) throws Exception {
+ return "dummyCreateGroup";
+ }
+
+ public String createAccessPolicy(AccessPolicy policy) throws Exception {
+ return "dummyCreateAccessPolicy";
+ }
+
+ public AccessPolicy getAccessPolicyByName(String policyName)
+ throws Exception {
+ return new RangerAccessPolicy(policyName);
+ }
+
+ public void deleteUser(String userId) throws IOException {
+ }
+
+ public void deleteGroup(String groupId) throws IOException {
+ }
+
+ @Override
+ public void deletePolicybyName(String policyName) throws Exception {
+ }
+
+ public void deletePolicybyId(String policyId) throws IOException {
+ }
+
+}
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
index db1e373..ceae46b 100644
---
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocol/OzoneManagerProtocol.java
@@ -47,6 +47,7 @@ import org.apache.hadoop.ozone.om.helpers.RepeatedOmKeyInfo;
import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
import org.apache.hadoop.ozone.om.helpers.ServiceInfo;
import org.apache.hadoop.ozone.om.helpers.ServiceInfoEx;
+import org.apache.hadoop.ozone.om.helpers.TenantUserInfoValue;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OzoneAclInfo;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.PrepareStatusResponse;
@@ -474,7 +475,14 @@ public interface OzoneManagerProtocol
S3SecretValue assignUserToTenant(String username, String tenantName,
String accessId) throws IOException;
- // TODO: modify, delete
+ /**
+ * Get tenant info for a user.
+ * @param userPrincipal Kerberos principal of a user.
+ * @return TenantUserInfo
+ * @throws IOException
+ */
+ TenantUserInfoValue tenantGetUserInfo(String userPrincipal)
+ throws IOException;
/**
* OzoneFS api to get file status for an entry.
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 d21e532..2b82c4b 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
@@ -56,6 +56,7 @@ import org.apache.hadoop.ozone.om.helpers.RepeatedOmKeyInfo;
import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
import org.apache.hadoop.ozone.om.helpers.ServiceInfo;
import org.apache.hadoop.ozone.om.helpers.ServiceInfoEx;
+import org.apache.hadoop.ozone.om.helpers.TenantUserInfoValue;
import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AddAclRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AddAclResponse;
@@ -920,8 +921,25 @@ public final class
OzoneManagerProtocolClientSideTranslatorPB
return S3SecretValue.fromProtobuf(resp.getS3Secret());
}
- // TODO: Add a variant that uses OmTenantUserArgs
- // TODO: modify, delete
+ // TODO: Add a variant that uses OmTenantUserArgs?
+
+ @Override
+ public TenantUserInfoValue tenantGetUserInfo(String userPrincipal)
+ throws IOException {
+
+ final TenantGetUserInfoRequest request =
+ TenantGetUserInfoRequest.newBuilder()
+ .setUserPrincipal(userPrincipal)
+ .build();
+ final OMRequest omRequest = createOMRequest(Type.TenantGetUserInfo)
+ .setTenantGetUserInfoRequest(request)
+ .build();
+ final OMResponse omResponse = submitRequest(omRequest);
+ final TenantGetUserInfoResponse resp = handleError(omResponse)
+ .getTenantGetUserInfoResponse();
+
+ return TenantUserInfoValue.fromProtobuf(resp.getTenantUserInfo());
+ }
/**
* Return the proxy object underlying this protocol translator.
diff --git
a/hadoop-ozone/dist/src/main/smoketest/security/ozone-secure-tenant.robot
b/hadoop-ozone/dist/src/main/smoketest/security/ozone-secure-tenant.robot
new file mode 100644
index 0000000..93ab1f9
--- /dev/null
+++ b/hadoop-ozone/dist/src/main/smoketest/security/ozone-secure-tenant.robot
@@ -0,0 +1,51 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+*** Settings ***
+Documentation Smoke test for ozone secure tenant commands.
+Library OperatingSystem
+Library String
+Library BuiltIn
+Resource ../commonlib.robot
+Test Timeout 5 minutes
+
+*** Variables ***
+${RANGER_ENDPOINT_URL} https://ranger:6182
+
+*** Keywords ***
+Init Ranger MockServer
+ ${output} = Execute curl -k ${RANGER_ENDPOINT_URL}
+ Should contain ${output} {}
+
+*** Test Cases ***
+Secure Tenant Create Tenant Success
+# Run Keyword Kinit test user testuser testuser.keytab
+ Run Keyword Init Ranger MockServer
+ ${output} = Execute ozone tenant create finance
+ Should contain ${output} Created tenant
'finance'
+
+Secure Tenant Assign User Success
+ ${output} = Execute ozone tenant user assign
[email protected] --tenant=finance
+ Should contain ${output} Assigned
'[email protected]' to 'finance'
+
+Secure Tenant GetUserInfo Success
+ ${output} = Execute ozone tenant user info
[email protected]
+ Should contain ${output} Tenant 'finance'
with accessId '[email protected]'
+
+Secure Tenant Assign User Failure
+ ${rc} ${result} = Run And Return Rc And Output ozone tenant user assign
[email protected] --tenant=nonexistenttenant
+# Should Be True ${rc} > 0
+ Should contain ${result} tenant
'nonexistenttenant' doesn't exist
+
diff --git a/hadoop-ozone/dist/src/shell/ozone/ozone
b/hadoop-ozone/dist/src/shell/ozone/ozone
index 777de10..a487726 100755
--- a/hadoop-ozone/dist/src/shell/ozone/ozone
+++ b/hadoop-ozone/dist/src/shell/ozone/ozone
@@ -53,6 +53,7 @@ function ozone_usage
ozone_add_subcommand "recon" daemon "run the Recon service"
ozone_add_subcommand "sh" client "command line interface for object store
operations"
ozone_add_subcommand "s3" client "command line interface for s3 related
operations"
+ ozone_add_subcommand "tenant" client "command line interface for
multi-tenant related operations"
ozone_add_subcommand "insight" client "tool to get runtime operation
information"
ozone_add_subcommand "version" client "print the version"
ozone_add_subcommand "dtutil" client "operations related to delegation
tokens"
@@ -186,6 +187,10 @@ function ozonecmd_case
OZONE_CLASSNAME='org.apache.hadoop.ozone.s3.Gateway'
OZONE_RUN_ARTIFACT_NAME="ozone-s3gateway"
;;
+ tenant)
+ OZONE_CLASSNAME=org.apache.hadoop.ozone.shell.tenant.TenantShell
+ OZONE_RUN_ARTIFACT_NAME="ozone-tools"
+ ;;
csi)
OZONE_SUBCMD_SUPPORTDAEMONIZATION="true"
OZONE_CLASSNAME='org.apache.hadoop.ozone.csi.CsiServer'
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java
index 213b6c7..a78bd38 100644
---
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneShellHA.java
@@ -859,21 +859,4 @@ public class TestOzoneShellHA {
objectStore.deleteVolume("vol4");
}
- /**
- * Test ozone tenant commands.
- */
- @Test
- public void testOzoneTenant() {
- // TODO: tenant subcommand will be moved from s3 to admin later.
-
- // Test create tenant
- execute(s3Shell, new String[] {
- "tenant", "create", "finance",
- "--om-service-id=" + omServiceId});
-
- // Test assign user
- execute(s3Shell, new String[] {
- "user", "assign", "[email protected]", "--tenant=finance",
- "--om-service-id=" + omServiceId});
- }
}
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
new file mode 100644
index 0000000..8fd32ff
--- /dev/null
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/shell/TestOzoneTenantShell.java
@@ -0,0 +1,383 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.ozone.shell;
+
+import com.google.common.base.Strings;
+import org.apache.commons.io.FileUtils;
+import org.apache.hadoop.fs.FileUtil;
+import org.apache.hadoop.hdds.cli.GenericCli;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.io.retry.RetryInvocationHandler;
+import org.apache.hadoop.ozone.MiniOzoneCluster;
+import org.apache.hadoop.ozone.OzoneConsts;
+import org.apache.hadoop.ozone.ha.ConfUtils;
+import org.apache.hadoop.ozone.om.OMConfigKeys;
+import org.apache.hadoop.ozone.om.OMMultiTenantManagerImpl;
+import
org.apache.hadoop.ozone.om.request.s3.tenant.OMAssignUserToTenantRequest;
+import org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantCreateRequest;
+import org.apache.hadoop.ozone.shell.tenant.TenantShell;
+import org.apache.ozone.test.GenericTestUtils;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.Timeout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.event.Level;
+import picocli.CommandLine;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.fail;
+
+/**
+ * Integration test for Ozone tenant shell command. HA enabled.
+ */
+public class TestOzoneTenantShell {
+
+ private static final Logger LOG =
+ LoggerFactory.getLogger(TestOzoneTenantShell.class);
+
+ static {
+ System.setProperty("log4j.configurationFile", "auditlog.properties");
+ System.setProperty("log4j2.contextSelector",
+ "org.apache.logging.log4j.core.async.AsyncLoggerContextSelector");
+ }
+
+ private static final String DEFAULT_ENCODING = UTF_8.name();
+
+ /**
+ * Set the timeout for every test.
+ */
+ @Rule
+ public Timeout testTimeout = Timeout.seconds(300);
+
+ private static File baseDir;
+ private static File testFile;
+ private static final File AUDIT_LOG_FILE = new File("audit.log");
+
+ private static OzoneConfiguration conf = null;
+ private static MiniOzoneCluster cluster = null;
+ private static TenantShell tenantShell = null;
+
+ private final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ private final ByteArrayOutputStream err = new ByteArrayOutputStream();
+ private static final PrintStream OLD_OUT = System.out;
+ private static final PrintStream OLD_ERR = System.err;
+
+ private static String omServiceId;
+ private static String clusterId;
+ private static String scmId;
+ private static int numOfOMs;
+
+ /**
+ * Create a MiniOzoneCluster for testing with using distributed Ozone
+ * handler type.
+ *
+ * @throws Exception
+ */
+ @BeforeClass
+ public static void init() throws Exception {
+ // Remove audit log output if it exists
+ if (AUDIT_LOG_FILE.exists()) {
+ AUDIT_LOG_FILE.delete();
+ }
+
+ conf = new OzoneConfiguration();
+
+ conf.setBoolean(
+ OMMultiTenantManagerImpl.OZONE_OM_TENANT_DEV_SKIP_RANGER, true);
+
+ String path = GenericTestUtils.getTempPath(
+ TestOzoneTenantShell.class.getSimpleName());
+ baseDir = new File(path);
+ baseDir.mkdirs();
+
+ testFile = new File(path + OzoneConsts.OZONE_URI_DELIMITER + "testFile");
+ testFile.getParentFile().mkdirs();
+ testFile.createNewFile();
+
+ tenantShell = new TenantShell();
+
+ // Init cluster
+ omServiceId = "om-service-test1";
+ numOfOMs = 3;
+ clusterId = UUID.randomUUID().toString();
+ scmId = UUID.randomUUID().toString();
+ cluster = MiniOzoneCluster.newOMHABuilder(conf)
+ .setClusterId(clusterId)
+ .setScmId(scmId)
+ .setOMServiceId(omServiceId)
+ .setNumOfOzoneManagers(numOfOMs)
+ .build();
+ cluster.waitForClusterToBeReady();
+// MiniOzoneHAClusterImpl impl = (MiniOzoneOMHAClusterImpl) cluster;
+ }
+
+ /**
+ * shutdown MiniOzoneCluster.
+ */
+ @AfterClass
+ public static void shutdown() {
+ if (cluster != null) {
+ cluster.shutdown();
+ }
+
+ if (baseDir != null) {
+ FileUtil.fullyDelete(baseDir, true);
+ }
+ }
+
+ @Before
+ public void setup() throws UnsupportedEncodingException {
+ System.setOut(new PrintStream(out, false, UTF_8.name()));
+ System.setErr(new PrintStream(err, false, UTF_8.name()));
+ }
+
+ @After
+ public void reset() {
+ // reset stream after each unit test
+ out.reset();
+ err.reset();
+
+ // restore system streams
+ System.setOut(OLD_OUT);
+ System.setErr(OLD_ERR);
+ }
+
+ private void execute(GenericCli shell, String[] args) {
+ LOG.info("Executing shell command with args {}", Arrays.asList(args));
+ CommandLine cmd = shell.getCmd();
+
+ CommandLine.IExecutionExceptionHandler exceptionHandler =
+ (ex, commandLine, parseResult) -> {
+ commandLine.getErr().println(ex.getMessage());
+ return commandLine.getCommandSpec().exitCodeOnExecutionException();
+ };
+
+ // Since there is no elegant way to pass Ozone config to the shell,
+ // the idea is to use 'set' to place those OM HA configs.
+ String[] argsWithHAConf = getHASetConfStrings(args);
+
+ cmd.setExecutionExceptionHandler(exceptionHandler);
+ cmd.execute(argsWithHAConf);
+ }
+
+ /**
+ * Helper that appends HA service id to args.
+ */
+ private void executeHA(GenericCli shell, String[] args) {
+ final String[] newArgs = new String[args.length + 1];
+ System.arraycopy(args, 0, newArgs, 0, args.length);
+ newArgs[args.length] = "--om-service-id=" + omServiceId;
+ execute(shell, newArgs);
+ }
+
+ /**
+ * Execute command, assert exception message and returns true if error
+ * was thrown.
+ */
+ private void executeWithError(OzoneShell shell, String[] args,
+ String expectedError) {
+ if (Strings.isNullOrEmpty(expectedError)) {
+ execute(shell, args);
+ } else {
+ try {
+ execute(shell, args);
+ fail("Exception is expected from command execution " + Arrays
+ .asList(args));
+ } catch (Exception ex) {
+ if (!Strings.isNullOrEmpty(expectedError)) {
+ Throwable exceptionToCheck = ex;
+ if (exceptionToCheck.getCause() != null) {
+ exceptionToCheck = exceptionToCheck.getCause();
+ }
+ Assert.assertTrue(
+ String.format(
+ "Error of OzoneShell code doesn't contain the " +
+ "exception [%s] in [%s]",
+ expectedError, exceptionToCheck.getMessage()),
+ exceptionToCheck.getMessage().contains(expectedError));
+ }
+ }
+ }
+ }
+
+ private String getSetConfStringFromConf(String key) {
+ return String.format("--set=%s=%s", key, conf.get(key));
+ }
+
+ private String generateSetConfString(String key, String value) {
+ return String.format("--set=%s=%s", key, value);
+ }
+
+ /**
+ * Helper function to get a String array to be fed into OzoneShell.
+ * @param numOfArgs Additional number of arguments after the HA conf string,
+ * this translates into the number of empty array elements
+ * after the HA conf string.
+ * @return String array.
+ */
+ private String[] getHASetConfStrings(int numOfArgs) {
+ assert(numOfArgs >= 0);
+ String[] res = new String[1 + 1 + numOfOMs + numOfArgs];
+ final int indexOmServiceIds = 0;
+ final int indexOmNodes = 1;
+ final int indexOmAddressStart = 2;
+
+ res[indexOmServiceIds] = getSetConfStringFromConf(
+ OMConfigKeys.OZONE_OM_SERVICE_IDS_KEY);
+
+ String omNodesKey = ConfUtils.addKeySuffixes(
+ OMConfigKeys.OZONE_OM_NODES_KEY, omServiceId);
+ String omNodesVal = conf.get(omNodesKey);
+ res[indexOmNodes] = generateSetConfString(omNodesKey, omNodesVal);
+
+ String[] omNodesArr = omNodesVal.split(",");
+ // Sanity check
+ assert(omNodesArr.length == numOfOMs);
+ for (int i = 0; i < numOfOMs; i++) {
+ res[indexOmAddressStart + i] =
+ getSetConfStringFromConf(ConfUtils.addKeySuffixes(
+ OMConfigKeys.OZONE_OM_ADDRESS_KEY, omServiceId, omNodesArr[i]));
+ }
+
+ return res;
+ }
+
+ /**
+ * Helper function to create a new set of arguments that contains HA configs.
+ * @param existingArgs Existing arguments to be fed into OzoneShell command.
+ * @return String array.
+ */
+ private String[] getHASetConfStrings(String[] existingArgs) {
+ // Get a String array populated with HA configs first
+ String[] res = getHASetConfStrings(existingArgs.length);
+
+ int indexCopyStart = res.length - existingArgs.length;
+ // Then copy the existing args to the returned String array
+ System.arraycopy(existingArgs, 0, res, indexCopyStart,
+ existingArgs.length);
+ return res;
+ }
+
+ /**
+ * Helper function that checks command output AND clears it.
+ */
+ private void checkOutput(ByteArrayOutputStream stream, String stringToMatch,
+ boolean exactMatch) throws IOException {
+ stream.flush();
+ final String str = stream.toString(DEFAULT_ENCODING);
+ checkOutput(str, stringToMatch, exactMatch);
+ stream.reset();
+ }
+
+ private void checkOutput(String str, String stringToMatch,
+ boolean exactMatch) {
+ if (exactMatch) {
+ Assert.assertEquals(stringToMatch, str);
+ } else {
+ Assert.assertTrue(str.contains(stringToMatch));
+ }
+ }
+
+ /**
+ * Test tenant create, assign user and get user info.
+ */
+ @Test
+ public void testOzoneTenantCreateAssignInfo() throws IOException {
+
+ // Suppress OMNotLeaderException in the log
+ GenericTestUtils.setLogLevel(RetryInvocationHandler.LOG, Level.WARN);
+
+ GenericTestUtils.setLogLevel(OMTenantCreateRequest.LOG, Level.DEBUG);
+ GenericTestUtils.setLogLevel(OMAssignUserToTenantRequest.LOG, Level.DEBUG);
+
+ List<String> lines = FileUtils.readLines(AUDIT_LOG_FILE, (String)null);
+ Assert.assertEquals(0, lines.size());
+
+ // Create tenants
+ // Equivalent to `ozone tenant create finance`
+ executeHA(tenantShell, new String[] {"create", "finance"});
+ checkOutput(out, "Created tenant 'finance'.\n", true);
+ checkOutput(err, "", true);
+
+ lines = FileUtils.readLines(AUDIT_LOG_FILE, (String)null);
+ checkOutput(lines.get(lines.size() - 1), "ret=SUCCESS", false);
+
+ // Creating the tenant with the same name again should result in failure
+ executeHA(tenantShell, new String[] {"create", "finance"});
+ checkOutput(out, "", true);
+ checkOutput(err, "Failed to create tenant 'finance':"
+ + " Tenant already exists\n", true);
+
+ executeHA(tenantShell, new String[] {"create", "research"});
+ checkOutput(out, "Created tenant 'research'.\n", true);
+ checkOutput(err, "", true);
+
+ executeHA(tenantShell, new String[] {"create", "dev"});
+ checkOutput(out, "Created tenant 'dev'.\n", true);
+ checkOutput(err, "", true);
+
+ // Assign user
+ // Equivalent to `ozone tenant user assign [email protected]
--tenant=finance`
+ executeHA(tenantShell, new String[] {
+ "user", "assign", "[email protected]", "--tenant=finance"});
+ checkOutput(out, "export AWS_ACCESS_KEY_ID='[email protected]'\n"
+ + "export AWS_SECRET_ACCESS_KEY='", false);
+ checkOutput(err, "Assigned '[email protected]' to 'finance' with accessId"
+ + " '[email protected]'.\n", true);
+
+ executeHA(tenantShell, new String[] {
+ "user", "assign", "[email protected]", "--tenant=research"});
+ checkOutput(out, "export AWS_ACCESS_KEY_ID='[email protected]'\n"
+ + "export AWS_SECRET_ACCESS_KEY='", false);
+ checkOutput(err, "Assigned '[email protected]' to 'research' with accessId"
+ + " '[email protected]'.\n", true);
+
+ executeHA(tenantShell, new String[] {
+ "user", "assign", "[email protected]", "--tenant=dev"});
+ checkOutput(out, "export AWS_ACCESS_KEY_ID='[email protected]'\n"
+ + "export AWS_SECRET_ACCESS_KEY='", false);
+ checkOutput(err, "Assigned '[email protected]' to 'dev' with accessId"
+ + " '[email protected]'.\n", true);
+
+ // Get user info
+ // Equivalent to `ozone tenant user info [email protected]`
+ executeHA(tenantShell, new String[] {
+ "user", "info", "[email protected]"});
+ checkOutput(out, "User '[email protected]' is assigned to:\n"
+ + "- Tenant 'finance' with accessId '[email protected]'\n"
+ + "- Tenant 'research' with accessId '[email protected]'\n"
+ + "- Tenant 'dev' with accessId '[email protected]'\n\n", true);
+ checkOutput(err, "", true);
+ }
+
+}
diff --git
a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
index 62fec08..916427a 100644
--- a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
+++ b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
@@ -107,9 +107,9 @@ enum Type {
ModifyTenant = 96;
DeleteTenant = 97;
- AssignUserToTenant = 98;
- ModifyTenantUser = 99;
- DeleteTenantUser = 100;
+ TenantGetUserInfo = 98;
+ AssignUserToTenant = 99;
+ RevokeUserAccessToTenant = 100;
}
message OMRequest {
@@ -197,9 +197,9 @@ message OMRequest {
optional ModifyTenantRequest ModifyTenantRequest =
96;
optional DeleteTenantRequest DeleteTenantRequest =
97;
- optional AssignUserToTenantRequest AssignUserToTenantRequest =
98;
- optional ModifyTenantUserRequest ModifyTenantUserRequest =
99;
- optional DeleteTenantUserRequest DeleteTenantUserRequest =
100;
+ optional TenantGetUserInfoRequest TenantGetUserInfoRequest =
98;
+ optional AssignUserToTenantRequest AssignUserToTenantRequest =
99;
+ optional RevokeUserAccessToTenantRequest RevokeUserAccessToTenantRequest =
100;
}
message OMResponse {
@@ -282,11 +282,9 @@ message OMResponse {
optional ModifyTenantResponse ModifyTenantResponse =
96;
optional DeleteTenantResponse DeleteTenantResponse =
97;
- optional AssignUserToTenantResponse AssignUserToTenantResponse =
98;
- // TODO: Remove ModifyTenantUserResponse since it won't be useful anymore?
- optional ModifyTenantUserResponse ModifyTenantUserResponse =
99;
- // TODO: Rename this to UnassignUserFromTenant Response ?
- optional DeleteTenantUserResponse DeleteTenantUserResponse =
100;
+ optional TenantGetUserInfoResponse TenantGetUserInfoResponse =
98;
+ optional AssignUserToTenantResponse AssignUserToTenantResponse =
99;
+ optional RevokeUserAccessToTenantResponse RevokeUserAccessToTenantResponse
= 100;
}
enum Status {
@@ -1361,6 +1359,25 @@ message GetS3SecretResponse {
required S3Secret s3Secret = 2;
}
+message TenantUserInfo {
+ optional string userPrincipal = 1;
+ repeated TenantAccessIdInfo accessIdInfo = 2;
+}
+
+message TenantAccessIdInfo {
+ optional string accessId = 1;
+ optional string tenantName = 2;
+}
+
+message TenantGetUserInfoRequest {
+ optional string userPrincipal = 1;
+}
+
+message TenantGetUserInfoResponse {
+ optional bool success = 1;
+ optional TenantUserInfo tenantUserInfo = 2;
+}
+
message LayoutVersion {
required uint64 version = 1;
}
@@ -1371,6 +1388,7 @@ message RevokeS3SecretRequest {
message CreateTenantRequest {
optional string tenantName = 1;
+ optional string tenantDefaultPolicyName = 2;
}
message ModifyTenantRequest {
@@ -1387,12 +1405,9 @@ message AssignUserToTenantRequest {
optional string accessId = 3;
}
-message ModifyTenantUserRequest {
- optional string tenantUsername = 1;
-}
-
-message DeleteTenantUserRequest {
- optional string tenantUsername = 1;
+message RevokeUserAccessToTenantRequest {
+ optional string accessId = 1;
+ optional string tenantName = 2;
}
message CreateTenantResponse {
@@ -1412,11 +1427,7 @@ message AssignUserToTenantResponse {
optional S3Secret s3Secret = 2;
}
-message ModifyTenantUserResponse {
- optional bool success = 1;
-}
-
-message DeleteTenantUserResponse {
+message RevokeUserAccessToTenantResponse {
optional bool success = 1;
}
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManagerImpl.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManagerImpl.java
index 8237214..a444bb8 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManagerImpl.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OMMultiTenantManagerImpl.java
@@ -48,6 +48,7 @@ import
org.apache.hadoop.ozone.om.multitenant.AccountNameSpace;
import org.apache.hadoop.ozone.om.multitenant.BucketNameSpace;
import org.apache.hadoop.ozone.om.multitenant.CephCompatibleTenantImpl;
import org.apache.hadoop.ozone.om.multitenant.MultiTenantAccessAuthorizer;
+import
org.apache.hadoop.ozone.om.multitenant.MultiTenantAccessAuthorizerDummyPlugin;
import
org.apache.hadoop.ozone.om.multitenant.MultiTenantAccessAuthorizerRangerPlugin;
import org.apache.hadoop.ozone.om.multitenant.OzoneMultiTenantPrincipal;
import org.apache.hadoop.ozone.om.multitenant.RangerAccessPolicy;
@@ -70,6 +71,12 @@ public class OMMultiTenantManagerImpl implements
OMMultiTenantManager {
private static final Logger LOG =
LoggerFactory.getLogger(OMMultiTenantManagerImpl.class);
+ // TODO: Remove when proper testing infra is deployed.
+ // Internal dev flag to skip Ranger communication.
+ public static final String OZONE_OM_TENANT_DEV_SKIP_RANGER =
+ "ozone.om.tenant.dev.skip.ranger";
+ private final boolean devSkipRanger;
+
private MultiTenantAccessAuthorizer authorizer;
private OMMetadataManager omMetadataManager;
private OzoneConfiguration conf;
@@ -129,12 +136,18 @@ public class OMMultiTenantManagerImpl implements
OMMultiTenantManager {
controlPathLock = new ReentrantReadWriteLock();
omMetadataManager = mgr;
+
+ devSkipRanger = conf.getBoolean(OZONE_OM_TENANT_DEV_SKIP_RANGER, false);
start(conf);
}
@Override
public void start(OzoneConfiguration configuration) throws IOException {
- authorizer = new MultiTenantAccessAuthorizerRangerPlugin();
+ if (devSkipRanger) {
+ authorizer = new MultiTenantAccessAuthorizerDummyPlugin();
+ } else {
+ authorizer = new MultiTenantAccessAuthorizerRangerPlugin();
+ }
authorizer.init(configuration);
}
@@ -247,6 +260,7 @@ public class OMMultiTenantManagerImpl implements
OMMultiTenantManager {
@Override
public Tenant getTenantInfo(String tenantID) throws IOException {
+ // TODO:Should read from DB. Ditch the in-memory maps.
if (!inMemoryTenantNameToTenantInfoMap.containsKey(tenantID)) {
return null;
}
@@ -266,6 +280,8 @@ public class OMMultiTenantManagerImpl implements
OMMultiTenantManager {
@Override
public void destroyTenant(Tenant tenant) throws Exception {
+ // TODO: Make sure this is idempotent. This can be called by ALL 3 OMs
+ // in the case of a createTenant checkAcl failure for instance.
try {
controlPathLock.writeLock().lock();
for (AccessPolicy policy : tenant.getTenantAccessPolicies()) {
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java
index 69ef912..3e0fe6c 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmMetadataManagerImpl.java
@@ -141,7 +141,7 @@ public class OmMetadataManagerImpl implements
OMMetadataManager {
* |----------------------------------------------------------------------|
* | principalToAccessIdsTable | Principal -> OmDBKerberosPrincipalInfo |
* |----------------------------------------------------------------------|
- * | tenantStateTable | accessId -> OmDBTenantInfo |
+ * | tenantStateTable | tenant name -> OmDBTenantInfo |
* |----------------------------------------------------------------------|
* | tenantGroupTable | accessId -> [tenant group A, B, ...] |
* |----------------------------------------------------------------------|
@@ -563,16 +563,17 @@ public class OmMetadataManagerImpl implements
OMMetadataManager {
checkTableStatus(metaTable, META_TABLE);
// tenant user name -> tenant name string
+ // TODO: Unused. Remove.
tenantUserTable = this.store.getTable(TENANT_USER_TABLE,
String.class, String.class);
checkTableStatus(tenantUserTable, TENANT_USER_TABLE);
- // tenantId -> OmDBAccessIdInfo (tenantId, secret, Kerberos principal)
+ // accessId -> OmDBAccessIdInfo (tenantId, secret, Kerberos principal)
tenantAccessIdTable = this.store.getTable(TENANT_ACCESS_ID_TABLE,
String.class, OmDBAccessIdInfo.class);
checkTableStatus(tenantAccessIdTable, TENANT_ACCESS_ID_TABLE);
- // Kerberos principal -> OmDBKerberosPrincipalInfo (A list of accessIds)
+ // User principal -> OmDBKerberosPrincipalInfo (A list of accessIds)
principalToAccessIdsTable = this.store.getTable(
PRINCIPAL_TO_ACCESS_IDS_TABLE,
String.class, OmDBKerberosPrincipalInfo.class);
@@ -583,13 +584,12 @@ public class OmMetadataManagerImpl implements
OMMetadataManager {
String.class, OmDBTenantInfo.class);
checkTableStatus(tenantStateTable, TENANT_STATE_TABLE);
- // tenant user name -> list of tenant groups the user belongs to
+ // accessId -> list of tenant groups the user belongs to
tenantGroupTable = this.store.getTable(TENANT_GROUP_TABLE,
String.class, String.class /* TODO: Use custom list */);
checkTableStatus(tenantGroupTable, TENANT_GROUP_TABLE);
- // tenant user name -> list of roles in a tenant. e.g. admin for "finance"
- // TODO: Placeholder. Unused in the prototype.
+ // accessId -> list of roles in a tenant. e.g. admin for "finance"
tenantRoleTable = this.store.getTable(TENANT_ROLE_TABLE,
String.class, String.class /* TODO: Use custom list */);
checkTableStatus(tenantRoleTable, TENANT_ROLE_TABLE);
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
index e7d8931..0d1bb0d 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OzoneManager.java
@@ -117,6 +117,8 @@ import org.apache.hadoop.ozone.om.helpers.OMNodeDetails;
import org.apache.hadoop.ozone.om.helpers.DBUpdates;
import org.apache.hadoop.ozone.om.helpers.OmBucketArgs;
import org.apache.hadoop.ozone.om.helpers.OmBucketInfo;
+import org.apache.hadoop.ozone.om.helpers.OmDBAccessIdInfo;
+import org.apache.hadoop.ozone.om.helpers.OmDBKerberosPrincipalInfo;
import org.apache.hadoop.ozone.om.helpers.OmDeleteKeys;
import org.apache.hadoop.ozone.om.helpers.OmKeyArgs;
import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
@@ -135,6 +137,7 @@ import org.apache.hadoop.ozone.om.helpers.RepeatedOmKeyInfo;
import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
import org.apache.hadoop.ozone.om.helpers.ServiceInfo;
import org.apache.hadoop.ozone.om.helpers.ServiceInfoEx;
+import org.apache.hadoop.ozone.om.helpers.TenantUserInfoValue;
import org.apache.hadoop.ozone.om.protocol.OMInterServiceProtocol;
import
org.apache.hadoop.ozone.om.protocolPB.OMInterServiceProtocolClientSideImpl;
import org.apache.hadoop.ozone.om.protocol.OzoneManagerProtocol;
@@ -155,6 +158,7 @@ import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.KeyArgs
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRoleInfo;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OzoneAclInfo;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ServicePort;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantAccessIdInfo;
import org.apache.hadoop.ozone.protocolPB.OMInterServiceProtocolServerSideImpl;
import
org.apache.hadoop.ozone.storage.proto.OzoneManagerStorageProtos.PersistedUserVolumeInfo;
import
org.apache.hadoop.ozone.protocolPB.OzoneManagerProtocolServerSideTranslatorPB;
@@ -3084,7 +3088,55 @@ public final class OzoneManager extends
ServiceRuntimeInfoImpl
"non-Ratis assignUserToTenant() is not implemented");
}
- // TODO: modify, delete
+ /**
+ * Tenant get user info.
+ */
+ public TenantUserInfoValue tenantGetUserInfo(String userPrincipal)
+ throws IOException {
+
+ if (StringUtils.isEmpty(userPrincipal)) {
+ return null;
+ }
+
+ final List<TenantAccessIdInfo> accessIdInfoList = new ArrayList<>();
+
+ // Retrieve a list of accessIds associates to this user principal
+ final OmDBKerberosPrincipalInfo kerberosPrincipalInfo =
+ metadataManager.getPrincipalToAccessIdsTable().get(userPrincipal);
+ if (kerberosPrincipalInfo == null) {
+ return null;
+ }
+ final Set<String> accessIds = kerberosPrincipalInfo.getAccessIds();
+
+ final Map<String, String> auditMap = new LinkedHashMap<>();
+ auditMap.put(OzoneConsts.TENANT, userPrincipal);
+
+ accessIds.forEach(accessId -> {
+ try {
+ // Use get() intentionally, which throws if entry doesn't exist in
table
+ final OmDBAccessIdInfo accessIdInfo =
+ metadataManager.getTenantAccessIdTable().get(accessId);
+ // Sanity check
+ assert(accessIdInfo.getKerberosPrincipal().equals(userPrincipal));
+ // Build TenantAccessIdInfo instances from accessId and tenantName
+ final String tenantName = accessIdInfo.getTenantId();
+ accessIdInfoList.add(TenantAccessIdInfo.newBuilder()
+ .setAccessId(accessId)
+ .setTenantName(tenantName)
+ .build());
+ } catch (IOException e) {
+ LOG.error("Found potential DB consistency issue! "
+ + "accessId '" + "' is supposed to exist in TenantAccessIdTable.");
+ AUDIT.logWriteFailure(buildAuditMessageForFailure(
+ OMAction.TENANT_GET_USER_INFO, auditMap, e));
+ }
+ });
+
+ AUDIT.logReadSuccess(buildAuditMessageForSuccess(
+ OMAction.TENANT_GET_USER_INFO, auditMap));
+
+ return new TenantUserInfoValue(userPrincipal, accessIdInfoList);
+ }
@Override
/**
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java
index 0a8c3dd..ec33456 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/ratis/utils/OzoneManagerRatisUtils.java
@@ -83,8 +83,7 @@ import
org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantCreateRequest;
import org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantDeleteRequest;
import org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantModifyRequest;
import
org.apache.hadoop.ozone.om.request.s3.tenant.OMAssignUserToTenantRequest;
-import org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantUserDeleteRequest;
-import org.apache.hadoop.ozone.om.request.s3.tenant.OMTenantUserModifyRequest;
+import
org.apache.hadoop.ozone.om.request.s3.tenant.OMRevokeUserAccessToTenantRequest;
import
org.apache.hadoop.ozone.om.request.security.OMCancelDelegationTokenRequest;
import org.apache.hadoop.ozone.om.request.security.OMGetDelegationTokenRequest;
import
org.apache.hadoop.ozone.om.request.security.OMRenewDelegationTokenRequest;
@@ -266,10 +265,8 @@ public final class OzoneManagerRatisUtils {
return new OMTenantDeleteRequest(omRequest);
case AssignUserToTenant:
return new OMAssignUserToTenantRequest(omRequest);
- case ModifyTenantUser:
- return new OMTenantUserModifyRequest(omRequest);
- case DeleteTenantUser:
- return new OMTenantUserDeleteRequest(omRequest);
+ case RevokeUserAccessToTenant:
+ return new OMRevokeUserAccessToTenantRequest(omRequest);
default:
throw new IllegalStateException("Unrecognized write command " +
"type request" + cmdType);
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMAssignUserToTenantRequest.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMAssignUserToTenantRequest.java
index 31a9b8f..5f2f489 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMAssignUserToTenantRequest.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMAssignUserToTenantRequest.java
@@ -33,10 +33,9 @@ import
org.apache.hadoop.ozone.om.helpers.OmDBKerberosPrincipalInfo;
import org.apache.hadoop.ozone.om.helpers.S3SecretValue;
import org.apache.hadoop.ozone.om.multitenant.OzoneMultiTenantPrincipal;
import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
+import org.apache.hadoop.ozone.om.request.OMClientRequest;
import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
-import org.apache.hadoop.ozone.om.request.volume.OMVolumeRequest;
import org.apache.hadoop.ozone.om.response.OMClientResponse;
-import org.apache.hadoop.ozone.om.response.s3.tenant.OMTenantCreateResponse;
import
org.apache.hadoop.ozone.om.response.s3.tenant.OMAssignUserToTenantResponse;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AssignUserToTenantRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.AssignUserToTenantResponse;
@@ -96,8 +95,8 @@ import static
org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_L
/**
* Handles OMAssignUserToTenantRequest.
*/
-public class OMAssignUserToTenantRequest extends OMVolumeRequest {
- private static final Logger LOG =
+public class OMAssignUserToTenantRequest extends OMClientRequest {
+ public static final Logger LOG =
LoggerFactory.getLogger(OMAssignUserToTenantRequest.class);
public OMAssignUserToTenantRequest(OMRequest omRequest) {
@@ -233,9 +232,10 @@ public class OMAssignUserToTenantRequest extends
OMVolumeRequest {
// Inform MultiTenantManager of user assignment so it could
// initialize some policies in Ranger.
// TODO: Is userId from MultiTenantManager still useful?
+ // TODO: Move this to preExecute as well.
userId = ozoneManager.getMultiTenantManager()
.assignUserToTenant(tenantName, accessId);
- LOG.info("userId = {}", userId);
+ LOG.debug("userId = {}", userId);
// Add to tenantAccessIdTable
final OmDBAccessIdInfo omDBAccessIdInfo = new OmDBAccessIdInfo.Builder()
@@ -291,7 +291,7 @@ public class OMAssignUserToTenantRequest extends
OMVolumeRequest {
// Set response success flag to false
omResponse.setAssignUserToTenantResponse(
AssignUserToTenantResponse.newBuilder().setSuccess(false).build());
- omClientResponse = new OMTenantCreateResponse(
+ omClientResponse = new OMAssignUserToTenantResponse(
createErrorOMResponse(omResponse, ex));
} finally {
if (omClientResponse != null) {
@@ -313,11 +313,11 @@ public class OMAssignUserToTenantRequest extends
OMVolumeRequest {
getOmRequest().getUserInfo()));
if (exception == null) {
- LOG.info("Assigned user '{}' to tenant '{}' under accessId '{}'",
+ LOG.info("Assigned user '{}' to tenant '{}' with accessId '{}'",
principal, tenantName, accessId);
// TODO: omMetrics.incNumTenantUsers()
} else {
- LOG.error("Failed to assign '{}' to tenant '{}' under accessId '{}': {}",
+ LOG.error("Failed to assign '{}' to tenant '{}' with accessId '{}': {}",
principal, tenantName, accessId, exception.getMessage());
// TODO: Check if the exception message is sufficient.
// TODO: omMetrics.incNumTenantUserCreateFails()
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantUserDeleteRequest.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMRevokeUserAccessToTenantRequest.java
similarity index 92%
rename from
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantUserDeleteRequest.java
rename to
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMRevokeUserAccessToTenantRequest.java
index f2ea962..c542304 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantUserDeleteRequest.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMRevokeUserAccessToTenantRequest.java
@@ -29,9 +29,9 @@ import java.io.IOException;
/**
* Handles OMTenantUserModify request.
*/
-public class OMTenantUserDeleteRequest extends OMVolumeRequest {
+public class OMRevokeUserAccessToTenantRequest extends OMVolumeRequest {
- public OMTenantUserDeleteRequest(OMRequest omRequest) {
+ public OMRevokeUserAccessToTenantRequest(OMRequest omRequest) {
super(omRequest);
}
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 86ef85f..4cd62ee 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
@@ -21,6 +21,7 @@ package org.apache.hadoop.ozone.om.request.s3.tenant;
import com.google.common.base.Optional;
import org.apache.hadoop.hdds.utils.db.cache.CacheKey;
import org.apache.hadoop.hdds.utils.db.cache.CacheValue;
+import org.apache.hadoop.ipc.ProtobufRpcEngine;
import org.apache.hadoop.ozone.OmUtils;
import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.audit.OMAction;
@@ -29,6 +30,7 @@ import org.apache.hadoop.ozone.om.OzoneManager;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.helpers.OmDBTenantInfo;
import org.apache.hadoop.ozone.om.helpers.OmVolumeArgs;
+import org.apache.hadoop.ozone.om.multitenant.AccessPolicy;
import org.apache.hadoop.ozone.om.multitenant.Tenant;
import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
import org.apache.hadoop.ozone.om.request.util.OmResponseUtil;
@@ -44,6 +46,7 @@ import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.VolumeI
import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
import org.apache.hadoop.ozone.security.acl.OzoneObj;
import
org.apache.hadoop.ozone.storage.proto.OzoneManagerStorageProtos.PersistedUserVolumeInfo;
+import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.util.Time;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -53,6 +56,8 @@ import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
+import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.TENANT_ALREADY_EXISTS;
+import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.VOLUME_ALREADY_EXISTS;
import static
org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.USER_LOCK;
import static
org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_LOCK;
@@ -89,9 +94,12 @@ import static
org.apache.hadoop.ozone.om.lock.OzoneManagerLock.Resource.VOLUME_L
/**
* Handles OMTenantCreate request.
+ *
+ * Extends OMVolumeRequest but not OMClientRequest since tenant creation
+ * involves volume creation.
*/
public class OMTenantCreateRequest extends OMVolumeRequest {
- private static final Logger LOG =
+ public static final Logger LOG =
LoggerFactory.getLogger(OMTenantCreateRequest.class);
public OMTenantCreateRequest(OMRequest omRequest) {
@@ -115,7 +123,7 @@ public class OMTenantCreateRequest extends OMVolumeRequest {
.isExist(tenantName)) {
LOG.debug("tenant: {} already exists", tenantName);
throw new OMException("Tenant already exists",
- OMException.ResultCodes.TENANT_ALREADY_EXISTS);
+ TENANT_ALREADY_EXISTS);
}
// getUserName returns:
@@ -123,7 +131,11 @@ public class OMTenantCreateRequest extends OMVolumeRequest
{
// - User's login name when security is not enabled
// - AWS_ACCESS_KEY_ID if the original request comes from S3 Gateway.
// Not Applicable to TenantCreateRequest.
- final String owner = getOmRequest().getUserInfo().getUserName();
+ final UserGroupInformation ugi = ProtobufRpcEngine.Server.getRemoteUser();
+ // getShortUserName here follows RpcClient#createVolume
+ // A caveat is that this assumes OM's auth_to_local is the same as
+ // the client's. Maybe move this logic to the client and pass VolumeArgs?
+ final String owner = ugi.getShortUserName();
final String volumeName = tenantName; // TODO: Configurable
final VolumeInfo volumeInfo = VolumeInfo.newBuilder()
.setVolume(volumeName)
@@ -133,6 +145,8 @@ public class OMTenantCreateRequest extends OMVolumeRequest {
// Verify volume name
OmUtils.validateVolumeName(volumeInfo.getVolume());
+ // TODO: Shall we check volume existence here as well?
+
// Generate volume modification time
long initialTime = Time.now();
final VolumeInfo updatedVolumeInfo = volumeInfo.toBuilder()
@@ -142,11 +156,19 @@ public class OMTenantCreateRequest extends
OMVolumeRequest {
// If we fail after pre-execute. handleRequestFailure() callback
// would clean up any state maintained by the getMultiTenantManager.
- ozoneManager.getMultiTenantManager().createTenant(tenantName);
+ final Tenant tenant =
+ ozoneManager.getMultiTenantManager().createTenant(tenantName);
+
+ // Get the tenant default policy, pass this along
+ final String tenantDefaultPolicies = tenant.getTenantAccessPolicies()
+ .stream().map(AccessPolicy::getPolicyID)
+ .collect(Collectors.joining(","));
final OMRequest.Builder omRequestBuilder = getOmRequest().toBuilder()
.setCreateTenantRequest(
- CreateTenantRequest.newBuilder().setTenantName(tenantName))
+ CreateTenantRequest.newBuilder()
+ .setTenantDefaultPolicyName(tenantDefaultPolicies)
+ .setTenantName(tenantName))
.setCreateVolumeRequest(
CreateVolumeRequest.newBuilder().setVolumeInfo(updatedVolumeInfo))
.setUserInfo(getUserInfo())
@@ -165,7 +187,7 @@ public class OMTenantCreateRequest extends OMVolumeRequest {
CreateTenantRequest request = getOmRequest().getCreateTenantRequest();
try {
- Tenant tenant = ozoneManager.getMultiTenantManager()
+ final Tenant tenant = ozoneManager.getMultiTenantManager()
.getTenantInfo(request.getTenantName());
// Cleanup any state maintained by OMMultiTenantManager
if (tenant != null) {
@@ -179,6 +201,7 @@ public class OMTenantCreateRequest extends OMVolumeRequest {
}
@Override
+ @SuppressWarnings("methodlength")
public OMClientResponse validateAndUpdateCache(
OzoneManager ozoneManager, long transactionLogIndex,
OzoneManagerDoubleBufferHelper ozoneManagerDoubleBufferHelper) {
@@ -193,13 +216,18 @@ public class OMTenantCreateRequest extends
OMVolumeRequest {
OMMetadataManager omMetadataManager = ozoneManager.getMetadataManager();
CreateTenantRequest request = getOmRequest().getCreateTenantRequest();
final String tenantName = request.getTenantName();
- Tenant tenant = null;
final VolumeInfo volumeInfo =
getOmRequest().getCreateVolumeRequest().getVolumeInfo();
final String volumeName = volumeInfo.getVolume();
final String dbVolumeKey = omMetadataManager.getVolumeKey(volumeName);
IOException exception = null;
- LOG.info("tenant: {} create Request", tenantName);
+
+ final String tenantDefaultPolicies = request.getTenantDefaultPolicyName();
+ assert(tenantDefaultPolicies != null);
+
+ LOG.debug("Processing tenant '{}' create request", tenantName);
+ LOG.debug("tenantDefaultPolicies = {}", tenantDefaultPolicies);
+
try {
// Check ACL: requires volume create permission. TODO: tenant create
perm?
if (ozoneManager.getAclsEnabled()) {
@@ -213,14 +241,12 @@ public class OMTenantCreateRequest extends
OMVolumeRequest {
// Check volume existence
if (omMetadataManager.getVolumeTable().isExist(volumeName)) {
LOG.debug("volume: {} already exists", volumeName);
- throw new OMException("Volume already exists",
- OMException.ResultCodes.VOLUME_ALREADY_EXISTS);
+ throw new OMException("Volume already exists", VOLUME_ALREADY_EXISTS);
}
// Check tenant existence in tenantStateTable
if (omMetadataManager.getTenantStateTable().isExist(tenantName)) {
LOG.debug("tenant: {} already exists", tenantName);
- throw new OMException("Tenant already exists",
- OMException.ResultCodes.TENANT_ALREADY_EXISTS);
+ throw new OMException("Tenant already exists", TENANT_ALREADY_EXISTS);
}
// Add to tenantStateTable. Redundant assignment for clarity
@@ -237,13 +263,6 @@ public class OMTenantCreateRequest extends OMVolumeRequest
{
new CacheKey<>(tenantName),
new CacheValue<>(Optional.of(omDBTenantInfo), transactionLogIndex));
-
- tenant = ozoneManager.getMultiTenantManager()
- .getTenantInfo(tenantName);
- final String tenantDefaultPolicies = tenant.getTenantAccessPolicies()
- .stream().map(e->e.getPolicyID())
- .collect(Collectors.joining(","));
-
// Add to tenantPolicyTable
omMetadataManager.getTenantPolicyTable().addCacheEntry(
new CacheKey<>(userPolicyGroupName),
@@ -284,21 +303,29 @@ public class OMTenantCreateRequest extends
OMVolumeRequest {
omResponse.build(),
omVolumeArgs, volumeList,
omDBTenantInfo, tenantDefaultPolicies, bucketPolicyId);
+
} catch (IOException ex) {
- exception = ex;
- if (tenant != null) {
- try {
- ozoneManager.getMultiTenantManager().destroyTenant(tenant);
- } catch (Exception e) {
- // TODO: Ignore for now. Multi-Tenant Manager is responsible for
- // cleaning up stale state eventually.
+ // Error handling. Clean up Ranger policies when necessary.
+ if (ex instanceof OMException) {
+ final OMException omEx = (OMException) ex;
+ if (!omEx.getResult().equals(VOLUME_ALREADY_EXISTS) &&
+ !omEx.getResult().equals(TENANT_ALREADY_EXISTS)) {
+ handleRequestFailure(ozoneManager);
}
+ // Do NOT perform any clean-up if the exception is a result of
+ // volume name or tenant name already existing.
+ // Otherwise in a race condition a late-comer could wipe the
+ // policies of an existing tenant from Ranger.
+ } else {
+ // ALL OMs should proactively call the clean-up handler in other cases
+ handleRequestFailure(ozoneManager);
}
- // Set response success flag to false
+ // Prepare omClientResponse
omResponse.setCreateTenantResponse(
CreateTenantResponse.newBuilder().setSuccess(false).build());
omClientResponse = new OMTenantCreateResponse(
createErrorOMResponse(omResponse, ex));
+ exception = ex;
} finally {
if (omClientResponse != null) {
omClientResponse.setFlushFuture(ozoneManagerDoubleBufferHelper
@@ -320,10 +347,10 @@ public class OMTenantCreateRequest extends
OMVolumeRequest {
getOmRequest().getUserInfo()));
if (exception == null) {
- LOG.info("Created tenant: {}, and volume: {}", tenantName, volumeName);
+ LOG.info("Created tenant '{}' and volume '{}'", tenantName, volumeName);
// TODO: omMetrics.incNumTenants()
} else {
- LOG.error("Failed to create tenant: {}", tenantName, exception);
+ LOG.error("Failed to create tenant '{}'", tenantName, exception);
// TODO: omMetrics.incNumTenantCreateFails()
}
return omClientResponse;
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantUserModifyRequest.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantUserModifyRequest.java
deleted file mode 100644
index 85957ff..0000000
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/s3/tenant/OMTenantUserModifyRequest.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-package org.apache.hadoop.ozone.om.request.s3.tenant;
-
-import org.apache.hadoop.ozone.om.OzoneManager;
-import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerDoubleBufferHelper;
-import org.apache.hadoop.ozone.om.request.volume.OMVolumeRequest;
-import org.apache.hadoop.ozone.om.response.OMClientResponse;
-import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
-
-import java.io.IOException;
-
-/**
- * Handles OMTenantUserModify request.
- */
-public class OMTenantUserModifyRequest extends OMVolumeRequest {
-
- public OMTenantUserModifyRequest(OMRequest omRequest) {
- super(omRequest);
- }
-
- @Override
- public OMRequest preExecute(OzoneManager ozoneManager) throws IOException {
- return getOmRequest();
- }
-
- @Override
- public OMClientResponse validateAndUpdateCache(
- OzoneManager ozoneManager, long transactionLogIndex,
- OzoneManagerDoubleBufferHelper ozoneManagerDoubleBufferHelper) {
-
- return null;
- }
-}
diff --git
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java
index 0295a5b..b3ead46 100644
---
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java
+++
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/protocolPB/OzoneManagerRequestHandler.java
@@ -43,6 +43,7 @@ import org.apache.hadoop.ozone.om.helpers.OzoneFileStatus;
import org.apache.hadoop.ozone.om.helpers.RepeatedOmKeyInfo;
import org.apache.hadoop.ozone.om.helpers.ServiceInfo;
import org.apache.hadoop.ozone.om.helpers.ServiceInfoEx;
+import org.apache.hadoop.ozone.om.helpers.TenantUserInfoValue;
import org.apache.hadoop.ozone.om.ratis.OzoneManagerDoubleBuffer;
import org.apache.hadoop.ozone.om.ratis.utils.OzoneManagerRatisUtils;
import org.apache.hadoop.ozone.om.request.OMClientRequest;
@@ -80,6 +81,8 @@ import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Prepare
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ServiceListRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ServiceListResponse;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantGetUserInfoRequest;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantGetUserInfoResponse;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type;
import org.apache.hadoop.ozone.security.acl.OzoneObjInfo;
@@ -221,6 +224,11 @@ public class OzoneManagerRequestHandler implements
RequestHandler {
PrepareStatusResponse prepareStatusResponse = getPrepareStatus();
responseBuilder.setPrepareStatusResponse(prepareStatusResponse);
break;
+ case TenantGetUserInfo:
+ TenantGetUserInfoResponse getUserInfoResponse = tenantGetUserInfo(
+ request.getTenantGetUserInfoRequest());
+ responseBuilder.setTenantGetUserInfoResponse(getUserInfoResponse);
+ break;
default:
responseBuilder.setSuccess(false);
responseBuilder.setMessage("Unrecognized Command Type: " + cmdType);
@@ -345,6 +353,23 @@ public class OzoneManagerRequestHandler implements
RequestHandler {
return resp.build();
}
+ private TenantGetUserInfoResponse tenantGetUserInfo(
+ TenantGetUserInfoRequest request) throws IOException {
+ final TenantGetUserInfoResponse.Builder resp =
+ TenantGetUserInfoResponse.newBuilder();
+ final String userPrincipal = request.getUserPrincipal();
+
+ TenantUserInfoValue ret = impl.tenantGetUserInfo(userPrincipal);
+ if (ret != null) {
+ resp.setSuccess(true);
+ resp.setTenantUserInfo(ret.getProtobuf());
+ } else {
+ resp.setSuccess(false);
+ }
+
+ return resp.build();
+ }
+
private ListVolumeResponse listVolumes(ListVolumeRequest request)
throws IOException {
ListVolumeResponse.Builder resp = ListVolumeResponse.newBuilder();
diff --git
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/s3/security/TestS3GetSecretRequest.java
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/s3/security/TestS3GetSecretRequest.java
index d8f67e9..0fe87d8 100644
---
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/s3/security/TestS3GetSecretRequest.java
+++
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/s3/security/TestS3GetSecretRequest.java
@@ -58,6 +58,7 @@ import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.UUID;
import static
org.apache.hadoop.ozone.OzoneConsts.TENANT_NAME_USER_NAME_DELIMITER;
@@ -134,6 +135,9 @@ public class TestS3GetSecretRequest {
tenant = mock(Tenant.class);
when(omMultiTenantManager.getTenantInfo(TENANT_NAME)).thenReturn(tenant);
when(ozoneManager.getMultiTenantManager()).thenReturn(omMultiTenantManager);
+
+ when(tenant.getTenantAccessPolicies()).thenReturn(new ArrayList<>());
+ when(omMultiTenantManager.createTenant(TENANT_NAME)).thenReturn(tenant);
}
@After
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/S3Shell.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/S3Shell.java
index 29fc988..d85b0e9 100644
---
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/S3Shell.java
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/S3Shell.java
@@ -31,9 +31,7 @@ import picocli.CommandLine.Command;
description = "Shell for S3 specific operations",
subcommands = {
GetS3SecretHandler.class,
- RevokeS3SecretHandler.class,
- TenantCommands.class,
- TenantUserCommands.class
+ RevokeS3SecretHandler.class
})
public class S3Shell extends Shell {
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantCommands.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantCommands.java
deleted file mode 100644
index 790ffc8..0000000
---
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantCommands.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.hadoop.ozone.shell.s3;
-
-import org.apache.hadoop.hdds.cli.GenericParentCommand;
-import org.apache.hadoop.hdds.cli.HddsVersionProvider;
-import org.apache.hadoop.hdds.cli.MissingSubcommandException;
-import org.apache.hadoop.hdds.cli.SubcommandWithParent;
-import org.apache.hadoop.hdds.conf.OzoneConfiguration;
-import org.apache.hadoop.ozone.shell.OzoneShell;
-import org.apache.hadoop.ozone.shell.Shell;
-import org.kohsuke.MetaInfServices;
-import picocli.CommandLine;
-
-import java.util.concurrent.Callable;
-
-/**
- * Subcommand to group tenant related operations. TODO: see VolumeCommands
- */
[email protected](name = "tenant",
- description = "Tenant management",
- subcommands = {
- TenantCreateHandler.class,
- TenantModifyHandler.class,
- TenantDeleteHandler.class
- },
- mixinStandardHelpOptions = true,
- versionProvider = HddsVersionProvider.class)
-@MetaInfServices(SubcommandWithParent.class)
-public class TenantCommands implements GenericParentCommand, Callable<Void>,
- SubcommandWithParent {
-
- @CommandLine.ParentCommand
- private Shell shell;
-
- @Override
- public Void call() throws Exception {
- throw new MissingSubcommandException(
- this.shell.getCmd().getSubcommands().get("tenant"));
- }
-
- @Override
- public boolean isVerbose() {
- return shell.isVerbose();
- }
-
- @Override
- public OzoneConfiguration createOzoneConfiguration() {
- return shell.createOzoneConfiguration();
- }
-
- @Override
- public Class<?> getParentType() {
- return OzoneShell.class;
- }
-}
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/AssignUserToTenantHandler.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/AssignUserToTenantHandler.java
similarity index 93%
rename from
hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/AssignUserToTenantHandler.java
rename to
hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/AssignUserToTenantHandler.java
index 1c465d8..c5feee1 100644
---
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/AssignUserToTenantHandler.java
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/AssignUserToTenantHandler.java
@@ -15,7 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.hadoop.ozone.shell.s3;
+package org.apache.hadoop.ozone.shell.tenant;
import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.hdds.cli.GenericCli;
@@ -33,11 +33,11 @@ import java.util.List;
import static
org.apache.hadoop.ozone.OzoneConsts.TENANT_NAME_USER_NAME_DELIMITER;
/**
- * ozone s3 user assign.
+ * ozone tenant user assign.
*/
@CommandLine.Command(name = "assign",
description = "Assign user to tenant")
-public class AssignUserToTenantHandler extends S3Handler {
+public class AssignUserToTenantHandler extends TenantHandler {
@CommandLine.Spec
private CommandLine.Model.CommandSpec spec;
@@ -59,11 +59,6 @@ public class AssignUserToTenantHandler extends S3Handler {
// `s3 getsecret` and leak the secret if an admin isn't careful.
private String accessId;
- // TODO: support dry-run?
-// @CommandLine.Option(names = {"--dry-run"},
-// description = "Dry-run")
-// private boolean dryRun;
-
private boolean isEmptyList(List<String> list) {
return list == null || list.size() == 0;
}
@@ -104,7 +99,7 @@ public class AssignUserToTenantHandler extends S3Handler {
final S3SecretValue resp =
objStore.assignUserToTenant(principal, tenantName, accessId);
err().println("Assigned '" + principal + "' to '" + tenantName +
- "' under accessId '" + accessId + "'.");
+ "' with accessId '" + accessId + "'.");
out().println("export AWS_ACCESS_KEY_ID='" +
resp.getAwsAccessKey() + "'");
out().println("export AWS_SECRET_ACCESS_KEY='" +
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/GetUserInfoHandler.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/GetUserInfoHandler.java
new file mode 100644
index 0000000..44f0bda
--- /dev/null
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/GetUserInfoHandler.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.ozone.shell.tenant;
+
+import org.apache.hadoop.hdds.cli.GenericCli;
+import org.apache.hadoop.ozone.client.ObjectStore;
+import org.apache.hadoop.ozone.client.OzoneClient;
+import org.apache.hadoop.ozone.om.helpers.TenantUserInfoValue;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.TenantAccessIdInfo;
+import org.apache.hadoop.ozone.shell.OzoneAddress;
+import picocli.CommandLine;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * ozone tenant user info.
+ */
[email protected](name = "info",
+ description = "Get tenant related information of a user")
+public class GetUserInfoHandler extends TenantHandler {
+
+ @CommandLine.Spec
+ private CommandLine.Model.CommandSpec spec;
+
+ @CommandLine.Parameters(description = "List of user principal(s)")
+ private List<String> userPrincipals = new ArrayList<>();
+
+ private boolean isEmptyList(List<String> list) {
+ return list == null || list.size() == 0;
+ }
+
+ @Override
+ protected void execute(OzoneClient client, OzoneAddress address) {
+ final ObjectStore objStore = client.getObjectStore();
+
+ if (isEmptyList(userPrincipals)) {
+ GenericCli.missingSubcommand(spec);
+ return;
+ }
+
+ for (final String userPrincipal : userPrincipals) {
+ try {
+ final TenantUserInfoValue tenantUserInfo =
+ objStore.tenantGetUserInfo(userPrincipal);
+ List<TenantAccessIdInfo> accessIdInfoList =
+ tenantUserInfo.getAccessIdInfoList();
+ if (accessIdInfoList.size() == 0) {
+ err().println("User '" + userPrincipal +
+ "' is not assigned to any tenant.");
+ continue;
+ }
+ out().println("User '" + userPrincipal + "' is assigned to:");
+
+ for (TenantAccessIdInfo accessIdInfo : accessIdInfoList) {
+ out().println("- Tenant '" + accessIdInfo.getTenantName() +
+ "' with accessId '" + accessIdInfo.getAccessId() + "'");
+ }
+
+ out().println();
+ } catch (IOException e) {
+ err().println("Failed to GetUserInfo of user '" + userPrincipal
+ + "': " + e.getMessage());
+ }
+ }
+ }
+}
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantUserModifyHandler.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/RevokeUserAccessToTenantHandler.java
similarity index 82%
rename from
hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantUserModifyHandler.java
rename to
hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/RevokeUserAccessToTenantHandler.java
index 010cea0..0c4cdf7 100644
---
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantUserModifyHandler.java
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/RevokeUserAccessToTenantHandler.java
@@ -15,18 +15,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.hadoop.ozone.shell.s3;
+package org.apache.hadoop.ozone.shell.tenant;
import org.apache.hadoop.ozone.client.OzoneClient;
import org.apache.hadoop.ozone.shell.OzoneAddress;
import picocli.CommandLine;
/**
- * ozone s3 user modify.
+ * ozone tenant user revoke.
*/
[email protected](name = "modify",
- description = "Modify a tenant user")
-public class TenantUserModifyHandler extends S3Handler {
[email protected](name = "revoke",
+ description = "Revoke user access to tenant")
+public class RevokeUserAccessToTenantHandler extends TenantHandler {
@Override
protected void execute(OzoneClient client, OzoneAddress address) {
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantCreateHandler.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantCreateHandler.java
similarity index 86%
rename from
hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantCreateHandler.java
rename to
hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantCreateHandler.java
index 672832e..86d9fbe 100644
---
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantCreateHandler.java
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantCreateHandler.java
@@ -15,7 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.hadoop.ozone.shell.s3;
+package org.apache.hadoop.ozone.shell.tenant;
import org.apache.hadoop.hdds.cli.GenericCli;
import org.apache.hadoop.ozone.client.OzoneClient;
@@ -27,11 +27,11 @@ import java.util.ArrayList;
import java.util.List;
/**
- * ozone s3 tenant create.
+ * ozone tenant create.
*/
@CommandLine.Command(name = "create",
description = "Create one or more tenants")
-public class TenantCreateHandler extends S3Handler {
+public class TenantCreateHandler extends TenantHandler {
@CommandLine.Spec
private CommandLine.Model.CommandSpec spec;
@@ -45,9 +45,9 @@ public class TenantCreateHandler extends S3Handler {
for (String tenantName : tenants) {
try {
client.getObjectStore().createTenant(tenantName);
- out().println("Created tenant " + tenantName);
+ out().println("Created tenant '" + tenantName + "'.");
} catch (IOException e) {
- out().println("Failed to create tenant " + tenantName + ": " +
+ err().println("Failed to create tenant '" + tenantName + "': " +
e.getMessage());
}
}
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantDeleteHandler.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantDeleteHandler.java
similarity index 87%
rename from
hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantDeleteHandler.java
rename to
hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantDeleteHandler.java
index afbcd43..a1f2503 100644
---
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantDeleteHandler.java
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantDeleteHandler.java
@@ -15,21 +15,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.hadoop.ozone.shell.s3;
+package org.apache.hadoop.ozone.shell.tenant;
import org.apache.hadoop.ozone.client.OzoneClient;
import org.apache.hadoop.ozone.shell.OzoneAddress;
import picocli.CommandLine;
/**
- * ozone s3 tenant delete.
+ * ozone tenant delete.
*/
@CommandLine.Command(name = "delete",
description = "Delete a tenant")
-public class TenantDeleteHandler extends S3Handler {
+public class TenantDeleteHandler extends TenantHandler {
@Override
protected void execute(OzoneClient client, OzoneAddress address) {
- out().println("Not Implemented.");
+ err().println("Not Implemented.");
}
}
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantHandler.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantHandler.java
similarity index 58%
rename from
hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantHandler.java
rename to
hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantHandler.java
index 345409d..a76ab74 100644
---
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantHandler.java
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantHandler.java
@@ -15,25 +15,40 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.hadoop.ozone.shell.s3;
+package org.apache.hadoop.ozone.shell.tenant;
+import org.apache.hadoop.ozone.client.OzoneClient;
+import org.apache.hadoop.ozone.client.OzoneClientException;
import org.apache.hadoop.ozone.shell.Handler;
import org.apache.hadoop.ozone.shell.OzoneAddress;
-import org.apache.hadoop.ozone.shell.volume.VolumeUri;
import picocli.CommandLine;
+import java.io.IOException;
+
/**
* Base class for tenant command handlers.
*/
-// TODO: VolumeHandler
public abstract class TenantHandler extends Handler {
- @CommandLine.Mixin
- private VolumeUri address;
+ @CommandLine.Option(names = {"--om-service-id"},
+ required = false,
+ description = "Service ID is required when OM is running in HA" +
+ " cluster")
+ private String omServiceID;
+
+ public String getOmServiceID() {
+ return omServiceID;
+ }
+
+ @Override
+ protected OzoneAddress getAddress() throws OzoneClientException {
+ return new OzoneAddress();
+ }
@Override
- protected OzoneAddress getAddress() {
- return address.getValue();
+ protected OzoneClient createClient(OzoneAddress address)
+ throws IOException, OzoneClientException {
+ return address.createClientForS3Commands(getConf(), omServiceID);
}
}
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantModifyHandler.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantModifyHandler.java
similarity index 91%
rename from
hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantModifyHandler.java
rename to
hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantModifyHandler.java
index 3c19099..a3f1877 100644
---
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantModifyHandler.java
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantModifyHandler.java
@@ -15,7 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.hadoop.ozone.shell.s3;
+package org.apache.hadoop.ozone.shell.tenant;
import org.apache.hadoop.ozone.client.OzoneClient;
import org.apache.hadoop.ozone.shell.OzoneAddress;
@@ -26,7 +26,7 @@ import picocli.CommandLine;
*/
@CommandLine.Command(name = "modify",
description = "Modify a tenant")
-public class TenantModifyHandler extends S3Handler {
+public class TenantModifyHandler extends TenantHandler {
@Override
protected void execute(OzoneClient client, OzoneAddress address) {
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/S3Shell.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantShell.java
similarity index 70%
copy from
hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/S3Shell.java
copy to
hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantShell.java
index 29fc988..f26e874 100644
---
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/S3Shell.java
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantShell.java
@@ -15,32 +15,31 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.hadoop.ozone.shell.s3;
-
-import java.util.function.Supplier;
+package org.apache.hadoop.ozone.shell.tenant;
import org.apache.hadoop.hdds.tracing.TracingUtil;
import org.apache.hadoop.ozone.shell.Shell;
-
import picocli.CommandLine.Command;
+import java.util.function.Supplier;
+
/**
- * Shell for s3 related operations.
+ * Shell for multi-tenant related operations.
*/
-@Command(name = "ozone s3",
- description = "Shell for S3 specific operations",
+@Command(name = "ozone tenant",
+ description = "Shell for multi-tenant specific operations",
subcommands = {
- GetS3SecretHandler.class,
- RevokeS3SecretHandler.class,
- TenantCommands.class,
+ TenantCreateHandler.class,
+ TenantModifyHandler.class,
+ TenantDeleteHandler.class,
TenantUserCommands.class
})
-public class S3Shell extends Shell {
+public class TenantShell extends Shell {
@Override
public void execute(String[] argv) {
- TracingUtil.initTracing("s3shell", createOzoneConfiguration());
- TracingUtil.executeInNewSpan("s3shell",
+ TracingUtil.initTracing("tenant-shell", createOzoneConfiguration());
+ TracingUtil.executeInNewSpan("tenant-shell",
(Supplier<Void>) () -> {
super.execute(argv);
return null;
@@ -48,11 +47,11 @@ public class S3Shell extends Shell {
}
/**
- * Main for the S3Shell Command handling.
+ * Main for the TenantShell Command handling.
*
* @param argv - System Args Strings[]
*/
public static void main(String[] argv) {
- new S3Shell().run(argv);
+ new TenantShell().run(argv);
}
}
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantUserCommands.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantUserCommands.java
similarity index 90%
rename from
hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantUserCommands.java
rename to
hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantUserCommands.java
index 8db2484..49c58b5 100644
---
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantUserCommands.java
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/TenantUserCommands.java
@@ -15,7 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.hadoop.ozone.shell.s3;
+package org.apache.hadoop.ozone.shell.tenant;
import org.apache.hadoop.hdds.cli.GenericParentCommand;
import org.apache.hadoop.hdds.cli.HddsVersionProvider;
@@ -35,15 +35,15 @@ import java.util.concurrent.Callable;
@CommandLine.Command(name = "user",
description = "Tenant user management",
subcommands = {
+ GetUserInfoHandler.class,
AssignUserToTenantHandler.class,
- TenantUserModifyHandler.class,
- TenantUserDeleteHandler.class
+ RevokeUserAccessToTenantHandler.class
},
mixinStandardHelpOptions = true,
versionProvider = HddsVersionProvider.class)
@MetaInfServices(SubcommandWithParent.class)
-public class TenantUserCommands implements GenericParentCommand,
Callable<Void>,
- SubcommandWithParent{
+public class TenantUserCommands implements
+ GenericParentCommand, Callable<Void>, SubcommandWithParent {
@CommandLine.ParentCommand
private Shell shell;
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantUserDeleteHandler.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/package-info.java
similarity index 63%
rename from
hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantUserDeleteHandler.java
rename to
hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/package-info.java
index 3dff2d4..dabb6bd 100644
---
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/s3/TenantUserDeleteHandler.java
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/shell/tenant/package-info.java
@@ -15,21 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.hadoop.ozone.shell.s3;
-
-import org.apache.hadoop.ozone.client.OzoneClient;
-import org.apache.hadoop.ozone.shell.OzoneAddress;
-import picocli.CommandLine;
-
/**
- * ozone s3 user delete.
+ * Tenant commands for Ozone.
*/
[email protected](name = "delete",
- description = "Delete a tenant user")
-public class TenantUserDeleteHandler extends S3Handler {
-
- @Override
- protected void execute(OzoneClient client, OzoneAddress address) {
- out().println("Not Implemented.");
- }
-}
+package org.apache.hadoop.ozone.shell.tenant;
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]