This is an automated email from the ASF dual-hosted git repository.

jiwq pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/submarine.git


The following commit(s) were added to refs/heads/master by this push:
     new d65ed1a  SUBMARINE-586. Add the get/delete api & tests.
d65ed1a is described below

commit d65ed1a89ef77b7d0ff05ed674c4bf122a635c55
Author: Ryan Lo <[email protected]>
AuthorDate: Sat Aug 22 14:07:43 2020 +0800

    SUBMARINE-586. Add the get/delete api & tests.
    
    ### What is this PR for?
    User can get/delete notebook instances with REST API
    GET request to the URI (/api/v1/notebook/{id}) could get detailed info 
about the notebook with the notebook ID.
    DELETE request to the URI (/api/v1/notebook/{id}) could delete the notebook 
instance with the notebook ID
    
    ### What type of PR is it?
    [Feature]
    
    ### Todos
    * [ ] - Task
    
    ### What is the Jira issue?
    [SUBMARINE-586](https://issues.apache.org/jira/browse/SUBMARINE-586)
    
    ### How should this be tested?
    [travis 
CI](https://travis-ci.org/github/lowc1012/submarine/builds/719366355)
    
    ### Screenshots (if appropriate)
    <img width="583" alt="螢幕快照 2020-08-22 下午2 11 39" 
src="https://user-images.githubusercontent.com/52355146/90950143-8a465280-e481-11ea-82bf-d81097bd57c5.png";>
    
    ### Questions:
    * Does the licenses files need update? No
    * Is there breaking changes for older versions? No
    * Does this needs documentation? No
    
    Author: Ryan Lo <[email protected]>
    
    Closes #384 from lowc1012/SUBMARINE-586 and squashes the following commits:
    
    c2d00e8 [Ryan Lo] SUBMARINE-586. Update json response
    387a62f [Ryan Lo] SUBMARINE-586. Add the get/delete api & tests.
---
 helm-charts/submarine/templates/rbac.yaml          | 106 +++++----
 .../manifests/submarine-cluster/rbac.yaml          |   1 +
 .../submarine/server/api/notebook/Notebook.java    |  26 ++
 .../submarine/server/notebook/NotebookManager.java |  50 +++-
 .../submarine/server/rest/NotebookRestApi.java     |  16 +-
 .../src/test/resources/environment/test_env_3.json |  10 +
 .../server/submitter/k8s/K8sSubmitter.java         |  40 +++-
 .../src/test/resources/notebook_req.json           |   2 +-
 .../apache/submarine/rest/NotebookRestApiIT.java   | 261 +++++++++++++++++++++
 .../src/test/resources/notebook/notebook-req.json  |  15 ++
 .../src/test/resources/notebook/notebook-req.yaml  |  10 +
 11 files changed, 464 insertions(+), 73 deletions(-)

diff --git a/helm-charts/submarine/templates/rbac.yaml 
b/helm-charts/submarine/templates/rbac.yaml
index fe0f5a5..c108122 100644
--- a/helm-charts/submarine/templates/rbac.yaml
+++ b/helm-charts/submarine/templates/rbac.yaml
@@ -1,52 +1,54 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#    http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
-metadata:
-  name: "{{ .Values.submarine.server.name }}"
-rules:
-- apiGroups:
-  - kubeflow.org
-  resources:
-  - tfjobs
-  - tfjobs/status
-  - pytorchjobs
-  - pytorchjobs/status
-  verbs:
-  - get
-  - list
-  - watch
-  - create
-  - delete
-  - deletecollection
-  - patch
-  - update
-  
----
-kind: ClusterRoleBinding
-apiVersion: rbac.authorization.k8s.io/v1
-metadata:
-  name: "{{ .Values.submarine.server.name }}"
-subjects:
-- kind: ServiceAccount
-  namespace: {{ .Release.Namespace }}
-  name: "{{ .Values.submarine.server.name }}"
-roleRef:
-  kind: ClusterRole
-  name: "{{ .Values.submarine.server.name }}"
-  apiGroup: ""
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: "{{ .Values.submarine.server.name }}"
+rules:
+- apiGroups:
+  - kubeflow.org
+  resources:
+  - tfjobs
+  - tfjobs/status
+  - pytorchjobs
+  - pytorchjobs/status
+  - notebooks
+  - notebooks/status
+  verbs:
+  - get
+  - list
+  - watch
+  - create
+  - delete
+  - deletecollection
+  - patch
+  - update
+
+---
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+  name: "{{ .Values.submarine.server.name }}"
+subjects:
+- kind: ServiceAccount
+  namespace: {{ .Release.Namespace }}
+  name: "{{ .Values.submarine.server.name }}"
+roleRef:
+  kind: ClusterRole
+  name: "{{ .Values.submarine.server.name }}"
+  apiGroup: ""
diff --git a/submarine-cloud/manifests/submarine-cluster/rbac.yaml 
b/submarine-cloud/manifests/submarine-cluster/rbac.yaml
index a8ecd37..6687a81 100644
--- a/submarine-cloud/manifests/submarine-cluster/rbac.yaml
+++ b/submarine-cloud/manifests/submarine-cluster/rbac.yaml
@@ -33,6 +33,7 @@ items:
         resources:
           - tfjobs
           - pytorchjobs
+          - notebooks
         verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
   - apiVersion: rbac.authorization.k8s.io/v1beta1
     kind: ClusterRoleBinding
diff --git 
a/submarine-server/server-api/src/main/java/org/apache/submarine/server/api/notebook/Notebook.java
 
b/submarine-server/server-api/src/main/java/org/apache/submarine/server/api/notebook/Notebook.java
index b71aded..1e0ea36 100644
--- 
a/submarine-server/server-api/src/main/java/org/apache/submarine/server/api/notebook/Notebook.java
+++ 
b/submarine-server/server-api/src/main/java/org/apache/submarine/server/api/notebook/Notebook.java
@@ -117,4 +117,30 @@ public class Notebook {
     }
   }
 
+  public void rebuild(Notebook notebook) {
+    if (notebook != null) {
+      if (notebook.getName() != null) {
+        this.setName(notebook.getName());
+      }
+      if (notebook.getUid() != null) {
+        this.setUid(notebook.getUid());
+      }
+      if (notebook.getUrl() != null) {
+        this.setUrl(notebook.getUrl());
+      }
+      if (notebook.getSpec() != null) {
+        this.setSpec(notebook.getSpec());
+      }
+      if (notebook.getStatus() != null) {
+        this.setStatus(notebook.getStatus());
+      }
+      if (notebook.getCreatedTime() != null) {
+        this.setCreatedTime(notebook.getCreatedTime());
+      }
+      if (notebook.getDeletedTime() != null) {
+        this.setDeletedTime(notebook.getDeletedTime());
+      }
+    }
+  }
+
 }
