|
@@ -1,6 +1,9 @@
|
|
|
package cn.seecoder.web.core.pipeline.handler;
|
|
package cn.seecoder.web.core.pipeline.handler;
|
|
|
|
|
|
|
|
import cn.seecoder.api.ApplicationProperties;
|
|
import cn.seecoder.api.ApplicationProperties;
|
|
|
|
|
+import cn.seecoder.api.docker.DockerApi;
|
|
|
|
|
+import cn.seecoder.api.git.GitApi;
|
|
|
|
|
+import cn.seecoder.common.util.SpringUtil;
|
|
|
import cn.seecoder.web.core.pipeline.Context;
|
|
import cn.seecoder.web.core.pipeline.Context;
|
|
|
import cn.seecoder.web.core.pipeline.PipelineException;
|
|
import cn.seecoder.web.core.pipeline.PipelineException;
|
|
|
import cn.seecoder.web.core.pipeline.template.PipelineTemplateTable;
|
|
import cn.seecoder.web.core.pipeline.template.PipelineTemplateTable;
|
|
@@ -13,11 +16,6 @@ import lombok.extern.slf4j.Slf4j;
|
|
|
import org.apache.commons.lang.StringUtils;
|
|
import org.apache.commons.lang.StringUtils;
|
|
|
import org.apache.http.HttpStatus;
|
|
import org.apache.http.HttpStatus;
|
|
|
import org.eclipse.jgit.api.Git;
|
|
import org.eclipse.jgit.api.Git;
|
|
|
-import org.eclipse.jgit.lib.Ref;
|
|
|
|
|
-
|
|
|
|
|
-import cn.seecoder.api.docker.DockerApi;
|
|
|
|
|
-import cn.seecoder.api.git.GitApi;
|
|
|
|
|
-import cn.seecoder.common.util.SpringUtil;
|
|
|
|
|
import org.springframework.web.util.UriComponentsBuilder;
|
|
import org.springframework.web.util.UriComponentsBuilder;
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
import java.io.IOException;
|
|
@@ -29,10 +27,14 @@ import java.util.Date;
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * @author PuHong Weng
|
|
|
|
|
|
|
+ * Docker 镜像构建处理器
|
|
|
|
|
+ * <p>
|
|
|
|
|
+ * 构建 Java 质量检查镜像(基于 Maven),并推送至镜像仓库。
|
|
|
|
|
+ * 如需支持其他语言,则使用 SonarQube 官方镜像,在其中运行对应的 Client 命令。
|
|
|
|
|
+ * </p>
|
|
|
|
|
+ *
|
|
|
|
|
+ * @author
|
|
|
* @date 2021/1/18
|
|
* @date 2021/1/18
|
|
|
- * @description: docker镜像构建。构建Java版的质量检查镜像,需要maven作为base,直接在上面运行maven的命令即可完成检查。如果需要构建其他语言
|
|
|
|
|
- * 的检查镜像,需要使用 SonarQube 官方提供的带有可执行 client 的镜像,在其中运行 client 的检查命令。后续步骤都是一致的。
|
|
|
|
|
*/
|
|
*/
|
|
|
@Slf4j
|
|
@Slf4j
|
|
|
@Data
|
|
@Data
|
|
@@ -40,43 +42,47 @@ import java.util.List;
|
|
|
public class JavaImageBuildHandler extends AbstractHandler {
|
|
public class JavaImageBuildHandler extends AbstractHandler {
|
|
|
|
|
|
|
|
private final DockerApi dockerApi;
|
|
private final DockerApi dockerApi;
|
|
|
-
|
|
|
|
|
private final GitApi gitApi;
|
|
private final GitApi gitApi;
|
|
|
-
|
|
|
|
|
private final ApplicationProperties applicationProperties;
|
|
private final ApplicationProperties applicationProperties;
|
|
|
|
|
|
|
|
- public JavaImageBuildHandler() {
|
|
|
|
|
- dockerApi = SpringUtil.getBean(DockerApi.class);
|
|
|
|
|
- gitApi = SpringUtil.getBean(GitApi.class);
|
|
|
|
|
- this.applicationProperties = SpringUtil.getBean(ApplicationProperties.class);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
private String repoUrl;
|
|
private String repoUrl;
|
|
|
-
|
|
|
|
|
private String branchName;
|
|
private String branchName;
|
|
|
-
|
|
|
|
|
private String jarName;
|
|
private String jarName;
|
|
|
|
|
|
|
|
|
|
+ public JavaImageBuildHandler() {
|
|
|
|
|
+ this.dockerApi = SpringUtil.getBean(DockerApi.class);
|
|
|
|
|
+ this.gitApi = SpringUtil.getBean(GitApi.class);
|
|
|
|
|
+ this.applicationProperties = SpringUtil.getBean(ApplicationProperties.class);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
@Override
|
|
@Override
|
|
|
public void process(Context context) throws PipelineException {
|
|
public void process(Context context) throws PipelineException {
|
|
|
- //0. 变量准备
|
|
|
|
|
|
|
+ // 0. 变量准备
|
|
|
String templateName = PipelineTemplateTable.SPRINGBOOT_JAVA8;
|
|
String templateName = PipelineTemplateTable.SPRINGBOOT_JAVA8;
|
|
|
String imageName = context.getNamespace() + "-" + context.getDeployName();
|
|
String imageName = context.getNamespace() + "-" + context.getDeployName();
|
|
|
|
|
|
|
|
- // 1. 有git从git拿到地址仓库
|
|
|
|
|
|
|
+ // 1. 克隆 Git 仓库
|
|
|
Path directory;
|
|
Path directory;
|
|
|
try {
|
|
try {
|
|
|
- //注:temp文件/目录系统会定时删,实际文件名喂 prefix + 它生成的suffix随机数,所以不会命名冲突
|
|
|
|
|
|
|
+ // 注:temp 文件/目录系统会定时删,文件名为 prefix + 随机 suffix,不会冲突
|
|
|
directory = Files.createTempDirectory("seecoder-devcloud-");
|
|
directory = Files.createTempDirectory("seecoder-devcloud-");
|
|
|
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(repoUrl);
|
|
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(repoUrl);
|
|
|
- repoUrl = builder.scheme("http").host(applicationProperties.getGitlab().getUrl().replace("http://","")).path(".git/").toUriString();
|
|
|
|
|
|
|
+ repoUrl = builder.scheme("http")
|
|
|
|
|
+ .host(applicationProperties.getGitlab().getUrl().replace("http://", ""))
|
|
|
|
|
+ .path(".git/")
|
|
|
|
|
+ .toUriString();
|
|
|
Git git = gitApi.clone(repoUrl, directory.toString());
|
|
Git git = gitApi.clone(repoUrl, directory.toString());
|
|
|
- git.checkout().setCreateBranch(false).setStartPoint("origin/" + branchName).setName("origin/" + branchName).call();
|
|
|
|
|
|
|
+ git.checkout()
|
|
|
|
|
+ .setCreateBranch(false)
|
|
|
|
|
+ .setStartPoint("origin/" + branchName)
|
|
|
|
|
+ .setName("origin/" + branchName)
|
|
|
|
|
+ .call();
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
context.appendErrorResult(PipelineException.GIT_CLONE_ERROR, e);
|
|
context.appendErrorResult(PipelineException.GIT_CLONE_ERROR, e);
|
|
|
throw new PipelineException(HttpStatus.SC_SERVICE_UNAVAILABLE, PipelineException.GIT_CLONE_ERROR, e);
|
|
throw new PipelineException(HttpStatus.SC_SERVICE_UNAVAILABLE, PipelineException.GIT_CLONE_ERROR, e);
|
|
|
}
|
|
}
|
|
|
- //2. 帮助学生创建dockerfile和maven的setting.xml文件, 读取学生项目architect名字
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 创建 Dockerfile 和 Maven settings.xml
|
|
|
Path dockerfile = Paths.get(directory.toString(), "Dockerfile");
|
|
Path dockerfile = Paths.get(directory.toString(), "Dockerfile");
|
|
|
Path mavenSetting = Paths.get(directory.toString(), "settings.xml");
|
|
Path mavenSetting = Paths.get(directory.toString(), "settings.xml");
|
|
|
try {
|
|
try {
|
|
@@ -87,54 +93,52 @@ public class JavaImageBuildHandler extends AbstractHandler {
|
|
|
throw new PipelineException(HttpStatus.SC_SERVICE_UNAVAILABLE, PipelineException.DOCKERFILE_CREATE_ERROR, e);
|
|
throw new PipelineException(HttpStatus.SC_SERVICE_UNAVAILABLE, PipelineException.DOCKERFILE_CREATE_ERROR, e);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- //3. 修改dockerfile内部需要替换的内容
|
|
|
|
|
- List<String> dockerfileLines = null;
|
|
|
|
|
|
|
+ // 3. 修改 Dockerfile 内容
|
|
|
try {
|
|
try {
|
|
|
- dockerfileLines = Files.readAllLines(dockerfile);
|
|
|
|
|
- StringBuffer sb = new StringBuffer();
|
|
|
|
|
|
|
+ List<String> dockerfileLines = Files.readAllLines(dockerfile);
|
|
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
for (String line : dockerfileLines) {
|
|
for (String line : dockerfileLines) {
|
|
|
line = line.replace("-jar name-", jarName);
|
|
line = line.replace("-jar name-", jarName);
|
|
|
- sb.append(line + "\n");
|
|
|
|
|
|
|
+ sb.append(line).append("\n");
|
|
|
}
|
|
}
|
|
|
Files.write(dockerfile, sb.toString().getBytes(), StandardOpenOption.WRITE);
|
|
Files.write(dockerfile, sb.toString().getBytes(), StandardOpenOption.WRITE);
|
|
|
} catch (IOException e) {
|
|
} catch (IOException e) {
|
|
|
throw new PipelineException(HttpStatus.SC_SERVICE_UNAVAILABLE, PipelineException.DOCKERFILE_MODIFY_ERROR, e);
|
|
throw new PipelineException(HttpStatus.SC_SERVICE_UNAVAILABLE, PipelineException.DOCKERFILE_MODIFY_ERROR, e);
|
|
|
}
|
|
}
|
|
|
- //4. 镜像构建
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 4. 构建镜像并推送
|
|
|
String imageTag = String.valueOf(new Date().getTime());
|
|
String imageTag = String.valueOf(new Date().getTime());
|
|
|
StringBuilder sb = new StringBuilder();
|
|
StringBuilder sb = new StringBuilder();
|
|
|
final int DEFAULT_BUFFER_LENGTH = 200;
|
|
final int DEFAULT_BUFFER_LENGTH = 200;
|
|
|
final long MAX_BUFFER_SIZE = 1 << 18;
|
|
final long MAX_BUFFER_SIZE = 1 << 18;
|
|
|
try {
|
|
try {
|
|
|
- dockerApi.buildAndPush(directory.toString(), imageName, 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) {
|
|
|
|
|
- context.appendResult(sb.toString());
|
|
|
|
|
- if (context.getResult().length() > MAX_BUFFER_SIZE) {
|
|
|
|
|
- context.setResult(context.getResult().substring((int) (context.getResult().length() - MAX_BUFFER_SIZE)));
|
|
|
|
|
- }
|
|
|
|
|
- sb.setLength(0);
|
|
|
|
|
|
|
+ dockerApi.buildAndPush(directory.toString(), imageName, imageTag, message -> {
|
|
|
|
|
+ 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) {
|
|
|
|
|
+ context.appendResult(sb.toString());
|
|
|
|
|
+ // 构建流程的记录过长时,截断前面的部分,保留后 MAX_BUFFER_SIZE 字符
|
|
|
|
|
+ if (context.getResult().length() > MAX_BUFFER_SIZE) {
|
|
|
|
|
+ context.setResult(context.getResult().substring((int) (context.getResult().length() - MAX_BUFFER_SIZE)));
|
|
|
}
|
|
}
|
|
|
|
|
+ sb.setLength(0);
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
- log.error("镜像构建失败,项目目录为:{}", directory.toString());
|
|
|
|
|
|
|
+ log.error("镜像构建失败,项目目录为:{}", directory);
|
|
|
context.appendErrorResult(PipelineException.IMAGE_BUILD_ERROR, e);
|
|
context.appendErrorResult(PipelineException.IMAGE_BUILD_ERROR, e);
|
|
|
throw new PipelineException(HttpStatus.SC_SERVICE_UNAVAILABLE, PipelineException.IMAGE_BUILD_ERROR, e);
|
|
throw new PipelineException(HttpStatus.SC_SERVICE_UNAVAILABLE, PipelineException.IMAGE_BUILD_ERROR, e);
|
|
|
}
|
|
}
|
|
|
- //5. 将镜像名字加入context
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 5. 将镜像信息写入 Context
|
|
|
context.getConfigs().put("imageName", imageName);
|
|
context.getConfigs().put("imageName", imageName);
|
|
|
context.getConfigs().put("imageTag", imageTag);
|
|
context.getConfigs().put("imageTag", imageTag);
|
|
|
context.appendSuccessResult("镜像构建成功,已推送进仓库");
|
|
context.appendSuccessResult("镜像构建成功,已推送进仓库");
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|