Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrated code lifecycle: Provide Instructors more options to control container configuration #9487

Open
wants to merge 66 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
5ee5b07
server side
BBesrour Oct 13, 2024
7660c8b
ui
BBesrour Oct 14, 2024
20f6666
ui
BBesrour Oct 15, 2024
b51ef93
ui
BBesrour Oct 15, 2024
3e3895a
server validation
BBesrour Oct 15, 2024
65751ff
ui
BBesrour Oct 15, 2024
8ec3f57
tests
BBesrour Oct 15, 2024
7c98073
tests
BBesrour Oct 15, 2024
08b22d3
coderabbit feedback
BBesrour Oct 16, 2024
566ede9
add warning
BBesrour Oct 16, 2024
b1cd291
fix style
BBesrour Oct 16, 2024
9d6ba04
fix translations
BBesrour Oct 16, 2024
f35a756
feedback
BBesrour Oct 18, 2024
2455bcb
feedback
BBesrour Oct 18, 2024
6702638
feedback
BBesrour Oct 18, 2024
ccb2a2a
Merge branch 'develop' into feature/integrated-code-lifecycle/custom-…
BBesrour Oct 20, 2024
4fbd6f4
add docs
BBesrour Oct 20, 2024
6d4393c
Merge branch 'develop' into feature/integrated-code-lifecycle/custom-…
BBesrour Oct 22, 2024
39b62f8
fix tests
BBesrour Oct 22, 2024
b775830
restrict env to 1000chars
BBesrour Oct 22, 2024
41f602b
restrict env to 1000chars
BBesrour Oct 22, 2024
16074b5
restrict env to 1000chars
BBesrour Oct 22, 2024
eb928d4
coderabbit feedback
BBesrour Oct 22, 2024
202c1f8
REVERT
BBesrour Oct 26, 2024
e822566
Merge branch 'develop' into feature/integrated-code-lifecycle/custom-…
BBesrour Oct 26, 2024
f99006b
REVERTED
BBesrour Oct 26, 2024
112842b
add warning
BBesrour Oct 26, 2024
7fd2909
add warning
BBesrour Oct 26, 2024
d006708
revert merge changes
BBesrour Oct 26, 2024
9efe970
Merge branch 'develop' into feature/integrated-code-lifecycle/custom-…
BBesrour Oct 26, 2024
9480726
feedback
BBesrour Oct 28, 2024
29b4dba
Merge branch 'develop' into feature/integrated-code-lifecycle/custom-…
BBesrour Oct 28, 2024
867c1a5
fix vulnerability
BBesrour Oct 30, 2024
a28c26c
fix vulnerability
BBesrour Oct 30, 2024
92069c2
fix vulnerability
BBesrour Oct 30, 2024
c0005d3
Merge branch 'develop' into feature/integrated-code-lifecycle/custom-…
BBesrour Nov 3, 2024
089b70d
feedback
BBesrour Nov 3, 2024
5a2ee74
feedback
BBesrour Nov 3, 2024
5d8a008
fix tests
BBesrour Nov 3, 2024
fb80482
fix tests
BBesrour Nov 4, 2024
999e5bd
fix bug
BBesrour Nov 4, 2024
3d8e2c0
fix docs
BBesrour Nov 4, 2024
e716b69
feedback
BBesrour Nov 6, 2024
3f5d53e
feedback
BBesrour Nov 6, 2024
497ff9b
feedback
BBesrour Nov 7, 2024
4a8d24f
feedback
BBesrour Nov 10, 2024
c3a3582
feedback
BBesrour Nov 11, 2024
5c253f9
fix tests
BBesrour Nov 11, 2024
2a8709f
Merge branch 'develop' into feature/integrated-code-lifecycle/custom-…
BBesrour Nov 11, 2024
4902f61
fix tests
BBesrour Nov 11, 2024
4239f33
Merge branch 'develop' into feature/integrated-code-lifecycle/custom-…
BBesrour Nov 11, 2024
0271f70
remove unnecessary code
BBesrour Nov 11, 2024
c725fd2
update docs
BBesrour Nov 11, 2024
d3a1651
fix translations
BBesrour Nov 11, 2024
bac63a7
fix tests
BBesrour Nov 12, 2024
26f3b38
fix 500 error
BBesrour Nov 12, 2024
a4c2e12
fix tests
BBesrour Nov 12, 2024
5b3802e
fail build if disconnection fails
BBesrour Nov 12, 2024
042437d
remove unused code
BBesrour Nov 12, 2024
a3232c1
feedback
BBesrour Nov 13, 2024
0f504ed
Merge branch 'develop' into feature/integrated-code-lifecycle/custom-…
BBesrour Nov 26, 2024
42d41dd
Merge branch 'develop' into feature/integrated-code-lifecycle/custom-…
krusche Nov 29, 2024
fc41a79
Merge branch 'develop' into feature/integrated-code-lifecycle/custom-…
krusche Nov 29, 2024
37e58a6
Merge branch 'develop' into feature/integrated-code-lifecycle/custom-…
BBesrour Nov 30, 2024
7276a06
merge conflicts
BBesrour Nov 30, 2024
08db5a4
Merge branch 'develop' into feature/integrated-code-lifecycle/custom-…
BBesrour Dec 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public record BuildConfig(String buildScript, String dockerImage, String commitHashToBuild, String assignmentCommitHash, String testCommitHash, String branch,
ProgrammingLanguage programmingLanguage, ProjectType projectType, boolean scaEnabled, boolean sequentialTestRunsEnabled, boolean testwiseCoverageEnabled,
List<String> resultPaths, int timeoutSeconds, String assignmentCheckoutPath, String testCheckoutPath, String solutionCheckoutPath) implements Serializable {
List<String> resultPaths, int timeoutSeconds, String assignmentCheckoutPath, String testCheckoutPath, String solutionCheckoutPath, DockerRunConfig dockerRunConfig)
implements Serializable {
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
BBesrour marked this conversation as resolved.
Show resolved Hide resolved

@Override
public String dockerImage() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package de.tum.cit.aet.artemis.buildagent.dto;

import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class DockerRunConfig implements Serializable {
BBesrour marked this conversation as resolved.
Show resolved Hide resolved

private boolean isNetworkDisabled;

private List<String> env;

public boolean isNetworkDisabled() {
return isNetworkDisabled;
}

public void setNetworkDisabled(boolean networkDisabled) {
isNetworkDisabled = networkDisabled;
}

public List<String> getEnv() {
return env;
}

public void setEnv(List<String> env) {
this.env = env;
}
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
BBesrour marked this conversation as resolved.
Show resolved Hide resolved

public enum AllowedDockerFlags {

NETWORK("network"), ENV("env");

private final String flag;

AllowedDockerFlags(String flag) {
this.flag = flag;
}

public String flag() {
return flag;
}

private static final Set<String> ALLOWED_FLAGS = new HashSet<>();

static {
for (AllowedDockerFlags value : values()) {
ALLOWED_FLAGS.add(value.flag());
}
}

BBesrour marked this conversation as resolved.
Show resolved Hide resolved
public static boolean isAllowed(String flag) {
return ALLOWED_FLAGS.contains(flag);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.async.ResultCallback;
import com.github.dockerjava.api.command.CreateContainerCmd;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.command.ExecCreateCmd;
import com.github.dockerjava.api.command.ExecCreateCmdResponse;
Expand All @@ -45,7 +46,9 @@

import de.tum.cit.aet.artemis.core.exception.LocalCIException;
import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage;
import de.tum.cit.aet.artemis.programming.domain.ProjectType;
import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry;
import de.tum.cit.aet.artemis.programming.service.ci.ContinuousIntegrationService.DependencyDownloadScript;
import de.tum.cit.aet.artemis.programming.service.ci.ContinuousIntegrationService.RepositoryCheckoutPath;

/**
Expand Down Expand Up @@ -85,28 +88,35 @@ public BuildJobContainerService(DockerClient dockerClient, HostConfig hostConfig
/**
* Configure a container with the Docker image, the container name, optional proxy config variables, and set the command that runs when the container starts.
*
* @param containerName the name of the container to be created
* @param image the Docker image to use for the container
* @param buildScript the build script to be executed in the container
* @param containerName the name of the container to be created
* @param image the Docker image to use for the container
* @param buildScript the build script to be executed in the container
* @param exerciseEnvVars the environment variables provided by the instructor
* @return {@link CreateContainerResponse} that can be used to start the container
*/
public CreateContainerResponse configureContainer(String containerName, String image, String buildScript) {
public CreateContainerResponse configureContainer(String containerName, String image, String buildScript, List<String> exerciseEnvVars) {
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
List<String> envVars = new ArrayList<>();
if (useSystemProxy) {
envVars.add("HTTP_PROXY=" + httpProxy);
envVars.add("HTTPS_PROXY=" + httpsProxy);
envVars.add("NO_PROXY=" + noProxy);
}
envVars.add("SCRIPT=" + buildScript);
return dockerClient.createContainerCmd(image).withName(containerName).withHostConfig(hostConfig).withEnv(envVars)
CreateContainerCmd createContainerCmd = dockerClient.createContainerCmd(image).withName(containerName).withHostConfig(hostConfig).withEnv(envVars)
// Command to run when the container starts. This is the command that will be executed in the container's main process, which runs in the foreground and blocks the
// container from exiting until it finishes.
// It waits until the script that is running the tests (see below execCreateCmdResponse) is completed, and until the result files are extracted which is indicated
// by the creation of a file "stop_container.txt" in the container's root directory.
.withCmd("sh", "-c", "while [ ! -f " + LOCALCI_WORKING_DIRECTORY + "/stop_container.txt ]; do sleep 0.5; done")
// .withCmd("tail", "-f", "/dev/null") // Activate for debugging purposes instead of the above command to get a running container that you can peek into using
// "docker exec -it <container-id> /bin/bash".
.exec();
.withCmd("sh", "-c", "while [ ! -f " + LOCALCI_WORKING_DIRECTORY + "/stop_container.txt ]; do sleep 0.5; done");
// .withCmd("tail", "-f", "/dev/null"); // Activate for debugging purposes instead of the above command to get a running container that you can peek into using
// "docker exec -it <container-id> /bin/bash".

if (exerciseEnvVars != null && !exerciseEnvVars.isEmpty()) {
envVars.addAll(exerciseEnvVars);
createContainerCmd.withEnv(envVars);
}
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
BBesrour marked this conversation as resolved.
Show resolved Hide resolved

return createContainerCmd.exec();
}

/**
Expand All @@ -125,7 +135,14 @@ public void startContainer(String containerId) {
* @param buildJobId the id of the build job that is currently being executed
*/

public void runScriptInContainer(String containerId, String buildJobId) {
public void runScriptInContainer(String containerId, String buildJobId, boolean isNetworkDisabled) {
if (isNetworkDisabled) {
// The "sh preScript.sh" execution command specified here is need to install dependencies and prepare the environment for the build script.
log.info("Started running the pre-script for build job in container with id {}", containerId);
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
executeDockerCommand(containerId, buildJobId, true, true, false, "bash", LOCALCI_WORKING_DIRECTORY + "/preScript.sh");
dockerClient.disconnectFromNetworkCmd().withContainerId(containerId).withNetworkId("bridge").exec();
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
}

BBesrour marked this conversation as resolved.
Show resolved Hide resolved
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
log.info("Started running the build script for build job in container with id {}", containerId);
// The "sh script.sh" execution command specified here is run inside the container as an additional process. This command runs in the background, independent of the
// container's
Expand Down Expand Up @@ -285,7 +302,7 @@ public String getIDOfRunningContainer(String containerName) {
*/
public void populateBuildJobContainer(String buildJobContainerId, Path assignmentRepositoryPath, Path testRepositoryPath, Path solutionRepositoryPath,
Path[] auxiliaryRepositoriesPaths, String[] auxiliaryRepositoryCheckoutDirectories, ProgrammingLanguage programmingLanguage, String assignmentCheckoutPath,
String testCheckoutPath, String solutionCheckoutPath) {
String testCheckoutPath, String solutionCheckoutPath, ProjectType projectType, boolean isNetworkDisabled) {

assignmentCheckoutPath = (!StringUtils.isBlank(assignmentCheckoutPath)) ? assignmentCheckoutPath
: RepositoryCheckoutPath.ASSIGNMENT.forProgrammingLanguage(programmingLanguage);
Expand All @@ -312,10 +329,17 @@ public void populateBuildJobContainer(String buildJobContainerId, Path assignmen
addAndPrepareDirectory(buildJobContainerId, auxiliaryRepositoriesPaths[i], LOCALCI_WORKING_DIRECTORY + "/testing-dir/" + auxiliaryRepositoryCheckoutDirectories[i]);
}

createScriptFile(buildJobContainerId);
createScriptFile(buildJobContainerId, isNetworkDisabled, programmingLanguage, projectType);
}

private void createScriptFile(String buildJobContainerId) {
private void createScriptFile(String buildJobContainerId, boolean isNetworkDisabled, ProgrammingLanguage programmingLanguage, ProjectType projectType) {
if (isNetworkDisabled) {
String preScript = getDependencyDownloadScript(programmingLanguage, projectType);

executeDockerCommand(buildJobContainerId, null, false, false, true, "bash", "-c", "echo '" + preScript + "' > " + LOCALCI_WORKING_DIRECTORY + "/preScript.sh");
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
executeDockerCommand(buildJobContainerId, null, false, false, true, "bash", "-c", "chmod +x " + LOCALCI_WORKING_DIRECTORY + "/preScript.sh");
}

BBesrour marked this conversation as resolved.
Show resolved Hide resolved
executeDockerCommand(buildJobContainerId, null, false, false, true, "bash", "-c", "echo \"$SCRIPT\" > " + LOCALCI_WORKING_DIRECTORY + "/script.sh");
executeDockerCommand(buildJobContainerId, null, false, false, true, "bash", "-c", "chmod +x " + LOCALCI_WORKING_DIRECTORY + "/script.sh");
}
Expand Down Expand Up @@ -447,4 +471,17 @@ private String getParentFolderPath(String path) {
Path parentPath = Paths.get(path).normalize().getParent();
return parentPath != null ? parentPath.toString() : "";
}

private String getDependencyDownloadScript(ProgrammingLanguage programmingLanguage, ProjectType projectType) {
return switch (programmingLanguage) {
case JAVA -> switch (projectType) {
case PLAIN_MAVEN, MAVEN_BLACKBOX, MAVEN_MAVEN -> DependencyDownloadScript.MAVEN.getScript();
case PLAIN_GRADLE, GRADLE_GRADLE -> DependencyDownloadScript.GRADLE.getScript();
default -> DependencyDownloadScript.OTHER.getScript();
};
case RUST -> DependencyDownloadScript.RUST.getScript();
case JAVASCRIPT -> DependencyDownloadScript.JAVASCRIPT.getScript();
default -> DependencyDownloadScript.OTHER.getScript();
};
}
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,18 @@ public BuildResult runBuildJob(BuildJobQueueItem buildJob, String containerName)
index++;
}

CreateContainerResponse container = buildJobContainerService.configureContainer(containerName, buildJob.buildConfig().dockerImage(), buildJob.buildConfig().buildScript());
List<String> envVars = new ArrayList<>();
boolean isNetworkDisabled = false;
if (buildJob.buildConfig().dockerRunConfig() != null) {
envVars = buildJob.buildConfig().dockerRunConfig().getEnv();
isNetworkDisabled = buildJob.buildConfig().dockerRunConfig().isNetworkDisabled();
}
BBesrour marked this conversation as resolved.
Show resolved Hide resolved

BBesrour marked this conversation as resolved.
Show resolved Hide resolved
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
CreateContainerResponse container = buildJobContainerService.configureContainer(containerName, buildJob.buildConfig().dockerImage(), buildJob.buildConfig().buildScript(),
envVars);
BBesrour marked this conversation as resolved.
Show resolved Hide resolved

return runScriptAndParseResults(buildJob, containerName, container.getId(), assignmentRepoUri, testsRepoUri, solutionRepoUri, auxiliaryRepositoriesUris,
assignmentRepositoryPath, testsRepositoryPath, solutionRepositoryPath, auxiliaryRepositoriesPaths, assignmentCommitHash, testCommitHash);
assignmentRepositoryPath, testsRepositoryPath, solutionRepositoryPath, auxiliaryRepositoriesPaths, assignmentCommitHash, testCommitHash, isNetworkDisabled);
}

/**
Expand Down Expand Up @@ -230,7 +238,7 @@ public BuildResult runBuildJob(BuildJobQueueItem buildJob, String containerName)
private BuildResult runScriptAndParseResults(BuildJobQueueItem buildJob, String containerName, String containerId, VcsRepositoryUri assignmentRepositoryUri,
VcsRepositoryUri testRepositoryUri, VcsRepositoryUri solutionRepositoryUri, VcsRepositoryUri[] auxiliaryRepositoriesUris, Path assignmentRepositoryPath,
Path testsRepositoryPath, Path solutionRepositoryPath, Path[] auxiliaryRepositoriesPaths, @Nullable String assignmentRepoCommitHash,
@Nullable String testRepoCommitHash) {
@Nullable String testRepoCommitHash, boolean isNetworkDisabled) {
BBesrour marked this conversation as resolved.
Show resolved Hide resolved

long timeNanoStart = System.nanoTime();

Expand All @@ -246,13 +254,13 @@ private BuildResult runScriptAndParseResults(BuildJobQueueItem buildJob, String
log.debug(msg);
buildJobContainerService.populateBuildJobContainer(containerId, assignmentRepositoryPath, testsRepositoryPath, solutionRepositoryPath, auxiliaryRepositoriesPaths,
buildJob.repositoryInfo().auxiliaryRepositoryCheckoutDirectories(), buildJob.buildConfig().programmingLanguage(), buildJob.buildConfig().assignmentCheckoutPath(),
buildJob.buildConfig().testCheckoutPath(), buildJob.buildConfig().solutionCheckoutPath());
buildJob.buildConfig().testCheckoutPath(), buildJob.buildConfig().solutionCheckoutPath(), buildJob.buildConfig().projectType(), isNetworkDisabled);

msg = "~~~~~~~~~~~~~~~~~~~~ Executing Build Script for Build job " + buildJob.id() + " ~~~~~~~~~~~~~~~~~~~~";
buildLogsMap.appendBuildLogEntry(buildJob.id(), msg);
log.debug(msg);

buildJobContainerService.runScriptInContainer(containerId, buildJob.id());
buildJobContainerService.runScriptInContainer(containerId, buildJob.id(), isNetworkDisabled);

msg = "~~~~~~~~~~~~~~~~~~~~ Finished Executing Build Script for Build job " + buildJob.id() + " ~~~~~~~~~~~~~~~~~~~~";
buildLogsMap.appendBuildLogEntry(buildJob.id(), msg);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,17 @@ public void validateSettingsForFeedbackRequest() {
}
}

/**
* Validates the network access feature for the given programming language.
* Currently, SWIFT and HASKELL do not support disabling the network access feature.
*
*/
public void validateBuildPlanNetworkAccessForProgrammingLanguage() {
if (List.of(ProgrammingLanguage.SWIFT, ProgrammingLanguage.HASKELL).contains(getProgrammingLanguage())) {
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
throw new BadRequestAlertException("This programming language does not support disabling the network access feature", "Exercise", "networkAccessNotSupported");
}
}
BBesrour marked this conversation as resolved.
Show resolved Hide resolved

BBesrour marked this conversation as resolved.
Show resolved Hide resolved
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
public Set<ExerciseHint> getExerciseHints() {
return exerciseHints;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package de.tum.cit.aet.artemis.programming.domain;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import jakarta.annotation.Nullable;
import jakarta.persistence.Column;
Expand All @@ -10,14 +14,18 @@
import jakarta.persistence.Table;
import jakarta.validation.constraints.Size;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import de.tum.cit.aet.artemis.buildagent.dto.DockerRunConfig;
import de.tum.cit.aet.artemis.core.domain.DomainObject;
import de.tum.cit.aet.artemis.programming.service.aeolus.Windfile;
import de.tum.cit.aet.artemis.programming.service.vcs.AbstractVersionControlService;
Expand Down Expand Up @@ -292,6 +300,81 @@ public void setSolutionCheckoutPath(String solutionCheckoutPath) {
this.solutionCheckoutPath = solutionCheckoutPath;
}

/**
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
* Converts a JSON string representing Docker flags (in the form of a list of key-value pairs)
* into a {@link DockerRunConfig} instance.
*
* <p>
* The JSON string is expected to represent a list of key-value pairs where each
* entry is a list containing two strings: the first being the key and the second being the value.
* Example JSON input:
*
* <pre>
* [["network", "none"], ["env", "TEST"]]
* </pre>
*
* @return a {@link DockerRunConfig} object initialized with the parsed flags, or {@code null} if an error occurs
*/
public DockerRunConfig getDockerRunConfigFromString() {
if (StringUtils.isBlank(dockerFlags)) {
return null;
}
BBesrour marked this conversation as resolved.
Show resolved Hide resolved

try {
ObjectMapper objectMapper = new ObjectMapper();

BBesrour marked this conversation as resolved.
Show resolved Hide resolved
List<List<String>> list = objectMapper.readValue(dockerFlags, new TypeReference<>() {
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
});

DockerRunConfig dockerRunConfig = new DockerRunConfig();
for (List<String> entry : list) {
if (entry.size() != 2 || entry.get(0) == null || entry.get(1) == null || entry.get(0).isBlank() || entry.get(1).isBlank()
|| !DockerRunConfig.AllowedDockerFlags.isAllowed(entry.get(0))) {
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
log.error("Invalid Docker flag entry: {}. Skipping.", entry);
continue;
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
}
switch (entry.get(0)) {
case "network":
dockerRunConfig.setNetworkDisabled(entry.get(1).equals("none"));
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
break;
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
case "env":
dockerRunConfig.setEnv(parseEnvVariableString(entry.get(1)));
break;
default:
log.error("Invalid Docker flag entry: {}. Skipping.", entry);
break;
}
BBesrour marked this conversation as resolved.
Show resolved Hide resolved

}
return dockerRunConfig;
}
catch (Exception e) {
log.error("Failed to parse DockerRunConfig from JSON string: {}. Using default settings.", dockerFlags, e);
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
}
BBesrour marked this conversation as resolved.
Show resolved Hide resolved

return null;
}
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
BBesrour marked this conversation as resolved.
Show resolved Hide resolved

private List<String> parseEnvVariableString(String envVariableString) {
Pattern pattern = Pattern.compile(
// match key-value pairs, where the key can be a single word or a string in single or double quotes
// key-value pairs are separated by commas
"(?:'([^']+)'|\"([^\"]+)\"|(\\w+))=(?:'([^']*)'|\"([^\"]*)\"|([^,]+))");
BBesrour marked this conversation as resolved.
Show resolved Hide resolved

Matcher matcher = pattern.matcher(envVariableString);
Fixed Show fixed Hide fixed

List<String> envVars = new ArrayList<>();
while (matcher.find()) {
String key = matcher.group(1) != null ? matcher.group(1) : matcher.group(2) != null ? matcher.group(2) : matcher.group(3);

String value = matcher.group(4) != null ? matcher.group(4) : matcher.group(5) != null ? matcher.group(5) : matcher.group(6);

BBesrour marked this conversation as resolved.
Show resolved Hide resolved
envVars.add(key + "=" + value);
}

return envVars;
}
BBesrour marked this conversation as resolved.
Show resolved Hide resolved

@Override
public String toString() {
return "BuildJobConfig{" + "id=" + getId() + ", sequentialTestRuns=" + sequentialTestRuns + ", branch='" + branch + '\'' + ", buildPlanConfiguration='"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ public void validateNewProgrammingExerciseSettings(ProgrammingExercise programmi
programmingExercise.validateProgrammingSettings();
programmingExercise.validateSettingsForFeedbackRequest();
validateCustomCheckoutPaths(programmingExercise);
programmingExercise.validateBuildPlanNetworkAccessForProgrammingLanguage();
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
BBesrour marked this conversation as resolved.
Show resolved Hide resolved
auxiliaryRepositoryService.validateAndAddAuxiliaryRepositoriesOfProgrammingExercise(programmingExercise, programmingExercise.getAuxiliaryRepositories());
submissionPolicyService.validateSubmissionPolicyCreation(programmingExercise);

Expand Down
Loading
Loading