diff --git 
a/submarine-server/server-core/src/main/java/org/apache/submarine/server/notebook/NotebookManager.java
 
b/submarine-server/server-core/src/main/java/org/apache/submarine/server/notebook/NotebookManager.java
index 5547c23..1e78022 100644
--- 
a/submarine-server/server-core/src/main/java/org/apache/submarine/server/notebook/NotebookManager.java
+++ 
b/submarine-server/server-core/src/main/java/org/apache/submarine/server/notebook/NotebookManager.java
@@ -23,12 +23,16 @@ import 
org.apache.submarine.commons.utils.exception.SubmarineRuntimeException;
 import org.apache.submarine.server.SubmarineServer;
 import org.apache.submarine.server.SubmitterManager;
 import org.apache.submarine.server.api.Submitter;
+import org.apache.submarine.server.api.environment.Environment;
 import org.apache.submarine.server.api.notebook.Notebook;
 import org.apache.submarine.server.api.notebook.NotebookId;
 import org.apache.submarine.server.api.spec.NotebookSpec;
+import org.apache.submarine.server.environment.EnvironmentManager;
 
 import javax.ws.rs.core.Response;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -78,19 +82,34 @@ public class NotebookManager {
     Notebook notebook = submitter.createNotebook(spec);
     notebook.setNotebookId(generateNotebookId());
     notebook.setSpec(spec);
+    NotebookSpec notebookSpec = notebook.getSpec();
+    EnvironmentManager environmentManager = EnvironmentManager.getInstance();
+    Environment environment = 
environmentManager.getEnvironment(spec.getEnvironment().getName());
+    if (environment.getEnvironmentSpec() != null) {
+      notebookSpec.setEnvironment(environment.getEnvironmentSpec());
+    }
     cachedNotebookMap.putIfAbsent(notebook.getNotebookId().toString(), 
notebook);
     return notebook;
   }
 
   /**
    * List notebook instances
-   * @param status status, if null will return all notebooks
+   * @param namespace namespace, if null will return all notebooks
    * @return list
    * @throws SubmarineRuntimeException the service error
    */
