blob: d7f69893e98507a8c81ba7c88843797f081764cf [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 {
98 Allocator string `json:"allocator"`
gioefa0ed42024-06-13 12:31:43 +040099 ReserveAddr string `json:"reservator"`
giocdfa3722024-06-13 20:10:14 +0400100 RemoveAddr string `json:"deallocator"`
gio3cdee592024-04-17 10:15:56 +0400101 Protocol string `json:"protocol"`
102 SourcePort int `json:"sourcePort"`
103 TargetService string `json:"targetService"`
104 TargetPort int `json:"targetPort"`
105}
106
107type AppType int
108
109const (
110 AppTypeInfra AppType = iota
111 AppTypeEnv
112)
113
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400114type App interface {
115 Name() string
gio44f621b2024-04-29 09:44:38 +0400116 Type() AppType
117 Slug() string
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400118 Description() string
119 Icon() template.HTML
120 Schema() Schema
gioef01fbb2024-04-12 16:52:59 +0400121 Namespace() string
gio3cdee592024-04-17 10:15:56 +0400122}
123
124type InfraConfig struct {
gioe72b54f2024-04-22 10:44:41 +0400125 Name string `json:"pcloudEnvName,omitempty"` // #TODO(gio): change to name
126 PublicIP []net.IP `json:"publicIP,omitempty"`
127 InfraNamespacePrefix string `json:"namespacePrefix,omitempty"`
128 InfraAdminPublicKey []byte `json:"infraAdminPublicKey,omitempty"`
gio3cdee592024-04-17 10:15:56 +0400129}
130
gio7841f4f2024-07-26 19:53:49 +0400131type InfraNetwork struct {
132 Name string `json:"name,omitempty"`
133 IngressClass string `json:"ingressClass,omitempty"`
134 CertificateIssuer string `json:"certificateIssuer,omitempty"`
135 AllocatePortAddr string `json:"allocatePortAddr,omitempty"`
136 ReservePortAddr string `json:"reservePortAddr,omitempty"`
137 DeallocatePortAddr string `json:"deallocatePortAddr,omitempty"`
138}
139
gio3cdee592024-04-17 10:15:56 +0400140type InfraApp interface {
141 App
gio7841f4f2024-07-26 19:53:49 +0400142 Render(release Release, infra InfraConfig, networks []InfraNetwork, values map[string]any, charts map[string]helmv2.HelmChartTemplateSpec) (InfraAppRendered, error)
gioe72b54f2024-04-22 10:44:41 +0400143}
144
145type EnvNetwork struct {
146 DNS net.IP `json:"dns,omitempty"`
147 DNSInClusterIP net.IP `json:"dnsInClusterIP,omitempty"`
148 Ingress net.IP `json:"ingress,omitempty"`
149 Headscale net.IP `json:"headscale,omitempty"`
150 ServicesFrom net.IP `json:"servicesFrom,omitempty"`
151 ServicesTo net.IP `json:"servicesTo,omitempty"`
152}
153
154func NewEnvNetwork(subnet net.IP) (EnvNetwork, error) {
155 addr, err := netip.ParseAddr(subnet.String())
156 if err != nil {
157 return EnvNetwork{}, err
158 }
159 if !addr.Is4() {
160 return EnvNetwork{}, fmt.Errorf("Expected IPv4, got %s instead", addr)
161 }
162 dns := addr.Next()
163 ingress := dns.Next()
164 headscale := ingress.Next()
165 b := addr.AsSlice()
166 if b[3] != 0 {
167 return EnvNetwork{}, fmt.Errorf("Expected last byte to be zero, got %d instead", b[3])
168 }
169 b[3] = 10
170 servicesFrom, ok := netip.AddrFromSlice(b)
171 if !ok {
172 return EnvNetwork{}, fmt.Errorf("Must not reach")
173 }
174 b[3] = 254
175 servicesTo, ok := netip.AddrFromSlice(b)
176 if !ok {
177 return EnvNetwork{}, fmt.Errorf("Must not reach")
178 }
179 b[3] = b[2]
180 b[2] = b[1]
181 b[0] = 10
182 b[1] = 44
183 dnsInClusterIP, ok := netip.AddrFromSlice(b)
184 if !ok {
185 return EnvNetwork{}, fmt.Errorf("Must not reach")
186 }
187 return EnvNetwork{
188 DNS: net.ParseIP(dns.String()),
189 DNSInClusterIP: net.ParseIP(dnsInClusterIP.String()),
190 Ingress: net.ParseIP(ingress.String()),
191 Headscale: net.ParseIP(headscale.String()),
192 ServicesFrom: net.ParseIP(servicesFrom.String()),
193 ServicesTo: net.ParseIP(servicesTo.String()),
194 }, nil
gio3cdee592024-04-17 10:15:56 +0400195}
196
gioe72b54f2024-04-22 10:44:41 +0400197type EnvConfig struct {
198 Id string `json:"id,omitempty"`
199 InfraName string `json:"pcloudEnvName,omitempty"`
200 Domain string `json:"domain,omitempty"`
201 PrivateDomain string `json:"privateDomain,omitempty"`
202 ContactEmail string `json:"contactEmail,omitempty"`
203 AdminPublicKey string `json:"adminPublicKey,omitempty"`
204 PublicIP []net.IP `json:"publicIP,omitempty"`
205 NameserverIP []net.IP `json:"nameserverIP,omitempty"`
206 NamespacePrefix string `json:"namespacePrefix,omitempty"`
207 Network EnvNetwork `json:"network,omitempty"`
gio3cdee592024-04-17 10:15:56 +0400208}
209
210type EnvApp interface {
211 App
gio36b23b32024-08-25 12:20:54 +0400212 Render(
213 release Release,
214 env EnvConfig,
215 networks []Network,
giof6ad2982024-08-23 17:42:49 +0400216 clusters []Cluster,
gio36b23b32024-08-25 12:20:54 +0400217 values map[string]any,
218 charts map[string]helmv2.HelmChartTemplateSpec,
gio864b4332024-09-05 13:56:47 +0400219 vpnKeyGen VPNAPIClient,
gio36b23b32024-08-25 12:20:54 +0400220 ) (EnvAppRendered, error)
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +0400221}
222
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400223type cueApp struct {
224 name string
225 description string
226 icon template.HTML
227 namespace string
228 schema Schema
gio308105e2024-04-19 13:12:13 +0400229 cfg cue.Value
230 data CueAppData
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400231}
232
gio308105e2024-04-19 13:12:13 +0400233type CueAppData map[string][]byte
234
235func ParseCueAppConfig(data CueAppData) (cue.Value, error) {
236 ctx := cuecontext.New()
237 buildCtx := build.NewContext()
238 cfg := &load.Config{
239 Context: buildCtx,
240 Overlay: map[string]load.Source{},
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400241 }
gio308105e2024-04-19 13:12:13 +0400242 names := make([]string, 0)
243 for n, b := range data {
244 a := fmt.Sprintf("/%s", n)
245 names = append(names, a)
246 cfg.Overlay[a] = load.FromString("package main\n\n" + string(b))
247 }
248 instances := load.Instances(names, cfg)
249 for _, inst := range instances {
250 if inst.Err != nil {
251 return cue.Value{}, inst.Err
252 }
253 }
254 if len(instances) != 1 {
255 return cue.Value{}, fmt.Errorf("invalid")
256 }
257 ret := ctx.BuildInstance(instances[0])
258 if ret.Err() != nil {
259 return cue.Value{}, ret.Err()
260 }
261 if err := ret.Validate(); err != nil {
262 return cue.Value{}, err
263 }
264 return ret, nil
265}
266
267func newCueApp(config cue.Value, data CueAppData) (cueApp, error) {
gio3cdee592024-04-17 10:15:56 +0400268 cfg := struct {
269 Name string `json:"name"`
270 Namespace string `json:"namespace"`
271 Description string `json:"description"`
272 Icon string `json:"icon"`
273 }{}
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400274 if err := config.Decode(&cfg); err != nil {
275 return cueApp{}, err
276 }
gio44f621b2024-04-29 09:44:38 +0400277 schema, err := NewCueSchema("input", config.LookupPath(cue.ParsePath("input")))
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400278 if err != nil {
279 return cueApp{}, err
280 }
281 return cueApp{
282 name: cfg.Name,
283 description: cfg.Description,
284 icon: template.HTML(cfg.Icon),
285 namespace: cfg.Namespace,
286 schema: schema,
287 cfg: config,
gio308105e2024-04-19 13:12:13 +0400288 data: data,
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400289 }, nil
290}
291
gio308105e2024-04-19 13:12:13 +0400292func ParseAndCreateNewCueApp(data CueAppData) (cueApp, error) {
293 config, err := ParseCueAppConfig(data)
294 if err != nil {
295 return cueApp{}, err
296 }
297 return newCueApp(config, data)
298}
299
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400300func (a cueApp) Name() string {
301 return a.name
302}
303
gio44f621b2024-04-29 09:44:38 +0400304func (a cueApp) Slug() string {
305 return strings.ReplaceAll(strings.ToLower(a.name), " ", "-")
306}
307
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400308func (a cueApp) Description() string {
309 return a.description
310}
311
312func (a cueApp) Icon() template.HTML {
313 return a.icon
314}
315
316func (a cueApp) Schema() Schema {
317 return a.schema
318}
319
gioef01fbb2024-04-12 16:52:59 +0400320func (a cueApp) Namespace() string {
321 return a.namespace
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400322}
323
gioe72b54f2024-04-22 10:44:41 +0400324func (a cueApp) render(values map[string]any) (rendered, error) {
325 ret := rendered{
gio44f621b2024-04-29 09:44:38 +0400326 Name: a.Slug(),
gio308105e2024-04-19 13:12:13 +0400327 Resources: make(CueAppData),
giof8843412024-05-22 16:38:05 +0400328 HelmCharts: HelmCharts{
329 Git: make(map[string]HelmChartGitRepo),
330 },
331 ContainerImages: make(map[string]ContainerImage),
332 Ports: make([]PortForward, 0),
333 Data: a.data,
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400334 }
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400335 var buf bytes.Buffer
gio3cdee592024-04-17 10:15:56 +0400336 if err := json.NewEncoder(&buf).Encode(values); err != nil {
gioe72b54f2024-04-22 10:44:41 +0400337 return rendered{}, err
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +0400338 }
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400339 ctx := a.cfg.Context()
340 d := ctx.CompileBytes(buf.Bytes())
341 res := a.cfg.Unify(d).Eval()
342 if err := res.Err(); err != nil {
gioe72b54f2024-04-22 10:44:41 +0400343 return rendered{}, err
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400344 }
345 if err := res.Validate(); err != nil {
gioe72b54f2024-04-22 10:44:41 +0400346 return rendered{}, err
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400347 }
gio308105e2024-04-19 13:12:13 +0400348 full, err := json.MarshalIndent(res, "", "\t")
349 if err != nil {
gioe72b54f2024-04-22 10:44:41 +0400350 return rendered{}, err
gio308105e2024-04-19 13:12:13 +0400351 }
gio94904702024-07-26 16:58:34 +0400352 ret.Raw = full
gio308105e2024-04-19 13:12:13 +0400353 ret.Data["rendered.json"] = full
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400354 readme, err := res.LookupPath(cue.ParsePath("readme")).String()
355 if err != nil {
gioe72b54f2024-04-22 10:44:41 +0400356 return rendered{}, err
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400357 }
358 ret.Readme = readme
giof6ad2982024-08-23 17:42:49 +0400359 res.LookupPath(cue.ParsePath("input.cluster.name")).Decode(&ret.Cluster)
360 if err := res.LookupPath(cue.ParsePath("output.clusterProxy")).Decode(&ret.ClusterProxies); err != nil {
361 return rendered{}, err
362 }
363 if err := res.LookupPath(cue.ParsePath("namespaces")).Decode(&ret.Namespaces); err != nil {
364 return rendered{}, err
365 }
Giorgi Lekveishvilib59b7c22024-04-03 22:17:50 +0400366 if err := res.LookupPath(cue.ParsePath("portForward")).Decode(&ret.Ports); err != nil {
gioe72b54f2024-04-22 10:44:41 +0400367 return rendered{}, err
Giorgi Lekveishvilib59b7c22024-04-03 22:17:50 +0400368 }
gio0eaf2712024-04-14 13:08:46 +0400369 {
gio7fbd4ad2024-08-27 10:06:39 +0400370 charts := res.LookupPath(cue.ParsePath("output.charts"))
giof8843412024-05-22 16:38:05 +0400371 i, err := charts.Fields()
372 if err != nil {
373 return rendered{}, err
374 }
375 for i.Next() {
376 var chartRef helmChartRef
377 if err := i.Value().Decode(&chartRef); err != nil {
378 return rendered{}, err
379 }
380 if chartRef.Kind == "GitRepository" {
381 var chart HelmChartGitRepo
382 if err := i.Value().Decode(&chart); err != nil {
383 return rendered{}, err
384 }
385 ret.HelmCharts.Git[cleanName(i.Selector().String())] = chart
386 }
387 }
388 }
389 {
gio7fbd4ad2024-08-27 10:06:39 +0400390 images := res.LookupPath(cue.ParsePath("output.images"))
giof8843412024-05-22 16:38:05 +0400391 i, err := images.Fields()
392 if err != nil {
393 return rendered{}, err
394 }
395 for i.Next() {
396 var img ContainerImage
397 if err := i.Value().Decode(&img); err != nil {
398 return rendered{}, err
399 }
400 ret.ContainerImages[cleanName(i.Selector().String())] = img
401 }
402 }
403 {
gio7fbd4ad2024-08-27 10:06:39 +0400404 helm := res.LookupPath(cue.ParsePath("output.helm"))
405 i, err := helm.Fields()
gio0eaf2712024-04-14 13:08:46 +0400406 if err != nil {
gioe72b54f2024-04-22 10:44:41 +0400407 return rendered{}, err
gio0eaf2712024-04-14 13:08:46 +0400408 }
409 for i.Next() {
410 if contents, err := cueyaml.Encode(i.Value()); err != nil {
411 return rendered{}, err
412 } else {
413 name := fmt.Sprintf("%s.yaml", cleanName(i.Selector().String()))
414 ret.Resources[name] = contents
415 }
416 }
417 }
418 {
419 resources := res.LookupPath(cue.ParsePath("resources"))
420 i, err := resources.Fields()
421 if err != nil {
422 return rendered{}, err
423 }
424 for i.Next() {
425 if contents, err := cueyaml.Encode(i.Value()); err != nil {
426 return rendered{}, err
427 } else {
428 name := fmt.Sprintf("%s.yaml", cleanName(i.Selector().String()))
429 ret.Resources[name] = contents
430 }
Giorgi Lekveishvili3f697b12024-01-04 00:56:06 +0400431 }
Giorgi Lekveishvili3f697b12024-01-04 00:56:06 +0400432 }
Davit Tabidze56f86a42024-04-09 19:15:25 +0400433 helpValue := res.LookupPath(cue.ParsePath("help"))
434 if helpValue.Exists() {
435 if err := helpValue.Decode(&ret.Help); err != nil {
gioe72b54f2024-04-22 10:44:41 +0400436 return rendered{}, err
Davit Tabidze56f86a42024-04-09 19:15:25 +0400437 }
438 }
439 url, err := res.LookupPath(cue.ParsePath("url")).String()
440 if err != nil {
gioe72b54f2024-04-22 10:44:41 +0400441 return rendered{}, err
Davit Tabidze56f86a42024-04-09 19:15:25 +0400442 }
gio09a3e5b2024-04-26 14:11:06 +0400443 ret.URL = url
Davit Tabidze56f86a42024-04-09 19:15:25 +0400444 icon, err := res.LookupPath(cue.ParsePath("icon")).String()
445 if err != nil {
gioe72b54f2024-04-22 10:44:41 +0400446 return rendered{}, err
Davit Tabidze56f86a42024-04-09 19:15:25 +0400447 }
448 ret.Icon = icon
Giorgi Lekveishvili3f697b12024-01-04 00:56:06 +0400449 return ret, nil
450}
451
gio3cdee592024-04-17 10:15:56 +0400452type cueEnvApp struct {
453 cueApp
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +0400454}
455
gio308105e2024-04-19 13:12:13 +0400456func NewCueEnvApp(data CueAppData) (EnvApp, error) {
457 app, err := ParseAndCreateNewCueApp(data)
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +0400458 if err != nil {
459 return nil, err
460 }
gio3cdee592024-04-17 10:15:56 +0400461 return cueEnvApp{app}, nil
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +0400462}
463
gio0eaf2712024-04-14 13:08:46 +0400464func NewDodoApp(appCfg []byte) (EnvApp, error) {
465 return NewCueEnvApp(CueAppData{
giofc9c4ea2024-06-26 13:46:53 +0400466 "app.cue": appCfg,
467 "base.cue": []byte(cueBaseConfig),
468 "dodo.cue": dodoAppCue,
469 "env.cue": []byte(cueEnvAppGlobal),
gio0eaf2712024-04-14 13:08:46 +0400470 })
471}
472
gio3cdee592024-04-17 10:15:56 +0400473func (a cueEnvApp) Type() AppType {
474 return AppTypeEnv
475}
476
giocb34ad22024-07-11 08:01:13 +0400477func (a cueEnvApp) Render(
478 release Release,
479 env EnvConfig,
480 networks []Network,
giof6ad2982024-08-23 17:42:49 +0400481 clusters []Cluster,
giocb34ad22024-07-11 08:01:13 +0400482 values map[string]any,
483 charts map[string]helmv2.HelmChartTemplateSpec,
gio864b4332024-09-05 13:56:47 +0400484 vpnKeyGen VPNAPIClient,
giocb34ad22024-07-11 08:01:13 +0400485) (EnvAppRendered, error) {
giof6ad2982024-08-23 17:42:49 +0400486 derived, err := deriveValues(values, values, a.Schema(), networks, clusters, vpnKeyGen)
gio3cdee592024-04-17 10:15:56 +0400487 if err != nil {
gioefa0ed42024-06-13 12:31:43 +0400488 return EnvAppRendered{}, err
gio3cdee592024-04-17 10:15:56 +0400489 }
giof6ad2982024-08-23 17:42:49 +0400490 // return EnvAppRendered{}, fmt.Errorf("asdasd")
giof8843412024-05-22 16:38:05 +0400491 if charts == nil {
492 charts = make(map[string]helmv2.HelmChartTemplateSpec)
493 }
giof15b9da2024-09-19 06:59:16 +0400494 if clusters == nil {
495 clusters = []Cluster{}
496 }
gio3cdee592024-04-17 10:15:56 +0400497 ret, err := a.cueApp.render(map[string]any{
giof8843412024-05-22 16:38:05 +0400498 "global": env,
499 "release": release,
500 "input": derived,
501 "localCharts": charts,
gio5e49bb62024-07-20 10:43:19 +0400502 "networks": NetworkMap(networks),
giof15b9da2024-09-19 06:59:16 +0400503 "clusters": clusters,
gio3cdee592024-04-17 10:15:56 +0400504 })
505 if err != nil {
gioe72b54f2024-04-22 10:44:41 +0400506 return EnvAppRendered{}, err
gio3cdee592024-04-17 10:15:56 +0400507 }
gioe72b54f2024-04-22 10:44:41 +0400508 return EnvAppRendered{
509 rendered: ret,
510 Config: AppInstanceConfig{
gio44f621b2024-04-29 09:44:38 +0400511 AppId: a.Slug(),
gioe72b54f2024-04-22 10:44:41 +0400512 Env: env,
513 Release: release,
514 Values: values,
515 Input: derived,
gio09a3e5b2024-04-26 14:11:06 +0400516 URL: ret.URL,
gioe72b54f2024-04-22 10:44:41 +0400517 Help: ret.Help,
gio09a3e5b2024-04-26 14:11:06 +0400518 Icon: ret.Icon,
gioe72b54f2024-04-22 10:44:41 +0400519 },
520 }, nil
gio3cdee592024-04-17 10:15:56 +0400521}
522
523type cueInfraApp struct {
524 cueApp
525}
526
gio308105e2024-04-19 13:12:13 +0400527func NewCueInfraApp(data CueAppData) (InfraApp, error) {
528 app, err := ParseAndCreateNewCueApp(data)
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +0400529 if err != nil {
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400530 return nil, err
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +0400531 }
gio3cdee592024-04-17 10:15:56 +0400532 return cueInfraApp{app}, nil
533}
534
535func (a cueInfraApp) Type() AppType {
536 return AppTypeInfra
537}
538
gio7841f4f2024-07-26 19:53:49 +0400539func (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 +0400540 if charts == nil {
541 charts = make(map[string]helmv2.HelmChartTemplateSpec)
542 }
gioe72b54f2024-04-22 10:44:41 +0400543 ret, err := a.cueApp.render(map[string]any{
giof8843412024-05-22 16:38:05 +0400544 "global": infra,
545 "release": release,
546 "input": values,
547 "localCharts": charts,
gio7841f4f2024-07-26 19:53:49 +0400548 "networks": InfraNetworkMap(networks),
gio3cdee592024-04-17 10:15:56 +0400549 })
gioe72b54f2024-04-22 10:44:41 +0400550 if err != nil {
551 return InfraAppRendered{}, err
552 }
553 return InfraAppRendered{
554 rendered: ret,
555 Config: InfraAppInstanceConfig{
gio44f621b2024-04-29 09:44:38 +0400556 AppId: a.Slug(),
gioe72b54f2024-04-22 10:44:41 +0400557 Infra: infra,
558 Release: release,
559 Values: values,
560 Input: values,
gio09a3e5b2024-04-26 14:11:06 +0400561 URL: ret.URL,
562 Help: ret.Help,
gioe72b54f2024-04-22 10:44:41 +0400563 },
564 }, nil
Giorgi Lekveishvilief21c132024-01-17 18:57:58 +0400565}
566
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400567func cleanName(s string) string {
568 return strings.ReplaceAll(strings.ReplaceAll(s, "\"", ""), "'", "")
Giorgi Lekveishvilief21c132024-01-17 18:57:58 +0400569}
gioe72b54f2024-04-22 10:44:41 +0400570
571func join[T fmt.Stringer](items []T, sep string) string {
572 var tmp []string
573 for _, i := range items {
574 tmp = append(tmp, i.String())
575 }
576 return strings.Join(tmp, ",")
577}
gio0eaf2712024-04-14 13:08:46 +0400578
gio5e49bb62024-07-20 10:43:19 +0400579func NetworkMap(networks []Network) map[string]Network {
gio0eaf2712024-04-14 13:08:46 +0400580 ret := make(map[string]Network)
581 for _, n := range networks {
582 ret[strings.ToLower(n.Name)] = n
583 }
584 return ret
585}
gio7841f4f2024-07-26 19:53:49 +0400586
587func InfraNetworkMap(networks []InfraNetwork) map[string]InfraNetwork {
588 ret := make(map[string]InfraNetwork)
589 for _, n := range networks {
590 ret[strings.ToLower(n.Name)] = n
591 }
592 return ret
593}