/*
* 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.tez.dag.app.launcher;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.io.IOException;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.yarn.api.records.ApplicationId;
import org.apache.hadoop.yarn.api.records.Container;
import org.apache.hadoop.yarn.api.records.ContainerId;
import org.apache.hadoop.yarn.api.records.ContainerLaunchContext;
import org.apache.hadoop.yarn.api.records.NodeId;
import org.apache.hadoop.yarn.event.Event;
import org.apache.hadoop.yarn.event.EventHandler;
import org.apache.tez.common.TezUtils;
import org.apache.tez.dag.api.NamedEntityDescriptor;
import org.apache.tez.dag.api.TezConstants;
import org.apache.tez.dag.api.TezException;
import org.apache.tez.dag.api.UserPayload;
import org.apache.tez.dag.app.AppContext;
import org.apache.tez.dag.app.TaskCommunicatorManagerInterface;
import org.apache.tez.dag.app.dag.DAG;
import org.apache.tez.dag.app.dag.event.DAGAppMasterEventType;
import org.apache.tez.dag.app.dag.event.DAGAppMasterEventUserServiceFatalError;
import org.apache.tez.dag.app.dag.event.DAGEventTerminateDag;
import org.apache.tez.dag.app.rm.ContainerLauncherLaunchRequestEvent;
import org.apache.tez.dag.app.rm.ContainerLauncherStopRequestEvent;
import org.apache.tez.dag.helpers.DagInfoImplForTest;
import org.apache.tez.dag.records.TezDAGID;
import org.apache.tez.serviceplugins.api.ContainerLaunchRequest;
import org.apache.tez.serviceplugins.api.ContainerLauncher;
import org.apache.tez.serviceplugins.api.ContainerLauncherContext;
import org.apache.tez.serviceplugins.api.ContainerStopRequest;
import org.apache.tez.serviceplugins.api.ServicePluginErrorDefaults;
import org.apache.tez.serviceplugins.api.ServicePluginException;
import org.apache.tez.serviceplugins.api.TaskCommunicatorDescriptor;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
public class TestContainerLauncherManager {
@Before
@After
public void resetTest() {
ContainerLaucherRouterForMultipleLauncherTest.reset();
}
@Test(timeout = 5000)
public void testNoLaunchersSpecified() throws IOException, TezException {
AppContext appContext = mock(AppContext.class);
TaskCommunicatorManagerInterface tal = mock(TaskCommunicatorManagerInterface.class);
try {
new ContainerLaucherRouterForMultipleLauncherTest(appContext, tal, null, null,
false);
fail("Expecting a failure without any launchers being specified");
} catch (IllegalArgumentException e) {
}
}
@Test(timeout = 5000)
public void testCustomLauncherSpecified() throws IOException, TezException {
Configuration conf = new Configuration(false);
AppContext appContext = mock(AppContext.class);
TaskCommunicatorManagerInterface tal = mock(TaskCommunicatorManagerInterface.class);
String customLauncherName = "customLauncher";
List<NamedEntityDescriptor> launcherDescriptors = new LinkedList<>();
ByteBuffer bb = ByteBuffer.allocate(4);
bb.putInt(0, 3);
UserPayload customPayload = UserPayload.create(bb);
launcherDescriptors.add(
new NamedEntityDescriptor(customLauncherName, FakeContainerLauncher.class.getName())
.setUserPayload(customPayload));
ContainerLaucherRouterForMultipleLauncherTest clr =
new ContainerLaucherRouterForMultipleLauncherTest(appContext, tal, null,
launcherDescriptors,
true);
try {
clr.init(conf);
clr.start();
assertEquals(1, clr.getNumContainerLaunchers());
assertFalse(clr.getYarnContainerLauncherCreated());
assertFalse(clr.getUberContainerLauncherCreated());
assertEquals(customLauncherName, clr.getContainerLauncherName(0));
assertEquals(bb, clr.getContainerLauncherContext(0).getInitialUserPayload().getPayload());
} finally {
clr.stop();
}
}
@Test(timeout = 5000)
public void testMultipleContainerLaunchers() throws IOException, TezException {
Configuration conf = new Configuration(false);
conf.set("testkey", "testvalue");
UserPayload userPayload = TezUtils.createUserPayloadFromConf(conf);
AppContext appContext = mock(AppContext.class);
TaskCommunicatorManagerInterface tal = mock(TaskCommunicatorManagerInterface.class);
String customLauncherName = "customLauncher";
List<NamedEntityDescriptor> launcherDescriptors = new LinkedList<>();
ByteBuffer bb = ByteBuffer.allocate(4);
bb.putInt(0, 3);
UserPayload customPayload = UserPayload.create(bb);
launcherDescriptors.add(
new NamedEntityDescriptor(customLauncherName, FakeContainerLauncher.class.getName())
.setUserPayload(customPayload));
launcherDescriptors
.add(new NamedEntityDescriptor(TezConstants.getTezYarnServicePluginName(), null)
.setUserPayload(userPayload));
ContainerLaucherRouterForMultipleLauncherTest clr =
new ContainerLaucherRouterForMultipleLauncherTest(appContext, tal, null,
launcherDescriptors,
true);
try {
clr.init(conf);
clr.start();
assertEquals(2, clr.getNumContainerLaunchers());
assertTrue(clr.getYarnContainerLauncherCreated());
assertFalse(clr.getUberContainerLauncherCreated());
assertEquals(customLauncherName, clr.getContainerLauncherName(0));
assertEquals(bb, clr.getContainerLauncherContext(0).getInitialUserPayload().getPayload());
assertEquals(TezConstants.getTezYarnServicePluginName(), clr.getContainerLauncherName(1));
Configuration confParsed = TezUtils
.createConfFromUserPayload(clr.getContainerLauncherContext(1).getInitialUserPayload());
assertEquals("testvalue", confParsed.get("testkey"));
} finally {
clr.stop();
}
}
@Test(timeout = 5000)
public void testEventRouting() throws Exception {
Configuration conf = new Configuration(false);
UserPayload userPayload = TezUtils.createUserPayloadFromConf(conf);
AppContext appContext = mock(AppContext.class);
TaskCommunicatorManagerInterface tal = mock(TaskCommunicatorManagerInterface.class);
String customLauncherName = "customLauncher";
List<NamedEntityDescriptor> launcherDescriptors = new LinkedList<>();
ByteBuffer bb = ByteBuffer.allocate(4);
bb.putInt(0, 3);
UserPayload customPayload = UserPayload.create(bb);
launcherDescriptors.add(
new NamedEntityDescriptor(customLauncherName, FakeContainerLauncher.class.getName())
.setUserPayload(customPayload));
launcherDescriptors
.add(new NamedEntityDescriptor(TezConstants.getTezYarnServicePluginName(), null)
.setUserPayload(userPayload));
ContainerLaucherRouterForMultipleLauncherTest clr =
new ContainerLaucherRouterForMultipleLauncherTest(appContext, tal, null,
launcherDescriptors,
true);
try {
clr.init(conf);
clr.start();
assertEquals(2, clr.getNumContainerLaunchers());
assertTrue(clr.getYarnContainerLauncherCreated());
assertFalse(clr.getUberContainerLauncherCreated());
assertEquals(customLauncherName, clr.getContainerLauncherName(0));
assertEquals(TezConstants.getTezYarnServicePluginName(), clr.getContainerLauncherName(1));
verify(clr.getTestContainerLauncher(0)).initialize();
verify(clr.getTestContainerLauncher(0)).start();
verify(clr.getTestContainerLauncher(1)).initialize();
verify(clr.getTestContainerLauncher(1)).start();
ContainerLaunchContext clc1 = mock(ContainerLaunchContext.class);
Container container1 = mock(Container.class);
ContainerLaunchContext clc2 = mock(ContainerLaunchContext.class);
Container container2 = mock(Container.class);
ContainerLauncherLaunchRequestEvent launchRequestEvent1 =
new ContainerLauncherLaunchRequestEvent(clc1, container1, 0, 0, 0);
ContainerLauncherLaunchRequestEvent launchRequestEvent2 =
new ContainerLauncherLaunchRequestEvent(clc2, container2, 1, 0, 0);
clr.handle(launchRequestEvent1);
ArgumentCaptor<ContainerLaunchRequest> captor =
ArgumentCaptor.forClass(ContainerLaunchRequest.class);
verify(clr.getTestContainerLauncher(0)).launchContainer(captor.capture());
assertEquals(1, captor.getAllValues().size());
ContainerLaunchRequest launchRequest1 = captor.getValue();
assertEquals(clc1, launchRequest1.getContainerLaunchContext());
clr.handle(launchRequestEvent2);
captor = ArgumentCaptor.forClass(ContainerLaunchRequest.class);
verify(clr.getTestContainerLauncher(1)).launchContainer(captor.capture());
assertEquals(1, captor.getAllValues().size());
ContainerLaunchRequest launchRequest2 = captor.getValue();
assertEquals(clc2, launchRequest2.getContainerLaunchContext());
} finally {
clr.stop();
verify(clr.getTestContainerLauncher(0)).shutdown();
verify(clr.getTestContainerLauncher(1)).shutdown();
}
}
@SuppressWarnings("unchecked")
@Test(timeout = 5000)
public void testReportFailureFromContainerLauncher() throws ServicePluginException, TezException {
final String dagName = DAG_NAME;
final int dagIndex = DAG_INDEX;
TezDAGID dagId = TezDAGID.getInstance(ApplicationId.newInstance(0, 0), dagIndex);
DAG dag = mock(DAG.class);
doReturn(dagName).when(dag).getName();
doReturn(dagId).when(dag).getID();
EventHandler eventHandler = mock(EventHandler.class);
AppContext appContext = mock(AppContext.class);
doReturn(eventHandler).when(appContext).getEventHandler();
doReturn(dag).when(appContext).getCurrentDAG();
doReturn("testlauncher").when(appContext).getContainerLauncherName(0);
NamedEntityDescriptor<TaskCommunicatorDescriptor> taskCommDescriptor =
new NamedEntityDescriptor<>("testlauncher", ContainerLauncherForTest.class.getName());
List<NamedEntityDescriptor> list = new LinkedList<>();
list.add(taskCommDescriptor);
ContainerLauncherManager containerLauncherManager =
new ContainerLauncherManager(appContext, mock(TaskCommunicatorManagerInterface.class), "",
list, false);
try {
ContainerLaunchContext clc1 = mock(ContainerLaunchContext.class);
Container container1 = mock(Container.class);
ContainerLauncherLaunchRequestEvent launchRequestEvent =
new ContainerLauncherLaunchRequestEvent(clc1, container1, 0, 0, 0);
containerLauncherManager.handle(launchRequestEvent);
ArgumentCaptor<Event> argumentCaptor = ArgumentCaptor.forClass(Event.class);
verify(eventHandler, times(1)).handle(argumentCaptor.capture());
Event rawEvent = argumentCaptor.getValue();
assertTrue(rawEvent instanceof DAGAppMasterEventUserServiceFatalError);
DAGAppMasterEventUserServiceFatalError event =
(DAGAppMasterEventUserServiceFatalError) rawEvent;
assertEquals(DAGAppMasterEventType.CONTAINER_LAUNCHER_SERVICE_FATAL_ERROR, event.getType());
assertTrue(event.getDiagnosticInfo().contains("ReportedFatalError"));
assertTrue(
event.getDiagnosticInfo().contains(ServicePluginErrorDefaults.INCONSISTENT_STATE.name()));
assertTrue(event.getDiagnosticInfo().contains("[0:testlauncher]"));
reset(eventHandler);
// stop container
ContainerId containerId2 = mock(ContainerId.class);
NodeId nodeId2 = mock(NodeId.class);
ContainerLauncherStopRequestEvent stopRequestEvent =
new ContainerLauncherStopRequestEvent(containerId2, nodeId2, null, 0, 0, 0);
argumentCaptor = ArgumentCaptor.forClass(Event.class);
containerLauncherManager.handle(stopRequestEvent);
verify(eventHandler, times(1)).handle(argumentCaptor.capture());
rawEvent = argumentCaptor.getValue();
assertTrue(rawEvent instanceof DAGEventTerminateDag);
DAGEventTerminateDag killEvent = (DAGEventTerminateDag) rawEvent;
assertTrue(killEvent.getDiagnosticInfo().contains("ReportError"));
assertTrue(killEvent.getDiagnosticInfo()
.contains(ServicePluginErrorDefaults.SERVICE_UNAVAILABLE.name()));
assertTrue(killEvent.getDiagnosticInfo().contains("[0:testlauncher]"));
} finally {
containerLauncherManager.stop();
}
}
@SuppressWarnings("unchecked")
@Test(timeout = 5000)
public void testContainerLauncherUserError() throws ServicePluginException {
ContainerLauncher containerLauncher = mock(ContainerLauncher.class);
EventHandler eventHandler = mock(EventHandler.class);
AppContext appContext = mock(AppContext.class);
doReturn(eventHandler).when(appContext).getEventHandler();
doReturn("testlauncher").when(appContext).getContainerLauncherName(0);
Configuration conf = new Configuration(false);
ContainerLauncherManager containerLauncherManager =
new ContainerLauncherManager(appContext);
containerLauncherManager.setContainerLauncher(containerLauncher);
try {
containerLauncherManager.init(conf);
containerLauncherManager.start();
// launch container
doThrow(new RuntimeException("testexception")).when(containerLauncher)
.launchContainer(any(ContainerLaunchRequest.class));
ContainerLaunchContext clc1 = mock(ContainerLaunchContext.class);
Container container1 = mock(Container.class);
ContainerLauncherLaunchRequestEvent launchRequestEvent =
new ContainerLauncherLaunchRequestEvent(clc1, container1, 0, 0, 0);
containerLauncherManager.handle(launchRequestEvent);
ArgumentCaptor<Event> argumentCaptor = ArgumentCaptor.forClass(Event.class);
verify(eventHandler, times(1)).handle(argumentCaptor.capture());
Event rawEvent = argumentCaptor.getValue();
assertTrue(rawEvent instanceof DAGAppMasterEventUserServiceFatalError);
DAGAppMasterEventUserServiceFatalError event =
(DAGAppMasterEventUserServiceFatalError) rawEvent;
assertEquals(DAGAppMasterEventType.CONTAINER_LAUNCHER_SERVICE_FATAL_ERROR, event.getType());
assertTrue(event.getError().getMessage().contains("testexception"));
assertTrue(event.getDiagnosticInfo().contains("launching container"));
assertTrue(event.getDiagnosticInfo().contains("[0:testlauncher]"));
reset(eventHandler);
// stop container
doThrow(new RuntimeException("teststopexception")).when(containerLauncher)
.stopContainer(any(ContainerStopRequest.class));
ContainerId containerId2 = mock(ContainerId.class);
NodeId nodeId2 = mock(NodeId.class);
ContainerLauncherStopRequestEvent stopRequestEvent =
new ContainerLauncherStopRequestEvent(containerId2, nodeId2, null, 0, 0, 0);
argumentCaptor = ArgumentCaptor.forClass(Event.class);
containerLauncherManager.handle(stopRequestEvent);
verify(eventHandler, times(1)).handle(argumentCaptor.capture());
rawEvent = argumentCaptor.getValue();
assertTrue(rawEvent instanceof DAGAppMasterEventUserServiceFatalError);
event = (DAGAppMasterEventUserServiceFatalError) rawEvent;
assertTrue(event.getError().getMessage().contains("teststopexception"));
assertTrue(event.getDiagnosticInfo().contains("stopping container"));
assertTrue(event.getDiagnosticInfo().contains("[0:testlauncher]"));
} finally {
containerLauncherManager.stop();
}
}
private static class ContainerLaucherRouterForMultipleLauncherTest
extends ContainerLauncherManager {
// All variables setup as static since methods being overridden are invoked by the ContainerLauncherRouter ctor,
// and regular variables will not be initialized at this point.
private static final AtomicInteger numContainerLaunchers = new AtomicInteger(0);
private static final Set<Integer> containerLauncherIndices = new HashSet<>();
private static final ContainerLauncher yarnContainerLauncher = mock(ContainerLauncher.class);
private static final ContainerLauncher uberContainerlauncher = mock(ContainerLauncher.class);
private static final AtomicBoolean yarnContainerLauncherCreated = new AtomicBoolean(false);
private static final AtomicBoolean uberContainerLauncherCreated = new AtomicBoolean(false);
private static final List<ContainerLauncherContext> containerLauncherContexts =
new LinkedList<>();
private static final List<String> containerLauncherNames = new LinkedList<>();
private static final List<ContainerLauncher> testContainerLaunchers = new LinkedList<>();
public static void reset() {
numContainerLaunchers.set(0);
containerLauncherIndices.clear();
yarnContainerLauncherCreated.set(false);
uberContainerLauncherCreated.set(false);
containerLauncherContexts.clear();
containerLauncherNames.clear();
testContainerLaunchers.clear();
}
public ContainerLaucherRouterForMultipleLauncherTest(AppContext context,
TaskCommunicatorManagerInterface taskCommunicatorManagerInterface,
String workingDirectory,
List<NamedEntityDescriptor> containerLauncherDescriptors,
boolean isPureLocalMode) throws
UnknownHostException, TezException {
super(context, taskCommunicatorManagerInterface, workingDirectory,
containerLauncherDescriptors, isPureLocalMode);
}
@Override
ContainerLauncher createContainerLauncher(NamedEntityDescriptor containerLauncherDescriptor,
AppContext context,
ContainerLauncherContext containerLauncherContext,
TaskCommunicatorManagerInterface taskCommunicatorManagerInterface,
String workingDirectory,
int containerLauncherIndex,
boolean isPureLocalMode) throws TezException {
numContainerLaunchers.incrementAndGet();
boolean added = containerLauncherIndices.add(containerLauncherIndex);
assertTrue("Cannot add multiple launchers with the same index", added);
containerLauncherNames.add(containerLauncherDescriptor.getEntityName());
containerLauncherContexts.add(containerLauncherContext);
return super
.createContainerLauncher(containerLauncherDescriptor, context, containerLauncherContext,
taskCommunicatorManagerInterface, workingDirectory, containerLauncherIndex, isPureLocalMode);
}
@Override
ContainerLauncher createYarnContainerLauncher(
ContainerLauncherContext containerLauncherContext) {
yarnContainerLauncherCreated.set(true);
testContainerLaunchers.add(yarnContainerLauncher);
return yarnContainerLauncher;
}
@Override
ContainerLauncher createUberContainerLauncher(ContainerLauncherContext containerLauncherContext,
AppContext context,
TaskCommunicatorManagerInterface taskCommunicatorManagerInterface,
String workingDirectory,
boolean isPureLocalMode) {
uberContainerLauncherCreated.set(true);
testContainerLaunchers.add(uberContainerlauncher);
return uberContainerlauncher;
}
@Override
ContainerLauncher createCustomContainerLauncher(
ContainerLauncherContext containerLauncherContext,
NamedEntityDescriptor containerLauncherDescriptor) throws TezException {
ContainerLauncher spyLauncher = spy(super.createCustomContainerLauncher(
containerLauncherContext, containerLauncherDescriptor));
testContainerLaunchers.add(spyLauncher);
return spyLauncher;
}
public int getNumContainerLaunchers() {
return numContainerLaunchers.get();
}
public boolean getYarnContainerLauncherCreated() {
return yarnContainerLauncherCreated.get();
}
public boolean getUberContainerLauncherCreated() {
return uberContainerLauncherCreated.get();
}
public String getContainerLauncherName(int containerLauncherIndex) {
return containerLauncherNames.get(containerLauncherIndex);
}
public ContainerLauncher getTestContainerLauncher(int containerLauncherIndex) {
return testContainerLaunchers.get(containerLauncherIndex);
}
public ContainerLauncherContext getContainerLauncherContext(int containerLauncherIndex) {
return containerLauncherContexts.get(containerLauncherIndex);
}
}
public static class FakeContainerLauncher extends ContainerLauncher {
public FakeContainerLauncher(
ContainerLauncherContext containerLauncherContext) {
super(containerLauncherContext);
}
@Override
public void launchContainer(ContainerLaunchRequest launchRequest) {
}
@Override
public void stopContainer(ContainerStopRequest stopRequest) {
}
}
private static final String DAG_NAME = "dagName";
private static final int DAG_INDEX = 1;
public static class ContainerLauncherForTest extends ContainerLauncher {
public ContainerLauncherForTest(
ContainerLauncherContext containerLauncherContext) {
super(containerLauncherContext);
}
@Override
public void launchContainer(ContainerLaunchRequest launchRequest) throws
ServicePluginException {
getContext().reportError(ServicePluginErrorDefaults.INCONSISTENT_STATE, "ReportedFatalError", null);
}
@Override
public void stopContainer(ContainerStopRequest stopRequest) throws ServicePluginException {
getContext()
.reportError(ServicePluginErrorDefaults.SERVICE_UNAVAILABLE, "ReportError", new DagInfoImplForTest(DAG_INDEX, DAG_NAME));
}
}
}