-  public List<Notebook> listNotebooksByStatus(String status) throws 
SubmarineRuntimeException {
-    //TODO(ryan): implement the method
-    return null;
+  public List<Notebook> listNotebooksByNamespace(String namespace) throws 
SubmarineRuntimeException {
+    List<Notebook> notebookList = new ArrayList<>();
+    for (Map.Entry<String, Notebook> entry : cachedNotebookMap.entrySet()) {
+      Notebook notebook = entry.getValue();
+      Notebook patchNotebook = submitter.findNotebook(notebook.getSpec());
+      if (namespace == null || namespace.length() == 0
+              || 
namespace.toLowerCase().equals(patchNotebook.getSpec().getMeta().getNamespace()))
 {
+        notebook.rebuild(patchNotebook);
+        notebookList.add(notebook);
+      }
+    }
+    return notebookList;
   }
 
   /**
@@ -100,8 +119,12 @@ public class NotebookManager {
    * @throws SubmarineRuntimeException the service error
    */
   public Notebook getNotebook(String id) throws SubmarineRuntimeException {
-    //TODO(ryan): implement the method
-    return null;
+    checkNotebookId(id);
+    Notebook notebook = cachedNotebookMap.get(id);
+    NotebookSpec spec = notebook.getSpec();
+    Notebook patchNotebook = submitter.findNotebook(spec);
+    notebook.rebuild(patchNotebook);
+    return notebook;
   }
 
   /**
@@ -111,8 +134,12 @@ public class NotebookManager {
    * @throws SubmarineRuntimeException the service error
    */
   public Notebook deleteNotebook(String id) throws SubmarineRuntimeException {
-    //TODO(ryan): implement the method
-    return null;
+    checkNotebookId(id);
+    Notebook notebook = cachedNotebookMap.remove(id);
+    NotebookSpec spec = notebook.getSpec();
+    Notebook patchNotebook = submitter.deleteNotebook(spec);
+    notebook.rebuild(patchNotebook);
+    return notebook;
   }
 
   /**
@@ -136,4 +163,11 @@ public class NotebookManager {
     }
   }
 
+  private void checkNotebookId(String id) throws SubmarineRuntimeException {
+    NotebookId notebookId = NotebookId.fromString(id);
+    if (notebookId == null || !cachedNotebookMap.containsKey(id)) {
+      throw new 
SubmarineRuntimeException(Response.Status.NOT_FOUND.getStatusCode(),
+              "Not found notebook server.");
+    }
+  }
 }
diff --git 
a/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/NotebookRestApi.java
 
b/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/NotebookRestApi.java
index ac61cb2..bfa4c91 100644
--- 
a/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/NotebookRestApi.java
+++ 
b/submarine-server/server-core/src/main/java/org/apache/submarine/server/rest/NotebookRestApi.java
@@ -43,7 +43,7 @@ import javax.ws.rs.core.Response;
 import java.util.List;
 
 /**
- * Notebook Service REST API v1
+ * Notebook REST API v1. It can accept {@link NotebookSpec} to create a 
notebook server.
  */
 @Path(RestConstants.V1 + "/" + RestConstants.NOTEBOOK)
 @Produces({MediaType.APPLICATION_JSON + "; " + RestConstants.CHARSET_UTF8})
@@ -94,8 +94,8 @@ public class NotebookRestApi {
   }
 
   /**
-   * List all notebooks created by the user
-   * @param status status
+   * List all notebooks
+   * @param namespace namespace
    * @return notebook list
    */
   @GET
@@ -105,11 +105,11 @@ public class NotebookRestApi {
           responses = {
                   @ApiResponse(description = "successful operation", content = 
@Content(
                           schema = @Schema(implementation = 
JsonResponse.class)))})
-  public Response listNotebooks(@QueryParam("status") String status) {
+  public Response listNotebooks(@QueryParam("namespace") String namespace) {
     try {
-      List<Notebook> notebookList = 
notebookManager.listNotebooksByStatus(status);
+      List<Notebook> notebookList = 
notebookManager.listNotebooksByNamespace(namespace);
       return new 
JsonResponse.Builder<List<Notebook>>(Response.Status.OK).success(true)
-              .result(notebookList).build();
+              .message("List all notebook 
instances").result(notebookList).build();
     } catch (SubmarineRuntimeException e) {
       return parseNotebookServiceException(e);
     }
@@ -134,7 +134,7 @@ public class NotebookRestApi {
     try {
       Notebook notebook = notebookManager.getNotebook(id);
       return new 
JsonResponse.Builder<Notebook>(Response.Status.OK).success(true)
-              .result(notebook).build();
+              .message("Get the notebook instance").result(notebook).build();
     } catch (SubmarineRuntimeException e) {
       return parseNotebookServiceException(e);
     }
@@ -159,7 +159,7 @@ public class NotebookRestApi {
     try {
       Notebook notebook = notebookManager.deleteNotebook(id);
       return new 
JsonResponse.Builder<Notebook>(Response.Status.OK).success(true)
-              .result(notebook).build();
+              .message("Delete the notebook 
instance").result(notebook).build();
     } catch (SubmarineRuntimeException e) {
       return parseNotebookServiceException(e);
     }
diff --git 
a/submarine-server/server-core/src/test/resources/environment/test_env_3.json 
b/submarine-server/server-core/src/test/resources/environment/test_env_3.json
new file mode 100644
index 0000000..0e4469a
--- /dev/null
+++ 
b/submarine-server/server-core/src/test/resources/environment/test_env_3.json
@@ -0,0 +1,10 @@
+{
+  "name": "my-submarine-env",
+  "dockerImage" : "apache/submarine:jupyter-notebook-0.5.0-SNAPSHOT",
+  "kernelSpec" : {
+    "name" : "team_default_python_3.7",
+    "channels" : ["defaults"],
+    "dependencies" :
+      ["_ipyw_jlab_nb_ext_conf=0.1.0=py37_0"]
+  }
+}
diff --git 
a/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/K8sSubmitter.java
 
b/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/K8sSubmitter.java
index 5fe17ad..cdc33f0 100644
--- 
a/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/K8sSubmitter.java
+++ 
b/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/K8sSubmitter.java
@@ -21,6 +21,8 @@ package org.apache.submarine.server.submitter.k8s;
 
 import java.io.FileReader;
 import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
 
 import com.google.gson.Gson;
 import com.google.gson.JsonSyntaxException;
@@ -30,6 +32,7 @@ import io.kubernetes.client.Configuration;
 import io.kubernetes.client.JSON;
 import io.kubernetes.client.apis.CoreV1Api;
 import io.kubernetes.client.apis.CustomObjectsApi;
+import io.kubernetes.client.models.V1DeleteOptionsBuilder;
 import io.kubernetes.client.models.V1Pod;
 import io.kubernetes.client.models.V1PodList;
 import io.kubernetes.client.models.V1Status;
@@ -252,14 +255,34 @@ public class K8sSubmitter implements Submitter {
 
   @Override
   public Notebook findNotebook(NotebookSpec spec) throws 
SubmarineRuntimeException {
-    // TODO(ryan): Implement this method
-    return null;
+    Notebook notebook;
+    try {
+      NotebookCR notebookCR = NotebookSpecParser.parseNotebook(spec);
+      Object object = api.getNamespacedCustomObject(notebookCR.getGroup(), 
notebookCR.getVersion(),
+              notebookCR.getMetadata().getNamespace(),
+              notebookCR.getPlural(), notebookCR.getMetadata().getName());
+      notebook = parseResponseObject(object);
+    } catch (ApiException e) {
+      throw new SubmarineRuntimeException(e.getCode(), e.getMessage());
+    }
+    return notebook;
   }
 
   @Override
   public Notebook deleteNotebook(NotebookSpec spec) throws 
SubmarineRuntimeException {
-    // TODO(ryan): Implement this method
-    return null;
+    Notebook notebook;
+    try {
+      NotebookCR notebookCR = NotebookSpecParser.parseNotebook(spec);
+      Object object = api.deleteNamespacedCustomObject(notebookCR.getGroup(), 
notebookCR.getVersion(),
+              notebookCR.getMetadata().getNamespace(), notebookCR.getPlural(),
+              notebookCR.getMetadata().getName(),
+              new 
V1DeleteOptionsBuilder().withApiVersion(notebookCR.getApiVersion()).build(),
+              null, null, null);
+      notebook = parseResponseObject(object);
+    } catch (ApiException e) {
+      throw new SubmarineRuntimeException(e.getCode(), e.getMessage());
+    }
+    return notebook;
   }
 
   private Notebook parseResponseObject(Object obj) throws 
SubmarineRuntimeException {
@@ -280,6 +303,15 @@ public class K8sSubmitter implements Submitter {
         notebook.setCreatedTime(createdTime.toString());
         notebook.setStatus(Notebook.Status.STATUS_CREATED.getValue());
       }
+
+      // deleted notebook
+      if (notebookCR.getMetadata().getName() == null) {
+        SimpleDateFormat dateFormat = new 
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
+        Date current = new Date();
+        notebook.setDeletedTime(dateFormat.format(current));
+        notebook.setStatus(Notebook.Status.STATUS_DELETED.toString());
+      }
+
     } catch (JsonSyntaxException e) {
       LOG.error("K8s submitter: parse response object failed by " + 
e.getMessage(), e);
       throw new SubmarineRuntimeException(500, "K8s Submitter parse upstream 
response failed.");
diff --git 
a/submarine-server/server-submitter/submitter-k8s/src/test/resources/notebook_req.json
 
b/submarine-server/server-submitter/submitter-k8s/src/test/resources/notebook_req.json
index e1dace6..e46aec0 100644
--- 
a/submarine-server/server-submitter/submitter-k8s/src/test/resources/notebook_req.json
+++ 
b/submarine-server/server-submitter/submitter-k8s/src/test/resources/notebook_req.json
@@ -4,7 +4,7 @@
     "namespace": "default"
   },
   "environment": {
-    "image": "apache/submarine:tf2.1.0-jupyter"
+    "name": "my-submarine-env"
   },
   "spec": {
     "envVars": {
diff --git 
a/submarine-test/test-k8s/src/test/java/org/apache/submarine/rest/NotebookRestApiIT.java
 
b/submarine-test/test-k8s/src/test/java/org/apache/submarine/rest/NotebookRestApiIT.java
new file mode 100644
index 0000000..1359ed8
--- /dev/null
+++ 
b/submarine-test/test-k8s/src/test/java/org/apache/submarine/rest/NotebookRestApiIT.java
@@ -0,0 +1,261 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.submarine.rest;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import io.kubernetes.client.ApiClient;
+import io.kubernetes.client.ApiException;
+import io.kubernetes.client.Configuration;
+import io.kubernetes.client.JSON;
+import io.kubernetes.client.apis.CustomObjectsApi;
+import io.kubernetes.client.util.ClientBuilder;
+import io.kubernetes.client.util.KubeConfig;
+import org.apache.commons.httpclient.methods.DeleteMethod;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.submarine.server.AbstractSubmarineServerTest;
+import org.apache.submarine.server.api.environment.Environment;
+import org.apache.submarine.server.api.experiment.Experiment;
+import org.apache.submarine.server.api.notebook.Notebook;
+import org.apache.submarine.server.api.notebook.NotebookId;
+import org.apache.submarine.server.gson.NotebookIdDeserializer;
+import org.apache.submarine.server.gson.NotebookIdSerializer;
+import org.apache.submarine.server.response.JsonResponse;
+import org.apache.submarine.server.rest.RestConstants;
+import org.joda.time.DateTime;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Date;
+
+@SuppressWarnings("rawtypes")
+public class NotebookRestApiIT extends AbstractSubmarineServerTest {
+
+  private static final Logger LOG = 
LoggerFactory.getLogger(NotebookRestApiIT.class);
+
+  private static CustomObjectsApi k8sApi;
+  private static final String BASE_API_PATH = "/api/" + RestConstants.V1 + "/" 
+ RestConstants.NOTEBOOK;
+  public static final String VERSION = "v1";
+  public static final String GROUP = "kubeflow.org";
+  public static final String PLURAL = "notebooks";
+
+  private final Gson gson = new GsonBuilder()
+          .registerTypeAdapter(NotebookId.class, new NotebookIdSerializer())
+          .registerTypeAdapter(NotebookId.class, new NotebookIdDeserializer())
+          .create();
+
+  @BeforeClass
+  public static void startUp() throws IOException {
+    Assert.assertTrue(checkIfServerIsRunning());
+
+    // The kube config path defined by kind-cluster-build.sh
+    String confPath = System.getProperty("user.home") + 
"/.kube/kind-config-kind";
+    KubeConfig config = KubeConfig.loadKubeConfig(new FileReader(confPath));
+    ApiClient client = ClientBuilder.kubeconfig(config).build();
+    Configuration.setDefaultApiClient(client);
+    k8sApi = new CustomObjectsApi();
+  }
+
+  @Test
+  public void testServerPing() throws IOException {
+    GetMethod response = httpGet(BASE_API_PATH + "/" + RestConstants.PING);
+    String requestBody = response.getResponseBodyAsString();
+    Gson gson = new Gson();
+    JsonResponse jsonResponse = gson.fromJson(requestBody, JsonResponse.class);
+    Assert.assertEquals(Response.Status.OK.getStatusCode(), 
jsonResponse.getCode());
+    Assert.assertEquals("Pong", jsonResponse.getResult().toString());
+  }
+
+  @Test
+  public void testCreateNotebookWithJsonSpec() throws Exception {
+    // create environment
+    String envBody = loadContent("environment/test_env_3.json");
+    run(envBody, "application/json");
+
+    Gson gson = new GsonBuilder().create();
+    GetMethod getMethod = httpGet(ENV_PATH + "/" + ENV_NAME);
+    Assert.assertEquals(Response.Status.OK.getStatusCode(),
+            getMethod.getStatusCode());
+
+    String json = getMethod.getResponseBodyAsString();
+    JsonResponse jsonResponse = gson.fromJson(json, JsonResponse.class);
+    Assert.assertEquals(Response.Status.OK.getStatusCode(),
+            jsonResponse.getCode());
+
+    Environment getEnvironment =
+            gson.fromJson(gson.toJson(jsonResponse.getResult()), 
Environment.class);
+    Assert.assertEquals(ENV_NAME, 
getEnvironment.getEnvironmentSpec().getName());
+
+    String body = loadContent("notebook/notebook-req.json");
+    runTest(body, "application/json");
+
+    deleteEnvironment();
+  }
+
+  @Test
+  public void testCreateNotebookWithYamlSpec() throws Exception {
+    // create environment
+    String envBody = loadContent("environment/test_env_3.json");
+    run(envBody, "application/json");
+
+    Gson gson = new GsonBuilder().create();
+    GetMethod getMethod = httpGet(ENV_PATH + "/" + ENV_NAME);
+    Assert.assertEquals(Response.Status.OK.getStatusCode(),
+            getMethod.getStatusCode());
+
+    String json = getMethod.getResponseBodyAsString();
+    JsonResponse jsonResponse = gson.fromJson(json, JsonResponse.class);
+    Assert.assertEquals(Response.Status.OK.getStatusCode(),
+            jsonResponse.getCode());
+
+    Environment getEnvironment =
+            gson.fromJson(gson.toJson(jsonResponse.getResult()), 
Environment.class);
+    Assert.assertEquals(ENV_NAME, 
getEnvironment.getEnvironmentSpec().getName());
+
+    String body = loadContent("notebook/notebook-req.yaml");
+    runTest(body, "application/yaml");
+
+    deleteEnvironment();
+  }
+
+  @Test
+  public void testCreateNotebookWithInvalidSpec() throws Exception {
+    PostMethod postMethod = httpPost(BASE_API_PATH, "", 
MediaType.APPLICATION_JSON);
+    Assert.assertEquals(Response.Status.OK.getStatusCode(), 
postMethod.getStatusCode());
+
+    String json = postMethod.getResponseBodyAsString();
+    JsonResponse jsonResponse = gson.fromJson(json, JsonResponse.class);
+    Assert.assertEquals(Response.Status.OK.getStatusCode(), 
jsonResponse.getCode());
+  }
+
+  private void runTest(String body, String contentType) throws Exception {
+    // create
+    LOG.info("Create a notebook server by Notebook REST API");
+    PostMethod postMethod = httpPost(BASE_API_PATH, body, contentType);
+    Assert.assertEquals(Response.Status.OK.getStatusCode(), 
postMethod.getStatusCode());
+
+    String json = postMethod.getResponseBodyAsString();
+    JsonResponse jsonResponse = gson.fromJson(json, JsonResponse.class);
+    Assert.assertEquals(Response.Status.OK.getStatusCode(), 
jsonResponse.getCode());
+
+    Notebook createdNotebook = 
gson.fromJson(gson.toJson(jsonResponse.getResult()), Notebook.class);
+    verifyCreateNotebookApiResult(createdNotebook);
+
+    // find
+    GetMethod getMethod = httpGet(BASE_API_PATH + "/" + 
createdNotebook.getNotebookId().toString());
+    Assert.assertEquals(Response.Status.OK.getStatusCode(), 
getMethod.getStatusCode());
+
+    json = getMethod.getResponseBodyAsString();
+    jsonResponse = gson.fromJson(json, JsonResponse.class);
+    Assert.assertEquals(Response.Status.OK.getStatusCode(), 
jsonResponse.getCode());
+
+    Notebook foundNotebook = 
gson.fromJson(gson.toJson(jsonResponse.getResult()), Notebook.class);
+    verifyGetNotebookApiResult(createdNotebook, foundNotebook);
+
+    // delete
+    DeleteMethod deleteMethod =
+            httpDelete(BASE_API_PATH + "/" + 
createdNotebook.getNotebookId().toString());
+    Assert.assertEquals(Response.Status.OK.getStatusCode(), 
deleteMethod.getStatusCode());
+
+    json = deleteMethod.getResponseBodyAsString();
+    jsonResponse = gson.fromJson(json, JsonResponse.class);
+    Assert.assertEquals(Response.Status.OK.getStatusCode(), 
jsonResponse.getCode());
+
+    Notebook deletedNotebook = 
gson.fromJson(gson.toJson(jsonResponse.getResult()), Notebook.class);
+    verifyDeleteNotebookApiResult(createdNotebook, deletedNotebook);
+  }
+
+  private void verifyCreateNotebookApiResult(Notebook createdNotebook) {
+    Assert.assertNotNull(createdNotebook.getUid());
+    Assert.assertNotNull(createdNotebook.getCreatedTime());
+    Assert.assertEquals(Experiment.Status.STATUS_CREATED.getValue(), 
createdNotebook.getStatus());
+  }
+
+  private void verifyGetNotebookApiResult(Notebook createdNotebook,
+                                          Notebook foundNotebook) throws 
Exception {
+    Assert.assertEquals(createdNotebook.getNotebookId(), 
foundNotebook.getNotebookId());
+    Assert.assertEquals(createdNotebook.getUid(), foundNotebook.getUid());
+    Assert.assertEquals(createdNotebook.getCreatedTime(), 
foundNotebook.getCreatedTime());
+    Assert.assertEquals(createdNotebook.getName(), foundNotebook.getName());
+    assertGetK8sResult(foundNotebook);
+  }
+
+  private void verifyDeleteNotebookApiResult(Notebook createdNotebook,
+                                             Notebook deletedNotebook) {
+    Assert.assertEquals(createdNotebook.getName(), deletedNotebook.getName());
+    Assert.assertEquals(Notebook.Status.STATUS_DELETED.getValue(), 
deletedNotebook.getStatus());
+    assertDeleteK8sResult(deletedNotebook);
+  }
+
+  private void assertGetK8sResult(Notebook notebook) throws Exception {
+    JsonObject rootObject = getNotebookByK8sApi(GROUP, VERSION, 
notebook.getSpec().getMeta().getNamespace(),
+            PLURAL, notebook.getName());
+
+    JsonObject metadataObject = rootObject.getAsJsonObject("metadata");
+    String uid = metadataObject.getAsJsonPrimitive("uid").getAsString();
+    LOG.info("Uid from Notebook REST is {}", notebook.getUid());
+    LOG.info("Uid from K8s REST is {}", uid);
+    Assert.assertEquals(notebook.getUid(), uid);
+
+    JsonArray envVars = (JsonArray) rootObject.getAsJsonObject("spec")
+            .getAsJsonObject("template").getAsJsonObject("spec")
+            .getAsJsonArray("containers").get(0).getAsJsonObject().get("env");
+    Assert.assertNotNull("The environment command not found.", envVars);
+
+    String creationTimestamp =
+            
metadataObject.getAsJsonPrimitive("creationTimestamp").getAsString();
+    Date expectedDate = new DateTime(notebook.getCreatedTime()).toDate();
+    Date actualDate = new DateTime(creationTimestamp).toDate();
+    LOG.info("CreationTimestamp from Notebook REST is {}", expectedDate);
+    LOG.info("CreationTimestamp from K8s REST is {}", actualDate);
+    Assert.assertEquals(expectedDate, actualDate);
+  }
+
+  private void assertDeleteK8sResult(Notebook notebook) {
+    JsonObject rootObject = null;
+    try {
+      rootObject = getNotebookByK8sApi(GROUP, VERSION, 
notebook.getSpec().getMeta().getNamespace(),
+              PLURAL, notebook.getName());
+    } catch (ApiException e) {
+      Assert.assertEquals(Response.Status.NOT_FOUND.getStatusCode(), 
e.getCode());
+    } finally {
+      Assert.assertNull(rootObject);
+    }
+  }
+
+  private JsonObject getNotebookByK8sApi(String group, String version, String 
namespace, String plural,
+                                   String name) throws ApiException {
+    Object obj = k8sApi.getNamespacedCustomObject(group, version, namespace, 
plural, name);
+    Gson gson = new JSON().getGson();
+    JsonObject rootObject = gson.toJsonTree(obj).getAsJsonObject();
+    Assert.assertNotNull("Parse the K8s API Server response failed.", 
rootObject);
+    return rootObject;
+  }
+}
diff --git 
a/submarine-test/test-k8s/src/test/resources/notebook/notebook-req.json 
b/submarine-test/test-k8s/src/test/resources/notebook/notebook-req.json
new file mode 100644
index 0000000..0b95433
--- /dev/null
+++ b/submarine-test/test-k8s/src/test/resources/notebook/notebook-req.json
@@ -0,0 +1,15 @@
+{
+  "meta": {
+    "name": "test-nb",
+    "namespace": "default"
+  },
+  "environment": {
+    "name": "my-submarine-env"
+  },
+  "spec": {
+    "envVars": {
+      "TEST_ENV": "test"
+    },
+    "resources": "cpu=1,memory=1.0Gi"
+  }
+}
diff --git 
a/submarine-test/test-k8s/src/test/resources/notebook/notebook-req.yaml 
b/submarine-test/test-k8s/src/test/resources/notebook/notebook-req.yaml
new file mode 100644
index 0000000..824c32e
--- /dev/null
+++ b/submarine-test/test-k8s/src/test/resources/notebook/notebook-req.yaml
@@ -0,0 +1,10 @@
+---
+meta:
+  name: test-nb
+  namespace: default
+environment:
+  name: my-submarine-env
+spec:
+  envVars:
+    TEST_ENV: test
+  resources: cpu=1,memory=1.0Gi


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to