/** * 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.reconciliation; import java.util.List; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicLong; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import org.apache.aurora.common.quantity.Amount; import org.apache.aurora.common.quantity.Time; import org.apache.aurora.common.stats.StatsProvider; import org.apache.aurora.common.testing.easymock.EasyMockTest; import org.apache.aurora.gen.AssignedTask; import org.apache.aurora.gen.ScheduleStatus; import org.apache.aurora.gen.ScheduledTask; import org.apache.aurora.gen.TaskEvent; import org.apache.aurora.scheduler.base.Query; import org.apache.aurora.scheduler.base.TaskTestUtil; import org.apache.aurora.scheduler.base.Tasks; import org.apache.aurora.scheduler.mesos.Driver; import org.apache.aurora.scheduler.storage.entities.IScheduledTask; import org.apache.aurora.scheduler.storage.entities.ITaskConfig; import org.apache.aurora.scheduler.storage.testing.StorageTestUtil; import org.apache.aurora.scheduler.testing.FakeScheduledExecutor; import org.apache.mesos.v1.Protos; import org.easymock.EasyMock; import org.junit.Before; import org.junit.Test; import static org.apache.aurora.common.quantity.Time.MINUTES; import static org.apache.aurora.common.quantity.Time.SECONDS; import static org.apache.aurora.scheduler.reconciliation.TaskReconciler.EXPLICIT_STAT_NAME; import static org.apache.aurora.scheduler.reconciliation.TaskReconciler.IMPLICIT_STAT_NAME; import static org.apache.aurora.scheduler.reconciliation.TaskReconciler.TASK_TO_PROTO; import static org.apache.aurora.scheduler.reconciliation.TaskReconciler.TaskReconcilerSettings; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expectLastCall; import static org.junit.Assert.assertEquals; public class TaskReconcilerTest extends EasyMockTest { private static final Amount<Long, Time> INITIAL_DELAY = Amount.of(10L, MINUTES); private static final Amount<Long, Time> EXPLICIT_SCHEDULE = Amount.of(60L, MINUTES); private static final Amount<Long, Time> IMPLICT_SCHEDULE = Amount.of(180L, MINUTES); private static final Amount<Long, Time> SPREAD = Amount.of(30L, MINUTES); private static final Amount<Long, Time> BATCH_DELAY = Amount.of(3L, SECONDS); private static final int BATCH_SIZE = 1; private static final TaskReconcilerSettings SETTINGS = new TaskReconcilerSettings( INITIAL_DELAY, EXPLICIT_SCHEDULE, IMPLICT_SCHEDULE, SPREAD, BATCH_DELAY, BATCH_SIZE); private StorageTestUtil storageUtil; private StatsProvider statsProvider; private Driver driver; private ScheduledExecutorService executorService; private AtomicLong explicitRuns; private AtomicLong implicitRuns; @Before public void setUp() { storageUtil = new StorageTestUtil(this); statsProvider = createMock(StatsProvider.class); driver = createMock(Driver.class); executorService = createMock(ScheduledExecutorService.class); explicitRuns = new AtomicLong(); implicitRuns = new AtomicLong(); } @Test public void testExecution() { expect(statsProvider.makeCounter(EXPLICIT_STAT_NAME)).andReturn(explicitRuns); expect(statsProvider.makeCounter(IMPLICIT_STAT_NAME)).andReturn(implicitRuns); FakeScheduledExecutor clock = FakeScheduledExecutor.scheduleAtFixedRateExecutor(executorService, 2, 5); IScheduledTask task1 = makeTask("id1", TaskTestUtil.makeConfig(TaskTestUtil.JOB)); IScheduledTask task2 = makeTask("id2", TaskTestUtil.makeConfig(TaskTestUtil.JOB)); storageUtil.expectOperations(); storageUtil.expectTaskFetch( Query.unscoped().byStatus(Tasks.SLAVE_ASSIGNED_STATES), task1, task2).times(7); List<List<Protos.TaskStatus>> batches = Lists.partition(ImmutableList.of( TASK_TO_PROTO.apply(task1), TASK_TO_PROTO.apply(task2)), BATCH_SIZE); driver.reconcileTasks(batches.get(0)); expectLastCall().times(7); driver.reconcileTasks(batches.get(1)); expectLastCall().times(7); driver.reconcileTasks(EasyMock.anyObject()); expectLastCall().times(3); control.replay(); TaskReconciler reconciler = new TaskReconciler( SETTINGS, storageUtil.storage, driver, executorService, statsProvider); reconciler.startAsync().awaitRunning(); clock.advance(INITIAL_DELAY); assertEquals(1L, explicitRuns.get()); assertEquals(0L, implicitRuns.get()); clock.advance(SPREAD); assertEquals(1L, explicitRuns.get()); assertEquals(1L, implicitRuns.get()); clock.advance(EXPLICIT_SCHEDULE); assertEquals(2L, explicitRuns.get()); assertEquals(1L, implicitRuns.get()); clock.advance(IMPLICT_SCHEDULE); assertEquals(5L, explicitRuns.get()); assertEquals(2L, implicitRuns.get()); reconciler.triggerExplicitReconciliation(Optional.of(BATCH_SIZE)); assertEquals(6L, explicitRuns.get()); reconciler.triggerImplicitReconciliation(); assertEquals(3L, implicitRuns.get()); reconciler.triggerExplicitReconciliation(Optional.absent()); assertEquals(7L, explicitRuns.get()); assertEquals(3L, implicitRuns.get()); } @Test(expected = IllegalArgumentException.class) public void testInvalidImplicitDelay() throws Exception { control.replay(); new TaskReconcilerSettings( INITIAL_DELAY, EXPLICIT_SCHEDULE, IMPLICT_SCHEDULE, Amount.of(Long.MAX_VALUE, MINUTES), BATCH_DELAY, BATCH_SIZE); } @Test(expected = IllegalArgumentException.class) public void testInvalidExplicitDelay() throws Exception { control.replay(); new TaskReconcilerSettings( Amount.of(Long.MAX_VALUE, MINUTES), EXPLICIT_SCHEDULE, IMPLICT_SCHEDULE, SPREAD, BATCH_DELAY, BATCH_SIZE); } private static IScheduledTask makeTask(String id, ITaskConfig config) { return IScheduledTask.build(new ScheduledTask() .setStatus(ScheduleStatus.ASSIGNED) .setTaskEvents(ImmutableList.of( new TaskEvent(100L, ScheduleStatus.ASSIGNED) .setMessage("message") .setScheduler("scheduler"), new TaskEvent(101L, ScheduleStatus.ASSIGNED) .setMessage("message") .setScheduler("scheduler2"))) .setAncestorId("ancestor") .setFailureCount(3) .setAssignedTask(new AssignedTask() .setInstanceId(2) .setTaskId(id) .setSlaveId("slave-id") .setAssignedPorts(ImmutableMap.of("http", 1000)) .setTask(config.newBuilder()))); } }