#1 分支开早了,把底层组的一些api合并过来。。qaq

Слито
ZhangYi слито 4 коммит(ов) из SEEC/master в SEEC/codeAssignment 7 лет назад
34 измененных файлов с 1264 добавлено и 49 удалено
  1. 4 5
      config.yml
  2. 14 0
      pom.xml
  3. 15 0
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/DeploymentApi.java
  4. 12 0
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/IngressApi.java
  5. 19 0
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/NamespaceApi.java
  6. 69 0
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/impl/DeploymentApiImpl.java
  7. 50 0
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/impl/NamespaceApiImpl.java
  8. 75 0
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/util/BeanAnnouncement.java
  9. 26 0
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/vo/ApiResult.java
  10. 32 0
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/vo/Constants.java
  11. 75 0
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/vo/Container.java
  12. 51 0
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/vo/ContainerPort.java
  13. 109 0
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/vo/Deployment.java
  14. 65 0
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/vo/Namespace.java
  15. 47 0
      src/main/java/nju/seec/SEECdemo/logic/api/k8s/vo/ServicePort.java
  16. 40 0
      src/main/java/nju/seec/SEECdemo/logic/service/ApplicationService.java
  17. 38 0
      src/main/java/nju/seec/SEECdemo/logic/service/ProjectService.java
  18. 40 0
      src/main/java/nju/seec/SEECdemo/logic/service/impl/ApplicationServiceImpl.java
  19. 36 0
      src/main/java/nju/seec/SEECdemo/logic/service/impl/ProjectServiceImpl.java
  20. 19 0
      src/main/java/nju/seec/SEECdemo/logic/vo/ApplicationVO.java
  21. 24 0
      src/main/java/nju/seec/SEECdemo/logic/vo/ContainerVO.java
  22. 21 0
      src/main/java/nju/seec/SEECdemo/logic/vo/ProjectVO.java
  23. 24 0
      src/main/java/nju/seec/SEECdemo/logic/vo/projecttemplate/ProjectTemplate.java
  24. 13 0
      src/main/java/nju/seec/SEECdemo/util/ApplicationProperties.java
  25. 37 0
      src/main/java/nju/seec/SEECdemo/util/LoggerUtil.java
  26. 25 0
      src/main/java/nju/seec/SEECdemo/web/dto/ApplicationDTO.java
  27. 19 0
      src/main/java/nju/seec/SEECdemo/web/dto/ContainerDTO.java
  28. 12 0
      src/main/java/nju/seec/SEECdemo/web/dto/PortDTO.java
  29. 45 0
      src/main/resources/application-backup.yml
  30. 74 0
      src/main/resources/application-dev.yml
  31. 45 0
      src/main/resources/application-test.yml
  32. 2 44
      src/main/resources/application.yml
  33. 57 0
      src/test/java/nju/seec/SEECdemo/service/api/DeploymentApiImplTest.java
  34. 30 0
      src/test/java/nju/seec/SEECdemo/service/api/NamespaceApiImplTest.java

+ 4 - 5
config.yml

@@ -31,8 +31,7 @@ moocoder:
     type: local
     properties:
       location: ./problem
-
-kubernetes:
-  external-ip:
-    k8s2: 10.10.241.0/24
-    k8s3: 10.10.242.0/24
+  k8s:
+    api-server: https://10.1.1.198:6443
+    token: eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi10b2tlbi0yZ2o5NSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJhZG1pbiIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjRjZGQ0MGFlLTA1OWItMTFlOS1hMTIwLTAyMDA2MTI3MDAwNSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTphZG1pbiJ9.LwQ-LQlA_yhtndGLpveRfs0ZFFW971P1tchzJZP81s1vz0msb0q6dLJmcsCHaYhHWZ58_DCc0e7suGmUFEeMZcJL-sj3js1Cp5WNdnyz99Y3vZ-krHvNHGoc42jOrNUeO27Kb7uTDCmmKcCJSQ9gw8s2ie--7Z_kPAKVjhqr9nHtfrmmuIqFgwbIBQrNyJLMxaXs6tsCS4YWBuIQK1aOPWsijJNXA8W1acQ7p95P3tBGYMq17AaOEdeK8m76CIWeIt1mzRmjAhWQ--06J2-zw1Xq62ogjQLnjZuS8Tq7G_wLeoAVo9llR72FNDBXuJo-eSdVKBrFP6-Q-nCaWcQkCw
+    ingress-host: deernowl.cn

+ 14 - 0
pom.xml

@@ -131,6 +131,20 @@
 			<artifactId>junit</artifactId>
 			<scope>test</scope>
 		</dependency>
+
+		<!--k8s java client Dependencies-->
+		<dependency>
+			<groupId>io.kubernetes</groupId>
+			<artifactId>client-java</artifactId>
+			<version>3.0.0</version>
+			<scope>compile</scope>
+		</dependency>
+
+		<!--<dependency>-->
+			<!--<groupId>io.fabric8</groupId>-->
+			<!--<artifactId>kubernetes-client</artifactId>-->
+			<!--<version>3.1.12</version>-->
+		<!--</dependency>-->
 	</dependencies>
 
 	<build>

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

@@ -0,0 +1,15 @@
+package nju.seec.SEECdemo.logic.api.k8s;
+
+import nju.seec.SEECdemo.logic.api.k8s.vo.ApiResult;
+import nju.seec.SEECdemo.logic.api.k8s.vo.Deployment;
+
+/**
+ * author: rale
+ * createdAt: 12/23/18
+ */
+public interface DeploymentApi {
+
+    ApiResult<Void> createSync(Deployment deployment);
+
+    ApiResult<Void> createAsync();
+}

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

