Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Dockerized Testing Toolkit
DTT Backend
Commits
ad4e5e34
Commit
ad4e5e34
authored
Jul 01, 2020
by
Dominik Vayhinger
Browse files
Initial Commit
parents
Pipeline
#894
failed with stages
in 23 seconds
Changes
50
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
src/main/java/de/hftstuttgart/dockertests/app/META-INF/war-tracker
0 → 100644
View file @
ad4e5e34
src/main/java/de/hftstuttgart/dockertests/app/index.html
0 → 100644
View file @
ad4e5e34
<!DOCTYPE html>
<html>
<head>
<title>
Example
</title>
</head>
<body>
<p>
Hello World
</p>
</body>
</html>
\ No newline at end of file
src/main/java/de/hftstuttgart/dockertests/app/junit-platform-console-standalone-1.6.2.jar
0 → 100644
View file @
ad4e5e34
File added
src/main/java/de/hftstuttgart/dockertests/filetest.txt
0 → 100644
View file @
ad4e5e34
Bind mount persistance data through bind Mount
src/main/java/de/hftstuttgart/exceptions/CorruptedZipFileException.java
0 → 100644
View file @
ad4e5e34
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
);
}
}
src/main/java/de/hftstuttgart/exceptions/NoZipFileException.java
0 → 100644
View file @
ad4e5e34
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
);
}
}
src/main/java/de/hftstuttgart/models/JenkinsJobData.java
0 → 100644
View file @
ad4e5e34
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
;
}
}
}
src/main/java/de/hftstuttgart/models/TestResult.java
0 → 100644
View file @
ad4e5e34
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
+
'}'
;
}
}
src/main/java/de/hftstuttgart/models/UserResult.java
0 → 100644
View file @
ad4e5e34
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
;
}
}
src/main/java/de/hftstuttgart/rest/v1/jenkins/RestAPIController.java
0 → 100644
View file @
ad4e5e34
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
src/main/java/de/hftstuttgart/rest/v1/task/TaskUpload.java
0 → 100644
View file @
ad4e5e34
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
);
}
}
}
src/main/java/de/hftstuttgart/rest/v1/unittest/UnitTestUpload.java
0 → 100644
View file @
ad4e5e34
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
);
}
}
src/main/java/de/hftstuttgart/utils/BackendUtil.java
0 → 100644
View file @
ad4e5e34
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
;
}
}
src/main/java/de/hftstuttgart/utils/BuildState.java
0 → 100644
View file @
ad4e5e34
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
}
src/main/java/de/hftstuttgart/utils/FileUtil.java
0 → 100644
View file @
ad4e5e34
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
();
}
}
src/main/java/de/hftstuttgart/utils/GitTeaUtil.java
0 → 100644
View file @
ad4e5e34
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
());
}
}
src/main/java/de/hftstuttgart/utils/JGitUtil.java
0 → 100644
View file @
ad4e5e34
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."
);
}
}
});
}
}
}
src/main/java/de/hftstuttgart/utils/RestCall.java
0 → 100644
View file @
ad4e5e34
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
);
}
}
src/main/java/de/hftstuttgart/utils/TaskUploadUtils.java
0 → 100644
View file @
ad4e5e34
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
;
}
}
src/main/java/de/hftstuttgart/utils/UnzipUtil.java
0 → 100644
View file @
ad4e5e34
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
);
}
}
}
Prev
1
2
3
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment