blob: ae9487e2de7420ad3ce7aaf7be160ecca5a329bb [file] [log] [blame]
/**
* 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.yarn.server.resourcemanager;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.yarn.api.records.QueueACL;
import org.junit.Assert;
import org.apache.hadoop.security.authorize.AccessControlList;
import org.apache.hadoop.yarn.api.ApplicationClientProtocol;
import org.apache.hadoop.yarn.api.protocolrecords.GetApplicationReportRequest;
import org.apache.hadoop.yarn.api.protocolrecords.GetApplicationReportResponse;
import org.apache.hadoop.yarn.api.protocolrecords.GetNewApplicationRequest;
import org.apache.hadoop.yarn.api.protocolrecords.KillApplicationRequest;
import org.apache.hadoop.yarn.api.protocolrecords.SubmitApplicationRequest;
import org.apache.hadoop.yarn.api.records.ApplicationAccessType;
import org.apache.hadoop.yarn.api.records.ApplicationId;
import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext;
import org.apache.hadoop.yarn.api.records.ContainerLaunchContext;
import org.apache.hadoop.yarn.api.records.Resource;
import org.apache.hadoop.yarn.exceptions.YarnException;
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMAppState;
import org.apache.hadoop.yarn.server.utils.BuilderUtils;
import org.junit.After;
import org.junit.Test;
public abstract class QueueACLsTestBase extends ACLsTestBase {
protected static final String QUEUED = "D";
protected static final String QUEUED1 = "D1";
private static final String ALL_ACL = "*";
private static final String NONE_ACL = " ";
abstract public String getQueueD();
abstract public String getQueueD1();
abstract public void updateConfigWithDAndD1Queues(String rootAcl,
String queueDAcl, String queueD1Acl) throws IOException;
@After
public void tearDown() {
if (resourceManager != null) {
resourceManager.stop();
}
}
@Test
public void testApplicationACLs() throws Exception {
verifyKillAppSuccess(QUEUE_A_USER, QUEUE_A_USER, QUEUEA, true);
verifyKillAppSuccess(QUEUE_A_USER, QUEUE_A_ADMIN, QUEUEA, true);
verifyKillAppSuccess(QUEUE_A_USER, COMMON_USER, QUEUEA, true);
verifyKillAppSuccess(QUEUE_A_USER, ROOT_ADMIN, QUEUEA, true);
verifyKillAppFailure(QUEUE_A_USER, QUEUE_B_USER, QUEUEA, true);
verifyKillAppFailure(QUEUE_A_USER, QUEUE_B_ADMIN, QUEUEA, true);
verifyKillAppSuccess(QUEUE_B_USER, QUEUE_B_USER, QUEUEB, true);
verifyKillAppSuccess(QUEUE_B_USER, QUEUE_B_ADMIN, QUEUEB, true);
verifyKillAppSuccess(QUEUE_B_USER, COMMON_USER, QUEUEB, true);
verifyKillAppSuccess(QUEUE_B_USER, ROOT_ADMIN, QUEUEB, true);
verifyKillAppFailure(QUEUE_B_USER, QUEUE_A_USER, QUEUEB, true);
verifyKillAppFailure(QUEUE_B_USER, QUEUE_A_ADMIN, QUEUEB, true);
verifyKillAppSuccess(ROOT_ADMIN, ROOT_ADMIN, QUEUEA, false);
verifyKillAppSuccess(ROOT_ADMIN, ROOT_ADMIN, QUEUEB, false);
verifyGetClientAMToken(QUEUE_A_USER, ROOT_ADMIN, QUEUEA, true);
}
/**
* Test for the case when the following submit application
* and administer queue ACLs are defined:
* root: (none)
* D: * (all)
* D1: * (all)
* Expected result: the user will have access only to D and D1 queues.
* @throws IOException
*/
@Test
public void testQueueAclRestrictedRootACL() throws IOException {
updateConfigWithDAndD1Queues(NONE_ACL, ALL_ACL, ALL_ACL);
checkAccess(false, true, true);
}
/**
* Test for the case when the following submit application
* and administer queue ACLs are defined:
* root: (none)
* D: (none)
* D1: (none)
* Expected result: the user will have to none of the queues.
* @throws IOException
*/
@Test
public void testQueueAclNoAccess() throws IOException {
updateConfigWithDAndD1Queues(NONE_ACL, NONE_ACL, NONE_ACL);
checkAccess(false, false, false);
}
/**
* Test for the case when the following submit application
* and administer queue ACLs are defined:
* root: (none)
* D: * (all)
* D1: (none)
* Expected result: access to D1 will be permitted by root.D,
* so the user will be able to access queues D and D1.
* @throws IOException
*/
@Test
public void testQueueAclRestrictedRootAndD1() throws IOException {
updateConfigWithDAndD1Queues(NONE_ACL, ALL_ACL, NONE_ACL);
checkAccess(false, true, true);
}
/**
* Test for the case when the following submit application
* and administer queue ACLs are defined:
* root: (none)
* D: (none)
* D1: (all)
* Expected result: only queue D1 can be accessed.
* @throws IOException
*/
@Test
public void testQueueAclRestrictedRootAndD() throws IOException {
updateConfigWithDAndD1Queues(NONE_ACL, NONE_ACL, ALL_ACL);
checkAccess(false, false, true);
}
/**
* Test for the case when the following submit application
* and administer queue ACLs are defined:
* root: * (all)
* D: (none)
* D1: * (all)
* Expected result: access to D will be permitted from the root queue,
* so the user will be able to access queues root, D and D1.
* @throws IOException
*/
@Test
public void testQueueAclRestrictedD() throws IOException {
updateConfigWithDAndD1Queues(ALL_ACL, NONE_ACL, ALL_ACL);
checkAccess(true, true, true);
}
/**
* Test for the case when the following submit application
* and administer queue ACLs are defined:
* root: * (all)
* D: * (all)
* D1: (none)
* Expected result: access to D1 will be permitted from queue D,
* so the user will be able to access queues root, D and D1.
* @throws IOException
*/
@Test
public void testQueueAclRestrictedD1() throws IOException {
updateConfigWithDAndD1Queues(ALL_ACL, ALL_ACL, NONE_ACL);
checkAccess(true, true, true);
}
/**
* Test for the case when no ACLs are defined, so the default values are used
* Expected result: The default ACLs for the root queue is "*"(all) and for
* the other queues are " " (none), so the user will have access to all the
* queues because they will have permissions from the root.
*
* @throws IOException
*/
@Test
public void testQueueAclDefaultValues() throws IOException {
updateConfigWithDAndD1Queues(null, null, null);
checkAccess(true, true, true);
}
private void checkAccess(boolean rootAccess, boolean dAccess,
boolean d1Access)throws IOException {
checkAccess(rootAccess, "root");
checkAccess(dAccess, getQueueD());
checkAccess(d1Access, getQueueD1());
}
private void checkAccess(boolean access, String queueName)
throws IOException {
UserGroupInformation user = UserGroupInformation.getCurrentUser();
String failureMsg = "Wrong %s access to %s queue";
Assert.assertEquals(
String.format(failureMsg, QueueACL.ADMINISTER_QUEUE, queueName),
access, resourceManager.getResourceScheduler()
.checkAccess(user, QueueACL.ADMINISTER_QUEUE, queueName));
Assert.assertEquals(
String.format(failureMsg, QueueACL.SUBMIT_APPLICATIONS, queueName),
access, resourceManager.getResourceScheduler()
.checkAccess(user, QueueACL.SUBMIT_APPLICATIONS, queueName));
}
private void verifyGetClientAMToken(String submitter, String queueAdmin,
String queueName, boolean setupACLs) throws Exception {
ApplicationId applicationId =
submitAppAndGetAppId(submitter, queueName, setupACLs);
final GetApplicationReportRequest appReportRequest =
GetApplicationReportRequest.newInstance(applicationId);
ApplicationClientProtocol submitterClient = getRMClientForUser(submitter);
ApplicationClientProtocol adMinUserClient = getRMClientForUser(queueAdmin);
GetApplicationReportResponse submitterGetReport =
submitterClient.getApplicationReport(appReportRequest);
GetApplicationReportResponse adMinUserGetReport =
adMinUserClient.getApplicationReport(appReportRequest);
Assert.assertEquals(submitterGetReport.getApplicationReport()
.getClientToAMToken(), adMinUserGetReport.getApplicationReport()
.getClientToAMToken());
}
private void verifyKillAppFailure(String submitter, String killer,
String queueName, boolean setupACLs) throws Exception {
ApplicationId applicationId =
submitAppAndGetAppId(submitter, queueName, setupACLs);
final KillApplicationRequest finishAppRequest =
KillApplicationRequest.newInstance(applicationId);
ApplicationClientProtocol killerClient = getRMClientForUser(killer);
// Kill app as the killer
try {
killerClient.forceKillApplication(finishAppRequest);
Assert.fail("App killing by the enemy should fail!!");
} catch (YarnException e) {
LOG.info("Got exception while killing app as the enemy", e);
Assert.assertTrue(e.getMessage().contains(
"User " + killer + " cannot perform operation MODIFY_APP on "
+ applicationId));
}
getRMClientForUser(submitter).forceKillApplication(finishAppRequest);
}
private void verifyKillAppSuccess(String submitter, String killer,
String queueName, boolean setupACLs) throws Exception {
ApplicationId applicationId =
submitAppAndGetAppId(submitter, queueName, setupACLs);
final KillApplicationRequest finishAppRequest =
KillApplicationRequest.newInstance(applicationId);
ApplicationClientProtocol ownerClient = getRMClientForUser(killer);
// Kill app as killer
ownerClient.forceKillApplication(finishAppRequest);
resourceManager.waitForState(applicationId, RMAppState.KILLED);
}
private ApplicationId submitAppAndGetAppId(String submitter,
String queueName, boolean setupACLs) throws Exception {
GetNewApplicationRequest newAppRequest =
GetNewApplicationRequest.newInstance();
ApplicationClientProtocol submitterClient = getRMClientForUser(submitter);
ApplicationId applicationId =
submitterClient.getNewApplication(newAppRequest).getApplicationId();
Resource resource = BuilderUtils.newResource(1024, 1);
Map<ApplicationAccessType, String> acls = createACLs(submitter, setupACLs);
ContainerLaunchContext amContainerSpec =
ContainerLaunchContext.newInstance(null, null, null, null, null, acls);
ApplicationSubmissionContext appSubmissionContext =
ApplicationSubmissionContext.newInstance(applicationId,
"applicationName", queueName, null, amContainerSpec, false, true, 1,
resource, "applicationType");
appSubmissionContext.setApplicationId(applicationId);
appSubmissionContext.setQueue(queueName);
SubmitApplicationRequest submitRequest =
SubmitApplicationRequest.newInstance(appSubmissionContext);
submitterClient.submitApplication(submitRequest);
resourceManager.waitForState(applicationId, RMAppState.ACCEPTED);
return applicationId;
}
private Map<ApplicationAccessType, String> createACLs(String submitter,
boolean setupACLs) {
AccessControlList viewACL = new AccessControlList("");
AccessControlList modifyACL = new AccessControlList("");
if (setupACLs) {
viewACL.addUser(submitter);
viewACL.addUser(COMMON_USER);
modifyACL.addUser(submitter);
modifyACL.addUser(COMMON_USER);
}
Map<ApplicationAccessType, String> acls =
new HashMap<ApplicationAccessType, String>();
acls.put(ApplicationAccessType.VIEW_APP, viewACL.getAclString());
acls.put(ApplicationAccessType.MODIFY_APP, modifyACL.getAclString());
return acls;
}
}