@@ -0,0 +1,12 @@
+package nju.seec.SEECdemo.logic.api.k8s;
+
+import nju.seec.SEECdemo.logic.api.k8s.vo.ApiResult;
+
+/**
+ * author: rale
+ * createdAt: 12/24/18
+ */
+public interface IngressApi {
+
+    ApiResult<String> create(String namespace, String name, String path, String serviceName, int port);
+}

+ 19 - 0
src/main/java/nju/seec/SEECdemo/logic/api/k8s/NamespaceApi.java

@@ -0,0 +1,19 @@
+package nju.seec.SEECdemo.logic.api.k8s;
+
+import io.kubernetes.client.models.V1Namespace;
+import nju.seec.SEECdemo.logic.api.k8s.vo.ApiResult;
+import nju.seec.SEECdemo.logic.api.k8s.vo.Namespace;
+
+/**
+ * author: rale
+ * createdAt: 12/22/18
+ */
+public interface NamespaceApi {
+
+    /**
+     * 新建一个命名空间
+     * @param namespace 命名空间名称需要唯一
+     * @return
+     */
+    ApiResult<Void> create(Namespace namespace);
+}

+ 69 - 0
src/main/java/nju/seec/SEECdemo/logic/api/k8s/impl/DeploymentApiImpl.java

@@ -0,0 +1,69 @@
+package nju.seec.SEECdemo.logic.api.k8s.impl;
+
+import io.kubernetes.client.ApiException;
+import io.kubernetes.client.apis.AppsV1Api;
+import io.kubernetes.client.apis.CoreV1Api;
+import io.kubernetes.client.models.V1Deployment;
+import io.kubernetes.client.models.V1ReplicationController;
+import io.kubernetes.client.models.V1Service;
+import nju.seec.SEECdemo.logic.api.k8s.DeploymentApi;
+import nju.seec.SEECdemo.logic.api.k8s.NamespaceApi;
+import nju.seec.SEECdemo.logic.api.k8s.vo.ApiResult;
+import nju.seec.SEECdemo.logic.api.k8s.vo.Deployment;
+import nju.seec.SEECdemo.util.LoggerUtil;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * author: rale
+ * createdAt: 12/23/18
+ */
+@Service
+public class DeploymentApiImpl implements DeploymentApi{
+
+    private static final Logger logger = LoggerUtil.getLogger(DeploymentApi.class);
+    @Autowired
+    private AppsV1Api appsV1Api;
+
+    @Autowired
+    private CoreV1Api coreV1Api;
+
+    @Autowired
+    private NamespaceApi namespaceApi;
+
+    @Override
+    public ApiResult<Void> createSync(Deployment deployment) {
+        //@todo 判断命名空间是否存在
+        try {
+            V1Deployment v1Deployment = deployment.toV1Deployment();
+            appsV1Api.createNamespacedDeployment(deployment.getNamespace(),v1Deployment, "true");
+        } catch (ApiException e) {
+            LoggerUtil.error(logger, e, "deployment创建失败, deployment={}, response={}", deployment, e.getResponseBody());
+            ApiResult<Void> result = new ApiResult<>();
+            result.setSuccess(false);
+            result.setErrorCode(e.getCode());
+            return result;
+        }
+
+        try {
+            V1Service v1Service = deployment.toV1Service();
+            coreV1Api.createNamespacedService(deployment.getNamespace(), v1Service, "true");
+        } catch (ApiException e) {
+            LoggerUtil.error(logger, e, "service创建失败, service={}, response={}", deployment, e.getResponseBody());
+            //@todo 清理现场 删除deployment
+            ApiResult<Void> result = new ApiResult<>();
+            result.setSuccess(false);
+            result.setErrorCode(e.getCode());
+            return result;
+        }
+        ApiResult<Void> result = new ApiResult<>();
+        result.setSuccess(true);
+        return result;
+    }
+
+    @Override
+    public ApiResult<Void> createAsync() {
+        return null;
+    }
+}

+ 50 - 0
src/main/java/nju/seec/SEECdemo/logic/api/k8s/impl/NamespaceApiImpl.java

@@ -0,0 +1,50 @@
+package nju.seec.SEECdemo.logic.api.k8s.impl;
+
+import io.kubernetes.client.ApiException;
+import io.kubernetes.client.apis.CoreV1Api;
+import io.kubernetes.client.models.V1Namespace;
+import nju.seec.SEECdemo.logic.api.k8s.NamespaceApi;
+import nju.seec.SEECdemo.logic.api.k8s.vo.ApiResult;
+import nju.seec.SEECdemo.logic.api.k8s.vo.Namespace;
+import nju.seec.SEECdemo.util.LoggerUtil;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import nju.seec.SEECdemo.logic.api.k8s.vo.ApiResult;
+
+/**
+ * author: rale
+ * createdAt: 12/22/18
+ */
+@Service
+public class NamespaceApiImpl implements NamespaceApi{
+
+    private Logger logger = LoggerUtil.getLogger(NamespaceApi.class);
+
+    @Autowired
+    private CoreV1Api coreV1Api;
+
+    /**
+     * @todo 此处没有明确项目的LimitRange
+     * @param namespace
+     * @return
+     * @see NamespaceApi#create(Namespace)
+     */
+    @Override
+    public ApiResult<Void> create(Namespace namespace) {
+        V1Namespace v1Namespace = namespace.toV1Namespace();
+        try {
+            coreV1Api.createNamespace(v1Namespace, "true");
+        } catch (ApiException e) {
+            LoggerUtil.error(logger, e, "Namespace创建失败,namespace={}, v1Namespace={}", namespace, v1Namespace);
+            ApiResult<Void> result = new ApiResult<>();
+            result.setSuccess(false);
+            result.setErrorCode(e.getCode());
+            return result;
+        }
+        ApiResult<Void> result = new ApiResult<>();
+        result.setSuccess(true);
+        return result;
+    }
+
+}

