blob: 56129f5ba0174c4b98d8f32cfff63aaa4d98b972 [file] [log] [blame]
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +04001package installer
2
3import (
gio3af43942024-04-16 08:13:50 +04004 "bytes"
5 "encoding/json"
6 "errors"
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +04007 "fmt"
gio3af43942024-04-16 08:13:50 +04008 "io/fs"
gio3af43942024-04-16 08:13:50 +04009 "net/http"
10 "path"
Giorgi Lekveishvili6e813182023-06-30 13:45:30 +040011 "path/filepath"
gioe72b54f2024-04-22 10:44:41 +040012
13 "github.com/giolekva/pcloud/core/installer/io"
14 "github.com/giolekva/pcloud/core/installer/soft"
gio778577f2024-04-29 09:44:38 +040015
16 "sigs.k8s.io/yaml"
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040017)
18
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040019const configFileName = "config.yaml"
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040020const kustomizationFileName = "kustomization.yaml"
21
gio778577f2024-04-29 09:44:38 +040022var ErrorNotFound = errors.New("not found")
23
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040024type AppManager struct {
gioe72b54f2024-04-22 10:44:41 +040025 repoIO soft.RepoIO
gio308105e2024-04-19 13:12:13 +040026 nsCreator NamespaceCreator
27 appDirRoot string
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +040028}
29
gioe72b54f2024-04-22 10:44:41 +040030func NewAppManager(repoIO soft.RepoIO, nsCreator NamespaceCreator, appDirRoot string) (*AppManager, error) {
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040031 return &AppManager{
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +040032 repoIO,
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +040033 nsCreator,
gio308105e2024-04-19 13:12:13 +040034 appDirRoot,
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040035 }, nil
36}
37
gioe72b54f2024-04-22 10:44:41 +040038func (m *AppManager) Config() (EnvConfig, error) {
39 var cfg EnvConfig
40 if err := soft.ReadYaml(m.repoIO, configFileName, &cfg); err != nil {
41 return EnvConfig{}, err
gio3af43942024-04-16 08:13:50 +040042 } else {
43 return cfg, nil
44 }
45}
46
gio3cdee592024-04-17 10:15:56 +040047func (m *AppManager) appConfig(path string) (AppInstanceConfig, error) {
48 var cfg AppInstanceConfig
gioe72b54f2024-04-22 10:44:41 +040049 if err := soft.ReadJson(m.repoIO, path, &cfg); err != nil {
gio3cdee592024-04-17 10:15:56 +040050 return AppInstanceConfig{}, err
gio3af43942024-04-16 08:13:50 +040051 } else {
52 return cfg, nil
53 }
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +040054}
55
gio308105e2024-04-19 13:12:13 +040056func (m *AppManager) FindAllInstances() ([]AppInstanceConfig, error) {
gio09a3e5b2024-04-26 14:11:06 +040057 m.repoIO.Pull()
gioe72b54f2024-04-22 10:44:41 +040058 kust, err := soft.ReadKustomization(m.repoIO, filepath.Join(m.appDirRoot, "kustomization.yaml"))
gio3af43942024-04-16 08:13:50 +040059 if err != nil {
60 return nil, err
61 }
gio3cdee592024-04-17 10:15:56 +040062 ret := make([]AppInstanceConfig, 0)
gio3af43942024-04-16 08:13:50 +040063 for _, app := range kust.Resources {
gio308105e2024-04-19 13:12:13 +040064 cfg, err := m.appConfig(filepath.Join(m.appDirRoot, app, "config.json"))
65 if err != nil {
66 return nil, err
67 }
68 cfg.Id = app
69 ret = append(ret, cfg)
70 }
71 return ret, nil
72}
73
74func (m *AppManager) FindAllAppInstances(name string) ([]AppInstanceConfig, error) {
gioe72b54f2024-04-22 10:44:41 +040075 kust, err := soft.ReadKustomization(m.repoIO, filepath.Join(m.appDirRoot, "kustomization.yaml"))
gio308105e2024-04-19 13:12:13 +040076 if err != nil {
77 return nil, err
78 }
79 ret := make([]AppInstanceConfig, 0)
80 for _, app := range kust.Resources {
81 cfg, err := m.appConfig(filepath.Join(m.appDirRoot, app, "config.json"))
gio3af43942024-04-16 08:13:50 +040082 if err != nil {
83 return nil, err
84 }
85 cfg.Id = app
86 if cfg.AppId == name {
87 ret = append(ret, cfg)
88 }
89 }
90 return ret, nil
Giorgi Lekveishvili76951482023-06-30 23:25:09 +040091}
92
gio778577f2024-04-29 09:44:38 +040093func (m *AppManager) FindInstance(id string) (*AppInstanceConfig, error) {
gioe72b54f2024-04-22 10:44:41 +040094 kust, err := soft.ReadKustomization(m.repoIO, filepath.Join(m.appDirRoot, "kustomization.yaml"))
gio3af43942024-04-16 08:13:50 +040095 if err != nil {
gio778577f2024-04-29 09:44:38 +040096 return nil, err
gio3af43942024-04-16 08:13:50 +040097 }
98 for _, app := range kust.Resources {
99 if app == id {
gio308105e2024-04-19 13:12:13 +0400100 cfg, err := m.appConfig(filepath.Join(m.appDirRoot, app, "config.json"))
gio3af43942024-04-16 08:13:50 +0400101 if err != nil {
gio778577f2024-04-29 09:44:38 +0400102 return nil, err
gio3af43942024-04-16 08:13:50 +0400103 }
104 cfg.Id = id
gio778577f2024-04-29 09:44:38 +0400105 return &cfg, nil
gio3af43942024-04-16 08:13:50 +0400106 }
107 }
gio778577f2024-04-29 09:44:38 +0400108 return nil, ErrorNotFound
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400109}
110
gio3cdee592024-04-17 10:15:56 +0400111func (m *AppManager) AppConfig(name string) (AppInstanceConfig, error) {
gio3cdee592024-04-17 10:15:56 +0400112 var cfg AppInstanceConfig
gioe72b54f2024-04-22 10:44:41 +0400113 if err := soft.ReadJson(m.repoIO, filepath.Join(m.appDirRoot, name, "config.json"), &cfg); err != nil {
gio3cdee592024-04-17 10:15:56 +0400114 return AppInstanceConfig{}, err
Giorgi Lekveishvili03ee5852023-05-30 13:20:10 +0400115 }
gio308105e2024-04-19 13:12:13 +0400116 return cfg, nil
Giorgi Lekveishvili03ee5852023-05-30 13:20:10 +0400117}
118
gio3af43942024-04-16 08:13:50 +0400119type allocatePortReq struct {
120 Protocol string `json:"protocol"`
121 SourcePort int `json:"sourcePort"`
122 TargetService string `json:"targetService"`
123 TargetPort int `json:"targetPort"`
124}
125
126func openPorts(ports []PortForward) error {
127 for _, p := range ports {
128 var buf bytes.Buffer
129 req := allocatePortReq{
130 Protocol: p.Protocol,
131 SourcePort: p.SourcePort,
132 TargetService: p.TargetService,
133 TargetPort: p.TargetPort,
134 }
135 if err := json.NewEncoder(&buf).Encode(req); err != nil {
136 return err
137 }
138 resp, err := http.Post(p.Allocator, "application/json", &buf)
139 if err != nil {
140 return err
141 }
142 if resp.StatusCode != http.StatusOK {
143 return fmt.Errorf("Could not allocate port %d, status code: %d", p.SourcePort, resp.StatusCode)
144 }
145 }
146 return nil
147}
148
gioe72b54f2024-04-22 10:44:41 +0400149func createKustomizationChain(r soft.RepoFS, path string) error {
gio3af43942024-04-16 08:13:50 +0400150 for p := filepath.Clean(path); p != "/"; {
151 parent, child := filepath.Split(p)
152 kustPath := filepath.Join(parent, "kustomization.yaml")
gioe72b54f2024-04-22 10:44:41 +0400153 kust, err := soft.ReadKustomization(r, kustPath)
gio3af43942024-04-16 08:13:50 +0400154 if err != nil {
155 if errors.Is(err, fs.ErrNotExist) {
gioe72b54f2024-04-22 10:44:41 +0400156 k := io.NewKustomization()
gio3af43942024-04-16 08:13:50 +0400157 kust = &k
158 } else {
159 return err
160 }
161 }
162 kust.AddResources(child)
gioe72b54f2024-04-22 10:44:41 +0400163 if err := soft.WriteYaml(r, kustPath, kust); err != nil {
gio3af43942024-04-16 08:13:50 +0400164 return err
165 }
166 p = filepath.Clean(parent)
167 }
168 return nil
169}
170
gio778577f2024-04-29 09:44:38 +0400171type Resource struct {
172 Name string `json:"name"`
173 Namespace string `json:"namespace"`
174}
175
176type ReleaseResources struct {
177 Helm []Resource
178}
179
gio3cdee592024-04-17 10:15:56 +0400180// TODO(gio): rename to CommitApp
gioe72b54f2024-04-22 10:44:41 +0400181func InstallApp(
182 repo soft.RepoIO,
183 appDir string,
184 name string,
185 config any,
186 ports []PortForward,
187 resources CueAppData,
188 data CueAppData,
gio778577f2024-04-29 09:44:38 +0400189 opts ...soft.DoOption,
190) (ReleaseResources, error) {
gio308105e2024-04-19 13:12:13 +0400191 // if err := openPorts(rendered.Ports); err != nil {
192 // return err
193 // }
gio778577f2024-04-29 09:44:38 +0400194 return ReleaseResources{}, repo.Do(func(r soft.RepoFS) (string, error) {
gio308105e2024-04-19 13:12:13 +0400195 if err := r.RemoveDir(appDir); err != nil {
196 return "", err
197 }
198 resourcesDir := path.Join(appDir, "resources")
199 if err := r.CreateDir(resourcesDir); err != nil {
gio3af43942024-04-16 08:13:50 +0400200 return "", err
201 }
202 {
gioe72b54f2024-04-22 10:44:41 +0400203 if err := soft.WriteYaml(r, path.Join(appDir, configFileName), config); err != nil {
gio3af43942024-04-16 08:13:50 +0400204 return "", err
205 }
gioe72b54f2024-04-22 10:44:41 +0400206 if err := soft.WriteJson(r, path.Join(appDir, "config.json"), config); err != nil {
gio308105e2024-04-19 13:12:13 +0400207 return "", err
208 }
gioe72b54f2024-04-22 10:44:41 +0400209 for name, contents := range data {
gio308105e2024-04-19 13:12:13 +0400210 if name == "config.json" || name == "kustomization.yaml" || name == "resources" {
211 return "", fmt.Errorf("%s is forbidden", name)
212 }
213 w, err := r.Writer(path.Join(appDir, name))
gio3af43942024-04-16 08:13:50 +0400214 if err != nil {
215 return "", err
216 }
gio308105e2024-04-19 13:12:13 +0400217 defer w.Close()
218 if _, err := w.Write(contents); err != nil {
gio3af43942024-04-16 08:13:50 +0400219 return "", err
220 }
221 }
gio308105e2024-04-19 13:12:13 +0400222 }
223 {
224 if err := createKustomizationChain(r, resourcesDir); err != nil {
225 return "", err
226 }
gioe72b54f2024-04-22 10:44:41 +0400227 appKust := io.NewKustomization()
228 for name, contents := range resources {
gio308105e2024-04-19 13:12:13 +0400229 appKust.AddResources(name)
230 w, err := r.Writer(path.Join(resourcesDir, name))
231 if err != nil {
232 return "", err
233 }
234 defer w.Close()
235 if _, err := w.Write(contents); err != nil {
236 return "", err
237 }
238 }
gioe72b54f2024-04-22 10:44:41 +0400239 if err := soft.WriteYaml(r, path.Join(resourcesDir, "kustomization.yaml"), appKust); err != nil {
gio3af43942024-04-16 08:13:50 +0400240 return "", err
241 }
242 }
gioe72b54f2024-04-22 10:44:41 +0400243 return fmt.Sprintf("install: %s", name), nil
gio308105e2024-04-19 13:12:13 +0400244 }, opts...)
gio3af43942024-04-16 08:13:50 +0400245}
246
gio3cdee592024-04-17 10:15:56 +0400247// TODO(gio): commit instanceId -> appDir mapping as well
gio778577f2024-04-29 09:44:38 +0400248func (m *AppManager) Install(app EnvApp, instanceId string, appDir string, namespace string, values map[string]any) (ReleaseResources, error) {
gio3af43942024-04-16 08:13:50 +0400249 appDir = filepath.Clean(appDir)
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400250 if err := m.repoIO.Pull(); err != nil {
gio778577f2024-04-29 09:44:38 +0400251 return ReleaseResources{}, err
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400252 }
gio3cdee592024-04-17 10:15:56 +0400253 if err := m.nsCreator.Create(namespace); err != nil {
gio778577f2024-04-29 09:44:38 +0400254 return ReleaseResources{}, err
gio3cdee592024-04-17 10:15:56 +0400255 }
256 env, err := m.Config()
Giorgi Lekveishvili6e813182023-06-30 13:45:30 +0400257 if err != nil {
gio778577f2024-04-29 09:44:38 +0400258 return ReleaseResources{}, err
Giorgi Lekveishvili6e813182023-06-30 13:45:30 +0400259 }
gio3cdee592024-04-17 10:15:56 +0400260 release := Release{
261 AppInstanceId: instanceId,
262 Namespace: namespace,
263 RepoAddr: m.repoIO.FullAddress(),
264 AppDir: appDir,
265 }
266 rendered, err := app.Render(release, env, values)
gioef01fbb2024-04-12 16:52:59 +0400267 if err != nil {
gio778577f2024-04-29 09:44:38 +0400268 return ReleaseResources{}, err
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400269 }
gio778577f2024-04-29 09:44:38 +0400270 if _, err := InstallApp(m.repoIO, appDir, rendered.Name, rendered.Config, rendered.Ports, rendered.Resources, rendered.Data); err != nil {
271 return ReleaseResources{}, err
272 }
273 return ReleaseResources{
274 Helm: extractHelm(rendered.Resources),
275 }, nil
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400276}
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400277
gio778577f2024-04-29 09:44:38 +0400278type helmRelease struct {
279 Metadata Resource `json:"metadata"`
280 Status struct {
281 Conditions []struct {
282 Type string `json:"type"`
283 Status string `json:"status"`
284 } `json:"conditions"`
285 } `json:"status,omitempty"`
286}
287
288func extractHelm(resources CueAppData) []Resource {
289 ret := make([]Resource, 0, len(resources))
290 for _, contents := range resources {
291 var h helmRelease
292 if err := yaml.Unmarshal(contents, &h); err != nil {
293 panic(err) // TODO(gio): handle
294 }
295 ret = append(ret, h.Metadata)
296 }
297 return ret
298}
299
300func (m *AppManager) Update(app EnvApp, instanceId string, values map[string]any, opts ...soft.DoOption) (ReleaseResources, error) {
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400301 if err := m.repoIO.Pull(); err != nil {
gio778577f2024-04-29 09:44:38 +0400302 return ReleaseResources{}, err
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400303 }
gio3cdee592024-04-17 10:15:56 +0400304 env, err := m.Config()
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400305 if err != nil {
gio778577f2024-04-29 09:44:38 +0400306 return ReleaseResources{}, err
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400307 }
gio308105e2024-04-19 13:12:13 +0400308 instanceDir := filepath.Join(m.appDirRoot, instanceId)
309 instanceConfigPath := filepath.Join(instanceDir, "config.json")
gio3cdee592024-04-17 10:15:56 +0400310 config, err := m.appConfig(instanceConfigPath)
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400311 if err != nil {
gio778577f2024-04-29 09:44:38 +0400312 return ReleaseResources{}, err
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400313 }
gio3cdee592024-04-17 10:15:56 +0400314 release := Release{
315 AppInstanceId: instanceId,
316 Namespace: config.Release.Namespace,
317 RepoAddr: m.repoIO.FullAddress(),
318 AppDir: instanceDir,
319 }
320 rendered, err := app.Render(release, env, values)
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400321 if err != nil {
gio778577f2024-04-29 09:44:38 +0400322 return ReleaseResources{}, err
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400323 }
gioe72b54f2024-04-22 10:44:41 +0400324 return InstallApp(m.repoIO, instanceDir, rendered.Name, rendered.Config, rendered.Ports, rendered.Resources, rendered.Data, opts...)
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400325}
326
327func (m *AppManager) Remove(instanceId string) error {
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400328 if err := m.repoIO.Pull(); err != nil {
329 return err
330 }
gioe72b54f2024-04-22 10:44:41 +0400331 return m.repoIO.Do(func(r soft.RepoFS) (string, error) {
gio308105e2024-04-19 13:12:13 +0400332 r.RemoveDir(filepath.Join(m.appDirRoot, instanceId))
333 kustPath := filepath.Join(m.appDirRoot, "kustomization.yaml")
gioe72b54f2024-04-22 10:44:41 +0400334 kust, err := soft.ReadKustomization(r, kustPath)
gio3af43942024-04-16 08:13:50 +0400335 if err != nil {
336 return "", err
337 }
338 kust.RemoveResources(instanceId)
gioe72b54f2024-04-22 10:44:41 +0400339 soft.WriteYaml(r, kustPath, kust)
gio3af43942024-04-16 08:13:50 +0400340 return fmt.Sprintf("uninstall: %s", instanceId), nil
341 })
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400342}
343
Giorgi Lekveishvili67383962024-03-22 19:27:34 +0400344// TODO(gio): deduplicate with cue definition in app.go, this one should be removed.
gioe72b54f2024-04-22 10:44:41 +0400345func CreateNetworks(env EnvConfig) []Network {
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400346 return []Network{
347 {
348 Name: "Public",
gio3cdee592024-04-17 10:15:56 +0400349 IngressClass: fmt.Sprintf("%s-ingress-public", env.InfraName),
350 CertificateIssuer: fmt.Sprintf("%s-public", env.Id),
351 Domain: env.Domain,
352 AllocatePortAddr: fmt.Sprintf("http://port-allocator.%s-ingress-public.svc.cluster.local/api/allocate", env.InfraName),
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400353 },
354 {
Giorgi Lekveishvilib59b7c22024-04-03 22:17:50 +0400355 Name: "Private",
gio3cdee592024-04-17 10:15:56 +0400356 IngressClass: fmt.Sprintf("%s-ingress-private", env.Id),
357 Domain: env.PrivateDomain,
358 AllocatePortAddr: fmt.Sprintf("http://port-allocator.%s-ingress-private.svc.cluster.local/api/allocate", env.Id),
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400359 },
360 }
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400361}
gio3cdee592024-04-17 10:15:56 +0400362
363// InfraAppmanager
364
365type InfraAppManager struct {
gioe72b54f2024-04-22 10:44:41 +0400366 repoIO soft.RepoIO
gio3cdee592024-04-17 10:15:56 +0400367 nsCreator NamespaceCreator
368}
369
gioe72b54f2024-04-22 10:44:41 +0400370func NewInfraAppManager(repoIO soft.RepoIO, nsCreator NamespaceCreator) (*InfraAppManager, error) {
gio3cdee592024-04-17 10:15:56 +0400371 return &InfraAppManager{
372 repoIO,
373 nsCreator,
374 }, nil
375}
376
377func (m *InfraAppManager) Config() (InfraConfig, error) {
378 var cfg InfraConfig
gioe72b54f2024-04-22 10:44:41 +0400379 if err := soft.ReadYaml(m.repoIO, configFileName, &cfg); err != nil {
gio3cdee592024-04-17 10:15:56 +0400380 return InfraConfig{}, err
381 } else {
382 return cfg, nil
383 }
384}
385
gioe72b54f2024-04-22 10:44:41 +0400386func (m *InfraAppManager) appConfig(path string) (InfraAppInstanceConfig, error) {
387 var cfg InfraAppInstanceConfig
388 if err := soft.ReadJson(m.repoIO, path, &cfg); err != nil {
389 return InfraAppInstanceConfig{}, err
390 } else {
391 return cfg, nil
392 }
393}
394
395func (m *InfraAppManager) FindInstance(id string) (InfraAppInstanceConfig, error) {
396 kust, err := soft.ReadKustomization(m.repoIO, filepath.Join("/infrastructure", "kustomization.yaml"))
397 if err != nil {
398 return InfraAppInstanceConfig{}, err
399 }
400 for _, app := range kust.Resources {
401 if app == id {
402 cfg, err := m.appConfig(filepath.Join("/infrastructure", app, "config.json"))
403 if err != nil {
404 return InfraAppInstanceConfig{}, err
405 }
406 cfg.Id = id
407 return cfg, nil
408 }
409 }
410 return InfraAppInstanceConfig{}, nil
411}
412
gio778577f2024-04-29 09:44:38 +0400413func (m *InfraAppManager) Install(app InfraApp, appDir string, namespace string, values map[string]any) (ReleaseResources, error) {
gio3cdee592024-04-17 10:15:56 +0400414 appDir = filepath.Clean(appDir)
415 if err := m.repoIO.Pull(); err != nil {
gio778577f2024-04-29 09:44:38 +0400416 return ReleaseResources{}, err
gio3cdee592024-04-17 10:15:56 +0400417 }
418 if err := m.nsCreator.Create(namespace); err != nil {
gio778577f2024-04-29 09:44:38 +0400419 return ReleaseResources{}, err
gio3cdee592024-04-17 10:15:56 +0400420 }
421 infra, err := m.Config()
422 if err != nil {
gio778577f2024-04-29 09:44:38 +0400423 return ReleaseResources{}, err
gio3cdee592024-04-17 10:15:56 +0400424 }
425 release := Release{
426 Namespace: namespace,
427 RepoAddr: m.repoIO.FullAddress(),
428 AppDir: appDir,
429 }
430 rendered, err := app.Render(release, infra, values)
431 if err != nil {
gio778577f2024-04-29 09:44:38 +0400432 return ReleaseResources{}, err
gio3cdee592024-04-17 10:15:56 +0400433 }
gioe72b54f2024-04-22 10:44:41 +0400434 return InstallApp(m.repoIO, appDir, rendered.Name, rendered.Config, rendered.Ports, rendered.Resources, rendered.Data)
435}
436
gio778577f2024-04-29 09:44:38 +0400437func (m *InfraAppManager) Update(app InfraApp, instanceId string, values map[string]any, opts ...soft.DoOption) (ReleaseResources, error) {
gioe72b54f2024-04-22 10:44:41 +0400438 if err := m.repoIO.Pull(); err != nil {
gio778577f2024-04-29 09:44:38 +0400439 return ReleaseResources{}, err
gioe72b54f2024-04-22 10:44:41 +0400440 }
441 env, err := m.Config()
442 if err != nil {
gio778577f2024-04-29 09:44:38 +0400443 return ReleaseResources{}, err
gioe72b54f2024-04-22 10:44:41 +0400444 }
445 instanceDir := filepath.Join("/infrastructure", instanceId)
446 instanceConfigPath := filepath.Join(instanceDir, "config.json")
447 config, err := m.appConfig(instanceConfigPath)
448 if err != nil {
gio778577f2024-04-29 09:44:38 +0400449 return ReleaseResources{}, err
gioe72b54f2024-04-22 10:44:41 +0400450 }
451 release := Release{
452 AppInstanceId: instanceId,
453 Namespace: config.Release.Namespace,
454 RepoAddr: m.repoIO.FullAddress(),
455 AppDir: instanceDir,
456 }
457 rendered, err := app.Render(release, env, values)
458 if err != nil {
gio778577f2024-04-29 09:44:38 +0400459 return ReleaseResources{}, err
gioe72b54f2024-04-22 10:44:41 +0400460 }
461 return InstallApp(m.repoIO, instanceDir, rendered.Name, rendered.Config, rendered.Ports, rendered.Resources, rendered.Data, opts...)
gio3cdee592024-04-17 10:15:56 +0400462}