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

Programming exercises: Add MATLAB programming exercise template #10039

Open
wants to merge 15 commits into
base: develop
Choose a base branch
from
Open
Changes from 1 commit
Commits
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
Prev Previous commit
Next Next commit
Move license handling to LicenseService
magaupp committed Dec 30, 2024
commit d73ae4909d9770b3765054bb2ddf7a0e224958ea
3 changes: 2 additions & 1 deletion src/main/java/de/tum/cit/aet/artemis/ArtemisApp.java
Original file line number Diff line number Diff line change
@@ -18,13 +18,14 @@
import org.springframework.boot.info.GitProperties;
import org.springframework.core.env.Environment;

import de.tum.cit.aet.artemis.core.config.LicenseConfiguration;
import de.tum.cit.aet.artemis.core.config.ProgrammingLanguageConfiguration;
import de.tum.cit.aet.artemis.core.config.TheiaConfiguration;
import tech.jhipster.config.DefaultProfileUtil;
import tech.jhipster.config.JHipsterConstants;

@SpringBootApplication
@EnableConfigurationProperties({ LiquibaseProperties.class, ProgrammingLanguageConfiguration.class, TheiaConfiguration.class })
@EnableConfigurationProperties({ LiquibaseProperties.class, ProgrammingLanguageConfiguration.class, TheiaConfiguration.class, LicenseConfiguration.class })
public class ArtemisApp {

private static final Logger log = LoggerFactory.getLogger(ArtemisApp.class);
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package de.tum.cit.aet.artemis.core.config;

import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE;

import jakarta.annotation.Nullable;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Profile;

@Profile(PROFILE_CORE)
@ConfigurationProperties(prefix = "artemis.licenses")
public class LicenseConfiguration {

private final MatLabLicense matlab;

public record MatLabLicense(String licenseServer) {
}

public LicenseConfiguration(MatLabLicense matlab) {
this.matlab = matlab;
}

@Nullable
public String getMatlabLicenseServer() {
if (matlab == null) {
return null;
}
return matlab.licenseServer();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package de.tum.cit.aet.artemis.programming.service;

import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE;

import java.util.Map;
import java.util.Objects;

import jakarta.annotation.Nullable;

import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

import de.tum.cit.aet.artemis.core.config.LicenseConfiguration;
import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage;
import de.tum.cit.aet.artemis.programming.domain.ProjectType;

@Profile(PROFILE_CORE)
@Service
public class LicenseService {

private final LicenseConfiguration licenseConfiguration;

public LicenseService(LicenseConfiguration licenseConfiguration) {
this.licenseConfiguration = licenseConfiguration;
}

public boolean isLicensed(ProgrammingLanguage programmingLanguage, @Nullable ProjectType projectType) {
if (programmingLanguage == ProgrammingLanguage.MATLAB && projectType == null) {
return licenseConfiguration.getMatlabLicenseServer() != null;
}

return true;
}

public Map<String, String> getEnvironment(ProgrammingLanguage programmingLanguage, @Nullable ProjectType projectType) {
if (programmingLanguage == ProgrammingLanguage.MATLAB && projectType == null) {
return Map.of("MLM_LICENSE_FILE", Objects.requireNonNull(licenseConfiguration.getMatlabLicenseServer()));
}

return Map.of();
}
}
Original file line number Diff line number Diff line change
@@ -11,7 +11,6 @@

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

@@ -21,6 +20,7 @@
import de.tum.cit.aet.artemis.buildagent.dto.DockerRunConfig;
import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseBuildConfig;
import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage;
import de.tum.cit.aet.artemis.programming.domain.ProjectType;

@Profile(PROFILE_CORE)
@Service
@@ -30,8 +30,11 @@ public class ProgrammingExerciseBuildConfigService {

private final ObjectMapper objectMapper = new ObjectMapper();

@Value("${artemis.languages.matlab.license-server}")
private String matlabLicense;
private final LicenseService licenseService;

public ProgrammingExerciseBuildConfigService(LicenseService licenseService) {
this.licenseService = licenseService;
}

/**
* Converts a JSON string representing Docker flags (in JSON format)
@@ -52,49 +55,55 @@ public class ProgrammingExerciseBuildConfigService {
public DockerRunConfig getDockerRunConfig(ProgrammingExerciseBuildConfig buildConfig) {
DockerFlagsDTO dockerFlagsDTO = parseDockerFlags(buildConfig);

dockerFlagsDTO = addLanguageSpecificEnvironment(dockerFlagsDTO, buildConfig.getProgrammingExercise().getProgrammingLanguage());
String network;
Map<String, String> exerciseEnvironment;
if (dockerFlagsDTO != null) {
network = dockerFlagsDTO.network();
exerciseEnvironment = dockerFlagsDTO.env();
}
else {
network = null;
exerciseEnvironment = null;
}

ProgrammingLanguage programmingLanguage = buildConfig.getProgrammingExercise().getProgrammingLanguage();
ProjectType projectType = buildConfig.getProgrammingExercise().getProjectType();
Map<String, String> environment = addLanguageSpecificEnvironment(exerciseEnvironment, programmingLanguage, projectType);

return getDockerRunConfigFromParsedFlags(dockerFlagsDTO);
return createDockerRunConfig(network, environment);
}

@Nullable
private DockerFlagsDTO addLanguageSpecificEnvironment(DockerFlagsDTO dockerFlagsDTO, ProgrammingLanguage language) {
if (language == ProgrammingLanguage.MATLAB && matlabLicense != null) {
Map<String, String> env;
String network;
if (dockerFlagsDTO != null) {
env = new HashMap<>(dockerFlagsDTO.env());
network = dockerFlagsDTO.network();
}
else {
env = new HashMap<>();
network = null;
}

env.put("MLM_LICENSE_FILE", matlabLicense);
private Map<String, String> addLanguageSpecificEnvironment(@Nullable Map<String, String> exerciseEnvironment, ProgrammingLanguage language, ProjectType projectType) {
Map<String, String> licenseEnvironment = licenseService.getEnvironment(language, projectType);
if (licenseEnvironment.isEmpty()) {
return exerciseEnvironment;
}

dockerFlagsDTO = new DockerFlagsDTO(network, env);
Map<String, String> env = new HashMap<>(licenseEnvironment);
if (exerciseEnvironment != null) {
env.putAll(exerciseEnvironment);
}

return dockerFlagsDTO;
return env;
}

DockerRunConfig getDockerRunConfigFromParsedFlags(DockerFlagsDTO dockerFlagsDTO) {
if (dockerFlagsDTO == null) {
DockerRunConfig createDockerRunConfig(String network, Map<String, String> environmentMap) {
if (network == null && environmentMap == null) {
return null;
}
List<String> env = new ArrayList<>();
boolean isNetworkDisabled = dockerFlagsDTO.network() != null && dockerFlagsDTO.network().equals("none");
List<String> environmentStrings = new ArrayList<>();
boolean isNetworkDisabled = network != null && network.equals("none");

if (dockerFlagsDTO.env() != null) {
for (Map.Entry<String, String> entry : dockerFlagsDTO.env().entrySet()) {
if (environmentMap != null) {
for (Map.Entry<String, String> entry : environmentMap.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
env.add(key + "=" + value);
environmentStrings.add(key + "=" + value);
}
}

return new DockerRunConfig(isNetworkDisabled, env);
return new DockerRunConfig(isNetworkDisabled, environmentStrings);
}

/**
Original file line number Diff line number Diff line change
@@ -1119,7 +1119,7 @@ public void validateDockerFlags(ProgrammingExercise programmingExercise) {
}
}

DockerRunConfig dockerRunConfig = programmingExerciseBuildConfigService.getDockerRunConfigFromParsedFlags(dockerFlagsDTO);
DockerRunConfig dockerRunConfig = programmingExerciseBuildConfigService.createDockerRunConfig(dockerFlagsDTO.network(), dockerFlagsDTO.env());
magaupp marked this conversation as resolved.
Show resolved Hide resolved

if (List.of(ProgrammingLanguage.SWIFT, ProgrammingLanguage.HASKELL).contains(programmingExercise.getProgrammingLanguage()) && dockerRunConfig.isNetworkDisabled()) {
throw new BadRequestAlertException("This programming language does not support disabling the network access feature", "Exercise", "networkAccessNotSupported");
Original file line number Diff line number Diff line change
@@ -32,7 +32,8 @@ public TemplateUpgradePolicyService(JavaTemplateUpgradeService javaRepositoryUpg
public TemplateUpgradeService getUpgradeService(ProgrammingLanguage programmingLanguage) {
return switch (programmingLanguage) {
case JAVA -> javaRepositoryUpgradeService;
case KOTLIN, PYTHON, C, HASKELL, VHDL, ASSEMBLER, SWIFT, OCAML, EMPTY, RUST, JAVASCRIPT, R, C_PLUS_PLUS, TYPESCRIPT, C_SHARP, GO, MATLAB -> defaultRepositoryUpgradeService;
case KOTLIN, PYTHON, C, HASKELL, VHDL, ASSEMBLER, SWIFT, OCAML, EMPTY, RUST, JAVASCRIPT, R, C_PLUS_PLUS, TYPESCRIPT, C_SHARP, GO, MATLAB ->
defaultRepositoryUpgradeService;
case SQL, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> throw new UnsupportedOperationException("Unsupported programming language: " + programmingLanguage);
};
}