+ 75 - 0
src/main/java/nju/seec/SEECdemo/logic/api/k8s/util/BeanAnnouncement.java

@@ -0,0 +1,75 @@
+package nju.seec.SEECdemo.logic.api.k8s.util;
+
+import io.kubernetes.client.ApiClient;
+import io.kubernetes.client.Configuration;
+import io.kubernetes.client.apis.AppsV1Api;
+import io.kubernetes.client.apis.CoreV1Api;
+import io.kubernetes.client.apis.ExtensionsV1beta1Api;
+import io.kubernetes.client.util.Config;
+import nju.seec.SEECdemo.logic.vo.projecttemplate.ProjectTemplate;
+import nju.seec.SEECdemo.data.entity.resource.ResourceConfig;
+import nju.seec.SEECdemo.util.ApplicationProperties;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.stereotype.Component;
+import nju.seec.SEECdemo.util.ApplicationProperties.K8s;
+
+@Component
+public class BeanAnnouncement {
+
+    private final CoreV1Api coreV1Api;
+
+    private final AppsV1Api appsV1Api;
+
+    private final ExtensionsV1beta1Api extensionsV1beta1Api;
+
+    @Autowired
+    public BeanAnnouncement(ApplicationProperties applicationProperties) {
+        K8s k8s = applicationProperties.getK8s();
+        String apiServer = k8s.getApiServer();
+        String token = k8s.getToken();
+
+        ApiClient apiClient = Config.fromToken(apiServer, token);
+        Configuration.setDefaultApiClient(apiClient);
+        this.coreV1Api = new CoreV1Api();
+        this.appsV1Api = new AppsV1Api();
+        this.extensionsV1beta1Api = new ExtensionsV1beta1Api();
+    }
+
+    @Bean
+    public CoreV1Api coreV1Api(){
+        return coreV1Api;
+    }
+
+    @Bean
+    public AppsV1Api appsV1Api() {
+        return appsV1Api;
+    }
+
+    @Bean
+    public ExtensionsV1beta1Api extensionsV1beta1Api(){
+        return extensionsV1beta1Api;
+    }
+
+    /**
+     * 软工二的项目模板
+     * @return
+     */
+    @Bean
+    public ProjectTemplate seecIITemplate() {
+        ProjectTemplate projectTemplate = new ProjectTemplate();
+        ResourceConfig resourceConfig = new ResourceConfig();
+        resourceConfig.setDefaultCPU(5);
+        resourceConfig.setMaxCPU(10);
+        resourceConfig.setMinCPU(5);
+        resourceConfig.setDefaultMemory(100);
+        resourceConfig.setMaxMemory(500);
+        resourceConfig.setMinMemory(100);
+        resourceConfig.setDefaultStorage(0);
+        resourceConfig.setMaxStorage(1000);
+        projectTemplate.setDefaultResourceConfig(resourceConfig);
+        projectTemplate.setName("软工二大作业");
+        projectTemplate.setDescription("部署单个应用到部署项目下,应用集中连接唯一的数据库");
+        return projectTemplate;
+    }
+}

+ 26 - 0
src/main/java/nju/seec/SEECdemo/logic/api/k8s/vo/ApiResult.java

@@ -0,0 +1,26 @@
+package nju.seec.SEECdemo.logic.api.k8s.vo;
+
+import lombok.Data;
+import org.springframework.data.annotation.ReadOnlyProperty;
+
+/**
+ * author: rale
+ * createdAt: 12/23/18
+ */
+
+@Data
+public class ApiResult<T> {
+
+    @ReadOnlyProperty
+    private boolean success;
+
+    private int errorCode;
+
+    private String errorDescription;
+
+    private T value;
+
+    public static final int CONFLICT = 409;//命名冲突
+
+    public static final int NOT_EXIST = 800; //资源不存在
+}

+ 32 - 0
src/main/java/nju/seec/SEECdemo/logic/api/k8s/vo/Constants.java

@@ -0,0 +1,32 @@
+package nju.seec.SEECdemo.logic.api.k8s.vo;
+
+public abstract class Constants {
+
+	//标签域名前缀
+	private static final String LABEL_PREFIX = "demo.seec.nju.cn";
+	//课程前缀
+	static final String TYPE_LABEL = LABEL_PREFIX + "/type";
+
+	static final String DESCRIPTION_LABEL = LABEL_PREFIX + "/description";
+
+	static final String APPLICATION_LABEL = LABEL_PREFIX + "/app";
+	// 用户namespace的label
+	static final String USERSPACE_LABEL = LABEL_PREFIX +"/userspace";
+	// pod的label
+	static final String SELECTOR_LABEL = LABEL_PREFIX + "/app";
+	// 非对外service的label
+	static final String INTERNAL_LABEL = LABEL_PREFIX + "/internal";
+	// 对外service的label
+	static final String EXTERNAL_LABEL = LABEL_PREFIX + "/external";
+	// 持久卷访问模式
+	static final String STORAGE_ACCESS_MODE = "ReadWriteMany";
+	// 持久卷存储类型
+	static final String STORAGE_CLASS = "glusterfs";
+
+	static final String RESOURCE_STORAGE = "storage";
+
+	static final String NAME_PATTERN = "^[0-9a-zA-Z-]{8,}$";
+
+	static final String NAMESPACE_API_VERSION = "v1";
+
+}

+ 75 - 0
src/main/java/nju/seec/SEECdemo/logic/api/k8s/vo/Container.java

