Commit ad4e5e34 authored by Dominik Vayhinger's avatar Dominik Vayhinger
Browse files

Initial Commit

parents
Pipeline #894 failed with stages
in 23 seconds
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
</head>
<body>
<p>Hello World</p>
</body>
</html>
\ No newline at end of file
Bind mount persistance data through bind Mount
package de.hftstuttgart.exceptions;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* Created by Marcel Bochtler on 28.11.16.
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
public class CorruptedZipFileException extends RuntimeException {
public CorruptedZipFileException(String s) {
super(s);
}
}
package de.hftstuttgart.exceptions;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* Created by Marcel Bochtler on 29.11.16.
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
public class NoZipFileException extends RuntimeException {
public NoZipFileException(String message) {
super(message);
}
}
package de.hftstuttgart.models;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import de.hftstuttgart.utils.BuildState;
import java.util.List;
@JsonInclude
public class JenkinsJobData {
public JenkinsJobData() {
}
public String getClassData() {
return classData;
}
public List<Job> getJobs() {
return jobs;
}
@JsonProperty("_class")
private String classData;
private List<Job> jobs;
@JsonProperty("jobs")
public void unpackingJobs(List<Job> jobs) {
this.jobs = jobs;
}
public static class Job {
@JsonProperty("_class")
private String jobClassData;
@JsonProperty("name")
private String name;
@JsonProperty("color")
private BuildState color;
public String getJobClassData() {
return jobClassData;
}
public void setJobClassData(String jobClassData) {
this.jobClassData = jobClassData;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BuildState getColor() {
return color;
}
public void setColor(BuildState color) {
this.color = color;
}
}
}
package de.hftstuttgart.models;
import org.junit.runner.notification.Failure;
import java.util.List;
public class TestResult {
private String testName;
private int testCount;
private int failureCount;
private List<String> successfulTests;
private List<Failure> testFailures;
public String getTestName() {
return testName;
}
public void setTestName(String testName) {
this.testName = testName;
}
public int getTestCount() {
return testCount;
}
public void setTestCount(int testCount) {
this.testCount = testCount;
}
public int getFailureCount() {
return failureCount;
}
public void setFailureCount(int failureCount) {
this.failureCount = failureCount;
}
public List<String> getSuccessfulTests() {
return successfulTests;
}
public void setSuccessfulTests(List<String> successfulTests) {
this.successfulTests = successfulTests;
}
public List<Failure> getTestFailures() {
return testFailures;
}
public void setTestFailures(List<Failure> testFailures) {
this.testFailures = testFailures;
}
@Override
public String toString() {
return "TestResult{" +
"testName='" + testName + '\'' +
", testCount=" + testCount +
", failureCount=" + failureCount +
", testFailures=" + testFailures +
'}';
}
}
package de.hftstuttgart.models;
import javax.tools.Diagnostic;
import java.util.List;
public class UserResult {
private List<TestResult> testResults;
private List<Diagnostic> compilationErrors;
public UserResult(List<TestResult> testResults) {
this.testResults = testResults;
}
public List<TestResult> getTestResults() {
return testResults;
}
public void setTestResults(List<TestResult> testResults) {
this.testResults = testResults;
}
public List<Diagnostic> getCompilationErrors() {
return compilationErrors;
}
public void setCompilationErrors(List<Diagnostic> compilationErrors) {
this.compilationErrors = compilationErrors;
}
}
package de.hftstuttgart.rest.v1.jenkins;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class RestAPIController {
private static final Logger LOG = LogManager.getLogger(RestAPIController.class);
public static final Map<String, String> JOB_MAP = new HashMap<>();
@PostMapping("/v1/uploaduserresults")
public void uploadsResults(@RequestParam("jobID") String jobId, @RequestBody String userResult) {
LOG.info("result: " + userResult);
if (!JOB_MAP.containsKey(jobId)) {
String keys = String.join(", ", JOB_MAP.keySet());
throw new IllegalArgumentException(
String.format("Key %s does not exist in JOB_MAP, available Keys: [%s]", jobId, keys));
}
JOB_MAP.put(jobId, userResult);
}
}
\ No newline at end of file
package de.hftstuttgart.rest.v1.task;
import de.hftstuttgart.config.ModocotProperties;
import de.hftstuttgart.rest.v1.jenkins.RestAPIController;
import de.hftstuttgart.utils.BackendUtil;
import de.hftstuttgart.utils.BuildState;
import de.hftstuttgart.utils.GitTeaUtil;
import de.hftstuttgart.utils.JGitUtil;
import de.hftstuttgart.utils.TaskUploadUtils;
import de.hftstuttgart.utils.UnzipUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.annotation.MultipartConfig;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.UUID;
/**
* Rest controller for everything related to the TASK files
*/
@RestController
@RequestMapping("/v1/task")
@MultipartConfig
public class TaskUpload {
private static final Logger LOG = LogManager.getLogger(TaskUpload.class);
private final ModocotProperties modocotProperties;
private final TaskUploadUtils taskUploadUtils;
private final JGitUtil jGitUtil;
private final String assignmentBasePath;
private final GitTeaUtil gitTeaUtil;
public TaskUpload(ModocotProperties modocotProperties,
TaskUploadUtils taskUploadUtils,
JGitUtil jGitUtil,
GitTeaUtil gitTeaUtil) {
this.modocotProperties = modocotProperties;
this.taskUploadUtils = taskUploadUtils;
this.jGitUtil = jGitUtil;
this.gitTeaUtil = gitTeaUtil;
this.assignmentBasePath =
this.modocotProperties.getModocotParentDirectory()
+ File.separator
+ this.modocotProperties.getModocotAssignmentFolderPrefix();
}
@RequestMapping(method = RequestMethod.POST)
public String uploadAndTestFile(@RequestParam("taskFile") MultipartFile taskFileRef,
@RequestParam("assignmentId") String assignmentId) throws IOException, InterruptedException {
String jobId = assignmentId + "_" + UUID.randomUUID();
String subFolderPath = this.assignmentBasePath + assignmentId;
LOG.info("subFolderPath: " + subFolderPath);
File workDirectory = new File(subFolderPath);
workDirectory.mkdirs();
LOG.info("created new File");
File file = new File(subFolderPath, String.valueOf(UUID.randomUUID()));
taskFileRef.transferTo(file);
List<File> unzippedFiles = UnzipUtil.unzip(file);
if (file.exists()) file.delete();
startFileRead(assignmentId, subFolderPath, unzippedFiles);
BuildState buildState = this.taskUploadUtils.startTask(jobId, subFolderPath);
if (buildState != BuildState.BLUE) {
String jenkinsError = this.taskUploadUtils.getJenkinsConsoleOutput(jobId);
LOG.error(jenkinsError);
return jenkinsError;
}
Thread.sleep(3000);
String userResult = RestAPIController.JOB_MAP.remove(jobId);
this.gitTeaUtil.deleteRepository(jobId);
this.taskUploadUtils.deleteJenkinsJob(jobId);
if (!this.taskUploadUtils.isValidJSON(userResult)) {
throw new IllegalArgumentException("userResult from JUnitTestLauncher is invalid: " + userResult);
}
return userResult;
}
/**
* Extracting all lines from repo.txt and cloning extracted repository
*
* @param assignmentId String assignmentId
* @param subFolderPath working-directory
* @param unzippedFiles List of all Files from {@link MultipartFile}
*/
private void startFileRead(String assignmentId, String subFolderPath, List<File> unzippedFiles) {
boolean gotRepoFile = unzippedFiles.stream().anyMatch(zipFile -> zipFile.getName().equalsIgnoreCase("repo.txt"));
if (gotRepoFile) {
List<String> lines = BackendUtil.extractLinesFromRepoFile(unzippedFiles, "repo.txt");
String repoUrl = (lines.size() > 0 && !lines.get(0).equals("")) ?
lines.get(0) : "";
String credentials = (lines.size() > 1 && !lines.get(1).equals("")) ?
lines.get(1) : null;
File unit = new File(subFolderPath + "/src/UnitTests");
unit.mkdirs();
File task = new File(subFolderPath + "/src/Test");
task.mkdirs();
this.jGitUtil.cloneRepository("http://" + this.modocotProperties.getDockerHostIp()
+ ":3000/" + this.modocotProperties.getGitTeaUsername() + "/"
+ assignmentId + ".git",
unit, null, false);
this.jGitUtil.cloneRepository(repoUrl, task, credentials, true);
}
}
}
package de.hftstuttgart.rest.v1.unittest;
import de.hftstuttgart.config.ModocotProperties;
import de.hftstuttgart.utils.BackendUtil;
import de.hftstuttgart.utils.FileUtil;
import de.hftstuttgart.utils.GitTeaUtil;
import de.hftstuttgart.utils.JGitUtil;
import de.hftstuttgart.utils.UnzipUtil;
import io.gitea.model.Repository;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.eclipse.jgit.transport.PushResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.annotation.MultipartConfig;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.UUID;
/**
* Rest controller for anything related to the TEST files.
*/
@RestController
@RequestMapping("/v1/unittest")
@MultipartConfig
public class UnitTestUpload {
private static final Logger LOG = LogManager.getLogger(UnitTestUpload.class);
private final ModocotProperties modocotProperties;
private final GitTeaUtil gitTeaUtil;
private final JGitUtil jGitUtil;
private final String assignmentBasePath;
public UnitTestUpload(ModocotProperties modocotProperties,
GitTeaUtil gitTeaUtil,
JGitUtil jGitUtil) {
this.modocotProperties = modocotProperties;
this.gitTeaUtil = gitTeaUtil;
this.jGitUtil = jGitUtil;
this.assignmentBasePath =
modocotProperties.getModocotParentDirectory()
+ File.separator
+ modocotProperties.getModocotAssignmentFolderPrefix();
}
/**
* Create a subfolder for the specific assignment.
* This is called when the teacher creates an assignment and uploads the JUnit test files
*
* @param unitTestFileRef The zip file which contains the JUnit tests
* @param assignmentId ID of the created assignment. Generated by Moodle
*/
@RequestMapping(method = RequestMethod.POST)
public void uploadUnitTestFile(@RequestParam("unitTestFile") MultipartFile unitTestFileRef, @RequestParam("assignmentId") String assignmentId) throws IOException {
// path to the directory in which we will be working
String subFolderPath = this.assignmentBasePath + assignmentId;
LOG.info("work-directory: " + subFolderPath);
// creating the work-directory
File workDirectory = new File(subFolderPath);
workDirectory.mkdirs();
// creating the file which the unitTestFileRef will be transferred into
File file = new File(subFolderPath, String.valueOf(UUID.randomUUID()));
// transferring MultipartFile into temporary file
unitTestFileRef.transferTo(file);
// unzipping temporary file to work-directory
List<File> zipFiles = UnzipUtil.unzip(file);
// unzipping temporary file (not needed anymore)
if (file.exists()) file.delete();
// check if any extracted files are named repo.txt
if (zipFiles.stream().anyMatch(zipFile -> zipFile.getName().equalsIgnoreCase("repo.txt"))) {
// reading all from the repo.txt
List<String> lines = BackendUtil.extractLinesFromRepoFile(zipFiles, "repo.txt");
// either set the first line of repo.txt or ""
String repoUrl = (lines.size() > 0 && !lines.get(0).equals("")) ?
lines.get(0) : "";
// either set the second line of repo.txt or null
String credentials = ((lines.size() > 1) && !lines.get(1).equals("")) ?
lines.get(1) : null;
// cloning repository repoUrl into work-directory, using credentials from second line in repo.txt
this.jGitUtil.cloneRepository(repoUrl, workDirectory, credentials, true);
}
// creating repository on internal giTea, name = assignmentId
Repository repo = this.gitTeaUtil.createRepository(assignmentId);
// giTea runs in docker and returns CloneUrl as localhost, replacing localhost with docker-host ip
repo.setCloneUrl(repo.getCloneUrl().replace("localhost", modocotProperties.getDockerHostIp()));
// committing everything in work-directory and push to created repository
Iterable<PushResult> pushResults = this.jGitUtil.commitAllAndPush(workDirectory, repo, false);
if (pushResults != null) {
for (PushResult pushResult : pushResults) {
LOG.info("Push-Result: " + pushResult.getMessages());
}
}
// deleting created work-directory
FileUtil.deleteFolderRecursively(workDirectory);
LOG.info("Uploaded unit test file: " + workDirectory);
}
/**
* Delete the folder for the assignment.
* Called when the teacher deletes the JUnitTest assignment
* <p>
* {{url}}:8080/v1/unittest?assignmentId=111
*
* @param assignmentId ID of the assignment to delete. Generated by Moodle
*/
@RequestMapping(method = RequestMethod.DELETE)
public void deleteUnitTestFiles(@RequestParam("assignmentId") String assignmentId) {
String path = this.assignmentBasePath + assignmentId;
File dir = new File(path);
FileUtil.deleteFolderRecursively(dir);
}
}
package de.hftstuttgart.utils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.stream.Collectors;
public class BackendUtil {
/**
* Loop through all {@link File}s in {@code filesFromZipFile} and find {@link File} {@code fileNameToSearchFor}. <br>
* And return a {@link List} of Strings for each line in file {@code fileNameToSearchFor}
*
* @param filesFromZipFile all files which get extracted from previous zipFile
* @param fileNameToSearchFor search for specific name
* @return {@link List} of Strings for each line in file {@code fileNameToSearchFor}
*/
public static List<String> extractLinesFromRepoFile(List<File> filesFromZipFile, String fileNameToSearchFor) {
if(filesFromZipFile.size() < 1 && fileNameToSearchFor != null) {
throw new IllegalArgumentException();
}
List<String> lines = null;
for (File file : filesFromZipFile) {
if (file.getName().equalsIgnoreCase(fileNameToSearchFor)) {
try {
FileInputStream fis = new FileInputStream(file);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
lines = br.lines().collect(Collectors.toList());
br.close();
if (file.exists())
file.delete();
} catch (IOException ignored) { }
}
}
return lines;
}
}
package de.hftstuttgart.utils;
import com.fasterxml.jackson.annotation.JsonProperty;
public enum BuildState {
@JsonProperty("blue") BLUE,
@JsonProperty("notbuilt") NOTBUILT,
@JsonProperty("notbuilt_anime") NOTBUILT_ANMIE,
@JsonProperty("red") RED,
@JsonProperty("yellow") YELLOW,
@JsonProperty("grey") GREY,
@JsonProperty("disabled") DISABLED,
@JsonProperty("aborted") ABORTED,
@JsonProperty("aborted_anime") ABORTED_ANIME,
@JsonProperty("grey_anime") GREY_ANIME
}
package de.hftstuttgart.utils;
import java.io.File;
/**
* Helper Class for all file related tasks.
*
* Created by Marcel Bochtler on 05.01.17.
*/
public class FileUtil {
/**
* Delete the folder and all containing files.
* @param folder Folder to delete
*/
public static void deleteFolderRecursively(File folder) {
File[] files = folder.listFiles();
if (files != null) {
for (File f : files) {
if (f.isDirectory()) {
deleteFolderRecursively(f);
} else {
f.delete();
}
}
}
folder.delete();
}
}
package de.hftstuttgart.utils;
import de.hftstuttgart.config.ModocotProperties;
import io.gitea.ApiClient;
import io.gitea.ApiException;
import io.gitea.Configuration;
import io.gitea.api.RepositoryApi;
import io.gitea.api.UserApi;
import io.gitea.auth.HttpBasicAuth;
import io.gitea.model.CreateRepoOption;
import io.gitea.model.Repository;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;
@Component
public class GitTeaUtil {
private static final Logger LOG = LogManager.getLogger(GitTeaUtil.class);
private final ModocotProperties modocotProperties;
public GitTeaUtil(ModocotProperties modocotProperties) {
this.modocotProperties = modocotProperties;
}
/**
* Create Git-Repository with name {@code repositoryName} on internal Git-Server
*
* @param repositoryName name of new Repository
* @return newly created Repository
*/
public Repository createRepository(String repositoryName) {
setupAuth();
Repository repo = null;
try {
repo = new UserApi()
.createCurrentUserRepo(new CreateRepoOption().name(repositoryName));
} catch (ApiException e) {
LOG.error(String.format("Error while creating repository: %s", repositoryName), e);
}
if (repo != null) {
LOG.info("Created repository {} on {}", repositoryName, repo.getCloneUrl());
} else {
throw new IllegalStateException("Repository is null");
}
return repo;
}
/**
* Delete Git-Repository with name {@code repositoryName} on internal Git-Server
*
* @param repositoryName name of new Repository
*/
public void deleteRepository(String repositoryName) {
setupAuth();
try {
new RepositoryApi().repoDelete(this.modocotProperties.getGitTeaUsername(), repositoryName);
} catch (ApiException e) {
LOG.error("Error while deleting repository:" + e.getMessage());
}
}
private void setupAuth() {
ApiClient defaultClient = Configuration.getDefaultApiClient();
defaultClient.setBasePath(this.modocotProperties.getGitTeaBasePath());
HttpBasicAuth basicAuth = (HttpBasicAuth) defaultClient.getAuthentication("BasicAuth");
basicAuth.setUsername(this.modocotProperties.getGitTeaUsername());
basicAuth.setPassword(this.modocotProperties.getGitTeaPassword());
}
}
package de.hftstuttgart.utils;
import de.hftstuttgart.config.ModocotProperties;
import io.gitea.model.Repository;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.SocketAddress;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
@Component
public class JGitUtil {
private static final Logger LOG = LogManager.getLogger(JGitUtil.class);
private final ModocotProperties modocotProperties;
public JGitUtil(ModocotProperties modocotProperties) {
this.modocotProperties = modocotProperties;
}
/**
* Clone Repository from {@code uriToClone} into Directory {@code cloneDirectory}, and using token {@code token}.<br>
* {@code boolean proxy} determines, if a proxy should be used
*
* @param uriToClone Repository cloneUrl
* @param cloneDirectory {@link File} directory in which Repository should be cloned into
* @param token AuthToken, when cloning Repository. If {@code null} use {@code ModocotProperties}
* @param proxy boolean if a proxy should be used
*/
public void cloneRepository(String uriToClone, File cloneDirectory, String token, boolean proxy) {
if (!cloneDirectory.isDirectory()) {
String error = String.format("%s is not a directory, cannot clone", cloneDirectory.getAbsolutePath());
LOG.error(error);
throw new IllegalArgumentException(error);
}
LOG.info(String.format("Cloning all files from %s into %s", uriToClone, cloneDirectory.getAbsolutePath()));
try {
deleteDotGitFolder(cloneDirectory);
UsernamePasswordCredentialsProvider credProvider;
if (token != null) {
LOG.info("Using token: " + token);
String[] credentials = token.split(":");
credProvider = new UsernamePasswordCredentialsProvider(credentials[0], credentials[1]);
} else {
LOG.info("Using credentials: " + this.modocotProperties.getGitTeaUsername() + ", " + this.modocotProperties.getGitTeaPassword());
credProvider = new UsernamePasswordCredentialsProvider(
this.modocotProperties.getGitTeaUsername(),
this.modocotProperties.getGitTeaPassword());
}
LOG.info("Starting cloning, url: " + uriToClone);
setProxy(proxy);
Git.cloneRepository()
.setCredentialsProvider(credProvider)
.setURI(uriToClone)
.setDirectory(cloneDirectory)
.call()
.close();
} catch (GitAPIException e) {
LOG.error(String.format("Error while cloning from %s", uriToClone), e);
}
}
/**
* Commit {@code directory} and push all files to the given {@code repository}.
*
* @param directory {@link File} directory to commit
* @param repository {@link Repository} Repository to push to
* @param proxy boolean if a proxy should be used
* @return {@code Iterable<PushResult>}
*/
public Iterable<PushResult> commitAllAndPush(File directory, Repository repository, boolean proxy) {
if (!directory.isDirectory()) {
String error = String.format("%s is not a directory, cannot commit or push", directory.getAbsolutePath());
LOG.error(error);
throw new IllegalArgumentException(error);
}
LOG.info(String.format("Committing all files in: %s and pushing them to: %s", directory.getAbsolutePath(), repository.getCloneUrl()));
try {
deleteDotGitFolder(directory);
// "git init" new repository
Git.init()
.setDirectory(directory)
.call();
// open new repository
Git git = Git.open(directory);
// "git add ." on repository
git.add()
.addFilepattern(".")
.call();
// "git commit -m %gitTeaCommitMessage%" on repository
git.commit()
.setMessage(this.modocotProperties.getGitTeaDefaultCommitMessage())
.call();
// add new remote from repository
git.remoteAdd()
.setName(this.modocotProperties.getGitTeaOrigin())
.setUri(new URIish(repository.getCloneUrl()))
.call();
// "git push" to new origin
setProxy(proxy);
return git.push()
.setCredentialsProvider(new UsernamePasswordCredentialsProvider(
this.modocotProperties.getGitTeaUsername(),
this.modocotProperties.getGitTeaPassword()))
.call();
} catch (Exception e) {
LOG.error(String.format("Error while committing to repo: %s from file: %s", repository, directory.getAbsolutePath()), e);
}
return null;
}
private void deleteDotGitFolder(File directory) {
File temp = new File(directory.getPath() + "/src/UnitTests/.git");
if (temp.exists() && temp.isDirectory() && temp.canWrite()) {
FileUtil.deleteFolderRecursively(temp);
}
temp = new File(directory.getPath() + "/src/.git");
if (temp.exists() && temp.isDirectory() && temp.canWrite()) {
FileUtil.deleteFolderRecursively(temp);
}
}
private void setProxy(boolean proxy) {
if (proxy) {
ProxySelector.setDefault(new ProxySelector() {
final ProxySelector delegate = ProxySelector.getDefault();
@Override
public List<Proxy> select(URI uri) {
// Filter the URIs to be proxied
if (uri.toString().contains("https")) {
return Arrays.asList(new Proxy(Proxy.Type.HTTP, InetSocketAddress
.createUnresolved("proxy.hft-stuttgart.de", 80)));
}
if (uri.toString().contains("http")) {
return Arrays.asList(new Proxy(Proxy.Type.HTTP, InetSocketAddress
.createUnresolved("proxy.hft-stuttgart.de", 80)));
}
// revert to the default behaviour
return delegate == null ? Arrays.asList(Proxy.NO_PROXY)
: delegate.select(uri);
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
if (uri == null || sa == null || ioe == null) {
throw new IllegalArgumentException(
"Arguments can't be null.");
}
}
});
}
}
}
package de.hftstuttgart.utils;
import de.hftstuttgart.config.ModocotProperties;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
@Component
public class RestCall {
private static final Logger LOG = LogManager.getLogger(RestCall.class);
private final ModocotProperties modocotProperties;
public RestCall(ModocotProperties modocotProperties) {
this.modocotProperties = modocotProperties;
}
public <T> ResponseEntity<T> exchange(String specificUrl, HttpMethod method, T body, Class<T> responseType, Object... uriVariables) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
headers.add("Authorization", "Basic " + new String(Base64.encodeBase64((this.modocotProperties.getJenkinsApiToken()).getBytes())));
headers.add("user", "admin");
LOG.info(method.toString() + ", to: " + this.modocotProperties.getJenkinsBaseUrl() + specificUrl);
return restTemplate.exchange(this.modocotProperties.getJenkinsBaseUrl() + specificUrl, method, new HttpEntity<>(body, headers), responseType, uriVariables);
}
}
package de.hftstuttgart.utils;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.hftstuttgart.config.ModocotProperties;
import de.hftstuttgart.models.JenkinsJobData;
import de.hftstuttgart.rest.v1.jenkins.RestAPIController;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.Version;
import io.gitea.model.Repository;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
@Component
public class TaskUploadUtils {
private static final Logger LOG = LogManager.getLogger(TaskUploadUtils.class);
private final GitTeaUtil gitTeaUtil;
private final ModocotProperties modocotProperties;
private final JGitUtil jGitUtil;
private final RestCall restCall;
public TaskUploadUtils(GitTeaUtil gitTeaUtil,
ModocotProperties modocotProperties,
JGitUtil jGitUtil,
RestCall restCall) {
this.gitTeaUtil = gitTeaUtil;
this.modocotProperties = modocotProperties;
this.jGitUtil = jGitUtil;
this.restCall = restCall;
}
/**
* Start jenkinsJob and return its BuildStatus after completion
*
* @param jobId String jenkinsJobId
* @param subFolderPath workingDirectory
* @return Jenkins BuildStatus
*/
public BuildState startTask(String jobId, String subFolderPath) throws IOException, InterruptedException {
// we clone the Student submission to /Test subfolder and then copy those files into the working directory
File f = new File(subFolderPath + "/src/Test");
for (File fi : f.listFiles()) {
Files.move(Paths.get(fi.getPath()), Paths.get(subFolderPath + "/src/" + fi.getName()));
}
if (f.exists()) f.delete();
File temp = new File(subFolderPath + "/src/UnitTests/.git");
if (temp.exists() && temp.isDirectory() && temp.canWrite()) {
FileUtil.deleteFolderRecursively(temp);
}
temp = new File(subFolderPath + "/src/.git");
if (temp.exists() && temp.isDirectory() && temp.canWrite()) {
FileUtil.deleteFolderRecursively(temp);
}
// creating the jobId.json containing the jobId
FileWriter fileWriter = new FileWriter(subFolderPath + "/src/jobId.json");
fileWriter.write(new ObjectMapper().readTree("{\"jobId\":\"" + jobId + "\"}").toString());
fileWriter.close();
// creating repository with name jobId
Repository repository = this.gitTeaUtil.createRepository(jobId);
repository.setCloneUrl(repository.getCloneUrl().replace("localhost", modocotProperties.getDockerHostIp()));
// committing work-directory and pushing all files to repository
this.jGitUtil.commitAllAndPush(new File(subFolderPath), repository, false);
FileUtil.deleteFolderRecursively(new File(subFolderPath));
// persisting the jobId
RestAPIController.JOB_MAP.put(jobId, null);
createJenkinsJob(jobId, repository.getCloneUrl());
buildJenkinsJob(jobId);
// waiting for jenkinsJob to finish then returning its BuildStatus
int timeout = 0;
BuildState buildState = getJenkinsBuildState(jobId);
while (buildState != BuildState.BLUE && buildState != BuildState.RED) {
buildState = getJenkinsBuildState(jobId);
if (timeout >= 100) break;
Thread.sleep(6000);
timeout++;
}
return buildState;
}
public void deleteJenkinsJob(String jobId) {
LOG.info("deleteJenkinsJob jobId: " + jobId);
ResponseEntity<String> response = this.restCall.exchange("job/" + jobId + "/doDelete", HttpMethod.POST, null, String.class);
}
private BuildState getJenkinsBuildState(String jenkinsJob) {
LOG.info("getJenkinsBuildState jenkinsJob: " + jenkinsJob);
ResponseEntity<JenkinsJobData> response = this.restCall.exchange("api/json?tree=jobs[name,color]", HttpMethod.GET, null, JenkinsJobData.class);
if (response.getBody() == null) {
throw new NullPointerException("Jenkins Response was null");
}
return response.getBody()
.getJobs()
.stream()
.filter(job -> job.getName().equals(jenkinsJob))
.findFirst()
.get()
.getColor();
}
public String getJenkinsConsoleOutput(String jenkinsJob) {
ResponseEntity<String> response = this.restCall.exchange("job/" + jenkinsJob + "/lastBuild/consoleText", HttpMethod.GET, null, String.class);
return response.getBody();
}
private void buildJenkinsJob(String user) {
LOG.info("buildJenkinsJob user: " + user);
ResponseEntity<String> response = this.restCall.exchange("job/" + user + "/build", HttpMethod.POST, null, String.class);
}
private void createJenkinsJob(String gitUser, String gitUrl) {
LOG.info("createJenkinsJob gitUser: " + gitUser + ", gitUrl: " + gitUrl);
ResponseEntity<String> response = this.restCall.exchange("createItem?name=" + gitUser, HttpMethod.POST, createXmlFile(gitUser, gitUrl), String.class);
}
private String createXmlFile(String gitUser, String gitUrl) {
// fill data map for template
Map<String, String> templateData = new HashMap<>();
templateData.put("gitUser", gitUser);
templateData.put("gitUrl", gitUrl);
// freemarker create config
Configuration cfg = new Configuration(new Version("2.3.30"));
cfg.setClassForTemplateLoading(this.getClass(), "/templates");
cfg.setDefaultEncoding("UTF-8");
// fuse config and data
StringWriter out = new StringWriter();
try {
Template template = cfg.getTemplate("JenkinsFile.ftl");
template.process(templateData, out);
LOG.info(String.format("Template created with gitUrl: %s and gitUser: %s", gitUrl, gitUser));
} catch (IOException | TemplateException e) {
e.printStackTrace();
}
return out.getBuffer().toString();
}
public boolean isValidJSON(final String json) {
boolean valid = false;
try {
final JsonParser parser = new ObjectMapper().getFactory().createParser(json);
while (parser.nextToken() != null) {
}
valid = true;
} catch (IOException e) {
LOG.error("Json is invalid", e);
}
return valid;
}
}
package de.hftstuttgart.utils;
import de.hftstuttgart.exceptions.CorruptedZipFileException;
import de.hftstuttgart.exceptions.NoZipFileException;
import org.apache.log4j.Logger;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipInputStream;
/**
* Created by Marcel Bochtler on 13.11.16.
* Based on: https://www.mkyong.com/java/how-to-decompress-files-from-a-zip-file/
*/
public class UnzipUtil {
private static final Logger LOG = Logger.getLogger(UnzipUtil.class);
/**
* Unzips files and saves them to the disk.
* Checks if the zip file is valid.
*/
public static List<File> unzip(File zipFile) throws IOException {
String outputFolder = zipFile.getParentFile().getAbsolutePath();
List<File> unzippedFiles = new ArrayList<>();
byte[] buffer = new byte[1024];
//create output directory is not exists
File folder = new File(zipFile.getAbsolutePath());
if (!folder.exists()) {
folder.mkdir();
}
try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipFile))) {
ZipEntry zipEntry = zipInputStream.getNextEntry();
if (zipEntry == null) {
String message = "The file " + zipFile.getAbsolutePath() + " does not seem be a zip file";
LOG.error(message);
throw new NoZipFileException(message);
}
while (zipEntry != null) {
String fileName = zipEntry.getName();
File unzippedFile = new File(outputFolder + File.separator + fileName);
LOG.info("Unzipped file: " + unzippedFile.getName());
// create all non exists folders
// else we will hit FileNotFoundException for compressed folder
new File(unzippedFile.getParent()).mkdirs();
FileOutputStream fos = new FileOutputStream(unzippedFile);
int length;
while ((length = zipInputStream.read(buffer)) > 0) {
fos.write(buffer, 0, length);
}
fos.close();
zipEntry = zipInputStream.getNextEntry();
unzippedFiles.add(unzippedFile);
}
if (zipFile.exists()) {
zipFile.delete();
}
return unzippedFiles;
} catch (ZipException ze) {
String msg = "Failed to unzip file " + zipFile;
LOG.error(msg);
throw new CorruptedZipFileException(msg);
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment