From dcfba4c07c4dd79e60d35b5c354c7eabfdb5f8d6 Mon Sep 17 00:00:00 2001 From: zachomedia Date: Wed, 16 Feb 2022 15:09:41 -0500 Subject: [PATCH] feat(endpoint): Add update notebook start / stop endpoint Co-authored-by: William Hearn Signed-off-by: zachomedia --- httputils.go | 4 +++- main.go | 14 +++++++++++ notebooks.go | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/httputils.go b/httputils.go index 5ee7a529..ffe4bee7 100644 --- a/httputils.go +++ b/httputils.go @@ -9,7 +9,9 @@ import ( // APIResponse contains the basic fields of a response from the APIs. type APIResponse struct { Success bool `json:"success"` - Log string `json:"log"` + Status int `json:"status"` + Log string `json:"log,omitempty"` + User string `json:"user"` } // respond response returns a JSON response to the client. diff --git a/main.go b/main.go index 77fe54af..840ec8dc 100644 --- a/main.go +++ b/main.go @@ -157,6 +157,7 @@ func main() { }, }, }, s.GetNamespaces)).Methods("GET") + router.HandleFunc("/api/namespaces/{namespace}/notebooks", s.checkAccess(authorizationv1.SubjectAccessReview{ Spec: authorizationv1.SubjectAccessReviewSpec{ ResourceAttributes: &authorizationv1.ResourceAttributes{ @@ -167,6 +168,7 @@ func main() { }, }, }, s.GetNotebooks)).Methods("GET") + router.HandleFunc("/api/namespaces/{namespace}/notebooks", s.checkAccess(authorizationv1.SubjectAccessReview{ Spec: authorizationv1.SubjectAccessReviewSpec{ ResourceAttributes: &authorizationv1.ResourceAttributes{ @@ -177,6 +179,18 @@ func main() { }, }, }, s.NewNotebook)).Headers("Content-Type", "application/json").Methods("POST") + + router.HandleFunc("/api/namespaces/{namespace}/notebooks/{notebook}", s.checkAccess(authorizationv1.SubjectAccessReview{ + Spec: authorizationv1.SubjectAccessReviewSpec{ + ResourceAttributes: &authorizationv1.ResourceAttributes{ + Group: kubeflowv1.SchemeGroupVersion.Group, + Verb: "update", + Resource: "notebooks", + Version: kubeflowv1.SchemeGroupVersion.Version, + }, + }, + }, s.UpdateNotebook)).Methods("PATCH") + router.HandleFunc("/api/namespaces/{namespace}/notebooks/{notebook}", s.checkAccess(authorizationv1.SubjectAccessReview{ Spec: authorizationv1.SubjectAccessReviewSpec{ ResourceAttributes: &authorizationv1.ResourceAttributes{ diff --git a/notebooks.go b/notebooks.go index de1ce6dc..3d3881d8 100644 --- a/notebooks.go +++ b/notebooks.go @@ -11,6 +11,7 @@ import ( "regexp" "sort" "strings" + "time" kubeflowv1 "github.com/StatCan/kubeflow-controller/pkg/apis/kubeflowcontroller/v1" "github.com/andanhm/go-prettytime" @@ -34,6 +35,9 @@ const SharedMemoryVolumePath string = "/dev/shm" // EnvKfLanguage String. const EnvKfLanguage string = "KF_LANG" +// StoppedAnnotation String. +const StoppedAnnotation string = "stopped" + type volumetype string const ( @@ -96,6 +100,10 @@ type notebooksresponse struct { Notebooks []notebookresponse `json:"notebooks"` } +type updatenotebookrequest struct { + Stopped bool `json:"stopped"` +} + // // EVENT_TYPE_NORMAL = "Normal" // EVENT_TYPE_WARNING = "Warning" @@ -558,3 +566,62 @@ func (s *server) DeleteNotebook(w http.ResponseWriter, r *http.Request) { Success: true, }) } + +func (s *server) UpdateNotebook(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + namespaceName := vars["namespace"] + notebookName := vars["notebook"] + + log.Printf("deleting notebook %q for %q", notebookName, namespaceName) + + // Read the incoming notebook + body, err := ioutil.ReadAll(r.Body) + if err != nil { + s.error(w, r, err) + return + } + defer r.Body.Close() + + var req updatenotebookrequest + err = json.Unmarshal(body, &req) + if err != nil { + s.error(w, r, err) + return + } + + // Read existing notebook + notebook, err := s.listers.notebooks.Notebooks(namespaceName).Get(notebookName) + if err != nil { + s.error(w, r, err) + return + } + + update := false + updatedNotebook := notebook.DeepCopy() + + // Compare start/stopped state + if _, ok := notebook.Annotations[StoppedAnnotation]; ok != req.Stopped { + update = true + + if req.Stopped { + // Set the stopped annotation + updatedNotebook.Annotations[StoppedAnnotation] = time.Now().Format(time.RFC3339) + } else { + // Remove the stopped annotation + delete(updatedNotebook.Annotations, StoppedAnnotation) + } + } + + if update { + _, err = s.clientsets.kubeflow.KubeflowV1().Notebooks(namespaceName).Update(r.Context(), updatedNotebook, v1.UpdateOptions{}) + if err != nil { + s.error(w, r, err) + return + } + } + + s.respond(w, r, APIResponse{ + Success: true, + Status: http.StatusOK, + }) +}