/* * ProActive Parallel Suite(TM): * The Open Source library for parallel and distributed * Workflows & Scheduling, Orchestration, Cloud Automation * and Big Data Analysis on Enterprise Grids & Clouds. * * Copyright (c) 2007 - 2017 ActiveEon * Contact: contact@activeeon.com * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation: version 3 of * the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * If needed, contact us to obtain a release under GPL Version 2 or 3 * or a different license than the AGPL. */ package functionaltests; import static functionaltests.RestFuncTHelper.getRestServerUrl; import static functionaltests.jobs.SimpleJob.TEST_JOB; import java.io.File; import java.net.URI; import java.net.URL; import java.util.Map; import java.util.Stack; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.commons.io.IOUtils; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.objectweb.proactive.core.util.wrapper.StringWrapper; import org.ow2.proactive.authentication.ConnectionInfo; import org.ow2.proactive.scheduler.common.NotificationData; import org.ow2.proactive.scheduler.common.SchedulerEvent; import org.ow2.proactive.scheduler.common.SchedulerEventListener; import org.ow2.proactive.scheduler.common.SchedulerStatus; import org.ow2.proactive.scheduler.common.job.Job; import org.ow2.proactive.scheduler.common.job.JobId; import org.ow2.proactive.scheduler.common.job.JobInfo; import org.ow2.proactive.scheduler.common.job.JobResult; import org.ow2.proactive.scheduler.common.job.JobState; import org.ow2.proactive.scheduler.common.job.JobStatus; import org.ow2.proactive.scheduler.common.job.TaskFlowJob; import org.ow2.proactive.scheduler.common.job.UserIdentification; import org.ow2.proactive.scheduler.common.task.ForkEnvironment; import org.ow2.proactive.scheduler.common.task.ScriptTask; import org.ow2.proactive.scheduler.common.task.TaskId; import org.ow2.proactive.scheduler.common.task.TaskInfo; import org.ow2.proactive.scheduler.common.task.TaskResult; import org.ow2.proactive.scheduler.common.task.TaskState; import org.ow2.proactive.scheduler.common.task.TaskStatus; import org.ow2.proactive.scheduler.rest.ISchedulerClient; import org.ow2.proactive.scheduler.rest.SchedulerClient; import org.ow2.proactive.scheduler.task.exceptions.TaskException; import org.ow2.proactive.scripting.SimpleScript; import org.ow2.proactive.scripting.TaskScript; import com.google.common.io.Files; import functionaltests.jobs.ErrorTask; import functionaltests.jobs.LogTask; import functionaltests.jobs.MetadataTask; import functionaltests.jobs.NonTerminatingJob; import functionaltests.jobs.SimpleJob; import functionaltests.jobs.VariableTask; public class SchedulerClientTest extends AbstractRestFuncTestCase { /** Maximum wait time of 5 minutes */ private static final long MAX_WAIT_TIME = 5 * 60 * 1000; @Rule public TemporaryFolder testFolder = new TemporaryFolder(); @BeforeClass public static void beforeClass() throws Exception { init(2); } @Test(timeout = MAX_WAIT_TIME) public void testLogin() throws Exception { clientInstance(); } @Test(timeout = MAX_WAIT_TIME) public void testRenewSession() throws Exception { ISchedulerClient client = clientInstance(); SchedulerStatus status = client.getStatus(); assertNotNull(status); // use an invalid session client.setSession("invalid-session-identifier"); // client should automatically renew the session identifier status = client.getStatus(); assertNotNull(status); } @Test(timeout = MAX_WAIT_TIME) public void testDisconnect() throws Exception { ISchedulerClient client = clientInstance(); client.disconnect(); Assert.assertFalse(client.isConnected()); client = clientInstance(); Assert.assertTrue(client.isConnected()); } @Test(timeout = MAX_WAIT_TIME) public void testWaitForTerminatingJob() throws Exception { ISchedulerClient client = clientInstance(); Job job = defaultJob(); JobId jobId = submitJob(job, client); // should return immediately client.waitForJob(jobId, TimeUnit.MINUTES.toMillis(3)); } @Test(timeout = MAX_WAIT_TIME) public void testJobResult() throws Throwable { ISchedulerClient client = clientInstance(); Job job = createJobManyTasks("JobResult", SimpleJob.class, ErrorTask.class, LogTask.class, VariableTask.class, MetadataTask.class); JobId jobId = submitJob(job, client); JobResult result = client.waitForJob(jobId, TimeUnit.MINUTES.toMillis(3)); // job result Assert.assertNotNull(result.getJobId()); Assert.assertNotNull(result.getJobInfo()); Assert.assertEquals(JobStatus.FINISHED, result.getJobInfo().getStatus()); // the following check cannot work because of the way the job id is created on the client side. //Assert.assertEquals(job.getName(), result.getName()); Assert.assertTrue(result.hadException()); Assert.assertEquals(1, result.getExceptionResults().size()); // job info checkJobInfo(result.getJobInfo()); checkJobInfo(client.getJobInfo(jobId.value())); JobState jobState = client.getJobState(jobId.value()); JobStatus status = jobState.getStatus(); Assert.assertFalse(status.isJobAlive()); Assert.assertEquals(JobStatus.FINISHED, status); checkJobInfo(jobState.getJobInfo()); TaskState errorTaskState = findTask(getTaskNameForClass(ErrorTask.class), jobState.getHMTasks()); Assert.assertNotNull(errorTaskState); TaskState simpleTaskState = findTask(getTaskNameForClass(SimpleJob.class), jobState.getHMTasks()); Assert.assertNotNull(simpleTaskState); Assert.assertEquals(TaskStatus.FAULTY, errorTaskState.getStatus()); Assert.assertEquals(TaskStatus.FINISHED, simpleTaskState.getStatus()); // task result simple TaskResult tResSimple = result.getResult(getTaskNameForClass(SimpleJob.class)); Assert.assertNotNull(tResSimple.value()); Assert.assertEquals(new StringWrapper(TEST_JOB), tResSimple.value()); // task result with error TaskResult tResError = result.getResult(getTaskNameForClass(ErrorTask.class)); Assert.assertNotNull(tResError); Assert.assertTrue(tResError.hadException()); Assert.assertNotNull(tResError.getException()); Assert.assertTrue(tResError.getException() instanceof TaskException); // task result with logs TaskResult tResLog = result.getResult(getTaskNameForClass(LogTask.class)); Assert.assertNotNull(tResLog); Assert.assertNotNull(tResLog.getOutput()); System.err.println(tResLog.getOutput().getStdoutLogs(false)); Assert.assertTrue(tResLog.getOutput().getStdoutLogs(false).contains(LogTask.HELLO_WORLD)); // task result with variables TaskResult tResVar = result.getResult(getTaskNameForClass(VariableTask.class)); Assert.assertNotNull(tResVar.getPropagatedVariables()); Assert.assertTrue(tResVar.getPropagatedVariables().containsKey(VariableTask.MYVAR)); // task result with metadata TaskResult tMetaVar = result.getResult(getTaskNameForClass(MetadataTask.class)); Assert.assertNotNull(tMetaVar.getMetadata()); Assert.assertTrue(tMetaVar.getMetadata().containsKey(MetadataTask.MYVAR)); } private TaskState findTask(String name, Map<TaskId, TaskState> hmTasks) { for (TaskId taskId : hmTasks.keySet()) { if (taskId.getReadableName().equals(name)) { return hmTasks.get(taskId); } } return null; } private void checkJobInfo(JobInfo jobInfo) { Assert.assertNotNull(jobInfo); Assert.assertEquals(5, jobInfo.getTotalNumberOfTasks()); Assert.assertEquals(5, jobInfo.getNumberOfFinishedTasks()); Assert.assertEquals(1, jobInfo.getNumberOfFaultyTasks()); } @Test(timeout = MAX_WAIT_TIME) public void testSchedulerNodeClient() throws Throwable { ISchedulerClient client = clientInstance(); Job job = nodeClientJob("/functionaltests/descriptors/scheduler_client_node.groovy", "/functionaltests/descriptors/scheduler_client_node_fork.groovy"); JobId jobId = submitJob(job, client); TaskResult tres = client.waitForTask(jobId.toString(), "NodeClientTask", TimeUnit.MINUTES.toMillis(5)); System.out.println(tres.getOutput().getAllLogs(false)); Assert.assertNotNull(tres); Assert.assertEquals("Hello NodeClientTask I'm HelloTask", tres.value()); } @Test(timeout = MAX_WAIT_TIME) public void testDataSpaceNodeClientPushPull() throws Throwable { ISchedulerClient client = clientInstance(); Job job = nodeClientJob("/functionaltests/descriptors/dataspace_client_node_push_pull.groovy", "/functionaltests/descriptors/dataspace_client_node_fork.groovy"); JobId jobId = submitJob(job, client); TaskResult tres = client.waitForTask(jobId.toString(), "NodeClientTask", TimeUnit.MINUTES.toMillis(5)); System.out.println(tres.getOutput().getAllLogs(false)); Assert.assertNotNull(tres); Assert.assertEquals("HelloWorld", tres.value()); } @Test(timeout = MAX_WAIT_TIME) public void testDataSpaceNodeClientPushDelete() throws Throwable { ISchedulerClient client = clientInstance(); Job job = nodeClientJob("/functionaltests/descriptors/dataspace_client_node_push_delete.groovy", "/functionaltests/descriptors/dataspace_client_node_fork.groovy"); JobId jobId = submitJob(job, client); TaskResult tres = client.waitForTask(jobId.toString(), "NodeClientTask", TimeUnit.MINUTES.toMillis(5)); System.out.println(tres.getOutput().getAllLogs(false)); Assert.assertNotNull(tres); Assert.assertEquals("OK", tres.value()); } protected Job nodeClientJob(String groovyScript, String forkScript) throws Exception { URL scriptURL = SchedulerClientTest.class.getResource(groovyScript); URL forkScriptURL = SchedulerClientTest.class.getResource(forkScript); TaskFlowJob job = new TaskFlowJob(); job.setName("NodeClientJob"); ScriptTask task = new ScriptTask(); task.setName("NodeClientTask"); ForkEnvironment forkEnvironment = new ForkEnvironment(); forkEnvironment.setEnvScript(new SimpleScript(IOUtils.toString(forkScriptURL.toURI()), "groovy")); task.setForkEnvironment(forkEnvironment); task.setScript(new TaskScript(new SimpleScript(IOUtils.toString(scriptURL.toURI()), "groovy"))); job.addTask(task); return job; } @Test(timeout = MAX_WAIT_TIME, expected = TimeoutException.class) public void testWaitForNonTerminatingJob() throws Exception { ISchedulerClient client = clientInstance(); Job job = pendingJob(); JobId jobId = submitJob(job, client); try { client.waitForJob(jobId, TimeUnit.SECONDS.toMillis(10)); } finally { // Once the TimeoutException has been thrown // kill the job to free the node client.killJob(jobId); } } @Test(timeout = MAX_WAIT_TIME) public void testPushPullDeleteEmptyFile() throws Exception { File emptyFile = File.createTempFile("emptyFile", ".tmp"); ISchedulerClient client = clientInstance(); // Push the empty file into the userspace client.pushFile("USERSPACE", "", emptyFile.getName(), emptyFile.getCanonicalPath()); // Delete the local file Assert.assertTrue("Unable to delete the local file after push, maybe there are still some open streams?", emptyFile.delete()); // Pull it from the userspace to be sure that it was pushed client.pullFile("USERSPACE", "", emptyFile.getCanonicalPath()); // Check the file was pulled Assert.assertTrue("Unable to pull the empty file, maybe the pull mechanism is broken?", emptyFile.exists()); // Delete the local file Assert.assertTrue("Unable to delete the local file after pull, maybe there are still some open streams?", emptyFile.delete()); // Delete the file in the user space client.deleteFile("USERSPACE", "/" + emptyFile.getName()); //TODO: TEST THIS // LATER } @Test(timeout = MAX_WAIT_TIME * 2) public void testJobSubmissionEventListener() throws Exception { ISchedulerClient client = clientInstance(); SchedulerEventListenerImpl listener = new SchedulerEventListenerImpl(); client.addEventListener(listener, true, SchedulerEvent.JOB_SUBMITTED); Job job = defaultJob(); JobId jobId = client.submit(job); JobState submittedJob = listener.getSubmittedJob(); while (!submittedJob.getId().value().equals(jobId.value())) { submittedJob = listener.getSubmittedJob(); } client.removeEventListener(); client.waitForJob(jobId, TimeUnit.SECONDS.toMillis(120)); } @Test(timeout = MAX_WAIT_TIME) public void testKillTask() throws Exception { ISchedulerClient client = clientInstance(); Job job = createJob(NonTerminatingJob.class); SchedulerEventListenerImpl listener = new SchedulerEventListenerImpl(); client.addEventListener(listener, true, SchedulerEvent.TASK_PENDING_TO_RUNNING); JobId jobId = submitJob(job, client); TaskInfo startedTask = listener.getStartedTask(); while (!startedTask.getJobId().value().equals(jobId.value())) { startedTask = listener.getStartedTask(); } client.killTask(jobId, getTaskNameForClass(NonTerminatingJob.class)); client.removeEventListener(); // should return immediately client.waitForJob(jobId, TimeUnit.MINUTES.toMillis(3)); } @Test(timeout = MAX_WAIT_TIME) public void testPushFileWithNonAdminUserPwdShouldSucceed() throws Exception { File tmpFile = testFolder.newFile(); Files.write("non_admin_user_push_file_contents".getBytes(), tmpFile); ISchedulerClient client = SchedulerClient.createInstance(); client.init(new ConnectionInfo(getRestServerUrl(), getNonAdminLogin(), getNonAdminLoginPassword(), null, true)); client.pushFile("USERSPACE", "/test_non_admin_user_push_file", "tmpfile.tmp", tmpFile.getAbsolutePath()); String destDirPath = URI.create(client.getUserSpaceURIs().get(0)).getPath(); File destFile = new File(destDirPath, "test_non_admin_user_push_file/tmpfile.tmp"); assertTrue(Files.equal(tmpFile, destFile)); } private ISchedulerClient clientInstance() throws Exception { ISchedulerClient client = SchedulerClient.createInstance(); client.init(new ConnectionInfo(getRestServerUrl(), getLogin(), getPassword(), null, true)); return client; } private JobId submitJob(Job job, ISchedulerClient client) throws Exception { return client.submit(job); } private static class SchedulerEventListenerImpl implements SchedulerEventListener { private Stack<JobState> jobStateStack = new Stack<>(); private Stack<TaskInfo> taskStateStack = new Stack<>(); @Override public void jobSubmittedEvent(JobState jobState) { System.out.println("JobSubmittedEvent()"); synchronized (this) { jobStateStack.push(jobState); notifyAll(); } } public JobState getSubmittedJob() { System.out.println("getSubmittedJob"); synchronized (this) { if (jobStateStack.isEmpty()) { System.out.println("Stack is empty"); try { System.out.println("wait"); wait(); } catch (InterruptedException ie) { } } return jobStateStack.pop(); } } public TaskInfo getStartedTask() { System.out.println("getStartedTask"); synchronized (this) { if (taskStateStack.isEmpty()) { System.out.println("Stack is empty"); try { System.out.println("wait"); wait(); } catch (InterruptedException ie) { } } return taskStateStack.pop(); } } @Override public void jobStateUpdatedEvent(NotificationData<JobInfo> arg0) { } @Override public void schedulerStateUpdatedEvent(SchedulerEvent arg0) { } @Override public void taskStateUpdatedEvent(NotificationData<TaskInfo> data) { System.out.println("taskStateUpdatedEvent() : " + data); synchronized (this) { taskStateStack.push(data.getData()); notifyAll(); } } @Override public void usersUpdatedEvent(NotificationData<UserIdentification> arg0) { } @Override public void jobUpdatedFullDataEvent(JobState job) { } } }