public class NotifyingSchedulingFilterTest extends EasyMockTest { private static final ITaskConfig TASK = ITaskConfig.build(new TaskConfig().setNumCpus(1).setRamMb(1024).setDiskMb(1024)); private static final TaskGroupKey GROUP_KEY = TaskGroupKey.from(TASK); private static final UnusedResource RESOURCE = new UnusedResource( ResourceSlot.from(TASK).withOverhead(TaskExecutors.NO_OVERHEAD_EXECUTOR), IHostAttributes.build( new HostAttributes().setHost("host").setMode(MaintenanceMode.NONE))); private static final ResourceRequest REQUEST = new ResourceRequest(TASK, AttributeAggregate.EMPTY); private static final Veto VETO_1 = Veto.insufficientResources("ram", 1); private static final Veto VETO_2 = Veto.insufficientResources("ram", 2); private SchedulingFilter filter; private EventSink eventSink; private SchedulingFilter delegate; @Before public void setUp() { delegate = createMock(SchedulingFilter.class); eventSink = createMock(EventSink.class); filter = new NotifyingSchedulingFilter(delegate, eventSink); } @Test public void testEvents() { Set<Veto> vetoes = ImmutableSet.of(VETO_1, VETO_2); expect(delegate.filter(RESOURCE, REQUEST)).andReturn(vetoes); eventSink.post(new Vetoed(GROUP_KEY, vetoes)); control.replay(); assertEquals(vetoes, filter.filter(RESOURCE, REQUEST)); } @Test public void testNoVetoes() { Set<Veto> vetoes = ImmutableSet.of(); expect(delegate.filter(RESOURCE, REQUEST)).andReturn(vetoes); control.replay(); assertEquals(vetoes, filter.filter(RESOURCE, REQUEST)); } }
@Test public void testDedicatedMismatchShortCircuits() { // Ensures that a dedicated mismatch short-circuits other filter operations, such as // evaluation of limit constraints. Reduction of task queries is the desired outcome. control.replay(); Constraint hostLimit = limitConstraint("host", 1); assertVetoes( makeTask(JOB_A, hostLimit, makeConstraint(DEDICATED_ATTRIBUTE, "xxx")), hostAttributes(HOST_A, host(HOST_A)), Veto.constraintMismatch(DEDICATED_ATTRIBUTE)); assertVetoes( makeTask(JOB_A, hostLimit, makeConstraint(DEDICATED_ATTRIBUTE, "xxx")), hostAttributes(HOST_B, dedicated(dedicatedFor(JOB_B)), host(HOST_B)), Veto.constraintMismatch(DEDICATED_ATTRIBUTE)); }
@Test public void testDedicatedRole() { control.replay(); IHostAttributes hostA = hostAttributes(HOST_A, dedicated(JOB_A.getRole())); checkConstraint(hostA, DEDICATED_ATTRIBUTE, true, JOB_A.getRole()); assertVetoes(makeTask(JOB_B), hostA, Veto.dedicatedHostConstraintMismatch()); }
@Test public void testHostDrainedForMaintenance() { control.replay(); assertVetoes( makeTask(), hostAttributes(HOST_A, MaintenanceMode.DRAINED, host(HOST_A), rack(RACK_A)), Veto.maintenance("drained")); }
@Test public void testAssignerDoesNotReturnOnFirstMismatch() throws Exception { // Ensures scheduling loop does not terminate prematurely when the first mismatch is identified. HostOffer mismatched = new HostOffer( Offer.newBuilder() .setId(OfferID.newBuilder().setValue("offerId0")) .setFrameworkId(FrameworkID.newBuilder().setValue("frameworkId")) .setSlaveId(SlaveID.newBuilder().setValue("slaveId0")) .setHostname("hostName0") .addResources( Resource.newBuilder() .setName("ports") .setType(Type.RANGES) .setRanges( Ranges.newBuilder() .addRange(Range.newBuilder().setBegin(PORT).setEnd(PORT)))) .build(), IHostAttributes.build(new HostAttributes())); expect(offerManager.getOffers(GROUP_KEY)).andReturn(ImmutableSet.of(mismatched, OFFER)); expect(tierManager.getTier(TASK.getAssignedTask().getTask())).andReturn(DEFAULT).times(2); expect( filter.filter( new UnusedResource( Resources.from(mismatched.getOffer()).slot(), mismatched.getAttributes()), new ResourceRequest(TASK.getAssignedTask().getTask(), EMPTY))) .andReturn(ImmutableSet.of(Veto.constraintMismatch("constraint mismatch"))); offerManager.banOffer(mismatched.getOffer().getId(), GROUP_KEY); expect( filter.filter( new UnusedResource(Resources.from(OFFER.getOffer()).slot(), OFFER.getAttributes()), new ResourceRequest(TASK.getAssignedTask().getTask(), EMPTY))) .andReturn(ImmutableSet.of()); expect( stateManager.assignTask( storeProvider, Tasks.id(TASK), OFFER.getOffer().getHostname(), OFFER.getOffer().getSlaveId(), ImmutableMap.of(PORT_NAME, PORT))) .andReturn(TASK.getAssignedTask()); expect(taskFactory.createFrom(TASK.getAssignedTask(), OFFER.getOffer().getSlaveId())) .andReturn(TASK_INFO); offerManager.launchTask(OFFER.getOffer().getId(), TASK_INFO); control.replay(); assertTrue( assigner.maybeAssign( storeProvider, new ResourceRequest(TASK.getAssignedTask().getTask(), EMPTY), TaskGroupKey.from(TASK.getAssignedTask().getTask()), Tasks.id(TASK), ImmutableMap.of(SLAVE_ID, GROUP_KEY))); }
@Test public void testAssignVetoes() { expect( filter.filter( new UnusedResource(ResourceSlot.from(MESOS_OFFER), OFFER.getAttributes()), new ResourceRequest(TASK.getAssignedTask().getTask(), EMPTY))) .andReturn(ImmutableSet.of(Veto.constraintMismatch("denied"))); control.replay(); assertEquals( Assignment.failure(ImmutableSet.of(Veto.constraintMismatch("denied"))), assigner.maybeAssign( storeProvider, OFFER, new ResourceRequest(TASK.getAssignedTask().getTask(), EMPTY), Tasks.id(TASK))); }
@Test public void testLimitWithinJob() throws Exception { control.replay(); AttributeAggregate stateA = AttributeAggregate.create( Suppliers.ofInstance( ImmutableList.of( host(HOST_A), rack(RACK_A), host(HOST_B), rack(RACK_A), host(HOST_B), rack(RACK_A), host(HOST_C), rack(RACK_B)))); AttributeAggregate stateB = AttributeAggregate.create( Suppliers.ofInstance( ImmutableList.of( host(HOST_A), rack(RACK_A), host(HOST_A), rack(RACK_A), host(HOST_B), rack(RACK_A)))); IHostAttributes hostA = hostAttributes(HOST_A, host(HOST_A), rack(RACK_A)); IHostAttributes hostB = hostAttributes(HOST_B, host(HOST_B), rack(RACK_A)); IHostAttributes hostC = hostAttributes(HOST_C, host(HOST_C), rack(RACK_B)); assertNoVetoes(hostLimitTask(JOB_A, 2), hostA, stateA); assertVetoes(hostLimitTask(JOB_A, 1), hostB, stateA, Veto.unsatisfiedLimit(HOST_ATTRIBUTE)); assertVetoes(hostLimitTask(JOB_A, 2), hostB, stateA, Veto.unsatisfiedLimit(HOST_ATTRIBUTE)); assertNoVetoes(hostLimitTask(JOB_A, 3), hostB, stateA); assertVetoes(rackLimitTask(JOB_A, 2), hostB, stateB, Veto.unsatisfiedLimit(RACK_ATTRIBUTE)); assertVetoes(rackLimitTask(JOB_A, 3), hostB, stateB, Veto.unsatisfiedLimit(RACK_ATTRIBUTE)); assertNoVetoes(rackLimitTask(JOB_A, 4), hostB, stateB); assertNoVetoes(rackLimitTask(JOB_A, 1), hostC, stateB); assertVetoes(rackLimitTask(JOB_A, 1), hostC, stateA, Veto.unsatisfiedLimit(RACK_ATTRIBUTE)); assertNoVetoes(rackLimitTask(JOB_A, 2), hostC, stateB); }
@Test public void testGetPendingReason() throws Exception { Builder query = Query.unscoped().byJob(JOB_KEY); Builder filterQuery = Query.unscoped().byJob(JOB_KEY).byStatus(ScheduleStatus.PENDING); String taskId1 = "task_id_test1"; String taskId2 = "task_id_test2"; ImmutableSet<Veto> result = ImmutableSet.of(Veto.constraintMismatch("first"), Veto.constraintMismatch("second")); ITaskConfig taskConfig = ITaskConfig.build(defaultTask(true)); IScheduledTask pendingTask1 = IScheduledTask.build( new ScheduledTask() .setAssignedTask( new AssignedTask().setTaskId(taskId1).setTask(taskConfig.newBuilder())) .setStatus(ScheduleStatus.PENDING)); IScheduledTask pendingTask2 = IScheduledTask.build( new ScheduledTask() .setAssignedTask( new AssignedTask().setTaskId(taskId2).setTask(taskConfig.newBuilder())) .setStatus(ScheduleStatus.PENDING)); storageUtil.expectTaskFetch(filterQuery, pendingTask1, pendingTask2); expect(nearestFit.getNearestFit(TaskGroupKey.from(taskConfig))).andReturn(result).times(2); control.replay(); String reason = "Constraint not satisfied: first,Constraint not satisfied: second"; Set<PendingReason> expected = ImmutableSet.of( new PendingReason().setTaskId(taskId1).setReason(reason), new PendingReason().setTaskId(taskId2).setReason(reason)); Response response = assertOkResponse(thrift.getPendingReason(query.get())); assertEquals(expected, response.getResult().getGetPendingReasonResult().getReasons()); }
@Test public void testVetoGroups() { control.replay(); assertEquals(VetoGroup.EMPTY, Veto.identifyGroup(ImmutableSet.of())); assertEquals( VetoGroup.STATIC, Veto.identifyGroup( ImmutableSet.of( Veto.constraintMismatch("denied"), Veto.insufficientResources("ram", 100), Veto.maintenance("draining")))); assertEquals( VetoGroup.DYNAMIC, Veto.identifyGroup(ImmutableSet.of(Veto.unsatisfiedLimit("denied")))); assertEquals( VetoGroup.MIXED, Veto.identifyGroup( ImmutableSet.of( Veto.insufficientResources("ram", 100), Veto.unsatisfiedLimit("denied")))); }
@Test public void testMultipleTaskConstraints() { control.replay(); Constraint constraint1 = makeConstraint("host", HOST_A); Constraint constraint2 = makeConstraint(DEDICATED_ATTRIBUTE, "xxx"); assertVetoes( makeTask(JOB_A, constraint1, constraint2), hostAttributes(HOST_A, dedicated(HOST_A), host(HOST_A)), Veto.constraintMismatch(DEDICATED_ATTRIBUTE)); assertNoVetoes( makeTask(JOB_B, constraint1, constraint2), hostAttributes(HOST_B, dedicated("xxx"), host(HOST_A))); }
@Test public void testAssignVetoesWithNoStaticBan() throws Exception { expect(offerManager.getOffers(GROUP_KEY)).andReturn(ImmutableSet.of(OFFER)); expect(tierManager.getTier(TASK.getAssignedTask().getTask())).andReturn(DEFAULT); expect(filter.filter(UNUSED, RESOURCE_REQUEST)) .andReturn(ImmutableSet.of(Veto.unsatisfiedLimit("limit"))); control.replay(); assertFalse( assigner.maybeAssign( storeProvider, new ResourceRequest(TASK.getAssignedTask().getTask(), EMPTY), TaskGroupKey.from(TASK.getAssignedTask().getTask()), Tasks.id(TASK), NO_RESERVATION)); }
@Test public void testAttributes() { control.replay(); IHostAttributes hostA = hostAttributes( HOST_A, valueAttribute("jvm", "1.4", "1.6", "1.7"), valueAttribute("zone", "a", "b", "c")); // Matches attribute, matching value. checkConstraint(hostA, "jvm", true, "1.4"); // Matches attribute, different value. checkConstraint(hostA, "jvm", false, "1.0"); // Does not match attribute. checkConstraint(hostA, "xxx", false, "1.4"); // Logical 'OR' with attribute and value match. checkConstraint(hostA, "jvm", true, "1.2", "1.4"); // Does not match attribute. checkConstraint(hostA, "xxx", false, "1.0", "1.4"); // Check that logical AND works. Constraint jvmConstraint = makeConstraint("jvm", "1.6"); Constraint zoneConstraint = makeConstraint("zone", "c"); ITaskConfig task = makeTask(JOB_A, jvmConstraint, zoneConstraint); assertEquals( ImmutableSet.of(), defaultFilter.filter( new UnusedResource(DEFAULT_OFFER, hostA), new ResourceRequest(task, EMPTY))); Constraint jvmNegated = jvmConstraint.deepCopy(); jvmNegated.getConstraint().getValue().setNegated(true); Constraint zoneNegated = jvmConstraint.deepCopy(); zoneNegated.getConstraint().getValue().setNegated(true); assertVetoes(makeTask(JOB_A, jvmNegated, zoneNegated), hostA, Veto.constraintMismatch("jvm")); }