You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@submarine.apache.org by ji...@apache.org on 2020/12/16 15:35:48 UTC

[submarine] branch master updated: SUBMARINE-675. Sync the status of notebook

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 d104080  SUBMARINE-675. Sync the status of notebook
d104080 is described below

commit d104080a926d30bcc45ccd740f9489dd7365d0b5
Author: Ryan Lo <lo...@gmail.com>
AuthorDate: Thu Dec 3 12:45:32 2020 +0800

    SUBMARINE-675. Sync the status of notebook
    
    ### What is this PR for?
    1. The notebook REST api will return four states of notebook (creating/waiting/running/terminating) and its reason.
    
    - creating : Users submitted the request to create notebook and its pod haven't been created.
    - waiting :  Still running the operations it requires in order to complete start up: for example, pulling the container image.
    - running : The container is executing without issues and readyReplicas equals to 1
    - terminating : Users submitted the request to delete notebook
    
    2. Refactoring K8sSubmitter
    3. Update userdocs/k8s/api/notebook.md
    
    ### What type of PR is it?
    [Improvement | Documentation | Refactoring]
    
    ### Todos
    * [kobe860219  ] - Front-end : Using RxJS to do polling the status of notebook instance and adding spinner when notebook status is creating/waiting.
    
    ### What is the Jira issue?
    [SUBMARINE-675](https://issues.apache.org/jira/projects/SUBMARINE/issues/SUBMARINE-675)
    
    ### How should this be tested?
    [Travis CI](https://travis-ci.org/github/lowc1012/submarine/builds/747154671)
    
    ### Screenshots (if appropriate)
    <img width="1189" alt="image" src="https://user-images.githubusercontent.com/52355146/100850958-8209a480-34bf-11eb-83b3-cef183a893cd.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 <lo...@gmail.com>
    
    Closes #468 from lowc1012/SUBMARINE-675 and squashes the following commits:
    
    0dfe1a1 [Ryan Lo] convert to static methods
    d4f89b9 [Ryan Lo] SUBMARINE-675. update
    a9a4717 [Ryan Lo] SUBMARINE-675. Sync the status of notebook
---
 docs/userdocs/k8s/api/notebook.md                  |  20 ++-
 .../submarine/server/api/notebook/Notebook.java    |  18 ++-
 .../submarine/server/notebook/NotebookManager.java |   2 +
 .../server/submitter/k8s/K8sSubmitter.java         |  74 +---------
 .../server/submitter/k8s/model/NotebookCR.java     |  10 ++
 .../server/submitter/k8s/model/NotebookCRList.java |  38 -----
 ...{NotebookCRList.java => NotebookCondition.java} |  52 ++-----
 .../{NotebookCRList.java => NotebookStatus.java}   |  51 ++-----
 .../server/submitter/k8s/util/NotebookUtils.java   | 161 +++++++++++++++++++++
 .../apache/submarine/rest/NotebookRestApiIT.java   |   5 +-
 10 files changed, 234 insertions(+), 197 deletions(-)

diff --git a/docs/userdocs/k8s/api/notebook.md b/docs/userdocs/k8s/api/notebook.md
index ca40e48..2f6db82 100644
--- a/docs/userdocs/k8s/api/notebook.md
+++ b/docs/userdocs/k8s/api/notebook.md
@@ -43,7 +43,7 @@ curl -X POST -H "Content-Type: application/json" -d '
     "resources": "cpu=1,memory=1.0Gi"
   }
 }
-' http://127.0.0.1:8080/api/v1/notebook
+' http://127.0.0.1:32080/api/v1/notebook
 ```
 
 **Example Response:**
@@ -58,7 +58,8 @@ curl -X POST -H "Content-Type: application/json" -d '
     "name":"test-nb",
     "uid":"5a94c01d-6a92-4222-bc66-c610c277546d",
     "url":"/notebook/default/test-nb/",
-    "status":"Created",
+    "status":"creating",
+    "reason":"The notebook instance is creating",
     "createdTime":"2020-08-20T21:58:27.000+08:00",
     "deletedTime":null,
     "spec":{
@@ -99,7 +100,7 @@ curl -X POST -H "Content-Type: application/json" -d '
 
 **Example Request:**
 ```sh
-curl -X GET http://127.0.0.1:8080/api/v1/notebook?id={user_id}
+curl -X GET http://127.0.0.1:32080/api/v1/notebook?id={user_id}
 ```
 
 **Example Response:**
@@ -115,7 +116,8 @@ curl -X GET http://127.0.0.1:8080/api/v1/notebook?id={user_id}
       "name":"test-nb",
       "uid":"5a94c01d-6a92-4222-bc66-c610c277546d",
       "url":"/notebook/default/test-nb/",
-      "status":"Created",
+      "status": "running",
+      "reason": "The notebook instance is running",
       "createdTime":"2020-08-20T21:58:27.000+08:00",
       "deletedTime":null,
       "spec":{
@@ -157,7 +159,7 @@ curl -X GET http://127.0.0.1:8080/api/v1/notebook?id={user_id}
 
 **Example Request:**
 ```sh
-curl -X GET http://127.0.0.1:8080/api/v1/notebook/{id}
+curl -X GET http://127.0.0.1:32080/api/v1/notebook/{id}
 ```
 
 **Example Response:**
@@ -172,7 +174,8 @@ curl -X GET http://127.0.0.1:8080/api/v1/notebook/{id}
     "name":"test-nb",
     "uid":"5a94c01d-6a92-4222-bc66-c610c277546d",
     "url":"/notebook/default/test-nb/",
-    "status":"Created",
+    "status":"running",
+    "reason":"The notebook instance is running",
     "createdTime":"2020-08-20T21:58:27.000+08:00",
     "deletedTime":null,
     "spec":{
@@ -213,7 +216,7 @@ curl -X GET http://127.0.0.1:8080/api/v1/notebook/{id}
 
 **Example Request:**
 ```sh
