Explorar el Código

refactor: 选中使用缓存时且没有新的commit时,使用上一次镜像

fanyanpeng hace 2 años
padre
commit
1a5a15f32b

+ 4 - 0
src/main/java/cn/seecoder/paas/data/entity/Environment.java

@@ -81,4 +81,8 @@ public class Environment {
     @Column(name = "build_status")
     private BuildStatus buildStatus = BuildStatus.NOT_STARTED;
 
+    @Basic
+    @Column(name = "last_success_commit_hash")
+    private String lastSuccessCommitHash;
+
 }

+ 1 - 1
src/main/java/cn/seecoder/paas/service/EnvironmentService.java

@@ -16,7 +16,7 @@ public interface EnvironmentService {
 
     List<EnvironmentVO> getListByAppId(Integer appId);
 
-    EnvironmentVO deploy(Integer id) throws ServiceException;
+    EnvironmentVO deploy(Integer id, Boolean useCache) throws ServiceException;
 
     Map<String,String> getLog(Integer id, String podName) throws ServiceException;
 

+ 2 - 2
src/main/java/cn/seecoder/paas/service/facade/docker/DockerApi.java

@@ -6,9 +6,9 @@ import java.util.List;
 
 public interface DockerApi {
 
-    void buildAndPush(String directory, String imageName, String imageTag, ProgressHandler handler) throws Exception;
+    void buildAndPush(String directory, String imageName, String imageTag, ProgressHandler handler, Boolean useCache) throws Exception;
 
-    void buildAndPush(String directory, String imageName, String imageTag) throws Exception;
+    void buildAndPush(String directory, String imageName, String imageTag, Boolean useCache) throws Exception;
 
     List<Image> listImage();
 

+ 10 - 4
src/main/java/cn/seecoder/paas/service/facade/docker/impl/DockerApiImpl.java

@@ -43,8 +43,12 @@ public class DockerApiImpl implements DockerApi {
     }
 
     @Override
-    public void buildAndPush(String directory, String imageName, String imageTag, ProgressHandler handler) throws Exception {
-        String imageId = client.build(new File(directory).toPath(), handler, DockerClient.BuildParam.name(registry + "/" + imageName + ":" + imageTag), DockerClient.BuildParam.forceRm());
+    public void buildAndPush(String directory, String imageName, String imageTag, ProgressHandler handler, Boolean useCache) throws Exception {
+        String imageId = client.build(new File(directory).toPath(),
+                handler,
+                DockerClient.BuildParam.name(registry + "/" + imageName + ":" + imageTag),
+                DockerClient.BuildParam.forceRm(),
+                DockerClient.BuildParam.create("no-cache", useCache ? "false" : "true"));
         if (imageId == null) {
             throw new IOException("Failed to build docker image under [" + directory + "]");
         }
@@ -52,10 +56,12 @@ public class DockerApiImpl implements DockerApi {
     }
 
     @Override
-    public void buildAndPush(String directory, String imageName, String imageTag) throws Exception {
-        buildAndPush(directory, imageName, imageTag, defaultHandler);
+    public void buildAndPush(String directory, String imageName, String imageTag, Boolean useCache) throws Exception {
+        buildAndPush(directory, imageName, imageTag, defaultHandler, useCache);
     }
 
+
+
     public List<Image> listImage() {
         List<Image> images = new ArrayList<>();
         try {

+ 133 - 74
src/main/java/cn/seecoder/paas/service/impl/EnvironmentServiceImpl.java

@@ -28,6 +28,9 @@ import io.kubernetes.client.openapi.models.*;
 import org.apache.commons.lang.StringUtils;
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.api.ResetCommand;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevCommit;
 import org.slf4j.Logger;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -210,7 +213,7 @@ public class EnvironmentServiceImpl implements EnvironmentService {
 
     @Override
     @Transactional
-    public EnvironmentVO deploy(Integer id) throws ServiceException {
+    public EnvironmentVO deploy(Integer id, Boolean useCache) throws ServiceException {
         Environment result = environmentDAO.findById(id).orElse(null);
         if (result == null) {
             throw ServiceException.BAD_REQUEST;
@@ -230,7 +233,7 @@ public class EnvironmentServiceImpl implements EnvironmentService {
             @Override
             public void afterCommit() {
                 super.afterCommit();
-                asyncWrapper.asyncInvoke(() -> buildAsync(id));
+                asyncWrapper.asyncInvoke(() -> buildAsync(id,useCache));
             }
         });
         return EnvironmentConverter.convertToVO(environmentDAO.save(result));
@@ -344,7 +347,7 @@ public class EnvironmentServiceImpl implements EnvironmentService {
         }
     }
 
-    void buildAsync(Integer id) {
+    void buildAsync(Integer id, Boolean useCache) {
         Environment result = environmentDAO.findById(id).get();
         try {
             Config config = configDAO.findByConfigBelongsAndAndEntityId(ConfigBelongsToType.ENVIRONMENT, id);
@@ -355,77 +358,12 @@ public class EnvironmentServiceImpl implements EnvironmentService {
             Map<String, String> labelsMap = Collections.singletonMap(labelKey, labelValue);
             String deploymentNamespace = applicationProperties.getDeploymentNamespace();
             String deploymentHost = applicationProperties.getDeploymentHost();
-            String imageName = result.getBuildTypeValue();
-            // 1. 有git从git拿到地址进行构建并push
-            if (result.getBuildType() == BuildType.FROM_BRANCH_OR_COMMIT) {
-                try {
-                    Path directory = Files.createTempDirectory("seecoder-paas-");
-                    Git git = gitApi.clone(application.getGitUrl(), directory.toString());
-                    try {
-                        // 尝试切换分支
-                        git.checkout().setCreateBranch(false).setName(result.getBuildTypeValue()).call();
-                    } catch (Exception gitException) {
-                        // 否则尝试commit
-                        git.reset().setMode(ResetCommand.ResetType.HARD).setRef(result.getBuildTypeValue()).call();
-                    }
-                    if (Files.exists(Paths.get(directory.toString(), "Dockerfile"))) {
-                        String imageTag = String.valueOf(new Date().getTime());
-                        StringBuilder sb = new StringBuilder();
-                        final int DEFAULT_BUFFER_LENGTH = 200;
-                        final long MAX_BUFFER_SIZE = 1 << 18;
-                        dockerApi.buildAndPush(directory.toString(), "seecoder-paas-" + labelValue, imageTag, new ProgressHandler() {
-                            @Override
-                            public void progress(ProgressMessage message) throws DockerException {
-                                String value = message.stream();
-                                if (value == null) {
-                                    value = "";
-                                }
-                                if (!StringUtils.isEmpty(message.error())) {
-                                    value += "\n" + message.error();
-                                }
-                                sb.append(value);
-                                if (sb.length() > DEFAULT_BUFFER_LENGTH) {
-                                    result.setBuildOutput(result.getBuildOutput() + sb.toString());
-                                    if (result.getBuildOutput().length() > MAX_BUFFER_SIZE) {
-                                        result.setBuildOutput(result.getBuildOutput().substring((int) (result.getBuildOutput().length() - MAX_BUFFER_SIZE)));
-                                    }
-                                    sb.setLength(0);
-                                    environmentDAO.save(result);
-                                }
-                            }
-                        });
-                        if (sb.length() > 0) {
-                            result.setBuildOutput(result.getBuildOutput() + sb.toString());
-                        }
-                        imageName = applicationProperties.getK8s().getImageRegistry() + "/seecoder-paas-" + labelValue + ":" + imageTag;
-                        result.setBuildStatus(BuildStatus.SUCCESS);
-                        result.setDeployStatus(DeployStatus.DEPLOYING);
-                        result.setDeployStartTime(LocalDateTime.now());
-                        result.setImage(imageName);
-                        environmentDAO.save(result);
-                    } else {
-                        result.setBuildStatus(BuildStatus.FAIL);
-                        result.setBuildOutput("找不到构建文件Dockerfile!");
-                        environmentDAO.save(result);
-                        throw new ServiceException("102", "找不到构建文件Dockerfile!");
-                    }
-                } catch (Exception e) {
-                    result.setBuildStatus(BuildStatus.FAIL);
-                    if (!e.getLocalizedMessage().contains("Could not acquire image ID or digest following build")) {
-                        result.setBuildOutput(e.getLocalizedMessage());
-                    }
-                    environmentDAO.save(result);
-                    LoggerUtil.error(logger, e, "构建失败!gitUrl={}, branchOrCommit={}", application.getGitUrl(), result.getBuildTypeValue());
-                    return;
-                }
-            } else if (result.getBuildType() == BuildType.FROM_IMAGE) {
-                result.setBuildStatus(BuildStatus.SUCCESS);
-                result.setBuildOutput("镜像构建无需部署");
-                result.setDeployStatus(DeployStatus.DEPLOYING);
-                result.setDeployStartTime(LocalDateTime.now());
-                result.setImage(imageName);
-                environmentDAO.save(result);
-            }
+
+            // 构建镜像阶段
+            buildImage(result,useCache, labelValue);
+
+            // 读取构建之后的镜像名称
+            String imageName = result.getImage();
 
             String[] ports = (configContent.getServicePort() == null ? "" : configContent.getServicePort()).split(",");
             if (!StringUtils.isEmpty(configContent.getHostPrefix())) {
@@ -614,4 +552,125 @@ public class EnvironmentServiceImpl implements EnvironmentService {
             return;
         }
     }
+    //获取当前git语境下的最后一次commit记录。
+    private static String getLatestCommitHash(Git git) throws GitAPIException {
+        Iterable<RevCommit> commits = git.log().setMaxCount(1).call();
+        return commits.iterator().next().getId().getName();
+    }
+
+    // 若判断是否命中hash,若命中hash,则不需要重新构建
+    private Boolean useLastImage(Environment result, String lastCommitHash) throws GitAPIException {
+        if(lastCommitHash.equals(result.getLastSuccessCommitHash())){
+            logger.info("commitId: "+lastCommitHash+" 与上次构建成功的commitId相同,无需构建");
+            result.setBuildOutput("commitId: "+lastCommitHash+" 与上次构建成功的commitId相同,无需构建");
+
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     *
+     * @author   fanyanpeng
+     * @date 2023/11/14 11:34
+     * @param result 环境
+     * @param useCache 是否使用缓存
+     * @param labelValue 标签值
+     * @return void
+     */
+    private void buildImage(Environment result, Boolean useCache, String labelValue) {
+        Application application = applicationDAO.findById(result.getAppId()).get();
+        String imageName = "";
+//         1. 有git从git拿到地址进行构建并push
+        if (result.getBuildType() == BuildType.FROM_BRANCH_OR_COMMIT) {
+            try {
+                Path directory = Files.createTempDirectory("seecoder-paas-");
+                Git git = gitApi.clone(application.getGitUrl(), directory.toString());
+                try {
+                    // 尝试切换分支
+                    git.checkout().setCreateBranch(false).setName(result.getBuildTypeValue()).call();
+                } catch (Exception gitException) {
+                    // 执行硬重置(hard reset)到指定分支 XXX否则尝试commitXXX
+                    git.reset().setMode(ResetCommand.ResetType.HARD).setRef(result.getBuildTypeValue()).call();
+                }
+
+                // 获取最新的commitId
+                String currentCommitHash = getLatestCommitHash(git);
+
+                // 若成功使用上一次的镜像,就不重新构建了
+                if(useCache && useLastImage(result, currentCommitHash)){
+
+                    result.setBuildStatus(BuildStatus.SUCCESS);
+                    result.setDeployStatus(DeployStatus.DEPLOYING);
+                    result.setDeployStartTime(LocalDateTime.now());
+                    // imageName 无需修改
+                    environmentDAO.save(result);
+                    return;
+                }
+
+
+                if (Files.exists(Paths.get(directory.toString(), "Dockerfile"))) {
+                    String imageTag = String.valueOf(new Date().getTime());
+                    StringBuilder sb = new StringBuilder();
+                    final int DEFAULT_BUFFER_LENGTH = 200;
+                    final long MAX_BUFFER_SIZE = 1 << 18;
+                    dockerApi.buildAndPush(directory.toString(), "seecoder-paas-" + labelValue, imageTag, new ProgressHandler() {
+                        @Override
+                        public void progress(ProgressMessage message) throws DockerException {
+                            String value = message.stream();
+                            if (value == null) {
+                                value = "";
+                            }
+                            if (!StringUtils.isEmpty(message.error())) {
+                                value += "\n" + message.error();
+                            }
+                            sb.append(value);
+                            if (sb.length() > DEFAULT_BUFFER_LENGTH) {
+                                result.setBuildOutput(result.getBuildOutput() + sb.toString());
+                                if (result.getBuildOutput().length() > MAX_BUFFER_SIZE) {
+                                    result.setBuildOutput(result.getBuildOutput().substring((int) (result.getBuildOutput().length() - MAX_BUFFER_SIZE)));
+                                }
+                                sb.setLength(0);
+                                environmentDAO.save(result);
+                            }
+                        }
+                    },useCache);
+                    if (sb.length() > 0) {
+                        result.setBuildOutput(result.getBuildOutput() + sb.toString());
+                    }
+                    imageName = applicationProperties.getK8s().getImageRegistry() + "/seecoder-paas-" + labelValue + ":" + imageTag;
+                    result.setBuildStatus(BuildStatus.SUCCESS);
+                    result.setDeployStatus(DeployStatus.DEPLOYING);
+                    result.setDeployStartTime(LocalDateTime.now());
+                    result.setImage(imageName);
+                    result.setLastSuccessCommitHash(currentCommitHash);
+                    environmentDAO.save(result);
+                } else {
+                    result.setBuildStatus(BuildStatus.FAIL);
+                    result.setBuildOutput("找不到构建文件Dockerfile!");
+                    environmentDAO.save(result);
+                    throw new ServiceException("102", "找不到构建文件Dockerfile!");
+                }
+            } catch (Exception e) {
+                result.setBuildStatus(BuildStatus.FAIL);
+                if (!e.getLocalizedMessage().contains("Could not acquire image ID or digest following build")) {
+                    result.setBuildOutput(e.getLocalizedMessage());
+                }
+                environmentDAO.save(result);
+                LoggerUtil.error(logger, e, "构建失败!gitUrl={}, branchOrCommit={}", application.getGitUrl(), result.getBuildTypeValue());
+
+
+            }
+        } else if (result.getBuildType() == BuildType.FROM_IMAGE) {
+            imageName = result.getBuildTypeValue();
+            result.setBuildStatus(BuildStatus.SUCCESS);
+            result.setBuildOutput("镜像构建无需部署");
+            result.setDeployStatus(DeployStatus.DEPLOYING);
+            result.setDeployStartTime(LocalDateTime.now());
+            result.setImage(imageName);
+            environmentDAO.save(result);
+        }
+
+
+    }
 }

+ 3 - 2
src/main/java/cn/seecoder/paas/web/controller/api/EnvironmentController.java

@@ -55,11 +55,12 @@ public class EnvironmentController {
     }
 
     @PostMapping("/deploy")
-    public Response deploy(@RequestParam Integer id) throws ServiceException {
+    public Response deploy(@RequestParam Integer id, @RequestParam(required = false, defaultValue = "true") Boolean useCache) throws ServiceException {
         return Response.buildSuccess(
-                environmentService.deploy(id));
+                environmentService.deploy(id,useCache));
     }
 
+
     @PostMapping("/restart")
     public Response deploy(@RequestParam Integer id, @RequestParam String podName) throws ServiceException {
         return Response.buildSuccess(