Просмотр исходного кода

细微修改:
1. 将类选择器单独封装为一个类
2. create方法返回创建对象
3. 部署应用会先删除原部署,再创建新部署(缺点:感受不到rolling update) 方案还需要再评估

raledong 7 лет назад
Родитель
Сommit
419bd5e640
25 измененных файлов с 475 добавлено и 102 удалено
  1. 2 2
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/DeploymentApi.java
  2. 1 1
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/IngressApi.java
  3. 14 0
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/LogApi.java
  4. 9 1
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/NamespaceApi.java
  5. 1 1
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/ResourceQuotaApi.java
  6. 6 5
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/impl/DeploymentApiImpl.java
  7. 4 2
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/impl/IngressApiImpl.java
  8. 42 0
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/impl/LogApiImpl.java
  9. 43 41
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/impl/NamespaceApiImpl.java
  10. 4 2
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/impl/ResourceQuotaImpl.java
  11. 9 0
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/model/Container.java
  12. 3 4
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/model/ContainerPort.java
  13. 9 0
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/model/Deployment.java
  14. 25 4
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/model/Ingress.java
  15. 153 0
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/model/LabelSelector.java
  16. 0 3
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/model/Namespace.java
  17. 9 1
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/model/ResourceQuota.java
  18. 1 1
      src/main/java/nju/seec/SEECdemo/logic/service/ApplicationService.java
  19. 42 22
      src/main/java/nju/seec/SEECdemo/logic/service/impl/ApplicationServiceImpl.java
  20. 2 0
      src/main/java/nju/seec/SEECdemo/logic/vo/ApplicationVO.java
  21. 7 7
      src/test/java/nju/seec/SEECdemo/service/api/IngressApiImplTest.java
  22. 28 0
      src/test/java/nju/seec/SEECdemo/service/api/LogApiImplTest.java
  23. 4 4
      src/test/java/nju/seec/SEECdemo/service/api/NamespaceApiImplTest.java
  24. 1 1
      src/test/java/nju/seec/SEECdemo/service/api/ResourceQuotaApiImplTest.java
  25. 56 0
      src/test/java/nju/seec/SEECdemo/service/api/model/LabelSelectorTest.java

+ 2 - 2
src/main/java/nju/seec/SEECdemo/logic/api/k8s/DeploymentApi.java

@@ -15,7 +15,7 @@ public interface DeploymentApi {
      * @throws nju.seec.SEECdemo.logic.api.k8s.exception.K8sApiException ALREADY_EXIST deployment已经存在
      * @throws nju.seec.SEECdemo.logic.api.k8s.exception.K8sApiException SYSTEM_ERROR 系统异常
      */
-    void createSync(Deployment deployment);
+    Deployment createSync(Deployment deployment);
 
     /**
      * 异步创建Deployment
@@ -24,7 +24,7 @@ public interface DeploymentApi {
      * @throws nju.seec.SEECdemo.logic.api.k8s.exception.K8sApiException ALREADY_EXIST deployment已经存在
      * @throws nju.seec.SEECdemo.logic.api.k8s.exception.K8sApiException SYSTEM_ERROR 系统异常
      */
-    void createAsync(Deployment deployment);
+    Deployment createAsync(Deployment deployment);
 
     /**
      * 删除deployment

+ 1 - 1
src/main/java/nju/seec/SEECdemo/logic/api/k8s/IngressApi.java

@@ -15,7 +15,7 @@ public interface IngressApi {
      * @throws nju.seec.SEECdemo.logic.api.k8s.exception.K8sApiException ALREADY_EXIST ingress已经存在
      * @throws nju.seec.SEECdemo.logic.api.k8s.exception.K8sApiException SYSTEM_ERROR 系统异常
      */
-    void create(Ingress ingress);
+    Ingress create(Ingress ingress);
 
     /**
      * 删除namespace命名空间下的ingress

+ 14 - 0
src/main/java/nju/seec/SEECdemo/logic/api/k8s/LogApi.java

@@ -0,0 +1,14 @@
+package nju.seec.SEECdemo.logic.api.k8s;
+
+import io.kubernetes.client.ApiException;
+
+import java.io.IOException;
+
+/**
+ * author: rale
+ * createdAt: 1/6/19
+ */
+public interface LogApi {
+
+    void test() throws ApiException, IOException;
+}

+ 9 - 1
src/main/java/nju/seec/SEECdemo/logic/api/k8s/NamespaceApi.java

@@ -1,5 +1,6 @@
 package nju.seec.SEECdemo.logic.api.k8s;
 
+import nju.seec.SEECdemo.logic.api.k8s.model.LabelSelector;
 import nju.seec.SEECdemo.logic.api.k8s.model.Namespace;
 import nju.seec.SEECdemo.logic.api.k8s.exception.K8sApiException;
 
@@ -15,7 +16,7 @@ public interface NamespaceApi {
      * @throws K8sApiException 命名空间已经存在
      * @throws K8sApiException 系统异常
      */
-    void create(Namespace namespace) throws K8sApiException;
+    Namespace create(Namespace namespace) throws K8sApiException;
 
 
     /**
@@ -66,6 +67,13 @@ public interface NamespaceApi {
      */
     List<Namespace> getByLabel(String key, String value);
 
+    /**
+     * 使用标签选择器筛选Namespace
+     * @param labelSelector
+     * @return
+     */
+    List<Namespace> getByLabelSelector(LabelSelector labelSelector);
+
     /**
      * 判断该命名空间是否存在
      * @param name

+ 1 - 1
src/main/java/nju/seec/SEECdemo/logic/api/k8s/ResourceQuotaApi.java

@@ -13,7 +13,7 @@ public interface ResourceQuotaApi {
      * @throws K8sApiException ALREADY_EXIST 资源已经存在
      * @throws K8sApiException SYSTEM_ERROR 系统异常
      */
-    void create(ResourceQuota resourceQuota);
+    ResourceQuota create(ResourceQuota resourceQuota);
 
 
     /**

+ 6 - 5
src/main/java/nju/seec/SEECdemo/logic/api/k8s/impl/DeploymentApiImpl.java

@@ -28,11 +28,12 @@ public class DeploymentApiImpl implements DeploymentApi{
     private AppsV1Api appsV1Api;
 
     @Override
-    public void createSync(Deployment deployment) {
+    public Deployment createSync(Deployment deployment) {
 
         try {
             V1Deployment v1Deployment = deployment.toV1Deployment();
-            appsV1Api.createNamespacedDeployment(deployment.getNamespace(),v1Deployment, PRETTY_FORMAT);
+            V1Deployment result = appsV1Api.createNamespacedDeployment(deployment.getNamespace(),v1Deployment, PRETTY_FORMAT);
+            return v1Deployment == null ? null : new Deployment(result);
         } catch (ApiException e) {
             LoggerUtil.error(logger, e, "deployment创建失败, deployment={}, response={}", deployment, e.getResponseBody());
 
@@ -46,12 +47,12 @@ public class DeploymentApiImpl implements DeploymentApi{
         } catch (Exception e) {
             LoggerUtil.error(logger, e, "deployment创建异常, deployment={}, response={}", deployment);
         }
+        return null;
     }
 
     @Override
-    public void createAsync(Deployment deployment) {
-
-
+    public Deployment createAsync(Deployment deployment) {
+        return null;
     }
 
     @Override

+ 4 - 2
src/main/java/nju/seec/SEECdemo/logic/api/k8s/impl/IngressApiImpl.java

@@ -35,14 +35,15 @@ public class IngressApiImpl implements IngressApi{
     }
 
     @Override
-    public void create(Ingress ingress) {
+    public Ingress create(Ingress ingress) {
         V1beta1Ingress v1beta1Ingress = ingress.toV1beta1Ingress();
 
         try {
-            extensionsV1beta1Api.createNamespacedIngress(
+            V1beta1Ingress result = extensionsV1beta1Api.createNamespacedIngress(
                     ingress.getNamespace(),
                     v1beta1Ingress,
                     PRETTY_FORMAT);
+            return result == null ? null : new Ingress(result);
         } catch (ApiException e) {
             LoggerUtil.error(logger, e, "ingress 创建失败,ingress={}, response={}", ingress, e.getResponseBody());
             if (e.getCode() == NOT_FOUND) {
@@ -55,6 +56,7 @@ public class IngressApiImpl implements IngressApi{
         } catch (Exception e) {
             LoggerUtil.error(logger, e, "ingress 创建异常,ingress={}", ingress);
         }
+        return null;
     }
 
     @Override

+ 42 - 0
src/main/java/nju/seec/SEECdemo/logic/api/k8s/impl/LogApiImpl.java

@@ -0,0 +1,42 @@
+package nju.seec.SEECdemo.logic.api.k8s.impl;
+
+import com.google.common.io.ByteStreams;
+import io.kubernetes.client.ApiException;
+import io.kubernetes.client.PodLogs;
+import io.kubernetes.client.apis.CoreV1Api;
+import io.kubernetes.client.models.V1Pod;
+import nju.seec.SEECdemo.logic.api.k8s.LogApi;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.*;
+
+/**
+ * author: rale
+ * createdAt: 1/6/19
+ */
+@Service
+public class LogApiImpl implements LogApi{
+
+    private CoreV1Api coreV1Api;
+
+    @Autowired
+    public LogApiImpl(CoreV1Api coreV1Api) {
+        this.coreV1Api = coreV1Api;
+    }
+
+    @Override
+    public void test() throws ApiException, IOException {
+        V1Pod pod =
+                coreV1Api
+                        .listNamespacedPod("group25", null, null, null, null, null, null, null, null, null)
+                        .getItems()
+                        .get(0);
+        PodLogs logs = new PodLogs();
+        InputStream inputStream = logs.streamNamespacedPodLog(pod);
+
+        byte[] data = ByteStreams.toByteArray(inputStream);
+        System.out.print(data.length);
+//        ByteStreams.copy(inputStream, System.out);
+    }
+}

+ 43 - 41
src/main/java/nju/seec/SEECdemo/logic/api/k8s/impl/NamespaceApiImpl.java

@@ -7,13 +7,15 @@ import io.kubernetes.client.models.V1DeleteOptionsBuilder;
 import io.kubernetes.client.models.V1Namespace;
 import io.kubernetes.client.models.V1NamespaceList;
 import nju.seec.SEECdemo.logic.api.k8s.NamespaceApi;
-import nju.seec.SEECdemo.logic.api.k8s.model.Namespace;
 import nju.seec.SEECdemo.logic.api.k8s.exception.K8sApiException;
+import nju.seec.SEECdemo.logic.api.k8s.model.LabelSelector;
+import nju.seec.SEECdemo.logic.api.k8s.model.Namespace;
 import nju.seec.SEECdemo.util.LoggerUtil;
 import org.slf4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
@@ -37,10 +39,11 @@ public class NamespaceApiImpl implements NamespaceApi{
      * @see NamespaceApi#create(Namespace)
      */
     @Override
-    public void create(Namespace namespace) throws K8sApiException{
+    public Namespace create(Namespace namespace) {
         V1Namespace v1Namespace = namespace.toV1Namespace();
         try {
-            coreV1Api.createNamespace(v1Namespace, "true");
+            V1Namespace result = coreV1Api.createNamespace(v1Namespace, PRETTY_FORMAT);
+            return result == null ? null : new Namespace(result);
         } catch (ApiException e) {
             LoggerUtil.error(logger, e, "Namespace创建失败,namespace={}, v1Namespace={}, response={}", namespace, v1Namespace, e.getResponseBody());
             if (e.getCode() == ALREADY_EXIST) {
@@ -51,6 +54,7 @@ public class NamespaceApiImpl implements NamespaceApi{
         } catch (Exception e) {
             LoggerUtil.error(logger, e, "Namespace创建异常, namespace={}, v1Namespace={}", namespace, v1Namespace);
         }
+        return null;
     }
 
     /**
@@ -59,7 +63,7 @@ public class NamespaceApiImpl implements NamespaceApi{
      * @throws K8sApiException
      */
     @Override
-    public void delete(String name) throws K8sApiException{
+    public void delete(String name) {
         try {
             deleteNamespace(name);
         } catch (ApiException e) {
@@ -91,20 +95,14 @@ public class NamespaceApiImpl implements NamespaceApi{
     }
 
     @Override
-    public Namespace getByName(String name) throws K8sApiException{
+    public Namespace getByName(String name) {
         try {
-            V1NamespaceList v1NamespaceList = coreV1Api.listNamespace(
+            V1Namespace v1Namespace = coreV1Api.readNamespace(
+                    name,
                     PRETTY_FORMAT,
                     null,
-                    METADATA_NAME_SELECTOR + name,
-                    null,
-                    null,
-                    null,
-                    null,
-                    null,
                     null);
-            List<Namespace> result = v1NamespaceList.getItems().stream().limit(1).map(Namespace::new).collect(Collectors.toList());
-            return result.get(0);
+            return v1Namespace == null ? null : new Namespace(v1Namespace);
         }catch (ApiException e) {
             LoggerUtil.error(logger, e, "查询Namespace失败,name={}, response={}", name, e.getResponseBody());
             if (e.getCode() != NOT_FOUND) {
@@ -115,7 +113,7 @@ public class NamespaceApiImpl implements NamespaceApi{
     }
 
     @Override
-    public List<Namespace> getAll() throws K8sApiException{
+    public List<Namespace> getAll() {
         try {
             V1NamespaceList v1NamespaceList = coreV1Api.listNamespace(
                     PRETTY_FORMAT,
@@ -138,13 +136,8 @@ public class NamespaceApiImpl implements NamespaceApi{
 
     @Override
     public List<Namespace> getByLabels(Map<String, String> labels) {
-        return null;
-    }
-
-    @Override
-    public List<Namespace> getByLabel(String key, String value) throws K8sApiException{
         try {
-            String labelSelector = buildLabelSelector(key, value);
+            String labelSelector = LabelSelector.toJsonString(labels);
             V1NamespaceList v1NamespaceList = coreV1Api.listNamespace(
                     PRETTY_FORMAT,
                     null,
@@ -159,24 +152,48 @@ public class NamespaceApiImpl implements NamespaceApi{
             LoggerUtil.info(logger,"result={}", namespaces);
             return namespaces;
         } catch (ApiException e) {
-            LoggerUtil.error(logger, e, "根据标签获取Namespace异常, key={}, value={}, response={}", key, value, e.getResponseBody());
+            LoggerUtil.error(logger, e, "根据标签获取Namespace异常, labels={}, response={}", labels, e.getResponseBody());
             throw K8sApiException.K8s_SYSTEM_ERROR_EXCEPTION;
         }
     }
 
     @Override
-    public boolean exists(String name) {
+    public List<Namespace> getByLabel(String key, String value){
+        return getByLabels(Collections.singletonMap(key, value));
+    }
+
+    @Override
+    public List<Namespace> getByLabelSelector(LabelSelector labelSelector) {
         try {
-            V1NamespaceList v1NamespaceList = coreV1Api.listNamespace(PRETTY_FORMAT,
+            String selector = labelSelector.toJsonString();
+            V1NamespaceList v1NamespaceList = coreV1Api.listNamespace(
+                    PRETTY_FORMAT,
                     null,
-                    METADATA_NAME_SELECTOR + name,
                     null,
+                    true,
+                    selector,
                     null,
                     null,
                     null,
+                    null);
+            List<Namespace> namespaces =  v1NamespaceList.getItems().stream().map(Namespace::new).collect(Collectors.toList());
+            LoggerUtil.info(logger,"result={}", namespaces);
+            return namespaces;
+        } catch (ApiException e) {
+            LoggerUtil.error(logger, e, "根据标签获取Namespace异常, labelSelector={}, response={}", labelSelector, e.getResponseBody());
+            throw K8sApiException.K8s_SYSTEM_ERROR_EXCEPTION;
+        }
+    }
+
+    @Override
+    public boolean exists(String name) {
+        try {
+            V1Namespace v1Namespace = coreV1Api.readNamespace(
+                    name,
+                    PRETTY_FORMAT,
                     null,
                     null);
-            return v1NamespaceList.getItems().size() > 0;
+            return v1Namespace != null;
         }catch (ApiException e) {
             if (e.getCode() != NOT_FOUND) {
                 LoggerUtil.error(logger, e, "查询命名空间失败, name={}, response={}", name, e.getResponseBody());
@@ -206,19 +223,4 @@ public class NamespaceApiImpl implements NamespaceApi{
                 FOREGROUND_PROPAGATION_POLICY);
     }
 
-    private String buildLabelSelector(String key, String value) {
-        return key + "=" + value;
-    }
-
-    private String buildLabelSelector(Map<String, String> labels) {
-        StringBuilder result = new StringBuilder();
-        for (Map.Entry<String, String> entry : labels.entrySet()) {
-            result.append(entry.getKey());
-            result.append("=");
-            result.append(entry.getValue());
-            result.append(",");
-        }
-        return result.toString();
-    }
-
 }

+ 4 - 2
src/main/java/nju/seec/SEECdemo/logic/api/k8s/impl/ResourceQuotaImpl.java

@@ -35,13 +35,14 @@ public class ResourceQuotaImpl implements ResourceQuotaApi {
     private NamespaceApi namespaceApi;
 
     @Override
-    public void create(ResourceQuota resourceQuota) throws K8sApiException{
+    public ResourceQuota create(ResourceQuota resourceQuota) throws K8sApiException{
         try {
             V1ResourceQuota v1ResourceQuota = resourceQuota.toV1ResourceQuota();
-            coreV1Api.createNamespacedResourceQuota(
+            V1ResourceQuota result = coreV1Api.createNamespacedResourceQuota(
                     resourceQuota.getNamespace(),
                     v1ResourceQuota,
                     PRETTY_FORMAT);
+            return result == null ? null : new ResourceQuota(v1ResourceQuota);
         } catch (ApiException e) {
             LoggerUtil.error(logger, e, "创建resource quota失败, resourceQuota={}, response={}",
                     resourceQuota, e.getResponseBody());
@@ -55,6 +56,7 @@ public class ResourceQuotaImpl implements ResourceQuotaApi {
         } catch (Exception e) {
             LoggerUtil.error(logger, e, "创建resource quota失败, resourceQuota={}", resourceQuota);
         }
+        return null;
     }
 
     @Override

+ 9 - 0
src/main/java/nju/seec/SEECdemo/logic/api/k8s/model/Container.java

@@ -2,6 +2,7 @@ package nju.seec.SEECdemo.logic.api.k8s.model;
 
 import io.kubernetes.client.models.V1Container;
 import io.kubernetes.client.models.V1ContainerBuilder;
+import io.kubernetes.client.models.V1EnvVar;
 import io.kubernetes.client.models.V1EnvVarBuilder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
@@ -45,6 +46,14 @@ public class Container {
     private Map<String, String> env = new HashMap<>();
 
 
+    public Container(V1Container v1Container) {
+        this.name = v1Container.getName();
+        this.image = v1Container.getImage();
+        this.ports = v1Container.getPorts().stream().map(ContainerPort::new).collect(Collectors.toSet());
+        this.args = v1Container.getArgs();
+        this.command = v1Container.getCommand();
+        this.env = v1Container.getEnv().stream().collect(Collectors.toMap(V1EnvVar::getName, V1EnvVar::getValue));
+    }
     public V1Container toV1Container(){
         return new V1ContainerBuilder()
                 .withName(name)

+ 3 - 4
src/main/java/nju/seec/SEECdemo/logic/api/k8s/model/ContainerPort.java

@@ -34,10 +34,9 @@ public class ContainerPort {
     private String protocol;
 
     public ContainerPort(V1ContainerPort v1ContainerPort) {
-        ContainerPort containerPort = new ContainerPort();
-        containerPort.setName(v1ContainerPort.getName());
-        containerPort.setPort(v1ContainerPort.getContainerPort());
-        containerPort.setProtocol(v1ContainerPort.getProtocol());
+        this.setName(v1ContainerPort.getName());
+        this.setPort(v1ContainerPort.getContainerPort());
+        this.setProtocol(v1ContainerPort.getProtocol());
     }
     public V1ContainerPort toV1ContainerPort(){
         V1ContainerPort v1ContainerPort = new V1ContainerPortBuilder()

+ 9 - 0
src/main/java/nju/seec/SEECdemo/logic/api/k8s/model/Deployment.java

@@ -35,6 +35,14 @@ public class Deployment {
 
     private Map<String, String> labels = new HashMap<>();
 
+    private Map<String, String> annotations = new HashMap<>();
+
+    public Deployment(V1Deployment v1Deployment) {
+        this.name = v1Deployment.getMetadata().getName();
+        this.namespace = v1Deployment.getMetadata().getNamespace();
+        this.containers = v1Deployment.getSpec().getTemplate().getSpec().getContainers()
+                .stream().map(Container::new).collect(Collectors.toList());
+    }
     public V1Deployment toV1Deployment(){
         return new V1DeploymentBuilder()
                 .withApiVersion(API_VERSION)
@@ -60,6 +68,7 @@ public class Deployment {
     private V1PodTemplateSpec toPodSpecTemplate(){
         V1ObjectMeta v1ObjectMeta = new V1ObjectMetaBuilder()
                 .withLabels(labels)
+                .withAnnotations(annotations)
                 .build();
         List<V1LocalObjectReference> references = imagePullSecrets
                 .stream()

+ 25 - 4
src/main/java/nju/seec/SEECdemo/logic/api/k8s/model/Ingress.java

@@ -23,23 +23,39 @@ public class Ingress {
     private static final String KIND = "Ingress";
 
     private static final String REWRITE_ANNOTATION = "nginx.ingress.kubernetes.io/rewrite-target";
+
+    private static final String DEFAULT_REWRITE_PATH = "/";
     private String namespace;
 
     private String name;
 
     //重定向的路径,默认为根路径
-    private String redirectPath = "/";
+    private String redirectPath = DEFAULT_REWRITE_PATH;
 
     private String httpHost;
 
     /*Ingress能暴露HTTP和HTTPS协议口,目前暂不支持HTTPS协议*/
-    private Set<IngressDetailDTO> httpRules = new HashSet<>();
-
+    private Set<IngressDetail> httpRules = new HashSet<>();
+
+    public Ingress(V1beta1Ingress v1beta1Ingress) {
+        this.name = v1beta1Ingress.getMetadata().getName();
+        this.namespace = v1beta1Ingress.getMetadata().getNamespace();
+        this.redirectPath = v1beta1Ingress.getMetadata().getAnnotations().getOrDefault(REWRITE_ANNOTATION, DEFAULT_REWRITE_PATH);
+        List<V1beta1IngressRule> rules = v1beta1Ingress.getSpec().getRules();
+        if (rules != null && !rules.isEmpty()) {
+            for (V1beta1IngressRule rule : rules) {
+                httpHost = rule.getHost();
+                if (rule.getHttp() != null && rule.getHttp().getPaths() != null) {
+                    httpRules = rule.getHttp().getPaths().stream().map(IngressDetail::new).collect(Collectors.toSet());
+                }
+            }
+        }
+    }
 
     @Data
     @NoArgsConstructor
     @EqualsAndHashCode
-    public static class IngressDetailDTO{
+    public static class IngressDetail {
 
         private String path;
 
@@ -47,6 +63,11 @@ public class Ingress {
 
         private int port;
 
+        public IngressDetail(V1beta1HTTPIngressPath path) {
+            this.path = path.getPath();
+            this.serviceName = path.getBackend().getServiceName();
+            this.port = path.getBackend().getServicePort().getIntValue();
+        }
     }
 
 

+ 153 - 0
src/main/java/nju/seec/SEECdemo/logic/api/k8s/model/LabelSelector.java

@@ -0,0 +1,153 @@
+package nju.seec.SEECdemo.logic.api.k8s.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import java.util.*;
+
+/**
+ * author: rale
+ * createdAt: 1/7/19
+ *
+ * 标签选择器
+ * 支持以下几种语法
+ * key IN value1,value2 ... 筛选出键为key且值在value中的资源 key equals value 等价于key in value
+ * key NOT_IN value1, value2 ... 筛选出键为key且值不在value中,或者不存在键为key的键值对的资源 key not equals value 等价于 key not in value
+ * Key EXISTS 筛选出包含键key的资源
+ * Key NOT_EXISTS 筛选出不包含键key的资源
+ *
+ * 本选择器不会删除同名的key
+ */
+@NoArgsConstructor
+public class LabelSelector {
+
+    public enum Operator{
+
+        IN {
+            @Override
+            public String toJsonString(MatchExpressionItem matchExpressionItem) {
+                Set<String> values = matchExpressionItem.getValues();
+                if (values == null || values.size() == 0) return "";
+                if (values.size() == 1) return matchExpressionItem.getKey() + "=" + values.iterator().next();
+                StringBuilder resultValue = new StringBuilder("(");
+                for (String value : matchExpressionItem.getValues()) {
+                    if (resultValue.length() > 1) {
+                        resultValue.append(",");
+                    }
+                    resultValue.append(value);
+                }
+                resultValue.append(")");
+                return matchExpressionItem.getKey() + " in " + resultValue;
+            }
+        },
+
+        NOT_IN {
+            @Override
+            public String toJsonString(MatchExpressionItem matchExpressionItem) {
+                Set<String> values = matchExpressionItem.getValues();
+                if (values == null || values.size() == 0) return "";
+                if (values.size() == 1) return matchExpressionItem.getKey() + "!=" + values.iterator().next();
+                StringBuilder resultValue = new StringBuilder("(");
+                for (String value : matchExpressionItem.getValues()) {
+                    if (resultValue.length() > 1) {
+                        resultValue.append(",");
+                    }
+                    resultValue.append(value);
+                }
+                resultValue.append(")");
+                return matchExpressionItem.getKey() + " notin " + resultValue;
+            }
+        },
+
+        EXISTS {
+            @Override
+            public String toJsonString(MatchExpressionItem matchExpressionItem) {
+                return matchExpressionItem.getKey();
+            }
+        },
+
+        NOT_EXISTS {
+            @Override
+            public String toJsonString(MatchExpressionItem matchExpressionItem) {
+                return "!" + matchExpressionItem.getKey();
+            }
+        };
+
+        public abstract String toJsonString(MatchExpressionItem matchExpressionItem);
+    }
+
+
+    private List<MatchExpressionItem> matchExpressionItems;
+
+
+    public void addMatchExpression(String key, Operator operator, Set<String> values) {
+        addMatchExpressionItem(new MatchExpressionItem(key, operator, values));
+    }
+
+
+    public void addMatchExpression(String key, Operator operator, String... values) {
+        Set<String> valueSet = new HashSet<>(Arrays.asList(values));
+        addMatchExpressionItem(new MatchExpressionItem(key, operator, valueSet));
+    }
+
+
+    private void addMatchExpressionItem(MatchExpressionItem matchExpressionItem) {
+        if (matchExpressionItem == null) {
+            return;
+        }
+        if (matchExpressionItems == null) {
+            matchExpressionItems = new LinkedList<>();
+        }
+        matchExpressionItems.add(matchExpressionItem);
+    }
+
+    @Data
+    @EqualsAndHashCode
+    @NoArgsConstructor
+    @AllArgsConstructor
+    private static class MatchExpressionItem{
+
+        private String key;
+
+        private Operator operator;
+
+        private Set<String> values;
+
+        private String toJsonString() {
+            return operator == null ? "" : operator.toJsonString(this);
+        }
+
+    }
+
+    public String toJsonString() {
+        StringBuilder sb = new StringBuilder();
+
+        if (matchExpressionItems != null && !matchExpressionItems.isEmpty()) {
+            for (MatchExpressionItem matchExpressionItem : matchExpressionItems) {
+                if (sb.length() > 0) {
+                    sb.append(",");
+                }
+                sb.append(matchExpressionItem.toJsonString());
+            }
+        }
+        return sb.toString();
+    }
+
+    public static String toJsonString(Map<String, String> labels) {
+        StringBuilder result = new StringBuilder();
+        for (Map.Entry<String, String> label : labels.entrySet()) {
+            if (result.length() > 0) {
+                result.append(",");
+            }
+            result.append(toJsonString(label.getKey(), label.getValue()));
+        }
+        return result.toString();
+    }
+
+    public static String toJsonString(String key, String value) {
+        return key + "=" + value;
+    }
+
+}

+ 0 - 3
src/main/java/nju/seec/SEECdemo/logic/api/k8s/model/Namespace.java

@@ -23,9 +23,6 @@ public class Namespace {
 
     public static final String VERSION = "v1";
 
-    //@todo 添加一个label标记该Namespace为seec创建的
-    //获取时只获取seec创建的label
-
     @NotNull
     private String name;
 

+ 9 - 1
src/main/java/nju/seec/SEECdemo/logic/api/k8s/model/ResourceQuota.java

@@ -14,6 +14,7 @@ import java.util.Map;
  */
 @Data
 @NoArgsConstructor
+//@todo 添加注释
 public class ResourceQuota {
     public static final String API_VERSION = "v1";
 
@@ -28,10 +29,16 @@ public class ResourceQuota {
 
     private String name;
 
+    /**
+     * 命名空间下请求CPU资源的最大数额
+     */
     private CpuQuantity maxCpuTotalRequest;
 
     private StorageQuantity maxMemoryTotalRequest;
 
+    /**
+     *
+     */
     private CpuQuantity maxCpuTotalLimit;
 
     private StorageQuantity maxMemoryTotalLimit;
@@ -39,7 +46,8 @@ public class ResourceQuota {
     public ResourceQuota(V1ResourceQuota v1ResourceQuota) {
         this.namespace = v1ResourceQuota.getMetadata().getNamespace();
         this.name = v1ResourceQuota.getMetadata().getName();
-
+        this.maxCpuTotalLimit = new CpuQuantity(v1ResourceQuota.getSpec().getHard().get(HARD_LIMITS_CPU));
+        this.maxMemoryTotalLimit = new StorageQuantity(v1ResourceQuota.getSpec().getHard().get(HARD_LIMITS_MEMORY));
     }
 
     public V1ResourceQuota toV1ResourceQuota(){

+ 1 - 1
src/main/java/nju/seec/SEECdemo/logic/service/ApplicationService.java

@@ -40,5 +40,5 @@ public interface ApplicationService {
      * 部署项目
      * @param applicationDTO
      */
-    void deploy(ApplicationDTO applicationDTO);
+    ApplicationVO deploy(ApplicationDTO applicationDTO);
 }

+ 42 - 22
src/main/java/nju/seec/SEECdemo/logic/service/impl/ApplicationServiceImpl.java

@@ -60,25 +60,45 @@ public class ApplicationServiceImpl implements ApplicationService{
     }
 
     @Override
-    public void deploy(ApplicationDTO applicationDTO) {
+    public ApplicationVO deploy(ApplicationDTO applicationDTO) {
         String projectName = applicationDTO.getProjectName();
-        if (namespaceApi.exists(projectName)) {
-            try {
-                Deployment deployment = buildDeploymentDTO(applicationDTO);
-                LoggerUtil.info(logger, "deployment={}", deployment);
-                deploymentApi.createSync(deployment);
-
-                //判断service是否存在
-                Service service = buildServiceDTO(applicationDTO);
-                serviceApi.create(service);
+        if (!namespaceApi.exists(projectName)) {
+            //抛出项目不存在异常
+            return null;
+        }
 
-                Ingress ingress = buildIngressDTO(applicationDTO);
-                ingressApi.create(ingress);
-            } catch (K8sApiException e) {
+        try {
+            //删除现有的同名deployment
+            deploymentApi.deleteIfExist(applicationDTO.getProjectName(), applicationDTO.getName());
+            Deployment deployment = buildDeploymentDTO(applicationDTO);
+            deploymentApi.createSync(deployment);
+
+            //删除现有的同名Service
+            serviceApi.deleteIfExists(applicationDTO.getProjectName(), applicationDTO.getName());
+            Service service = buildServiceDTO(applicationDTO);
+            serviceApi.create(service);
+
+            //删除现有的同名Ingress
+            ingressApi.deleteIfExists(applicationDTO.getProjectName(), applicationDTO.getName());
+            Ingress ingress = buildIngressDTO(applicationDTO);
+            ingressApi.create(ingress);
+
+            //构建返回数据
+            ApplicationVO applicationVO = new ApplicationVO();
+            applicationVO.setName(applicationDTO.getName());
+            applicationVO.setProjectName(applicationDTO.getProjectName());
+            if (ingress.getHttpRules() != null) {
+                applicationVO.setUrl(
+                        ingress.getHttpRules().stream()
+                                .map(rule -> ingress.getHttpHost() + rule).collect(Collectors.toList())
+                );
 
             }
+            return applicationVO;
+        } catch (K8sApiException e) {
+            //抛出创建失败异常
         }
-
+        return null;
     }
 
     private Deployment buildDeploymentDTO(ApplicationDTO applicationDTO) {
@@ -120,22 +140,22 @@ public class ApplicationServiceImpl implements ApplicationService{
         ingress.setHttpHost("deernowl.cn");
 
         //@todo lambda表达式解决
-        Set<Ingress.IngressDetailDTO> detailDTOS = new HashSet<>();
+        Set<Ingress.IngressDetail> detailDTOS = new HashSet<>();
         for (ContainerDTO containerDTO : applicationDTO.getContainers()) {
             String containerName = containerDTO.getName();
 
-            Set<Ingress.IngressDetailDTO> ingressDetailDTOS = containerDTO.getPorts()
+            Set<Ingress.IngressDetail> ingressDetails = containerDTO.getPorts()
                     .stream()
                     .filter(PortDTO::isExpose)
                     .map(portDTO ->{
-                        Ingress.IngressDetailDTO ingressDetailDTO = new Ingress.IngressDetailDTO();
-                        ingressDetailDTO.setServiceName(applicationDTO.getName());
-                        ingressDetailDTO.setPort(portDTO.getPort());
-                        ingressDetailDTO.setPath("/" + appName + "/" + containerName);
-                        return ingressDetailDTO;
+                        Ingress.IngressDetail ingressDetail = new Ingress.IngressDetail();
+                        ingressDetail.setServiceName(applicationDTO.getName());
+                        ingressDetail.setPort(portDTO.getPort());
+                        ingressDetail.setPath("/" + appName + "/" + containerName);
+                        return ingressDetail;
                     })
                     .collect(Collectors.toSet());
-            detailDTOS.addAll(ingressDetailDTOS);
+            detailDTOS.addAll(ingressDetails);
         }
         ingress.setHttpRules(detailDTOS);
         return ingress;

+ 2 - 0
src/main/java/nju/seec/SEECdemo/logic/vo/ApplicationVO.java

@@ -15,6 +15,8 @@ public class ApplicationVO {
 
     private String name;
 
+    private List<String> url;
+
     //传递回额外的信息
     private Map<String, Object> extra = new HashMap<>();
 

+ 7 - 7
src/test/java/nju/seec/SEECdemo/service/api/IngressApiImplTest.java

@@ -2,7 +2,7 @@ package nju.seec.SEECdemo.service.api;
 
 import nju.seec.SEECdemo.logic.api.k8s.IngressApi;
 import nju.seec.SEECdemo.logic.api.k8s.model.Ingress;
-import nju.seec.SEECdemo.logic.api.k8s.model.Ingress.IngressDetailDTO;
+import nju.seec.SEECdemo.logic.api.k8s.model.Ingress.IngressDetail;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -29,12 +29,12 @@ public class IngressApiImplTest {
         ingress.setName("test");
         ingress.setNamespace("demo");
         ingress.setHttpHost("deernowl.cn");
-        IngressDetailDTO ingressDetailDTO = new IngressDetailDTO();
-        ingressDetailDTO.setPath("/demo/test");
-        ingressDetailDTO.setPort(8081);
-        ingressDetailDTO.setServiceName("test");
-        Set<IngressDetailDTO> details = new HashSet<>();
-        details.add(ingressDetailDTO);
+        IngressDetail ingressDetail = new IngressDetail();
+        ingressDetail.setPath("/demo/test");
+        ingressDetail.setPort(8081);
+        ingressDetail.setServiceName("test");
+        Set<IngressDetail> details = new HashSet<>();
+        details.add(ingressDetail);
         ingress.setHttpRules(details);
         ingressApi.create(ingress);
 

+ 28 - 0
src/test/java/nju/seec/SEECdemo/service/api/LogApiImplTest.java

@@ -0,0 +1,28 @@
+package nju.seec.SEECdemo.service.api;
+
+import io.kubernetes.client.ApiException;
+import nju.seec.SEECdemo.logic.api.k8s.LogApi;
+import org.junit.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 java.io.IOException;
+
+/**
+ * author: rale
+ * createdAt: 1/6/19
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class LogApiImplTest {
+
+    @Autowired
+    private LogApi logApi;
+
+    @Test
+    public void test() throws IOException, ApiException {
+        logApi.test();
+    }
+}

+ 4 - 4
src/test/java/nju/seec/SEECdemo/service/api/NamespaceApiImplTest.java

@@ -51,10 +51,10 @@ public class NamespaceApiImplTest {
     }
 
     @Test
-    public void testGetByLabels() throws K8sApiException {
+    public void testGetByLabels(){
         Map<String, String> map = new HashMap<>();
-        map.put("test1", "test1");
-        map.put("test2", "test2");
-        namespaceApi.getByLabels(map);
+        map.put("demo.seec.nju.cn/description", "demo");
+        map.put("demo.seec.nju.cn/start", "2018-10-12");
+        System.out.print(namespaceApi.getByLabels(map));
     }
 }

+ 1 - 1
src/test/java/nju/seec/SEECdemo/service/api/ResourceQuotaApiImplTest.java

@@ -1,7 +1,7 @@
 package nju.seec.SEECdemo.service.api;
 
 import nju.seec.SEECdemo.logic.api.k8s.model.CpuQuantity;
-import nju.seec.SEECdemo.logic.api.k8s.vo.ResourceQuota;
+import nju.seec.SEECdemo.logic.api.k8s.model.ResourceQuota;
 import nju.seec.SEECdemo.logic.api.k8s.model.StorageQuantity;
 import org.junit.Test;
 import org.junit.runner.RunWith;

+ 56 - 0
src/test/java/nju/seec/SEECdemo/service/api/model/LabelSelectorTest.java

@@ -0,0 +1,56 @@
+package nju.seec.SEECdemo.service.api.model;
+
+import nju.seec.SEECdemo.logic.api.k8s.model.LabelSelector;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * author: rale
+ * createdAt: 1/7/19
+ */
+
+public class LabelSelectorTest {
+
+    @Test
+    public void testEquals() {
+        LabelSelector labelSelector = new LabelSelector();
+        labelSelector.addMatchExpression("key1", LabelSelector.Operator.IN, "value1");
+        String result = labelSelector.toJsonString();
+        Assert.assertEquals("key1=value1", result);
+    }
+
+    @Test
+    public void testNotEquals() {
+        LabelSelector labelSelector = new LabelSelector();
+        labelSelector.addMatchExpression("key1", LabelSelector.Operator.NOT_IN, "value1");
+        String result = labelSelector.toJsonString();
+        Assert.assertEquals("key1!=value1", result);
+    }
+
+    @Test
+    public void testIn() {
+        LabelSelector labelSelector = new LabelSelector();
+        labelSelector.addMatchExpression("key1", LabelSelector.Operator.NOT_IN, "value1", "value2");
+        String result = labelSelector.toJsonString();
+        Assert.assertEquals("key1 notin (value2,value1)", result);
+    }
+
+    @Test
+    public void testExists() {
+        LabelSelector labelSelector = new LabelSelector();
+        labelSelector.addMatchExpression("key1", LabelSelector.Operator.EXISTS, "value1");
+        String result = labelSelector.toJsonString();
+        Assert.assertEquals("key1", result);
+    }
+
+    @Test
+    public void testInAndExists() {
+        LabelSelector labelSelector = new LabelSelector();
+        labelSelector.addMatchExpression("key1", LabelSelector.Operator.EXISTS, "value1");
+    }
+}