mirror of
https://github.com/PowerJob/PowerJob.git
synced 2025-07-17 00:00:04 +08:00
[dev] finished partly of deploy websocket & don't forget to fix the md5 bug tomorrow
This commit is contained in:
parent
7247f41199
commit
c18658003e
@ -120,9 +120,9 @@ public class OmsFileUtils {
|
||||
* 计算文件的 MD5
|
||||
* @param f 文件
|
||||
* @return md5
|
||||
* @throws Exception 异常
|
||||
* @throws IOException 异常
|
||||
*/
|
||||
public static String md5(File f) throws Exception {
|
||||
public static String md5(File f) throws IOException {
|
||||
String md5;
|
||||
try(FileInputStream fis = new FileInputStream(f)) {
|
||||
md5 = DigestUtils.md5DigestAsHex(fis);
|
||||
|
@ -15,16 +15,20 @@ import com.github.kfcfans.oms.server.persistence.core.repository.ContainerInfoRe
|
||||
import com.github.kfcfans.oms.server.service.ha.ClusterStatusHolder;
|
||||
import com.github.kfcfans.oms.server.service.ha.WorkerManagerService;
|
||||
import com.github.kfcfans.oms.server.service.lock.LockService;
|
||||
import com.github.kfcfans.oms.server.web.request.SaveContainerInfoRequest;
|
||||
import com.google.common.collect.Lists;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.filefilter.FileFilterUtils;
|
||||
import org.apache.commons.io.filefilter.IOFileFilter;
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.apache.maven.shared.invoker.*;
|
||||
import org.eclipse.jgit.api.CloneCommand;
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.transport.CredentialsProvider;
|
||||
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.data.mongodb.gridfs.GridFsResource;
|
||||
@ -32,12 +36,15 @@ import org.springframework.data.mongodb.gridfs.GridFsTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.websocket.RemoteEndpoint;
|
||||
import javax.websocket.Session;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@ -63,6 +70,78 @@ public class ContainerService {
|
||||
|
||||
// 并发部署的机器数量
|
||||
private static final int DEPLOY_BATCH_NUM = 50;
|
||||
// 部署间隔
|
||||
private static final long DEPLOY_MIN_INTERVAL = 10 * 60 * 1000;
|
||||
// 时间格式
|
||||
private static final String TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
/**
|
||||
* 保存容器
|
||||
* @param request 容器保存请求
|
||||
*/
|
||||
public void save(SaveContainerInfoRequest request) {
|
||||
ContainerInfoDO container;
|
||||
Long originId = request.getId();
|
||||
if (originId != null) {
|
||||
container = containerInfoRepository.findById(originId).orElseThrow(() -> new IllegalArgumentException("can't find container by id: " + originId));
|
||||
}else {
|
||||
container = new ContainerInfoDO();
|
||||
container.setGmtCreate(new Date());
|
||||
}
|
||||
BeanUtils.copyProperties(request, container);
|
||||
container.setGmtModified(new Date());
|
||||
container.setSourceType(request.getSourceType().getV());
|
||||
container.setStatus(request.getStatus().getV());
|
||||
|
||||
// 文件上传形式的 sourceInfo 为该文件的 md5 值,Git形式的 md5 在部署阶段生成
|
||||
if (request.getSourceType() == ContainerSourceType.JarFile) {
|
||||
container.setMd5(request.getSourceInfo());
|
||||
}
|
||||
containerInfoRepository.saveAndFlush(container);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传用于部署的容器的 Jar 文件
|
||||
* @param file 接受的文件
|
||||
* @return 该文件的 md5 值
|
||||
* @throws IOException 异常
|
||||
*/
|
||||
public String uploadContainerJarFile(MultipartFile file) throws IOException {
|
||||
|
||||
String workerDirStr = OmsFileUtils.genTemporaryPath();
|
||||
String tmpFileStr = workerDirStr + "tmp.jar";
|
||||
|
||||
File workerDir = new File(workerDirStr);
|
||||
File tmpFile = new File(tmpFileStr);
|
||||
|
||||
try {
|
||||
// 下载到本地
|
||||
FileUtils.forceMkdirParent(tmpFile);
|
||||
file.transferTo(tmpFile);
|
||||
|
||||
// TODO:检验 jar 是否合法
|
||||
|
||||
// 生成MD5
|
||||
String md5 = OmsFileUtils.md5(tmpFile);
|
||||
String fileName = genContainerJarName(md5);
|
||||
|
||||
// 上传到 mongoDB
|
||||
if (gridFsTemplate != null) {
|
||||
OmsFileUtils.storeFile2GridFS(gridFsTemplate, tmpFile, fileName, null);
|
||||
}
|
||||
|
||||
// 将文件拷贝到正确的路径
|
||||
String finalFileStr = OmsFileUtils.genContainerJarPath() + fileName;
|
||||
File finalFile = new File(finalFileStr);
|
||||
FileUtils.forceDelete(finalFile);
|
||||
FileUtils.moveFile(tmpFile, finalFile);
|
||||
|
||||
return md5;
|
||||
|
||||
}finally {
|
||||
CommonUtils.executeIgnoreException(() -> FileUtils.forceDelete(workerDir));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取构建容器所需要的 Jar 文件
|
||||
@ -109,11 +188,29 @@ public class ContainerService {
|
||||
}
|
||||
ContainerInfoDO container = containerInfoOpt.get();
|
||||
|
||||
Date lastDeployTime = container.getLastDeployTime();
|
||||
if (lastDeployTime != null) {
|
||||
if ((System.currentTimeMillis() - lastDeployTime.getTime()) < DEPLOY_MIN_INTERVAL) {
|
||||
remote.sendText("SYSTEM: [warn] deploy too frequent, last deploy time is: " + DateFormatUtils.format(lastDeployTime, TIME_PATTERN));
|
||||
}
|
||||
}
|
||||
|
||||
// 准备文件
|
||||
File jarFile = prepareJarFile(container, session);
|
||||
if (jarFile == null) {
|
||||
remote.sendText("SYSTEM: prepare jarFile failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
double sizeMB = 1.0 * jarFile.length() / FileUtils.ONE_MB;
|
||||
remote.sendText(String.format("SYSTEM: the jarFile(size=%fMB) is prepared and ready to be deployed to the worker.", sizeMB));
|
||||
|
||||
// 修改数据库,更新 MD5和最新部署时间
|
||||
Date now = new Date();
|
||||
container.setGmtModified(now);
|
||||
container.setLastDeployTime(now);
|
||||
containerInfoRepository.saveAndFlush(container);
|
||||
|
||||
// 开始部署(需要分批进行)
|
||||
Set<String> workerAddressList = WorkerManagerService.getActiveWorkerInfo(container.getAppId()).keySet();
|
||||
if (workerAddressList.isEmpty()) {
|
||||
@ -156,67 +253,68 @@ public class ContainerService {
|
||||
File workerDir = new File(workerDirStr);
|
||||
FileUtils.forceMkdir(workerDir);
|
||||
|
||||
// git clone
|
||||
remote.sendText("SYSTEM: start to git clone the code repo, using config: " + container.getSourceInfo());
|
||||
GitRepoInfo gitRepoInfo = JsonUtils.parseObject(container.getSourceInfo(), GitRepoInfo.class);
|
||||
try {
|
||||
// git clone
|
||||
remote.sendText("SYSTEM: start to git clone the code repo, using config: " + container.getSourceInfo());
|
||||
GitRepoInfo gitRepoInfo = JsonUtils.parseObject(container.getSourceInfo(), GitRepoInfo.class);
|
||||
|
||||
CloneCommand cloneCommand = Git.cloneRepository()
|
||||
.setDirectory(workerDir)
|
||||
.setURI(gitRepoInfo.getRepo())
|
||||
.setBranch(gitRepoInfo.getBranch());
|
||||
if (!StringUtils.isEmpty(gitRepoInfo.getUsername())) {
|
||||
CredentialsProvider credentialsProvider = new UsernamePasswordCredentialsProvider(gitRepoInfo.getUsername(), gitRepoInfo.getPassword());
|
||||
cloneCommand.setCredentialsProvider(credentialsProvider);
|
||||
CloneCommand cloneCommand = Git.cloneRepository()
|
||||
.setDirectory(workerDir)
|
||||
.setURI(gitRepoInfo.getRepo())
|
||||
.setBranch(gitRepoInfo.getBranch());
|
||||
if (!StringUtils.isEmpty(gitRepoInfo.getUsername())) {
|
||||
CredentialsProvider credentialsProvider = new UsernamePasswordCredentialsProvider(gitRepoInfo.getUsername(), gitRepoInfo.getPassword());
|
||||
cloneCommand.setCredentialsProvider(credentialsProvider);
|
||||
}
|
||||
cloneCommand.call();
|
||||
|
||||
// mvn clean package -DskipTests -U
|
||||
remote.sendText("SYSTEM: git clone successfully, star to compile the project.");
|
||||
Invoker mvnInvoker = new DefaultInvoker();
|
||||
InvocationRequest ivkReq = new DefaultInvocationRequest();
|
||||
ivkReq.setGoals(Lists.newArrayList("clean", "package", "-DskipTests", "-U"));
|
||||
ivkReq.setBaseDirectory(workerDir);
|
||||
ivkReq.setOutputHandler(remote::sendText);
|
||||
|
||||
mvnInvoker.execute(ivkReq);
|
||||
|
||||
String targetDirStr = workerDirStr + "/target";
|
||||
File targetDir = new File(targetDirStr);
|
||||
IOFileFilter fileFilter = FileFilterUtils.asFileFilter((dir, name) -> name.endsWith("jar-with-dependencies.jar"));
|
||||
Collection<File> jarFile = FileUtils.listFiles(targetDir, fileFilter, null);
|
||||
|
||||
if (CollectionUtils.isEmpty(jarFile)) {
|
||||
remote.sendText("SYSTEM: can't find packaged jar(maybe maven build failed), so deploy failed.");
|
||||
return null;
|
||||
}
|
||||
|
||||
File jarWithDependency = jarFile.iterator().next();
|
||||
String md5 = OmsFileUtils.md5(jarWithDependency);
|
||||
// 更新 MD5
|
||||
container.setMd5(md5);
|
||||
|
||||
String jarFileName = genContainerJarName(md5);
|
||||
GridFsResource resource = gridFsTemplate.getResource(jarFileName);
|
||||
|
||||
if (!resource.exists()) {
|
||||
remote.sendText("SYSTEM: can't find the jar resource in remote, maybe this is a new version, start to upload new version.");
|
||||
OmsFileUtils.storeFile2GridFS(gridFsTemplate, jarWithDependency, jarFileName, null);
|
||||
remote.sendText("SYSTEM: upload to GridFS successfully~");
|
||||
}
|
||||
|
||||
// 将文件从临时工作目录移动到正式目录
|
||||
String localFileStr = OmsFileUtils.genContainerJarPath() + jarFileName;
|
||||
File localFile = new File(localFileStr);
|
||||
if (localFile.exists()) {
|
||||
FileUtils.forceDelete(localFile);
|
||||
}
|
||||
FileUtils.copyFile(jarWithDependency, localFile);
|
||||
|
||||
return localFile;
|
||||
}finally {
|
||||
// 删除工作区数据
|
||||
FileUtils.forceDelete(workerDir);
|
||||
}
|
||||
cloneCommand.call();
|
||||
|
||||
// mvn clean package -DskipTests -U
|
||||
remote.sendText("SYSTEM: git clone successfully, star to compile the project.");
|
||||
Invoker mvnInvoker = new DefaultInvoker();
|
||||
InvocationRequest ivkReq = new DefaultInvocationRequest();
|
||||
ivkReq.setGoals(Lists.newArrayList("clean", "package", "-DskipTests", "-U"));
|
||||
ivkReq.setBaseDirectory(workerDir);
|
||||
ivkReq.setOutputHandler(remote::sendText);
|
||||
|
||||
InvocationResult mvnResult = mvnInvoker.execute(ivkReq);
|
||||
if (mvnResult.getExitCode() != 0) {
|
||||
throw mvnResult.getExecutionException();
|
||||
}
|
||||
|
||||
String targetDirStr = workerDirStr + "/target";
|
||||
File targetDir = new File(targetDirStr);
|
||||
IOFileFilter fileFilter = FileFilterUtils.asFileFilter((dir, name) -> name.endsWith("jar-with-dependencies.jar"));
|
||||
Collection<File> jarFile = FileUtils.listFiles(targetDir, fileFilter, null);
|
||||
|
||||
if (CollectionUtils.isEmpty(jarFile)) {
|
||||
remote.sendText("SYSTEM: can't find packaged jar, deploy failed!");
|
||||
throw new RuntimeException("can't find packaged jar");
|
||||
}
|
||||
|
||||
File jarWithDependency = jarFile.iterator().next();
|
||||
String md5 = OmsFileUtils.md5(jarWithDependency);
|
||||
// 更新 MD5
|
||||
container.setMd5(md5);
|
||||
|
||||
String jarFileName = genContainerJarName(md5);
|
||||
GridFsResource resource = gridFsTemplate.getResource(jarFileName);
|
||||
|
||||
if (!resource.exists()) {
|
||||
remote.sendText("SYSTEM: can't find the jar resource in remote, maybe this is a new version, start to upload new version.");
|
||||
OmsFileUtils.storeFile2GridFS(gridFsTemplate, jarWithDependency, jarFileName, null);
|
||||
remote.sendText("SYSTEM: upload to GridFS successfully~");
|
||||
}
|
||||
|
||||
// 将文件从临时工作目录移动到正式目录
|
||||
String localFileStr = OmsFileUtils.genContainerJarPath() + jarFileName;
|
||||
File localFile = new File(localFileStr);
|
||||
FileUtils.forceDelete(localFile);
|
||||
FileUtils.copyFile(jarWithDependency, localFile);
|
||||
|
||||
// 删除工作区数据
|
||||
FileUtils.forceDelete(workerDir);
|
||||
|
||||
return localFile;
|
||||
}
|
||||
|
||||
// 先查询本地是否存在目标 Jar 文件
|
||||
@ -232,7 +330,7 @@ public class ContainerService {
|
||||
GridFsResource resource = gridFsTemplate.getResource(jarFileName);
|
||||
if (!resource.exists()) {
|
||||
remote.sendText(String.format("SYSTEM: can't find %s in local disk and GridFS, deploy failed!", jarFileName));
|
||||
throw new RuntimeException("can't find jar");
|
||||
return null;
|
||||
}
|
||||
remote.sendText("SYSTEM: start to download jar file from GridFS......");
|
||||
OmsFileUtils.gridFs2File(resource, localFile);
|
||||
|
@ -1,7 +1,6 @@
|
||||
package com.github.kfcfans.oms.server.web.controller;
|
||||
|
||||
import com.github.kfcfans.oms.common.response.ResultDTO;
|
||||
import com.github.kfcfans.oms.server.common.constans.ContainerSourceType;
|
||||
import com.github.kfcfans.oms.server.common.utils.ContainerTemplateGenerator;
|
||||
import com.github.kfcfans.oms.server.common.utils.OmsFileUtils;
|
||||
import com.github.kfcfans.oms.server.persistence.core.model.ContainerInfoDO;
|
||||
@ -12,21 +11,14 @@ import com.github.kfcfans.oms.server.web.request.SaveContainerInfoRequest;
|
||||
import com.github.kfcfans.oms.server.web.response.ContainerInfoVO;
|
||||
import com.google.common.collect.Lists;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
|
||||
import org.springframework.util.DigestUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.*;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@ -40,8 +32,6 @@ import java.util.stream.Collectors;
|
||||
@RequestMapping("/container")
|
||||
public class ContainerController {
|
||||
|
||||
private GridFsTemplate gridFsTemplate;
|
||||
|
||||
@Resource
|
||||
private ContainerInfoRepository containerInfoRepository;
|
||||
|
||||
@ -63,57 +53,16 @@ public class ContainerController {
|
||||
}
|
||||
|
||||
@PostMapping("/jarUpload")
|
||||
public ResultDTO<String> fileUpload(@RequestParam("file")MultipartFile file) throws Exception {
|
||||
public ResultDTO<String> fileUpload(@RequestParam("file") MultipartFile file) throws Exception {
|
||||
if (file == null || file.isEmpty()) {
|
||||
return ResultDTO.failed("empty file");
|
||||
}
|
||||
|
||||
// 1. 本地持久化
|
||||
String path = OmsFileUtils.genContainerJarPath();
|
||||
String tmpFileName = UUID.randomUUID().toString() + ".jar";
|
||||
tmpFileName = StringUtils.replace(tmpFileName, "-", "");
|
||||
File jarFile = new File(path + tmpFileName);
|
||||
FileUtils.forceMkdirParent(jarFile);
|
||||
|
||||
file.transferTo(jarFile);
|
||||
log.debug("[ContainerController] upload jarFile({}) to local disk success.", tmpFileName);
|
||||
|
||||
// 2. 检查是否符合标准(是否为Jar,是否符合 template)
|
||||
|
||||
// 3. 生成MD5
|
||||
String md5 = OmsFileUtils.md5(jarFile);
|
||||
|
||||
// 3. 推送到 mongoDB
|
||||
if (gridFsTemplate != null) {
|
||||
}
|
||||
|
||||
return ResultDTO.success(md5);
|
||||
return ResultDTO.success(containerService.uploadContainerJarFile(file));
|
||||
}
|
||||
|
||||
@PostMapping("/save")
|
||||
public ResultDTO<Void> saveContainer(@RequestBody SaveContainerInfoRequest request) {
|
||||
|
||||
ContainerInfoDO containerInfoDO;
|
||||
if (request.getId() == null) {
|
||||
containerInfoDO = new ContainerInfoDO();
|
||||
containerInfoDO.setGmtModified(new Date());
|
||||
}else {
|
||||
containerInfoDO = containerInfoRepository.findById(request.getId()).orElseThrow(() -> new IllegalArgumentException("can't find container by id: " + request.getId()));
|
||||
}
|
||||
BeanUtils.copyProperties(request, containerInfoDO);
|
||||
|
||||
containerInfoDO.setSourceType(request.getSourceType().getV());
|
||||
containerInfoDO.setStatus(request.getStatus().getV());
|
||||
containerInfoDO.setGmtCreate(new Date());
|
||||
|
||||
// git clone -> mvn clean package -> md5 生成文件名称
|
||||
if (request.getSourceType() == ContainerSourceType.Git) {
|
||||
|
||||
}else {
|
||||
containerInfoDO.setMd5(request.getSourceInfo());
|
||||
}
|
||||
|
||||
containerInfoRepository.saveAndFlush(containerInfoDO);
|
||||
containerService.save(request);
|
||||
return ResultDTO.success(null);
|
||||
}
|
||||
|
||||
@ -143,9 +92,4 @@ public class ContainerController {
|
||||
BeanUtils.copyProperties(containerInfoDO, vo);
|
||||
return vo;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setGridFsTemplate(GridFsTemplate gridFsTemplate) {
|
||||
this.gridFsTemplate = gridFsTemplate;
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ spring.datasource.core.hikari.maximum-pool-size=20
|
||||
spring.datasource.core.hikari.minimum-idle=5
|
||||
|
||||
####### mongoDB配置,非核心依赖,可移除 #######
|
||||
spring.data.mongodb.uri=mongodb://remotehost:27017/oms
|
||||
spring.data.mongodb.uri=mongodb://remotehost:27017/oms-daily
|
||||
|
||||
####### 邮件配置(启用邮件报警则需要) #######
|
||||
spring.mail.host=smtp.qq.com
|
||||
|
@ -10,7 +10,7 @@ spring.datasource.core.hikari.maximum-pool-size=20
|
||||
spring.datasource.core.hikari.minimum-idle=5
|
||||
|
||||
####### mongoDB配置,非核心依赖,可移除 #######
|
||||
spring.data.mongodb.uri=mongodb://remotehost:27017/oms
|
||||
spring.data.mongodb.uri=mongodb://remotehost:27017/oms-pre
|
||||
|
||||
####### 邮件配置(启用邮件报警则需要) #######
|
||||
spring.mail.host=smtp.qq.com
|
||||
|
@ -10,7 +10,7 @@ spring.datasource.core.hikari.maximum-pool-size=20
|
||||
spring.datasource.core.hikari.minimum-idle=5
|
||||
|
||||
####### mongoDB配置,非核心依赖,可移除 #######
|
||||
spring.data.mongodb.uri=mongodb://remotehost:27017/oms
|
||||
spring.data.mongodb.uri=mongodb://remotehost:27017/oms-product
|
||||
|
||||
####### 邮件配置(启用邮件报警则需要) #######
|
||||
spring.mail.host=smtp.qq.com
|
||||
|
Loading…
x
Reference in New Issue
Block a user