/*
*
* Copyright 2016 Netflix, Inc.
*
* 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.netflix.genie.web.controllers;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.netflix.genie.common.dto.Application;
import com.netflix.genie.common.dto.ApplicationStatus;
import com.netflix.genie.common.dto.Cluster;
import com.netflix.genie.common.dto.ClusterCriteria;
import com.netflix.genie.common.dto.ClusterStatus;
import com.netflix.genie.common.dto.Command;
import com.netflix.genie.common.dto.CommandStatus;
import com.netflix.genie.common.dto.JobExecution;
import com.netflix.genie.common.dto.JobRequest;
import com.netflix.genie.common.dto.JobStatus;
import com.netflix.genie.core.jpa.repositories.JpaApplicationRepository;
import com.netflix.genie.core.jpa.repositories.JpaClusterRepository;
import com.netflix.genie.core.jpa.repositories.JpaCommandRepository;
import com.netflix.genie.core.jpa.repositories.JpaJobExecutionRepository;
import com.netflix.genie.core.jpa.repositories.JpaJobMetadataRepository;
import com.netflix.genie.core.jpa.repositories.JpaJobRepository;
import com.netflix.genie.core.jpa.repositories.JpaJobRequestRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.SystemUtils;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.restdocs.headers.HeaderDocumentation;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders;
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
import org.springframework.restdocs.operation.preprocess.Preprocessors;
import org.springframework.restdocs.payload.PayloadDocumentation;
import org.springframework.restdocs.request.RequestDocumentation;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
/**
* Integration tests for Jobs REST API.
*
* @author amsharma
* @author tgianos
* @since 3.0.0
*/
@Slf4j
public class JobRestControllerIntegrationTests extends RestControllerIntegrationTestsBase {
private static final long SLEEP_TIME = 1000L;
private static final String COMMAND_ARGS_PATH = "$.commandArgs";
private static final String STATUS_MESSAGE_PATH = "$.statusMsg";
private static final String CLUSTER_NAME_PATH = "$.clusterName";
private static final String COMMAND_NAME_PATH = "$.commandName";
private static final String ARCHIVE_LOCATION_PATH = "$.archiveLocation";
private static final String STARTED_PATH = "$.started";
private static final String FINISHED_PATH = "$.finished";
private static final String CLUSTER_CRITERIAS_PATH = "$.clusterCriterias";
private static final String COMMAND_CRITERIA_PATH = "$.commandCriteria";
private static final String GROUP_PATH = "$.group";
private static final String DISABLE_LOG_ARCHIVAL_PATH = "$.disableLogArchival";
private static final String EMAIL_PATH = "$.email";
private static final String CPU_PATH = "$.cpu";
private static final String MEMORY_PATH = "$.memory";
private static final String DEPENDENCIES_PATH = "$.dependencies";
private static final String APPLICATIONS_PATH = "$.applications";
private static final String HOST_NAME_PATH = "$.hostName";
private static final String PROCESS_ID_PATH = "$.processId";
private static final String CHECK_DELAY_PATH = "$.checkDelay";
private static final String EXIT_CODE_PATH = "$.exitCode";
private static final long CHECK_DELAY = 1L;
private static final String BASE_DIR
= "com/netflix/genie/web/controllers/JobRestControllerIntegrationTests/";
private static final String FILE_DELIMITER = "/";
private static final String JOB_NAME = "List * ... Directories bash job";
private static final String JOB_USER = "genie";
private static final String JOB_VERSION = "1.0";
private static final String JOB_DESCRIPTION = "Genie 3 Test Job";
private static final String JOB_STATUS_MSG = "Job finished successfully.";
private static final String APP1_ID = "app1";
private static final String APP1_NAME = "Application 1";
private static final String APP1_USER = "genie";
private static final String APP1_VERSION = "1.0";
private static final String APP2_ID = "app2";
private static final String APP2_NAME = "Application 2";
private static final String CMD1_ID = "cmd1";
private static final String CMD1_NAME = "Unix Bash command";
private static final String CMD1_USER = "genie";
private static final String CMD1_VERSION = "1.0";
private static final String CMD1_EXECUTABLE = "/bin/bash";
private static final String CLUSTER1_ID = "cluster1";
private static final String CLUSTER1_NAME = "Local laptop";
private static final String CLUSTER1_USER = "genie";
private static final String CLUSTER1_VERSION = "1.0";
private static final String JOBS_LIST_PATH = EMBEDDED_PATH + ".jobSearchResultList";
private ResourceLoader resourceLoader;
@Autowired
private JpaJobRepository jobRepository;
@Autowired
private JpaJobRequestRepository jobRequestRepository;
@Autowired
private JpaJobMetadataRepository jobRequestMetadataRepository;
@Autowired
private JpaJobExecutionRepository jobExecutionRepository;
@Autowired
private JpaApplicationRepository applicationRepository;
@Autowired
private JpaCommandRepository commandRepository;
@Autowired
private JpaClusterRepository clusterRepository;
@Autowired
private String hostname;
@Autowired
private Resource jobDirResource;
@Value("${genie.file.cache.location}")
private String baseCacheLocation;
/**
* Setup for tests.
*
* @throws Exception If there is an error.
*/
@Before
public void setup() throws Exception {
this.resourceLoader = new DefaultResourceLoader();
//this.jobsBaseUrl = "http://localhost:" + this.port + "/api/v3/jobs";
createAnApplication(APP1_ID, APP1_NAME);
createAnApplication(APP2_ID, APP2_NAME);
createAllClusters();
createAllCommands();
linkAllEntities();
}
private void linkAllEntities() throws Exception {
final List<String> apps = new ArrayList<>();
apps.add(APP1_ID);
apps.add(APP2_ID);
this.mvc
.perform(
MockMvcRequestBuilders
.post(COMMANDS_API + FILE_DELIMITER + CMD1_ID + FILE_DELIMITER + APPLICATIONS_LINK_KEY)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(apps))
)
.andExpect(MockMvcResultMatchers.status().isNoContent());
final List<String> cmds = Lists.newArrayList(CMD1_ID);
this.mvc
.perform(
MockMvcRequestBuilders
.post(CLUSTERS_API + FILE_DELIMITER + CLUSTER1_ID + FILE_DELIMITER + COMMANDS_LINK_KEY)
.contentType(MediaType.APPLICATION_JSON)
.content(this.objectMapper.writeValueAsBytes(cmds))
)
.andExpect(MockMvcResultMatchers.status().isNoContent());
}
private void createAnApplication(final String id, final String appName) throws Exception {
final String setUpFile = this.resourceLoader.getResource(
BASE_DIR
+ id
+ FILE_DELIMITER
+ "setupfile"
).getFile().getAbsolutePath();
final Set<String> app1Dependencies = new HashSet<>();
final String depFile1 = this.resourceLoader.getResource(
BASE_DIR
+ id
+ FILE_DELIMITER
+ "dep1"
).getFile().getAbsolutePath();
final String depFile2 = this.resourceLoader.getResource(
BASE_DIR
+ id
+ FILE_DELIMITER
+ "dep2"
).getFile().getAbsolutePath();
app1Dependencies.add(depFile1);
app1Dependencies.add(depFile2);
final Set<String> app1Configs = new HashSet<>();
final String configFile1 = this.resourceLoader.getResource(
BASE_DIR
+ id
+ FILE_DELIMITER
+ "config1"
).getFile().getAbsolutePath();
final String configFile2 = this.resourceLoader.getResource(
BASE_DIR
+ id
+ FILE_DELIMITER
+ "config2"
).getFile().getAbsolutePath();
app1Configs.add(configFile1);
app1Configs.add(configFile2);
final Application app = new Application.Builder(
appName,
APP1_USER,
APP1_VERSION,
ApplicationStatus.ACTIVE)
.withId(id)
.withSetupFile(setUpFile)
.withConfigs(app1Configs)
.withDependencies(app1Dependencies)
.build();
this.mvc
.perform(
MockMvcRequestBuilders
.post(APPLICATIONS_API)
.contentType(MediaType.APPLICATION_JSON)
.content(this.objectMapper.writeValueAsBytes(app))
)
.andExpect(MockMvcResultMatchers.status().isCreated())
.andExpect(MockMvcResultMatchers.header().string(HttpHeaders.LOCATION, Matchers.notNullValue()));
}
private void createAllClusters() throws Exception {
final String setUpFile = this.resourceLoader.getResource(
BASE_DIR + CLUSTER1_ID + FILE_DELIMITER + "setupfile"
).getFile().getAbsolutePath();
final Set<String> configs = new HashSet<>();
final String configFile1 = this.resourceLoader.getResource(
BASE_DIR + CLUSTER1_ID + FILE_DELIMITER + "config1"
).getFile().getAbsolutePath();
final String configFile2 = this.resourceLoader.getResource(
BASE_DIR + CLUSTER1_ID + FILE_DELIMITER + "config2"
).getFile().getAbsolutePath();
configs.add(configFile1);
configs.add(configFile2);
final Set<String> tags = new HashSet<>();
tags.add("localhost");
final Cluster cluster = new Cluster.Builder(
CLUSTER1_NAME,
CLUSTER1_USER,
CLUSTER1_VERSION,
ClusterStatus.UP
)
.withId(CLUSTER1_ID)
.withSetupFile(setUpFile)
.withConfigs(configs)
.withTags(tags)
.build();
this.mvc
.perform(
MockMvcRequestBuilders
.post(CLUSTERS_API)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(cluster))
)
.andExpect(MockMvcResultMatchers.status().isCreated())
.andExpect(MockMvcResultMatchers.header().string(HttpHeaders.LOCATION, Matchers.notNullValue()));
}
private void createAllCommands() throws Exception {
final String setUpFile = this.resourceLoader.getResource(
BASE_DIR + CMD1_ID + FILE_DELIMITER + "setupfile"
).getFile().getAbsolutePath();
final Set<String> configs = new HashSet<>();
final String configFile1 = this.resourceLoader.getResource(
BASE_DIR + CMD1_ID + FILE_DELIMITER + "config1"
).getFile().getAbsolutePath();
final String configFile2 = this.resourceLoader.getResource(
BASE_DIR + CMD1_ID + FILE_DELIMITER + "config2"
).getFile().getAbsolutePath();
configs.add(configFile1);
configs.add(configFile2);
final Set<String> tags = new HashSet<>();
tags.add("bash");
final Command cmd = new Command.Builder(
CMD1_NAME,
CMD1_USER,
CMD1_VERSION,
CommandStatus.ACTIVE,
CMD1_EXECUTABLE,
CHECK_DELAY
)
.withId(CMD1_ID)
.withSetupFile(setUpFile)
.withConfigs(configs)
.withTags(tags)
.build();
this.mvc
.perform(
MockMvcRequestBuilders
.post(COMMANDS_API)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(cmd))
)
.andExpect(MockMvcResultMatchers.status().isCreated())
.andExpect(MockMvcResultMatchers.header().string(HttpHeaders.LOCATION, Matchers.notNullValue()));
}
/**
* Cleanup after tests.
*
* @throws Exception who cares
*/
@After
public void cleanup() throws Exception {
this.jobRequestMetadataRepository.deleteAll();
this.jobExecutionRepository.deleteAll();
this.jobRepository.deleteAll();
this.jobRequestRepository.deleteAll();
this.clusterRepository.deleteAll();
this.commandRepository.deleteAll();
this.applicationRepository.deleteAll();
}
/**
* Test the job submit method for success.
*
* @throws Exception If there is a problem.
*/
@Test
public void testSubmitJobMethodSuccess() throws Exception {
this.submitAndCheckJob(1);
}
private void submitAndCheckJob(final int documentationId) throws Exception {
Assume.assumeTrue(SystemUtils.IS_OS_UNIX);
final String commandArgs = "-c 'echo hello world'";
final String clusterTag = "localhost";
final List<ClusterCriteria> clusterCriteriaList = Lists.newArrayList(
new ClusterCriteria(Sets.newHashSet(clusterTag))
);
final String setUpFile = this.resourceLoader
.getResource(BASE_DIR + "job" + FILE_DELIMITER + "jobsetupfile")
.getFile()
.getAbsolutePath();
final String depFile1 = this.resourceLoader
.getResource(BASE_DIR + "job" + FILE_DELIMITER + "dep1")
.getFile()
.getAbsolutePath();
final Set<String> dependencies = Sets.newHashSet(depFile1);
final String commandTag = "bash";
final Set<String> commandCriteria = Sets.newHashSet(commandTag);
final JobRequest jobRequest = new JobRequest.Builder(
JOB_NAME,
JOB_USER,
JOB_VERSION,
commandArgs,
clusterCriteriaList,
commandCriteria
)
.withDisableLogArchival(true)
.withSetupFile(setUpFile)
.withDependencies(dependencies)
.withDescription(JOB_DESCRIPTION)
.build();
final String id = this.submitJob(documentationId, jobRequest, null);
this.waitForDone(id);
this.checkJobStatus(documentationId, id);
this.checkJob(documentationId, id, commandArgs);
this.checkJobOutput(documentationId, id);
this.checkJobRequest(documentationId, id, commandArgs, setUpFile, clusterTag, commandTag, depFile1);
this.checkJobExecution(documentationId, id);
this.checkJobCluster(documentationId, id);
this.checkJobCommand(documentationId, id);
this.checkJobApplications(documentationId, id);
this.checkFindJobs(documentationId, id, JOB_USER);
Assert.assertThat(this.jobRepository.count(), Matchers.is(1L));
Assert.assertThat(this.jobRequestRepository.count(), Matchers.is(1L));
Assert.assertThat(this.jobRequestMetadataRepository.count(), Matchers.is(1L));
Assert.assertThat(this.jobExecutionRepository.count(), Matchers.is(1L));
// Check if the cluster setup file is cached
final String clusterSetUpFilePath = this.resourceLoader
.getResource(BASE_DIR + CMD1_ID + FILE_DELIMITER + "setupfile")
.getFile()
.getAbsolutePath();
Assert.assertTrue(
Files.exists(
Paths.get(
new URI(this.baseCacheLocation).getPath(),
UUID.nameUUIDFromBytes(clusterSetUpFilePath.getBytes(Charset.forName("UTF-8"))).toString()
)
)
);
// Test for conflicts
this.testForConflicts(id, commandArgs, clusterCriteriaList, commandCriteria);
}
private String submitJob(
final int documentationId,
final JobRequest jobRequest,
final List<MockMultipartFile> attachments
) throws Exception {
final MvcResult result;
if (attachments != null) {
final RestDocumentationResultHandler createResultHandler = MockMvcRestDocumentation.document(
"{class-name}/" + documentationId + "/submitJobWithAttachments/",
Preprocessors.preprocessRequest(Preprocessors.prettyPrint()),
Preprocessors.preprocessResponse(Preprocessors.prettyPrint()),
HeaderDocumentation.requestHeaders(
HeaderDocumentation
.headerWithName(HttpHeaders.CONTENT_TYPE)
.description(MediaType.MULTIPART_FORM_DATA_VALUE)
), // Request headers
RequestDocumentation.requestParts(
RequestDocumentation
.partWithName("request")
.description("The job request JSON. Content type must be application/json for part"),
RequestDocumentation
.partWithName("attachment")
.description("An attachment file. There can be multiple. Type should be octet-stream")
), // Request parts
Snippets.LOCATION_HEADER // Response Headers
);
final MockMultipartFile json = new MockMultipartFile(
"request",
"",
MediaType.APPLICATION_JSON_VALUE,
this.objectMapper.writeValueAsBytes(jobRequest)
);
final MockMultipartHttpServletRequestBuilder builder = RestDocumentationRequestBuilders
.fileUpload(JOBS_API)
.file(json);
for (final MockMultipartFile attachment : attachments) {
builder.file(attachment);
}
builder.contentType(MediaType.MULTIPART_FORM_DATA);
result = this.mvc
.perform(builder)
.andExpect(MockMvcResultMatchers.status().isAccepted())
.andExpect(MockMvcResultMatchers.header().string(HttpHeaders.LOCATION, Matchers.notNullValue()))
.andDo(createResultHandler)
.andReturn();
} else {
// Use regular POST
final RestDocumentationResultHandler createResultHandler = MockMvcRestDocumentation.document(
"{class-name}/" + documentationId + "/submitJobWithoutAttachments/",
Preprocessors.preprocessRequest(Preprocessors.prettyPrint()),
Preprocessors.preprocessResponse(Preprocessors.prettyPrint()),
Snippets.CONTENT_TYPE_HEADER, // Request headers
Snippets.getJobRequestRequestPayload(), // Request Fields
Snippets.LOCATION_HEADER // Response Headers
);
result = this.mvc
.perform(
MockMvcRequestBuilders
.post(JOBS_API)
.contentType(MediaType.APPLICATION_JSON)
.content(this.objectMapper.writeValueAsBytes(jobRequest))
)
.andExpect(MockMvcResultMatchers.status().isAccepted())
.andExpect(MockMvcResultMatchers.header().string(HttpHeaders.LOCATION, Matchers.notNullValue()))
.andDo(createResultHandler)
.andReturn();
}
return this.getIdFromLocation(result.getResponse().getHeader(HttpHeaders.LOCATION));
}
private void checkJobStatus(final int documentationId, final String id) throws Exception {
final RestDocumentationResultHandler getResultHandler = MockMvcRestDocumentation.document(
"{class-name}/" + documentationId + "/getJobStatus/",
Preprocessors.preprocessRequest(Preprocessors.prettyPrint()),
Preprocessors.preprocessResponse(Preprocessors.prettyPrint()),
Snippets.ID_PATH_PARAM, // Path parameters
Snippets.JSON_CONTENT_TYPE_HEADER, // Response Headers
PayloadDocumentation.responseFields(
PayloadDocumentation
.fieldWithPath("status")
.description("The job status. One of: " + Arrays.toString(JobStatus.values()))
.attributes(Snippets.EMPTY_CONSTRAINTS)
) // Response fields
);
this.mvc
.perform(RestDocumentationRequestBuilders.get(JOBS_API + "/{id}/status", id))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.jsonPath(STATUS_PATH, Matchers.is(JobStatus.SUCCEEDED.toString())))
.andDo(getResultHandler);
}
private void checkJob(final int documentationId, final String id, final String commandArgs) throws Exception {
final RestDocumentationResultHandler getResultHandler = MockMvcRestDocumentation.document(
"{class-name}/" + documentationId + "/getJob/",
Preprocessors.preprocessRequest(Preprocessors.prettyPrint()),
Preprocessors.preprocessResponse(Preprocessors.prettyPrint()),
Snippets.ID_PATH_PARAM, // Path parameters
Snippets.HAL_CONTENT_TYPE_HEADER, // Response Headers
Snippets.getJobResponsePayload(), // Response fields
Snippets.JOB_LINKS // Links
);
this.mvc
.perform(RestDocumentationRequestBuilders.get(JOBS_API + "/{id}", id))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().contentTypeCompatibleWith(MediaTypes.HAL_JSON))
.andExpect(MockMvcResultMatchers.jsonPath(ID_PATH, Matchers.is(id)))
.andExpect(MockMvcResultMatchers.jsonPath(CREATED_PATH, Matchers.notNullValue()))
.andExpect(MockMvcResultMatchers.jsonPath(UPDATED_PATH, Matchers.notNullValue()))
.andExpect(MockMvcResultMatchers.jsonPath(VERSION_PATH, Matchers.is(JOB_VERSION)))
.andExpect(MockMvcResultMatchers.jsonPath(USER_PATH, Matchers.is(JOB_USER)))
.andExpect(MockMvcResultMatchers.jsonPath(NAME_PATH, Matchers.is(JOB_NAME)))
.andExpect(MockMvcResultMatchers.jsonPath(DESCRIPTION_PATH, Matchers.is(JOB_DESCRIPTION)))
.andExpect(MockMvcResultMatchers.jsonPath(COMMAND_ARGS_PATH, Matchers.is(commandArgs)))
.andExpect(MockMvcResultMatchers.jsonPath(STATUS_PATH, Matchers.is(JobStatus.SUCCEEDED.toString())))
.andExpect(MockMvcResultMatchers.jsonPath(STATUS_MESSAGE_PATH, Matchers.is(JOB_STATUS_MSG)))
.andExpect(MockMvcResultMatchers.jsonPath(STARTED_PATH, Matchers.not(new Date(0))))
.andExpect(MockMvcResultMatchers.jsonPath(FINISHED_PATH, Matchers.notNullValue()))
.andExpect(MockMvcResultMatchers.jsonPath(ARCHIVE_LOCATION_PATH, Matchers.isEmptyOrNullString()))
.andExpect(MockMvcResultMatchers.jsonPath(CLUSTER_NAME_PATH, Matchers.is(CLUSTER1_NAME)))
.andExpect(MockMvcResultMatchers.jsonPath(COMMAND_NAME_PATH, Matchers.is(CMD1_NAME)))
.andExpect(MockMvcResultMatchers.jsonPath(LINKS_PATH + ".*", Matchers.hasSize(8)))
.andExpect(MockMvcResultMatchers.jsonPath(LINKS_PATH, Matchers.hasKey(SELF_LINK_KEY)))
.andExpect(MockMvcResultMatchers.jsonPath(LINKS_PATH, Matchers.hasKey("request")))
.andExpect(MockMvcResultMatchers.jsonPath(LINKS_PATH, Matchers.hasKey("execution")))
.andExpect(MockMvcResultMatchers.jsonPath(LINKS_PATH, Matchers.hasKey("output")))
.andExpect(MockMvcResultMatchers.jsonPath(LINKS_PATH, Matchers.hasKey("status")))
.andExpect(MockMvcResultMatchers.jsonPath(LINKS_PATH, Matchers.hasKey("cluster")))
.andExpect(MockMvcResultMatchers.jsonPath(LINKS_PATH, Matchers.hasKey("command")))
.andExpect(MockMvcResultMatchers.jsonPath(LINKS_PATH, Matchers.hasKey("applications")))
.andDo(getResultHandler);
}
private void checkJobOutput(final int documentationId, final String id) throws Exception {
// Check getting a directory as json
final RestDocumentationResultHandler jsonResultHandler = MockMvcRestDocumentation.document(
"{class-name}/" + documentationId + "/getJobOutput/json/",
Preprocessors.preprocessRequest(Preprocessors.prettyPrint()),
Preprocessors.preprocessResponse(Preprocessors.prettyPrint()),
Snippets.ID_PATH_PARAM.and(
RequestDocumentation
.parameterWithName("filePath")
.description("The path to the directory to get")
.optional()
), // Path parameters
HeaderDocumentation.requestHeaders(
HeaderDocumentation
.headerWithName(HttpHeaders.ACCEPT)
.description(MediaType.APPLICATION_JSON_VALUE)
.optional()
), // Request header
HeaderDocumentation.responseHeaders(
HeaderDocumentation
.headerWithName(HttpHeaders.CONTENT_TYPE)
.description(MediaType.APPLICATION_JSON_VALUE)
), // Response Headers
Snippets.OUTPUT_DIRECTORY_FIELDS
);
this.mvc
.perform(RestDocumentationRequestBuilders.get(JOBS_API + "/{id}/output/{filePath}", id, ""))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.jsonPath("parent", Matchers.isEmptyOrNullString()))
.andExpect(MockMvcResultMatchers.jsonPath("$.directories[0].name", Matchers.is("genie/")))
.andExpect(MockMvcResultMatchers.jsonPath("$.files[0].name", Matchers.is("dep1")))
.andExpect(MockMvcResultMatchers.jsonPath("$.files[1].name", Matchers.is("jobsetupfile")))
.andExpect(MockMvcResultMatchers.jsonPath("$.files[2].name", Matchers.is("run")))
.andExpect(MockMvcResultMatchers.jsonPath("$.files[3].name", Matchers.is("stderr")))
.andExpect(MockMvcResultMatchers.jsonPath("$.files[4].name", Matchers.is("stdout")))
.andDo(jsonResultHandler);
// Check getting a directory as HTML
final RestDocumentationResultHandler htmlResultHandler = MockMvcRestDocumentation.document(
"{class-name}/" + documentationId + "/getJobOutput/html/",
Preprocessors.preprocessRequest(Preprocessors.prettyPrint()),
Preprocessors.preprocessResponse(Preprocessors.prettyPrint()),
Snippets.ID_PATH_PARAM.and(
RequestDocumentation
.parameterWithName("filePath")
.description("The path to the directory to get")
.optional()
), // Path parameters
HeaderDocumentation.requestHeaders(
HeaderDocumentation
.headerWithName(HttpHeaders.ACCEPT)
.description(MediaType.TEXT_HTML)
), // Request header
HeaderDocumentation.responseHeaders(
HeaderDocumentation
.headerWithName(HttpHeaders.CONTENT_TYPE)
.description(MediaType.TEXT_HTML)
) // Response Headers
);
this.mvc
.perform(RestDocumentationRequestBuilders
.get(JOBS_API + "/{id}/output/{filePath}", id, "")
.accept(MediaType.TEXT_HTML)
)
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().contentTypeCompatibleWith(MediaType.TEXT_HTML))
.andDo(htmlResultHandler);
// Check getting a file
// Check getting a directory as HTML
final RestDocumentationResultHandler fileResultHandler = MockMvcRestDocumentation.document(
"{class-name}/" + documentationId + "/getJobOutput/file/",
Preprocessors.preprocessRequest(Preprocessors.prettyPrint()),
Preprocessors.preprocessResponse(Preprocessors.prettyPrint()),
Snippets.ID_PATH_PARAM.and(
RequestDocumentation
.parameterWithName("filePath")
.description("The path to the file to get")
.optional()
), // Path parameters
HeaderDocumentation.requestHeaders(
HeaderDocumentation
.headerWithName(HttpHeaders.ACCEPT)
.description(MediaType.ALL_VALUE)
.optional()
), // Request header
HeaderDocumentation.responseHeaders(
HeaderDocumentation
.headerWithName(HttpHeaders.CONTENT_TYPE)
.description("The content type of the file being returned")
.optional()
) // Response Headers
);
// check that the generated run file is correct
final String runShFileName = SystemUtils.IS_OS_LINUX ? "linux-runsh.txt" : "non-linux-runsh.txt";
final String runShFile = this.resourceLoader
.getResource(BASE_DIR + runShFileName)
.getFile()
.getAbsolutePath();
final String runFileContents = new String(Files.readAllBytes(Paths.get(runShFile)), "UTF-8");
final String jobWorkingDir = this.jobDirResource.getFile().getCanonicalPath() + FILE_DELIMITER + id;
final String expectedRunScriptContent = this.getExpectedRunContents(runFileContents, jobWorkingDir, id);
this.mvc
.perform(RestDocumentationRequestBuilders
.get(JOBS_API + "/{id}/output/{filePath}", id, "run")
)
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().string(expectedRunScriptContent))
.andDo(fileResultHandler);
}
private void checkJobRequest(
final int documentationId,
final String id,
final String commandArgs,
final String setupFile,
final String clusterTag,
final String commandTag,
final String depFile1
) throws Exception {
final RestDocumentationResultHandler getResultHandler = MockMvcRestDocumentation.document(
"{class-name}/" + documentationId + "/getJobRequest/",
Preprocessors.preprocessRequest(Preprocessors.prettyPrint()),
Preprocessors.preprocessResponse(Preprocessors.prettyPrint()),
Snippets.ID_PATH_PARAM, // Path parameters
Snippets.HAL_CONTENT_TYPE_HEADER, // Response Headers
Snippets.getJobRequestResponsePayload(), // Response fields
Snippets.JOB_REQUEST_LINKS // Links
);
this.mvc
.perform(RestDocumentationRequestBuilders.get(JOBS_API + "/{id}/request", id))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath(ID_PATH, Matchers.is(id)))
.andExpect(MockMvcResultMatchers.jsonPath(CREATED_PATH, Matchers.notNullValue()))
.andExpect(MockMvcResultMatchers.jsonPath(UPDATED_PATH, Matchers.notNullValue()))
.andExpect(MockMvcResultMatchers.jsonPath(NAME_PATH, Matchers.is(JOB_NAME)))
.andExpect(MockMvcResultMatchers.jsonPath(VERSION_PATH, Matchers.is(JOB_VERSION)))
.andExpect(MockMvcResultMatchers.jsonPath(USER_PATH, Matchers.is(JOB_USER)))
.andExpect(MockMvcResultMatchers.jsonPath(DESCRIPTION_PATH, Matchers.is(JOB_DESCRIPTION)))
.andExpect(MockMvcResultMatchers.jsonPath(COMMAND_ARGS_PATH, Matchers.is(commandArgs)))
.andExpect(MockMvcResultMatchers.jsonPath(SETUP_FILE_PATH, Matchers.is(setupFile)))
.andExpect(MockMvcResultMatchers.jsonPath(CLUSTER_CRITERIAS_PATH, Matchers.hasSize(1)))
.andExpect(MockMvcResultMatchers.jsonPath(CLUSTER_CRITERIAS_PATH + "[0].tags", Matchers.hasSize(1)))
.andExpect(MockMvcResultMatchers.jsonPath(CLUSTER_CRITERIAS_PATH + "[0].tags[0]", Matchers.is(clusterTag)))
.andExpect(MockMvcResultMatchers.jsonPath(COMMAND_CRITERIA_PATH, Matchers.hasSize(1)))
.andExpect(MockMvcResultMatchers.jsonPath(COMMAND_CRITERIA_PATH + "[0]", Matchers.is(commandTag)))
.andExpect(MockMvcResultMatchers.jsonPath(GROUP_PATH, Matchers.nullValue()))
.andExpect(MockMvcResultMatchers.jsonPath(DISABLE_LOG_ARCHIVAL_PATH, Matchers.is(true)))
.andExpect(MockMvcResultMatchers.jsonPath(DEPENDENCIES_PATH, Matchers.hasSize(1)))
.andExpect(MockMvcResultMatchers.jsonPath(DEPENDENCIES_PATH + "[0]", Matchers.is(depFile1)))
.andExpect(MockMvcResultMatchers.jsonPath(EMAIL_PATH, Matchers.nullValue()))
.andExpect(MockMvcResultMatchers.jsonPath(CPU_PATH, Matchers.nullValue()))
.andExpect(MockMvcResultMatchers.jsonPath(MEMORY_PATH, Matchers.nullValue()))
.andExpect(MockMvcResultMatchers.jsonPath(APPLICATIONS_PATH, Matchers.empty()))
.andDo(getResultHandler);
}
private void checkJobExecution(final int documentationId, final String id) throws Exception {
final RestDocumentationResultHandler getResultHandler = MockMvcRestDocumentation.document(
"{class-name}/" + documentationId + "/getJobExecution/",
Preprocessors.preprocessRequest(Preprocessors.prettyPrint()),
Preprocessors.preprocessResponse(Preprocessors.prettyPrint()),
Snippets.ID_PATH_PARAM, // Path parameters
Snippets.HAL_CONTENT_TYPE_HEADER, // Response Headers
Snippets.getJobExecutionResponsePayload(), // Response fields
Snippets.JOB_EXECUTION_LINKS // Links
);
this.mvc
.perform(RestDocumentationRequestBuilders.get(JOBS_API + "/{id}/execution", id))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath(ID_PATH, Matchers.is(id)))
.andExpect(MockMvcResultMatchers.jsonPath(CREATED_PATH, Matchers.notNullValue()))
.andExpect(MockMvcResultMatchers.jsonPath(UPDATED_PATH, Matchers.notNullValue()))
.andExpect(MockMvcResultMatchers.jsonPath(HOST_NAME_PATH, Matchers.is(this.hostname)))
.andExpect(MockMvcResultMatchers.jsonPath(PROCESS_ID_PATH, Matchers.notNullValue()))
.andExpect(MockMvcResultMatchers.jsonPath(CHECK_DELAY_PATH, Matchers.is((int) CHECK_DELAY)))
.andExpect(MockMvcResultMatchers.jsonPath(EXIT_CODE_PATH, Matchers.is(JobExecution.SUCCESS_EXIT_CODE)))
.andDo(getResultHandler);
}
private void checkJobCluster(final int documentationId, final String id) throws Exception {
final RestDocumentationResultHandler getResultHandler = MockMvcRestDocumentation.document(
"{class-name}/" + documentationId + "/getJobCluster/",
Preprocessors.preprocessRequest(Preprocessors.prettyPrint()),
Preprocessors.preprocessResponse(Preprocessors.prettyPrint()),
Snippets.ID_PATH_PARAM, // Path parameters
Snippets.HAL_CONTENT_TYPE_HEADER, // Response Headers
Snippets.getClusterResponsePayload(), // Response fields
Snippets.CLUSTER_LINKS // Links
);
this.mvc
.perform(RestDocumentationRequestBuilders.get(JOBS_API + "/{id}/cluster", id))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath(ID_PATH, Matchers.is(CLUSTER1_ID)))
.andExpect(MockMvcResultMatchers.jsonPath(CREATED_PATH, Matchers.notNullValue()))
.andExpect(MockMvcResultMatchers.jsonPath(UPDATED_PATH, Matchers.notNullValue()))
.andExpect(MockMvcResultMatchers.jsonPath(NAME_PATH, Matchers.is(CLUSTER1_NAME)))
.andExpect(MockMvcResultMatchers.jsonPath(USER_PATH, Matchers.is(CLUSTER1_USER)))
.andExpect(MockMvcResultMatchers.jsonPath(VERSION_PATH, Matchers.is(CLUSTER1_VERSION)))
.andDo(getResultHandler);
}
private void checkJobCommand(final int documentationId, final String id) throws Exception {
final RestDocumentationResultHandler getResultHandler = MockMvcRestDocumentation.document(
"{class-name}/" + documentationId + "/getJobCommand/",
Preprocessors.preprocessRequest(Preprocessors.prettyPrint()),
Preprocessors.preprocessResponse(Preprocessors.prettyPrint()),
Snippets.ID_PATH_PARAM, // Path parameters
Snippets.HAL_CONTENT_TYPE_HEADER, // Response Headers
Snippets.getCommandResponsePayload(), // Response fields
Snippets.COMMAND_LINKS // Links
);
this.mvc
.perform(RestDocumentationRequestBuilders.get(JOBS_API + "/{id}/command", id))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath(ID_PATH, Matchers.is(CMD1_ID)))
.andExpect(MockMvcResultMatchers.jsonPath(CREATED_PATH, Matchers.notNullValue()))
.andExpect(MockMvcResultMatchers.jsonPath(UPDATED_PATH, Matchers.notNullValue()))
.andExpect(MockMvcResultMatchers.jsonPath(NAME_PATH, Matchers.is(CMD1_NAME)))
.andExpect(MockMvcResultMatchers.jsonPath(USER_PATH, Matchers.is(CMD1_USER)))
.andExpect(MockMvcResultMatchers.jsonPath(VERSION_PATH, Matchers.is(CMD1_VERSION)))
.andExpect(MockMvcResultMatchers.jsonPath("$.executable", Matchers.is(CMD1_EXECUTABLE)))
.andDo(getResultHandler);
}
private void checkJobApplications(final int documentationId, final String id) throws Exception {
final RestDocumentationResultHandler getResultHandler = MockMvcRestDocumentation.document(
"{class-name}/" + documentationId + "/getJobApplications/",
Preprocessors.preprocessRequest(Preprocessors.prettyPrint()),
Preprocessors.preprocessResponse(Preprocessors.prettyPrint()),
Snippets.ID_PATH_PARAM, // Path parameters
Snippets.HAL_CONTENT_TYPE_HEADER, // Response Headers
PayloadDocumentation.responseFields(
PayloadDocumentation
.fieldWithPath("[]")
.description("The applications for the job")
.attributes(Snippets.EMPTY_CONSTRAINTS)
) // Response fields
);
this.mvc
.perform(RestDocumentationRequestBuilders.get(JOBS_API + "/{id}/applications", id))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(2)))
.andExpect(MockMvcResultMatchers.jsonPath("$[0].id", Matchers.is(APP1_ID)))
.andExpect(MockMvcResultMatchers.jsonPath("$[1].id", Matchers.is(APP2_ID)))
.andDo(getResultHandler);
}
private void checkFindJobs(final int documentationId, final String id, final String user) throws Exception {
final RestDocumentationResultHandler findResultHandler = MockMvcRestDocumentation.document(
"{class-name}/" + documentationId + "/findJobs/",
Preprocessors.preprocessRequest(Preprocessors.prettyPrint()),
Preprocessors.preprocessResponse(Preprocessors.prettyPrint()),
Snippets.JOB_SEARCH_QUERY_PARAMETERS, // Request query parameters
Snippets.HAL_CONTENT_TYPE_HEADER, // Response headers
Snippets.JOB_SEARCH_RESULT_FIELDS, // Result fields
Snippets.SEARCH_LINKS // HAL Links
);
this.mvc
.perform(MockMvcRequestBuilders.get(JOBS_API).param("user", user))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().contentTypeCompatibleWith(MediaTypes.HAL_JSON))
.andExpect(MockMvcResultMatchers.jsonPath(JOBS_LIST_PATH, Matchers.hasSize(1)))
.andExpect(MockMvcResultMatchers.jsonPath(JOBS_LIST_PATH + "[0].id", Matchers.is(id)))
.andDo(findResultHandler);
}
private void testForConflicts(
final String id,
final String commandArgs,
final List<ClusterCriteria> clusterCriteriaList,
final Set<String> commandCriteria
) throws Exception {
final JobRequest jobConflictRequest = new JobRequest.Builder(
JOB_NAME,
JOB_USER,
JOB_VERSION,
commandArgs,
clusterCriteriaList,
commandCriteria
)
.withId(id)
.withDisableLogArchival(true)
.build();
this.mvc
.perform(
MockMvcRequestBuilders
.post(JOBS_API)
.contentType(MediaType.APPLICATION_JSON)
.content(this.objectMapper.writeValueAsBytes(jobConflictRequest))
)
.andExpect(MockMvcResultMatchers.status().isConflict());
}
/**
* Test the job submit method for success twice to validate the file cache use.
*
* @throws Exception If there is a problem.
*/
@Test
public void testSubmitJobMethodTwiceSuccess() throws Exception {
submitAndCheckJob(2);
cleanup();
setup();
submitAndCheckJob(3);
}
/**
* Test to make sure we can submit a job with attachments.
*
* @throws Exception on any error
*/
@Test
public void canSubmitJobWithAttachments() throws Exception {
final String commandArgs = "-c 'echo hello world'";
final String clusterTag = "localhost";
final List<ClusterCriteria> clusterCriteriaList = Lists.newArrayList(
new ClusterCriteria(Sets.newHashSet(clusterTag))
);
final String setUpFile = this.resourceLoader
.getResource(BASE_DIR + "job" + FILE_DELIMITER + "jobsetupfile")
.getFile()
.getAbsolutePath();
final File attachment1File = this.resourceLoader
.getResource(BASE_DIR + "job/query.sql")
.getFile();
final MockMultipartFile attachment1;
try (final InputStream is = new FileInputStream(attachment1File)) {
attachment1 = new MockMultipartFile(
"attachment",
attachment1File.getName(),
MediaType.APPLICATION_OCTET_STREAM_VALUE,
is
);
}
final File attachment2File = this.resourceLoader
.getResource(BASE_DIR + "job/query2.sql")
.getFile();
final MockMultipartFile attachment2;
try (final InputStream is = new FileInputStream(attachment2File)) {
attachment2 = new MockMultipartFile(
"attachment",
attachment2File.getName(),
MediaType.APPLICATION_OCTET_STREAM_VALUE,
is
);
}
final Set<String> commandCriteria = Sets.newHashSet("bash");
final JobRequest jobRequest = new JobRequest.Builder(
JOB_NAME,
JOB_USER,
JOB_VERSION,
commandArgs,
clusterCriteriaList,
commandCriteria
)
.withDisableLogArchival(true)
.withSetupFile(setUpFile)
.withDescription(JOB_DESCRIPTION)
.build();
this.waitForDone(this.submitJob(4, jobRequest, Lists.newArrayList(attachment1, attachment2)));
}
/**
* Test the job submit method for incorrect cluster resolved.
*
* @throws Exception If there is a problem.
*/
@Test
public void testSubmitJobMethodMissingCluster() throws Exception {
Assume.assumeTrue(SystemUtils.IS_OS_UNIX);
final String commandArgs = "-c 'echo hello world'";
final List<ClusterCriteria> clusterCriteriaList = new ArrayList<>();
final Set<String> clusterTags = new HashSet<>();
clusterTags.add("undefined");
final ClusterCriteria clusterCriteria = new ClusterCriteria(clusterTags);
clusterCriteriaList.add(clusterCriteria);
final String jobId = UUID.randomUUID().toString();
final Set<String> commandCriteria = new HashSet<>();
commandCriteria.add("bash");
final JobRequest jobRequest = new JobRequest.Builder(
JOB_NAME,
JOB_USER,
JOB_VERSION,
commandArgs,
clusterCriteriaList,
commandCriteria
)
.withId(jobId)
.withDisableLogArchival(true)
.build();
this.mvc
.perform(
MockMvcRequestBuilders
.post(JOBS_API)
.contentType(MediaType.APPLICATION_JSON)
.content(this.objectMapper.writeValueAsBytes(jobRequest))
.accept(MediaType.APPLICATION_JSON)
)
.andExpect(MockMvcResultMatchers.status().isPreconditionFailed());
Assert.assertThat(this.getStatus(jobId), Matchers.is("{\"status\":\"FAILED\"}"));
}
/**
* Test the job submit method for incorrect command resolved.
*
* @throws Exception If there is a problem.
*/
@Test
public void testSubmitJobMethodMissingCommand() throws Exception {
Assume.assumeTrue(SystemUtils.IS_OS_UNIX);
final String commandArgs = "-c 'echo hello world'";
final List<ClusterCriteria> clusterCriteriaList = new ArrayList<>();
final Set<String> clusterTags = new HashSet<>();
clusterTags.add("localhost");
final ClusterCriteria clusterCriteria = new ClusterCriteria(clusterTags);
clusterCriteriaList.add(clusterCriteria);
final String jobId = UUID.randomUUID().toString();
final Set<String> commandCriteria = new HashSet<>();
commandCriteria.add("undefined");
final JobRequest jobRequest = new JobRequest.Builder(
JOB_NAME,
JOB_USER,
JOB_VERSION,
commandArgs,
clusterCriteriaList,
commandCriteria
)
.withId(jobId)
.withDisableLogArchival(true)
.build();
this.mvc
.perform(
MockMvcRequestBuilders
.post(JOBS_API)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(jobRequest))
)
.andExpect(MockMvcResultMatchers.status().isPreconditionFailed());
Assert.assertThat(this.getStatus(jobId), Matchers.is("{\"status\":\"FAILED\"}"));
}
/**
* Test the job submit method for when the job is killed by sending a DELETE HTTP call.
*
* @throws Exception If there is a problem.
*/
@Test
public void testSubmitJobMethodKill() throws Exception {
Assume.assumeTrue(SystemUtils.IS_OS_UNIX);
final String commandArgs = "-c 'sleep 60'";
final List<ClusterCriteria> clusterCriteriaList = new ArrayList<>();
final Set<String> clusterTags = new HashSet<>();
clusterTags.add("localhost");
final ClusterCriteria clusterCriteria = new ClusterCriteria(clusterTags);
clusterCriteriaList.add(clusterCriteria);
final Set<String> commandCriteria = new HashSet<>();
commandCriteria.add("bash");
final JobRequest jobRequest = new JobRequest.Builder(
JOB_NAME,
JOB_USER,
JOB_VERSION,
commandArgs,
clusterCriteriaList,
commandCriteria
)
.withDisableLogArchival(true)
.build();
final MvcResult result = this.mvc
.perform(
MockMvcRequestBuilders
.post(JOBS_API)
.contentType(MediaType.APPLICATION_JSON)
.content(this.objectMapper.writeValueAsBytes(jobRequest))
)
.andExpect(MockMvcResultMatchers.status().isAccepted())
.andExpect(MockMvcResultMatchers.header().string(HttpHeaders.LOCATION, Matchers.notNullValue()))
.andReturn();
final String jobId = this.getIdFromLocation(result.getResponse().getHeader(HttpHeaders.LOCATION));
this.waitForRunning(jobId);
// Let it run for a couple of seconds
Thread.sleep(2000);
// Send a kill request to the job.
final RestDocumentationResultHandler killResultHandler = MockMvcRestDocumentation.document(
"{class-name}/killJob/",
Preprocessors.preprocessRequest(Preprocessors.prettyPrint()),
Preprocessors.preprocessResponse(Preprocessors.prettyPrint()),
Snippets.ID_PATH_PARAM
);
this.mvc
.perform(RestDocumentationRequestBuilders.delete(JOBS_API + "/{id}", jobId))
.andExpect(MockMvcResultMatchers.status().isAccepted())
.andDo(killResultHandler);
this.waitForDone(jobId);
this.mvc
.perform(MockMvcRequestBuilders.get(JOBS_API + "/" + jobId))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().contentTypeCompatibleWith(MediaTypes.HAL_JSON))
.andExpect(MockMvcResultMatchers.jsonPath(ID_PATH, Matchers.is(jobId)))
.andExpect(MockMvcResultMatchers.jsonPath(STATUS_PATH, Matchers.is(JobStatus.KILLED.toString())));
// Kill the job again to make sure it doesn't cause a problem.
this.mvc
.perform(MockMvcRequestBuilders.delete(JOBS_API + "/" + jobId))
.andExpect(MockMvcResultMatchers.status().isAccepted());
}
/**
* Test the job submit method for when the job is killed as it times out.
*
* @throws Exception If there is a problem.
*/
@Test
public void testSubmitJobMethodKillOnTimeout() throws Exception {
Assume.assumeTrue(SystemUtils.IS_OS_UNIX);
final String commandArgs = "-c 'sleep 60'";
final List<ClusterCriteria> clusterCriteriaList = new ArrayList<>();
final Set<String> clusterTags = new HashSet<>();
clusterTags.add("localhost");
final ClusterCriteria clusterCriteria = new ClusterCriteria(clusterTags);
clusterCriteriaList.add(clusterCriteria);
final Set<String> commandCriteria = new HashSet<>();
commandCriteria.add("bash");
final JobRequest jobRequest = new JobRequest.Builder(
JOB_NAME,
JOB_USER,
JOB_VERSION,
commandArgs,
clusterCriteriaList,
commandCriteria
)
.withTimeout(5)
.withDisableLogArchival(true)
.build();
final MvcResult result = this.mvc
.perform(
MockMvcRequestBuilders
.post(JOBS_API)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(jobRequest))
).andReturn();
if (result.getResponse().getStatus() != HttpStatus.ACCEPTED.value()) {
log.error(
"RESPONSE WASN'T 202 IT WAS: {} AND THE ERROR MESSAGE IS: {} AND THE CONTENT IS {}",
result.getResponse().getStatus(),
result.getResponse().getErrorMessage(),
result.getResponse().getContentAsString()
);
Assert.fail();
}
final String id = this.getIdFromLocation(result.getResponse().getHeader(HttpHeaders.LOCATION));
this.waitForDone(id);
this.mvc
.perform(MockMvcRequestBuilders.get(JOBS_API + "/{id}", id))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().contentTypeCompatibleWith(MediaTypes.HAL_JSON))
.andExpect(MockMvcResultMatchers.jsonPath(ID_PATH, Matchers.is(id)))
.andExpect(MockMvcResultMatchers.jsonPath(STATUS_PATH, Matchers.is(JobStatus.KILLED.toString())));
}
/**
* Test the job submit method for when the job fails.
*
* @throws Exception If there is a problem.
*/
@Test
public void testSubmitJobMethodFailure() throws Exception {
Assume.assumeTrue(SystemUtils.IS_OS_UNIX);
final String commandArgs = "-c 'exit 1'";
final List<ClusterCriteria> clusterCriteriaList = new ArrayList<>();
final Set<String> clusterTags = new HashSet<>();
clusterTags.add("localhost");
final ClusterCriteria clusterCriteria = new ClusterCriteria(clusterTags);
clusterCriteriaList.add(clusterCriteria);
final Set<String> commandCriteria = new HashSet<>();
commandCriteria.add("bash");
final JobRequest jobRequest = new JobRequest.Builder(
JOB_NAME,
JOB_USER,
JOB_VERSION,
commandArgs,
clusterCriteriaList,
commandCriteria
)
.withDisableLogArchival(true)
.build();
final MvcResult result = this.mvc
.perform(
MockMvcRequestBuilders
.post(JOBS_API)
.contentType(MediaType.APPLICATION_JSON)
.content(this.objectMapper.writeValueAsBytes(jobRequest))
)
.andExpect(MockMvcResultMatchers.status().isAccepted())
.andExpect(MockMvcResultMatchers.header().string(HttpHeaders.LOCATION, Matchers.notNullValue()))
.andReturn();
final String id = this.getIdFromLocation(result.getResponse().getHeader(HttpHeaders.LOCATION));
this.waitForDone(id);
Assert.assertEquals(this.getStatus(id), "{\"status\":\"FAILED\"}");
this.mvc
.perform(MockMvcRequestBuilders.get(JOBS_API + "/{id}", id))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().contentTypeCompatibleWith(MediaTypes.HAL_JSON))
.andExpect(MockMvcResultMatchers.jsonPath(ID_PATH, Matchers.is(id)))
.andExpect(MockMvcResultMatchers.jsonPath(STATUS_PATH, Matchers.is(JobStatus.FAILED.toString())));
}
private String getIdFromLocation(final String location) {
return location.substring(location.lastIndexOf("/") + 1);
}
private String getExpectedRunContents(
final String runFileContents,
final String jobWorkingDir,
final String jobId
) {
return runFileContents
.replace("TEST_GENIE_JOB_WORKING_DIR_PLACEHOLDER", jobWorkingDir)
.replace("JOB_ID_PLACEHOLDER", jobId)
.replace("COMMAND_ID_PLACEHOLDER", CMD1_ID)
.replace("COMMAND_NAME_PLACEHOLDER", CMD1_NAME)
.replace("CLUSTER_ID_PLACEHOLDER", CLUSTER1_ID)
.replace("CLUSTER_NAME_PLACEHOLDER", CLUSTER1_NAME);
}
private String getStatus(final String jobId) throws Exception {
return this.mvc
.perform(MockMvcRequestBuilders.get(JOBS_API + "/{id}/status", jobId))
.andExpect(MockMvcResultMatchers.status().isOk())
.andReturn()
.getResponse()
.getContentAsString();
}
private void waitForDone(final String jobId) throws Exception {
int counter = 0;
while (true) {
final String statusString = this.getStatus(jobId);
if (statusString.contains("INIT") || statusString.contains("RUNNING")) {
log.info("Iteration {} sleeping for {} ms", counter, SLEEP_TIME);
Thread.sleep(SLEEP_TIME);
counter++;
} else {
break;
}
}
}
private void waitForRunning(final String jobId) throws Exception {
int counter = 0;
while (true) {
final String statusString = this.getStatus(jobId);
if (statusString.contains("INIT")) {
log.info("Iteration {} sleeping for {} ms", counter, SLEEP_TIME);
Thread.sleep(SLEEP_TIME);
counter++;
} else {
break;
}
}
}
}