/*
* Copyright 2014 LinkedIn Corp.
*
* 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 azkaban.execapp;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import azkaban.event.Event;
import azkaban.event.Event.Type;
import azkaban.event.EventData;
import azkaban.executor.ExecutableFlow;
import azkaban.executor.ExecutableNode;
import azkaban.executor.ExecutorLoader;
import azkaban.executor.JavaJob;
import azkaban.executor.MockExecutorLoader;
import azkaban.executor.SleepJavaJob;
import azkaban.executor.Status;
import azkaban.jobExecutor.ProcessJob;
import azkaban.jobtype.JobTypeManager;
import azkaban.utils.Props;
public class JobRunnerTest {
private File workingDir;
private JobTypeManager jobtypeManager;
private Logger logger = Logger.getLogger("JobRunnerTest");
public JobRunnerTest() {
}
@Before
public void setUp() throws Exception {
System.out.println("Create temp dir");
workingDir = new File("_AzkabanTestDir_" + System.currentTimeMillis());
if (workingDir.exists()) {
FileUtils.deleteDirectory(workingDir);
}
workingDir.mkdirs();
jobtypeManager =
new JobTypeManager(null, null, this.getClass().getClassLoader());
jobtypeManager.getJobTypePluginSet().addPluginClass("java", JavaJob.class);
}
@After
public void tearDown() throws IOException {
System.out.println("Teardown temp dir");
if (workingDir != null) {
FileUtils.deleteDirectory(workingDir);
workingDir = null;
}
}
@Ignore @Test
public void testBasicRun() {
MockExecutorLoader loader = new MockExecutorLoader();
EventCollectorListener eventCollector = new EventCollectorListener();
JobRunner runner =
createJobRunner(1, "testJob", 1, false, loader, eventCollector);
ExecutableNode node = runner.getNode();
eventCollector.handleEvent(Event.create(null, Event.Type.JOB_STARTED, new EventData(node.getStatus())));
Assert.assertTrue(runner.getStatus() != Status.SUCCEEDED
|| runner.getStatus() != Status.FAILED);
runner.run();
eventCollector.handleEvent(Event.create(null, Event.Type.JOB_FINISHED, new EventData(node.getStatus())));
Assert.assertTrue(runner.getStatus() == node.getStatus());
Assert.assertTrue("Node status is " + node.getStatus(),
node.getStatus() == Status.SUCCEEDED);
Assert.assertTrue(node.getStartTime() > 0 && node.getEndTime() > 0);
Assert.assertTrue(node.getEndTime() - node.getStartTime() > 1000);
File logFile = new File(runner.getLogFilePath());
Props outputProps = runner.getNode().getOutputProps();
Assert.assertTrue(outputProps != null);
Assert.assertTrue(logFile.exists());
Assert.assertTrue(loader.getNodeUpdateCount(node.getId()) == 3);
Assert.assertTrue(eventCollector.checkOrdering());
try {
eventCollector.checkEventExists(new Type[] { Type.JOB_STARTED,
Type.JOB_STATUS_CHANGED, Type.JOB_FINISHED });
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
@Ignore @Test
public void testFailedRun() {
MockExecutorLoader loader = new MockExecutorLoader();
EventCollectorListener eventCollector = new EventCollectorListener();
JobRunner runner =
createJobRunner(1, "testJob", 1, true, loader, eventCollector);
ExecutableNode node = runner.getNode();
Assert.assertTrue(runner.getStatus() != Status.SUCCEEDED
|| runner.getStatus() != Status.FAILED);
runner.run();
Assert.assertTrue(runner.getStatus() == node.getStatus());
Assert.assertTrue(node.getStatus() == Status.FAILED);
Assert.assertTrue(node.getStartTime() > 0 && node.getEndTime() > 0);
Assert.assertTrue(node.getEndTime() - node.getStartTime() > 1000);
File logFile = new File(runner.getLogFilePath());
Props outputProps = runner.getNode().getOutputProps();
Assert.assertTrue(outputProps == null);
Assert.assertTrue(logFile.exists());
Assert.assertTrue(eventCollector.checkOrdering());
Assert.assertTrue(!runner.isKilled());
Assert.assertTrue(loader.getNodeUpdateCount(node.getId()) == 3);
try {
eventCollector.checkEventExists(new Type[] { Type.JOB_STARTED,
Type.JOB_STATUS_CHANGED, Type.JOB_FINISHED });
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
@Test
public void testDisabledRun() {
MockExecutorLoader loader = new MockExecutorLoader();
EventCollectorListener eventCollector = new EventCollectorListener();
JobRunner runner =
createJobRunner(1, "testJob", 1, false, loader, eventCollector);
ExecutableNode node = runner.getNode();
node.setStatus(Status.DISABLED);
// Should be disabled.
Assert.assertTrue(runner.getStatus() == Status.DISABLED);
runner.run();
Assert.assertTrue(runner.getStatus() == node.getStatus());
Assert.assertTrue(node.getStatus() == Status.SKIPPED);
Assert.assertTrue(node.getStartTime() > 0 && node.getEndTime() > 0);
// Give it 10 ms to fail.
Assert.assertTrue(node.getEndTime() - node.getStartTime() < 10);
// Log file and output files should not exist.
Props outputProps = runner.getNode().getOutputProps();
Assert.assertTrue(outputProps == null);
Assert.assertTrue(runner.getLogFilePath() == null);
Assert.assertTrue(eventCollector.checkOrdering());
Assert.assertTrue(loader.getNodeUpdateCount(node.getId()) == null);
try {
eventCollector.checkEventExists(new Type[] { Type.JOB_STARTED,
Type.JOB_FINISHED });
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
@Test
public void testPreKilledRun() {
MockExecutorLoader loader = new MockExecutorLoader();
EventCollectorListener eventCollector = new EventCollectorListener();
JobRunner runner =
createJobRunner(1, "testJob", 1, false, loader, eventCollector);
ExecutableNode node = runner.getNode();
node.setStatus(Status.KILLED);
// Should be killed.
Assert.assertTrue(runner.getStatus() == Status.KILLED);
runner.run();
// Should just skip the run and not change
Assert.assertTrue(runner.getStatus() == node.getStatus());
Assert.assertTrue(node.getStatus() == Status.KILLED);
Assert.assertTrue(node.getStartTime() > 0 && node.getEndTime() > 0);
// Give it 10 ms to fail.
Assert.assertTrue(node.getEndTime() - node.getStartTime() < 10);
Assert.assertTrue(loader.getNodeUpdateCount(node.getId()) == null);
// Log file and output files should not exist.
Props outputProps = runner.getNode().getOutputProps();
Assert.assertTrue(outputProps == null);
Assert.assertTrue(runner.getLogFilePath() == null);
Assert.assertTrue(!runner.isKilled());
try {
eventCollector.checkEventExists(new Type[] { Type.JOB_STARTED,
Type.JOB_FINISHED });
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
@Ignore @Test
// todo: HappyRay investigate if it is worth fixing this test. If not, remove it.
// The change history doesn't mention why this test was ignored.
public void testCancelRun() throws InterruptedException {
MockExecutorLoader loader = new MockExecutorLoader();
EventCollectorListener eventCollector = new EventCollectorListener();
JobRunner runner =
createJobRunner(13, "testJob", 10, false, loader, eventCollector);
ExecutableNode node = runner.getNode();
Assert.assertTrue(runner.getStatus() != Status.SUCCEEDED
|| runner.getStatus() != Status.FAILED);
Thread thread = new Thread(runner);
thread.start();
Thread.sleep(2000);
runner.kill();
Thread.sleep(500);
Assert.assertTrue(runner.getStatus() == node.getStatus());
Assert.assertTrue("Status is " + node.getStatus(),
node.getStatus() == Status.KILLED);
Assert.assertTrue(node.getStartTime() > 0 && node.getEndTime() > 0);
// Give it 10 ms to fail.
Assert.assertTrue(node.getEndTime() - node.getStartTime() < 3000);
Assert.assertTrue(loader.getNodeUpdateCount(node.getId()) == 3);
// Log file and output files should not exist.
File logFile = new File(runner.getLogFilePath());
Props outputProps = runner.getNode().getOutputProps();
Assert.assertTrue(outputProps == null);
Assert.assertTrue(logFile.exists());
Assert.assertTrue(eventCollector.checkOrdering());
Assert.assertTrue(runner.isKilled());
try {
eventCollector.checkEventExists(new Type[] { Type.JOB_STARTED,
Type.JOB_STATUS_CHANGED, Type.JOB_FINISHED });
} catch (Exception e) {
System.out.println(e.getMessage());
Assert.fail(e.getMessage());
}
}
@Ignore @Test
public void testDelayedExecutionJob() {
MockExecutorLoader loader = new MockExecutorLoader();
EventCollectorListener eventCollector = new EventCollectorListener();
JobRunner runner =
createJobRunner(1, "testJob", 1, false, loader, eventCollector);
runner.setDelayStart(5000);
long startTime = System.currentTimeMillis();
ExecutableNode node = runner.getNode();
eventCollector.handleEvent(Event.create(null, Event.Type.JOB_STARTED, new EventData(node.getStatus())));
Assert.assertTrue(runner.getStatus() != Status.SUCCEEDED);
runner.run();
eventCollector.handleEvent(Event.create(null, Event.Type.JOB_FINISHED, new EventData(node.getStatus())));
Assert.assertTrue(runner.getStatus() == node.getStatus());
Assert.assertTrue("Node status is " + node.getStatus(),
node.getStatus() == Status.SUCCEEDED);
Assert.assertTrue(node.getStartTime() > 0 && node.getEndTime() > 0);
Assert.assertTrue(node.getEndTime() - node.getStartTime() > 1000);
Assert.assertTrue(node.getStartTime() - startTime >= 5000);
File logFile = new File(runner.getLogFilePath());
Props outputProps = runner.getNode().getOutputProps();
Assert.assertTrue(outputProps != null);
Assert.assertTrue(logFile.exists());
Assert.assertFalse(runner.isKilled());
Assert.assertTrue(loader.getNodeUpdateCount(node.getId()) == 3);
Assert.assertTrue(eventCollector.checkOrdering());
try {
eventCollector.checkEventExists(new Type[] { Type.JOB_STARTED,
Type.JOB_STATUS_CHANGED, Type.JOB_FINISHED });
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
@Test
public void testDelayedExecutionCancelledJob() throws InterruptedException {
MockExecutorLoader loader = new MockExecutorLoader();
EventCollectorListener eventCollector = new EventCollectorListener();
JobRunner runner =
createJobRunner(1, "testJob", 1, false, loader, eventCollector);
runner.setDelayStart(5000);
long startTime = System.currentTimeMillis();
ExecutableNode node = runner.getNode();
eventCollector.handleEvent(Event.create(null, Event.Type.JOB_STARTED, new EventData(node.getStatus())));
Assert.assertTrue(runner.getStatus() != Status.SUCCEEDED);
Thread thread = new Thread(runner);
thread.start();
Thread.sleep(2000);
runner.kill();
Thread.sleep(500);
eventCollector.handleEvent(Event.create(null, Event.Type.JOB_FINISHED, new EventData(node.getStatus())));
Assert.assertTrue(runner.getStatus() == node.getStatus());
Assert.assertTrue("Node status is " + node.getStatus(),
node.getStatus() == Status.KILLED);
Assert.assertTrue(node.getStartTime() > 0 && node.getEndTime() > 0);
Assert.assertTrue(node.getEndTime() - node.getStartTime() < 1000);
Assert.assertTrue(node.getStartTime() - startTime >= 2000);
Assert.assertTrue(node.getStartTime() - startTime <= 5000);
Assert.assertTrue(runner.isKilled());
File logFile = new File(runner.getLogFilePath());
Props outputProps = runner.getNode().getOutputProps();
Assert.assertTrue(outputProps == null);
Assert.assertTrue(logFile.exists());
Assert.assertTrue(eventCollector.checkOrdering());
try {
eventCollector.checkEventExists(new Type[] { Type.JOB_FINISHED });
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
private Props createProps(int sleepSec, boolean fail) {
Props props = new Props();
props.put("type", "java");
props.put(JavaJob.JOB_CLASS, SleepJavaJob.class.getName());
props.put("seconds", sleepSec);
props.put(ProcessJob.WORKING_DIR, workingDir.getPath());
props.put("fail", String.valueOf(fail));
return props;
}
private JobRunner createJobRunner(int execId, String name, int time,
boolean fail, ExecutorLoader loader, EventCollectorListener listener) {
return createJobRunner(execId, name, time, fail, loader, listener, new Props());
}
private JobRunner createJobRunner(int execId, String name, int time,
boolean fail, ExecutorLoader loader, EventCollectorListener listener, Props azkabanProps) {
ExecutableFlow flow = new ExecutableFlow();
flow.setExecutionId(execId);
ExecutableNode node = new ExecutableNode();
node.setId(name);
node.setParentFlow(flow);
Props props = createProps(time, fail);
node.setInputProps(props);
HashSet<String> proxyUsers = new HashSet<String>();
proxyUsers.add(flow.getSubmitUser());
JobRunner runner = new JobRunner(node, workingDir, loader, jobtypeManager, azkabanProps);
runner.setLogSettings(logger, "5MB", 4);
runner.addListener(listener);
return runner;
}
}