فهرست منبع

test: 后端部署流程

370774330@qq.com 5 سال پیش
والد
کامیت
b6a9c94e5b

+ 2 - 1
core/src/main/java/seecoder/devcloud/core/pipeline/PipelineException.java

@@ -17,7 +17,8 @@ public class PipelineException extends Exception{
     public static final String DEPLOYMENT_CREATE_ERROR = "K8S deployment 创建错误";
     public static final String NAMESPACE_CREATE_ERROR = "K8S namespace 创建错误";
     public static final String CONFIG_JSON_ERROR = "获取模板的config json错误";
-    public static final String DOCKERFILE_ERROR = "获取模板的dockerfile错误";
+    public static final String DOCKERFILE_MODIFY_ERROR = "修改模板的dockerfile错误";
+    public static final String POM_READ_ERROR = "获取pom中的jar name错误";
 
 
 

+ 1 - 1
core/src/main/java/seecoder/devcloud/core/pipeline/config/HandlerConfigTable.java

@@ -26,7 +26,7 @@ public class HandlerConfigTable {
      */
     static {
         handlerConfigMap = new HashMap<>();
-        handlerConfigMap.put("image-build", DockerImageBuildHandler.class);
+        handlerConfigMap.put("java-image-build", JavaImageBuildHandler.class);
         handlerConfigMap.put("k8s-deployment", K8sDeploymentHandler.class);
         handlerConfigMap.put("k8s-service", K8sServiceHandler.class);
         handlerConfigMap.put("k8s-ingress", K8sIngressHandler.class);

+ 59 - 15
core/src/main/java/seecoder/devcloud/core/pipeline/handler/DockerImageBuildHandler.java → core/src/main/java/seecoder/devcloud/core/pipeline/handler/JavaImageBuildHandler.java

@@ -7,6 +7,8 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang.StringUtils;
 import org.apache.http.HttpStatus;
 import org.eclipse.jgit.api.Git;
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
 import seecoder.devcloud.api.docker.DockerApi;
 import seecoder.devcloud.api.git.GitApi;
 import seecoder.devcloud.common.util.SpringUtil;
@@ -14,11 +16,13 @@ import seecoder.devcloud.core.pipeline.Context;
 import seecoder.devcloud.core.pipeline.PipelineException;
 import seecoder.devcloud.core.pipeline.template.PipelineTemplateTable;
 
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.File;
 import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
+import java.nio.file.*;
 import java.util.Date;
+import java.util.List;
 
 /**
  * @author PuHong Weng
@@ -26,15 +30,13 @@ import java.util.Date;
  * @description: docker镜像构建
  */
 @Slf4j
-public class DockerImageBuildHandler extends AbstractHandler {
-
-    private static final String TEMP_PREFIX = "SEECODER-DEVCLOUD-";
+public class JavaImageBuildHandler extends AbstractHandler {
 
     public final DockerApi dockerApi;
 
     public final GitApi gitApi;
 
-    public DockerImageBuildHandler() {
+    public JavaImageBuildHandler() {
         dockerApi = SpringUtil.getBean(DockerApi.class);
         gitApi = SpringUtil.getBean(GitApi.class);
     }
@@ -48,7 +50,7 @@ public class DockerImageBuildHandler extends AbstractHandler {
         String imageName = context.getNamespace() + "-" + context.getProjectName();
 
         // 1. 有git从git拿到地址仓库
-        Path directory = null;
+        Path directory;
         try {
             //注:temp文件/目录系统会定时删,实际文件名喂 prefix + 它生成的suffix随机数,所以不会命名冲突
             directory = Files.createTempDirectory("seecoder-devcloud-");
@@ -58,8 +60,7 @@ public class DockerImageBuildHandler extends AbstractHandler {
             context.appendErrorResult(PipelineException.GIT_CLONE_ERROR,e);
             throw new PipelineException(HttpStatus.SC_SERVICE_UNAVAILABLE, PipelineException.GIT_CLONE_ERROR,e);
         }
-        //2. 帮助学生创建dockerfile和maven的setting.xml文件
-        //todo 待测试创建非临时文件会不会有问题
+        //2. 帮助学生创建dockerfile和maven的setting.xml文件, 读取学生项目architect名字
         Path dockerfile = Paths.get(directory.toString(), "Dockerfile");
         Path mavenSetting = Paths.get(directory.toString(), "settings.xml");
         try {
@@ -70,7 +71,51 @@ public class DockerImageBuildHandler extends AbstractHandler {
             throw new PipelineException(HttpStatus.SC_SERVICE_UNAVAILABLE,PipelineException.DOCKERFILE_CREATE_ERROR,e);
         }
 
-        //3. 镜像构建
+        //3. 读取项目 groupId artifactId version, 替换dockerfile的project name
+        //todo 因为不知道学生构建的jar名,且dockerapi没有提供可以输入ARG参数的接口,逼不得已只能读取pom来获取jar包名。
+        // 这种默认的实现形式,必须保证需要运行的项目pom在根目录(单模块)
+        // pom的结构顺序没有经过人为调整,不奇葩(也就是groupId artifactId version按默认就在pom最前面)
+        // 使用xml 解析强行读取
+        // 待使用更好的实现
+        String artifactId = null;
+        String version = null;
+        File pom = Paths.get(directory.toString(),"pom.xml").toFile();
+        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+        DocumentBuilder dBuilder;
+        Document doc = null;
+        String jarName = context.getConfigs().getOrDefault("jarName",null);
+        if (jarName == null){
+            try {
+                dBuilder = dbFactory.newDocumentBuilder();
+                doc = dBuilder.parse(pom);
+                NodeList nodes = doc.getChildNodes().item(0).getChildNodes();
+                //按默认顺序 前二十个xml节点肯定有那三个量了
+                for (int i = 0; i < 20; i++){
+                    if ("artifactId".equals(nodes.item(i).getNodeName())) {
+                        artifactId = nodes.item(i).getTextContent();
+                    } else if ("version".equals(nodes.item(i).getNodeName())){
+                        version = nodes.item(i).getTextContent();
+                    }
+                }
+            } catch (Exception e) {
+                throw new PipelineException(HttpStatus.SC_SERVICE_UNAVAILABLE,PipelineException.POM_READ_ERROR,e);
+            }
+            jarName = artifactId+"-"+version;
+        }
+        //4. 修改dockerfile内部需要替换的内容
+        List<String> dockerfileLines = null;
+        try {
+            dockerfileLines = Files.readAllLines(dockerfile);
+            StringBuffer sb = new StringBuffer();
+            for (String line: dockerfileLines){
+                line = line.replace("-jar name-",jarName);
+                sb.append(line+"\n");
+            }
+            Files.write(dockerfile,sb.toString().getBytes(), StandardOpenOption.WRITE);
+        } catch (IOException e) {
+            throw new PipelineException(HttpStatus.SC_SERVICE_UNAVAILABLE,PipelineException.DOCKERFILE_MODIFY_ERROR,e);
+        }
+        //5. 镜像构建
         String imageTag = String.valueOf(new Date().getTime());
         StringBuilder sb = new StringBuilder();
         try {
@@ -78,14 +123,14 @@ public class DockerImageBuildHandler extends AbstractHandler {
                 @Override
                 public void progress(ProgressMessage message) throws DockerException {
                     String value = message.stream();
-                    System.out.println(value);
                     if (value == null) {
                         value = "";
                     }
                     if (!StringUtils.isEmpty(message.error())) {
                         value += "\n" + message.error();
+                        sb.append(value);
+                        System.out.println(sb.toString());
                     }
-                    sb.append(value);
                     context.setResult(context.getResult() + sb.toString());
                 }
             });
@@ -93,10 +138,9 @@ public class DockerImageBuildHandler extends AbstractHandler {
             context.appendErrorResult(PipelineException.IMAGE_BUILD_ERROR,e);
             throw new PipelineException(HttpStatus.SC_SERVICE_UNAVAILABLE, PipelineException.IMAGE_BUILD_ERROR ,e);
         }
-        //4. 将镜像名字加入context
+        //6. 将镜像名字加入context
         context.getConfigs().put("imageName", imageName);
         context.getConfigs().put("imageTag", imageTag);
-
         context.appendSuccessResult("镜像构建成功,已推送进仓库");
     }
 }

+ 1 - 1
core/src/main/java/seecoder/devcloud/core/pipeline/handler/K8sDeploymentHandler.java

@@ -66,7 +66,7 @@ public class K8sDeploymentHandler extends AbstractHandler {
 
         //2. 配置容器
         Container container = Container.builder()
-                .image(imageName+":"+imageTag)
+                .image(applicationProperties.getDocker().getRegistry()+"/"+imageName+":"+imageTag)
                 .name(projectName + K8sConstants.CONTAINER_SUFFIX)
                 .ports(Collections.singleton(containerPort))
                 .build();

+ 9 - 3
core/src/main/java/seecoder/devcloud/core/pipeline/handler/K8sIngressHandler.java

@@ -41,12 +41,11 @@ public class K8sIngressHandler extends AbstractHandler {
 
         //1. ingress路径规则
         Ingress.IngressRule ingressRule = new Ingress.IngressRule();
-        ingressRule.setHost(namespace + applicationProperties.getK8s().getIngressHostSuffix());
+        ingressRule.setHost( projectName+"."+namespace + applicationProperties.getK8s().getIngressHostSuffix());
         Ingress.IngressPath path = new Ingress.IngressPath();
-        path.setPath("/" + projectName + "/");
+        path.setPath("/");
         path.setPort(Integer.parseInt(servicePort));
         path.setServiceName(projectName+K8sConstants.SERVICE_SUFFIX);
-        path.setPort(Integer.parseInt(servicePort));
         ingressRule.setRuleValues(Collections.singleton(path));
 
         //2. 配置ingress
@@ -55,6 +54,13 @@ public class K8sIngressHandler extends AbstractHandler {
         ingress.setNamespace(namespace);
         ingress.setHttpRules(Collections.singletonList(ingressRule));
         ingress.setLabel(K8sConstants.APPLICATION_LABEL, projectName);
+        ingress.setAnnotation("kubernetes.io/ingress.class","nginx");
+        ingress.setAnnotation("kubernetes.io/ingress.provider","nginx");
+        ingress.setAnnotation("nginx.ingress.kubernetes.io/rewrite-target","/");
+        ingress.setAnnotation("nginx.ingress.kubernetes.io/proxy-body-size","512m");
+        ingress.setAnnotation("nginx.ingress.kubernetes.io/proxy-connect-timeout","15");
+        ingress.setAnnotation("nginx.ingress.kubernetes.io/proxy-read-timeout","600");
+        ingress.setAnnotation("nginx.ingress.kubernetes.io/service-upstream","true");
 
         //3. 创建
         try {

+ 1 - 0
core/src/main/resources/application-local.yml

@@ -35,6 +35,7 @@ seecoder:
   gitlab:
     host: http://gitlab.192.168.99.105.nip.io
     token: wXDeETv2Kmt-JhiApJ9H
+    webhook-proxy: test
   mail:
     from: 370774330@qq.com
   git:

+ 1 - 1
core/src/main/resources/docker/docker-config.json

@@ -1,6 +1,6 @@
 {
   "auths": {
-    "http://192.168.99.105:30005": {
+    "https://192.168.99.105:30060": {
       "username": "admin",
       "password": "admin"
     }

+ 4 - 4
core/src/main/resources/template/dockerfile/SPRINGBOOT-JAVA8

@@ -1,15 +1,15 @@
 # 用于为学生SPRINGBOOT-JAVA8项目打包成镜像的dockerfile
 FROM maven:3.6.1 AS compile_stage
-ARG PROJECT_NAME
-ENV WORK_PATH /opt/$PROJECT_NAME
+ENV WORK_PATH /opt/-jar name-
 COPY settings.xml /root/.m2/settings.xml
 COPY . $WORK_PATH
 RUN cd $WORK_PATH && mvn clean package -DskipTests
 
 FROM java:8-jre-alpine
-# ENV PROJECT_NAME seecoder-paas
+ENV PROJECT_NAME -jar name-
 ENV WORK_PATH /opt/$PROJECT_NAME
-RUN apk add --no-cache git
+# todo 本地测试环境运行不了 apk add 生产环境要改回来
+# RUN apk add --no-cache git
 WORKDIR /app
 COPY --from=compile_stage $WORK_PATH/target/${PROJECT_NAME}.jar .
 CMD ["sh", "-c", "java -Xdebug -Xrunjdwp:transport=dt_socket,address=8888,server=y,suspend=n -jar -server /app/${PROJECT_NAME}.jar"]

+ 85 - 11
core/src/test/java/seecoder/devcloud/core/CoreApplicationTests.java

@@ -3,12 +3,22 @@ package seecoder.devcloud.core;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.spotify.docker.client.DefaultDockerClient;
+import com.spotify.docker.client.DockerCertificates;
+import com.spotify.docker.client.DockerClient;
+import com.spotify.docker.client.DockerConfigReader;
+import com.spotify.docker.client.auth.ConfigFileRegistryAuthSupplier;
+import com.spotify.docker.client.auth.RegistryAuthSupplier;
+import com.spotify.docker.client.exceptions.DockerCertificateException;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.runner.RunWith;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.util.ResourceUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
 import seecoder.devcloud.api.ApplicationProperties;
 import seecoder.devcloud.api.k8s.DeploymentApi;
 import seecoder.devcloud.api.k8s.IngressApi;
@@ -18,14 +28,24 @@ import seecoder.devcloud.core.pipeline.PipelineException;
 import seecoder.devcloud.core.pipeline.config.HandlerConfig;
 import seecoder.devcloud.core.pipeline.handler.*;
 
-import java.util.*;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.net.URI;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 
 @RunWith(SpringRunner.class)
 @SpringBootTest
 class CoreApplicationTests {
 
     @Autowired
-    private ApplicationProperties applicationProperties;
+    private ApplicationProperties properties;
 
     @Autowired
     private DeploymentApi deploymentApi;
@@ -48,7 +68,9 @@ class CoreApplicationTests {
     void k8sDeployment() {
 
         K8sDeploymentHandler handler = new K8sDeploymentHandler();
-        context.getConfigs().put("imageName","mysql:5.7");
+        context.getConfigs().put("imageName","seecoder-devcloud-g-p");
+        context.getConfigs().put("imageTag","1616447743417");
+        context.getConfigs().put("port","8081");
         try {
             handler.process(context);
         } catch (PipelineException e) {
@@ -72,7 +94,7 @@ class CoreApplicationTests {
 
     @Test
     void k8sService() {
-        context.getConfigs().put("port","8000");
+        context.getConfigs().put("port","8081");
         K8sServiceHandler k8sServiceHandler = new K8sServiceHandler();
         try {
             k8sServiceHandler.process(context);
@@ -83,7 +105,7 @@ class CoreApplicationTests {
 
     @Test
     void k8sIngress() {
-        context.getConfigs().put("servicePort","8000");
+        context.getConfigs().put("servicePort","8081");
         K8sIngressHandler ingressHandler = new K8sIngressHandler();
         try {
             ingressHandler.process(context);
@@ -96,15 +118,41 @@ class CoreApplicationTests {
     void k8sImageBuild() {
         context.getConfigs().put("repoUrl","http://gitlab.192.168.99.105.nip.io/root/imagebuildetest.git");
         context.getConfigs().put("branchName", "master");
-        DockerImageBuildHandler dockerImageBuildHandler = new DockerImageBuildHandler();
+        JavaImageBuildHandler javaImageBuildHandler = new JavaImageBuildHandler();
 
         try {
-            dockerImageBuildHandler.process(context);
+            javaImageBuildHandler.process(context);
         } catch (PipelineException e) {
             e.printStackTrace();
         }
     }
 
+    @Test
+    void k8sImagePush() {
+        String registry = "192.168.99.105:30060";
+        String imageName = "seecoder-devcloud-g-p";
+        String imageTag = "1616399211969";
+        Path dockerConfigPath = null;
+        DockerClient client = null;
+        try {
+            dockerConfigPath = ResourceUtils.getFile("classpath:docker").toPath();
+            if (properties.getDocker().getTls()){
+                client = new DefaultDockerClient(URI.create(properties.getDocker().getHost()), new DockerCertificates(dockerConfigPath));
+            } else {
+                client = new DefaultDockerClient(properties.getDocker().getHost());
+            }
+        } catch (FileNotFoundException | DockerCertificateException e) {
+            e.printStackTrace();
+        }
+        try {
+            RegistryAuthSupplier registryAuthSupplier = new ConfigFileRegistryAuthSupplier(new DockerConfigReader(), Paths.get(dockerConfigPath.toString(),"docker-config.json"));
+            client.push(registry + "/" + imageName + ":" + imageTag, registryAuthSupplier.authFor("http://" + registry + "/" + imageName + ":" + imageTag));
+        } catch (Exception e){
+            e.printStackTrace();
+        }
+
+    }
+
     @Test
     void jsonTrans(){
         ObjectMapper mapper = new ObjectMapper();
@@ -136,13 +184,39 @@ class CoreApplicationTests {
         System.out.println();
     }
 
+    @Test
+    void pomReader(){
+
+        File fXmlFile = Paths.get("C:\\Users\\deponia\\AppData\\Local\\Temp\\seecoder-devcloud-956573437311297579","pom.xml").toFile();
+        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+        DocumentBuilder dBuilder = null;
+        Document doc = null;
+        try {
+            dBuilder = dbFactory.newDocumentBuilder();
+            doc = dBuilder.parse(fXmlFile);
+            doc.getDocumentElement().normalize();
+            NodeList nodes = doc.getChildNodes().item(0).getChildNodes();
+            for (int i = 0; i < 20; i++){
+                System.out.println("dododo: ");
+                System.out.println(nodes.item(i).getNodeName());
+                System.out.println("gogogo: ");
+                System.out.println(nodes.item(i).getNodeValue());
+                System.out.println(nodes.item(i).getTextContent());
+            }
+            System.out.println("Root element :" + doc.getDocumentElement().getNodeName());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+    }
+
     private void mockContext(){
         this.context = Context.builder()
-                .namespace("test-group")
-                .projectName("test-project")
-                .templateName("JAVA8")
+                .namespace("g")
+                .projectName("p")
+                .templateName("SPRINGBOOT-JAVA8")
                 .pipelineId(1)
-                .applicationProperties(applicationProperties)
+                .applicationProperties(properties)
                 .build();
     }
 }