Skip to content
GitLab
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
c9e59167
Verified
Commit
c9e59167
authored
Dec 30, 2020
by
Lukas Wiest
🚂
Browse files
Merge branch 'feat-allow-direct-submissions'
parents
657b50aa
09926fdc
Changes
5
Hide whitespace changes
Inline
Side-by-side
pom.xml
View file @
c9e59167
...
...
@@ -74,6 +74,13 @@
<artifactId>
okhttp
</artifactId>
<version>
4.9.0
</version>
</dependency>
<!-- check if received multipart file is text or zip file -->
<dependency>
<groupId>
org.apache.tika
</groupId>
<artifactId>
tika-core
</artifactId>
<version>
1.25
</version>
</dependency>
</dependencies>
<build>
...
...
src/main/java/de/hftstuttgart/rest/v1/task/TaskUpload.java
View file @
c9e59167
package
de.hftstuttgart.rest.v1.task
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.github.dockerjava.api.model.Bind
;
import
com.github.dockerjava.api.model.Volume
;
import
de.hftstuttgart.models.LegacyMoodleResult
;
import
de.hftstuttgart.models.ModocotResultSummary
;
import
de.hftstuttgart.rest.v1.unittest.UnitTestUpload
;
import
de.hftstuttgart.utils.DockerUtil
;
import
de.hftstuttgart.utils.FileUtil
;
import
de.hftstuttgart.utils.JGitUtil
;
import
de.hftstuttgart.utils.UnifiedTicketingUtil
;
import
de.hftstuttgart.utils.*
;
import
org.apache.logging.log4j.LogManager
;
import
org.apache.logging.log4j.Logger
;
import
org.apache.tika.Tika
;
import
org.springframework.core.env.Environment
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RequestMethod
;
...
...
@@ -25,7 +19,6 @@ import java.nio.file.Files;
import
java.nio.file.Path
;
import
java.nio.file.Paths
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
/**
* Rest controller for everything related to the TASK files
...
...
@@ -37,28 +30,18 @@ public class TaskUpload {
private
static
final
Logger
LOG
=
LogManager
.
getLogger
(
TaskUpload
.
class
);
private
final
JGitUtil
jGitUtil
;
private
final
DockerUtil
dockerUtil
;
private
final
String
assignmentBasePath
;
private
final
Path
testTmpPathHost
;
private
final
Path
testTmpPathModocot
;
private
final
ExecuteTestUtil
executeTestUtil
;
public
TaskUpload
(
Environment
env
,
JGitUtil
jGitUtil
,
DockerUtil
docker
Util
ExecuteTestUtil
executeTest
Util
)
{
this
.
jGitUtil
=
jGitUtil
;
this
.
dockerUtil
=
dockerUtil
;
// set base path for assignments to be stored
Path
p
=
Paths
.
get
(
env
.
getProperty
(
"modocot.dir"
),
env
.
getProperty
(
"modocot.dir.test.folder.name"
));
this
.
assignmentBasePath
=
p
.
toAbsolutePath
().
toString
();
this
.
executeTestUtil
=
executeTestUtil
;
// set path of temporary directory on host and inside our container
this
.
testTmpPathHost
=
Paths
.
get
(
env
.
getProperty
(
"host.tests.tmp.dir"
));
this
.
testTmpPathModocot
=
Paths
.
get
(
env
.
getProperty
(
"modocot.tests.tmp.dir"
));
}
...
...
@@ -73,137 +56,42 @@ public class TaskUpload {
LOG
.
debug
(
String
.
format
(
"working dir for test is: %s"
,
workDirectory
.
toAbsolutePath
().
toString
()));
// define paths for the test, the submission and where the result is to be expected afterwards
Path
testPath
=
Paths
.
get
(
workDirectory
.
toAbsolutePath
().
toString
(),
"test"
);
Path
srcPath
=
Paths
.
get
(
workDirectory
.
toAbsolutePath
().
toString
(),
"src"
);
Path
resultPath
=
Paths
.
get
(
workDirectory
.
toAbsolutePath
().
toString
(),
"result"
);
// clone stored test to tmpdir
LOG
.
debug
(
"copying pre-downloaded unitttest repo"
);
FileUtil
.
copyFolder
(
Paths
.
get
(
assignmentBasePath
,
assignmentId
),
testPath
);
LOG
.
debug
(
"copy test config"
);
Files
.
copy
(
Paths
.
get
(
assignmentBasePath
,
assignmentId
+
".txt"
),
Paths
.
get
(
workDirectory
.
toAbsolutePath
().
toString
(),
"config.txt"
)
);
// clone student code to tmpdir
Pattern
pattern
=
Pattern
.
compile
(
UnitTestUpload
.
modocotDueConfigRegex
);
Matcher
config
=
null
;
LOG
.
debug
(
"reading task file"
);
// open received file in a try-with
try
(
BufferedReader
br
=
new
BufferedReader
(
new
InputStreamReader
(
taskFileRef
.
getInputStream
())))
{
String
line
;
// as long as we haven't found a configuration and have lines left, search
while
(
config
==
null
&&
(
line
=
br
.
readLine
())
!=
null
)
{
Matcher
matcher
=
pattern
.
matcher
(
line
);
if
(
matcher
.
matches
())
{
LOG
.
debug
(
String
.
format
(
"found modocot line: %s"
,
line
));
config
=
matcher
;
}
}
}
catch
(
IOException
e
)
{
LOG
.
error
(
"Error while reading repo config"
,
e
);
}
finally
{
if
(
config
==
null
)
{
throw
new
RuntimeException
(
"couldn't find repo config for unittest clone"
);
}
}
LOG
.
debug
(
"calling repo clone"
);
jGitUtil
.
cloneRepository
(
config
,
srcPath
.
toAbsolutePath
().
toString
());
Files
.
createDirectory
(
resultPath
);
LOG
.
info
(
"starting unittest"
);
pattern
=
Pattern
.
compile
(
UnitTestUpload
.
modocotTestConfigRegex
);
config
=
null
;
LOG
.
debug
(
"reading test config"
);
// open test config and read it in again, important to know with which docker image the test should be run
try
(
BufferedReader
br
=
new
BufferedReader
(
new
InputStreamReader
(
new
FileInputStream
(
Paths
.
get
(
workDirectory
.
toAbsolutePath
().
toString
(),
"config.txt"
).
toFile
()))))
{
String
line
;
while
(
config
==
null
&&
(
line
=
br
.
readLine
())
!=
null
)
{
Matcher
matcher
=
pattern
.
matcher
(
line
);
if
(
matcher
.
matches
())
{
LOG
.
debug
(
String
.
format
(
"found modocot line: %s"
,
line
));
config
=
matcher
;
}
}
}
catch
(
IOException
e
)
{
LOG
.
error
(
"Error while reading test config"
,
e
);
}
finally
{
if
(
config
==
null
)
{
throw
new
RuntimeException
(
"couldn't find test config for unittest"
);
}
}
// define the paths to mount as Binds from Host to the test-container
Path
testPathHost
=
Paths
.
get
(
testTmpPathHost
.
toAbsolutePath
().
toString
(),
workDirectory
.
getName
(
workDirectory
.
getNameCount
()-
1
).
toString
(),
testPath
.
getName
(
testPath
.
getNameCount
()-
1
).
toString
()
);
Path
srcPathHost
=
Paths
.
get
(
testTmpPathHost
.
toAbsolutePath
().
toString
(),
workDirectory
.
getName
(
workDirectory
.
getNameCount
()-
1
).
toString
(),
srcPath
.
getName
(
srcPath
.
getNameCount
()-
1
).
toString
()
);
Path
resultPathHost
=
Paths
.
get
(
testTmpPathHost
.
toAbsolutePath
().
toString
(),
workDirectory
.
getName
(
workDirectory
.
getNameCount
()-
1
).
toString
(),
resultPath
.
getName
(
resultPath
.
getNameCount
()-
1
).
toString
()
);
// start test-container with professor given image and bind mounts for test, submission and result
dockerUtil
.
runContainer
(
config
.
group
(
4
),
new
Bind
(
testPathHost
.
toAbsolutePath
().
toString
(),
new
Volume
(
"/modocot/test"
)),
new
Bind
(
srcPathHost
.
toAbsolutePath
().
toString
(),
new
Volume
(
"/modocot/src"
)),
new
Bind
(
resultPathHost
.
toAbsolutePath
().
toString
(),
new
Volume
(
"/modocot/result"
))
);
// define expected result file
File
resultFile
=
Paths
.
get
(
resultPath
.
toAbsolutePath
().
toString
(),
"result.json"
).
toFile
();
// check if result file is there
if
(!
resultFile
.
exists
()
||
!
resultFile
.
isFile
())
{
LOG
.
error
(
String
.
format
(
"couln't find result file in %s"
,
resultFile
.
getAbsolutePath
()));
throw
new
RuntimeException
(
"no resultfile found"
);
String
mimeInfo
=
new
Tika
().
detect
(
taskFileRef
.
getInputStream
());
switch
(
mimeInfo
)
{
case
"text/plain"
:
LOG
.
debug
(
"textfile uploaded, searching for modocot config"
);
// find modocot URI in config file
Matcher
config
=
RegexUtil
.
findModocotStudentConfig
(
taskFileRef
.
getInputStream
());
LOG
.
debug
(
"calling repo clone"
);
jGitUtil
.
cloneRepository
(
config
,
srcPath
.
toAbsolutePath
().
toString
());
break
;
case
"application/zip"
:
LOG
.
debug
(
"zip archive uploaded, extracting content as student submission"
);
ArchiveUtil
.
extractProjectFromZip
(
taskFileRef
.
getInputStream
(),
srcPath
.
toAbsolutePath
());
break
;
default
:
String
msg
=
String
.
format
(
"couldn't process uploaded file with mime type %s"
,
mimeInfo
);
LOG
.
error
(
msg
);
throw
new
RuntimeException
(
msg
);
}
LOG
.
debug
(
"parse results json"
);
ObjectMapper
objectMapper
=
new
ObjectMapper
();
ModocotResultSummary
resultSummary
=
objectMapper
.
readValue
(
resultFile
.
toURI
().
toURL
(),
ModocotResultSummary
.
class
);
// run test
LOG
.
debug
(
"calling test execution"
);
ModocotResultSummary
resultSummary
=
executeTestUtil
.
runTests
(
assignmentId
,
workDirectory
);
// convert to moddle plugin readable format and return to moodle
LOG
.
debug
(
"convert to moodle understandable format"
);
LegacyMoodleResult
moodleResult
=
LegacyMoodleResult
.
convertToModdleResult
(
resultSummary
);
LOG
.
info
(
"check for provided Ticketsystem information"
);
UnifiedTicketingUtil
.
reportResults
(
taskFileRef
.
getInputStream
(),
resultSummary
);
if
(
mimeInfo
.
equals
(
"text/plain"
))
{
LOG
.
info
(
"check for provided Ticketsystem information"
);
UnifiedTicketingUtil
.
reportResults
(
taskFileRef
.
getInputStream
(),
resultSummary
);
}
LOG
.
info
(
"submission tested successfully"
);
return
moodleResult
;
...
...
src/main/java/de/hftstuttgart/utils/ArchiveUtil.java
0 → 100644
View file @
c9e59167
package
de.hftstuttgart.utils
;
import
org.apache.logging.log4j.LogManager
;
import
org.apache.logging.log4j.Logger
;
import
java.io.*
;
import
java.nio.file.Path
;
import
java.util.zip.ZipEntry
;
import
java.util.zip.ZipInputStream
;
public
class
ArchiveUtil
{
private
static
final
Logger
LOG
=
LogManager
.
getLogger
(
ArchiveUtil
.
class
);
public
static
void
extractProjectFromZip
(
InputStream
is
,
Path
outDir
)
throws
IOException
{
LOG
.
info
(
String
.
format
(
"starting archive extraction to %s"
,
outDir
.
toAbsolutePath
().
toString
()));
try
(
ZipInputStream
stream
=
new
ZipInputStream
(
is
))
{
ZipEntry
entry
;
while
((
entry
=
stream
.
getNextEntry
())
!=
null
)
{
File
file
=
outDir
.
resolve
(
entry
.
getName
()).
toFile
();
LOG
.
debug
(
String
.
format
(
"processing entry %s"
,
file
.
getAbsolutePath
()));
if
(!
entry
.
isDirectory
())
{
LOG
.
debug
(
"creating parent folders"
);
file
.
getParentFile
().
mkdirs
();
LOG
.
debug
(
"creating new file"
);
file
.
createNewFile
();
byte
[]
buffer
=
new
byte
[
2048
];
try
(
FileOutputStream
fos
=
new
FileOutputStream
(
file
);
BufferedOutputStream
bos
=
new
BufferedOutputStream
(
fos
,
buffer
.
length
))
{
LOG
.
debug
(
"writing content from zip entry to file"
);
int
len
;
while
((
len
=
stream
.
read
(
buffer
))
>
0
)
{
bos
.
write
(
buffer
,
0
,
len
);
}
}
}
}
}
}
}
src/main/java/de/hftstuttgart/utils/ExecuteTestUtil.java
0 → 100644
View file @
c9e59167
package
de.hftstuttgart.utils
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.github.dockerjava.api.model.Bind
;
import
com.github.dockerjava.api.model.Volume
;
import
de.hftstuttgart.models.ModocotResultSummary
;
import
org.apache.log4j.LogManager
;
import
org.apache.log4j.Logger
;
import
org.springframework.core.env.Environment
;
import
org.springframework.stereotype.Component
;
import
java.io.*
;
import
java.nio.file.Files
;
import
java.nio.file.Path
;
import
java.nio.file.Paths
;
import
java.util.regex.Matcher
;
@Component
public
class
ExecuteTestUtil
{
private
static
final
Logger
LOG
=
LogManager
.
getLogger
(
ExecuteTestUtil
.
class
);
private
final
JGitUtil
jGitUtil
;
private
final
DockerUtil
dockerUtil
;
private
final
String
assignmentBasePath
;
private
final
Path
testTmpPathHost
;
private
final
Path
testTmpPathModocot
;
public
ExecuteTestUtil
(
Environment
env
,
JGitUtil
jGitUtil
,
DockerUtil
dockerUtil
)
{
this
.
jGitUtil
=
jGitUtil
;
this
.
dockerUtil
=
dockerUtil
;
// set base path for assignments to be stored
Path
p
=
Paths
.
get
(
env
.
getProperty
(
"modocot.dir"
),
env
.
getProperty
(
"modocot.dir.test.folder.name"
));
this
.
assignmentBasePath
=
p
.
toAbsolutePath
().
toString
();
// set path of temporary directory on host and inside our container
this
.
testTmpPathHost
=
Paths
.
get
(
env
.
getProperty
(
"host.tests.tmp.dir"
));
this
.
testTmpPathModocot
=
Paths
.
get
(
env
.
getProperty
(
"modocot.tests.tmp.dir"
));
}
public
ModocotResultSummary
runTests
(
String
assignmentId
,
Path
workDirectory
)
throws
IOException
,
InterruptedException
{
// define paths for the test, the submission and where the result is to be expected afterwards
Path
testPath
=
Paths
.
get
(
workDirectory
.
toAbsolutePath
().
toString
(),
"test"
);
Path
srcPath
=
Paths
.
get
(
workDirectory
.
toAbsolutePath
().
toString
(),
"src"
);
Path
resultPath
=
Paths
.
get
(
workDirectory
.
toAbsolutePath
().
toString
(),
"result"
);
// clone stored test to tmpdir
LOG
.
debug
(
"copying pre-downloaded unitttest repo"
);
FileUtil
.
copyFolder
(
Paths
.
get
(
assignmentBasePath
,
assignmentId
),
testPath
);
LOG
.
debug
(
"copy test config"
);
Files
.
copy
(
Paths
.
get
(
assignmentBasePath
,
assignmentId
+
".txt"
),
Paths
.
get
(
workDirectory
.
toAbsolutePath
().
toString
(),
"config.txt"
)
);
Files
.
createDirectory
(
resultPath
);
LOG
.
info
(
"reading test config"
);
Matcher
config
=
RegexUtil
.
findModocotProfessorConfig
(
new
FileInputStream
(
Paths
.
get
(
workDirectory
.
toAbsolutePath
().
toString
(),
"config.txt"
).
toFile
()));
// define the paths to mount as Binds from Host to the test-container
Path
testPathHost
=
Paths
.
get
(
testTmpPathHost
.
toAbsolutePath
().
toString
(),
workDirectory
.
getName
(
workDirectory
.
getNameCount
()-
1
).
toString
(),
testPath
.
getName
(
testPath
.
getNameCount
()-
1
).
toString
()
);
Path
srcPathHost
=
Paths
.
get
(
testTmpPathHost
.
toAbsolutePath
().
toString
(),
workDirectory
.
getName
(
workDirectory
.
getNameCount
()-
1
).
toString
(),
srcPath
.
getName
(
srcPath
.
getNameCount
()-
1
).
toString
()
);
Path
resultPathHost
=
Paths
.
get
(
testTmpPathHost
.
toAbsolutePath
().
toString
(),
workDirectory
.
getName
(
workDirectory
.
getNameCount
()-
1
).
toString
(),
resultPath
.
getName
(
resultPath
.
getNameCount
()-
1
).
toString
()
);
// start test-container with professor given image and bind mounts for test, submission and result
dockerUtil
.
runContainer
(
config
.
group
(
4
),
new
Bind
(
testPathHost
.
toAbsolutePath
().
toString
(),
new
Volume
(
"/modocot/test"
)),
new
Bind
(
srcPathHost
.
toAbsolutePath
().
toString
(),
new
Volume
(
"/modocot/src"
)),
new
Bind
(
resultPathHost
.
toAbsolutePath
().
toString
(),
new
Volume
(
"/modocot/result"
))
);
// define expected result file
File
resultFile
=
Paths
.
get
(
resultPath
.
toAbsolutePath
().
toString
(),
"result.json"
).
toFile
();
// check if result file is there
if
(!
resultFile
.
exists
()
||
!
resultFile
.
isFile
())
{
LOG
.
error
(
String
.
format
(
"couln't find result file in %s"
,
resultFile
.
getAbsolutePath
()));
throw
new
RuntimeException
(
"no resultfile found"
);
}
LOG
.
debug
(
"parse results json"
);
ObjectMapper
objectMapper
=
new
ObjectMapper
();
ModocotResultSummary
resultSummary
=
objectMapper
.
readValue
(
resultFile
.
toURI
().
toURL
(),
ModocotResultSummary
.
class
);
return
resultSummary
;
}
}
src/main/java/de/hftstuttgart/utils/RegexUtil.java
0 → 100644
View file @
c9e59167
package
de.hftstuttgart.utils
;
import
de.hftstuttgart.rest.v1.unittest.UnitTestUpload
;
import
org.apache.log4j.LogManager
;
import
org.apache.log4j.Logger
;
import
java.io.BufferedReader
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.InputStreamReader
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
public
class
RegexUtil
{
public
enum
ConfigType
{
PROFESSOR
,
STUDENT
,
}
private
static
final
Logger
LOG
=
LogManager
.
getLogger
(
RegexUtil
.
class
);
public
static
Matcher
findModocotStudentConfig
(
InputStream
is
)
{
return
findModoctConfig
(
is
,
ConfigType
.
STUDENT
);
}
public
static
Matcher
findModocotProfessorConfig
(
InputStream
is
)
{
return
findModoctConfig
(
is
,
ConfigType
.
PROFESSOR
);
}
public
static
Matcher
findModoctConfig
(
InputStream
is
,
ConfigType
configType
)
{
Pattern
pattern
;
switch
(
configType
)
{
case
PROFESSOR:
pattern
=
Pattern
.
compile
(
UnitTestUpload
.
modocotTestConfigRegex
);
break
;
case
STUDENT:
pattern
=
Pattern
.
compile
(
UnitTestUpload
.
modocotDueConfigRegex
);
break
;
default
:
String
msg
=
String
.
format
(
"unknown config type: %s"
,
configType
.
name
());
LOG
.
error
(
msg
);
throw
new
RuntimeException
(
msg
);
}
Matcher
config
=
null
;
LOG
.
debug
(
"reading config file"
);
// open received file in a try-with
try
(
BufferedReader
br
=
new
BufferedReader
(
new
InputStreamReader
(
is
)))
{
String
line
;
// as long as we haven't found a configuration and have lines left, search
while
(
config
==
null
&&
(
line
=
br
.
readLine
())
!=
null
)
{
Matcher
matcher
=
pattern
.
matcher
(
line
);
if
(
matcher
.
matches
())
{
LOG
.
debug
(
String
.
format
(
"found modocot line: %s"
,
line
));
config
=
matcher
;
}
}
}
catch
(
IOException
e
)
{
LOG
.
error
(
"Error while reading repo config"
,
e
);
}
finally
{
if
(
config
==
null
)
{
throw
new
RuntimeException
(
"couldn't find repo config for clone"
);
}
}
return
config
;
}
}
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new 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