package com.hubspot.singularity.scheduler;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.mesos.Protos.TaskState;
import org.junit.Assert;
import org.junit.Test;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import com.hubspot.singularity.MachineState;
import com.hubspot.singularity.ExtendedTaskState;
import com.hubspot.singularity.SingularityRequest;
import com.hubspot.singularity.SingularityTaskCleanup;
import com.hubspot.singularity.SingularityTaskHistoryUpdate;
import com.hubspot.singularity.SlavePlacement;
import com.hubspot.singularity.TaskCleanupType;
public class SingularitySlavePlacementTest extends SingularitySchedulerTestBase {
public SingularitySlavePlacementTest() {
super(false);
}
@Test
public void testSlavePlacementSeparate() {
initRequest();
initFirstDeploy();
saveAndSchedule(request.toBuilder().setInstances(Optional.of(2)).setSlavePlacement(Optional.of(SlavePlacement.SEPARATE)));
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave1", "host1"), createOffer(20, 20000, "slave1", "host1")));
Assert.assertTrue(taskManager.getPendingTaskIds().size() == 1);
Assert.assertTrue(taskManager.getActiveTaskIds().size() == 1);
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave1", "host1")));
Assert.assertTrue(taskManager.getPendingTaskIds().size() == 1);
Assert.assertTrue(taskManager.getActiveTaskIds().size() == 1);
eventListener.taskHistoryUpdateEvent(new SingularityTaskHistoryUpdate(taskManager.getActiveTaskIds().get(0), System.currentTimeMillis(), ExtendedTaskState.TASK_CLEANING, Optional.<String>absent(), Optional.<String>absent()));
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave1", "host1")));
Assert.assertTrue(taskManager.getPendingTaskIds().size() == 1);
Assert.assertTrue(taskManager.getActiveTaskIds().size() == 1);
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave2", "host2")));
Assert.assertTrue(taskManager.getPendingTaskIds().isEmpty());
Assert.assertTrue(taskManager.getActiveTaskIds().size() == 2);
}
@Test
public void testSlavePlacementSpread() {
initRequest();
initFirstDeploy();
saveAndSchedule(request.toBuilder().setInstances(Optional.of(1)).setSlavePlacement(Optional.of(SlavePlacement.SPREAD_ALL_SLAVES)));
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave1", "host1", Optional.of("rack1"))));
// assert one Request on one slave.
Assert.assertTrue(slaveManager.getNumObjectsAtState(MachineState.ACTIVE) == 1);
Assert.assertTrue(taskManager.getPendingTaskIds().size() == 0);
Assert.assertTrue(taskManager.getActiveTaskIds().size() == 1);
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave2", "host2")));
Assert.assertTrue(slaveManager.getNumObjectsAtState(MachineState.ACTIVE) == 2);
spreadAllPoller.runActionOnPoll();
scheduler.drainPendingQueue(stateCacheProvider.get());
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave2", "host2")));
// assert Request is spread over the two slaves
Assert.assertTrue(taskManager.getPendingTaskIds().size() == 0);
Assert.assertTrue(taskManager.getActiveTaskIds().size() == 2);
Assert.assertEquals(1, taskManager.getTasksOnSlave(taskManager.getActiveTaskIds(), slaveManager.getObject("slave1").get()).size());
Assert.assertEquals(1, taskManager.getTasksOnSlave(taskManager.getActiveTaskIds(), slaveManager.getObject("slave2").get()).size());
// decommission slave and kill task
slaveManager.changeState("slave2", MachineState.FROZEN, Optional.<String>absent(), Optional.<String>absent());
slaveManager.changeState("slave2", MachineState.STARTING_DECOMMISSION, Optional.<String>absent(), Optional.<String>absent());
cleaner.drainCleanupQueue();
statusUpdate(taskManager.getTasksOnSlave(taskManager.getActiveTaskIds(), slaveManager.getObject("slave2").get()).get(0), TaskState.TASK_KILLED);
spreadAllPoller.runActionOnPoll();
scheduler.drainPendingQueue(stateCacheProvider.get());
Assert.assertTrue(taskManager.getPendingTaskIds().isEmpty());
Assert.assertTrue(taskManager.getActiveTaskIds().size() == 1);
}
@Test
public void testSlavePlacementOptimistic() {
initRequest();
initFirstDeploy();
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave1", "host1"), createOffer(20, 20000, "slave2", "host2")));
saveAndSchedule(request.toBuilder().setInstances(Optional.of(3)).setSlavePlacement(Optional.of(SlavePlacement.OPTIMISTIC)));
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave1", "host1")));
Assert.assertTrue(taskManager.getActiveTaskIds().size() < 3);
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave1", "host1")));
Assert.assertTrue(taskManager.getActiveTaskIds().size() < 3);
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave2", "host2")));
eventListener.taskHistoryUpdateEvent(new SingularityTaskHistoryUpdate(taskManager.getActiveTaskIds().get(0), System.currentTimeMillis(), ExtendedTaskState.TASK_CLEANING, Optional.<String>absent(), Optional.<String>absent()));
Assert.assertTrue(taskManager.getPendingTaskIds().isEmpty());
Assert.assertTrue(taskManager.getActiveTaskIds().size() == 3);
}
@Test
public void testSlavePlacementOptimisticSingleOffer() {
initRequest();
initFirstDeploy();
saveAndSchedule(request.toBuilder().setInstances(Optional.of(3)).setSlavePlacement(Optional.of(SlavePlacement.OPTIMISTIC)));
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave1", "host1"), createOffer(20, 20000, "slave2", "host2")));
eventListener.taskHistoryUpdateEvent(new SingularityTaskHistoryUpdate(taskManager.getActiveTaskIds().get(0), System.currentTimeMillis(), ExtendedTaskState.TASK_CLEANING, Optional.<String>absent(), Optional.<String>absent()));
Assert.assertTrue(taskManager.getPendingTaskIds().isEmpty());
Assert.assertTrue(taskManager.getActiveTaskIds().size() == 3);
}
@Test
public void testSlavePlacementGreedy() {
initRequest();
initFirstDeploy();
saveAndSchedule(request.toBuilder().setInstances(Optional.of(3)).setSlavePlacement(Optional.of(SlavePlacement.GREEDY)));
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave1", "host1")));
Assert.assertTrue(taskManager.getActiveTaskIds().size() == 3);
}
@Test
public void testReservedSlaveAttribute() {
Map<String, List<String>> reservedAttributes = new HashMap<>();
reservedAttributes.put("reservedKey", Arrays.asList("reservedValue1"));
configuration.setReserveSlavesWithAttributes(reservedAttributes);
initRequest();
initFirstDeploy();
saveAndSchedule(request.toBuilder().setInstances(Optional.of(1)));
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave1", "host1", Optional.<String>absent(), ImmutableMap.of("reservedKey", "reservedValue1"))));
Assert.assertTrue(taskManager.getActiveTaskIds().size() == 0);
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave2", "host2", Optional.<String>absent(), ImmutableMap.of("reservedKey", "notAReservedValue"))));
Assert.assertTrue(taskManager.getActiveTaskIds().size() == 1);
}
@Test
public void testReservedSlaveWithMatchinRequestAttribute() {
Map<String, List<String>> reservedAttributes = new HashMap<>();
reservedAttributes.put("reservedKey", Arrays.asList("reservedValue1"));
configuration.setReserveSlavesWithAttributes(reservedAttributes);
Map<String, String> reservedAttributesMap = ImmutableMap.of("reservedKey", "reservedValue1");
initRequest();
initFirstDeploy();
saveAndSchedule(request.toBuilder().setInstances(Optional.of(1)));
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave1", "host1", Optional.<String>absent(), reservedAttributesMap)));
Assert.assertTrue(taskManager.getActiveTaskIds().size() == 0);
saveAndSchedule(request.toBuilder().setInstances(Optional.of(1)).setRequiredSlaveAttributes(Optional.of(reservedAttributesMap)));
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave1", "host1", Optional.<String>absent(), ImmutableMap.of("reservedKey", "reservedValue1"))));
Assert.assertTrue(taskManager.getActiveTaskIds().size() == 1);
}
@Test
public void testAllowedSlaveAttributes() {
Map<String, List<String>> reservedAttributes = new HashMap<>();
reservedAttributes.put("reservedKey", Arrays.asList("reservedValue1"));
configuration.setReserveSlavesWithAttributes(reservedAttributes);
Map<String, String> allowedAttributes = new HashMap<>();
allowedAttributes.put("reservedKey", "reservedValue1");
initRequest();
initFirstDeploy();
saveAndSchedule(request.toBuilder().setInstances(Optional.of(1)));
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave1", "host1", Optional.<String>absent(), ImmutableMap.of("reservedKey", "reservedValue1"))));
Assert.assertTrue(taskManager.getActiveTaskIds().size() == 0);
saveAndSchedule(request.toBuilder().setInstances(Optional.of(1)).setAllowedSlaveAttributes(Optional.of(allowedAttributes)));
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave1", "host1", Optional.<String>absent(), ImmutableMap.of("reservedKey", "reservedValue1"))));
Assert.assertTrue(taskManager.getActiveTaskIds().size() == 1);
}
@Test
public void testRequiredSlaveAttributesForRequest() {
Map<String, String> requiredAttributes = new HashMap<>();
requiredAttributes.put("requiredKey", "requiredValue1");
initRequest();
initFirstDeploy();
saveAndSchedule(request.toBuilder().setInstances(Optional.of(1)).setRequiredSlaveAttributes(Optional.of(requiredAttributes)));
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave1", "host1", Optional.<String>absent(), ImmutableMap.of("requiredKey", "notTheRightValue"))));
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave2", "host2", Optional.<String>absent(), ImmutableMap.of("notTheRightKey", "requiredValue1"))));
Assert.assertTrue(taskManager.getActiveTaskIds().size() == 0);
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave2", "host2", Optional.<String>absent(), requiredAttributes)));
Assert.assertTrue(taskManager.getActiveTaskIds().size() == 1);
}
@Test
public void testMultipleRequiredAttributes() {
Map<String, String> requiredAttributes = new HashMap<>();
requiredAttributes.put("requiredKey1", "requiredValue1");
requiredAttributes.put("requiredKey2", "requiredValue2");
initRequest();
initFirstDeploy();
saveAndSchedule(request.toBuilder().setInstances(Optional.of(1)).setRequiredSlaveAttributes(Optional.of(requiredAttributes)));
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave1", "host1", Optional.<String>absent(), ImmutableMap.of("requiredKey1", "requiredValue1"))));
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave2", "host2", Optional.<String>absent(), ImmutableMap.of("requiredKey1", "requiredValue1", "someotherkey", "someothervalue"))));
Assert.assertTrue(taskManager.getActiveTaskIds().size() == 0);
sms.resourceOffers(driver, Arrays.asList(createOffer(20, 20000, "slave2", "host2", Optional.<String>absent(), requiredAttributes)));
Assert.assertTrue(taskManager.getActiveTaskIds().size() == 1);
}
@Test
public void testEvenRackPlacement() {
// Set up 3 active racks
sms.resourceOffers(driver, Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1"))));
sms.resourceOffers(driver, Arrays.asList(createOffer(1, 128, "slave2", "host2", Optional.of("rack2"))));
sms.resourceOffers(driver, Arrays.asList(createOffer(1, 128, "slave3", "host3", Optional.of("rack3"))));
initRequest();
initFirstDeploy();
saveAndSchedule(request.toBuilder().setInstances(Optional.of(7)).setRackSensitive(Optional.of(true)));
// rack1 -> 1, rack2 -> 2, rack3 -> 3
sms.resourceOffers(driver, Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1"))));
sms.resourceOffers(driver, Arrays.asList(createOffer(1, 128, "slave2", "host2", Optional.of("rack2"))));
sms.resourceOffers(driver, Arrays.asList(createOffer(1, 128, "slave3", "host3", Optional.of("rack3"))));
Assert.assertEquals(3, taskManager.getActiveTaskIds().size());
sms.resourceOffers(driver, Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1"))));
Assert.assertEquals(4, taskManager.getActiveTaskIds().size());
sms.resourceOffers(driver, Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1"))));
Assert.assertEquals(4, taskManager.getActiveTaskIds().size());
sms.resourceOffers(driver, Arrays.asList(createOffer(1, 128, "slave2", "host2", Optional.of("rack2"))));
Assert.assertEquals(5, taskManager.getActiveTaskIds().size());
// rack1 should not get a third instance until rack3 has a second
sms.resourceOffers(driver, Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1"))));
Assert.assertEquals(5, taskManager.getActiveTaskIds().size());
sms.resourceOffers(driver, Arrays.asList(createOffer(1, 128, "slave3", "host3", Optional.of("rack3"))));
Assert.assertEquals(6, taskManager.getActiveTaskIds().size());
sms.resourceOffers(driver, Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1"))));
Assert.assertEquals(7, taskManager.getActiveTaskIds().size());
}
@Test
public void testRackPlacementOnScaleDown() {
try {
configuration.setRebalanceRacksOnScaleDown(true);
// Set up 3 active racks
sms.resourceOffers(driver, Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1"))));
sms.resourceOffers(driver, Arrays.asList(createOffer(1, 128, "slave2", "host2", Optional.of("rack2"))));
sms.resourceOffers(driver, Arrays.asList(createOffer(1, 128, "slave3", "host3", Optional.of("rack3"))));
initRequest();
initFirstDeploy();
saveAndSchedule(request.toBuilder().setInstances(Optional.of(7)).setRackSensitive(Optional.of(true)));
sms.resourceOffers(driver, Arrays.asList(createOffer(2, 256, "slave1", "host1", Optional.of("rack1"))));
sms.resourceOffers(driver, Arrays.asList(createOffer(2, 256, "slave2", "host2", Optional.of("rack2"))));
sms.resourceOffers(driver, Arrays.asList(createOffer(3, 384, "slave3", "host3", Optional.of("rack3"))));
Assert.assertEquals(7, taskManager.getActiveTaskIds().size());
requestResource.postRequest(request.toBuilder().setInstances(Optional.of(4)).setRackSensitive(Optional.of(true)).build());
scheduler.drainPendingQueue(stateCacheProvider.get());
Assert.assertEquals(4, taskManager.getNumCleanupTasks());
int rebalanceRackCleanups = 0;
for (SingularityTaskCleanup cleanup : taskManager.getCleanupTasks()) {
if (cleanup.getCleanupType() == TaskCleanupType.REBALANCE_RACKS) {
rebalanceRackCleanups++;
}
}
Assert.assertEquals(1, rebalanceRackCleanups);
Assert.assertEquals(1, taskManager.getPendingTaskIds().size());
} finally {
configuration.setRebalanceRacksOnScaleDown(false);
}
}
@Test
public void testPlacementOfBounceTasks() {
// Set up 1 active rack
sms.resourceOffers(driver, Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1"))));
initRequest();
initFirstDeploy();
SingularityRequest newRequest = request.toBuilder()
.setInstances(Optional.of(2))
.setRackSensitive(Optional.of(true))
.setSlavePlacement(Optional.of(SlavePlacement.SEPARATE))
.setAllowBounceToSameHost(Optional.of(true))
.build();
saveAndSchedule(newRequest.toBuilder());
scheduler.drainPendingQueue(stateCacheProvider.get());
sms.resourceOffers(driver, Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1"))));
sms.resourceOffers(driver, Arrays.asList(createOffer(1, 128, "slave2", "host2", Optional.of("rack1"))));
Assert.assertEquals(2, taskManager.getActiveTaskIds().size());
requestResource.bounce(requestId, Optional.absent());
cleaner.drainCleanupQueue();
scheduler.drainPendingQueue(stateCacheProvider.get());
Assert.assertEquals(2, taskManager.getNumCleanupTasks());
Assert.assertEquals(2, taskManager.getPendingTaskIds().size());
Assert.assertEquals(taskManager.getCleanupTasks().get(0).getActionId().get(), taskManager.getPendingTasks().get(0).getActionId().get());
// BOUNCE should allow a task to launch on the same host
sms.resourceOffers(driver, Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1"))));
Assert.assertEquals(3, taskManager.getActiveTaskIds().size());
// But not a second one from the same bounce
sms.resourceOffers(driver, Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1"))));
Assert.assertEquals(3, taskManager.getActiveTaskIds().size());
// Other pending type should not allow tasks on same host
saveAndSchedule(newRequest.toBuilder().setInstances(Optional.of(2)));
sms.resourceOffers(driver, Arrays.asList(createOffer(1, 128, "slave1", "host1", Optional.of("rack1"))));
Assert.assertEquals(3, taskManager.getActiveTaskIds().size());
}
}