| gio | f8acc61 | 2025-04-26 08:20:55 +0400 | [diff] [blame] | 1 | package status |
| 2 | |
| gio | da70865 | 2025-04-30 14:57:38 +0400 | [diff] [blame] | 3 | import ( |
| 4 | "bytes" |
| 5 | "encoding/json" |
| 6 | "fmt" |
| gio | ea6d912 | 2025-05-22 17:57:18 +0400 | [diff] [blame] | 7 | "strings" |
| gio | da70865 | 2025-04-30 14:57:38 +0400 | [diff] [blame] | 8 | ) |
| 9 | |
| gio | f8acc61 | 2025-04-26 08:20:55 +0400 | [diff] [blame] | 10 | type InstanceMonitor struct { |
| 11 | m Monitor |
| gio | da70865 | 2025-04-30 14:57:38 +0400 | [diff] [blame] | 12 | instances map[string]ResourceOuts |
| gio | f8acc61 | 2025-04-26 08:20:55 +0400 | [diff] [blame] | 13 | } |
| 14 | |
| 15 | func NewInstanceMonitor(m Monitor) *InstanceMonitor { |
| 16 | return &InstanceMonitor{ |
| 17 | m: m, |
| gio | da70865 | 2025-04-30 14:57:38 +0400 | [diff] [blame] | 18 | instances: map[string]ResourceOuts{}, |
| gio | f8acc61 | 2025-04-26 08:20:55 +0400 | [diff] [blame] | 19 | } |
| 20 | } |
| 21 | |
| gio | da70865 | 2025-04-30 14:57:38 +0400 | [diff] [blame] | 22 | func (m *InstanceMonitor) Monitor(id string, resources ResourceOuts) { |
| gio | f8acc61 | 2025-04-26 08:20:55 +0400 | [diff] [blame] | 23 | m.instances[id] = resources |
| 24 | } |
| 25 | |
| gio | da70865 | 2025-04-30 14:57:38 +0400 | [diff] [blame] | 26 | func (m *InstanceMonitor) Get(id string) (map[DodoResource]Status, error) { |
| 27 | ret := map[DodoResource]Status{} |
| 28 | namespace := m.instances[id].Release.Namespace |
| 29 | for _, out := range m.instances[id].Outs { |
| 30 | if _, err := m.monitor(namespace, DodoResource{"internal", out.Name}, out, ret); err != nil { |
| 31 | return nil, err |
| gio | f8acc61 | 2025-04-26 08:20:55 +0400 | [diff] [blame] | 32 | } |
| 33 | } |
| gio | da70865 | 2025-04-30 14:57:38 +0400 | [diff] [blame] | 34 | for i := range ret { |
| 35 | if i.Type == "internal" { |
| 36 | delete(ret, i) |
| 37 | } |
| 38 | } |
| 39 | return ret, nil |
| 40 | } |
| 41 | |
| 42 | func mergeStatus(a Status, b Status) Status { |
| 43 | if a > b { |
| 44 | return a |
| 45 | } else { |
| 46 | return b |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | // TODO(gio): handle volume |
| 51 | func (m *InstanceMonitor) monitor(namespace string, resource DodoResource, out ResourceOut, ret map[DodoResource]Status) (Status, error) { |
| 52 | status := StatusNoStatus |
| gio | ea6d912 | 2025-05-22 17:57:18 +0400 | [diff] [blame] | 53 | if resource.Type == "ingress" { |
| 54 | var err error |
| 55 | status, err = m.m.Get(Resource{ |
| 56 | Type: ResourceIngress, |
| gio | da70865 | 2025-04-30 14:57:38 +0400 | [diff] [blame] | 57 | ResourceRef: ResourceRef{ |
| gio | ea6d912 | 2025-05-22 17:57:18 +0400 | [diff] [blame] | 58 | Id: out.Id, |
| 59 | Name: fmt.Sprintf("ingress-%s", strings.TrimPrefix(resource.Name, "https://")), |
| gio | da70865 | 2025-04-30 14:57:38 +0400 | [diff] [blame] | 60 | Namespace: namespace, |
| 61 | }, |
| 62 | }) |
| gio | da70865 | 2025-04-30 14:57:38 +0400 | [diff] [blame] | 63 | if err != nil { |
| 64 | return StatusNoStatus, err |
| 65 | } |
| gio | ea6d912 | 2025-05-22 17:57:18 +0400 | [diff] [blame] | 66 | } else { |
| 67 | for _, h := range out.Helm { |
| 68 | hs, err := m.m.Get(Resource{ |
| 69 | Type: ResourceHelmRelease, |
| 70 | ResourceRef: ResourceRef{ |
| 71 | Id: h.Id, |
| 72 | Name: h.Name, |
| 73 | Namespace: namespace, |
| 74 | }, |
| 75 | }) |
| 76 | if err != nil { |
| 77 | return StatusNoStatus, err |
| 78 | } |
| 79 | status = mergeStatus(status, hs) |
| 80 | } |
| gio | da70865 | 2025-04-30 14:57:38 +0400 | [diff] [blame] | 81 | } |
| gio | 0af17d6 | 2025-05-01 15:36:00 +0400 | [diff] [blame] | 82 | for _, i := range out.Volume { |
| 83 | if s, err := m.monitor(namespace, DodoResource{"volume", i.Name}, i, ret); err != nil { |
| 84 | return StatusNoStatus, err |
| 85 | } else { |
| gio | ea6d912 | 2025-05-22 17:57:18 +0400 | [diff] [blame] | 86 | status = mergeStatus(status, s) |
| gio | 0af17d6 | 2025-05-01 15:36:00 +0400 | [diff] [blame] | 87 | } |
| 88 | } |
| gio | da70865 | 2025-04-30 14:57:38 +0400 | [diff] [blame] | 89 | for _, i := range out.PostgreSQL { |
| 90 | if s, err := m.monitor(namespace, DodoResource{"postgresql", i.Name}, i, ret); err != nil { |
| 91 | return StatusNoStatus, err |
| 92 | } else { |
| gio | ea6d912 | 2025-05-22 17:57:18 +0400 | [diff] [blame] | 93 | status = mergeStatus(status, s) |
| gio | da70865 | 2025-04-30 14:57:38 +0400 | [diff] [blame] | 94 | } |
| 95 | } |
| 96 | for _, i := range out.MongoDB { |
| 97 | if s, err := m.monitor(namespace, DodoResource{"mongodb", i.Name}, i, ret); err != nil { |
| 98 | return StatusNoStatus, err |
| 99 | } else { |
| gio | ea6d912 | 2025-05-22 17:57:18 +0400 | [diff] [blame] | 100 | status = mergeStatus(status, s) |
| gio | da70865 | 2025-04-30 14:57:38 +0400 | [diff] [blame] | 101 | } |
| 102 | } |
| 103 | for _, i := range out.Ingress { |
| 104 | name := fmt.Sprintf("https://%s.%s", i.Subdomain, i.Network.Domain) |
| 105 | if s, err := m.monitor(namespace, DodoResource{"ingress", name}, i.ResourceOut, ret); err != nil { |
| 106 | return StatusNoStatus, err |
| 107 | } else { |
| gio | ea6d912 | 2025-05-22 17:57:18 +0400 | [diff] [blame] | 108 | status = mergeStatus(status, s) |
| gio | da70865 | 2025-04-30 14:57:38 +0400 | [diff] [blame] | 109 | } |
| 110 | } |
| gio | a602462 | 2025-05-01 18:37:16 +0400 | [diff] [blame] | 111 | for _, i := range out.Services { |
| 112 | if s, err := m.monitor(namespace, DodoResource{"service", i.Name}, i, ret); err != nil { |
| 113 | return StatusNoStatus, err |
| 114 | } else { |
| gio | ea6d912 | 2025-05-22 17:57:18 +0400 | [diff] [blame] | 115 | status = mergeStatus(status, s) |
| gio | a602462 | 2025-05-01 18:37:16 +0400 | [diff] [blame] | 116 | } |
| 117 | } |
| gio | da70865 | 2025-04-30 14:57:38 +0400 | [diff] [blame] | 118 | ret[resource] = status |
| 119 | return status, nil |
| 120 | } |
| 121 | |
| 122 | type resourceIngress struct { |
| 123 | ResourceOut |
| 124 | Network struct { |
| 125 | Domain string `json:"domain"` |
| 126 | } `json:"network"` |
| 127 | Subdomain string `json:"subdomain"` |
| 128 | } |
| 129 | |
| 130 | type resourceHelm struct { |
| 131 | Id string `json:"id"` |
| 132 | Name string `json:"name"` |
| 133 | } |
| 134 | |
| 135 | type ResourceOut struct { |
| gio | ea6d912 | 2025-05-22 17:57:18 +0400 | [diff] [blame] | 136 | Id string `json:"id"` |
| gio | da70865 | 2025-04-30 14:57:38 +0400 | [diff] [blame] | 137 | Name string `json:"name"` |
| gio | 0af17d6 | 2025-05-01 15:36:00 +0400 | [diff] [blame] | 138 | Volume map[string]ResourceOut `json:"volume"` |
| gio | da70865 | 2025-04-30 14:57:38 +0400 | [diff] [blame] | 139 | PostgreSQL map[string]ResourceOut `json:"postgresql"` |
| 140 | MongoDB map[string]ResourceOut `json:"mongodb"` |
| 141 | Ingress map[string]resourceIngress `json:"ingress"` |
| gio | a602462 | 2025-05-01 18:37:16 +0400 | [diff] [blame] | 142 | Services map[string]ResourceOut `json:"services"` |
| gio | da70865 | 2025-04-30 14:57:38 +0400 | [diff] [blame] | 143 | Helm map[string]resourceHelm `json:"helmR"` |
| 144 | } |
| 145 | |
| 146 | type ResourceOuts struct { |
| 147 | Release struct { |
| 148 | Namespace string `json:"namespace"` |
| 149 | } |
| 150 | Outs map[string]ResourceOut `json:"outs"` |
| 151 | } |
| 152 | |
| 153 | type DodoResource struct { |
| 154 | Type string |
| 155 | Name string |
| 156 | } |
| 157 | |
| 158 | type DodoResourceStatus struct { |
| 159 | Type string `json:"type"` |
| 160 | Name string `json:"name"` |
| 161 | Status string `json:"status"` |
| 162 | } |
| 163 | |
| 164 | func DecodeResourceOuts(raw []byte) (ResourceOuts, error) { |
| 165 | var outs ResourceOuts |
| 166 | if err := json.NewDecoder(bytes.NewReader(raw)).Decode(&outs); err != nil { |
| 167 | return ResourceOuts{}, err |
| 168 | } |
| 169 | return outs, nil |
| gio | f8acc61 | 2025-04-26 08:20:55 +0400 | [diff] [blame] | 170 | } |