Compare commits
17 Commits
master
...
single_fil
Author | SHA1 | Date |
---|---|---|
郑明仁 | 706a341b0b | |
郑明仁 | 0d29d7a136 | |
郑明仁 | 9b08265ada | |
郑明仁 | ef97c856d1 | |
郑明仁 | a903f54b44 | |
郑明仁 | 2ac89342bc | |
郑明仁 | 1245e1ce28 | |
郑明仁 | 02d32b147c | |
郑明仁 | 44ee8144a4 | |
郑明仁 | 385c245977 | |
郑明仁 | c3ff401bb4 | |
郑明仁 | 05801ad68b | |
郑明仁 | 1975fd04c6 | |
郑明仁 | 0f5b3404be | |
郑明仁 | 420b5023da | |
郑明仁 | 0a16a8c0ae | |
郑明仁 | 25cdc66972 |
|
@ -84,6 +84,9 @@ public class AppConfig {
|
|||
@Value("${k8s.serverUrl}")
|
||||
private String k8sServerUrl;
|
||||
|
||||
@Value("${singleEvaCode}")
|
||||
private String singleEvaCode;
|
||||
|
||||
private String bridgeInstanceName = IpUtils.getLocalIp();
|
||||
|
||||
public String getServerUrl() {
|
||||
|
@ -222,4 +225,7 @@ public class AppConfig {
|
|||
return bridgeInstanceName;
|
||||
}
|
||||
|
||||
public String getSingleEvaCode() {
|
||||
return singleEvaCode;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,10 @@ public abstract class TpUtils {
|
|||
return tpiWorkspace + File.separator + tpiRepoName;
|
||||
}
|
||||
|
||||
public static String buildCodeFileNamePath(String codeFileName) {
|
||||
return "src" + File.separator + codeFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转化评测pod类型
|
||||
*
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.educoder.bridge.game.controller;
|
|||
import java.io.File;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import com.educoder.bridge.game.thread.SingleEvaThread;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -212,6 +213,113 @@ public class GameController {
|
|||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个文件评测
|
||||
*/
|
||||
@RequestMapping(path = "/singleEvaluate")
|
||||
@ApiOperation(value = "单个文件评测", httpMethod = "POST", produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
public JSONObject singleEvaluate(
|
||||
@ApiParam(name = "tpiID", required = true, value = "实训实例的ID") @RequestParam String tpiID,
|
||||
@ApiParam(name = "buildID", required = true, value = "本次评测ID") @RequestParam String buildID,
|
||||
@ApiParam(name = "isPublished", required = false, value = "实训是否已经发布,1代表调试模式,实训未发布") @RequestParam Integer isPublished,
|
||||
@ApiParam(name = "instanceChallenge", required = true, value = "当前处在第几关") @RequestParam String instanceChallenge,
|
||||
@ApiParam(name = "testCases", required = true, value = "测试用例") @RequestParam String testCases,
|
||||
@ApiParam(name = "codeFileName", required = true, value = "代码文件名") @RequestParam String codeFileName,
|
||||
@ApiParam(name = "evaScriptType", required = true, value = "代码评测脚本类型") @RequestParam String evaScriptType,
|
||||
@ApiParam(name = "codeFile", required = true, value = "代码文件") @RequestParam String codeFile,
|
||||
@ApiParam(name = "timeLimit", required = false, value = "时间限制") Integer timeLimit,
|
||||
@ApiParam(name = "resubmit", required = true, value = "是否是重复评测") @RequestParam String resubmit,
|
||||
@ApiParam(name = "needPortMapping", required = false, value = "容器中需要被映射的端口") Integer needPortMapping,
|
||||
@ApiParam(name = "podType", required = true, value = "pod类型(0.evaluate,1.webssh,2.evassh)") @RequestParam Integer podType,
|
||||
@ApiParam(name = "file", required = false, value = "需要传文件的实训,给出文件存放路径(一个目录)及文件类型") String file,
|
||||
@ApiParam(name = "containers", required = true, value = "需要使用的容器,base64编码") @RequestParam String containers,
|
||||
@ApiParam(name = "sec_key", required = false, value = "每一次评测的唯一标识") String sec_key) throws Exception {
|
||||
logger.info("[start]单个文件评测:tpiID: {}, buildID: {}, isPublished: {}, instanceChallenge: {}, "
|
||||
+ "testCases: {}, codeFile: {}, timeLimit: {}, resubmit: {}, "
|
||||
+ "needPortMapping: {}, podType: {}, file: {}, containers: {}, sec_key: {}",
|
||||
tpiID, buildID, isPublished, instanceChallenge, testCases, codeFile, timeLimit, resubmit,
|
||||
needPortMapping, podType, file, containers, sec_key);
|
||||
|
||||
// 记录开始时间
|
||||
LocalDateTime requestTime = LocalDateTime.now();
|
||||
String evaluateStartTime = requestTime.toString();
|
||||
JSONObject cost = new JSONObject();
|
||||
cost.put("evaluateStartTime", evaluateStartTime);
|
||||
JedisUtil.set("timeCost:" + tpiID + ":" + buildID, cost.toJSONString());
|
||||
|
||||
JSONObject response = new JSONObject();
|
||||
|
||||
// 参数处理
|
||||
needPortMapping = needPortMapping == null ? 0 : needPortMapping;
|
||||
timeLimit = timeLimit == null ? Integer.parseInt(appConfig.getDefaultTimeLimit()) : timeLimit;
|
||||
testCases = Base64Util.decode(testCases);
|
||||
containers = Base64Util.decode(containers);
|
||||
|
||||
// 每次评测均生成新评测脚本
|
||||
String tpiWorkspace = TpUtils.buildTpiWorkspace(appConfig.getWorkspace(), tpiID);
|
||||
gameService.generateEvaluateShellScript(tpiWorkspace, evaScriptType);
|
||||
codeFile = Base64Util.decode(codeFile);
|
||||
codeFileName = TpUtils.buildCodeFileNamePath(codeFileName);
|
||||
gameService.generateEvaluateCodeFile(tpiWorkspace, codeFileName, codeFile);
|
||||
|
||||
String tpiRepoPath = TpUtils.buildTpiRepoPath(tpiWorkspace, appConfig.getSingleEvaCode());
|
||||
|
||||
JSONObject buildParams = new JSONObject(true);
|
||||
buildParams.put("tpiID", tpiID);
|
||||
buildParams.put("buildID", buildID);
|
||||
buildParams.put("isPublished", isPublished);
|
||||
buildParams.put("instanceChallenge", instanceChallenge);
|
||||
buildParams.put("testCases", testCases);
|
||||
buildParams.put("timeLimit", timeLimit);
|
||||
buildParams.put("resubmit", resubmit);
|
||||
buildParams.put("needPortMapping", needPortMapping);
|
||||
buildParams.put("podType", podType);
|
||||
buildParams.put("containers", containers);
|
||||
buildParams.put("codeFileName", codeFileName);
|
||||
buildParams.put("sec_key", sec_key);
|
||||
buildParams.put("tpiRepoPath", tpiRepoPath);
|
||||
|
||||
// 若实训生成文件 todo:这个处理方式好2b,扩展性极差
|
||||
if (!StringUtils.isEmpty(file)) {
|
||||
file = Base64Util.decode(file);
|
||||
buildParams.put("file", file);
|
||||
// 清空目标文件夹以防止影响此次评测结果
|
||||
GameHelper.clearFiles(tpiRepoPath + File.separator
|
||||
+ JSONObject.parseObject(file).getString("path"));
|
||||
}
|
||||
|
||||
// podName
|
||||
String podName = podType != 2 ? "evaluate-" + tpiID : "evassh-" + tpiID;
|
||||
|
||||
BridgePod bridgePod = bridgePodService.createBridgePod(podName, tpiID, requestTime, sec_key);
|
||||
buildParams.put("bridgePodId", bridgePod.getId());
|
||||
|
||||
// 若需要端口映射服务,则分配端口,将podName-port键值对存储于redis
|
||||
String port = "-1";
|
||||
if (needPortMapping != -1) {
|
||||
port = portService.allocatePort() + "";
|
||||
JedisUtil.hset("port", podName, port);
|
||||
}
|
||||
|
||||
sendSingleToKafka(buildParams, tpiID, requestTime);
|
||||
|
||||
response.put("ableToCreate", 1);
|
||||
response.put("costTime", System.currentTimeMillis() - TimeHelper.convertTimeToMillis(evaluateStartTime));
|
||||
response.put("waitNum", 0);
|
||||
response.put("code", 0);
|
||||
response.put("port", port);
|
||||
response.put("msg", "评测完成");
|
||||
|
||||
logger.info("[end]单个文件评测:tpiID: {}, buildID: {}", tpiID, buildID);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private void sendSingleToKafka(JSONObject buildparams, String tpiID, final LocalDateTime requestTime) {
|
||||
SingleEvaThread singleEvaThread = gameService.getSingleEvaThread(buildparams);
|
||||
threadPoolTaskExecutor.execute(singleEvaThread);
|
||||
}
|
||||
|
||||
private void sendToKafka(JSONObject buildParams, String tpiID, final LocalDateTime requestTime) {
|
||||
BuildThread buildThread = gameService.getBuildThread(buildParams);
|
||||
threadPoolTaskExecutor.execute(buildThread);
|
||||
|
|
|
@ -13,11 +13,12 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.educoder.bridge.game.thread.SingleEvaThread;
|
||||
import com.educoder.bridge.k8s.service.EvaScriptService;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.MDC;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@ -67,6 +68,9 @@ public class GameService {
|
|||
@Autowired
|
||||
ResourceFileService resourceFileService;
|
||||
|
||||
@Autowired
|
||||
private EvaScriptService evaScriptService;
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(GameService.class);
|
||||
|
||||
/**
|
||||
|
@ -679,7 +683,18 @@ public class GameService {
|
|||
BuildThread buildThread = (BuildThread) BeanFactory.getObejct("BuildThread");
|
||||
buildThread.setBuildParams(buildParams);
|
||||
return buildThread;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据单个文件评测信息,构建一个singleEvaThread
|
||||
*
|
||||
* @param buildParams
|
||||
* @return
|
||||
*/
|
||||
public SingleEvaThread getSingleEvaThread(JSONObject buildParams) {
|
||||
SingleEvaThread singleEvaThread = (SingleEvaThread) BeanFactory.getObejct("singleEvaThread");
|
||||
singleEvaThread.setBuildParams(buildParams);
|
||||
return singleEvaThread;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -824,4 +839,52 @@ public class GameService {
|
|||
return result;
|
||||
}
|
||||
|
||||
public void generateEvaluateShellScript(String tpiWorkspace, String evaScriptType) throws GameException {
|
||||
logger.debug("tpiWorkspace: {}, evaScriptType: {}", tpiWorkspace, evaScriptType);
|
||||
|
||||
// 获取脚本
|
||||
String script = evaScriptService.getScriptByType(evaScriptType);
|
||||
if (StringUtils.isEmpty(script)) {
|
||||
logger.error("生成评测脚本失败: 无该类型评测脚本");
|
||||
|
||||
throw new GameException(-1, "生成评测脚本失败: 无该类型评测脚本");
|
||||
}
|
||||
script = Base64Util.decode(script);
|
||||
script = script.replace("WORKSPACE", TpUtils.buildTpiRepoPath(tpiWorkspace, appConfig.getSingleEvaCode().split("/")[0]));
|
||||
|
||||
try {
|
||||
// 写脚本到工作空间目录下
|
||||
FileUtils.deleteQuietly(new File(tpiWorkspace + File.separator + "evaluate.sh"));
|
||||
FileUtils.writeStringToFile(new File(tpiWorkspace + File.separator + "evaluate.sh"), script, "UTF-8");
|
||||
} catch (Exception e) {
|
||||
logger.error("生成评测脚本失败!{}", e);
|
||||
|
||||
throw new GameException(-1, "生成评测脚本失败");
|
||||
}
|
||||
|
||||
try {
|
||||
String content = resourceFileService.getResourceFileContent("evaluate/execeva.sh");
|
||||
FileUtils.deleteQuietly(new File(tpiWorkspace + File.separator + "execeva.sh"));
|
||||
FileUtils.writeStringToFile(new File(tpiWorkspace + File.separator + "execeva.sh"), content, "UTF-8");
|
||||
} catch (Exception e) {
|
||||
logger.error("生成评测脚本execeva失败!{}", e);
|
||||
|
||||
throw new GameException(-1, "生成评测脚本execeva失败");
|
||||
}
|
||||
}
|
||||
|
||||
public void generateEvaluateCodeFile(String tpiWorkspace, String codeFileName, String codeFile) throws GameException {
|
||||
|
||||
logger.debug("生成评测代码文件: tpiWorkspace: {}, codeFileName: {}", tpiWorkspace, codeFileName);
|
||||
String codeFilePath = tpiWorkspace + File.separator + appConfig.getSingleEvaCode() + File.separator + codeFileName;
|
||||
try {
|
||||
// 写脚本到工作空间目录下
|
||||
FileUtils.deleteQuietly(new File(codeFilePath));
|
||||
FileUtils.writeStringToFile(new File(codeFilePath), codeFile, "UTF-8");
|
||||
} catch (Exception e) {
|
||||
logger.error("生成评测代码文件失败!{}", e);
|
||||
|
||||
throw new GameException(-1, "生成评测代码文件失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
package com.educoder.bridge.game.service;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
|
@ -50,7 +51,7 @@ public class KubernetesService {
|
|||
* 在pod中执行评测脚本
|
||||
*/
|
||||
public void executeShellInPod(JSONObject buildParams, Pod pod, int needPortMapping, int timeLimit, int isPublished,
|
||||
int instanceChallenge, String testCases) {
|
||||
int instanceChallenge, String testCases, Boolean isSingleEva) {
|
||||
String podName = pod.getMetadata().getName();
|
||||
logger.debug("podName: {}, needPortMapping: {}, timeLimit: {}, isPublished: {}, instanceChallenge: {}", podName,
|
||||
needPortMapping, timeLimit, isPublished, instanceChallenge);
|
||||
|
@ -67,7 +68,11 @@ public class KubernetesService {
|
|||
String buildID = buildParams.getString("buildID");
|
||||
String tpiWorkspace = TpUtils.buildTpiWorkspace(appConfig.getWorkspace(), tpiID);
|
||||
String shFile = tpiWorkspace + "/evaluate.sh";
|
||||
String command = " timeout " + timeLimit + " kubectl exec " + podName + " timeout " + timeLimit + " bash " + shFile + " " + instanceChallenge + " " + ins;
|
||||
String command = " timeout " + timeLimit + " kubectl exec " + podName + " timeout " + timeLimit + " bash " + shFile + " " + instanceChallenge + " " + ins;
|
||||
if (isSingleEva) {
|
||||
String codeFileName = buildParams.getString("codeFileName");
|
||||
command = " timeout " + timeLimit + " kubectl exec " + podName + " timeout " + timeLimit + " bash " + shFile + " " + codeFileName + " " + ins;
|
||||
}
|
||||
logger.info("evaluate command: {}", command);
|
||||
// 为pod的score加1,若pod之前不存在,则score为1
|
||||
JedisUtil.zup("taskNum", podName);
|
||||
|
|
|
@ -179,7 +179,7 @@ public class BuildThread extends Thread {
|
|||
: buildParams.getInteger("executeTime");
|
||||
kubernetesService.executeShellInPod(buildParams, podRef.getPod(), buildParams.getInteger("needPortMapping"),
|
||||
executeTimeLimit, buildParams.getInteger("isPublished"), buildParams.getInteger("instanceChallenge"),
|
||||
buildParams.getString("testCases"));
|
||||
buildParams.getString("testCases"), Boolean.FALSE);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
package com.educoder.bridge.game.thread;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.educoder.bridge.common.constant.BuildResultCsts;
|
||||
import com.educoder.bridge.common.settings.AppConfig;
|
||||
import com.educoder.bridge.common.utils.GameHelper;
|
||||
import com.educoder.bridge.game.model.PodRef;
|
||||
import com.educoder.bridge.game.service.GameService;
|
||||
import com.educoder.bridge.game.service.K8sService;
|
||||
import com.educoder.bridge.game.service.KubernetesService;
|
||||
import com.educoder.bridge.k8s.service.RunPodService;
|
||||
import io.fabric8.kubernetes.api.model.Pod;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 单个文件评测线程
|
||||
*/
|
||||
|
||||
@Service("singleEvaThread")
|
||||
@Scope("prototype")
|
||||
public class SingleEvaThread extends Thread {
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
@Autowired
|
||||
private GameService gameService;
|
||||
@Autowired
|
||||
private KubernetesService kubernetesService;
|
||||
@Autowired
|
||||
private K8sService k8sService;
|
||||
@Autowired
|
||||
private RunPodService runPodService;
|
||||
|
||||
private JSONObject buildParams;
|
||||
|
||||
public SingleEvaThread() {
|
||||
}
|
||||
|
||||
public SingleEvaThread(JSONObject buildParams) {
|
||||
this();
|
||||
this.buildParams = buildParams;
|
||||
}
|
||||
|
||||
public void setBuildParams(JSONObject buildParams) {
|
||||
this.buildParams = buildParams;
|
||||
}
|
||||
|
||||
public JSONObject getBuildParams() {
|
||||
return buildParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
String tpiID = buildParams.getString("tpiID");
|
||||
String buildID = buildParams.getString("buildID");
|
||||
Map<String, String> podInLabels = new HashMap<>(1);
|
||||
Map<String, String> podNotInLabels = new HashMap<>(1);
|
||||
podInLabels.put("tpiID", tpiID);
|
||||
podInLabels.put("type", "evaluate,evassh");
|
||||
String type = buildParams.getInteger("podType") != 2 ? "evaluate" : "evassh";
|
||||
|
||||
String testCases = buildParams.getString("testCases");
|
||||
String out = GameHelper.getTextMsgResult("服务启动中...");
|
||||
gameService.encapsulateStepByStepResult(out, buildParams, testCases);
|
||||
|
||||
Instant podStartInstant = Instant.now();
|
||||
|
||||
// 设立(重设)对应pod的定时删除任务(这个时间必须大于${timeLimit},确保脚本执行期间pod不会中途死) 单位:秒
|
||||
int timeLimit = buildParams.getInteger("timeLimit");
|
||||
gameService.reTiming(type + "-" + tpiID, timeLimit + 60);
|
||||
|
||||
// 查询 pod
|
||||
Pod pod = k8sService.getK8sRunningPod(podInLabels, podNotInLabels);
|
||||
// 如果pod不存在,还将创建
|
||||
pod = runPodService.createRunPod(pod, buildParams, tpiID, type, timeLimit);
|
||||
|
||||
final PodRef podRef = new PodRef();
|
||||
if (pod != null) {
|
||||
podRef.setPod(pod);
|
||||
String podName = pod.getMetadata().getName();
|
||||
|
||||
double createPodAndSvcCost = (double) Duration.between(podStartInstant, Instant.now()).toMillis()
|
||||
/ 1000;
|
||||
podRef.setCreatePodAndSvcCost(createPodAndSvcCost);
|
||||
|
||||
// 设立(重设)对应pod的定时删除任务(这个时间必须大于${timeLimit},确保脚本执行期间pod不会中途死) 单位:秒
|
||||
gameService.reTiming(podName, timeLimit + 60);
|
||||
}
|
||||
|
||||
JSONArray cases = JSONArray.parseArray(testCases);
|
||||
if (podRef.getPod() == null) {
|
||||
logger.error("实训 {} 创建 pod 失败", tpiID);
|
||||
String msg = GameHelper.getResult("系统繁忙,请稍后重试", cases.size(), BuildResultCsts.DOWNLOAD_STATUS_SUCCESS,
|
||||
BuildResultCsts.CREATE_POD_STATUS_FAIL);
|
||||
gameService.encapsulateBuildResult(msg, buildParams, testCases);
|
||||
return;
|
||||
}
|
||||
|
||||
// 记录创建pod耗时
|
||||
GameHelper.setTimeCostInJedis("timeCost:" + tpiID + ":" + buildID, "createPod",
|
||||
String.format("%.3f", podRef.getCreatePodAndSvcCost()));
|
||||
|
||||
out = GameHelper.getTextMsgResult("服务启动完成");
|
||||
gameService.encapsulateStepByStepResult(out, buildParams, testCases);
|
||||
|
||||
// 在pod中执行评测脚本
|
||||
Integer executeTimeLimit = buildParams.getInteger("executeTime") == null ? buildParams.getInteger("timeLimit")
|
||||
: buildParams.getInteger("executeTime");
|
||||
kubernetesService.executeShellInPod(buildParams, podRef.getPod(), buildParams.getInteger("needPortMapping"),
|
||||
executeTimeLimit, buildParams.getInteger("isPublished"), buildParams.getInteger("instanceChallenge"),
|
||||
buildParams.getString("testCases"), Boolean.TRUE);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "singleEvaThread{" + buildParams.toJSONString() + '}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package com.educoder.bridge.k8s.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.educoder.bridge.k8s.model.EvaluatingScript;
|
||||
import com.educoder.bridge.k8s.service.EvaScriptService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 评测脚本相关的接口
|
||||
*/
|
||||
@Api(value = "script", hidden = true)
|
||||
@RestController
|
||||
@RequestMapping("/script")
|
||||
public class ScriptController {
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@Autowired
|
||||
private EvaScriptService evaScriptService;
|
||||
|
||||
/**
|
||||
* 设置评测脚本
|
||||
*/
|
||||
@RequestMapping(path = "/set", method = RequestMethod.POST)
|
||||
@ApiOperation(value = "设置评测脚本", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
|
||||
public JSONObject set(
|
||||
@ApiParam(name = "scriptType", required = true, value = "脚本类型") @RequestParam("scriptType") String scriptType,
|
||||
@ApiParam(name = "script", required = true, value = "脚本") @RequestParam("script") String script
|
||||
) throws Exception {
|
||||
logger.info("[start]设置评测脚本, type: {}, script: {}", scriptType, script);
|
||||
JSONObject response = new JSONObject();
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
|
||||
EvaluatingScript evaluatingScript = evaScriptService.getEvaluatingScriptByType(scriptType);
|
||||
if (evaluatingScript == null) {
|
||||
evaluatingScript = new EvaluatingScript();
|
||||
evaluatingScript.setScriptType(scriptType);
|
||||
evaluatingScript.setScript(script);
|
||||
evaluatingScript.setCreateTime(now);
|
||||
evaluatingScript.setUpdateTime(now);
|
||||
evaScriptService.saveEvaluationScript(evaluatingScript);
|
||||
} else {
|
||||
evaluatingScript.setScript(script);
|
||||
evaluatingScript.setUpdateTime(now);
|
||||
evaScriptService.updateEvaluatingScript(evaluatingScript);
|
||||
}
|
||||
|
||||
response.put("code", 0);
|
||||
response.put("msg", "设置评测脚本成功");
|
||||
logger.info("[end]设置评测脚本");
|
||||
return response;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.educoder.bridge.k8s.dao;
|
||||
|
||||
import com.educoder.bridge.k8s.model.EvaluatingScript;
|
||||
|
||||
public interface EvaScriptMapper {
|
||||
int deleteByPrimaryKey(Long id);
|
||||
|
||||
int insert(EvaluatingScript record);
|
||||
|
||||
int insertSelective(EvaluatingScript record);
|
||||
|
||||
EvaluatingScript selectByPrimaryKey(Long id);
|
||||
|
||||
int updateByPrimaryKey(EvaluatingScript record);
|
||||
|
||||
EvaluatingScript selectScriptByType(String scriptType);
|
||||
|
||||
EvaluatingScript selectEvaScriptForUpdate(String scriptType);
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package com.educoder.bridge.k8s.model;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 单个文件评测脚本
|
||||
*/
|
||||
@ApiModel(value = "evaluating_script", description = "单个文件评测脚本")
|
||||
public class EvaluatingScript {
|
||||
|
||||
@ApiModelProperty(value = "脚本ID")
|
||||
private long id;
|
||||
|
||||
@ApiModelProperty(value = "脚本类型")
|
||||
private String scriptType;
|
||||
|
||||
@ApiModelProperty(value = "脚本")
|
||||
private String script;
|
||||
|
||||
private LocalDateTime createTime;
|
||||
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getScriptType() {
|
||||
return scriptType;
|
||||
}
|
||||
|
||||
public void setScriptType(String scriptType) {
|
||||
this.scriptType = scriptType;
|
||||
}
|
||||
|
||||
public String getScript() {
|
||||
return script;
|
||||
}
|
||||
|
||||
public void setScript(String script) {
|
||||
this.script = script;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public void setCreateTime(LocalDateTime createTime) {
|
||||
this.createTime = createTime;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdateTime() {
|
||||
return updateTime;
|
||||
}
|
||||
|
||||
public void setUpdateTime(LocalDateTime updateTime) {
|
||||
this.updateTime = updateTime;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package com.educoder.bridge.k8s.service;
|
||||
|
||||
import com.educoder.bridge.k8s.dao.EvaScriptMapper;
|
||||
import com.educoder.bridge.k8s.model.EvaluatingScript;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 评测脚本服务
|
||||
*/
|
||||
@Service("evaScriptService")
|
||||
public class EvaScriptService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
@Autowired
|
||||
private EvaScriptMapper evaScriptMapper;
|
||||
|
||||
public void saveEvaluationScript(EvaluatingScript evaluatingScript) {
|
||||
evaScriptMapper.insert(evaluatingScript);
|
||||
}
|
||||
|
||||
public String getScriptByType(String evaScriptType) {
|
||||
EvaluatingScript evaluatingScript = evaScriptMapper.selectScriptByType(evaScriptType);
|
||||
if (evaluatingScript != null) {
|
||||
return evaluatingScript.getScript();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public EvaluatingScript getEvaluatingScriptByType(String scriptType) {
|
||||
return evaScriptMapper.selectEvaScriptForUpdate(scriptType);
|
||||
}
|
||||
|
||||
public void updateEvaluatingScript(EvaluatingScript evaluatingScript) {
|
||||
evaScriptMapper.updateByPrimaryKey(evaluatingScript);
|
||||
}
|
||||
}
|
|
@ -74,4 +74,6 @@ dbpool.minIdle=100
|
|||
vncPort=6901
|
||||
|
||||
#kafka
|
||||
bootstrapServersConfig=172.16.94.173:9092
|
||||
bootstrapServersConfig=172.16.94.173:9092
|
||||
|
||||
singleEvaCode=code
|
|
@ -0,0 +1,124 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||
<mapper namespace="com.educoder.bridge.k8s.dao.EvaScriptMapper">
|
||||
<resultMap id="BaseResultMap"
|
||||
type="com.educoder.bridge.k8s.model.EvaluatingScript">
|
||||
<id column="id" property="id" jdbcType="BIGINT" />
|
||||
<result column="script_type" property="scriptType" jdbcType="VARCHAR" />
|
||||
<result column="script" property="script" jdbcType="VARCHAR" />
|
||||
<result column="create_time" property="createTime"
|
||||
jdbcType="TIMESTAMP" />
|
||||
<result column="update_time" property="updateTime"
|
||||
jdbcType="TIMESTAMP" />
|
||||
</resultMap>
|
||||
<sql id="Base_Column_List">
|
||||
id, script_type, script, create_time, update_time
|
||||
</sql>
|
||||
<select id="selectByPrimaryKey" resultMap="BaseResultMap"
|
||||
parameterType="java.lang.Long">
|
||||
select
|
||||
<include refid="Base_Column_List" />
|
||||
from evaluating_script
|
||||
where id = #{id,jdbcType=BIGINT}
|
||||
</select>
|
||||
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
|
||||
delete from
|
||||
evaluating_script
|
||||
where id = #{id,jdbcType=BIGINT}
|
||||
</delete>
|
||||
<insert id="insert"
|
||||
parameterType="com.educoder.bridge.k8s.model.EvaluatingScript">
|
||||
insert into evaluating_script (id, script_type, script,
|
||||
create_time,
|
||||
update_time)
|
||||
values (#{id,jdbcType=BIGINT}, #{scriptType,jdbcType=VARCHAR},
|
||||
#{script,jdbcType=VARCHAR},
|
||||
#{createTime,jdbcType=TIMESTAMP},
|
||||
#{updateTime,jdbcType=TIMESTAMP})
|
||||
</insert>
|
||||
<insert id="insertSelective"
|
||||
parameterType="com.educoder.bridge.k8s.model.EvaluatingScript">
|
||||
insert into evaluating_script
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="id != null">
|
||||
id,
|
||||
</if>
|
||||
<if test="scriptType != null">
|
||||
script_type,
|
||||
</if>
|
||||
<if test="script != null">
|
||||
script,
|
||||
</if>
|
||||
<if test="createTime != null">
|
||||
create_time,
|
||||
</if>
|
||||
<if test="updateTime != null">
|
||||
update_time,
|
||||
</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="id != null">
|
||||
#{id,jdbcType=BIGINT},
|
||||
</if>
|
||||
<if test="scriptType != null">
|
||||
#{scriptType,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="script != null">
|
||||
#{script,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="createTime != null">
|
||||
#{createTime,jdbcType=TIMESTAMP},
|
||||
</if>
|
||||
<if test="updateTime != null">
|
||||
#{updateTime,jdbcType=TIMESTAMP},
|
||||
</if>
|
||||
</trim>
|
||||
</insert>
|
||||
<update id="updateByPrimaryKeySelective"
|
||||
parameterType="com.educoder.bridge.k8s.model.EvaluatingScript">
|
||||
update evaluating_script
|
||||
<set>
|
||||
<if test="scriptType != null">
|
||||
script_type = #{scriptType,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="script != null">
|
||||
script = #{script,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="createTime != null">
|
||||
create_time = #{createTime,jdbcType=TIMESTAMP},
|
||||
</if>
|
||||
<if test="updateTime != null">
|
||||
update_time = #{updateTime,jdbcType=TIMESTAMP},
|
||||
</if>
|
||||
</set>
|
||||
where id = #{id,jdbcType=BIGINT}
|
||||
</update>
|
||||
<update id="updateByPrimaryKey"
|
||||
parameterType="com.educoder.bridge.k8s.model.EvaluatingScript">
|
||||
update evaluating_script
|
||||
set script_type = #{scriptType,jdbcType=VARCHAR},
|
||||
script = #{script,jdbcType=VARCHAR},
|
||||
create_time =
|
||||
#{createTime,jdbcType=TIMESTAMP},
|
||||
update_time =
|
||||
#{updateTime,jdbcType=TIMESTAMP}
|
||||
where id = #{id,jdbcType=BIGINT}
|
||||
</update>
|
||||
|
||||
<select id="selectScriptByType" resultMap="BaseResultMap"
|
||||
parameterType="java.lang.String">
|
||||
select
|
||||
<include refid="Base_Column_List" />
|
||||
from evaluating_script
|
||||
where script_type = #{scriptType,jdbcType=VARCHAR}
|
||||
</select>
|
||||
|
||||
<select id="selectEvaScriptForUpdate" resultMap="BaseResultMap"
|
||||
parameterType="java.lang.String">
|
||||
select
|
||||
<include refid="Base_Column_List" />
|
||||
from evaluating_script
|
||||
where script_type = #{scriptType,jdbcType=VARCHAR} for update
|
||||
</select>
|
||||
|
||||
</mapper>
|
|
@ -66,6 +66,8 @@
|
|||
<!-- 静态文件 -->
|
||||
<mvc:exclude-mapping path="/manifest.json" />
|
||||
<mvc:exclude-mapping path="/static/**" />
|
||||
<!-- k8s包 -->
|
||||
<mvc:exclude-mapping path="/script/**" />
|
||||
<!-- 拦截器 -->
|
||||
<bean class="com.educoder.bridge.user.interceptor.AuthorizationInterceptor"></bean>
|
||||
</mvc:interceptor>
|
||||
|
|
|
@ -53,11 +53,54 @@ public class Base64UtilTest {
|
|||
|
||||
@Test
|
||||
public void test3() {
|
||||
String str = "W3siaW5wdXQiOiIiLCJvdXRwdXQiOiJtYXN0ZXI6XG5ITWFzdGVyXG5cbkpwc1xuXG5OYW1lTm9kZVxuXG5RdW9ydW1QZWVyTWFpblxuXG5SZXNvdXJjZU1hbmFnZXJcblxuU2Vjb25kYXJ5TmFtZU5vZGVcblxuc2xhdmUxOlxuRGF0YU5vZGVcblxuSFJlZ2lvblNlcnZlclxuXG5KcHNcblxuTmFtZU5vZGVcblxuUXVvcnVtUGVlck1haW5cblxuc2xhdmUyOlxuRGF0YU5vZGVcblxuSE1hc3RlclxuXG5IUmVnaW9uU2VydmVyXG5cbkpwc1xuXG5RdW9ydW1QZWVyTWFpblxuXG4ifV0=";
|
||||
str = Base64Util.decode(str);
|
||||
String str = "#!/bin/bash\n" +
|
||||
"cd WORKSPACE\n" +
|
||||
"\n" +
|
||||
"# 执行命令\n" +
|
||||
"executeCommand=\"bash \"\n" +
|
||||
"\n" +
|
||||
"# 获取测试用例的输入(请勿改动此行语句)\n" +
|
||||
"input=$2; OLD_IFS=\"$IFS\"; IFS=,; ins=($input); IFS=\"$OLD_IFS\"\n" +
|
||||
"\n" +
|
||||
"compileResult=$(echo -n \"compile successfully\" | base64)\n" +
|
||||
"\n" +
|
||||
"# 执行函数\n" +
|
||||
"execute(){\n" +
|
||||
" # 当前关卡的执行目标文件\n" +
|
||||
" sourceClassName=$1\n" +
|
||||
" # 循环检测数据库是否已启动\n" +
|
||||
" while :;\n" +
|
||||
" do\n" +
|
||||
" sqlcmd -S localhost -U sa -P '<123123Aa!@>' -Q \"SELECT Name from sys.Databases\" 2>&1 >/dev/null\n" +
|
||||
" if [ $? -eq 0 ];then\n" +
|
||||
" break\n" +
|
||||
" else\n" +
|
||||
" sleep 1\n" +
|
||||
" fi\n" +
|
||||
" done\n" +
|
||||
"\t# 循环获取各测试用例下的实际输出\n" +
|
||||
" output=''\n" +
|
||||
" i=0\n" +
|
||||
" while [[ i -lt ${#ins[*]} ]]; do\n" +
|
||||
" result=$(echo \"${ins[$i]}\" | base64 -d | $executeCommand $sourceClassName | tr -s ' ' |tr ' ' '\\t' 2>&1 | base64)\n" +
|
||||
" #拼接输出结果\n" +
|
||||
" output=$output\\\"$result\\\",\n" +
|
||||
" let i++\n" +
|
||||
" done\n" +
|
||||
" output=\"[${output%?}]\"\n" +
|
||||
"}\n" +
|
||||
"execute $1\n" +
|
||||
"\n" +
|
||||
"# 返回评测结果\n" +
|
||||
"returnResult(){\n" +
|
||||
" result=\"{\\\"compileResult\\\":\\\"$compileResult\\\",\\\"out\\\":$output}\"\n" +
|
||||
" echo $result\n" +
|
||||
"}\n" +
|
||||
"returnResult";
|
||||
str = Base64Util.encode(str);
|
||||
System.out.println(str);
|
||||
System.out.println("**********************");
|
||||
str = "bWFzdGVyOg0KSE1hc3Rlcg0NCkpwcw0NCk5hbWVOb2RlDQ0KUXVvcnVtUGVlck1haW4NDQpSZXNvdXJjZU1hbmFnZXINDQpTZWNvbmRhcnlOYW1lTm9kZQ0NCnNsYXZlMToNCkRhdGFOb2RlDQ0KSFJlZ2lvblNlcnZlcg0NCkpwcw0NCk5hbWVOb2RlDQ0KUXVvcnVtUGVlck1haW4NDQpzbGF2ZTI6DQpEYXRhTm9kZQ0NCkhNYXN0ZXINDQpIUmVnaW9uU2VydmVyDQ0KSnBzDQ0KUXVvcnVtUGVlck1haW4NDQo";
|
||||
str = "W3siaW1hZ2UiOiJzcWxzZXJ2ZXItc3NoOnYxLjAiLCJjcHVMaW1pdCI6MSwiY3B1UmVxdWVzdCI6MC4xLCJtZW1vcnlMaW1pdCI6IjIwMDBNIiwibWVtb3J5UmVxdWVzdCI6IjEwMDAwTSIsInJlc291cmNlTGltaXQiOiIxMDAwMEsiLCJ0eXBlIjoibWFpbiJ9XQ==";
|
||||
str = Base64Util.decode(str);
|
||||
System.out.println(str);
|
||||
System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
||||
|
|
Loading…
Reference in New Issue