Repository: aurora Updated Branches: refs/heads/master d56f8c644 -> 8e07b04bb
Added the 'reason' to the /pendingTasks endpoint Bugs closed: AURORA-1762 Reviewed at https://reviews.apache.org/r/51993/ Project: http://git-wip-us.apache.org/repos/asf/aurora/repo Commit: http://git-wip-us.apache.org/repos/asf/aurora/commit/8e07b04b Tree: http://git-wip-us.apache.org/repos/asf/aurora/tree/8e07b04b Diff: http://git-wip-us.apache.org/repos/asf/aurora/diff/8e07b04b Branch: refs/heads/master Commit: 8e07b04bbd4de23b8f492627da4a614d1e517cf1 Parents: d56f8c6 Author: Pradyumna Kaushik <[email protected]> Authored: Mon Nov 28 15:05:56 2016 -0600 Committer: Joshua Cohen <[email protected]> Committed: Mon Nov 28 15:05:56 2016 -0600 ---------------------------------------------------------------------- config/legacy_untested_classes.txt | 1 - .../aurora/scheduler/http/PendingTasks.java | 33 +++- .../aurora/scheduler/metadata/NearestFit.java | 24 ++- .../aurora/scheduler/scheduling/TaskGroup.java | 12 +- .../aurora/scheduler/scheduling/TaskGroups.java | 3 +- .../aurora/scheduler/http/OffersTest.java | 1 - .../aurora/scheduler/http/PendingTasksTest.java | 172 +++++++++++++++++++ .../apache/aurora/scheduler/http/TestUtils.java | 56 ++++++ .../scheduler/metadata/NearestFitTest.java | 41 +++++ 9 files changed, 332 insertions(+), 11 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/aurora/blob/8e07b04b/config/legacy_untested_classes.txt ---------------------------------------------------------------------- diff --git a/config/legacy_untested_classes.txt b/config/legacy_untested_classes.txt index 8426506..ec3e934 100644 --- a/config/legacy_untested_classes.txt +++ b/config/legacy_untested_classes.txt @@ -25,7 +25,6 @@ org/apache/aurora/scheduler/http/Offers$2 org/apache/aurora/scheduler/http/Offers$3 org/apache/aurora/scheduler/http/Offers$4 org/apache/aurora/scheduler/http/Offers$5 -org/apache/aurora/scheduler/http/PendingTasks org/apache/aurora/scheduler/http/Quotas org/apache/aurora/scheduler/http/Quotas$1 org/apache/aurora/scheduler/http/Quotas$2 http://git-wip-us.apache.org/repos/asf/aurora/blob/8e07b04b/src/main/java/org/apache/aurora/scheduler/http/PendingTasks.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/http/PendingTasks.java b/src/main/java/org/apache/aurora/scheduler/http/PendingTasks.java index c80e0c8..4571070 100644 --- a/src/main/java/org/apache/aurora/scheduler/http/PendingTasks.java +++ b/src/main/java/org/apache/aurora/scheduler/http/PendingTasks.java @@ -13,6 +13,10 @@ */ package org.apache.aurora.scheduler.http; +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Map; import java.util.Objects; import javax.inject.Inject; @@ -22,7 +26,11 @@ import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import org.apache.aurora.scheduler.metadata.NearestFit; import org.apache.aurora.scheduler.scheduling.TaskGroups; +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.node.ObjectNode; /** * Servlet that exposes detailed information about tasks that are pending. @@ -31,10 +39,12 @@ import org.apache.aurora.scheduler.scheduling.TaskGroups; public class PendingTasks { private final TaskGroups taskGroups; + private final NearestFit nearestFit; @Inject - PendingTasks(TaskGroups taskGroups) { + PendingTasks(TaskGroups taskGroups, NearestFit nearestFit) { this.taskGroups = Objects.requireNonNull(taskGroups); + this.nearestFit = Objects.requireNonNull(nearestFit); } /** @@ -44,7 +54,24 @@ public class PendingTasks { */ @GET @Produces(MediaType.APPLICATION_JSON) - public Response getOffers() { - return Response.ok(taskGroups.getGroups()).build(); + public Response getOffers() throws IOException { + // Adding reason, received from NearestFit#getPendingReasons() to the JSON Object. + Map<String, List<String>> taskGroupReasonMap = + nearestFit.getPendingReasons(taskGroups.getGroups()); + // Adding the attribute "reason" to each of the JSON Objects in the JsonNode. + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonNode = mapper.valueToTree(taskGroups.getGroups()); + Iterator<JsonNode> jsonNodeIterator = jsonNode.iterator(); + + while (jsonNodeIterator.hasNext()) { + JsonNode pendingTask = jsonNodeIterator.next(); + + // Retrieving the reasons corresponding to this pendingTask. + List<String> reasons = taskGroupReasonMap.get(pendingTask.get("name").asText()); + // Adding the reasons corresponding to the pendingTask. + ((ObjectNode) pendingTask).put("reason", reasons.toString()); + } + return Response.ok(jsonNode).build(); } + } http://git-wip-us.apache.org/repos/asf/aurora/blob/8e07b04b/src/main/java/org/apache/aurora/scheduler/metadata/NearestFit.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/metadata/NearestFit.java b/src/main/java/org/apache/aurora/scheduler/metadata/NearestFit.java index f783e7f..1c015a2 100644 --- a/src/main/java/org/apache/aurora/scheduler/metadata/NearestFit.java +++ b/src/main/java/org/apache/aurora/scheduler/metadata/NearestFit.java @@ -13,8 +13,13 @@ */ package org.apache.aurora.scheduler.metadata; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import javax.inject.Inject; @@ -28,6 +33,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.eventbus.Subscribe; +import org.apache.aurora.GuavaUtils; import org.apache.aurora.common.quantity.Amount; import org.apache.aurora.common.quantity.Time; import org.apache.aurora.gen.ScheduleStatus; @@ -38,6 +44,7 @@ import org.apache.aurora.scheduler.events.PubsubEvent.TaskStateChange; import org.apache.aurora.scheduler.events.PubsubEvent.TasksDeleted; import org.apache.aurora.scheduler.events.PubsubEvent.Vetoed; import org.apache.aurora.scheduler.filter.SchedulingFilter.Veto; +import org.apache.aurora.scheduler.scheduling.TaskGroup; /** * Tracks vetoes against scheduling decisions and maintains the closest fit among all the vetoes @@ -52,8 +59,7 @@ public class NearestFit implements EventSubscriber { private final LoadingCache<TaskGroupKey, Fit> fitByGroupKey; - @VisibleForTesting - NearestFit(Ticker ticker) { + public NearestFit(Ticker ticker) { fitByGroupKey = CacheBuilder.newBuilder() .expireAfterWrite(EXPIRATION.getValue(), EXPIRATION.getUnit().getTimeUnit()) .ticker(ticker) @@ -118,6 +124,20 @@ public class NearestFit implements EventSubscriber { fitByGroupKey.getUnchecked(vetoEvent.getGroupKey()).maybeUpdate(vetoEvent.getVetoes()); } + /** + * Determine the pending reason, for each of the given tasks in taskGroups. + * + * @param taskGroups Group of pending tasks. + * @return A map with key=String (the taskgroup key) and value=List of reasons. + */ + public synchronized Map<String, List<String>> getPendingReasons(Iterable<TaskGroup> taskGroups) { + return StreamSupport.stream(taskGroups.spliterator(), false).map(t -> { + List<String> reasons = getNearestFit(t.getKey()).stream() + .map(Veto::getReason).collect(Collectors.toList()); + return new HashMap.SimpleEntry<>(t.getKey().toString(), reasons); + }).collect(GuavaUtils.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)); + } + private static class Fit { private ImmutableSet<Veto> vetoes; http://git-wip-us.apache.org/repos/asf/aurora/blob/8e07b04b/src/main/java/org/apache/aurora/scheduler/scheduling/TaskGroup.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/scheduling/TaskGroup.java b/src/main/java/org/apache/aurora/scheduler/scheduling/TaskGroup.java index b521620..b5c50bb 100644 --- a/src/main/java/org/apache/aurora/scheduler/scheduling/TaskGroup.java +++ b/src/main/java/org/apache/aurora/scheduler/scheduling/TaskGroup.java @@ -17,29 +17,34 @@ import java.util.Collection; import java.util.Queue; import java.util.Set; +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import org.apache.aurora.scheduler.base.TaskGroupKey; +import org.codehaus.jackson.annotate.JsonIgnore; import static org.apache.aurora.GuavaUtils.toImmutableSet; /** * A group of task IDs that are eligible for scheduling, but may be waiting for a backoff to expire. */ -class TaskGroup { +public class TaskGroup { private final TaskGroupKey key; private long penaltyMs; private final Queue<String> tasks; - TaskGroup(TaskGroupKey key, String initialTaskId) { + @VisibleForTesting + public TaskGroup(TaskGroupKey key, String initialTaskId) { this.key = key; this.penaltyMs = 0; this.tasks = Lists.newLinkedList(); this.tasks.add(initialTaskId); } - synchronized TaskGroupKey getKey() { + // This class is serialized by the PendingTasks endpoint, but the key is exposed via getName(). + @JsonIgnore + public synchronized TaskGroupKey getKey() { return key; } @@ -76,4 +81,5 @@ class TaskGroup { public synchronized long getPenaltyMs() { return penaltyMs; } + } http://git-wip-us.apache.org/repos/asf/aurora/blob/8e07b04b/src/main/java/org/apache/aurora/scheduler/scheduling/TaskGroups.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/aurora/scheduler/scheduling/TaskGroups.java b/src/main/java/org/apache/aurora/scheduler/scheduling/TaskGroups.java index 2cd2105..2d548b0 100644 --- a/src/main/java/org/apache/aurora/scheduler/scheduling/TaskGroups.java +++ b/src/main/java/org/apache/aurora/scheduler/scheduling/TaskGroups.java @@ -126,8 +126,9 @@ public class TaskGroups implements EventSubscriber { } } + @VisibleForTesting @Inject - TaskGroups( + public TaskGroups( @AsyncExecutor DelayExecutor executor, TaskGroupsSettings settings, TaskScheduler taskScheduler, http://git-wip-us.apache.org/repos/asf/aurora/blob/8e07b04b/src/test/java/org/apache/aurora/scheduler/http/OffersTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/aurora/scheduler/http/OffersTest.java b/src/test/java/org/apache/aurora/scheduler/http/OffersTest.java index 9e35732..add0eb8 100644 --- a/src/test/java/org/apache/aurora/scheduler/http/OffersTest.java +++ b/src/test/java/org/apache/aurora/scheduler/http/OffersTest.java @@ -152,5 +152,4 @@ public class OffersTest extends EasyMockTest { private static List toList(String json) { return new Gson().fromJson(json, List.class); } - } http://git-wip-us.apache.org/repos/asf/aurora/blob/8e07b04b/src/test/java/org/apache/aurora/scheduler/http/PendingTasksTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/aurora/scheduler/http/PendingTasksTest.java b/src/test/java/org/apache/aurora/scheduler/http/PendingTasksTest.java new file mode 100644 index 0000000..b71669f --- /dev/null +++ b/src/test/java/org/apache/aurora/scheduler/http/PendingTasksTest.java @@ -0,0 +1,172 @@ +/** + * Licensed 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.aurora.scheduler.http; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import com.google.common.collect.ImmutableSet; + +import org.apache.aurora.common.testing.easymock.EasyMockTest; +import org.apache.aurora.common.util.testing.FakeTicker; +import org.apache.aurora.gen.JobKey; +import org.apache.aurora.gen.ScheduleStatus; +import org.apache.aurora.scheduler.base.TaskGroupKey; +import org.apache.aurora.scheduler.events.PubsubEvent; +import org.apache.aurora.scheduler.filter.SchedulingFilter.Veto; +import org.apache.aurora.scheduler.metadata.NearestFit; +import org.apache.aurora.scheduler.scheduling.TaskGroup; +import org.apache.aurora.scheduler.scheduling.TaskGroups; +import org.apache.aurora.scheduler.storage.entities.IJobKey; +import org.apache.aurora.scheduler.storage.entities.IScheduledTask; +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.node.ArrayNode; +import org.codehaus.jackson.node.ObjectNode; +import org.junit.Before; +import org.junit.Test; + +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.expectLastCall; +import static org.easymock.EasyMock.replay; +import static org.junit.Assert.assertEquals; + +public class PendingTasksTest extends EasyMockTest { + + private TaskGroups pendingTaskGroups; + private NearestFit nearestFit; + + @Before + public void setUp() { + pendingTaskGroups = createMock(TaskGroups.class); + nearestFit = new NearestFit(new FakeTicker()); + } + + /** + * Create a {@link JsonNode} object to mimic the response. + * + * @param penaltyMs + * @param taskIds + * @param name + * @param reasons + * @return Json node for pending tasks whose values are initialized to the provided values. + * @throws IOException + */ + private JsonNode getMimicResponseJson( + long penaltyMs, String[] taskIds, String name, List<String> reasons) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + ObjectNode mutablePendingTaskJson = mapper.createObjectNode(); + // Adding the key=value pairs to mutablePendingTaskJson. + mutablePendingTaskJson.put("penaltyMs", penaltyMs); + mutablePendingTaskJson.putArray("taskIds"); + for (String taskId : taskIds) { + ((ArrayNode) mutablePendingTaskJson.get("taskIds")).add(taskId); + } + mutablePendingTaskJson.put("name", name); + mutablePendingTaskJson.put("reason", reasons.toString()); + return mutablePendingTaskJson; + } + + @Test + public void testNoOffers() throws IOException { + // Making a task that is not in PENDING state. + IJobKey jobKey = IJobKey.build(new JobKey("role", "test", "nonPendingJob")); + IScheduledTask task = TestUtils.makeTask(jobKey, "task0", 0, + ScheduleStatus.ASSIGNED, 10, 10, 10); + + PubsubEvent.TaskStateChange taskStateChange = PubsubEvent.TaskStateChange.transition( + task, ScheduleStatus.INIT); + + pendingTaskGroups.taskChangedState(taskStateChange); + expectLastCall(); + + // Recording the return value of pendingTaskGroups.getGroups(). + List<TaskGroup> taskGroupList = new ArrayList<>(); + expect(pendingTaskGroups.getGroups()).andReturn(taskGroupList).anyTimes(); + + replay(pendingTaskGroups); + + // Testing. + pendingTaskGroups.taskChangedState(taskStateChange); + PendingTasks pendingTasks = new PendingTasks(pendingTaskGroups, nearestFit); + JsonNode mimicResponseNoPendingTaskJson = new ObjectMapper().createArrayNode(); + JsonNode actualResponseJson = new ObjectMapper().valueToTree( + pendingTasks.getOffers().getEntity()); + assertEquals(mimicResponseNoPendingTaskJson, actualResponseJson); + } + + @Test + public void testOffers() throws IOException { + // Making pending tasks. + IJobKey jobKey0 = IJobKey.build(new JobKey("role", "test", "jobA")); + IJobKey jobKey1 = IJobKey.build(new JobKey("role", "test", "jobB")); + IScheduledTask task0 = TestUtils.makeTask(jobKey0, "task0", 0, + ScheduleStatus.PENDING, 1000, 1000000, 10); + IScheduledTask task1 = TestUtils.makeTask(jobKey1, "task1", 0, + ScheduleStatus.PENDING, 1000, 10, 1000000); + + PubsubEvent.TaskStateChange taskStateChange0 = PubsubEvent.TaskStateChange.transition( + task0, ScheduleStatus.INIT); + PubsubEvent.TaskStateChange taskStateChange1 = PubsubEvent.TaskStateChange.transition( + task1, ScheduleStatus.INIT); + + pendingTaskGroups.taskChangedState(taskStateChange0); + pendingTaskGroups.taskChangedState(taskStateChange1); + expectLastCall(); + + // Recording the return value of pendingTaskGroups.getGroups(). + TaskGroupKey taskGroupKey0 = TaskGroupKey.from(task0.getAssignedTask().getTask()); + TaskGroupKey taskGroupKey1 = TaskGroupKey.from(task1.getAssignedTask().getTask()); + TaskGroup taskGroup0 = new TaskGroup(taskGroupKey0, "task0"); + TaskGroup taskGroup1 = new TaskGroup(taskGroupKey1, "task1"); + List<TaskGroup> taskGroupList = new ArrayList<>(); + taskGroupList.add(taskGroup0); + taskGroupList.add(taskGroup1); + expect(pendingTaskGroups.getGroups()).andReturn(taskGroupList).anyTimes(); + + // Creating vetoes for CPU and RAM, corresponding to task0. + ImmutableSet<Veto> vetoes = ImmutableSet.<Veto>builder() + .add(Veto.insufficientResources("CPU", 1)) + .add(Veto.insufficientResources("RAM", 1)).build(); + nearestFit.vetoed(new PubsubEvent.Vetoed(taskGroupKey0, vetoes)); + // Creating vetoes for CPU and DISK, corresponding to task1. + ImmutableSet<Veto> vetoes1 = ImmutableSet.<Veto>builder() + .add(Veto.insufficientResources("CPU", 1)) + .add(Veto.insufficientResources("DISK", 1)).build(); + nearestFit.vetoed(new PubsubEvent.Vetoed(taskGroupKey1, vetoes1)); + replay(pendingTaskGroups); + + // Testing. + pendingTaskGroups.taskChangedState(taskStateChange0); + pendingTaskGroups.taskChangedState(taskStateChange1); + PendingTasks pendingTasks0 = new PendingTasks(pendingTaskGroups, nearestFit); + String[] taskIds0 = {"task0"}; + String[] taskIds1 = {"task1"}; + String[] reasonsArr0 = {"Insufficient: CPU", "Insufficient: RAM"}; + String[] reasonsArr1 = {"Insufficient: CPU", "Insufficient: DISK"}; + List<String> reasons0 = Arrays.stream(reasonsArr0).collect(Collectors.toList()); + List<String> reasons1 = Arrays.stream(reasonsArr1).collect(Collectors.toList()); + JsonNode mimicResponseTwoPendingTasksJson = new ObjectMapper().createArrayNode(); + JsonNode mimicJson0 = getMimicResponseJson(0, taskIds0, "role/test/jobA", reasons0); + JsonNode mimicJson1 = getMimicResponseJson(0, taskIds1, "role/test/jobB", reasons1); + ((ArrayNode) mimicResponseTwoPendingTasksJson).add(mimicJson0); + ((ArrayNode) mimicResponseTwoPendingTasksJson).add(mimicJson1); + JsonNode actualResponseJson = new ObjectMapper().valueToTree( + pendingTasks0.getOffers().getEntity()); + assertEquals(mimicResponseTwoPendingTasksJson, actualResponseJson); + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/8e07b04b/src/test/java/org/apache/aurora/scheduler/http/TestUtils.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/aurora/scheduler/http/TestUtils.java b/src/test/java/org/apache/aurora/scheduler/http/TestUtils.java new file mode 100644 index 0000000..689482c --- /dev/null +++ b/src/test/java/org/apache/aurora/scheduler/http/TestUtils.java @@ -0,0 +1,56 @@ +/** + * Licensed 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.aurora.scheduler.http; + +import com.google.common.annotations.VisibleForTesting; + +import org.apache.aurora.gen.AssignedTask; +import org.apache.aurora.gen.ScheduleStatus; +import org.apache.aurora.gen.ScheduledTask; +import org.apache.aurora.gen.TaskConfig; +import org.apache.aurora.scheduler.storage.entities.IJobKey; +import org.apache.aurora.scheduler.storage.entities.IScheduledTask; + +public final class TestUtils { + + private TestUtils() { } + + /** + * Make task and set status to the given {@link ScheduleStatus}. + * + * @param jobKey The task group key. + * @param id The task id. + * @param instanceId The id of the instance of the task. + * @param taskStatus The status of the task. + * @param numCPUs The number of CPUs required for the task. + * @param ramMB The amount of RAM (in MegaBytes) required for the task. + * @param diskMB The amount of disk space (in MegaBytes) required for the task. + * @return Task. + */ + @VisibleForTesting + public static IScheduledTask makeTask( + IJobKey jobKey, String id, int instanceId, + ScheduleStatus taskStatus, double numCPUs, long ramMB, long diskMB) { + return IScheduledTask.build(new ScheduledTask() + .setStatus(taskStatus) + .setAssignedTask(new AssignedTask() + .setInstanceId(instanceId) + .setTaskId(id) + .setTask(new TaskConfig() + .setJob(jobKey.newBuilder()) + .setNumCpus(numCPUs) + .setRamMb(ramMB) + .setDiskMb(diskMB)))); + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/8e07b04b/src/test/java/org/apache/aurora/scheduler/metadata/NearestFitTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/aurora/scheduler/metadata/NearestFitTest.java b/src/test/java/org/apache/aurora/scheduler/metadata/NearestFitTest.java index d9b3cc6..195d083 100644 --- a/src/test/java/org/apache/aurora/scheduler/metadata/NearestFitTest.java +++ b/src/test/java/org/apache/aurora/scheduler/metadata/NearestFitTest.java @@ -13,7 +13,13 @@ */ package org.apache.aurora.scheduler.metadata; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import com.google.common.collect.ImmutableSet; @@ -21,6 +27,7 @@ import org.apache.aurora.common.quantity.Amount; import org.apache.aurora.common.quantity.Time; import org.apache.aurora.common.util.testing.FakeTicker; import org.apache.aurora.gen.AssignedTask; +import org.apache.aurora.gen.JobKey; import org.apache.aurora.gen.ScheduleStatus; import org.apache.aurora.gen.ScheduledTask; import org.apache.aurora.gen.TaskConfig; @@ -29,6 +36,9 @@ import org.apache.aurora.scheduler.events.PubsubEvent.TaskStateChange; import org.apache.aurora.scheduler.events.PubsubEvent.TasksDeleted; import org.apache.aurora.scheduler.events.PubsubEvent.Vetoed; import org.apache.aurora.scheduler.filter.SchedulingFilter.Veto; +import org.apache.aurora.scheduler.http.TestUtils; +import org.apache.aurora.scheduler.scheduling.TaskGroup; +import org.apache.aurora.scheduler.storage.entities.IJobKey; import org.apache.aurora.scheduler.storage.entities.IScheduledTask; import org.apache.aurora.scheduler.storage.entities.ITaskConfig; import org.junit.Before; @@ -111,6 +121,32 @@ public class NearestFitTest { assertNearest(); } + @Test + public void testGetPendingReasons() { + // Making task that requires lot of CPUs and RAM. + IJobKey jobKey = IJobKey.build(new JobKey("role", "test", "jobA")); + IScheduledTask task = TestUtils.makeTask(jobKey, "task0", 0, + ScheduleStatus.ASSIGNED, 1000, 10000000, 10); + // Changing the state of the task to PENDING. + nearest.stateChanged(TaskStateChange.transition(task, ScheduleStatus.PENDING)); + // Adding the task to a list of pending tasks. + TaskGroupKey taskGroupKey = TaskGroupKey.from(task.getAssignedTask().getTask()); + TaskGroup taskGroup = new TaskGroup(taskGroupKey, "task0"); + List<TaskGroup> pendingTaskGroups = new ArrayList<>(); + pendingTaskGroups.add(taskGroup); + + // Creating vetoes for CPU and RAM. + nearest.vetoed(new Vetoed(taskGroupKey, vetoes(SEVERITY_4_CPU, SEVERITY_4_RAM))); + + // Testing. + Map<String, List<String>> mimicPendingReasons = new LinkedHashMap<>(); + List<String> reasons = Arrays.stream( + new String[]{SEVERITY_4_CPU.getReason(), SEVERITY_4_RAM.getReason()}) + .collect(Collectors.toList()); + mimicPendingReasons.put("role/test/jobA", reasons); + assertPendingReasons(nearest.getPendingReasons(pendingTaskGroups), mimicPendingReasons); + } + private Set<Veto> vetoes(Veto... vetoes) { return ImmutableSet.copyOf(vetoes); } @@ -122,4 +158,9 @@ public class NearestFitTest { private void assertNearest(Veto... vetoes) { assertEquals(vetoes(vetoes), nearest.getNearestFit(GROUP_KEY)); } + + private void assertPendingReasons(Map<String, List<String>> actualPendingReasons, + Map<String, List<String>> mimicPendingReasons) { + assertEquals(actualPendingReasons, mimicPendingReasons); + } }
