AppManager: Implement task status API
Change-Id: I70c895d7461ffe4afc45868ca6bf754d37072a0f
diff --git a/core/installer/server/appmanager/server.go b/core/installer/server/appmanager/server.go
index cb372f3..b26fc85 100644
--- a/core/installer/server/appmanager/server.go
+++ b/core/installer/server/appmanager/server.go
@@ -142,6 +142,7 @@
r.HandleFunc("/api/instance/{slug}", s.handleInstance).Methods(http.MethodGet)
r.HandleFunc("/api/instance/{slug}/update", s.handleAppUpdate).Methods(http.MethodPost)
r.HandleFunc("/api/instance/{slug}/remove", s.handleAppRemove).Methods(http.MethodPost)
+ r.HandleFunc("/api/tasks/{instanceId}", s.handleTaskStatusAPI).Methods(http.MethodGet)
r.HandleFunc("/api/dodo-app/{instanceId}", s.handleDodoAppUpdate).Methods(http.MethodPut)
r.HandleFunc("/api/dodo-app", s.handleDodoAppInstall).Methods(http.MethodPost)
r.HandleFunc("/clusters/{cluster}/servers/{server}/remove", s.handleClusterRemoveServer).Methods(http.MethodPost)
@@ -178,6 +179,8 @@
}
func (s *Server) handleDodoAppInstall(w http.ResponseWriter, r *http.Request) {
+ s.l.Lock()
+ defer s.l.Unlock()
var req dodoAppInstallReq
// TODO(gio): validate that no internal fields are overridden by request
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
@@ -218,10 +221,16 @@
}
func (s *Server) handleDodoAppUpdate(w http.ResponseWriter, r *http.Request) {
+ s.l.Lock()
+ defer s.l.Unlock()
instanceId, ok := mux.Vars(r)["instanceId"]
if !ok {
http.Error(w, "missing instance id", http.StatusBadRequest)
}
+ if _, ok := s.tasks[instanceId]; ok {
+ http.Error(w, "task in progress", http.StatusTooEarly)
+ return
+ }
var req dodoAppInstallReq
// TODO(gio): validate that no internal fields are overridden by request
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
@@ -242,10 +251,23 @@
overrides := installer.CueAppData{
"app.cue": cfg.Bytes(),
}
- // TODO(gio): return monitoring info
- if _, err := s.m.Update(instanceId, nil, overrides); err != nil {
+ rr, err := s.m.Update(instanceId, nil, overrides)
+ if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
+ t := tasks.NewInstallTask(s.h, func() (installer.ReleaseResources, error) {
+ if err == nil {
+ ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
+ go s.reconciler.Reconcile(ctx)
+ }
+ return rr, err
+ })
+ if _, ok := s.tasks[instanceId]; ok {
+ panic("MUST NOT REACH!")
+ }
+ s.tasks[instanceId] = &taskForward{t, fmt.Sprintf("/instance/%s", instanceId), 0}
+ t.OnDone(s.cleanTask(instanceId, 0))
+ go t.Start()
}
func (s *Server) handleNetworks(w http.ResponseWriter, r *http.Request) {
@@ -467,6 +489,7 @@
t.OnDone(s.cleanTask(slug, tid))
s.tasks[slug] = &taskForward{t, fmt.Sprintf("/instance/%s", slug), tid}
go t.Start()
+ fmt.Printf("Created task for %s\n", slug)
if _, err := fmt.Fprintf(w, "/tasks/%s", slug); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
@@ -665,7 +688,6 @@
t, ok := s.tasks[slug]
if !ok {
http.Error(w, "task not found", http.StatusInternalServerError)
-
return
}
if ok && t.task == nil {
@@ -682,6 +704,48 @@
}
}
+type resourceStatus struct {
+ Type string `json:"type"`
+ Name string `json:"name"`
+ Status string `json:"status"`
+}
+
+func extractResources(t tasks.Task) []resourceStatus {
+ var ret []resourceStatus
+ if t.Resource() != nil {
+ ret = append(ret, resourceStatus{
+ Type: t.Resource().Type,
+ Name: t.Resource().Name,
+ Status: tasks.StatusString(t.Status()),
+ })
+ }
+ for _, st := range t.Subtasks() {
+ ret = append(ret, extractResources(st)...)
+ }
+ return ret
+}
+
+func (s *Server) handleTaskStatusAPI(w http.ResponseWriter, r *http.Request) {
+ s.l.Lock()
+ defer s.l.Unlock()
+ instanceId, ok := mux.Vars(r)["instanceId"]
+ if !ok {
+ http.Error(w, "empty slug", http.StatusBadRequest)
+ return
+ }
+ t, ok := s.tasks[instanceId]
+ if !ok {
+ http.Error(w, "task not found", http.StatusInternalServerError)
+ return
+ }
+ if ok && t.task == nil {
+ http.Error(w, "not found", http.StatusNotFound)
+ return
+ }
+ resources := extractResources(t.task)
+ json.NewEncoder(w).Encode(resources)
+}
+
type clustersData struct {
CurrentPage string
Clusters []cluster.State