blob: 8ca42f2054b1ebf6aa375a1e0cc8a8296f2d99ea [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) {
gioe72b54f2024-04-22 10:44:41 +040057 kust, err := soft.ReadKustomization(m.repoIO, filepath.Join(m.appDirRoot, "kustomization.yaml"))
gio3af43942024-04-16 08:13:50 +040058 if err != nil {
59 return nil, err
60 }
gio3cdee592024-04-17 10:15:56 +040061 ret := make([]AppInstanceConfig, 0)
gio3af43942024-04-16 08:13:50 +040062 for _, app := range kust.Resources {
gio308105e2024-04-19 13:12:13 +040063 cfg, err := m.appConfig(filepath.Join(m.appDirRoot, app, "config.json"))
64 if err != nil {
65 return nil, err
66 }
67 cfg.Id = app
68 ret = append(ret, cfg)
69 }
70 return ret, nil
71}
72
73func (m *AppManager) FindAllAppInstances(name string) ([]AppInstanceConfig, error) {
gioe72b54f2024-04-22 10:44:41 +040074 kust, err := soft.ReadKustomization(m.repoIO, filepath.Join(m.appDirRoot, "kustomization.yaml"))
gio308105e2024-04-19 13:12:13 +040075 if err != nil {
76 return nil, err
77 }
78 ret := make([]AppInstanceConfig, 0)
79 for _, app := range kust.Resources {
80 cfg, err := m.appConfig(filepath.Join(m.appDirRoot, app, "config.json"))
gio3af43942024-04-16 08:13:50 +040081 if err != nil {
82 return nil, err
83 }
84 cfg.Id = app
85 if cfg.AppId == name {
86 ret = append(ret, cfg)
87 }
88 }
89 return ret, nil
Giorgi Lekveishvili76951482023-06-30 23:25:09 +040090}
91
gio778577f2024-04-29 09:44:38 +040092func (m *AppManager) FindInstance(id string) (*AppInstanceConfig, error) {
gioe72b54f2024-04-22 10:44:41 +040093 kust, err := soft.ReadKustomization(m.repoIO, filepath.Join(m.appDirRoot, "kustomization.yaml"))
gio3af43942024-04-16 08:13:50 +040094 if err != nil {
gio778577f2024-04-29 09:44:38 +040095 return nil, err
gio3af43942024-04-16 08:13:50 +040096 }
97 for _, app := range kust.Resources {
98 if app == id {
gio308105e2024-04-19 13:12:13 +040099 cfg, err := m.appConfig(filepath.Join(m.appDirRoot, app, "config.json"))
gio3af43942024-04-16 08:13:50 +0400100 if err != nil {
gio778577f2024-04-29 09:44:38 +0400101 return nil, err
gio3af43942024-04-16 08:13:50 +0400102 }
103 cfg.Id = id
gio778577f2024-04-29 09:44:38 +0400104 return &cfg, nil
gio3af43942024-04-16 08:13:50 +0400105 }
106 }
gio778577f2024-04-29 09:44:38 +0400107 return nil, ErrorNotFound
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400108}
109
gio3cdee592024-04-17 10:15:56 +0400110func (m *AppManager) AppConfig(name string) (AppInstanceConfig, error) {
gio3cdee592024-04-17 10:15:56 +0400111 var cfg AppInstanceConfig
gioe72b54f2024-04-22 10:44:41 +0400112 if err := soft.ReadJson(m.repoIO, filepath.Join(m.appDirRoot, name, "config.json"), &cfg); err != nil {
gio3cdee592024-04-17 10:15:56 +0400113 return AppInstanceConfig{}, err
Giorgi Lekveishvili03ee5852023-05-30 13:20:10 +0400114 }
gio308105e2024-04-19 13:12:13 +0400115 return cfg, nil
Giorgi Lekveishvili03ee5852023-05-30 13:20:10 +0400116}
117
gio3af43942024-04-16 08:13:50 +0400118type allocatePortReq struct {
119 Protocol string `json:"protocol"`
120 SourcePort int `json:"sourcePort"`
121 TargetService string `json:"targetService"`
122 TargetPort int `json:"targetPort"`
123}
124
125func openPorts(ports []PortForward) error {
126 for _, p := range ports {
127 var buf bytes.Buffer
128 req := allocatePortReq{
129 Protocol: p.Protocol,
130 SourcePort: p.SourcePort,
131 TargetService: p.TargetService,
132 TargetPort: p.TargetPort,
133 }
134 if err := json.NewEncoder(&buf).Encode(req); err != nil {
135 return err
136 }
137 resp, err := http.Post(p.Allocator, "application/json", &buf)
138 if err != nil {
139 return err
140 }
141 if resp.StatusCode != http.StatusOK {
142 return fmt.Errorf("Could not allocate port %d, status code: %d", p.SourcePort, resp.StatusCode)
143 }
144 }
145 return nil
146}
147
gioe72b54f2024-04-22 10:44:41 +0400148func createKustomizationChain(r soft.RepoFS, path string) error {
gio3af43942024-04-16 08:13:50 +0400149 for p := filepath.Clean(path); p != "/"; {
150 parent, child := filepath.Split(p)
151 kustPath := filepath.Join(parent, "kustomization.yaml")
gioe72b54f2024-04-22 10:44:41 +0400152 kust, err := soft.ReadKustomization(r, kustPath)
gio3af43942024-04-16 08:13:50 +0400153 if err != nil {
154 if errors.Is(err, fs.ErrNotExist) {
gioe72b54f2024-04-22 10:44:41 +0400155 k := io.NewKustomization()
gio3af43942024-04-16 08:13:50 +0400156 kust = &k
157 } else {
158 return err
159 }
160 }
161 kust.AddResources(child)
gioe72b54f2024-04-22 10:44:41 +0400162 if err := soft.WriteYaml(r, kustPath, kust); err != nil {
gio3af43942024-04-16 08:13:50 +0400163 return err
164 }
165 p = filepath.Clean(parent)
166 }
167 return nil
168}
169
gio778577f2024-04-29 09:44:38 +0400170type Resource struct {
171 Name string `json:"name"`
172 Namespace string `json:"namespace"`
173}
174
175type ReleaseResources struct {
176 Helm []Resource
177}
178
gio3cdee592024-04-17 10:15:56 +0400179// TODO(gio): rename to CommitApp
gioe72b54f2024-04-22 10:44:41 +0400180func InstallApp(
181 repo soft.RepoIO,
182 appDir string,
183 name string,
184 config any,
185 ports []PortForward,
186 resources CueAppData,
187 data CueAppData,
gio778577f2024-04-29 09:44:38 +0400188 opts ...soft.DoOption,
189) (ReleaseResources, error) {
gio308105e2024-04-19 13:12:13 +0400190 // if err := openPorts(rendered.Ports); err != nil {
191 // return err
192 // }
gio778577f2024-04-29 09:44:38 +0400193 return ReleaseResources{}, repo.Do(func(r soft.RepoFS) (string, error) {
gio308105e2024-04-19 13:12:13 +0400194 if err := r.RemoveDir(appDir); err != nil {
195 return "", err
196 }
197 resourcesDir := path.Join(appDir, "resources")
198 if err := r.CreateDir(resourcesDir); err != nil {
gio3af43942024-04-16 08:13:50 +0400199 return "", err
200 }
201 {
gioe72b54f2024-04-22 10:44:41 +0400202 if err := soft.WriteYaml(r, path.Join(appDir, configFileName), config); err != nil {
gio3af43942024-04-16 08:13:50 +0400203 return "", err
204 }
gioe72b54f2024-04-22 10:44:41 +0400205 if err := soft.WriteJson(r, path.Join(appDir, "config.json"), config); err != nil {
gio308105e2024-04-19 13:12:13 +0400206 return "", err
207 }
gioe72b54f2024-04-22 10:44:41 +0400208 for name, contents := range data {
gio308105e2024-04-19 13:12:13 +0400209 if name == "config.json" || name == "kustomization.yaml" || name == "resources" {
210 return "", fmt.Errorf("%s is forbidden", name)
211 }
212 w, err := r.Writer(path.Join(appDir, name))
gio3af43942024-04-16 08:13:50 +0400213 if err != nil {
214 return "", err
215 }
gio308105e2024-04-19 13:12:13 +0400216 defer w.Close()
217 if _, err := w.Write(contents); err != nil {
gio3af43942024-04-16 08:13:50 +0400218 return "", err
219 }
220 }
gio308105e2024-04-19 13:12:13 +0400221 }
222 {
223 if err := createKustomizationChain(r, resourcesDir); err != nil {
224 return "", err
225 }
gioe72b54f2024-04-22 10:44:41 +0400226 appKust := io.NewKustomization()
227 for name, contents := range resources {
gio308105e2024-04-19 13:12:13 +0400228 appKust.AddResources(name)
229 w, err := r.Writer(path.Join(resourcesDir, name))
230 if err != nil {
231 return "", err
232 }
233 defer w.Close()
234 if _, err := w.Write(contents); err != nil {
235 return "", err
236 }
237 }
gioe72b54f2024-04-22 10:44:41 +0400238 if err := soft.WriteYaml(r, path.Join(resourcesDir, "kustomization.yaml"), appKust); err != nil {
gio3af43942024-04-16 08:13:50 +0400239 return "", err
240 }
241 }
gioe72b54f2024-04-22 10:44:41 +0400242 return fmt.Sprintf("install: %s", name), nil
gio308105e2024-04-19 13:12:13 +0400243 }, opts...)
gio3af43942024-04-16 08:13:50 +0400244}
245
gio3cdee592024-04-17 10:15:56 +0400246// TODO(gio): commit instanceId -> appDir mapping as well
gio778577f2024-04-29 09:44:38 +0400247func (m *AppManager) Install(app EnvApp, instanceId string, appDir string, namespace string, values map[string]any) (ReleaseResources, error) {
gio3af43942024-04-16 08:13:50 +0400248 appDir = filepath.Clean(appDir)
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400249 if err := m.repoIO.Pull(); err != nil {
gio778577f2024-04-29 09:44:38 +0400250 return ReleaseResources{}, err
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400251 }
gio3cdee592024-04-17 10:15:56 +0400252 if err := m.nsCreator.Create(namespace); err != nil {
gio778577f2024-04-29 09:44:38 +0400253 return ReleaseResources{}, err
gio3cdee592024-04-17 10:15:56 +0400254 }
255 env, err := m.Config()
Giorgi Lekveishvili6e813182023-06-30 13:45:30 +0400256 if err != nil {
gio778577f2024-04-29 09:44:38 +0400257 return ReleaseResources{}, err
Giorgi Lekveishvili6e813182023-06-30 13:45:30 +0400258 }
gio3cdee592024-04-17 10:15:56 +0400259 release := Release{
260 AppInstanceId: instanceId,
261 Namespace: namespace,
262 RepoAddr: m.repoIO.FullAddress(),
263 AppDir: appDir,
264 }
265 rendered, err := app.Render(release, env, values)
gioef01fbb2024-04-12 16:52:59 +0400266 if err != nil {
gio778577f2024-04-29 09:44:38 +0400267 return ReleaseResources{}, err
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400268 }
gio778577f2024-04-29 09:44:38 +0400269 if _, err := InstallApp(m.repoIO, appDir, rendered.Name, rendered.Config, rendered.Ports, rendered.Resources, rendered.Data); err != nil {
270 return ReleaseResources{}, err
271 }
272 return ReleaseResources{
273 Helm: extractHelm(rendered.Resources),
274 }, nil
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400275}
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400276
gio778577f2024-04-29 09:44:38 +0400277type helmRelease struct {
278 Metadata Resource `json:"metadata"`
279 Status struct {
280 Conditions []struct {
281 Type string `json:"type"`
282 Status string `json:"status"`
283 } `json:"conditions"`
284 } `json:"status,omitempty"`
285}
286
287func extractHelm(resources CueAppData) []Resource {
288 ret := make([]Resource, 0, len(resources))
289 for _, contents := range resources {
290 var h helmRelease
291 if err := yaml.Unmarshal(contents, &h); err != nil {
292 panic(err) // TODO(gio): handle
293 }
294 ret = append(ret, h.Metadata)
295 }
296 return ret
297}
298
299func (m *AppManager) Update(app EnvApp, instanceId string, values map[string]any, opts ...soft.DoOption) (ReleaseResources, error) {
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400300 if err := m.repoIO.Pull(); err != nil {
gio778577f2024-04-29 09:44:38 +0400301 return ReleaseResources{}, err
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400302 }
gio3cdee592024-04-17 10:15:56 +0400303 env, err := m.Config()
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400304 if err != nil {
gio778577f2024-04-29 09:44:38 +0400305 return ReleaseResources{}, err
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400306 }
gio308105e2024-04-19 13:12:13 +0400307 instanceDir := filepath.Join(m.appDirRoot, instanceId)
308 instanceConfigPath := filepath.Join(instanceDir, "config.json")
gio3cdee592024-04-17 10:15:56 +0400309 config, err := m.appConfig(instanceConfigPath)
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400310 if err != nil {
gio778577f2024-04-29 09:44:38 +0400311 return ReleaseResources{}, err
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400312 }
gio3cdee592024-04-17 10:15:56 +0400313 release := Release{
314 AppInstanceId: instanceId,
315 Namespace: config.Release.Namespace,
316 RepoAddr: m.repoIO.FullAddress(),
317 AppDir: instanceDir,
318 }
319 rendered, err := app.Render(release, env, values)
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400320 if err != nil {
gio778577f2024-04-29 09:44:38 +0400321 return ReleaseResources{}, err
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400322 }
gioe72b54f2024-04-22 10:44:41 +0400323 return InstallApp(m.repoIO, instanceDir, rendered.Name, rendered.Config, rendered.Ports, rendered.Resources, rendered.Data, opts...)
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400324}
325
326func (m *AppManager) Remove(instanceId string) error {
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400327 if err := m.repoIO.Pull(); err != nil {
328 return err
329 }
gioe72b54f2024-04-22 10:44:41 +0400330 return m.repoIO.Do(func(r soft.RepoFS) (string, error) {
gio308105e2024-04-19 13:12:13 +0400331 r.RemoveDir(filepath.Join(m.appDirRoot, instanceId))
332 kustPath := filepath.Join(m.appDirRoot, "kustomization.yaml")
gioe72b54f2024-04-22 10:44:41 +0400333 kust, err := soft.ReadKustomization(r, kustPath)
gio3af43942024-04-16 08:13:50 +0400334 if err != nil {
335 return "", err
336 }
337 kust.RemoveResources(instanceId)
gioe72b54f2024-04-22 10:44:41 +0400338 soft.WriteYaml(r, kustPath, kust)
gio3af43942024-04-16 08:13:50 +0400339 return fmt.Sprintf("uninstall: %s", instanceId), nil
340 })
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400341}
342
Giorgi Lekveishvili67383962024-03-22 19:27:34 +0400343// TODO(gio): deduplicate with cue definition in app.go, this one should be removed.
gioe72b54f2024-04-22 10:44:41 +0400344func CreateNetworks(env EnvConfig) []Network {
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400345 return []Network{
346 {
347 Name: "Public",
gio3cdee592024-04-17 10:15:56 +0400348 IngressClass: fmt.Sprintf("%s-ingress-public", env.InfraName),
349 CertificateIssuer: fmt.Sprintf("%s-public", env.Id),
350 Domain: env.Domain,
351 AllocatePortAddr: fmt.Sprintf("http://port-allocator.%s-ingress-public.svc.cluster.local/api/allocate", env.InfraName),
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400352 },
353 {
Giorgi Lekveishvilib59b7c22024-04-03 22:17:50 +0400354 Name: "Private",
gio3cdee592024-04-17 10:15:56 +0400355 IngressClass: fmt.Sprintf("%s-ingress-private", env.Id),
356 Domain: env.PrivateDomain,
357 AllocatePortAddr: fmt.Sprintf("http://port-allocator.%s-ingress-private.svc.cluster.local/api/allocate", env.Id),
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400358 },
359 }
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400360}
gio3cdee592024-04-17 10:15:56 +0400361
362// InfraAppmanager
363
364type InfraAppManager struct {
gioe72b54f2024-04-22 10:44:41 +0400365 repoIO soft.RepoIO
gio3cdee592024-04-17 10:15:56 +0400366 nsCreator NamespaceCreator
367}
368
gioe72b54f2024-04-22 10:44:41 +0400369func NewInfraAppManager(repoIO soft.RepoIO, nsCreator NamespaceCreator) (*InfraAppManager, error) {
gio3cdee592024-04-17 10:15:56 +0400370 return &InfraAppManager{
371 repoIO,
372 nsCreator,
373 }, nil
374}
375
376func (m *InfraAppManager) Config() (InfraConfig, error) {
377 var cfg InfraConfig
gioe72b54f2024-04-22 10:44:41 +0400378 if err := soft.ReadYaml(m.repoIO, configFileName, &cfg); err != nil {
gio3cdee592024-04-17 10:15:56 +0400379 return InfraConfig{}, err
380 } else {
381 return cfg, nil
382 }
383}
384
gioe72b54f2024-04-22 10:44:41 +0400385func (m *InfraAppManager) appConfig(path string) (InfraAppInstanceConfig, error) {
386 var cfg InfraAppInstanceConfig
387 if err := soft.ReadJson(m.repoIO, path, &cfg); err != nil {
388 return InfraAppInstanceConfig{}, err
389 } else {
390 return cfg, nil
391 }
392}
393
394func (m *InfraAppManager) FindInstance(id string) (InfraAppInstanceConfig, error) {
395 kust, err := soft.ReadKustomization(m.repoIO, filepath.Join("/infrastructure", "kustomization.yaml"))
396 if err != nil {
397 return InfraAppInstanceConfig{}, err
398 }
399 for _, app := range kust.Resources {
400 if app == id {
401 cfg, err := m.appConfig(filepath.Join("/infrastructure", app, "config.json"))
402 if err != nil {
403 return InfraAppInstanceConfig{}, err
404 }
405 cfg.Id = id
406 return cfg, nil
407 }
408 }
409 return InfraAppInstanceConfig{}, nil
410}
411
gio778577f2024-04-29 09:44:38 +0400412func (m *InfraAppManager) Install(app InfraApp, appDir string, namespace string, values map[string]any) (ReleaseResources, error) {
gio3cdee592024-04-17 10:15:56 +0400413 appDir = filepath.Clean(appDir)
414 if err := m.repoIO.Pull(); err != nil {
gio778577f2024-04-29 09:44:38 +0400415 return ReleaseResources{}, err
gio3cdee592024-04-17 10:15:56 +0400416 }
417 if err := m.nsCreator.Create(namespace); err != nil {
gio778577f2024-04-29 09:44:38 +0400418 return ReleaseResources{}, err
gio3cdee592024-04-17 10:15:56 +0400419 }
420 infra, err := m.Config()
421 if err != nil {
gio778577f2024-04-29 09:44:38 +0400422 return ReleaseResources{}, err
gio3cdee592024-04-17 10:15:56 +0400423 }
424 release := Release{
425 Namespace: namespace,
426 RepoAddr: m.repoIO.FullAddress(),
427 AppDir: appDir,
428 }
429 rendered, err := app.Render(release, infra, values)
430 if err != nil {
gio778577f2024-04-29 09:44:38 +0400431 return ReleaseResources{}, err
gio3cdee592024-04-17 10:15:56 +0400432 }
gioe72b54f2024-04-22 10:44:41 +0400433 return InstallApp(m.repoIO, appDir, rendered.Name, rendered.Config, rendered.Ports, rendered.Resources, rendered.Data)
434}
435
gio778577f2024-04-29 09:44:38 +0400436func (m *InfraAppManager) Update(app InfraApp, instanceId string, values map[string]any, opts ...soft.DoOption) (ReleaseResources, error) {
gioe72b54f2024-04-22 10:44:41 +0400437 if err := m.repoIO.Pull(); err != nil {
gio778577f2024-04-29 09:44:38 +0400438 return ReleaseResources{}, err
gioe72b54f2024-04-22 10:44:41 +0400439 }
440 env, err := m.Config()
441 if err != nil {
gio778577f2024-04-29 09:44:38 +0400442 return ReleaseResources{}, err
gioe72b54f2024-04-22 10:44:41 +0400443 }
444 instanceDir := filepath.Join("/infrastructure", instanceId)
445 instanceConfigPath := filepath.Join(instanceDir, "config.json")
446 config, err := m.appConfig(instanceConfigPath)
447 if err != nil {
gio778577f2024-04-29 09:44:38 +0400448 return ReleaseResources{}, err
gioe72b54f2024-04-22 10:44:41 +0400449 }
450 release := Release{
451 AppInstanceId: instanceId,
452 Namespace: config.Release.Namespace,
453 RepoAddr: m.repoIO.FullAddress(),
454 AppDir: instanceDir,
455 }
456 rendered, err := app.Render(release, env, values)
457 if err != nil {
gio778577f2024-04-29 09:44:38 +0400458 return ReleaseResources{}, err
gioe72b54f2024-04-22 10:44:41 +0400459 }
460 return InstallApp(m.repoIO, instanceDir, rendered.Name, rendered.Config, rendered.Ports, rendered.Resources, rendered.Data, opts...)
gio3cdee592024-04-17 10:15:56 +0400461}