AppManager: Recursively monitor dodo resources

Change-Id: I23f3014e416f5b68f8909dbfbcba27db66eaee3f
diff --git a/core/installer/status/instance.go b/core/installer/status/instance.go
index 285f68c..ad05e5a 100644
--- a/core/installer/status/instance.go
+++ b/core/installer/status/instance.go
@@ -1,28 +1,138 @@
 package status
 
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+)
+
 type InstanceMonitor struct {
 	m         Monitor
-	instances map[string][]Resource
+	instances map[string]ResourceOuts
 }
 
 func NewInstanceMonitor(m Monitor) *InstanceMonitor {
 	return &InstanceMonitor{
 		m:         m,
-		instances: map[string][]Resource{},
+		instances: map[string]ResourceOuts{},
 	}
 }
 
-func (m *InstanceMonitor) Monitor(id string, resources []Resource) {
+func (m *InstanceMonitor) Monitor(id string, resources ResourceOuts) {
 	m.instances[id] = resources
 }
 
-func (m *InstanceMonitor) Get(id string) (ret map[Resource]Status, err error) {
-	ret = map[Resource]Status{}
-	for _, r := range m.instances[id] {
-		ret[r], err = m.m.Get(r)
-		if err != nil {
-			break
+func (m *InstanceMonitor) Get(id string) (map[DodoResource]Status, error) {
+	ret := map[DodoResource]Status{}
+	namespace := m.instances[id].Release.Namespace
+	for _, out := range m.instances[id].Outs {
+		if _, err := m.monitor(namespace, DodoResource{"internal", out.Name}, out, ret); err != nil {
+			return nil, err
 		}
 	}
-	return
+	for i := range ret {
+		if i.Type == "internal" {
+			delete(ret, i)
+		}
+	}
+	return ret, nil
+}
+
+func mergeStatus(a Status, b Status) Status {
+	if a > b {
+		return a
+	} else {
+		return b
+	}
+}
+
+// TODO(gio): handle volume
+func (m *InstanceMonitor) monitor(namespace string, resource DodoResource, out ResourceOut, ret map[DodoResource]Status) (Status, error) {
+	status := StatusNoStatus
+	for _, h := range out.Helm {
+		hs, err := m.m.Get(Resource{
+			Type: ResourceHelmRelease,
+			ResourceRef: ResourceRef{
+				Id:        h.Id,
+				Name:      h.Name,
+				Namespace: namespace,
+			},
+		})
+		fmt.Println(hs, err)
+		if err != nil {
+			return StatusNoStatus, err
+		}
+		status = mergeStatus(status, hs)
+	}
+	for _, i := range out.PostgreSQL {
+		if s, err := m.monitor(namespace, DodoResource{"postgresql", i.Name}, i, ret); err != nil {
+			return StatusNoStatus, err
+		} else {
+			s = mergeStatus(status, s)
+		}
+	}
+	for _, i := range out.MongoDB {
+		if s, err := m.monitor(namespace, DodoResource{"mongodb", i.Name}, i, ret); err != nil {
+			return StatusNoStatus, err
+		} else {
+			s = mergeStatus(status, s)
+		}
+	}
+	for _, i := range out.Ingress {
+		name := fmt.Sprintf("https://%s.%s", i.Subdomain, i.Network.Domain)
+		if s, err := m.monitor(namespace, DodoResource{"ingress", name}, i.ResourceOut, ret); err != nil {
+			return StatusNoStatus, err
+		} else {
+			s = mergeStatus(status, s)
+		}
+	}
+	ret[resource] = status
+	return status, nil
+}
+
+type resourceIngress struct {
+	ResourceOut
+	Network struct {
+		Domain string `json:"domain"`
+	} `json:"network"`
+	Subdomain string `json:"subdomain"`
+}
+
+type resourceHelm struct {
+	Id   string `json:"id"`
+	Name string `json:"name"`
+}
+
+type ResourceOut struct {
+	Name       string                     `json:"name"`
+	PostgreSQL map[string]ResourceOut     `json:"postgresql"`
+	MongoDB    map[string]ResourceOut     `json:"mongodb"`
+	Ingress    map[string]resourceIngress `json:"ingress"`
+	Helm       map[string]resourceHelm    `json:"helmR"`
+}
+
+type ResourceOuts struct {
+	Release struct {
+		Namespace string `json:"namespace"`
+	}
+	Outs map[string]ResourceOut `json:"outs"`
+}
+
+type DodoResource struct {
+	Type string
+	Name string
+}
+
+type DodoResourceStatus struct {
+	Type   string `json:"type"`
+	Name   string `json:"name"`
+	Status string `json:"status"`
+}
+
+func DecodeResourceOuts(raw []byte) (ResourceOuts, error) {
+	var outs ResourceOuts
+	if err := json.NewDecoder(bytes.NewReader(raw)).Decode(&outs); err != nil {
+		return ResourceOuts{}, err
+	}
+	return outs, nil
 }