-curl -X DELETE http://127.0.0.1:8080/api/v1/notebook/{id}
+curl -X DELETE http://127.0.0.1:32080/api/v1/notebook/{id}
 ```
 
 **Example Response:**
@@ -228,7 +231,8 @@ curl -X DELETE http://127.0.0.1:8080/api/v1/notebook/{id}
     "name": "test-nb",
     "uid": "5a94c01d-6a92-4222-bc66-c610c277546d",
     "url": "/notebook/default/test-nb/",
-    "status": "Deleted",
+    "status": "terminating",
+    "reason": "The notebook instance is terminating",
     "createdTime": "2020-08-22T14:03:19.000+08:00",
     "deletedTime": "2020-08-22T14:46:28+0800",
     "spec": {
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 1e0ea36..1902cca 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
@@ -30,6 +30,7 @@ public class Notebook {
   private String uid;
   private String url;
   private String status;
+  private String reason;
   private String createdTime;
   private String deletedTime;
   private NotebookSpec spec;
@@ -74,6 +75,14 @@ public class Notebook {
     this.status = status;
   }
 
+  public String getReason() {
+    return reason;
+  }
+
+  public void setReason(String reason) {
+    this.reason = reason;
+  }
+
   public String getCreatedTime() {
     return createdTime;
   }
@@ -99,8 +108,10 @@ public class Notebook {
   }
 
   public enum Status {
-    STATUS_CREATED("Created"),
-    STATUS_DELETED("Deleted");
+    STATUS_CREATING("creating"),
+    STATUS_RUNNING("running"),
+    STATUS_WAITING("waiting"),
+    STATUS_TERMINATING("terminating");
 
     private String value;
     Status(String value) {
@@ -134,6 +145,9 @@ public class Notebook {
       if (notebook.getStatus() != null) {
         this.setStatus(notebook.getStatus());
       }
+      if (notebook.getReason() != null) {
+        this.setReason(notebook.getReason());
+      }
       if (notebook.getCreatedTime() != null) {
         this.setCreatedTime(notebook.getCreatedTime());
       }
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 92dbfed..da23c9c 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
@@ -84,6 +84,8 @@ public class NotebookManager {
     Notebook notebook = submitter.createNotebook(spec);
     notebook.setNotebookId(generateNotebookId());
     notebook.setSpec(spec);
+
+    // environment information
     NotebookSpec notebookSpec = notebook.getSpec();
     EnvironmentManager environmentManager = EnvironmentManager.getInstance();
     Environment environment = environmentManager.getEnvironment(spec.getEnvironment().getName());
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 78a427e..a97aa63 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,14 +21,11 @@ package org.apache.submarine.server.submitter.k8s;
 
 import java.io.FileReader;
 import java.io.IOException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.List;
-import java.util.ArrayList;
 
 import com.google.gson.Gson;
 import com.google.gson.JsonSyntaxException;
@@ -57,14 +54,13 @@ import org.apache.submarine.server.api.spec.ExperimentSpec;
 import org.apache.submarine.server.api.spec.NotebookSpec;
 import org.apache.submarine.server.submitter.k8s.model.MLJob;
 import org.apache.submarine.server.submitter.k8s.model.NotebookCR;
-import org.apache.submarine.server.submitter.k8s.model.NotebookCRList;
 import org.apache.submarine.server.submitter.k8s.model.ingressroute.IngressRoute;
 import org.apache.submarine.server.submitter.k8s.model.ingressroute.IngressRouteSpec;
 import org.apache.submarine.server.submitter.k8s.model.ingressroute.SpecRoute;
 import org.apache.submarine.server.submitter.k8s.parser.ExperimentSpecParser;
 import org.apache.submarine.server.submitter.k8s.parser.NotebookSpecParser;
 import org.apache.submarine.server.submitter.k8s.util.MLJobConverter;
-import org.joda.time.DateTime;
+import org.apache.submarine.server.submitter.k8s.util.NotebookUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -260,7 +256,7 @@ public class K8sSubmitter implements Submitter {
       notebookCR.getMetadata().setLabels(labels);
       Object object = api.createNamespacedCustomObject(notebookCR.getGroup(), notebookCR.getVersion(),
               notebookCR.getMetadata().getNamespace(), notebookCR.getPlural(), notebookCR, "true");
-      notebook = parseNotebookResponseObject(object);
+      notebook = NotebookUtils.parseObject(object, NotebookUtils.ParseOpt.PARSE_OPT_CREATE);
 
       // create Traefik custom resource
       createIngressRoute(notebookCR.getMetadata().getNamespace(), notebookCR.getMetadata().getName());
@@ -284,7 +280,7 @@ public class K8sSubmitter implements Submitter {
       Object object = api.getNamespacedCustomObject(notebookCR.getGroup(), notebookCR.getVersion(),
               notebookCR.getMetadata().getNamespace(),
               notebookCR.getPlural(), notebookCR.getMetadata().getName());
-      notebook = parseNotebookResponseObject(object);
+      notebook = NotebookUtils.parseObject(object, NotebookUtils.ParseOpt.PARSE_OPT_GET);
     } catch (ApiException e) {
       throw new SubmarineRuntimeException(e.getCode(), e.getMessage());
     }
@@ -301,7 +297,7 @@ public class K8sSubmitter implements Submitter {
               notebookCR.getMetadata().getName(),
               new V1DeleteOptionsBuilder().withApiVersion(notebookCR.getApiVersion()).build(),
               null, null, null);
-      notebook = parseNotebookResponseObject(object);
+      notebook = NotebookUtils.parseObject(object, NotebookUtils.ParseOpt.PARSE_OPT_DELETE);
       deleteIngressRoute(notebookCR.getMetadata().getNamespace(), notebookCR.getMetadata().getName());
     } catch (ApiException e) {
       throw new SubmarineRuntimeException(e.getCode(), e.getMessage());
@@ -317,73 +313,13 @@ public class K8sSubmitter implements Submitter {
               NotebookCR.CRD_NOTEBOOK_VERSION_V1, NotebookCR.CRD_NOTEBOOK_PLURAL_V1,
               "true", null, NotebookCR.NOTEBOOK_OWNER_SELECTOR_KET + "=" + id,
               null, null, null);
-      notebookList = parseNotebookListResponseObject(object);
+      notebookList = NotebookUtils.parseObjectForList(object);
     } catch (ApiException e) {
       throw new SubmarineRuntimeException(e.getCode(), e.getMessage());
     }
     return notebookList;
   }
 
-  private Notebook parseNotebookResponseObject(Object obj) throws SubmarineRuntimeException {
-    Gson gson = new JSON().getGson();
-    String jsonString = gson.toJson(obj);
-    LOG.info("Upstream response JSON: {}", jsonString);
-    Notebook notebook;
-    try {
-      NotebookCR notebookCR = gson.fromJson(jsonString, NotebookCR.class);
-      if (notebookCR.getMetadata().getName() != null) {
-        notebook = buildNotebookResponse(notebookCR);
-        // notebook is deleted
-      } else {
-        notebook = new Notebook();
-        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.");
-    }
-    return notebook;
-  }
-
-  private List<Notebook> parseNotebookListResponseObject(Object object) {
-    Gson gson = new JSON().getGson();
-    String jsonString = gson.toJson(object);
-    LOG.info("Upstream response JSON: {}", jsonString);
-
-    Notebook notebook;
-    List<Notebook> notebookList = new ArrayList<>();
-    try {
-      NotebookCRList notebookCRList = gson.fromJson(jsonString, NotebookCRList.class);
-      for (NotebookCR notebookCR : notebookCRList.getItems()) {
-        notebook = buildNotebookResponse(notebookCR);
-        notebookList.add(notebook);
-      }
-    } 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.");
-    }
-    return notebookList;
-  }
-
-  private Notebook buildNotebookResponse(NotebookCR notebookCR) {
-    Notebook notebook = new Notebook();
-    notebook.setUid(notebookCR.getMetadata().getUid());
-    notebook.setName(notebookCR.getMetadata().getName());
-    // notebook url
-    notebook.setUrl("/notebook/" + notebookCR.getMetadata().getNamespace() + "/" +
-            notebookCR.getMetadata().getName() + "/");
-    DateTime createdTime = notebookCR.getMetadata().getCreationTimestamp();
-    if (createdTime != null) {
-      notebook.setCreatedTime(createdTime.toString());
-      notebook.setStatus(Notebook.Status.STATUS_CREATED.getValue());
-    }
-    return notebook;
-  }
-
   private String getJobLabelSelector(ExperimentSpec experimentSpec) {
     // TODO(JohnTing): SELECTOR_KEY should be obtained from individual models in MLJOB
     if (experimentSpec.getMeta().getFramework()
diff --git a/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/model/NotebookCR.java b/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/model/NotebookCR.java
index 69d9fcc..c93e189 100644
--- a/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/model/NotebookCR.java
+++ b/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/model/NotebookCR.java
@@ -49,6 +49,9 @@ public class NotebookCR {
   @SerializedName("spec")
   private NotebookCRSpec spec;
 
+  @SerializedName("status")
+  private NotebookStatus status;
+
   public NotebookCR() {
     setApiVersion(CRD_APIVERSION_V1);
     setKind(CRD_NOTEBOOK_KIND_V1);
@@ -113,4 +116,11 @@ public class NotebookCR {
     this.spec = spec;
   }
 
+  public NotebookStatus getStatus() {
+    return status;
+  }
+
+  public void setStatus(NotebookStatus status) {
+    this.status = status;
+  }
 }
diff --git a/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/model/NotebookCRList.java b/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/model/NotebookCRList.java
index 0fa78b2..ccdca63 100644
--- a/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/model/NotebookCRList.java
+++ b/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/model/NotebookCRList.java
@@ -20,53 +20,15 @@
 package org.apache.submarine.server.submitter.k8s.model;
 
 import com.google.gson.annotations.SerializedName;
-import io.kubernetes.client.models.V1ListMeta;
 
 import java.util.List;
 
 public class NotebookCRList {
 
-  @SerializedName("apiVersion")
-  private String apiVersion;
-
   @SerializedName("items")
   private List<NotebookCR> items;
 
-  @SerializedName("kind")
-  private String kind;
-
-  @SerializedName("metadata")
-  private V1ListMeta metadata;
-
-  public String getApiVersion() {
-    return apiVersion;
-  }
-
-  public void setApiVersion(String apiVersion) {
-    this.apiVersion = apiVersion;
-  }
-
   public List<NotebookCR> getItems() {
     return items;
   }
-
-  public void setItems(List<NotebookCR> items) {
-    this.items = items;
-  }
-
-  public String getKind() {
-    return kind;
-  }
-
-  public void setKing(String kind) {
-    this.kind = kind;
-  }
-
-  public V1ListMeta getMetadata() {
-    return metadata;
-  }
-
-  public void setMetadata(V1ListMeta metadata) {
-    this.metadata = metadata;
-  }
 }
diff --git a/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/model/NotebookCRList.java b/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/model/NotebookCondition.java
similarity index 51%
copy from submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/model/NotebookCRList.java
copy to submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/model/NotebookCondition.java
index 0fa78b2..d16802f 100644
--- a/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/model/NotebookCRList.java
+++ b/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/model/NotebookCondition.java
@@ -16,57 +16,27 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.apache.submarine.server.submitter.k8s.model;
 
 import com.google.gson.annotations.SerializedName;
-import io.kubernetes.client.models.V1ListMeta;
-
-import java.util.List;
-
-public class NotebookCRList {
-
-  @SerializedName("apiVersion")
-  private String apiVersion;
-
-  @SerializedName("items")
-  private List<NotebookCR> items;
-
-  @SerializedName("kind")
-  private String kind;
-
-  @SerializedName("metadata")
-  private V1ListMeta metadata;
+import org.joda.time.DateTime;
 
-  public String getApiVersion() {
-    return apiVersion;
-  }
+public class NotebookCondition {
 
-  public void setApiVersion(String apiVersion) {
-    this.apiVersion = apiVersion;
-  }
+  public NotebookCondition() {
 
-  public List<NotebookCR> getItems() {
-    return items;
   }
 
-  public void setItems(List<NotebookCR> items) {
-    this.items = items;
-  }
+  @SerializedName("type")
+  private String type;
 
-  public String getKind() {
-    return kind;
-  }
+  @SerializedName("lastProbeTime")
+  private DateTime lastProbeTime;
 
-  public void setKing(String kind) {
-    this.kind = kind;
-  }
+  @SerializedName("reason")
+  private String reason;
 
-  public V1ListMeta getMetadata() {
-    return metadata;
-  }
+  @SerializedName("message")
+  private String message;
 
-  public void setMetadata(V1ListMeta metadata) {
-    this.metadata = metadata;
-  }
 }
diff --git a/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/model/NotebookCRList.java b/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/model/NotebookStatus.java
similarity index 53%
copy from submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/model/NotebookCRList.java
copy to submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/model/NotebookStatus.java
index 0fa78b2..bc11006 100644
--- a/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/model/NotebookCRList.java
+++ b/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/model/NotebookStatus.java
@@ -16,57 +16,36 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.apache.submarine.server.submitter.k8s.model;
 
 import com.google.gson.annotations.SerializedName;
-import io.kubernetes.client.models.V1ListMeta;
+import io.kubernetes.client.models.V1ContainerState;
 
 import java.util.List;
 
-public class NotebookCRList {
-
-  @SerializedName("apiVersion")
-  private String apiVersion;
-
-  @SerializedName("items")
-  private List<NotebookCR> items;
-
-  @SerializedName("kind")
-  private String kind;
-
-  @SerializedName("metadata")
-  private V1ListMeta metadata;
+public class NotebookStatus {
 
-  public String getApiVersion() {
-    return apiVersion;
-  }
+  @SerializedName("conditions")
+  private List<NotebookCondition> conditions;
 
-  public void setApiVersion(String apiVersion) {
-    this.apiVersion = apiVersion;
-  }
+  @SerializedName("readyReplicas")
+  private int readyReplicas;
 
-  public List<NotebookCR> getItems() {
-    return items;
-  }
-
-  public void setItems(List<NotebookCR> items) {
-    this.items = items;
-  }
+  @SerializedName("containerState")
+  private V1ContainerState containerState;
 
-  public String getKind() {
-    return kind;
+  NotebookStatus() {
   }
 
-  public void setKing(String kind) {
-    this.kind = kind;
+  public int getReadyReplicas() {
+    return readyReplicas;
   }
 
-  public V1ListMeta getMetadata() {
-    return metadata;
+  public V1ContainerState getContainerState() {
+    return containerState;
   }
 
-  public void setMetadata(V1ListMeta metadata) {
-    this.metadata = metadata;
+  public List<NotebookCondition> getConditions() {
+    return conditions;
   }
 }
diff --git a/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/util/NotebookUtils.java b/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/util/NotebookUtils.java
new file mode 100644
index 0000000..cf33ff7
--- /dev/null
+++ b/submarine-server/server-submitter/submitter-k8s/src/main/java/org/apache/submarine/server/submitter/k8s/util/NotebookUtils.java
@@ -0,0 +1,161 @@
+/*
+ * 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.server.submitter.k8s.util;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+import io.kubernetes.client.JSON;
+import io.kubernetes.client.models.V1ContainerState;
+import io.kubernetes.client.models.V1Status;
+import org.apache.submarine.commons.utils.exception.SubmarineRuntimeException;
+import org.apache.submarine.server.api.notebook.Notebook;
+import org.apache.submarine.server.submitter.k8s.model.NotebookCR;
+import org.apache.submarine.server.submitter.k8s.model.NotebookCRList;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utils for building response of k8s (notebook) submitter
+ */
+public class NotebookUtils {
+
+  private static final Logger LOG = LoggerFactory.getLogger(NotebookUtils.class);
+
+  public enum ParseOpt {
+    PARSE_OPT_CREATE,
+    PARSE_OPT_GET,
+    PARSE_OPT_DELETE;
+  }
+
+  public static Notebook parseObject(Object obj, ParseOpt opt) throws SubmarineRuntimeException {
+    Gson gson = new JSON().getGson();
+    String jsonString = gson.toJson(obj);
+    LOG.info("Upstream response JSON: {}", jsonString);
+    try {
+      if (opt == ParseOpt.PARSE_OPT_DELETE) {
+        V1Status status = gson.fromJson(jsonString, V1Status.class);
+        return buildNotebookResponseFromStatus(status);
+      } else {
+        NotebookCR notebookCR = gson.fromJson(jsonString, NotebookCR.class);
+        return buildNotebookResponse(notebookCR);
+      }
+    } 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.");
+  }
+
+  public static List<Notebook> parseObjectForList(Object object) throws SubmarineRuntimeException {
+    Gson gson = new JSON().getGson();
+    String jsonString = gson.toJson(object);
+    LOG.info("Upstream response JSON: {}", jsonString);
+
+    try {
+      List<Notebook> notebookList = new ArrayList<>();
+      NotebookCRList notebookCRList = gson.fromJson(jsonString, NotebookCRList.class);
+      for (NotebookCR notebookCR : notebookCRList.getItems()) {
+        Notebook notebook = buildNotebookResponse(notebookCR);
+        notebookList.add(notebook);
+      }
+      return notebookList;
+    } 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.");
+  }
+
+  private static Notebook buildNotebookResponse(NotebookCR notebookCR) {
+    Notebook notebook = new Notebook();
+    notebook.setUid(notebookCR.getMetadata().getUid());
+    notebook.setName(notebookCR.getMetadata().getName());
+    notebook.setCreatedTime(notebookCR.getMetadata().getCreationTimestamp().toString());
+    // notebook url
+    notebook.setUrl("/notebook/" + notebookCR.getMetadata().getNamespace() + "/" +
+            notebookCR.getMetadata().getName() + "/");
+
+    // process status
+    Map<String, String> statusMap = processStatus(notebookCR);
+    notebook.setStatus(statusMap.get("status"));
+    notebook.setReason(statusMap.get("reason"));
+
+    if (notebookCR.getMetadata().getDeletionTimestamp() != null) {
+      notebook.setDeletedTime(notebookCR.getMetadata().getDeletionTimestamp().toString());
+    }
+    return notebook;
+  }
+
+  private static Map<String, String> processStatus(NotebookCR notebookCR) {
+    Map<String, String> statusMap = new HashMap<>();
+    // if the notebook instance is deleted
+    if (notebookCR.getMetadata().getDeletionTimestamp() != null) {
+      statusMap = createStatusMap(Notebook.Status.STATUS_TERMINATING.toString(),
+              "The notebook instance is terminating");
+    }
+
+    if (notebookCR.getStatus() == null) {
+      // if the notebook pod has not been created
+      statusMap = createStatusMap(Notebook.Status.STATUS_CREATING.toString(),
+              "The notebook instance is creating");
+    } else {
+      // if the notebook instance is ready(Running)
+      int replicas = notebookCR.getStatus().getReadyReplicas();
+      if (replicas == 1) {
+        statusMap = createStatusMap(Notebook.Status.STATUS_RUNNING.toString(),
+                "The notebook instance is running");
+      }
+
+      // if the notebook instance is waiting
+      V1ContainerState containerState = notebookCR.getStatus().getContainerState();
+      if (containerState.getWaiting() != null) {
+        statusMap = createStatusMap(Notebook.Status.STATUS_WAITING.toString(),
+                containerState.getWaiting().getReason());
+      }
+    }
+
+    return statusMap;
+  }
+
+  private static Map<String, String> createStatusMap(String status, String reason) {
+    Map<String, String> statusMap = new HashMap<>();
+    statusMap.put("status", status);
+    statusMap.put("reason", reason);
+    return statusMap;
+  }
+
+  private static Notebook buildNotebookResponseFromStatus(V1Status status) {
+    Notebook notebook = new Notebook();
+    if (status.getStatus().toLowerCase().equals("success")) {
+      notebook.setStatus(Notebook.Status.STATUS_TERMINATING.toString());
+      notebook.setReason("The notebook instance is terminating");
+    }
+
+    if (status.getDetails() != null) {
+      notebook.setUid(status.getDetails().getUid());
+      notebook.setName(status.getDetails().getName());
+    }
+    return notebook;
+  }
+}
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
index 4351bac..d47adcf 100644
--- 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
@@ -37,7 +37,6 @@ 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.environment.EnvironmentId;
-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.EnvironmentIdDeserializer;
@@ -263,7 +262,7 @@ public class NotebookRestApiIT extends AbstractSubmarineServerTest {
   private void verifyCreateNotebookApiResult(Notebook createdNotebook) {
     Assert.assertNotNull(createdNotebook.getUid());
     Assert.assertNotNull(createdNotebook.getCreatedTime());
-    Assert.assertEquals(Experiment.Status.STATUS_CREATED.getValue(), createdNotebook.getStatus());
+    Assert.assertEquals(Notebook.Status.STATUS_CREATING.toString(), createdNotebook.getStatus());
   }
 
   private void verifyGetNotebookApiResult(Notebook createdNotebook,
@@ -278,7 +277,7 @@ public class NotebookRestApiIT extends AbstractSubmarineServerTest {
   private void verifyDeleteNotebookApiResult(Notebook createdNotebook,
                                              Notebook deletedNotebook) {
     Assert.assertEquals(createdNotebook.getName(), deletedNotebook.getName());
-    Assert.assertEquals(Notebook.Status.STATUS_DELETED.getValue(), deletedNotebook.getStatus());
+    Assert.assertEquals(Notebook.Status.STATUS_TERMINATING.getValue(), deletedNotebook.getStatus());
     assertDeleteK8sResult(deletedNotebook);
   }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@submarine.apache.org
For additional commands, e-mail: dev-help@submarine.apache.org