@@ -0,0 +1,75 @@
+package nju.seec.SEECdemo.logic.api.k8s.vo;
+
+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;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static nju.seec.SEECdemo.logic.api.k8s.vo.Constants.NAME_PATTERN;
+
+/**
+ * author: rale
+ * createdAt: 12/23/18
+ */
+@Data
+@NoArgsConstructor
+public class Container {
+
+    @NotNull
+    @Pattern(regexp = NAME_PATTERN)
+    private String name;
+
+    @NotBlank
+    private String image;
+
+
+    private Set<ContainerPort>  ports = new HashSet<>();
+
+    /* 启动指令的参数 */
+    private List<String> args;
+
+    /* 启动指令 */
+    private List<String> command;
+
+    /* 环境变量 */
+    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(){
+        V1Container container = new V1ContainerBuilder()
+                .withName(name)
+                .withImage(image)
+                .withImagePullPolicy("Always")
+                .withArgs(args)
+                .withCommand(command)
+                .withPorts(ports.stream().map(ContainerPort::toV1ContainerPort).collect(Collectors.toList()))
+                .withEnv(env
+                        .entrySet()
+                        .stream()
+                        .map(entry ->
+                                new V1EnvVarBuilder()
+                                        .withName(entry.getKey())
+                                        .withValue(entry.getValue())
+                                        .build())
+                        .collect(Collectors.toList())
+                )
+                .build();
+        return container;
+    }
+}

+ 51 - 0
src/main/java/nju/seec/SEECdemo/logic/api/k8s/vo/ContainerPort.java

@@ -0,0 +1,51 @@
+package nju.seec.SEECdemo.logic.api.k8s.vo;
+
+import io.kubernetes.client.models.V1ContainerPort;
+import io.kubernetes.client.models.V1ContainerPortBuilder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.hibernate.validator.constraints.Range;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
+
+import static nju.seec.SEECdemo.logic.api.k8s.vo.Constants.NAME_PATTERN;
+
+/**
+ * author: rale
+ * createdAt: 12/23/18
+ */
+@Data
+@EqualsAndHashCode
+@NoArgsConstructor
+public class ContainerPort {
+
+    @NotNull
+    @Pattern(regexp = NAME_PATTERN)
+    private String name;
+
+    @NotNull
+    @Range(min = 1, max = 65535)
+    private int port;
+
+    @NotNull
+    @Pattern(regexp = "^(TCP|UDP)$")
+    private String protocol;
+
+    public ContainerPort(V1ContainerPort v1ContainerPort) {
+        ContainerPort containerPort = new ContainerPort();
+        containerPort.setName(v1ContainerPort.getName());
+        containerPort.setPort(v1ContainerPort.getContainerPort());
+        containerPort.setProtocol(v1ContainerPort.getProtocol());
+    }
+    public V1ContainerPort toV1ContainerPort(){
+        V1ContainerPort v1ContainerPort = new V1ContainerPortBuilder()
+                .withContainerPort(port)
+                .withName(name)
+                .withProtocol(protocol)
+                .build();
+        return v1ContainerPort;
+    }
+
+}

+ 109 - 0
src/main/java/nju/seec/SEECdemo/logic/api/k8s/vo/Deployment.java

@@ -0,0 +1,109 @@
+package nju.seec.SEECdemo.logic.api.k8s.vo;
+
+import io.kubernetes.client.models.*;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.hibernate.validator.constraints.Range;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static nju.seec.SEECdemo.logic.api.k8s.vo.Constants.APPLICATION_LABEL;
+
+/**
+ * author: rale
+ * createdAt: 12/23/18
+ */
+@Data
+@NoArgsConstructor
+public class Deployment {
+
+    private static final String DEPLOYMENT_VERSION = "apps/v1";
+
+    private static final String DEPLOYMENT_KIND = "Deployment";
+
+    private static final String SERVICE_VERSION = "v1";
+
+    private static final String SERVICE_KIND = "Service";
+
+    @NotEmpty
+    private String name;
+
+    @NotNull
+    private String namespace;
+
+    //@todo 加上部署应用的类型,如数据库,web应用
+
+    @Range(min = 1, max = 3)
+    private int replicas;
+
+    private List<Container> containers = new ArrayList<>();
+
+    private Set<ServicePort> servicePorts = new HashSet<>();
+
+    public V1Deployment toV1Deployment(){
+        V1Deployment v1Deployment = new V1DeploymentBuilder()
+                .withApiVersion(DEPLOYMENT_VERSION)
+                .withKind(DEPLOYMENT_KIND)
+                .withMetadata(this.toV1ObjectMeta())
+                .withSpec(this.toV1DeploymentSpec())
+                .build();
+        return v1Deployment;
+    }
+
+    public V1Service toV1Service() {
+        V1Service v1Service = new V1ServiceBuilder()
+                .withApiVersion(SERVICE_VERSION)
+                .withKind(SERVICE_KIND)
+                .withMetadata(this.toV1ObjectMeta())
+                .withSpec(this.toV1ServiceSpec())
+                .build();
+        return v1Service;
+    }
+    private V1ObjectMeta toV1ObjectMeta(){
+        return new V1ObjectMetaBuilder()
+                .withName(name)
+                .withNamespace(namespace)
+                .build();
+    }
+
+    private V1LabelSelector toDeploymentV1LabelSelector() {
+        return new V1LabelSelectorBuilder()
+                .withMatchLabels(Collections.singletonMap(APPLICATION_LABEL, name))
+                .build();
+    }
+
+    private V1PodTemplateSpec toPodSpecTemplate(){
+        V1ObjectMeta v1ObjectMeta = new V1ObjectMetaBuilder()
+                .withLabels(Collections.singletonMap(APPLICATION_LABEL, name))
+                .build();
+        V1PodSpec v1PodSpec = new V1PodSpecBuilder()
+                .addAllToContainers(containers.stream().map(Container::toV1Container).collect(Collectors.toList()))
+                .withImagePullSecrets(new V1LocalObjectReference().name("regcred"))
+                .build();
+        V1PodTemplateSpec v1PodTemplateSpec = new V1PodTemplateSpecBuilder()
+                .withMetadata(v1ObjectMeta)
+                .withSpec(v1PodSpec)
+                .build();
+        return v1PodTemplateSpec;
+    }
+
+    private V1DeploymentSpec toV1DeploymentSpec(){
+        return new V1DeploymentSpecBuilder()
+                .withReplicas(replicas)
+                .withSelector(this.toDeploymentV1LabelSelector())
+                .withTemplate(this.toPodSpecTemplate())
+                .build();
+    }
+
+    private V1ServiceSpec toV1ServiceSpec(){
+        V1ServiceSpec serviceSpec = new V1ServiceSpecBuilder()
+                .withSelector(Collections.singletonMap(APPLICATION_LABEL, name))
+                .withPorts(servicePorts.stream().map(ServicePort::toV1ServicePort).collect(Collectors.toList()))
+                .build();
+        return serviceSpec;
+    }
+
+}

