blob: 0351b213865101aa7a44a2e3931d0da7175caa53 [file] [log] [blame]
gio3af43942024-04-16 08:13:50 +04001package installer
2
3import (
4 "fmt"
gio09a3e5b2024-04-26 14:11:06 +04005 "html/template"
gio36b23b32024-08-25 12:20:54 +04006 "strings"
gio3af43942024-04-16 08:13:50 +04007)
8
9type Release struct {
gio3cdee592024-04-17 10:15:56 +040010 AppInstanceId string `json:"appInstanceId"`
11 Namespace string `json:"namespace"`
12 RepoAddr string `json:"repoAddr"`
13 AppDir string `json:"appDir"`
giof8843412024-05-22 16:38:05 +040014 ImageRegistry string `json:"imageRegistry,omitempty"`
gio3af43942024-04-16 08:13:50 +040015}
16
17type Network struct {
giocdfa3722024-06-13 20:10:14 +040018 Name string `json:"name,omitempty"`
19 IngressClass string `json:"ingressClass,omitempty"`
20 CertificateIssuer string `json:"certificateIssuer,omitempty"`
21 Domain string `json:"domain,omitempty"`
22 AllocatePortAddr string `json:"allocatePortAddr,omitempty"`
23 ReservePortAddr string `json:"reservePortAddr,omitempty"`
24 DeallocatePortAddr string `json:"deallocatePortAddr,omitempty"`
gio3af43942024-04-16 08:13:50 +040025}
26
gioe72b54f2024-04-22 10:44:41 +040027type InfraAppInstanceConfig struct {
28 Id string `json:"id"`
29 AppId string `json:"appId"`
30 Infra InfraConfig `json:"infra"`
31 Release Release `json:"release"`
32 Values map[string]any `json:"values"`
33 Input map[string]any `json:"input"`
gio09a3e5b2024-04-26 14:11:06 +040034 URL string `json:"url"`
35 Help []HelpDocument `json:"help"`
36 Icon template.HTML `json:"icon"`
gioe72b54f2024-04-22 10:44:41 +040037}
38
gio3cdee592024-04-17 10:15:56 +040039type AppInstanceConfig struct {
gio3af43942024-04-16 08:13:50 +040040 Id string `json:"id"`
41 AppId string `json:"appId"`
gioe72b54f2024-04-22 10:44:41 +040042 Env EnvConfig `json:"env"`
gio3cdee592024-04-17 10:15:56 +040043 Release Release `json:"release"`
44 Values map[string]any `json:"values"`
45 Input map[string]any `json:"input"`
gio09a3e5b2024-04-26 14:11:06 +040046 URL string `json:"url"`
Davit Tabidze56f86a42024-04-09 19:15:25 +040047 Help []HelpDocument `json:"help"`
gio09a3e5b2024-04-26 14:11:06 +040048 Icon string `json:"icon"`
gio3af43942024-04-16 08:13:50 +040049}
50
gio3cdee592024-04-17 10:15:56 +040051func (a AppInstanceConfig) InputToValues(schema Schema) map[string]any {
52 ret, err := derivedToConfig(a.Input, schema)
gio3af43942024-04-16 08:13:50 +040053 if err != nil {
gio3cdee592024-04-17 10:15:56 +040054 panic(err)
gio3af43942024-04-16 08:13:50 +040055 }
56 return ret
57}
58
gio36b23b32024-08-25 12:20:54 +040059func getField(v any, f string) any {
60 for _, i := range strings.Split(f, ".") {
61 vm := v.(map[string]any)
62 v = vm[i]
63 }
64 return v
65}
66
67func deriveValues(
68 root any,
69 values any,
70 schema Schema,
71 networks []Network,
gio864b4332024-09-05 13:56:47 +040072 vpnKeyGen VPNAPIClient,
gio36b23b32024-08-25 12:20:54 +040073) (map[string]any, error) {
gio3af43942024-04-16 08:13:50 +040074 ret := make(map[string]any)
gio44f621b2024-04-29 09:44:38 +040075 for _, f := range schema.Fields() {
76 k := f.Name
77 def := f.Schema
gio3af43942024-04-16 08:13:50 +040078 // TODO(gio): validate that it is map
79 v, ok := values.(map[string]any)[k]
80 // TODO(gio): if missing use default value
81 if !ok {
82 if def.Kind() == KindSSHKey {
83 key, err := NewECDSASSHKeyPair("tmp")
84 if err != nil {
85 return nil, err
86 }
87 ret[k] = map[string]string{
88 "public": string(key.RawAuthorizedKey()),
89 "private": string(key.RawPrivateKey()),
90 }
91 }
gio36b23b32024-08-25 12:20:54 +040092 if def.Kind() == KindVPNAuthKey {
gio29f6b872024-09-08 16:14:58 +040093 enabled := true
94 if v, ok := def.Meta()["enabledField"]; ok {
95 // TODO(gio): Improve getField
96 enabled, ok = getField(root, v).(bool)
97 if !ok {
98 return nil, fmt.Errorf("could not resolve enabled: %+v %s %+v", def.Meta(), v, root)
99 }
100 }
101 if !enabled {
102 continue
103 }
gio7fbd4ad2024-08-27 10:06:39 +0400104 var username string
105 if v, ok := def.Meta()["username"]; ok {
106 username = v
107 } else if v, ok := def.Meta()["usernameField"]; ok {
108 // TODO(gio): Improve getField
109 username, ok = getField(root, v).(string)
110 if !ok {
111 return nil, fmt.Errorf("could not resolve username: %+v %s %+v", def.Meta(), v, root)
112 }
113 }
gio864b4332024-09-05 13:56:47 +0400114 authKey, err := vpnKeyGen.GenerateAuthKey(username)
gio36b23b32024-08-25 12:20:54 +0400115 if err != nil {
116 return nil, err
117 }
118 ret[k] = authKey
119 }
gio3af43942024-04-16 08:13:50 +0400120 continue
121 }
122 switch def.Kind() {
123 case KindBoolean:
124 ret[k] = v
125 case KindString:
126 ret[k] = v
127 case KindInt:
128 ret[k] = v
gioefa0ed42024-06-13 12:31:43 +0400129 case KindPort:
130 ret[k] = v
gio36b23b32024-08-25 12:20:54 +0400131 case KindVPNAuthKey:
132 ret[k] = v
gioe72b54f2024-04-22 10:44:41 +0400133 case KindArrayString:
134 a, ok := v.([]string)
135 if !ok {
136 return nil, fmt.Errorf("expected string array")
137 }
138 ret[k] = a
gio3af43942024-04-16 08:13:50 +0400139 case KindNetwork:
gio4ece99c2024-07-18 11:05:50 +0400140 name, ok := v.(string)
141 if !ok {
142 return nil, fmt.Errorf("not a string")
143 }
144 n, err := findNetwork(networks, name)
gio3af43942024-04-16 08:13:50 +0400145 if err != nil {
146 return nil, err
147 }
148 ret[k] = n
gio4ece99c2024-07-18 11:05:50 +0400149 case KindMultiNetwork:
150 vv, ok := v.([]any)
151 if !ok {
152 return nil, fmt.Errorf("not an array")
153 }
154 picked := []Network{}
155 for _, nn := range vv {
156 name, ok := nn.(string)
157 if !ok {
158 return nil, fmt.Errorf("not a string")
159 }
160 n, err := findNetwork(networks, name)
161 if err != nil {
162 return nil, err
163 }
164 picked = append(picked, n)
165 }
166 ret[k] = picked
gio3af43942024-04-16 08:13:50 +0400167 case KindAuth:
gio7fbd4ad2024-08-27 10:06:39 +0400168 r, err := deriveValues(root, v, AuthSchema, networks, vpnKeyGen)
gio3af43942024-04-16 08:13:50 +0400169 if err != nil {
170 return nil, err
171 }
172 ret[k] = r
173 case KindSSHKey:
gio7fbd4ad2024-08-27 10:06:39 +0400174 r, err := deriveValues(root, v, SSHKeySchema, networks, vpnKeyGen)
gio3af43942024-04-16 08:13:50 +0400175 if err != nil {
176 return nil, err
177 }
178 ret[k] = r
179 case KindStruct:
gio7fbd4ad2024-08-27 10:06:39 +0400180 r, err := deriveValues(root, v, def, networks, vpnKeyGen)
gio3af43942024-04-16 08:13:50 +0400181 if err != nil {
182 return nil, err
183 }
184 ret[k] = r
185 default:
186 return nil, fmt.Errorf("Should not reach!")
187 }
188 }
189 return ret, nil
190}
191
192func derivedToConfig(derived map[string]any, schema Schema) (map[string]any, error) {
193 ret := make(map[string]any)
gio44f621b2024-04-29 09:44:38 +0400194 for _, f := range schema.Fields() {
195 k := f.Name
196 def := f.Schema
gio3af43942024-04-16 08:13:50 +0400197 v, ok := derived[k]
198 // TODO(gio): if missing use default value
199 if !ok {
200 continue
201 }
202 switch def.Kind() {
203 case KindBoolean:
204 ret[k] = v
205 case KindString:
206 ret[k] = v
207 case KindInt:
208 ret[k] = v
gioefa0ed42024-06-13 12:31:43 +0400209 case KindPort:
210 ret[k] = v
gio36b23b32024-08-25 12:20:54 +0400211 case KindVPNAuthKey:
212 ret[k] = v
gioe72b54f2024-04-22 10:44:41 +0400213 case KindArrayString:
214 a, ok := v.([]string)
215 if !ok {
216 return nil, fmt.Errorf("expected string array")
217 }
218 ret[k] = a
gio3af43942024-04-16 08:13:50 +0400219 case KindNetwork:
220 vm, ok := v.(map[string]any)
221 if !ok {
222 return nil, fmt.Errorf("expected map")
223 }
224 name, ok := vm["name"]
225 if !ok {
226 return nil, fmt.Errorf("expected network name")
227 }
228 ret[k] = name
gio4ece99c2024-07-18 11:05:50 +0400229 case KindMultiNetwork:
230 nl, ok := v.([]any)
231 if !ok {
232 return nil, fmt.Errorf("expected map")
233 }
234 names := []string{}
235 for _, n := range nl {
236 i, ok := n.(map[string]any)
237 if !ok {
238 return nil, fmt.Errorf("expected map")
239 }
240 name, ok := i["name"]
241 if !ok {
242 return nil, fmt.Errorf("expected network name")
243 }
244 names = append(names, name.(string))
245 }
246 ret[k] = names
gio3af43942024-04-16 08:13:50 +0400247 case KindAuth:
248 vm, ok := v.(map[string]any)
249 if !ok {
250 return nil, fmt.Errorf("expected map")
251 }
252 r, err := derivedToConfig(vm, AuthSchema)
253 if err != nil {
254 return nil, err
255 }
256 ret[k] = r
257 case KindSSHKey:
258 vm, ok := v.(map[string]any)
259 if !ok {
260 return nil, fmt.Errorf("expected map")
261 }
262 r, err := derivedToConfig(vm, SSHKeySchema)
263 if err != nil {
264 return nil, err
265 }
266 ret[k] = r
267 case KindStruct:
268 vm, ok := v.(map[string]any)
269 if !ok {
270 return nil, fmt.Errorf("expected map")
271 }
272 r, err := derivedToConfig(vm, def)
273 if err != nil {
274 return nil, err
275 }
276 ret[k] = r
277 default:
278 return nil, fmt.Errorf("Should not reach!")
279 }
280 }
281 return ret, nil
282}
283
284func findNetwork(networks []Network, name string) (Network, error) {
285 for _, n := range networks {
286 if n.Name == name {
287 return n, nil
288 }
289 }
290 return Network{}, fmt.Errorf("Network not found: %s", name)
291}