// Copyright 2016 Twitter. All rights reserved. // // 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 com.twitter.heron.scheduler.mesos; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Properties; import java.util.Set; import org.apache.mesos.SchedulerDriver; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import com.twitter.heron.common.basics.ByteAmount; import com.twitter.heron.proto.scheduler.Scheduler; import com.twitter.heron.scheduler.mesos.framework.BaseContainer; import com.twitter.heron.scheduler.mesos.framework.MesosFramework; import com.twitter.heron.scheduler.utils.SchedulerUtils; import com.twitter.heron.spi.common.Config; import com.twitter.heron.spi.common.Key; import com.twitter.heron.spi.packing.PackingPlan; import com.twitter.heron.spi.packing.Resource; public class MesosSchedulerTest { private static final String TOPOLOGY_NAME = "testTopology"; private static final String ROLE = "role"; private static final long NUM_CONTAINER = 2; private static final String TOPOLOGY_PACKAGE_URI = "topologyPackageURI"; private static final String CORE_PACKAGE_URI = "corePackageURI"; private MesosScheduler scheduler; private MesosFramework mesosFramework; private BaseContainer baseContainer; @Before public void before() throws Exception { Config config = Mockito.mock(Config.class); Mockito.when(config.getStringValue(Key.TOPOLOGY_NAME)).thenReturn(TOPOLOGY_NAME); Mockito.when(config.getStringValue(Key.ROLE)).thenReturn(ROLE); Mockito.when(config.getStringValue(Key.CORE_PACKAGE_URI)).thenReturn(CORE_PACKAGE_URI); Config runtime = Mockito.mock(Config.class); Mockito.when(runtime.getLongValue(Key.NUM_CONTAINERS)).thenReturn(NUM_CONTAINER); Properties properties = new Properties(); properties.put(Key.TOPOLOGY_PACKAGE_URI.value(), TOPOLOGY_PACKAGE_URI); Mockito.when(runtime.get(Key.SCHEDULER_PROPERTIES)).thenReturn(properties); mesosFramework = Mockito.mock(MesosFramework.class); SchedulerDriver driver = Mockito.mock(SchedulerDriver.class); baseContainer = Mockito.mock(BaseContainer.class); scheduler = Mockito.spy(MesosScheduler.class); Mockito.doReturn(mesosFramework).when(scheduler).getMesosFramework(); Mockito.doReturn(driver).when(scheduler) .getSchedulerDriver(Mockito.anyString(), Mockito.eq(mesosFramework)); Mockito.doNothing().when(scheduler) .startSchedulerDriver(); scheduler.initialize(config, runtime); } @After public void after() throws Exception { scheduler.close(); } @Test public void testOnSchedule() throws Exception { Mockito.doReturn(baseContainer).when(scheduler) .getBaseContainer(Mockito.anyInt(), Mockito.any(PackingPlan.class)); scheduler.onSchedule(Mockito.mock(PackingPlan.class)); Map<Integer, BaseContainer> expectedJobDef = new HashMap<>(); for (int containerIndex = 0; containerIndex < NUM_CONTAINER; containerIndex++) { expectedJobDef.put(containerIndex, baseContainer); } Mockito.verify(mesosFramework).createJob(Mockito.eq(expectedJobDef)); } @Test public void testOnKill() throws Exception { scheduler.onKill(Scheduler.KillTopologyRequest.getDefaultInstance()); Mockito.verify(mesosFramework).killJob(); } @Test public void testOnRestart() throws Exception { Scheduler.RestartTopologyRequest request = Scheduler.RestartTopologyRequest.newBuilder() .setContainerIndex(-1) .setTopologyName(TOPOLOGY_NAME).build(); scheduler.onRestart(request); Mockito.verify(mesosFramework).restartJob(-1); } @Test public void testGetBaseContainer() throws Exception { final double CPU = 0.5; final ByteAmount MEM = ByteAmount.fromMegabytes(100); final ByteAmount DISK = ByteAmount.fromMegabytes(100); Resource containerResources = new Resource(CPU, MEM, DISK); PackingPlan.ContainerPlan containerPlan = new PackingPlan.ContainerPlan( 0, new HashSet<PackingPlan.InstancePlan>(), containerResources); Set<PackingPlan.ContainerPlan> containerPlans = new HashSet<>(); containerPlans.add(containerPlan); PackingPlan packingPlan = new PackingPlan(TOPOLOGY_NAME, containerPlans); BaseContainer container = scheduler.getBaseContainer(0, packingPlan); // Assert we have constructed the correct BaseContainer structure Assert.assertEquals(ROLE, container.runAsUser); Assert.assertEquals(CPU, container.cpu, 0.01); Assert.assertEquals(MEM, ByteAmount.fromMegabytes(((Double) container.memInMB).longValue())); Assert.assertEquals(DISK, ByteAmount.fromMegabytes(((Double) container.diskInMB).longValue())); Assert.assertEquals(SchedulerUtils.PORTS_REQUIRED_FOR_EXECUTOR, container.ports); Assert.assertEquals(2, container.dependencies.size()); Assert.assertTrue(container.dependencies.contains(CORE_PACKAGE_URI)); Assert.assertTrue(container.dependencies.contains(TOPOLOGY_PACKAGE_URI)); Assert.assertTrue(container.environmentVariables.isEmpty()); Assert.assertTrue(container.name.startsWith("container_0")); // Convert to JSON String str = container.toString(); String json = BaseContainer.getJobDefinitionInJSON(container); Assert.assertEquals(json, str); // Convert the JSON back to BaseContainer BaseContainer newContainer = BaseContainer.getJobFromJSONString(json); Assert.assertEquals(json, BaseContainer.getJobDefinitionInJSON(newContainer)); } }