+ 65 - 0
src/main/java/nju/seec/SEECdemo/logic/api/k8s/vo/Namespace.java

@@ -0,0 +1,65 @@
+package nju.seec.SEECdemo.logic.api.k8s.vo;
+
+import io.kubernetes.client.models.*;
+import lombok.Data;
+import lombok.ToString;
+import org.springframework.util.CollectionUtils;
+
+import javax.validation.constraints.NotNull;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * author: rale
+ * createdAt: 12/22/18
+ */
+@ToString
+@Data
+public class Namespace {
+
+    private static final String KIND = "Namespace";
+
+    private static final String VERSION = "v1";
+    @NotNull
+    private String name;
+
+    private String description;
+
+    @NotNull
+    private String type;
+
+    public V1Namespace toV1Namespace(){
+        V1ObjectMeta v1ObjectMeta = new V1ObjectMetaBuilder()
+                .withName(name)
+                .withLabels(Collections.singletonMap(Constants.TYPE_LABEL, type))
+                .addToAnnotations(Constants.DESCRIPTION_LABEL, description == null ? name : description)
+                .build();
+        V1NamespaceSpec v1NamespaceSpec = new V1NamespaceSpecBuilder()
+                .build();
+        V1Namespace v1Namespace = new V1NamespaceBuilder()
+                .withApiVersion(VERSION)
+                .withKind(KIND)
+                .withMetadata(v1ObjectMeta)
+                .withSpec(v1NamespaceSpec)
+                .build();
+        return v1Namespace;
+
+    }
+
+    public Namespace fromV1Namespace(V1Namespace v1Namespace) {
+        Namespace namespace = new Namespace();
+        V1ObjectMeta v1ObjectMeta = v1Namespace.getMetadata();
+        if (v1ObjectMeta != null) {
+            namespace.setName(v1Namespace.getMetadata().getName());
+            Map<String, String> annotations = v1ObjectMeta.getAnnotations();
+            if (!CollectionUtils.isEmpty(annotations)) {
+                namespace.setDescription(annotations.get(Constants.DESCRIPTION_LABEL));
+            }
+            Map<String, String> labels = v1ObjectMeta.getLabels();
+            if (!CollectionUtils.isEmpty(labels)) {
+                namespace.setType(labels.get(Constants.TYPE_LABEL));
+            }
+        }
+        return namespace;
+    }
+}

+ 47 - 0
src/main/java/nju/seec/SEECdemo/logic/api/k8s/vo/ServicePort.java

@@ -0,0 +1,47 @@
+package nju.seec.SEECdemo.logic.api.k8s.vo;
+
+import io.kubernetes.client.custom.IntOrString;
+import io.kubernetes.client.models.V1ServicePort;
+import io.kubernetes.client.models.V1ServicePortBuilder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.hibernate.validator.constraints.Range;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * author: rale
+ * createdAt: 12/23/18
+ */
+@Data
+@NoArgsConstructor
+@EqualsAndHashCode
+public class ServicePort {
+
+    @NotNull
+    private String name;
+
+    @Range(min = 1, max = 633)
+    private int port;
+
+    @NotNull
+    private IntOrString targetPort;
+
+    public ServicePort(V1ServicePort v1ServicePort) {
+        this.name = v1ServicePort.getName();
+        this.port = v1ServicePort.getPort();
+        this.targetPort = v1ServicePort.getTargetPort();
+    }
+
+    public V1ServicePort toV1ServicePort(){
+        V1ServicePort v1ServicePort = new V1ServicePortBuilder()
+                .withName(name)
+                .withPort(port)
+                .withTargetPort(targetPort)
+                .build();
+        return v1ServicePort;
+    }
+
+
+}

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

@@ -0,0 +1,40 @@
+package nju.seec.SEECdemo.logic.service;
+
+import nju.seec.SEECdemo.logic.vo.ApplicationVO;
+import nju.seec.SEECdemo.web.dto.ApplicationDTO;
+
+public interface ApplicationService {
+
+    /**
+     * 新建应用
+     * @todo 挂载
+     * @param projectId 项目ID
+     * @param applicationDTO 应用详情
+     * @return
+     */
+    ApplicationVO create(long projectId, ApplicationDTO applicationDTO);
+
+    /**
+     * 删除应用
+     * @param name
+     * @return
+     */
+    boolean deleteByName(String name);
+
+    /**
+     * 更新应用
+     * 可更新的内容包括应用的镜像,应用的资源
+     * @param projectId
+     * @param applicationDTO
+     * @return
+     */
+    ApplicationVO update(long projectId, ApplicationDTO applicationDTO);
+
+    /**
+     * 查看应用的情况
+     * @param projectId
+     * @param name
+     * @return
+     */
+    ApplicationVO getByName(long projectId, String name);
+}

