|
|
@@ -0,0 +1,176 @@
|
|
|
+package cn.seecoder.web.core.pipeline.handler;
|
|
|
+
|
|
|
+
|
|
|
+import cn.seecoder.api.ApplicationProperties;
|
|
|
+import cn.seecoder.api.k8s.*;
|
|
|
+import cn.seecoder.api.k8s.exception.K8sApiException;
|
|
|
+import cn.seecoder.api.k8s.model.*;
|
|
|
+import cn.seecoder.api.k8s.vo.SecretVO;
|
|
|
+import cn.seecoder.common.util.SpringUtil;
|
|
|
+import cn.seecoder.web.core.pipeline.Context;
|
|
|
+import cn.seecoder.web.core.pipeline.PipelineException;
|
|
|
+import lombok.Data;
|
|
|
+import lombok.EqualsAndHashCode;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.http.HttpStatus;
|
|
|
+
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @author PuHong Weng
|
|
|
+ * @date 2021/6/13
|
|
|
+ * @description:
|
|
|
+ * devcloud Mysql部署
|
|
|
+ * 目前seecoder平台无法暴露tcp端口,所以只部署service和deployment供集群使用
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Data
|
|
|
+@EqualsAndHashCode(callSuper = false)
|
|
|
+public class MysqlDeployHandler extends AbstractHandler{
|
|
|
+
|
|
|
+ private final SecretApi secretApi;
|
|
|
+
|
|
|
+ private final DeploymentApi deploymentApi;
|
|
|
+
|
|
|
+ private final ApplicationProperties applicationProperties;
|
|
|
+
|
|
|
+ private final NamespaceApi namespaceApi;
|
|
|
+
|
|
|
+ private final ServiceApi serviceApi;
|
|
|
+
|
|
|
+ private final static String DEFAULT_IMAGE_PULL_SECRET_NAME = "seecoder-devcloud-image-pull-secret";
|
|
|
+
|
|
|
+ public MysqlDeployHandler(){
|
|
|
+ this.applicationProperties = SpringUtil.getBean(ApplicationProperties.class);
|
|
|
+ secretApi = SpringUtil.getBean(SecretApi.class);
|
|
|
+ deploymentApi = SpringUtil.getBean(DeploymentApi.class);
|
|
|
+ namespaceApi = SpringUtil.getBean(NamespaceApi.class);
|
|
|
+ serviceApi = SpringUtil.getBean(ServiceApi.class);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String version = "5.7";
|
|
|
+
|
|
|
+ private Integer port = 3306;
|
|
|
+
|
|
|
+ private String password = "root";
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void process(Context context) throws PipelineException {
|
|
|
+
|
|
|
+ //1. 创建namespace
|
|
|
+ Namespace k8sNamespace = new Namespace();
|
|
|
+ k8sNamespace.setName(context.getNamespace());
|
|
|
+ //todo namespace在底下资源未删除时,不能轻易删除重建,只能直接通过异常查是否已创建,丑陋,待修改
|
|
|
+ try {
|
|
|
+ List<Namespace> namespaces = namespaceApi.getByCondition(K8sObjectRequest.builder()
|
|
|
+ .name(context.getNamespace()).build());
|
|
|
+ if (namespaces.size() == 0) {
|
|
|
+ namespaceApi.create(k8sNamespace);
|
|
|
+ log.info("Namespace创建成功: [namespace: " + context.getNamespace() + ", deployName: " + context.getDeployName() + "]");
|
|
|
+ } else {
|
|
|
+ log.info("Namespace已经存在, 无需新建: [namespace: " + context.getNamespace() + ", deployName: " + context.getDeployName() + "]");
|
|
|
+ }
|
|
|
+ } catch (K8sApiException e) {
|
|
|
+ context.appendErrorResult(PipelineException.NAMESPACE_CREATE_ERROR, e);
|
|
|
+ throw new PipelineException(HttpStatus.SC_SERVICE_UNAVAILABLE, PipelineException.NAMESPACE_CREATE_ERROR, e);
|
|
|
+ }
|
|
|
+ context.appendSuccessResult("K8s Namespace创建成功: [namespace: " + context.getNamespace() + ", deployName: " + context.getDeployName() + "]");
|
|
|
+
|
|
|
+
|
|
|
+ //2. 创建deployment
|
|
|
+ //2.0. 镜像配置信息获取
|
|
|
+ String imageName = "mysql";
|
|
|
+ String imageTag = version;
|
|
|
+ int replicas = 1;
|
|
|
+ String namespace = context.getNamespace();
|
|
|
+ String deployName = context.getDeployName();
|
|
|
+
|
|
|
+ //2.1. 配置容器暴露端口
|
|
|
+ ContainerPort containerPort = ContainerPort.builder()
|
|
|
+ .port(port)
|
|
|
+ .build();
|
|
|
+
|
|
|
+ //2.2. 配置容器
|
|
|
+ Container container = Container.builder()
|
|
|
+ .image(imageName + ":" + imageTag)
|
|
|
+ .name(deployName + K8sConstants.CONTAINER_SUFFIX)
|
|
|
+ .ports(Collections.singleton(containerPort))
|
|
|
+ .env(Collections.singletonMap("MYSQL_ROOT_PASSWORD", password))
|
|
|
+ .build();
|
|
|
+
|
|
|
+ //2.3. 配置deployment
|
|
|
+ Deployment deployment = Deployment.builder()
|
|
|
+ .replicas(replicas)
|
|
|
+ .containers(Collections.singletonList(container))
|
|
|
+ .build();
|
|
|
+ deployment.setName(deployName + K8sConstants.DEPLOYMENT_SUFFIX);
|
|
|
+ deployment.setNamespace(namespace);
|
|
|
+ deployment.setLabel(K8sConstants.APPLICATION_LABEL, deployName);
|
|
|
+
|
|
|
+ //2.4. 检查镜像仓库secret是否存在,没有则创建。
|
|
|
+ SecretVO secretVO = secretApi.getSecretByName(namespace, DEFAULT_IMAGE_PULL_SECRET_NAME);
|
|
|
+ if (secretVO == null) {
|
|
|
+ secretApi.createPrivateRegistrySecret(
|
|
|
+ namespace,
|
|
|
+ DEFAULT_IMAGE_PULL_SECRET_NAME,
|
|
|
+ applicationProperties.getDocker().getRegistry(),
|
|
|
+ applicationProperties.getDocker().getRegistryUsername(),
|
|
|
+ applicationProperties.getDocker().getRegistryPassword()
|
|
|
+ );
|
|
|
+ }
|
|
|
+ deployment.setImagePullSecrets(Collections.singletonList(DEFAULT_IMAGE_PULL_SECRET_NAME));
|
|
|
+
|
|
|
+
|
|
|
+ //2.5. 创建Deployment实例
|
|
|
+ try {
|
|
|
+ List<Deployment> deployments = deploymentApi.getByCondition(K8sObjectRequest.builder()
|
|
|
+ .namespace(context.getNamespace())
|
|
|
+ .name(context.getDeployName() + K8sConstants.DEPLOYMENT_SUFFIX)
|
|
|
+ .build());
|
|
|
+ if (deployments.size() == 0) {
|
|
|
+ deploymentApi.create(deployment);
|
|
|
+ log.info("deployment创建成功: [namespace: " + context.getNamespace() + ", deployName: " + context.getDeployName() + "]");
|
|
|
+ } else {
|
|
|
+ deploymentApi.update(deployment);
|
|
|
+ log.info("deployment更新成功: [namespace: " + context.getNamespace() + ", deployName: " + context.getDeployName() + "]");
|
|
|
+
|
|
|
+ }
|
|
|
+ } catch (K8sApiException e) {
|
|
|
+ context.appendErrorResult(PipelineException.DEPLOYMENT_CREATE_ERROR, e);
|
|
|
+ throw new PipelineException(HttpStatus.SC_SERVICE_UNAVAILABLE, PipelineException.DEPLOYMENT_CREATE_ERROR, e);
|
|
|
+ }
|
|
|
+
|
|
|
+ //3. 创建service
|
|
|
+ //3.1. 配置K8s的service
|
|
|
+ Service service = new Service();
|
|
|
+ service.setNamespace(context.getNamespace());
|
|
|
+ service.setName(context.getDeployName() + K8sConstants.SERVICE_SUFFIX);
|
|
|
+ //标签选择器选择 此项目对应的deployment的name
|
|
|
+ service.setSelectors(Collections.singletonMap(K8sConstants.APPLICATION_LABEL, context.getDeployName()));
|
|
|
+ service.setServicePorts(
|
|
|
+ Collections.singleton(ServicePort.builder().port(port).targetPort(port).build())
|
|
|
+ );
|
|
|
+
|
|
|
+ //3.2. 创建service实例
|
|
|
+ try {
|
|
|
+ List<Service> services = serviceApi.getByCondition(K8sObjectRequest.builder()
|
|
|
+ .namespace(context.getNamespace())
|
|
|
+ .name(context.getDeployName() + K8sConstants.SERVICE_SUFFIX)
|
|
|
+ .build());
|
|
|
+ if (services.size() == 0) {
|
|
|
+ serviceApi.create(service);
|
|
|
+ log.info("service创建成功: [namespace: " + context.getNamespace() + ", deployName: " + context.getDeployName() + "]");
|
|
|
+ } else {
|
|
|
+ serviceApi.update(service);
|
|
|
+ log.info("service更新成功: [namespace: " + context.getNamespace() + ", deployName: " + context.getDeployName() + "]");
|
|
|
+ }
|
|
|
+ } catch (K8sApiException e) {
|
|
|
+ context.appendErrorResult(PipelineException.SERVICE_CREATE_ERROR, e);
|
|
|
+ throw new PipelineException(HttpStatus.SC_SERVICE_UNAVAILABLE, PipelineException.SERVICE_CREATE_ERROR, e);
|
|
|
+ }
|
|
|
+ context.appendSuccessResult("K8s Service创建成功: [namespace: " + context.getNamespace() + ", deployName: " + context.getDeployName() + "]");
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+}
|