[dev] finished partly of deploy websocket & don't forget to fix the md5 bug tomorrow

This commit is contained in:
tjq 2020-05-17 23:19:11 +08:00
parent 7247f41199
commit c18658003e
6 changed files with 166 additions and 124 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View File

@ -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