+ 38 - 0
src/main/java/nju/seec/SEECdemo/logic/service/ProjectService.java

@@ -0,0 +1,38 @@
+package nju.seec.SEECdemo.logic.service;
+
+import nju.seec.SEECdemo.logic.vo.ProjectVO;
+
+import java.util.Date;
+
+public interface ProjectService {
+
+    /**
+     * 创建软工二的项目
+     * //@Todo 后期希望能够根据课程类型选择课程模板
+     * @param projectName
+     * @param description
+     * @param startsAt
+     * @param endsAt
+     * @return
+     */
+     long createSeecIIProject(String projectName, String description, Date startsAt, Date endsAt);
+
+    /**
+     * 删除项目
+     * 如果项目底下仍有尚在运行的应用,则不会删除
+     * @param projectId
+     * @return
+     */
+     boolean deleteProject(long projectId);
+
+    /**
+     * 强制删除项目
+     * 无论项目之下是否有运行中的应用,都会删除
+     * 会强制删除项目之下的所有的应用,并回收资源
+     * @param projectId
+     * @return
+     */
+     boolean deleteProjectForce(long projectId);
+
+
+}

+ 40 - 0
src/main/java/nju/seec/SEECdemo/logic/service/impl/ApplicationServiceImpl.java

@@ -0,0 +1,40 @@
+package nju.seec.SEECdemo.logic.service.impl;
+
+import io.kubernetes.client.apis.CoreV1Api;
+import io.kubernetes.client.models.*;
+import nju.seec.SEECdemo.logic.api.k8s.DeploymentApi;
+import nju.seec.SEECdemo.logic.service.ApplicationService;
+import nju.seec.SEECdemo.logic.vo.ApplicationVO;
+import nju.seec.SEECdemo.logic.vo.ContainerVO;
+import nju.seec.SEECdemo.web.dto.ApplicationDTO;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class ApplicationServiceImpl implements ApplicationService{
+
+    @Autowired
+    private DeploymentApi deploymentApi;
+
+
+    @Override
+    public ApplicationVO create(long projectId, ApplicationDTO applicationDTO) {
+        return null;
+    }
+
+    @Override
+    public boolean deleteByName(String name) {
+        return false;
+    }
+
+    @Override
+    public ApplicationVO update(long projectId, ApplicationDTO applicationDTO) {
+        return null;
+    }
+
+    @Override
+    public ApplicationVO getByName(long projectId, String name) {
+        return null;
+    }
+}

+ 36 - 0
src/main/java/nju/seec/SEECdemo/logic/service/impl/ProjectServiceImpl.java

@@ -0,0 +1,36 @@
+package nju.seec.SEECdemo.logic.service.impl;
+
+import io.kubernetes.client.ApiException;
+import io.kubernetes.client.apis.CoreV1Api;
+import io.kubernetes.client.models.V1Namespace;
+import io.kubernetes.client.models.V1ObjectMeta;
+import nju.seec.SEECdemo.logic.service.ProjectService;
+import nju.seec.SEECdemo.logic.vo.ProjectVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+@Service
+public class ProjectServiceImpl implements ProjectService{
+    @Autowired
+    private CoreV1Api api;
+
+
+    @Override
+    public long createSeecIIProject(String projectName, String description, Date startsAt, Date endsAt) {
+        return 0;
+    }
+
+    @Override
+    public boolean deleteProject(long projectId) {
+        return false;
+    }
+
+    @Override
+    public boolean deleteProjectForce(long projectId) {
+        return false;
+    }
+}

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

@@ -0,0 +1,19 @@
+package nju.seec.SEECdemo.logic.vo;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+@Data
+@EqualsAndHashCode
+public class ApplicationVO {
+
+
+    private long id;
+
+    private long projectId;
+
+    private String name;
+
+}

+ 24 - 0
src/main/java/nju/seec/SEECdemo/logic/vo/ContainerVO.java

@@ -0,0 +1,24 @@
+package nju.seec.SEECdemo.logic.vo;
+
+import lombok.Data;
+import nju.seec.SEECdemo.data.entity.Port;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+@Data
+public class ContainerVO {
+
+    private String name;
+
+    private String image;
+
+    private String command;
+
+    private String entryPoint;
+
+    private Map<String, String> env = new HashMap<>();
+
+    private Set<Port> ports;
+}

+ 21 - 0
src/main/java/nju/seec/SEECdemo/logic/vo/ProjectVO.java

@@ -0,0 +1,21 @@
+package nju.seec.SEECdemo.logic.vo;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+@Data
+@EqualsAndHashCode
+public class ProjectVO {
+
+    private Long id;
+
+    private String name;
+
+    private String description;
+
+    private Date startsAt;
+
+    private Date endsAt;
+}

+ 24 - 0
src/main/java/nju/seec/SEECdemo/logic/vo/projecttemplate/ProjectTemplate.java

@@ -0,0 +1,24 @@
+package nju.seec.SEECdemo.logic.vo.projecttemplate;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import nju.seec.SEECdemo.data.entity.resource.ResourceConfig;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+
+@Data
+@EqualsAndHashCode
+public class ProjectTemplate {
+
+    private Long id;
+
+    private String name;
+
+    private String description;
+
+    private ResourceConfig defaultResourceConfig;
+
+    private List<Function> operations = new ArrayList<>();
+}

+ 13 - 0
src/main/java/nju/seec/SEECdemo/util/ApplicationProperties.java

