云平台控制器的一些优化
This commit is contained in:
parent
bf9c8c6cab
commit
abe0990313
|
@ -0,0 +1,25 @@
|
|||
package com.educoder.bridge.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
|
||||
/**
|
||||
* @author lqk
|
||||
* @version 0.1
|
||||
*/
|
||||
public class BaseController {
|
||||
protected HttpServletRequest request;
|
||||
protected HttpServletResponse response;
|
||||
protected HttpSession session;
|
||||
|
||||
@ModelAttribute
|
||||
public void setReqAndRes(HttpServletRequest request, HttpServletResponse response) {
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
this.session = request.getSession();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
package com.educoder.bridge.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.educoder.bridge.exception.GameException;
|
||||
import com.educoder.bridge.service.GameService;
|
||||
import com.educoder.bridge.settings.AppConfig;
|
||||
import com.educoder.bridge.utils.Base64Util;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
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.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
|
||||
/**
|
||||
* created by weishao at 2017/8/7
|
||||
* 数据迁移控制器,用于保证已经发布的实训不受影响
|
||||
*/
|
||||
|
||||
@Api(value = "数据迁移控制器", hidden = true)
|
||||
@RestController
|
||||
@RequestMapping("/dataTransfer")
|
||||
public class DataTransferController extends BaseController{
|
||||
@Autowired
|
||||
private GameService gameService;
|
||||
|
||||
@Autowired
|
||||
private AppConfig appConfig;
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DataTransferController.class);
|
||||
|
||||
@RequestMapping(path = "/transfer")
|
||||
@ApiOperation(value = "数据迁移,写测试用例,生成模板脚本,tpi评测脚本", httpMethod = "POST", produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
public JSONObject transfer(
|
||||
@ApiParam(name = "gameInfo", required = true, value = "实训的相关配置,base64编码") @RequestParam String gameInfo,
|
||||
@ApiParam(name = "tpiList", required = true, value = "实训实例的信息,包括tpiID与instanceGitURL") @RequestParam String tpiList) {
|
||||
logger.debug("/dataTransfer/transfer: gameInfo: " + gameInfo + ", tpiList: " + tpiList);
|
||||
|
||||
JSONObject result = new JSONObject();
|
||||
// // 将字符串解码后转换为JSON对象
|
||||
// gameInfo = Base64Util.decode(gameInfo);
|
||||
// JSONObject info = JSONObject.parseObject(gameInfo);
|
||||
//
|
||||
// tpiList = Base64Util.decode(tpiList);
|
||||
// JSONArray list = JSONObject.parseArray(tpiList);
|
||||
//
|
||||
// try {
|
||||
// transferTPM(info);
|
||||
//
|
||||
// for (int i = 0; i < list.size(); i++) {
|
||||
// try {
|
||||
// transferTPI(info.getString("tpmID"), list.getJSONObject(i).getString("tpiID"),
|
||||
// list.getJSONObject(i).getString("instanceGitURL"));
|
||||
// } catch (GameException e) {
|
||||
// logger.error("TPI 转换失败,tpiID:{}", list.getJSONObject(i).getString("tpiID"));
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
// }catch (GameException e) {
|
||||
//
|
||||
// logger.error("TPM 转换失败,tpmID:{}", info.getString("tpmID"));
|
||||
// result.put("code", -1);
|
||||
// result.put("msg", "转换失败");
|
||||
// }
|
||||
|
||||
result.put("code", 0);
|
||||
result.put("msg", "转换成功");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/transferTPI")
|
||||
@ApiOperation(value = "数据迁移,单个tpi迁移", httpMethod = "POST", produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
public JSONObject transferATPI(
|
||||
@ApiParam(name = "tpmID", required = true, value = "tpmID") @RequestParam String tpmID,
|
||||
@ApiParam(name = "tpiID", required = true, value = "tpiID") @RequestParam String tpiID,
|
||||
@ApiParam(name = "instanceGitURL", required = true, value = "TPI版本库地址,base64编码") @RequestParam String instanceGitURL)
|
||||
throws GameException {
|
||||
JSONObject result = new JSONObject();
|
||||
|
||||
instanceGitURL = Base64Util.decode(instanceGitURL);
|
||||
transferTPI (tpmID, tpiID, instanceGitURL);
|
||||
|
||||
result.put("code", 0);
|
||||
result.put("msg", "转换成功");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换tpm
|
||||
* @param gameInfo
|
||||
*/
|
||||
private void transferTPM (JSONObject gameInfo) throws GameException {
|
||||
logger.debug("DataTransferController: transferTPM: gameInfo: " + gameInfo);
|
||||
// 将测试用例写入工作目录下的文件中
|
||||
// gameService.generateTestCasesForGame(gameInfo.getString("tpmID"),
|
||||
// gameInfo.getJSONArray("testCases"));
|
||||
//
|
||||
// // 生成Tpm评测脚本到工作目录下指定文件
|
||||
// gameService.generateTpmEvaluateShellScript(gameInfo);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换tpi
|
||||
* @param tpmID
|
||||
* @param tpiID
|
||||
* @param instanceGitURL
|
||||
*/
|
||||
private void transferTPI (String tpmID, String tpiID, String instanceGitURL) throws GameException {
|
||||
logger.debug("DataTransferController: transferTPI: tpmID: " + tpmID + ", tpiID: " + tpiID + ", instanceGitURL: " + instanceGitURL);
|
||||
|
||||
// String repoName = StringUtil.getRepoName(instanceGitURL);
|
||||
// String path = appConfig.getWorkspace() + File.separator + "myshixun_" + tpiID;
|
||||
// // 删除旧数据
|
||||
// if (!StringUtils.isEmpty(tpiID)) {
|
||||
// ShellUtil.execute("rm -rf " + path);
|
||||
// }
|
||||
// // 克隆版本库
|
||||
// gameService.gitClone(path, instanceGitURL, "origin", repoName);
|
||||
//
|
||||
// //根据实训模板评测脚本生成Tpi脚本到工作目录
|
||||
// gameService.generateTpiEvaluateShellScript(appConfig.getWorkspace() + "/shell/" + tpmID + "/evaluate.sh",
|
||||
// appConfig.getWorkspace() + "/myshixun_" + tpiID + "/" + repoName + "/");
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
package com.educoder.bridge.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.educoder.bridge.service.DockerService;
|
||||
import com.educoder.bridge.utils.ClientUtil;
|
||||
import com.spotify.docker.client.DockerClient;
|
||||
import com.spotify.docker.client.exceptions.DockerException;
|
||||
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.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Api(value = "镜像管理", hidden = true)
|
||||
@RestController
|
||||
@RequestMapping("/docker")
|
||||
public class DockerController {
|
||||
@Autowired
|
||||
private DockerService dockerService;
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(DockerController.class);
|
||||
|
||||
@RequestMapping(path = "/images")
|
||||
@ApiOperation(value = "获取全部实训镜像", httpMethod = "GET")
|
||||
public JSONObject getImages() throws DockerException, InterruptedException {
|
||||
logger.info("收到获取镜像请求");
|
||||
|
||||
JSONObject result = new JSONObject();
|
||||
// 获取主节点全部镜像
|
||||
DockerClient docker = ClientUtil.getDocker();
|
||||
List<String> imageList = dockerService.getImageList(docker);
|
||||
result.put("images", imageList);
|
||||
|
||||
// 获取未同步的镜像
|
||||
List<DockerClient> dockerDeputies = ClientUtil.getDockerDeputies();
|
||||
List<String> imageDiff = dockerService.imageDiff(imageList, dockerDeputies);
|
||||
result.put("imagesNotSync", imageDiff);
|
||||
|
||||
result.put("code", 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/syncImage")
|
||||
@ApiOperation(value = "同步镜像", httpMethod = "POST", produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
public JSONObject syncImage(
|
||||
@ApiParam(name = "imageName", required = true, value = "镜像名") @RequestParam String imageName)
|
||||
throws DockerException, InterruptedException {
|
||||
logger.info("imageName: {}", imageName);
|
||||
|
||||
JSONObject result = new JSONObject();
|
||||
|
||||
DockerClient dockerMaster = ClientUtil.getDocker();
|
||||
List<DockerClient> dockerDeputies = ClientUtil.getDockerDeputies();
|
||||
|
||||
dockerService.syncImage(dockerMaster, dockerDeputies, imageName);
|
||||
|
||||
result.put("code", 0);
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/updateImage")
|
||||
@ApiOperation(value = "镜像有更新", httpMethod = "POST", produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
public JSONObject updateImage(
|
||||
@ApiParam(name = "imageName", required = true, value = "镜像名") @RequestParam String imageName,
|
||||
@ApiParam(name = "imageID", required = true, value = "旧的镜像ID") @RequestParam String imageID,
|
||||
@ApiParam(name = "flag", required = true, value = "更新行为选择,0更新 1回退") @RequestParam Integer flag)
|
||||
throws DockerException, InterruptedException {
|
||||
logger.info("imageName: {}, imageID: {}, flag: {}", imageName, imageID, flag);
|
||||
|
||||
JSONObject result = new JSONObject();
|
||||
|
||||
DockerClient dockerMaster = ClientUtil.getDocker();
|
||||
List<DockerClient> dockerDeputies = ClientUtil.getDockerDeputies();
|
||||
|
||||
if (flag == 0) {
|
||||
// 选择更新
|
||||
dockerService.syncImage(dockerMaster, dockerDeputies, imageName);
|
||||
} else {
|
||||
// 选择回退
|
||||
dockerService.resetImage(dockerMaster, imageID, imageName);
|
||||
}
|
||||
|
||||
result.put("code", 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,347 @@
|
|||
package com.educoder.bridge.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.educoder.bridge.exception.GameException;
|
||||
import com.educoder.bridge.service.*;
|
||||
import com.educoder.bridge.settings.AppConfig;
|
||||
import com.educoder.bridge.utils.*;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
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 java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
/**
|
||||
* 和实训相关的接口
|
||||
*
|
||||
* @author weishao
|
||||
* @date 2017/11/02
|
||||
*/
|
||||
@Api(value = "game控制器", hidden = true)
|
||||
@RestController
|
||||
@RequestMapping("/game")
|
||||
public class GameController extends BaseController {
|
||||
@Autowired
|
||||
private AppConfig appConfig;
|
||||
@Autowired
|
||||
private GameService gameService;
|
||||
@Autowired
|
||||
private KubernetesService kubernetesService;
|
||||
@Autowired
|
||||
private K8sService k8sService;
|
||||
@Autowired
|
||||
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(GameController.class);
|
||||
|
||||
/**
|
||||
* 开启实训:
|
||||
* 克隆版本库
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(path = "/openGameInstance")
|
||||
@ApiOperation(value = "开启实训", httpMethod = "POST", produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
public JSONObject openGameInstance(
|
||||
@ApiParam(name = "tpmGitURL", required = true, value = "Tpm的gitUrl,需要base64编码") @RequestParam String tpmGitURL,
|
||||
@ApiParam(name = "tpiID", required = true, value = "实训实例的ID") @RequestParam String tpiID,
|
||||
@ApiParam(name = "tpiRepoName", required = true, value = "tpiRepoName") @RequestParam String tpiRepoName
|
||||
) throws Exception {
|
||||
logger.info("tpmGitURL: {}\n, tpiID: {}, tpiRepoName: {}", tpmGitURL, tpiID, tpiRepoName);
|
||||
|
||||
JSONObject response = new JSONObject();
|
||||
|
||||
// 设定工作路径为${workspace}/myshixun_${tpiID}
|
||||
String path = appConfig.getWorkspace() + File.separator + "myshixun_" + tpiID;
|
||||
|
||||
// 对当前TPI,从TPM clone 版本库
|
||||
tpmGitURL = Base64Util.decode(tpmGitURL);
|
||||
gameService.gitClone(path, tpmGitURL, "remote_origin", tpiRepoName);
|
||||
|
||||
response.put("code", 0);
|
||||
response.put("msg", "开启成功");
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 评测:
|
||||
* 对每一次评测请求,先判断是否能立即执行还是需要先排队,如果能立即执行,开启一个线程执行以下步骤
|
||||
* 如果需要排队,将相关参数封装成一个线程,存入redis队列,等待执行,并返回标志位给前台,通知前台进行轮询
|
||||
*
|
||||
* 1. gitpull
|
||||
* 2. 如果有端口映射
|
||||
* 3. 创建pod 和 service 并 创建定时删除pod的定时任务
|
||||
* 4. 在pod中执行评测脚本
|
||||
* 5. 回传结果
|
||||
*
|
||||
*
|
||||
* @param tpiID
|
||||
* @param tpiGitURL
|
||||
* @param buildID
|
||||
* @param instanceChallenge
|
||||
* @param resubmit
|
||||
* @param times
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@RequestMapping(path = "/gameEvaluate")
|
||||
@ApiOperation(value = "评测", httpMethod = "POST", produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
public JSONObject gameEvaluate(
|
||||
@ApiParam(name = "tpiID", required = true, value = "实训实例的ID") @RequestParam String tpiID,
|
||||
@ApiParam(name = "tpiGitURL", required = true, value = "学员对应当前实训的版本库地址,需要base64编码") @RequestParam String tpiGitURL,
|
||||
@ApiParam(name = "buildID", required = true, value = "本次评测ID") @RequestParam String buildID,
|
||||
@ApiParam(name = "instanceChallenge", required = true, value = "当前处在第几关") @RequestParam String instanceChallenge,
|
||||
@ApiParam(name = "testCases", required = true, value = "测试用例") @RequestParam String testCases,
|
||||
@ApiParam(name = "tpmScript", required = true, value = "tpm评测脚本,需要base64编码") @RequestParam String tpmScript,
|
||||
@ApiParam(name = "timeLimit", required = false, value = "时间限制") Integer timeLimit,
|
||||
@ApiParam(name = "resubmit", required = true, value = "是否是重复评测") @RequestParam String resubmit,
|
||||
@ApiParam(name = "times", required = true, value = "是否是时间轮询请求") @RequestParam Integer times,
|
||||
@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)
|
||||
throws Exception {
|
||||
// 记录开始时间
|
||||
SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
|
||||
logger.info("training_task_status start#1**{}**** {}", tpiID, dateformat.format(System.currentTimeMillis()));
|
||||
|
||||
long evaStart = System.currentTimeMillis();
|
||||
String evaluateStart = dateformat.format(evaStart);
|
||||
JSONObject cost = new JSONObject();
|
||||
cost.put("evaluateStart", evaluateStart);
|
||||
cost.put("evaluateAllTime", evaStart);
|
||||
ConstantUtil.costMap.put(tpiID, cost);
|
||||
|
||||
JSONObject response = new JSONObject();
|
||||
|
||||
// 所有构建需要的参数放到map中,供执行线程使用
|
||||
tpiGitURL = Base64Util.decode(tpiGitURL);
|
||||
needPortMapping = needPortMapping == null ? 0 : needPortMapping;
|
||||
timeLimit = timeLimit == null ? Integer.parseInt(appConfig.getDefaultTimeLimit()) : timeLimit;
|
||||
testCases = Base64Util.decode(testCases);
|
||||
containers = Base64Util.decode(containers);
|
||||
|
||||
// 每次评测均生成新TPI评测脚本
|
||||
tpmScript = Base64Util.decode(tpmScript);
|
||||
String path = appConfig.getWorkspace() + File.separator + "myshixun_" + tpiID;
|
||||
String tpiRepoName = StringHelper.getRepoName(tpiGitURL);
|
||||
logger.info("generateTpiEvaluateShellScript start#2**{}**** {}", tpiID, dateformat.format(System.currentTimeMillis()));
|
||||
gameService.generateTpiEvaluateShellScript(tpmScript, path, tpiRepoName);
|
||||
|
||||
logger.info("retryformat start#3**{}**** {}", tpiID, dateformat.format(System.currentTimeMillis()));
|
||||
JSONObject buildParams = new JSONObject(true);
|
||||
buildParams.put("tpiID", tpiID);
|
||||
buildParams.put("tpiGitURL", tpiGitURL);
|
||||
buildParams.put("buildID", buildID);
|
||||
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);
|
||||
|
||||
// 若实训生成文件
|
||||
if (!StringUtils.isEmpty(file)) {
|
||||
file = Base64Util.decode(file);
|
||||
buildParams.put("file", file);
|
||||
JSONObject jsonObject = JSONObject.parseObject(file);
|
||||
// 清空目标文件夹以防止影响此次评测结果
|
||||
String dir = path + "/" + tpiRepoName + "/" + jsonObject.getString("path");
|
||||
StringHelper.deleteFiles(dir, "pic");
|
||||
StringHelper.deleteFiles(dir, "apk");
|
||||
StringHelper.deleteFiles(dir, "html");
|
||||
}
|
||||
|
||||
// 最大running pod数量
|
||||
int maxRunningPodNum = Integer.parseInt(appConfig.getMaxRunningPodNum());
|
||||
|
||||
// podName
|
||||
String podName = podType == 0 ? "evaluate-" + tpiID : "evassh-" + tpiID;
|
||||
|
||||
// 若需要端口映射服务,则分配端口,将podName-port键值对存储于redis
|
||||
String port = "-1";
|
||||
if(needPortMapping != -1) {
|
||||
port = JedisUtil.hget("port", podName);
|
||||
if(port == null) {
|
||||
port = PortUtil.getPort() + "";
|
||||
JedisUtil.hset("port", podName, port);
|
||||
}
|
||||
}
|
||||
|
||||
// 构建任务字符串,便于redis存储
|
||||
String task = buildParams.toJSONString();
|
||||
|
||||
// 此次请求只为轮询时间,只返回轮询的时间结果
|
||||
if (times != 1) {
|
||||
double buildRank;
|
||||
|
||||
try {
|
||||
buildRank = JedisUtil.zrank("task", task);
|
||||
} catch (Exception e) {
|
||||
response.put("ableToCreate", 0);
|
||||
response.put("waitNum", maxRunningPodNum + JedisUtil.zlen("task"));
|
||||
response.put("code", 0);
|
||||
response.put("msg", "等待评测");
|
||||
return response;
|
||||
}
|
||||
|
||||
if (buildRank != Double.MIN_VALUE) {
|
||||
logger.debug("任务:{} : 还在等待队列中!", task);
|
||||
|
||||
response.put("ableToCreate", 0);
|
||||
response.put("waitNum", buildRank == Double.MAX_VALUE ? maxRunningPodNum + JedisUtil.zlen("task") : buildRank + maxRunningPodNum);
|
||||
response.put("code", 0);
|
||||
response.put("msg", "等待评测");
|
||||
return response;
|
||||
} else {
|
||||
// 轮询时间请求,发现目标任务不在队列,就直接认为其正在运行
|
||||
logger.debug("任务:{} : 正在执行中!", task);
|
||||
|
||||
response.put("ableToCreate", 1);
|
||||
response.put("waitNum", 0);
|
||||
response.put("code", 0);
|
||||
response.put("port", Integer.parseInt(port));
|
||||
response.put("msg", "正在评测");
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
logger.info("kubernetes start#4**{}**** {}", tpiID, dateformat.format(System.currentTimeMillis()));
|
||||
// 获取Running状态的pod数
|
||||
int runningPodNum = kubernetesService.getRunningPodNum();
|
||||
// 判断是否可以立即执行(若pod已经存在,或是,pod不存在但是此时可以创建pod)
|
||||
boolean executeImmediately = kubernetesService.isPodRunning("tpiID", tpiID)
|
||||
|| (runningPodNum < maxRunningPodNum && k8sService.ableToEvaluate());
|
||||
|
||||
if (executeImmediately) {
|
||||
// 直接执行任务
|
||||
logger.info("execute start#5**{}**** {}", tpiID, dateformat.format(System.currentTimeMillis()));
|
||||
BuildThread buildThread = gameService.getBuildThread(buildParams);
|
||||
threadPoolTaskExecutor.execute(buildThread);
|
||||
long evaEnd = System.currentTimeMillis();
|
||||
long costTime = evaEnd - evaStart;
|
||||
|
||||
response.put("ableToCreate", 1);
|
||||
response.put("costTime", costTime);
|
||||
response.put("waitNum", 0);
|
||||
response.put("code", 0);
|
||||
response.put("port", port);
|
||||
response.put("msg", "评测完成");
|
||||
|
||||
logger.debug("直接执行任务!task: {}, tpi: {}", task, tpiID);
|
||||
} else {
|
||||
// 否则,将构建任务推入redis
|
||||
JedisUtil.zpush("task", task);
|
||||
|
||||
//评测任务等待执行
|
||||
response.put("ableToCreate", 0);
|
||||
//在等待队列中的位置
|
||||
response.put("waitNum", JedisUtil.zlen("task") + maxRunningPodNum);
|
||||
response.put("code", 0);
|
||||
response.put("msg", "等待评测");
|
||||
|
||||
logger.debug("任务入队等待!task: {}, tpi: {}", task, tpiID);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* tpm版本库已更新,同步
|
||||
*
|
||||
* @param tpiID
|
||||
* @param tpiGitURL
|
||||
* @param tpmGitURL
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(path = "/resetTpmRepository", method = RequestMethod.POST)
|
||||
@ApiOperation(value = "tpm版本库已更新,同步操作", httpMethod = "POST", produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
public JSONObject reset(
|
||||
@ApiParam(name = "tpiID", required = true, value = "tpi") @RequestParam String tpiID,
|
||||
@ApiParam(name = "tpiGitURL", required = true, value = "学员对应当前实训的版本库地址,base64编码") @RequestParam String tpiGitURL,
|
||||
@ApiParam(name = "tpmGitURL", required = true, value = "学员对应当前实训的tpm版本库地址,base64编码") @RequestParam String tpmGitURL,
|
||||
@ApiParam(name = "identifier", required = true, value = "push权限") @RequestParam String identifier)
|
||||
throws Exception {
|
||||
|
||||
logger.debug("tpm版本库已更新,同步tpi版本库,tpiID:{}", tpiID);
|
||||
|
||||
JSONObject response = new JSONObject();
|
||||
|
||||
tpmGitURL = Base64Util.decode(tpmGitURL);
|
||||
tpiGitURL = Base64Util.decode(tpiGitURL);
|
||||
String tpiRepoName = StringHelper.getRepoName(tpiGitURL);
|
||||
String path = appConfig.getWorkspace() + File.separator + "myshixun_" + tpiID;
|
||||
|
||||
try {
|
||||
// 从tpm远程库拉取代码到myshixun版本库然后强推到tpi远程库
|
||||
gameService.gitPullFromTpm(path, tpmGitURL, tpiRepoName);
|
||||
gameService.gitPushToTpi(path, tpiGitURL, identifier);
|
||||
|
||||
logger.debug("tpm库更新内容已同步,tpiID:{}", tpiID);
|
||||
response.put("code", 0);
|
||||
response.put("msg", "版本库更新成功");
|
||||
} catch (GameException e) {
|
||||
logger.debug("tpm库更新内容同步失败, tpiID:{}", tpiID);
|
||||
response.put("code", -1);
|
||||
response.put("msg", "版本库同步失败!");
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* tpi版本库head是否存在,若缺失则修复
|
||||
*
|
||||
* @param tpiID
|
||||
* @param tpiGitURL
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@RequestMapping(path = "/check")
|
||||
@ApiOperation(value = "进入实训时做校验", httpMethod = "POST", produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
public JSONObject check(
|
||||
@ApiParam(name = "tpiID", required = true, value = "实训实例的ID") @RequestParam String tpiID,
|
||||
@ApiParam(name = "tpiGitURL", required = true, value = "学员对应当前实训的版本库地址,base64编码") @RequestParam String tpiGitURL)
|
||||
throws Exception {
|
||||
|
||||
JSONObject response = new JSONObject();
|
||||
|
||||
tpiGitURL = Base64Util.decode(tpiGitURL);
|
||||
String tpiRepoName = StringHelper.getRepoName(tpiGitURL);
|
||||
String tpiRepoPath = appConfig.getWorkspace() + "/myshixun_" + tpiID + "/" + tpiRepoName;
|
||||
|
||||
// 远程tpi库是否缺失head了
|
||||
boolean noHead = ShellUtil.execute("cd " + tpiRepoPath + " && git remote show origin").contains("unknown");
|
||||
if (noHead) {
|
||||
logger.debug("tpiID:{} 远程tpi版本库head丢失", tpiID);
|
||||
|
||||
String identifier = StringHelper.getIdentifier(tpiGitURL);
|
||||
|
||||
// 处理仓库head丢失 ssh -p1122 git@10.9.191.219 'cd /home/git/repositories/p79061248/klp26sqc.git/refs/tmp/;
|
||||
// cd `ls -t | sed -n "2p"`; cat head >../../heads/master; git update-ref HEAD `cat head`'
|
||||
String command = "ssh -p1122 git@" + appConfig.getGitIP() + " 'cd repositories/" + identifier + "/"
|
||||
+ tpiRepoName + ".git/refs/tmp; cd `ls -t | sed -n \"2p\"`; cat head >../../heads/master; git update-ref HEAD `cat head`'";
|
||||
JSONObject result = ShellUtil.executeAndGetExitStatus(command);
|
||||
|
||||
response.put("fixHead", result.getIntValue("exitStatus"));
|
||||
}
|
||||
|
||||
response.put("msg", "finished");
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package com.educoder.bridge.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.educoder.bridge.service.K8sService;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by liqiankun on 2017/11/16 0016
|
||||
* Description:
|
||||
*/
|
||||
@Api(value = "资源监控", hidden = true)
|
||||
@RestController
|
||||
@RequestMapping("/monitor")
|
||||
public class MonitorController {
|
||||
Logger logger = LoggerFactory.getLogger(MonitorController.class);
|
||||
@Autowired
|
||||
private K8sService k8sService;
|
||||
|
||||
/**
|
||||
* 简单地获取集群中的Pod总量及各个节点上的Pod分布
|
||||
*
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@RequestMapping(path = "/getPodsInfo")
|
||||
@ApiOperation(value = "获取集群中某个节点的信息", httpMethod = "POST", produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
public JSONObject podsInfo() throws Exception {
|
||||
int podSum = k8sService.getPodSum();
|
||||
List<String> nodeIP = k8sService.getNodesIP();
|
||||
//生成Json格式的返回结果
|
||||
StringBuffer jsonResult = new StringBuffer("[");
|
||||
for (String ip : nodeIP) {
|
||||
int num = k8sService.getPodNum(ip);
|
||||
jsonResult.append("{\"ip\":" + "\"" + ip + "\"," + "\"num\":" + "\"" + num + "\"},");
|
||||
}
|
||||
int length = jsonResult.length() - 1;
|
||||
jsonResult.deleteCharAt(length);
|
||||
jsonResult.append("]");
|
||||
|
||||
JSONObject response = new JSONObject();
|
||||
response.put("sum", podSum);
|
||||
response.put("distr", jsonResult);
|
||||
response.put("code", 0);
|
||||
return response;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package com.educoder.bridge.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.educoder.bridge.service.VncService;
|
||||
import com.educoder.bridge.utils.Base64Util;
|
||||
import com.educoder.bridge.utils.JedisUtil;
|
||||
import com.educoder.bridge.utils.PortUtil;
|
||||
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.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Api(value = "vnc控制器", hidden = true)
|
||||
@RestController
|
||||
@RequestMapping("/vnc")
|
||||
public class VncController {
|
||||
|
||||
@Autowired
|
||||
private VncService vncService;
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(VncController.class);
|
||||
|
||||
@RequestMapping(path = "/getvnc")
|
||||
@ApiOperation(value = "图形界面", httpMethod = "POST", produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
public JSONObject getvnc(
|
||||
@ApiParam(name = "tpiID", required = true, value = "实训实例的ID") @RequestParam String tpiID,
|
||||
@ApiParam(name = "containers", required = true, value = "需要使用的容器,base64编码") @RequestParam String containers)
|
||||
throws Exception{
|
||||
|
||||
JSONObject response = new JSONObject();
|
||||
|
||||
String podName = "vnc-" + tpiID;
|
||||
containers = Base64Util.decode(containers);
|
||||
|
||||
// 为vnc分配port
|
||||
String port = JedisUtil.hget("port", podName);
|
||||
if (port == null) {
|
||||
port = PortUtil.getPort() + "";
|
||||
JedisUtil.hset("port", podName, port);
|
||||
}
|
||||
|
||||
vncService.getvnc(tpiID, containers);
|
||||
|
||||
response.put("code", 0);
|
||||
response.put("port", port);
|
||||
return response;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package com.educoder.bridge.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.educoder.bridge.model.Webssh;
|
||||
import com.educoder.bridge.service.WebsshService;
|
||||
import com.educoder.bridge.utils.Base64Util;
|
||||
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.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.servlet.ModelAndView;
|
||||
|
||||
/**
|
||||
* @author guange
|
||||
* @date 2017/08/02
|
||||
*/
|
||||
@Api(value = "提供webssh连接", hidden = true)
|
||||
@RestController
|
||||
public class WebsshController extends BaseController {
|
||||
private final static Logger logger = LoggerFactory.getLogger(WebsshController.class);
|
||||
|
||||
@Autowired
|
||||
private WebsshService websshService;
|
||||
|
||||
@ApiOperation(value = "获取连接容器所需的ip和端口信息", httpMethod = "POST", produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
@RequestMapping(path = "/webssh/getConnectInfo")
|
||||
public JSONObject getConnectInfo(
|
||||
@ApiParam(name = "tpiID", required = true, value = "tpiID") @RequestParam String tpiID,
|
||||
@ApiParam(name = "podType", required = true, value = "pod类型,0为evaluate, 1为webssh,2为evassh") @RequestParam Integer podType,
|
||||
@ApiParam(name = "containers", required = true, value = "需要使用的容器,base64编码") @RequestParam String containers)
|
||||
throws Exception {
|
||||
logger.info("tpiID: {}, podType: {},containers: {}", tpiID, podType, containers);
|
||||
|
||||
containers = Base64Util.decode(containers);
|
||||
Webssh sshInfo = websshService.create(tpiID, podType, containers);
|
||||
|
||||
JSONObject response = new JSONObject();
|
||||
response.put("address", sshInfo.getAddress());
|
||||
response.put("port", Integer.parseInt(sshInfo.getPort()) + "");
|
||||
response.put("code", 0);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@RequestMapping(value={"/", "ssh"}, method= RequestMethod.GET)
|
||||
public ModelAndView index(@RequestParam("Host")String host,
|
||||
@RequestParam("Port")int port,
|
||||
@RequestParam("Gameid")int gameId,
|
||||
@RequestParam("Username")String username,
|
||||
@RequestParam("Password")String password,
|
||||
@RequestParam("Tab")String tab,
|
||||
@RequestParam("Rows")int rows) {
|
||||
//index就是视图的名称(index.ftl)
|
||||
logger.debug("/ssh: 接收到前端连接请求,host: {}, port: {}", host, port);
|
||||
ModelAndView mv = new ModelAndView();
|
||||
mv.setViewName("index");
|
||||
mv.addObject("Host", host);
|
||||
mv.addObject("Port", port);
|
||||
mv.addObject("Username", username);
|
||||
mv.addObject("Password", password);
|
||||
mv.addObject("Tab", tab);
|
||||
mv.addObject("Rows", rows);
|
||||
mv.addObject("Gameid", gameId);
|
||||
mv.addObject("digest", System.currentTimeMillis());
|
||||
return mv;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue