blob: 9c658df819f6d0c9ecbf851e6fb42f9f85e17de3 [file] [log] [blame]
giolekva8aa73e82022-07-09 11:34:39 +04001package installer
giolekva050609f2021-12-29 15:51:40 +04002
giolekva8aa73e82022-07-09 11:34:39 +04003import (
Giorgi Lekveishvili3f697b12024-01-04 00:56:06 +04004 "bytes"
gio0eaf2712024-04-14 13:08:46 +04005 _ "embed"
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +04006 "encoding/json"
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +04007 "fmt"
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +04008 template "html/template"
gio3cdee592024-04-17 10:15:56 +04009 "net"
gioe72b54f2024-04-22 10:44:41 +040010 "net/netip"
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +040011 "strings"
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +040012
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +040013 "cuelang.org/go/cue"
gio308105e2024-04-19 13:12:13 +040014 "cuelang.org/go/cue/build"
15 "cuelang.org/go/cue/cuecontext"
16 "cuelang.org/go/cue/load"
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +040017 cueyaml "cuelang.org/go/encoding/yaml"
giof8843412024-05-22 16:38:05 +040018 helmv2 "github.com/fluxcd/helm-controller/api/v2"
giolekva8aa73e82022-07-09 11:34:39 +040019)
giolekva050609f2021-12-29 15:51:40 +040020
giof8843412024-05-22 16:38:05 +040021//go:embed app_configs/dodo_app.cue
22var dodoAppCue []byte
gio0eaf2712024-04-14 13:08:46 +040023
giof8843412024-05-22 16:38:05 +040024//go:embed app_configs/app_base.cue
25var cueBaseConfig []byte
gio0eaf2712024-04-14 13:08:46 +040026
giof8843412024-05-22 16:38:05 +040027//go:embed app_configs/app_global_env.cue
28var cueEnvAppGlobal []byte
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +040029
giof8843412024-05-22 16:38:05 +040030//go:embed app_configs/app_global_infra.cue
31var cueInfraAppGlobal []byte
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +040032
gioe72b54f2024-04-22 10:44:41 +040033type rendered struct {
giof8843412024-05-22 16:38:05 +040034 Name string
35 Readme string
giof6ad2982024-08-23 17:42:49 +040036 Cluster string
37 Namespaces []Namespace
giof8843412024-05-22 16:38:05 +040038 Resources CueAppData
39 HelmCharts HelmCharts
40 ContainerImages map[string]ContainerImage
41 Ports []PortForward
giof6ad2982024-08-23 17:42:49 +040042 ClusterProxies map[string]ClusterProxy
giof8843412024-05-22 16:38:05 +040043 Data CueAppData
44 URL string
45 Help []HelpDocument
46 Icon string
gio94904702024-07-26 16:58:34 +040047 Raw []byte
Davit Tabidze56f86a42024-04-09 19:15:25 +040048}
49
giof6ad2982024-08-23 17:42:49 +040050type Namespace struct {
51 Name string `json:"name"`
52 Kubeconfig string `json:"kubeconfig,omitempty"`
53}
54
Davit Tabidze56f86a42024-04-09 19:15:25 +040055type HelpDocument struct {
56 Title string
57 Contents string
58 Children []HelpDocument
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +040059}
60
giof8843412024-05-22 16:38:05 +040061type ContainerImage struct {
62 Registry string `json:"registry"`
63 Repository string `json:"repository"`
64 Name string `json:"name"`
65 Tag string `json:"tag"`
66}
67
68type helmChartRef struct {
69 Kind string `json:"kind"`
70}
71
72type HelmCharts struct {
73 Git map[string]HelmChartGitRepo
74}
75
76type HelmChartGitRepo struct {
77 Address string `json:"address"`
78 Branch string `json:"branch"`
79 Path string `json:"path"`
80}
81
gioe72b54f2024-04-22 10:44:41 +040082type EnvAppRendered struct {
83 rendered
84 Config AppInstanceConfig
85}
86
87type InfraAppRendered struct {
88 rendered
89 Config InfraAppInstanceConfig
90}
91
giof6ad2982024-08-23 17:42:49 +040092type ClusterProxy struct {
93 From string `json:"from"`
94 To string `json:"to"`
95}
96
gio3cdee592024-04-17 10:15:56 +040097type PortForward struct {
gio721c0042025-04-03 11:56:36 +040098 Cluster string `json:"cluster,omitempty"`
gio802311e2024-11-04 08:37:34 +040099 Allocator string `json:"allocator"`
100 ReserveAddr string `json:"reservator"`
101 RemoveAddr string `json:"deallocator"`
102 Protocol string `json:"protocol"`
103 Port int `json:"port"`
104 Service struct {
giof4344632025-04-08 20:04:35 +0400105 Name string `json:"name"`
106 Namespace string `json:"namespace,omitempty"`
107 Port int `json:"port"`
gio802311e2024-11-04 08:37:34 +0400108 } `json:"service"`
gio3cdee592024-04-17 10:15:56 +0400109}
110
111type AppType int
112
113const (
114 AppTypeInfra AppType = iota
115 AppTypeEnv
116)
117
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400118type App interface {
119 Name() string
gio44f621b2024-04-29 09:44:38 +0400120 Type() AppType
121 Slug() string
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400122 Description() string
123 Icon() template.HTML
124 Schema() Schema
gioef01fbb2024-04-12 16:52:59 +0400125 Namespace() string
gio3cdee592024-04-17 10:15:56 +0400126}
127
128type InfraConfig struct {
gioe72b54f2024-04-22 10:44:41 +0400129 Name string `json:"pcloudEnvName,omitempty"` // #TODO(gio): change to name
130 PublicIP []net.IP `json:"publicIP,omitempty"`
131 InfraNamespacePrefix string `json:"namespacePrefix,omitempty"`
132 InfraAdminPublicKey []byte `json:"infraAdminPublicKey,omitempty"`
gio3cdee592024-04-17 10:15:56 +0400133}
134
gio7841f4f2024-07-26 19:53:49 +0400135type InfraNetwork struct {
136 Name string `json:"name,omitempty"`
137 IngressClass string `json:"ingressClass,omitempty"`
138 CertificateIssuer string `json:"certificateIssuer,omitempty"`
139 AllocatePortAddr string `json:"allocatePortAddr,omitempty"`
140 ReservePortAddr string `json:"reservePortAddr,omitempty"`
141 DeallocatePortAddr string `json:"deallocatePortAddr,omitempty"`
142}
143
gio3cdee592024-04-17 10:15:56 +0400144type InfraApp interface {
145 App
gio7841f4f2024-07-26 19:53:49 +0400146 Render(release Release, infra InfraConfig, networks []InfraNetwork, values map[string]any, charts map[string]helmv2.HelmChartTemplateSpec) (InfraAppRendered, error)
gioe72b54f2024-04-22 10:44:41 +0400147}
148
149type EnvNetwork struct {
150 DNS net.IP `json:"dns,omitempty"`
151 DNSInClusterIP net.IP `json:"dnsInClusterIP,omitempty"`
152 Ingress net.IP `json:"ingress,omitempty"`
153 Headscale net.IP `json:"headscale,omitempty"`
154 ServicesFrom net.IP `json:"servicesFrom,omitempty"`
155 ServicesTo net.IP `json:"servicesTo,omitempty"`
156}
157
158func NewEnvNetwork(subnet net.IP) (EnvNetwork, error) {
159 addr, err := netip.ParseAddr(subnet.String())
160 if err != nil {
161 return EnvNetwork{}, err
162 }
163 if !addr.Is4() {
164 return EnvNetwork{}, fmt.Errorf("Expected IPv4, got %s instead", addr)
165 }
166 dns := addr.Next()
167 ingress := dns.Next()
168 headscale := ingress.Next()
169 b := addr.AsSlice()
170 if b[3] != 0 {
171 return EnvNetwork{}, fmt.Errorf("Expected last byte to be zero, got %d instead", b[3])
172 }
173 b[3] = 10
174 servicesFrom, ok := netip.AddrFromSlice(b)
175 if !ok {
176 return EnvNetwork{}, fmt.Errorf("Must not reach")
177 }
178 b[3] = 254
179 servicesTo, ok := netip.AddrFromSlice(b)
180 if !ok {
181 return EnvNetwork{}, fmt.Errorf("Must not reach")
182 }
183 b[3] = b[2]
184 b[2] = b[1]
185 b[0] = 10
186 b[1] = 44
187 dnsInClusterIP, ok := netip.AddrFromSlice(b)
188 if !ok {
189 return EnvNetwork{}, fmt.Errorf("Must not reach")
190 }
191 return EnvNetwork{
192 DNS: net.ParseIP(dns.String()),
193 DNSInClusterIP: net.ParseIP(dnsInClusterIP.String()),
194 Ingress: net.ParseIP(ingress.String()),
195 Headscale: net.ParseIP(headscale.String()),
196 ServicesFrom: net.ParseIP(servicesFrom.String()),
197 ServicesTo: net.ParseIP(servicesTo.String()),
198 }, nil
gio3cdee592024-04-17 10:15:56 +0400199}
200
gioe72b54f2024-04-22 10:44:41 +0400201type EnvConfig struct {
202 Id string `json:"id,omitempty"`
203 InfraName string `json:"pcloudEnvName,omitempty"`
204 Domain string `json:"domain,omitempty"`
205 PrivateDomain string `json:"privateDomain,omitempty"`
206 ContactEmail string `json:"contactEmail,omitempty"`
207 AdminPublicKey string `json:"adminPublicKey,omitempty"`
208 PublicIP []net.IP `json:"publicIP,omitempty"`
209 NameserverIP []net.IP `json:"nameserverIP,omitempty"`
210 NamespacePrefix string `json:"namespacePrefix,omitempty"`
211 Network EnvNetwork `json:"network,omitempty"`
gio3cdee592024-04-17 10:15:56 +0400212}
213
214type EnvApp interface {
215 App
gio36b23b32024-08-25 12:20:54 +0400216 Render(
217 release Release,
218 env EnvConfig,
219 networks []Network,
giof6ad2982024-08-23 17:42:49 +0400220 clusters []Cluster,
gio36b23b32024-08-25 12:20:54 +0400221 values map[string]any,
222 charts map[string]helmv2.HelmChartTemplateSpec,
gio864b4332024-09-05 13:56:47 +0400223 vpnKeyGen VPNAPIClient,
gio36b23b32024-08-25 12:20:54 +0400224 ) (EnvAppRendered, error)
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +0400225}
226
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400227type cueApp struct {
228 name string
229 description string
230 icon template.HTML
231 namespace string
232 schema Schema
gio308105e2024-04-19 13:12:13 +0400233 cfg cue.Value
234 data CueAppData
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400235}
236
gio308105e2024-04-19 13:12:13 +0400237type CueAppData map[string][]byte
238
239func ParseCueAppConfig(data CueAppData) (cue.Value, error) {
240 ctx := cuecontext.New()
241 buildCtx := build.NewContext()
242 cfg := &load.Config{
243 Context: buildCtx,
244 Overlay: map[string]load.Source{},
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400245 }
gio308105e2024-04-19 13:12:13 +0400246 names := make([]string, 0)
247 for n, b := range data {
248 a := fmt.Sprintf("/%s", n)
249 names = append(names, a)
250 cfg.Overlay[a] = load.FromString("package main\n\n" + string(b))
251 }
252 instances := load.Instances(names, cfg)
253 for _, inst := range instances {
254 if inst.Err != nil {
255 return cue.Value{}, inst.Err
256 }
257 }
258 if len(instances) != 1 {
259 return cue.Value{}, fmt.Errorf("invalid")
260 }
261 ret := ctx.BuildInstance(instances[0])
gioc81a8472024-09-24 13:06:19 +0200262 if err := ret.Err(); err != nil {
263 return cue.Value{}, err
gio308105e2024-04-19 13:12:13 +0400264 }
265 if err := ret.Validate(); err != nil {
266 return cue.Value{}, err
267 }
268 return ret, nil
269}
270
271func newCueApp(config cue.Value, data CueAppData) (cueApp, error) {
gio3cdee592024-04-17 10:15:56 +0400272 cfg := struct {
273 Name string `json:"name"`
274 Namespace string `json:"namespace"`
275 Description string `json:"description"`
276 Icon string `json:"icon"`
277 }{}
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400278 if err := config.Decode(&cfg); err != nil {
279 return cueApp{}, err
280 }
gio44f621b2024-04-29 09:44:38 +0400281 schema, err := NewCueSchema("input", config.LookupPath(cue.ParsePath("input")))
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400282 if err != nil {
283 return cueApp{}, err
284 }
285 return cueApp{
286 name: cfg.Name,
287 description: cfg.Description,
288 icon: template.HTML(cfg.Icon),
289 namespace: cfg.Namespace,
290 schema: schema,
291 cfg: config,
gio308105e2024-04-19 13:12:13 +0400292 data: data,
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400293 }, nil
294}
295
gio308105e2024-04-19 13:12:13 +0400296func ParseAndCreateNewCueApp(data CueAppData) (cueApp, error) {
297 config, err := ParseCueAppConfig(data)
298 if err != nil {
299 return cueApp{}, err
300 }
301 return newCueApp(config, data)
302}
303
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400304func (a cueApp) Name() string {
305 return a.name
306}
307
gio44f621b2024-04-29 09:44:38 +0400308func (a cueApp) Slug() string {
309 return strings.ReplaceAll(strings.ToLower(a.name), " ", "-")
310}
311
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400312func (a cueApp) Description() string {
313 return a.description
314}
315
316func (a cueApp) Icon() template.HTML {
317 return a.icon
318}
319
320func (a cueApp) Schema() Schema {
321 return a.schema
322}
323
gioef01fbb2024-04-12 16:52:59 +0400324func (a cueApp) Namespace() string {
325 return a.namespace
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400326}
327
gioe72b54f2024-04-22 10:44:41 +0400328func (a cueApp) render(values map[string]any) (rendered, error) {
329 ret := rendered{
gio44f621b2024-04-29 09:44:38 +0400330 Name: a.Slug(),
gio308105e2024-04-19 13:12:13 +0400331 Resources: make(CueAppData),
giof8843412024-05-22 16:38:05 +0400332 HelmCharts: HelmCharts{
333 Git: make(map[string]HelmChartGitRepo),
334 },
335 ContainerImages: make(map[string]ContainerImage),
336 Ports: make([]PortForward, 0),
337 Data: a.data,
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400338 }
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400339 var buf bytes.Buffer
gio3cdee592024-04-17 10:15:56 +0400340 if err := json.NewEncoder(&buf).Encode(values); err != nil {
gioe72b54f2024-04-22 10:44:41 +0400341 return rendered{}, err
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400342 }
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400343 ctx := a.cfg.Context()
344 d := ctx.CompileBytes(buf.Bytes())
345 res := a.cfg.Unify(d).Eval()
346 if err := res.Err(); err != nil {
gioe72b54f2024-04-22 10:44:41 +0400347 return rendered{}, err
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400348 }
349 if err := res.Validate(); err != nil {
gioe72b54f2024-04-22 10:44:41 +0400350 return rendered{}, err
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400351 }
gio308105e2024-04-19 13:12:13 +0400352 full, err := json.MarshalIndent(res, "", "\t")
353 if err != nil {
gioe72b54f2024-04-22 10:44:41 +0400354 return rendered{}, err
gio308105e2024-04-19 13:12:13 +0400355 }
gio94904702024-07-26 16:58:34 +0400356 ret.Raw = full
gio308105e2024-04-19 13:12:13 +0400357 ret.Data["rendered.json"] = full
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400358 readme, err := res.LookupPath(cue.ParsePath("readme")).String()
359 if err != nil {
gioe72b54f2024-04-22 10:44:41 +0400360 return rendered{}, err
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400361 }
362 ret.Readme = readme
giof6ad2982024-08-23 17:42:49 +0400363 res.LookupPath(cue.ParsePath("input.cluster.name")).Decode(&ret.Cluster)
364 if err := res.LookupPath(cue.ParsePath("output.clusterProxy")).Decode(&ret.ClusterProxies); err != nil {
365 return rendered{}, err
366 }
367 if err := res.LookupPath(cue.ParsePath("namespaces")).Decode(&ret.Namespaces); err != nil {
368 return rendered{}, err
369 }
gio802311e2024-11-04 08:37:34 +0400370 if err := res.LookupPath(cue.ParsePath("output.openPort")).Decode(&ret.Ports); err != nil {
gioe72b54f2024-04-22 10:44:41 +0400371 return rendered{}, err
Giorgi Lekveishvilib59b7c22024-04-03 22:17:50 +0400372 }
gio0eaf2712024-04-14 13:08:46 +0400373 {
gio7fbd4ad2024-08-27 10:06:39 +0400374 charts := res.LookupPath(cue.ParsePath("output.charts"))
giof8843412024-05-22 16:38:05 +0400375 i, err := charts.Fields()
376 if err != nil {
377 return rendered{}, err
378 }
379 for i.Next() {
380 var chartRef helmChartRef
381 if err := i.Value().Decode(&chartRef); err != nil {
382 return rendered{}, err
383 }
384 if chartRef.Kind == "GitRepository" {
385 var chart HelmChartGitRepo
386 if err := i.Value().Decode(&chart); err != nil {
387 return rendered{}, err
388 }
389 ret.HelmCharts.Git[cleanName(i.Selector().String())] = chart
390 }
391 }
392 }
393 {
gio7fbd4ad2024-08-27 10:06:39 +0400394 images := res.LookupPath(cue.ParsePath("output.images"))
giof8843412024-05-22 16:38:05 +0400395 i, err := images.Fields()
396 if err != nil {
397 return rendered{}, err
398 }
399 for i.Next() {
400 var img ContainerImage
401 if err := i.Value().Decode(&img); err != nil {
402 return rendered{}, err
403 }
404 ret.ContainerImages[cleanName(i.Selector().String())] = img
405 }
406 }
407 {
gio7fbd4ad2024-08-27 10:06:39 +0400408 helm := res.LookupPath(cue.ParsePath("output.helm"))
409 i, err := helm.Fields()
gio0eaf2712024-04-14 13:08:46 +0400410 if err != nil {
gioe72b54f2024-04-22 10:44:41 +0400411 return rendered{}, err
gio0eaf2712024-04-14 13:08:46 +0400412 }
413 for i.Next() {
414 if contents, err := cueyaml.Encode(i.Value()); err != nil {
415 return rendered{}, err
416 } else {
417 name := fmt.Sprintf("%s.yaml", cleanName(i.Selector().String()))
418 ret.Resources[name] = contents
419 }
420 }
421 }
422 {
423 resources := res.LookupPath(cue.ParsePath("resources"))
424 i, err := resources.Fields()
425 if err != nil {
426 return rendered{}, err
427 }
428 for i.Next() {
429 if contents, err := cueyaml.Encode(i.Value()); err != nil {
430 return rendered{}, err
431 } else {
432 name := fmt.Sprintf("%s.yaml", cleanName(i.Selector().String()))
433 ret.Resources[name] = contents
434 }
Giorgi Lekveishvili3f697b12024-01-04 00:56:06 +0400435 }
Giorgi Lekveishvili3f697b12024-01-04 00:56:06 +0400436 }
Davit Tabidze56f86a42024-04-09 19:15:25 +0400437 helpValue := res.LookupPath(cue.ParsePath("help"))
438 if helpValue.Exists() {
439 if err := helpValue.Decode(&ret.Help); err != nil {
gioe72b54f2024-04-22 10:44:41 +0400440 return rendered{}, err
Davit Tabidze56f86a42024-04-09 19:15:25 +0400441 }
442 }
443 url, err := res.LookupPath(cue.ParsePath("url")).String()
444 if err != nil {
gioe72b54f2024-04-22 10:44:41 +0400445 return rendered{}, err
Davit Tabidze56f86a42024-04-09 19:15:25 +0400446 }
gio09a3e5b2024-04-26 14:11:06 +0400447 ret.URL = url
Davit Tabidze56f86a42024-04-09 19:15:25 +0400448 icon, err := res.LookupPath(cue.ParsePath("icon")).String()
449 if err != nil {
gioe72b54f2024-04-22 10:44:41 +0400450 return rendered{}, err
Davit Tabidze56f86a42024-04-09 19:15:25 +0400451 }
452 ret.Icon = icon
Giorgi Lekveishvili3f697b12024-01-04 00:56:06 +0400453 return ret, nil
454}
455
gio3cdee592024-04-17 10:15:56 +0400456type cueEnvApp struct {
457 cueApp
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +0400458}
459
gio308105e2024-04-19 13:12:13 +0400460func NewCueEnvApp(data CueAppData) (EnvApp, error) {
461 app, err := ParseAndCreateNewCueApp(data)
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +0400462 if err != nil {
463 return nil, err
464 }
gio3cdee592024-04-17 10:15:56 +0400465 return cueEnvApp{app}, nil
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +0400466}
467
gio0eaf2712024-04-14 13:08:46 +0400468func NewDodoApp(appCfg []byte) (EnvApp, error) {
469 return NewCueEnvApp(CueAppData{
giofc9c4ea2024-06-26 13:46:53 +0400470 "app.cue": appCfg,
471 "base.cue": []byte(cueBaseConfig),
472 "dodo.cue": dodoAppCue,
473 "env.cue": []byte(cueEnvAppGlobal),
gio0eaf2712024-04-14 13:08:46 +0400474 })
475}
476
gio3cdee592024-04-17 10:15:56 +0400477func (a cueEnvApp) Type() AppType {
478 return AppTypeEnv
479}
480
giofc441e32024-11-11 16:26:14 +0400481func merge(d map[string]any, v map[string]any) map[string]any {
482 ret := map[string]any{}
483 for k, val := range d {
484 if vv, ok := v[k]; ok && vv != nil {
485 if mv, ok := val.(map[string]any); ok {
486 // TODO(gio): check that it is actually map
487 fmt.Println(vv)
488 ret[k] = merge(mv, vv.(map[string]any))
489 } else {
490 ret[k] = vv
491 }
492 } else {
493 ret[k] = val
494 }
495 }
496 for k, v := range v {
497 if v != nil {
498 if _, ok := d[k]; !ok {
499 d[k] = v
500 }
501 }
502 }
503 return ret
504}
505
giocb34ad22024-07-11 08:01:13 +0400506func (a cueEnvApp) Render(
507 release Release,
508 env EnvConfig,
509 networks []Network,
giof6ad2982024-08-23 17:42:49 +0400510 clusters []Cluster,
giocb34ad22024-07-11 08:01:13 +0400511 values map[string]any,
512 charts map[string]helmv2.HelmChartTemplateSpec,
gio864b4332024-09-05 13:56:47 +0400513 vpnKeyGen VPNAPIClient,
giocb34ad22024-07-11 08:01:13 +0400514) (EnvAppRendered, error) {
giofc441e32024-11-11 16:26:14 +0400515 dv, err := ExtractDefaultValues(a.cueApp.cfg.LookupPath(cue.ParsePath("input")))
516 if err != nil {
517 return EnvAppRendered{}, err
518 }
519 mv := merge(dv.(map[string]any), values)
520 derived, err := deriveValues(mv, values, a.Schema(), networks, clusters, vpnKeyGen)
gio3cdee592024-04-17 10:15:56 +0400521 if err != nil {
gioefa0ed42024-06-13 12:31:43 +0400522 return EnvAppRendered{}, err
gio3cdee592024-04-17 10:15:56 +0400523 }
giof6ad2982024-08-23 17:42:49 +0400524 // return EnvAppRendered{}, fmt.Errorf("asdasd")
giof8843412024-05-22 16:38:05 +0400525 if charts == nil {
526 charts = make(map[string]helmv2.HelmChartTemplateSpec)
527 }
giof15b9da2024-09-19 06:59:16 +0400528 if clusters == nil {
529 clusters = []Cluster{}
530 }
gio3cdee592024-04-17 10:15:56 +0400531 ret, err := a.cueApp.render(map[string]any{
giof8843412024-05-22 16:38:05 +0400532 "global": env,
533 "release": release,
534 "input": derived,
535 "localCharts": charts,
gio5e49bb62024-07-20 10:43:19 +0400536 "networks": NetworkMap(networks),
giof15b9da2024-09-19 06:59:16 +0400537 "clusters": clusters,
gio3cdee592024-04-17 10:15:56 +0400538 })
539 if err != nil {
gioe72b54f2024-04-22 10:44:41 +0400540 return EnvAppRendered{}, err
gio3cdee592024-04-17 10:15:56 +0400541 }
gioe72b54f2024-04-22 10:44:41 +0400542 return EnvAppRendered{
543 rendered: ret,
544 Config: AppInstanceConfig{
gio44f621b2024-04-29 09:44:38 +0400545 AppId: a.Slug(),
gioe72b54f2024-04-22 10:44:41 +0400546 Env: env,
547 Release: release,
548 Values: values,
549 Input: derived,
gio09a3e5b2024-04-26 14:11:06 +0400550 URL: ret.URL,
gioe72b54f2024-04-22 10:44:41 +0400551 Help: ret.Help,
gio09a3e5b2024-04-26 14:11:06 +0400552 Icon: ret.Icon,
gioe72b54f2024-04-22 10:44:41 +0400553 },
554 }, nil
gio3cdee592024-04-17 10:15:56 +0400555}
556
557type cueInfraApp struct {
558 cueApp
559}
560
gio308105e2024-04-19 13:12:13 +0400561func NewCueInfraApp(data CueAppData) (InfraApp, error) {
562 app, err := ParseAndCreateNewCueApp(data)
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +0400563 if err != nil {
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400564 return nil, err
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +0400565 }
gio3cdee592024-04-17 10:15:56 +0400566 return cueInfraApp{app}, nil
567}
568
569func (a cueInfraApp) Type() AppType {
570 return AppTypeInfra
571}
572
gio7841f4f2024-07-26 19:53:49 +0400573func (a cueInfraApp) Render(release Release, infra InfraConfig, networks []InfraNetwork, values map[string]any, charts map[string]helmv2.HelmChartTemplateSpec) (InfraAppRendered, error) {
giof8843412024-05-22 16:38:05 +0400574 if charts == nil {
575 charts = make(map[string]helmv2.HelmChartTemplateSpec)
576 }
gioe72b54f2024-04-22 10:44:41 +0400577 ret, err := a.cueApp.render(map[string]any{
giof8843412024-05-22 16:38:05 +0400578 "global": infra,
579 "release": release,
580 "input": values,
581 "localCharts": charts,
gio7841f4f2024-07-26 19:53:49 +0400582 "networks": InfraNetworkMap(networks),
gio3cdee592024-04-17 10:15:56 +0400583 })
gioe72b54f2024-04-22 10:44:41 +0400584 if err != nil {
585 return InfraAppRendered{}, err
586 }
587 return InfraAppRendered{
588 rendered: ret,
589 Config: InfraAppInstanceConfig{
gio44f621b2024-04-29 09:44:38 +0400590 AppId: a.Slug(),
gioe72b54f2024-04-22 10:44:41 +0400591 Infra: infra,
592 Release: release,
593 Values: values,
594 Input: values,
gio09a3e5b2024-04-26 14:11:06 +0400595 URL: ret.URL,
596 Help: ret.Help,
gioe72b54f2024-04-22 10:44:41 +0400597 },
598 }, nil
Giorgi Lekveishvilief21c132024-01-17 18:57:58 +0400599}
600
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400601func cleanName(s string) string {
602 return strings.ReplaceAll(strings.ReplaceAll(s, "\"", ""), "'", "")
Giorgi Lekveishvilief21c132024-01-17 18:57:58 +0400603}
gioe72b54f2024-04-22 10:44:41 +0400604
605func join[T fmt.Stringer](items []T, sep string) string {
606 var tmp []string
607 for _, i := range items {
608 tmp = append(tmp, i.String())
609 }
610 return strings.Join(tmp, ",")
611}
gio0eaf2712024-04-14 13:08:46 +0400612
gio5e49bb62024-07-20 10:43:19 +0400613func NetworkMap(networks []Network) map[string]Network {
gio0eaf2712024-04-14 13:08:46 +0400614 ret := make(map[string]Network)
615 for _, n := range networks {
616 ret[strings.ToLower(n.Name)] = n
617 }
618 return ret
619}
gio7841f4f2024-07-26 19:53:49 +0400620
621func InfraNetworkMap(networks []InfraNetwork) map[string]InfraNetwork {
622 ret := make(map[string]InfraNetwork)
623 for _, n := range networks {
624 ret[strings.ToLower(n.Name)] = n
625 }
626 return ret
627}