@@ -32,6 +32,8 @@ public class ApplicationProperties {
 	private Mail mail = new Mail();
 	@Valid
 	private Storage storage = new Storage();
+	@Valid
+	private K8s k8s = new K8s();
 
 	@Data
 	public static class Gitlab {
@@ -85,4 +87,15 @@ public class ApplicationProperties {
 		private String type = "local";
 		private Map<String, String> properties = new HashMap<>();
 	}
+
+	@Data
+	public static class K8s{
+		@URL
+		@NotEmpty
+		private String apiServer;
+		@NotEmpty
+		private String token;
+		@NotEmpty
+		private String ingressHost;
+	}
 }

+ 37 - 0
src/main/java/nju/seec/SEECdemo/util/LoggerUtil.java

@@ -0,0 +1,37 @@
+package nju.seec.SEECdemo.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * author: rale
+ * createdAt: 12/22/18
+ * 日志工具
+ */
+public class LoggerUtil {
+
+    public static Logger getLogger(Class<?> className){
+        return LoggerFactory.getLogger(className);
+    }
+
+    public static void info(Logger logger, String info, Object... parameters){
+        logger.info(info, parameters);
+    }
+
+    public static void warn(Logger logger, String info, Object... parameters){
+        logger.warn(info, parameters);
+    }
+
+    public static void  error(Logger logger, String info, Object... parameters) {
+        logger.error(info, parameters);
+    }
+
+    public static void error(Logger logger, Exception e, String info, Object... parameters) {
+        logger.error("", e);
+        logger.error(info, parameters);
+    }
+
+    public static void debug(Logger logger, String info, Object... parameters) {
+        logger.debug(info, parameters);
+    }
+}

+ 25 - 0
src/main/java/nju/seec/SEECdemo/web/dto/ApplicationDTO.java

@@ -0,0 +1,25 @@
+package nju.seec.SEECdemo.web.dto;
+
+import lombok.Data;
+import org.hibernate.validator.constraints.Range;
+
+import javax.validation.constraints.NotEmpty;
+import java.util.List;
+
+@Data
+public class ApplicationDTO {
+
+    @NotEmpty
+    private String name;
+
+    @Range(min = 1, max = 3)
+    private int replicas;
+
+    private List<ContainerDTO> containers;
+
+    private int cpuRequest;
+
+    private int memoryRequest;
+
+    private int storageRequest;
+}

+ 19 - 0
src/main/java/nju/seec/SEECdemo/web/dto/ContainerDTO.java

@@ -0,0 +1,19 @@
+package nju.seec.SEECdemo.web.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@Data
+public class ContainerDTO {
+
+    @NotEmpty
+    private String image;
+
+    private Set<PortDTO> ports = new HashSet<>();
+
+}

+ 12 - 0
src/main/java/nju/seec/SEECdemo/web/dto/PortDTO.java

@@ -0,0 +1,12 @@
+package nju.seec.SEECdemo.web.dto;
+
+public class PortDTO {
+
+    private String name;
+
+    private int port;
+
+    private int targetPort;
+
+
+}

+ 45 - 0
src/main/resources/application-backup.yml

@@ -0,0 +1,45 @@
+spring:
+  jpa:
+    hibernate:
+      ddl-auto: update
+      naming:
+        implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
+        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
+  mail:
+    properties:
+      mail:
+        smtp:
+          auth: true
+          starttls:
+            enable: true
+            required: true
+  resources:
+    chain:
+      strategy:
+        content:
+          enabled: true
+          paths:
+            - /moocoder-frontend/**
+            - /js/**
+            - /css/**
+  servlet:
+    multipart:
+      max-file-size: 16MB
+      max-request-size: 16MB
+server:
+  tomcat:
+    remote-ip-header: X-Forwarded-For
+    protocol-header: X-Forwarded-Proto
+  servlet:
+    session:
+      persistent: true
+      store-dir: ${java.io.tmpdir}/moocoder-session
+      timeout: 1d
+  use-forward-headers: true
+management:
+  endpoints:
+    web:
+      base-path: /api/actuator
+  endpoint:
+    shutdown:
+      enabled: true

+ 74 - 0
src/main/resources/application-dev.yml

@@ -0,0 +1,74 @@
+spring:
+  datasource:
+    url: jdbc:mysql://10.1.1.198:13306/seec2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false
+    username: root
+    password: NJU67software
+  jpa:
+    hibernate:
+      ddl-auto: update
+      naming:
+        implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
+        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
+    database-platform: org.hibernate.dialect.MySQL57InnoDBDialect
+  mail:
+    properties:
+      mail:
+        smtp:
+          auth: true
+          starttls:
+            enable: true
+            required: true
+  resources:
+    chain:
+      strategy:
+        content:
+          enabled: true
+          paths:
+            - /moocoder-frontend/**
+            - /js/**
+            - /css/**
+  servlet:
+    multipart:
+      max-file-size: 16MB
+      max-request-size: 16MB
+server:
+  tomcat:
+    remote-ip-header: X-Forwarded-For
+    protocol-header: X-Forwarded-Proto
+  servlet:
+    session:
+      persistent: true
+      store-dir: ${java.io.tmpdir}/moocoder-session
+      timeout: 1d
+  use-forward-headers: true
+management:
+  endpoints:
+    web:
+      base-path: /api/actuator
+  endpoint:
+    shutdown:
+      enabled: true
+
+moocoder:
+  secret: 1qaz2wsx
+  gitlab:
+    host: http://10.1.1.172:8081
+    git-proxy: http://10.1.1.172:8081
+    username: root
+    token: 1hXhC2YLkuAb9iaykqWi
+  jenkins:
+    host: http://10.1.1.243:8082
+    username: root
+    token: 113cb5bd4a6fea342892a08c8d2e07dc68
+  docker:
+    nexus: 10.1.1.243:10082
+  mail:
+    from: noreply@moocoder.cn
+  storage:
+    type: local
+    properties:
+      location: ./problem
+  k8s:
+    api-server: https://10.1.1.198:6443
+    token: eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi10b2tlbi0yZ2o5NSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJhZG1pbiIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjRjZGQ0MGFlLTA1OWItMTFlOS1hMTIwLTAyMDA2MTI3MDAwNSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTphZG1pbiJ9.LwQ-LQlA_yhtndGLpveRfs0ZFFW971P1tchzJZP81s1vz0msb0q6dLJmcsCHaYhHWZ58_DCc0e7suGmUFEeMZcJL-sj3js1Cp5WNdnyz99Y3vZ-krHvNHGoc42jOrNUeO27Kb7uTDCmmKcCJSQ9gw8s2ie--7Z_kPAKVjhqr9nHtfrmmuIqFgwbIBQrNyJLMxaXs6tsCS4YWBuIQK1aOPWsijJNXA8W1acQ7p95P3tBGYMq17AaOEdeK8m76CIWeIt1mzRmjAhWQ--06J2-zw1Xq62ogjQLnjZuS8Tq7G_wLeoAVo9llR72FNDBXuJo-eSdVKBrFP6-Q-nCaWcQkCw
+    ingress-host: deernowl.cn

+ 45 - 0
src/main/resources/application-test.yml

@@ -0,0 +1,45 @@
+spring:
+  jpa:
+    hibernate:
+      ddl-auto: update
+      naming:
+        implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
+        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
+  mail:
+    properties:
+      mail:
+        smtp:
+          auth: true
+          starttls:
+            enable: true
+            required: true
+  resources:
+    chain:
+      strategy:
+        content:
+          enabled: true
+          paths:
+            - /moocoder-frontend/**
+            - /js/**
+            - /css/**
+  servlet:
+    multipart:
+      max-file-size: 16MB
+      max-request-size: 16MB
+server:
+  tomcat:
+    remote-ip-header: X-Forwarded-For
+    protocol-header: X-Forwarded-Proto
+  servlet:
+    session:
+      persistent: true
+      store-dir: ${java.io.tmpdir}/moocoder-session
+      timeout: 1d
+  use-forward-headers: true
+management:
+  endpoints:
+    web:
+      base-path: /api/actuator
+  endpoint:
+    shutdown:
+      enabled: true

+ 2 - 44
src/main/resources/application.yml

@@ -1,45 +1,3 @@
 spring:
-  jpa:
-    hibernate:
-      ddl-auto: update
-      naming:
-        implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
-        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
-  mail:
-    properties:
-      mail:
-        smtp:
-          auth: true
-          starttls:
-            enable: true
-            required: true
-  resources:
-    chain:
-      strategy:
-        content:
-          enabled: true
-          paths:
-            - /moocoder-frontend/**
-            - /js/**
-            - /css/**
-  servlet:
-    multipart:
-      max-file-size: 16MB
-      max-request-size: 16MB
-server:
-  tomcat:
-    remote-ip-header: X-Forwarded-For
-    protocol-header: X-Forwarded-Proto
-  servlet:
-    session:
-      persistent: true
-      store-dir: ${java.io.tmpdir}/moocoder-session
-      timeout: 1d
-  use-forward-headers: true
-management:
-  endpoints:
-    web:
-      base-path: /api/actuator
-  endpoint:
-    shutdown:
-      enabled: true
+  profiles:
+    active: dev

+ 57 - 0
src/test/java/nju/seec/SEECdemo/service/api/DeploymentApiImplTest.java

@@ -0,0 +1,57 @@
+package nju.seec.SEECdemo.service.api;
+
+import io.kubernetes.client.custom.IntOrString;
+import nju.seec.SEECdemo.logic.api.k8s.DeploymentApi;
+import nju.seec.SEECdemo.logic.api.k8s.vo.Container;
+import nju.seec.SEECdemo.logic.api.k8s.vo.ContainerPort;
+import nju.seec.SEECdemo.logic.api.k8s.vo.Deployment;
+import nju.seec.SEECdemo.logic.api.k8s.vo.ServicePort;
+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.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * author: rale
+ * createdAt: 12/24/18
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class DeploymentApiImplTest {
+
+    @Autowired
+    private DeploymentApi deploymentApi;
+
+    @Test
+    public void testCreate() {
+        Deployment deployment = new Deployment();
+        deployment.setName("test");
+        deployment.setNamespace("group24");
+        deployment.setReplicas(1);
+
+        ServicePort servicePort = new ServicePort();
+        servicePort.setPort(80);
+        servicePort.setTargetPort(new IntOrString(8081));
+        Set<ServicePort> servicePorts = new HashSet<>();
+        servicePorts.add(servicePort);
+        deployment.setServicePorts(servicePorts);
+
+        Container container = new Container();
+        container.setName("test-container");
+        container.setImage("10.1.1.243:18082/container_demo:1.0");
+        ContainerPort containerPort = new ContainerPort();
+        containerPort.setPort(8081);
+        containerPort.setProtocol("TCP");
+        Set<ContainerPort> ports = new HashSet<>();
+        ports.add(containerPort);
+        container.setPorts(ports);
+        deployment.setContainers(Collections.singletonList(container));
+
+        deploymentApi.createSync(deployment);
+    }
+}

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

@@ -0,0 +1,30 @@
+package nju.seec.SEECdemo.service.api;
+
+import nju.seec.SEECdemo.logic.api.k8s.NamespaceApi;
+import nju.seec.SEECdemo.logic.api.k8s.vo.Namespace;
+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;
+
+/**
+ * author: rale
+ * createdAt: 12/22/18
+ */
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class NamespaceApiImplTest {
+
+    @Autowired
+    private NamespaceApi namespaceApi;
+
+    @Test
+    public void testCreate() {
+        Namespace namespace = new Namespace();
+        namespace.setName("group21");
+        namespace.setDescription("这是软工二的大作业");
+        namespaceApi.create(namespace);
+    }
+}