blob: 948e17e58fd34c47b69da765ae9a813cac6acd47 [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 {
gio802311e2024-11-04 08:37:34 +040098 Allocator string `json:"allocator"`
99 ReserveAddr string `json:"reservator"`
100 RemoveAddr string `json:"deallocator"`
101 Protocol string `json:"protocol"`
102 Port int `json:"port"`
103 Service struct {
104 Name string `json:"name"`
105 Port int `json:"port"`
106 } `json:"service"`
gio3cdee592024-04-17 10:15:56 +0400107}
108
109type AppType int
110
111const (
112 AppTypeInfra AppType = iota
113 AppTypeEnv
114)
115
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400116type App interface {
117 Name() string
gio44f621b2024-04-29 09:44:38 +0400118 Type() AppType
119 Slug() string
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400120 Description() string
121 Icon() template.HTML
122 Schema() Schema
gioef01fbb2024-04-12 16:52:59 +0400123 Namespace() string
gio3cdee592024-04-17 10:15:56 +0400124}
125
126type InfraConfig struct {
gioe72b54f2024-04-22 10:44:41 +0400127 Name string `json:"pcloudEnvName,omitempty"` // #TODO(gio): change to name
128 PublicIP []net.IP `json:"publicIP,omitempty"`
129 InfraNamespacePrefix string `json:"namespacePrefix,omitempty"`
130 InfraAdminPublicKey []byte `json:"infraAdminPublicKey,omitempty"`
gio3cdee592024-04-17 10:15:56 +0400131}
132
gio7841f4f2024-07-26 19:53:49 +0400133type InfraNetwork struct {
134 Name string `json:"name,omitempty"`
135 IngressClass string `json:"ingressClass,omitempty"`
136 CertificateIssuer string `json:"certificateIssuer,omitempty"`
137 AllocatePortAddr string `json:"allocatePortAddr,omitempty"`
138 ReservePortAddr string `json:"reservePortAddr,omitempty"`
139 DeallocatePortAddr string `json:"deallocatePortAddr,omitempty"`
140}
141
gio3cdee592024-04-17 10:15:56 +0400142type InfraApp interface {
143 App
gio7841f4f2024-07-26 19:53:49 +0400144 Render(release Release, infra InfraConfig, networks []InfraNetwork, values map[string]any, charts map[string]helmv2.HelmChartTemplateSpec) (InfraAppRendered, error)
gioe72b54f2024-04-22 10:44:41 +0400145}
146
147type EnvNetwork struct {
148 DNS net.IP `json:"dns,omitempty"`
149 DNSInClusterIP net.IP `json:"dnsInClusterIP,omitempty"`
150 Ingress net.IP `json:"ingress,omitempty"`
151 Headscale net.IP `json:"headscale,omitempty"`
152 ServicesFrom net.IP `json:"servicesFrom,omitempty"`
153 ServicesTo net.IP `json:"servicesTo,omitempty"`
154}
155
156func NewEnvNetwork(subnet net.IP) (EnvNetwork, error) {
157 addr, err := netip.ParseAddr(subnet.String())
158 if err != nil {
159 return EnvNetwork{}, err
160 }
161 if !addr.Is4() {
162 return EnvNetwork{}, fmt.Errorf("Expected IPv4, got %s instead", addr)
163 }
164 dns := addr.Next()
165 ingress := dns.Next()
166 headscale := ingress.Next()
167 b := addr.AsSlice()
168 if b[3] != 0 {
169 return EnvNetwork{}, fmt.Errorf("Expected last byte to be zero, got %d instead", b[3])
170 }
171 b[3] = 10
172 servicesFrom, ok := netip.AddrFromSlice(b)
173 if !ok {
174 return EnvNetwork{}, fmt.Errorf("Must not reach")
175 }
176 b[3] = 254
177 servicesTo, ok := netip.AddrFromSlice(b)
178 if !ok {
179 return EnvNetwork{}, fmt.Errorf("Must not reach")
180 }
181 b[3] = b[2]
182 b[2] = b[1]
183 b[0] = 10
184 b[1] = 44
185 dnsInClusterIP, ok := netip.AddrFromSlice(b)
186 if !ok {
187 return EnvNetwork{}, fmt.Errorf("Must not reach")
188 }
189 return EnvNetwork{
190 DNS: net.ParseIP(dns.String()),
191 DNSInClusterIP: net.ParseIP(dnsInClusterIP.String()),
192 Ingress: net.ParseIP(ingress.String()),
193 Headscale: net.ParseIP(headscale.String()),
194 ServicesFrom: net.ParseIP(servicesFrom.String()),
195 ServicesTo: net.ParseIP(servicesTo.String()),
196 }, nil
gio3cdee592024-04-17 10:15:56 +0400197}
198
gioe72b54f2024-04-22 10:44:41 +0400199type EnvConfig struct {
200 Id string `json:"id,omitempty"`
201 InfraName string `json:"pcloudEnvName,omitempty"`
202 Domain string `json:"domain,omitempty"`
203 PrivateDomain string `json:"privateDomain,omitempty"`
204 ContactEmail string `json:"contactEmail,omitempty"`
205 AdminPublicKey string `json:"adminPublicKey,omitempty"`
206 PublicIP []net.IP `json:"publicIP,omitempty"`
207 NameserverIP []net.IP `json:"nameserverIP,omitempty"`
208 NamespacePrefix string `json:"namespacePrefix,omitempty"`
209 Network EnvNetwork `json:"network,omitempty"`
gio3cdee592024-04-17 10:15:56 +0400210}
211
212type EnvApp interface {
213 App
gio36b23b32024-08-25 12:20:54 +0400214 Render(
215 release Release,
216 env EnvConfig,
217 networks []Network,
giof6ad2982024-08-23 17:42:49 +0400218 clusters []Cluster,
gio36b23b32024-08-25 12:20:54 +0400219 values map[string]any,
220 charts map[string]helmv2.HelmChartTemplateSpec,
gio864b4332024-09-05 13:56:47 +0400221 vpnKeyGen VPNAPIClient,
gio36b23b32024-08-25 12:20:54 +0400222 ) (EnvAppRendered, error)
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +0400223}
224
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400225type cueApp struct {
226 name string
227 description string
228 icon template.HTML
229 namespace string
230 schema Schema
gio308105e2024-04-19 13:12:13 +0400231 cfg cue.Value
232 data CueAppData
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400233}
234
gio308105e2024-04-19 13:12:13 +0400235type CueAppData map[string][]byte
236
237func ParseCueAppConfig(data CueAppData) (cue.Value, error) {
238 ctx := cuecontext.New()
239 buildCtx := build.NewContext()
240 cfg := &load.Config{
241 Context: buildCtx,
242 Overlay: map[string]load.Source{},
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400243 }
gio308105e2024-04-19 13:12:13 +0400244 names := make([]string, 0)
245 for n, b := range data {
246 a := fmt.Sprintf("/%s", n)
247 names = append(names, a)
248 cfg.Overlay[a] = load.FromString("package main\n\n" + string(b))
249 }
250 instances := load.Instances(names, cfg)
251 for _, inst := range instances {
252 if inst.Err != nil {
253 return cue.Value{}, inst.Err
254 }
255 }
256 if len(instances) != 1 {
257 return cue.Value{}, fmt.Errorf("invalid")
258 }
259 ret := ctx.BuildInstance(instances[0])
gioc81a8472024-09-24 13:06:19 +0200260 if err := ret.Err(); err != nil {
261 return cue.Value{}, err
gio308105e2024-04-19 13:12:13 +0400262 }
263 if err := ret.Validate(); err != nil {
264 return cue.Value{}, err
265 }
266 return ret, nil
267}
268
269func newCueApp(config cue.Value, data CueAppData) (cueApp, error) {
gio3cdee592024-04-17 10:15:56 +0400270 cfg := struct {
271 Name string `json:"name"`
272 Namespace string `json:"namespace"`
273 Description string `json:"description"`
274 Icon string `json:"icon"`
275 }{}
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400276 if err := config.Decode(&cfg); err != nil {
277 return cueApp{}, err
278 }
gio44f621b2024-04-29 09:44:38 +0400279 schema, err := NewCueSchema("input", config.LookupPath(cue.ParsePath("input")))
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400280 if err != nil {
281 return cueApp{}, err
282 }
283 return cueApp{
284 name: cfg.Name,
285 description: cfg.Description,
286 icon: template.HTML(cfg.Icon),
287 namespace: cfg.Namespace,
288 schema: schema,
289 cfg: config,
gio308105e2024-04-19 13:12:13 +0400290 data: data,
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400291 }, nil
292}
293
gio308105e2024-04-19 13:12:13 +0400294func ParseAndCreateNewCueApp(data CueAppData) (cueApp, error) {
295 config, err := ParseCueAppConfig(data)
296 if err != nil {
297 return cueApp{}, err
298 }
299 return newCueApp(config, data)
300}
301
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400302func (a cueApp) Name() string {
303 return a.name
304}
305
gio44f621b2024-04-29 09:44:38 +0400306func (a cueApp) Slug() string {
307 return strings.ReplaceAll(strings.ToLower(a.name), " ", "-")
308}
309
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400310func (a cueApp) Description() string {
311 return a.description
312}
313
314func (a cueApp) Icon() template.HTML {
315 return a.icon
316}
317
318func (a cueApp) Schema() Schema {
319 return a.schema
320}
321
gioef01fbb2024-04-12 16:52:59 +0400322func (a cueApp) Namespace() string {
323 return a.namespace
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400324}
325
gioe72b54f2024-04-22 10:44:41 +0400326func (a cueApp) render(values map[string]any) (rendered, error) {
327 ret := rendered{
gio44f621b2024-04-29 09:44:38 +0400328 Name: a.Slug(),
gio308105e2024-04-19 13:12:13 +0400329 Resources: make(CueAppData),
giof8843412024-05-22 16:38:05 +0400330 HelmCharts: HelmCharts{
331 Git: make(map[string]HelmChartGitRepo),
332 },
333 ContainerImages: make(map[string]ContainerImage),
334 Ports: make([]PortForward, 0),
335 Data: a.data,
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400336 }
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400337 var buf bytes.Buffer
gio3cdee592024-04-17 10:15:56 +0400338 if err := json.NewEncoder(&buf).Encode(values); err != nil {
gioe72b54f2024-04-22 10:44:41 +0400339 return rendered{}, err
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400340 }
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400341 ctx := a.cfg.Context()
342 d := ctx.CompileBytes(buf.Bytes())
343 res := a.cfg.Unify(d).Eval()
344 if err := res.Err(); err != nil {
gioe72b54f2024-04-22 10:44:41 +0400345 return rendered{}, err
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400346 }
347 if err := res.Validate(); err != nil {
gioe72b54f2024-04-22 10:44:41 +0400348 return rendered{}, err
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400349 }
gio308105e2024-04-19 13:12:13 +0400350 full, err := json.MarshalIndent(res, "", "\t")
351 if err != nil {
gioe72b54f2024-04-22 10:44:41 +0400352 return rendered{}, err
gio308105e2024-04-19 13:12:13 +0400353 }
gio94904702024-07-26 16:58:34 +0400354 ret.Raw = full
gio308105e2024-04-19 13:12:13 +0400355 ret.Data["rendered.json"] = full
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400356 readme, err := res.LookupPath(cue.ParsePath("readme")).String()
357 if err != nil {
gioe72b54f2024-04-22 10:44:41 +0400358 return rendered{}, err
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400359 }
360 ret.Readme = readme
giof6ad2982024-08-23 17:42:49 +0400361 res.LookupPath(cue.ParsePath("input.cluster.name")).Decode(&ret.Cluster)
362 if err := res.LookupPath(cue.ParsePath("output.clusterProxy")).Decode(&ret.ClusterProxies); err != nil {
363 return rendered{}, err
364 }
365 if err := res.LookupPath(cue.ParsePath("namespaces")).Decode(&ret.Namespaces); err != nil {
366 return rendered{}, err
367 }
gio802311e2024-11-04 08:37:34 +0400368 if err := res.LookupPath(cue.ParsePath("output.openPort")).Decode(&ret.Ports); err != nil {
gioe72b54f2024-04-22 10:44:41 +0400369 return rendered{}, err
Giorgi Lekveishvilib59b7c22024-04-03 22:17:50 +0400370 }
gio0eaf2712024-04-14 13:08:46 +0400371 {
gio7fbd4ad2024-08-27 10:06:39 +0400372 charts := res.LookupPath(cue.ParsePath("output.charts"))
giof8843412024-05-22 16:38:05 +0400373 i, err := charts.Fields()
374 if err != nil {
375 return rendered{}, err
376 }
377 for i.Next() {
378 var chartRef helmChartRef
379 if err := i.Value().Decode(&chartRef); err != nil {
380 return rendered{}, err
381 }
382 if chartRef.Kind == "GitRepository" {
383 var chart HelmChartGitRepo
384 if err := i.Value().Decode(&chart); err != nil {
385 return rendered{}, err
386 }
387 ret.HelmCharts.Git[cleanName(i.Selector().String())] = chart
388 }
389 }
390 }
391 {
gio7fbd4ad2024-08-27 10:06:39 +0400392 images := res.LookupPath(cue.ParsePath("output.images"))
giof8843412024-05-22 16:38:05 +0400393 i, err := images.Fields()
394 if err != nil {
395 return rendered{}, err
396 }
397 for i.Next() {
398 var img ContainerImage
399 if err := i.Value().Decode(&img); err != nil {
400 return rendered{}, err
401 }
402 ret.ContainerImages[cleanName(i.Selector().String())] = img
403 }
404 }
405 {
gio7fbd4ad2024-08-27 10:06:39 +0400406 helm := res.LookupPath(cue.ParsePath("output.helm"))
407 i, err := helm.Fields()
gio0eaf2712024-04-14 13:08:46 +0400408 if err != nil {
gioe72b54f2024-04-22 10:44:41 +0400409 return rendered{}, err
gio0eaf2712024-04-14 13:08:46 +0400410 }
411 for i.Next() {
412 if contents, err := cueyaml.Encode(i.Value()); err != nil {
413 return rendered{}, err
414 } else {
415 name := fmt.Sprintf("%s.yaml", cleanName(i.Selector().String()))
416 ret.Resources[name] = contents
417 }
418 }
419 }
420 {
421 resources := res.LookupPath(cue.ParsePath("resources"))
422 i, err := resources.Fields()
423 if err != nil {
424 return rendered{}, err
425 }
426 for i.Next() {
427 if contents, err := cueyaml.Encode(i.Value()); err != nil {
428 return rendered{}, err
429 } else {
430 name := fmt.Sprintf("%s.yaml", cleanName(i.Selector().String()))
431 ret.Resources[name] = contents
432 }
Giorgi Lekveishvili3f697b12024-01-04 00:56:06 +0400433 }
Giorgi Lekveishvili3f697b12024-01-04 00:56:06 +0400434 }
Davit Tabidze56f86a42024-04-09 19:15:25 +0400435 helpValue := res.LookupPath(cue.ParsePath("help"))
436 if helpValue.Exists() {
437 if err := helpValue.Decode(&ret.Help); err != nil {
gioe72b54f2024-04-22 10:44:41 +0400438 return rendered{}, err
Davit Tabidze56f86a42024-04-09 19:15:25 +0400439 }
440 }
441 url, err := res.LookupPath(cue.ParsePath("url")).String()
442 if err != nil {
gioe72b54f2024-04-22 10:44:41 +0400443 return rendered{}, err
Davit Tabidze56f86a42024-04-09 19:15:25 +0400444 }
gio09a3e5b2024-04-26 14:11:06 +0400445 ret.URL = url
Davit Tabidze56f86a42024-04-09 19:15:25 +0400446 icon, err := res.LookupPath(cue.ParsePath("icon")).String()
447 if err != nil {
gioe72b54f2024-04-22 10:44:41 +0400448 return rendered{}, err
Davit Tabidze56f86a42024-04-09 19:15:25 +0400449 }
450 ret.Icon = icon
Giorgi Lekveishvili3f697b12024-01-04 00:56:06 +0400451 return ret, nil
452}
453
gio3cdee592024-04-17 10:15:56 +0400454type cueEnvApp struct {
455 cueApp
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +0400456}
457
gio308105e2024-04-19 13:12:13 +0400458func NewCueEnvApp(data CueAppData) (EnvApp, error) {
459 app, err := ParseAndCreateNewCueApp(data)
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +0400460 if err != nil {
461 return nil, err
462 }
gio3cdee592024-04-17 10:15:56 +0400463 return cueEnvApp{app}, nil
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +0400464}
465
gio0eaf2712024-04-14 13:08:46 +0400466func NewDodoApp(appCfg []byte) (EnvApp, error) {
467 return NewCueEnvApp(CueAppData{
giofc9c4ea2024-06-26 13:46:53 +0400468 "app.cue": appCfg,
469 "base.cue": []byte(cueBaseConfig),
470 "dodo.cue": dodoAppCue,
471 "env.cue": []byte(cueEnvAppGlobal),
gio0eaf2712024-04-14 13:08:46 +0400472 })
473}
474
gio3cdee592024-04-17 10:15:56 +0400475func (a cueEnvApp) Type() AppType {
476 return AppTypeEnv
477}
478
giofc441e32024-11-11 16:26:14 +0400479func merge(d map[string]any, v map[string]any) map[string]any {
480 ret := map[string]any{}
481 for k, val := range d {
482 if vv, ok := v[k]; ok && vv != nil {
483 if mv, ok := val.(map[string]any); ok {
484 // TODO(gio): check that it is actually map
485 fmt.Println(vv)
486 ret[k] = merge(mv, vv.(map[string]any))
487 } else {
488 ret[k] = vv
489 }
490 } else {
491 ret[k] = val
492 }
493 }
494 for k, v := range v {
495 if v != nil {
496 if _, ok := d[k]; !ok {
497 d[k] = v
498 }
499 }
500 }
501 return ret
502}
503
giocb34ad22024-07-11 08:01:13 +0400504func (a cueEnvApp) Render(
505 release Release,
506 env EnvConfig,
507 networks []Network,
giof6ad2982024-08-23 17:42:49 +0400508 clusters []Cluster,
giocb34ad22024-07-11 08:01:13 +0400509 values map[string]any,
510 charts map[string]helmv2.HelmChartTemplateSpec,
gio864b4332024-09-05 13:56:47 +0400511 vpnKeyGen VPNAPIClient,
giocb34ad22024-07-11 08:01:13 +0400512) (EnvAppRendered, error) {
giofc441e32024-11-11 16:26:14 +0400513 dv, err := ExtractDefaultValues(a.cueApp.cfg.LookupPath(cue.ParsePath("input")))
514 if err != nil {
515 return EnvAppRendered{}, err
516 }
517 mv := merge(dv.(map[string]any), values)
518 derived, err := deriveValues(mv, values, a.Schema(), networks, clusters, vpnKeyGen)
gio3cdee592024-04-17 10:15:56 +0400519 if err != nil {
gioefa0ed42024-06-13 12:31:43 +0400520 return EnvAppRendered{}, err
gio3cdee592024-04-17 10:15:56 +0400521 }
giof6ad2982024-08-23 17:42:49 +0400522 // return EnvAppRendered{}, fmt.Errorf("asdasd")
giof8843412024-05-22 16:38:05 +0400523 if charts == nil {
524 charts = make(map[string]helmv2.HelmChartTemplateSpec)
525 }
giof15b9da2024-09-19 06:59:16 +0400526 if clusters == nil {
527 clusters = []Cluster{}
528 }
gio3cdee592024-04-17 10:15:56 +0400529 ret, err := a.cueApp.render(map[string]any{
giof8843412024-05-22 16:38:05 +0400530 "global": env,
531 "release": release,
532 "input": derived,
533 "localCharts": charts,
gio5e49bb62024-07-20 10:43:19 +0400534 "networks": NetworkMap(networks),
giof15b9da2024-09-19 06:59:16 +0400535 "clusters": clusters,
gio3cdee592024-04-17 10:15:56 +0400536 })
537 if err != nil {
gioe72b54f2024-04-22 10:44:41 +0400538 return EnvAppRendered{}, err
gio3cdee592024-04-17 10:15:56 +0400539 }
gioe72b54f2024-04-22 10:44:41 +0400540 return EnvAppRendered{
541 rendered: ret,
542 Config: AppInstanceConfig{
gio44f621b2024-04-29 09:44:38 +0400543 AppId: a.Slug(),
gioe72b54f2024-04-22 10:44:41 +0400544 Env: env,
545 Release: release,
546 Values: values,
547 Input: derived,
gio09a3e5b2024-04-26 14:11:06 +0400548 URL: ret.URL,
gioe72b54f2024-04-22 10:44:41 +0400549 Help: ret.Help,
gio09a3e5b2024-04-26 14:11:06 +0400550 Icon: ret.Icon,
gioe72b54f2024-04-22 10:44:41 +0400551 },
552 }, nil
gio3cdee592024-04-17 10:15:56 +0400553}
554
555type cueInfraApp struct {
556 cueApp
557}
558
gio308105e2024-04-19 13:12:13 +0400559func NewCueInfraApp(data CueAppData) (InfraApp, error) {
560 app, err := ParseAndCreateNewCueApp(data)
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +0400561 if err != nil {
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400562 return nil, err
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +0400563 }
gio3cdee592024-04-17 10:15:56 +0400564 return cueInfraApp{app}, nil
565}
566
567func (a cueInfraApp) Type() AppType {
568 return AppTypeInfra
569}
570
gio7841f4f2024-07-26 19:53:49 +0400571func (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 +0400572 if charts == nil {
573 charts = make(map[string]helmv2.HelmChartTemplateSpec)
574 }
gioe72b54f2024-04-22 10:44:41 +0400575 ret, err := a.cueApp.render(map[string]any{
giof8843412024-05-22 16:38:05 +0400576 "global": infra,
577 "release": release,
578 "input": values,
579 "localCharts": charts,
gio7841f4f2024-07-26 19:53:49 +0400580 "networks": InfraNetworkMap(networks),
gio3cdee592024-04-17 10:15:56 +0400581 })
gioe72b54f2024-04-22 10:44:41 +0400582 if err != nil {
583 return InfraAppRendered{}, err
584 }
585 return InfraAppRendered{
586 rendered: ret,
587 Config: InfraAppInstanceConfig{
gio44f621b2024-04-29 09:44:38 +0400588 AppId: a.Slug(),
gioe72b54f2024-04-22 10:44:41 +0400589 Infra: infra,
590 Release: release,
591 Values: values,
592 Input: values,
gio09a3e5b2024-04-26 14:11:06 +0400593 URL: ret.URL,
594 Help: ret.Help,
gioe72b54f2024-04-22 10:44:41 +0400595 },
596 }, nil
Giorgi Lekveishvilief21c132024-01-17 18:57:58 +0400597}
598
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400599func cleanName(s string) string {
600 return strings.ReplaceAll(strings.ReplaceAll(s, "\"", ""), "'", "")
Giorgi Lekveishvilief21c132024-01-17 18:57:58 +0400601}
gioe72b54f2024-04-22 10:44:41 +0400602
603func join[T fmt.Stringer](items []T, sep string) string {
604 var tmp []string
605 for _, i := range items {
606 tmp = append(tmp, i.String())
607 }
608 return strings.Join(tmp, ",")
609}
gio0eaf2712024-04-14 13:08:46 +0400610
gio5e49bb62024-07-20 10:43:19 +0400611func NetworkMap(networks []Network) map[string]Network {
gio0eaf2712024-04-14 13:08:46 +0400612 ret := make(map[string]Network)
613 for _, n := range networks {
614 ret[strings.ToLower(n.Name)] = n
615 }
616 return ret
617}
gio7841f4f2024-07-26 19:53:49 +0400618
619func InfraNetworkMap(networks []InfraNetwork) map[string]InfraNetwork {
620 ret := make(map[string]InfraNetwork)
621 for _, n := range networks {
622 ret[strings.ToLower(n.Name)] = n
623 }
624 return ret
625}