blob: 37ff02d5039f6a11f5156a9a5e3b71b4cb42e3a1 [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 static junit.framework.TestCase.assertTrue;
import static org.apache.hadoop.yarn.exceptions.InvalidResourceRequestException.InvalidResourceType.GREATER_THEN_MAX_ALLOCATION;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import org.apache.hadoop.security.AccessControlException;
import org.apache.hadoop.test.GenericTestUtils;
import org.apache.hadoop.yarn.MockApps;
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.Priority;
import org.apache.hadoop.yarn.api.records.Resource;
import org.apache.hadoop.yarn.api.records.ResourceRequest;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.exceptions.InvalidResourceRequestException;
import org.apache.hadoop.yarn.exceptions.YarnException;
import org.apache.hadoop.yarn.server.resourcemanager.placement.ApplicationPlacementContext;
import org.apache.hadoop.yarn.server.resourcemanager.placement.PlacementManager;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.ResourceScheduler;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairSchedulerConfiguration;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair
.allocationfile.AllocationFileQueue;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair
.allocationfile.AllocationFileWriter;
import org.apache.hadoop.yarn.server.resourcemanager.security.ClientToAMTokenSecretManagerInRM;
import org.apache.hadoop.yarn.server.security.ApplicationACLsManager;
import org.apache.hadoop.yarn.util.Records;
import org.apache.hadoop.yarn.util.resource.Resources;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* Testing RMAppManager application submission with fair scheduler.
*/
public class TestAppManagerWithFairScheduler extends AppManagerTestBase {
private static final String TEST_FOLDER = "test-queues";
private static YarnConfiguration conf = new YarnConfiguration();
private PlacementManager placementMgr;
private TestRMAppManager rmAppManager;
private RMContext rmContext;
private static String allocFileName =
GenericTestUtils.getTestDir(TEST_FOLDER).getAbsolutePath();
@Before
public void setup() throws IOException {
// Basic config with one queue (override in test if needed)
AllocationFileWriter.create()
.addQueue(new AllocationFileQueue.Builder("test").build())
.writeToFile(allocFileName);
conf.setClass(YarnConfiguration.RM_SCHEDULER, FairScheduler.class,
ResourceScheduler.class);
conf.set(FairSchedulerConfiguration.ALLOCATION_FILE, allocFileName);
placementMgr = mock(PlacementManager.class);
MockRM mockRM = new MockRM(conf);
rmContext = mockRM.getRMContext();
rmContext.setQueuePlacementManager(placementMgr);
ApplicationMasterService masterService = new ApplicationMasterService(
rmContext, rmContext.getScheduler());
rmAppManager = new TestRMAppManager(rmContext,
new ClientToAMTokenSecretManagerInRM(), rmContext.getScheduler(),
masterService, new ApplicationACLsManager(conf), conf);
}
@After
public void teardown(){
File allocFile = GenericTestUtils.getTestDir(TEST_FOLDER);
allocFile.delete();
}
@Test
public void testQueueSubmitWithHighQueueContainerSize()
throws YarnException, IOException {
int maxAlloc =
YarnConfiguration.DEFAULT_RM_SCHEDULER_MINIMUM_ALLOCATION_MB;
// scheduler config with a limited queue
AllocationFileWriter.create()
.addQueue(new AllocationFileQueue.Builder("root")
.subQueue(new AllocationFileQueue.Builder("limited")
.maxContainerAllocation(maxAlloc + " mb 1 vcores")
.build())
.subQueue(new AllocationFileQueue.Builder("unlimited")
.build())
.build())
.writeToFile(allocFileName);
rmContext.getScheduler().reinitialize(conf, rmContext);
ApplicationId appId = MockApps.newAppID(1);
Resource res = Resources.createResource(maxAlloc + 1);
ApplicationSubmissionContext asContext = createAppSubmitCtx(appId, res);
// Submit to limited queue
when(placementMgr.placeApplication(any(), any()))
.thenReturn(new ApplicationPlacementContext("limited"));
try {
rmAppManager.submitApplication(asContext, "test");
Assert.fail("Test should fail on too high allocation!");
} catch (InvalidResourceRequestException e) {
Assert.assertEquals(GREATER_THEN_MAX_ALLOCATION,
e.getInvalidResourceType());
}
// submit same app but now place it in the unlimited queue
when(placementMgr.placeApplication(any(), any()))
.thenReturn(new ApplicationPlacementContext("root.unlimited"));
rmAppManager.submitApplication(asContext, "test");
}
@Test
public void testQueueSubmitWithPermissionLimits()
throws YarnException, IOException {
conf.set(YarnConfiguration.YARN_ACL_ENABLE, "true");
AllocationFileWriter.create()
.addQueue(new AllocationFileQueue.Builder("root")
.aclSubmitApps(" ")
.aclAdministerApps(" ")
.subQueue(new AllocationFileQueue.Builder("noaccess")
.build())
.subQueue(new AllocationFileQueue.Builder("submitonly")
.aclSubmitApps("test ")
.aclAdministerApps(" ")
.build())
.subQueue(new AllocationFileQueue.Builder("adminonly")
.aclSubmitApps(" ")
.aclAdministerApps("test ")
.build())
.build())
.writeToFile(allocFileName);
rmContext.getScheduler().reinitialize(conf, rmContext);
ApplicationId appId = MockApps.newAppID(1);
Resource res = Resources.createResource(1024, 1);
ApplicationSubmissionContext asContext = createAppSubmitCtx(appId, res);
// Submit to no access queue
when(placementMgr.placeApplication(any(), any()))
.thenReturn(new ApplicationPlacementContext("noaccess"));
try {
rmAppManager.submitApplication(asContext, "test");
Assert.fail("Test should have failed with access denied");
} catch (YarnException e) {
assertTrue("Access exception not found",
e.getCause() instanceof AccessControlException);
}
// Submit to submit access queue
when(placementMgr.placeApplication(any(), any()))
.thenReturn(new ApplicationPlacementContext("submitonly"));
rmAppManager.submitApplication(asContext, "test");
// Submit second app to admin access queue
appId = MockApps.newAppID(2);
asContext = createAppSubmitCtx(appId, res);
when(placementMgr.placeApplication(any(), any()))
.thenReturn(new ApplicationPlacementContext("adminonly"));
rmAppManager.submitApplication(asContext, "test");
}
@Test
public void testQueueSubmitWithRootPermission()
throws YarnException, IOException {
conf.set(YarnConfiguration.YARN_ACL_ENABLE, "true");
AllocationFileWriter.create()
.addQueue(new AllocationFileQueue.Builder("root")
.subQueue(new AllocationFileQueue.Builder("noaccess")
.aclSubmitApps(" ")
.aclAdministerApps(" ")
.build())
.build())
.writeToFile(allocFileName);
rmContext.getScheduler().reinitialize(conf, rmContext);
ApplicationId appId = MockApps.newAppID(1);
Resource res = Resources.createResource(1024, 1);
ApplicationSubmissionContext asContext = createAppSubmitCtx(appId, res);
// Submit to noaccess queue should be allowed by root ACL
when(placementMgr.placeApplication(any(), any()))
.thenReturn(new ApplicationPlacementContext("noaccess"));
rmAppManager.submitApplication(asContext, "test");
}
@Test
public void testQueueSubmitWithAutoCreateQueue()
throws YarnException, IOException {
conf.set(YarnConfiguration.YARN_ACL_ENABLE, "true");
AllocationFileWriter.create()
.addQueue(new AllocationFileQueue.Builder("root")
.aclSubmitApps(" ")
.aclAdministerApps(" ")
.subQueue(new AllocationFileQueue.Builder("noaccess")
.parent(true)
.build())
.subQueue(new AllocationFileQueue.Builder("submitonly")
.parent(true)
.aclSubmitApps("test ")
.build())
.build())
.writeToFile(allocFileName);
rmContext.getScheduler().reinitialize(conf, rmContext);
ApplicationId appId = MockApps.newAppID(1);
Resource res = Resources.createResource(1024, 1);
ApplicationSubmissionContext asContext = createAppSubmitCtx(appId, res);
// Submit to noaccess parent with non existent child queue
when(placementMgr.placeApplication(any(), any()))
.thenReturn(new ApplicationPlacementContext("root.noaccess.child"));
try {
rmAppManager.submitApplication(asContext, "test");
Assert.fail("Test should have failed with access denied");
} catch (YarnException e) {
assertTrue("Access exception not found",
e.getCause() instanceof AccessControlException);
}
// Submit to submitonly parent with non existent child queue
when(placementMgr.placeApplication(any(), any()))
.thenReturn(new ApplicationPlacementContext("root.submitonly.child"));
rmAppManager.submitApplication(asContext, "test");
}
private ApplicationSubmissionContext createAppSubmitCtx(ApplicationId appId,
Resource res) {
ApplicationSubmissionContext asContext =
Records.newRecord(ApplicationSubmissionContext.class);
asContext.setApplicationId(appId);
ResourceRequest resReg =
ResourceRequest.newInstance(Priority.newInstance(0),
ResourceRequest.ANY, res, 1);
asContext.setAMContainerResourceRequests(
Collections.singletonList(resReg));
asContext.setAMContainerSpec(mock(ContainerLaunchContext.class));
asContext.setQueue("default");
return asContext;
}
}