blob: 44e39e6fe8701c50e27bacce2983aad7eb56563e [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) {
gio778577f2024-04-29 09:44:38 +0400191 return ReleaseResources{}, repo.Do(func(r soft.RepoFS) (string, error) {
gio308105e2024-04-19 13:12:13 +0400192 if err := r.RemoveDir(appDir); err != nil {
193 return "", err
194 }
195 resourcesDir := path.Join(appDir, "resources")
196 if err := r.CreateDir(resourcesDir); err != nil {
gio3af43942024-04-16 08:13:50 +0400197 return "", err
198 }
199 {
gioe72b54f2024-04-22 10:44:41 +0400200 if err := soft.WriteYaml(r, path.Join(appDir, configFileName), config); err != nil {
gio3af43942024-04-16 08:13:50 +0400201 return "", err
202 }
gioe72b54f2024-04-22 10:44:41 +0400203 if err := soft.WriteJson(r, path.Join(appDir, "config.json"), config); err != nil {
gio308105e2024-04-19 13:12:13 +0400204 return "", err
205 }
gioe72b54f2024-04-22 10:44:41 +0400206 for name, contents := range data {
gio308105e2024-04-19 13:12:13 +0400207 if name == "config.json" || name == "kustomization.yaml" || name == "resources" {
208 return "", fmt.Errorf("%s is forbidden", name)
209 }
210 w, err := r.Writer(path.Join(appDir, name))
gio3af43942024-04-16 08:13:50 +0400211 if err != nil {
212 return "", err
213 }
gio308105e2024-04-19 13:12:13 +0400214 defer w.Close()
215 if _, err := w.Write(contents); err != nil {
gio3af43942024-04-16 08:13:50 +0400216 return "", err
217 }
218 }
gio308105e2024-04-19 13:12:13 +0400219 }
220 {
221 if err := createKustomizationChain(r, resourcesDir); err != nil {
222 return "", err
223 }
gioe72b54f2024-04-22 10:44:41 +0400224 appKust := io.NewKustomization()
225 for name, contents := range resources {
gio308105e2024-04-19 13:12:13 +0400226 appKust.AddResources(name)
227 w, err := r.Writer(path.Join(resourcesDir, name))
228 if err != nil {
229 return "", err
230 }
231 defer w.Close()
232 if _, err := w.Write(contents); err != nil {
233 return "", err
234 }
235 }
gioe72b54f2024-04-22 10:44:41 +0400236 if err := soft.WriteYaml(r, path.Join(resourcesDir, "kustomization.yaml"), appKust); err != nil {
gio3af43942024-04-16 08:13:50 +0400237 return "", err
238 }
239 }
gioe72b54f2024-04-22 10:44:41 +0400240 return fmt.Sprintf("install: %s", name), nil
gio308105e2024-04-19 13:12:13 +0400241 }, opts...)
gio3af43942024-04-16 08:13:50 +0400242}
243
gio3cdee592024-04-17 10:15:56 +0400244// TODO(gio): commit instanceId -> appDir mapping as well
gio778577f2024-04-29 09:44:38 +0400245func (m *AppManager) Install(app EnvApp, instanceId string, appDir string, namespace string, values map[string]any) (ReleaseResources, error) {
gio3af43942024-04-16 08:13:50 +0400246 appDir = filepath.Clean(appDir)
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400247 if err := m.repoIO.Pull(); err != nil {
gio778577f2024-04-29 09:44:38 +0400248 return ReleaseResources{}, err
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400249 }
gio3cdee592024-04-17 10:15:56 +0400250 if err := m.nsCreator.Create(namespace); err != nil {
gio778577f2024-04-29 09:44:38 +0400251 return ReleaseResources{}, err
gio3cdee592024-04-17 10:15:56 +0400252 }
253 env, err := m.Config()
Giorgi Lekveishvili6e813182023-06-30 13:45:30 +0400254 if err != nil {
gio778577f2024-04-29 09:44:38 +0400255 return ReleaseResources{}, err
Giorgi Lekveishvili6e813182023-06-30 13:45:30 +0400256 }
gio3cdee592024-04-17 10:15:56 +0400257 release := Release{
258 AppInstanceId: instanceId,
259 Namespace: namespace,
260 RepoAddr: m.repoIO.FullAddress(),
261 AppDir: appDir,
262 }
263 rendered, err := app.Render(release, env, values)
gioef01fbb2024-04-12 16:52:59 +0400264 if err != nil {
gio778577f2024-04-29 09:44:38 +0400265 return ReleaseResources{}, err
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400266 }
gio778577f2024-04-29 09:44:38 +0400267 if _, err := InstallApp(m.repoIO, appDir, rendered.Name, rendered.Config, rendered.Ports, rendered.Resources, rendered.Data); err != nil {
268 return ReleaseResources{}, err
269 }
gioff2a29a2024-05-01 17:06:42 +0400270 // TODO(gio): add ingress-nginx to release resources
271 if err := openPorts(rendered.Ports); err != nil {
272 return ReleaseResources{}, err
273 }
gio778577f2024-04-29 09:44:38 +0400274 return ReleaseResources{
275 Helm: extractHelm(rendered.Resources),
276 }, nil
Giorgi Lekveishvili23ef7f82023-05-26 11:57:48 +0400277}
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400278
gio778577f2024-04-29 09:44:38 +0400279type helmRelease struct {
280 Metadata Resource `json:"metadata"`
281 Status struct {
282 Conditions []struct {
283 Type string `json:"type"`
284 Status string `json:"status"`
285 } `json:"conditions"`
286 } `json:"status,omitempty"`
287}
288
289func extractHelm(resources CueAppData) []Resource {
290 ret := make([]Resource, 0, len(resources))
291 for _, contents := range resources {
292 var h helmRelease
293 if err := yaml.Unmarshal(contents, &h); err != nil {
294 panic(err) // TODO(gio): handle
295 }
296 ret = append(ret, h.Metadata)
297 }
298 return ret
299}
300
301func (m *AppManager) Update(app EnvApp, instanceId string, values map[string]any, opts ...soft.DoOption) (ReleaseResources, error) {
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400302 if err := m.repoIO.Pull(); err != nil {
gio778577f2024-04-29 09:44:38 +0400303 return ReleaseResources{}, err
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400304 }
gio3cdee592024-04-17 10:15:56 +0400305 env, err := m.Config()
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400306 if err != nil {
gio778577f2024-04-29 09:44:38 +0400307 return ReleaseResources{}, err
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400308 }
gio308105e2024-04-19 13:12:13 +0400309 instanceDir := filepath.Join(m.appDirRoot, instanceId)
310 instanceConfigPath := filepath.Join(instanceDir, "config.json")
gio3cdee592024-04-17 10:15:56 +0400311 config, err := m.appConfig(instanceConfigPath)
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400312 if err != nil {
gio778577f2024-04-29 09:44:38 +0400313 return ReleaseResources{}, err
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400314 }
gio3cdee592024-04-17 10:15:56 +0400315 release := Release{
316 AppInstanceId: instanceId,
317 Namespace: config.Release.Namespace,
318 RepoAddr: m.repoIO.FullAddress(),
319 AppDir: instanceDir,
320 }
321 rendered, err := app.Render(release, env, values)
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400322 if err != nil {
gio778577f2024-04-29 09:44:38 +0400323 return ReleaseResources{}, err
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400324 }
gioe72b54f2024-04-22 10:44:41 +0400325 return InstallApp(m.repoIO, instanceDir, rendered.Name, rendered.Config, rendered.Ports, rendered.Resources, rendered.Data, opts...)
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400326}
327
328func (m *AppManager) Remove(instanceId string) error {
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400329 if err := m.repoIO.Pull(); err != nil {
330 return err
331 }
gioe72b54f2024-04-22 10:44:41 +0400332 return m.repoIO.Do(func(r soft.RepoFS) (string, error) {
gio308105e2024-04-19 13:12:13 +0400333 r.RemoveDir(filepath.Join(m.appDirRoot, instanceId))
334 kustPath := filepath.Join(m.appDirRoot, "kustomization.yaml")
gioe72b54f2024-04-22 10:44:41 +0400335 kust, err := soft.ReadKustomization(r, kustPath)
gio3af43942024-04-16 08:13:50 +0400336 if err != nil {
337 return "", err
338 }
339 kust.RemoveResources(instanceId)
gioe72b54f2024-04-22 10:44:41 +0400340 soft.WriteYaml(r, kustPath, kust)
gio3af43942024-04-16 08:13:50 +0400341 return fmt.Sprintf("uninstall: %s", instanceId), nil
342 })
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400343}
344
Giorgi Lekveishvili67383962024-03-22 19:27:34 +0400345// TODO(gio): deduplicate with cue definition in app.go, this one should be removed.
gioe72b54f2024-04-22 10:44:41 +0400346func CreateNetworks(env EnvConfig) []Network {
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400347 return []Network{
348 {
349 Name: "Public",
gio3cdee592024-04-17 10:15:56 +0400350 IngressClass: fmt.Sprintf("%s-ingress-public", env.InfraName),
351 CertificateIssuer: fmt.Sprintf("%s-public", env.Id),
352 Domain: env.Domain,
353 AllocatePortAddr: fmt.Sprintf("http://port-allocator.%s-ingress-public.svc.cluster.local/api/allocate", env.InfraName),
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400354 },
355 {
Giorgi Lekveishvilib59b7c22024-04-03 22:17:50 +0400356 Name: "Private",
gio3cdee592024-04-17 10:15:56 +0400357 IngressClass: fmt.Sprintf("%s-ingress-private", env.Id),
358 Domain: env.PrivateDomain,
359 AllocatePortAddr: fmt.Sprintf("http://port-allocator.%s-ingress-private.svc.cluster.local/api/allocate", env.Id),
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400360 },
361 }
Giorgi Lekveishvili76951482023-06-30 23:25:09 +0400362}
gio3cdee592024-04-17 10:15:56 +0400363
364// InfraAppmanager
365
366type InfraAppManager struct {
gioe72b54f2024-04-22 10:44:41 +0400367 repoIO soft.RepoIO
gio3cdee592024-04-17 10:15:56 +0400368 nsCreator NamespaceCreator
369}
370
gioe72b54f2024-04-22 10:44:41 +0400371func NewInfraAppManager(repoIO soft.RepoIO, nsCreator NamespaceCreator) (*InfraAppManager, error) {
gio3cdee592024-04-17 10:15:56 +0400372 return &InfraAppManager{
373 repoIO,
374 nsCreator,
375 }, nil
376}
377
378func (m *InfraAppManager) Config() (InfraConfig, error) {
379 var cfg InfraConfig
gioe72b54f2024-04-22 10:44:41 +0400380 if err := soft.ReadYaml(m.repoIO, configFileName, &cfg); err != nil {
gio3cdee592024-04-17 10:15:56 +0400381 return InfraConfig{}, err
382 } else {
383 return cfg, nil
384 }
385}
386
gioe72b54f2024-04-22 10:44:41 +0400387func (m *InfraAppManager) appConfig(path string) (InfraAppInstanceConfig, error) {
388 var cfg InfraAppInstanceConfig
389 if err := soft.ReadJson(m.repoIO, path, &cfg); err != nil {
390 return InfraAppInstanceConfig{}, err
391 } else {
392 return cfg, nil
393 }
394}
395
396func (m *InfraAppManager) FindInstance(id string) (InfraAppInstanceConfig, error) {
397 kust, err := soft.ReadKustomization(m.repoIO, filepath.Join("/infrastructure", "kustomization.yaml"))
398 if err != nil {
399 return InfraAppInstanceConfig{}, err
400 }
401 for _, app := range kust.Resources {
402 if app == id {
403 cfg, err := m.appConfig(filepath.Join("/infrastructure", app, "config.json"))
404 if err != nil {
405 return InfraAppInstanceConfig{}, err
406 }
407 cfg.Id = id
408 return cfg, nil
409 }
410 }
411 return InfraAppInstanceConfig{}, nil
412}
413
gio778577f2024-04-29 09:44:38 +0400414func (m *InfraAppManager) Install(app InfraApp, appDir string, namespace string, values map[string]any) (ReleaseResources, error) {
gio3cdee592024-04-17 10:15:56 +0400415 appDir = filepath.Clean(appDir)
416 if err := m.repoIO.Pull(); err != nil {
gio778577f2024-04-29 09:44:38 +0400417 return ReleaseResources{}, err
gio3cdee592024-04-17 10:15:56 +0400418 }
419 if err := m.nsCreator.Create(namespace); err != nil {
gio778577f2024-04-29 09:44:38 +0400420 return ReleaseResources{}, err
gio3cdee592024-04-17 10:15:56 +0400421 }
422 infra, err := m.Config()
423 if err != nil {
gio778577f2024-04-29 09:44:38 +0400424 return ReleaseResources{}, err
gio3cdee592024-04-17 10:15:56 +0400425 }
426 release := Release{
427 Namespace: namespace,
428 RepoAddr: m.repoIO.FullAddress(),
429 AppDir: appDir,
430 }
431 rendered, err := app.Render(release, infra, values)
432 if err != nil {
gio778577f2024-04-29 09:44:38 +0400433 return ReleaseResources{}, err
gio3cdee592024-04-17 10:15:56 +0400434 }
gioe72b54f2024-04-22 10:44:41 +0400435 return InstallApp(m.repoIO, appDir, rendered.Name, rendered.Config, rendered.Ports, rendered.Resources, rendered.Data)
436}
437
gio778577f2024-04-29 09:44:38 +0400438func (m *InfraAppManager) Update(app InfraApp, instanceId string, values map[string]any, opts ...soft.DoOption) (ReleaseResources, error) {
gioe72b54f2024-04-22 10:44:41 +0400439 if err := m.repoIO.Pull(); err != nil {
gio778577f2024-04-29 09:44:38 +0400440 return ReleaseResources{}, err
gioe72b54f2024-04-22 10:44:41 +0400441 }
442 env, err := m.Config()
443 if err != nil {
gio778577f2024-04-29 09:44:38 +0400444 return ReleaseResources{}, err
gioe72b54f2024-04-22 10:44:41 +0400445 }
446 instanceDir := filepath.Join("/infrastructure", instanceId)
447 instanceConfigPath := filepath.Join(instanceDir, "config.json")
448 config, err := m.appConfig(instanceConfigPath)
449 if err != nil {
gio778577f2024-04-29 09:44:38 +0400450 return ReleaseResources{}, err
gioe72b54f2024-04-22 10:44:41 +0400451 }
452 release := Release{
453 AppInstanceId: instanceId,
454 Namespace: config.Release.Namespace,
455 RepoAddr: m.repoIO.FullAddress(),
456 AppDir: instanceDir,
457 }
458 rendered, err := app.Render(release, env, values)
459 if err != nil {
gio778577f2024-04-29 09:44:38 +0400460 return ReleaseResources{}, err
gioe72b54f2024-04-22 10:44:41 +0400461 }
462 return InstallApp(m.repoIO, instanceDir, rendered.Name, rendered.Config, rendered.Ports, rendered.Resources, rendered.Data, opts...)
gio3cdee592024-04-17 10:15:56 +0400463}