/** * 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.mapred; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.fail; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.mapreduce.QueueState; import org.apache.hadoop.mapreduce.server.jobtracker.TaskTracker; import static org.apache.hadoop.mapred.QueueManagerTestUtils.*; import org.junit.After; import org.junit.Test; /** * Test the refresh feature of QueueManager. */ public class TestQueueManagerRefresh { private static final Log LOG = LogFactory.getLog(TestQueueManagerRefresh.class); /** * Remove the configuration file after the test's done. */ @After public void tearDown() { deleteQueuesConfigFile(); } /** * @return a simple hierarchy of JobQueueInfos */ static JobQueueInfo[] getSimpleQueueHierarchy() { int numQs = 3; JobQueueInfo[] queues = new JobQueueInfo[numQs]; queues[0] = newJobQueueInfo(new ArrayList<JobQueueInfo>(), null, "q1", QueueState.UNDEFINED, null); queues[1] = newJobQueueInfo(new ArrayList<JobQueueInfo>(), null, "q1:q2", QueueState.RUNNING, null); queues[2] = newJobQueueInfo(new ArrayList<JobQueueInfo>(), null, "q1:q3", QueueState.RUNNING, null); queues[0].addChild(queues[1]); queues[0].addChild(queues[2]); return queues; } /** * Test to verify that the refresh of queue properties fails if a new queue is * added. * * @throws Exception */ @Test public void testRefreshWithAddedQueues() throws Exception { JobQueueInfo[] queues = getSimpleQueueHierarchy(); // write the configuration file writeQueueConfigurationFile( QUEUES_CONFIG_FILE_PATH, new JobQueueInfo[] { queues[0] }); QueueManager qManager = new QueueManager(); JobQueueInfo newQueue = newJobQueueInfo(new ArrayList<JobQueueInfo>(), null, "q4", QueueState.UNDEFINED, null); queues[0].addChild(newQueue); // Rewrite the configuration file writeQueueConfigurationFile( QUEUES_CONFIG_FILE_PATH, new JobQueueInfo[] { queues[0] }); testRefreshFailureWithChangeOfHierarchy(qManager); } /** * Test to verify that the refresh of queue properties fails if queues are * removed. * * @throws Exception */ @Test public void testRefreshWithRemovedQueues() throws Exception { JobQueueInfo[] queues = getSimpleQueueHierarchy(); // write the configuration file writeQueueConfigurationFile( QUEUES_CONFIG_FILE_PATH, new JobQueueInfo[] { queues[0] }); QueueManager qManager = new QueueManager(); // Remove queue[2] JobQueueInfo q2 = queues[2]; queues[0].removeChild(q2); // Rewrite the configuration file writeQueueConfigurationFile( QUEUES_CONFIG_FILE_PATH, new JobQueueInfo[] { queues[0] }); testRefreshFailureWithChangeOfHierarchy(qManager); } /** * @param originalQManager * @throws Exception */ private void testRefreshFailureWithChangeOfHierarchy( QueueManager originalQManager) throws Exception { // Make sure that isHierarchySame returns false. QueueManager modifiedQueueManager = new QueueManager(); assertFalse("Hierarchy changed after refresh!", originalQManager.getRoot().isHierarchySameAs( modifiedQueueManager.getRoot())); // Refresh the QueueManager and make sure it fails. try { originalQManager.refreshQueues(null, null); fail("Queue-refresh should have failed!"); } catch (Exception e) { // Refresh failed as expected. Check the error message. assertTrue( "Exception message should point to a change in queue hierarchy!", e.getMessage().contains( QueueManager.MSG_REFRESH_FAILURE_WITH_CHANGE_OF_HIERARCHY)); } // Make sure that the old configuration is retained. List<JobQueueInfo> rootQueues = originalQManager.getRoot().getJobQueueInfo().getChildren(); assertTrue(rootQueues.size() == 1); } /** * Test to verify that the refresh of queue properties fails if scheduler * fails to reload itself. * * @throws Exception */ // @Test public void testRefreshWithSchedulerFailure() throws Exception { JobQueueInfo[] queues = getSimpleQueueHierarchy(); // write the configuration file writeQueueConfigurationFile( QUEUES_CONFIG_FILE_PATH, new JobQueueInfo[] { queues[0] }); QueueManager qManager = new QueueManager(); // No change in configuration. Just Refresh the QueueManager and make sure // it fails. try { qManager.refreshQueues(null, new MyTaskScheduler().new MyFailingQueueRefresher()); fail("Queue-refresh should have failed!"); } catch (Exception e) { // Refresh failed as expected. Check the error message. assertTrue( "Exception message should point to a refresh-failure in scheduler!", e.getMessage().contains( QueueManager.MSG_REFRESH_FAILURE_WITH_SCHEDULER_FAILURE)); } } /** * Test to verify that the refresh of scheduler properties passes smoothly. * * @throws Exception */ @Test public void testRefreshOfSchedulerProperties() throws Exception { JobQueueInfo[] queues = getSimpleQueueHierarchy(); // Set some scheduler properties for (JobQueueInfo jqi : queues) { Properties props = new Properties(); props.setProperty("testing.property", "testing.value." + jqi.getQueueName()); jqi.setProperties(props); } // write the configuration file writeQueueConfigurationFile( QUEUES_CONFIG_FILE_PATH, new JobQueueInfo[] { queues[0] }); QueueManager qManager = new QueueManager(); MyTaskScheduler myScheduler = new MyTaskScheduler(); qManager.refreshQueues(null, myScheduler.new MyQueueRefresher()); // Verify that the scheduler props are set correctly by scheduler-refresh. Map<String, Properties> schedProps = myScheduler.getSchedulerProperties(); for (JobQueueInfo jqi : queues) { String expectedVal = "testing.value." + jqi.getQueueName(); Properties qProperties = schedProps.get(jqi.getQueueName()); assertNotNull("Properties should not be null for the SchedulerQueue " + jqi.getQueueName(), qProperties); String observedVal = qProperties.getProperty("testing.property"); assertEquals("Properties for the SchedulerQueue " + jqi.getQueueName() + " are not reloaded properly!", expectedVal, observedVal); } } /** * Test to verify that the scheduling information per queue in the * {@link QueueManager} is retained across queue-refresh. * * @throws Exception */ @Test public void testSchedulingInfoAfterRefresh() throws Exception { JobQueueInfo[] queues = getSimpleQueueHierarchy(); // write the configuration file writeQueueConfigurationFile( QUEUES_CONFIG_FILE_PATH, new JobQueueInfo[] { queues[0] }); QueueManager qManager = new QueueManager(); // Set some scheduling information for the queues in the QueueManager. for (String qName : qManager.getLeafQueueNames()) { qManager.setSchedulerInfo(qName, new String( "scheduling-information-for-queue-" + qName)); } qManager.refreshQueues(null, null); // Verify that the scheduling information is retained across refresh. for (String qName : qManager.getLeafQueueNames()) { assertEquals("scheduling-information-for-queue-" + qName, qManager.getSchedulerInfo(qName)); } } static class MyTaskScheduler extends TaskScheduler { Map<String, Properties> schedulerPropsMap = new HashMap<String, Properties>(); Map<String, Properties> getSchedulerProperties() { return schedulerPropsMap; } class MyQueueRefresher extends QueueRefresher { private void updateSchedulerProps(JobQueueInfo jqi) { LOG.info("Updating properties for SchedulerQueue " + jqi.getQueueName()); LOG.info("Putting " + jqi.getProperties() + " in " + jqi.getQueueName()); schedulerPropsMap.put(jqi.getQueueName(), jqi.getProperties()); for (JobQueueInfo child : jqi.getChildren()) { updateSchedulerProps(child); } } @Override void refreshQueues(List<JobQueueInfo> newRootQueues) { LOG.info("Refreshing scheduler's properties"); for (JobQueueInfo jqi : newRootQueues) { updateSchedulerProps(jqi); } } } class MyFailingQueueRefresher extends QueueRefresher { @Override void refreshQueues(List<JobQueueInfo> newRootQueues) throws Throwable { throw new IOException("Scheduler cannot refresh the queues!"); } } @Override public List<Task> assignTasks(TaskTracker taskTracker) { return null; } @Override public Collection<JobInProgress> getJobs(String queueName) { return null; } } static JobQueueInfo newJobQueueInfo(List<JobQueueInfo> children, Properties props, String queueName, QueueState state, String schedulingInfo) { JobQueueInfo jqi = new JobQueueInfo(); jqi.setChildren(children); if (props != null) { jqi.setProperties(props); } jqi.setQueueName(queueName); jqi.setQueueState(state.getStateName()); jqi.setSchedulingInfo(schedulingInfo); return jqi; } }