Installer: Clean up RepoIO interface
Change-Id: If80d7be1460c725b7df9d1d27c9354cb9141acfe
diff --git a/core/installer/app_manager.go b/core/installer/app_manager.go
index 295c8ff..e438a26 100644
--- a/core/installer/app_manager.go
+++ b/core/installer/app_manager.go
@@ -1,8 +1,14 @@
package installer
import (
+ "bytes"
+ "encoding/json"
+ "errors"
"fmt"
+ "io/fs"
"io/ioutil"
+ "net/http"
+ "path"
"path/filepath"
"sigs.k8s.io/yaml"
@@ -25,15 +31,58 @@
}
func (m *AppManager) Config() (Config, error) {
- return m.repoIO.ReadConfig()
+ var cfg Config
+ if err := ReadYaml(m.repoIO, configFileName, &cfg); err != nil {
+ return Config{}, err
+ } else {
+ return cfg, nil
+ }
+}
+
+func (m *AppManager) appConfig(path string) (AppConfig, error) {
+ var cfg AppConfig
+ if err := ReadYaml(m.repoIO, path, &cfg); err != nil {
+ return AppConfig{}, err
+ } else {
+ return cfg, nil
+ }
}
func (m *AppManager) FindAllInstances(name string) ([]AppConfig, error) {
- return m.repoIO.FindAllInstances(appDir, name)
+ kust, err := ReadKustomization(m.repoIO, filepath.Join(appDir, "kustomization.yaml"))
+ if err != nil {
+ return nil, err
+ }
+ ret := make([]AppConfig, 0)
+ for _, app := range kust.Resources {
+ cfg, err := m.appConfig(filepath.Join(appDir, app, "config.yaml"))
+ if err != nil {
+ return nil, err
+ }
+ cfg.Id = app
+ if cfg.AppId == name {
+ ret = append(ret, cfg)
+ }
+ }
+ return ret, nil
}
func (m *AppManager) FindInstance(id string) (AppConfig, error) {
- return m.repoIO.FindInstance(appDir, id)
+ kust, err := ReadKustomization(m.repoIO, filepath.Join(appDir, "kustomization.yaml"))
+ if err != nil {
+ return AppConfig{}, err
+ }
+ for _, app := range kust.Resources {
+ if app == id {
+ cfg, err := m.appConfig(filepath.Join(appDir, app, "config.yaml"))
+ if err != nil {
+ return AppConfig{}, err
+ }
+ cfg.Id = id
+ return cfg, nil
+ }
+ }
+ return AppConfig{}, nil
}
func (m *AppManager) AppConfig(name string) (AppConfig, error) {
@@ -51,58 +100,146 @@
return cfg, err
}
-func (m *AppManager) Install(app App, ns NamespaceGenerator, suffixGen SuffixGenerator, config map[string]any) error {
+type allocatePortReq struct {
+ Protocol string `json:"protocol"`
+ SourcePort int `json:"sourcePort"`
+ TargetService string `json:"targetService"`
+ TargetPort int `json:"targetPort"`
+}
+
+func openPorts(ports []PortForward) error {
+ for _, p := range ports {
+ var buf bytes.Buffer
+ req := allocatePortReq{
+ Protocol: p.Protocol,
+ SourcePort: p.SourcePort,
+ TargetService: p.TargetService,
+ TargetPort: p.TargetPort,
+ }
+ if err := json.NewEncoder(&buf).Encode(req); err != nil {
+ return err
+ }
+ resp, err := http.Post(p.Allocator, "application/json", &buf)
+ if err != nil {
+ return err
+ }
+ if resp.StatusCode != http.StatusOK {
+ return fmt.Errorf("Could not allocate port %d, status code: %d", p.SourcePort, resp.StatusCode)
+ }
+ }
+ return nil
+}
+
+func createKustomizationChain(r RepoFS, path string) error {
+ for p := filepath.Clean(path); p != "/"; {
+ parent, child := filepath.Split(p)
+ kustPath := filepath.Join(parent, "kustomization.yaml")
+ kust, err := ReadKustomization(r, kustPath)
+ if err != nil {
+ if errors.Is(err, fs.ErrNotExist) {
+ k := NewKustomization()
+ kust = &k
+ } else {
+ return err
+ }
+ }
+ kust.AddResources(child)
+ if err := WriteYaml(r, kustPath, kust); err != nil {
+ return err
+ }
+ p = filepath.Clean(parent)
+ }
+ return nil
+}
+
+func InstallApp(repo RepoIO, nsc NamespaceCreator, app App, appDir string, namespace string, initValues map[string]any, derived Derived) error {
+ if err := nsc.Create(namespace); err != nil {
+ return err
+ }
+ derived.Release = Release{
+ Namespace: namespace,
+ RepoAddr: repo.FullAddress(),
+ AppDir: appDir,
+ }
+ rendered, err := app.Render(derived)
+ if err != nil {
+ return err
+ }
+ if err := openPorts(rendered.Ports); err != nil {
+ return err
+ }
+ return repo.Atomic(func(r RepoFS) (string, error) {
+ if err := createKustomizationChain(r, appDir); err != nil {
+ return "", err
+ }
+ {
+ if err := r.RemoveDir(appDir); err != nil {
+ return "", err
+ }
+ if err := r.CreateDir(appDir); err != nil {
+ return "", err
+ }
+ cfg := AppConfig{
+ AppId: app.Name(),
+ Config: initValues,
+ Derived: derived,
+ }
+ if err := WriteYaml(r, path.Join(appDir, configFileName), cfg); err != nil {
+ return "", err
+ }
+ }
+ {
+ appKust := NewKustomization()
+ for name, contents := range rendered.Resources {
+ appKust.AddResources(name)
+ out, err := r.Writer(path.Join(appDir, name))
+ if err != nil {
+ return "", err
+ }
+ defer out.Close()
+ if _, err := out.Write(contents); err != nil {
+ return "", err
+ }
+ }
+ if err := WriteYaml(r, path.Join(appDir, "kustomization.yaml"), appKust); err != nil {
+ return "", err
+ }
+ }
+ return fmt.Sprintf("install: %s", app.Name()), nil
+ })
+}
+
+func (m *AppManager) Install(app App, appDir string, namespace string, values map[string]any) error {
+ appDir = filepath.Clean(appDir)
if err := m.repoIO.Pull(); err != nil {
return err
}
- suffix, err := suffixGen.Generate()
+ globalConfig, err := m.Config()
if err != nil {
return err
}
- nms, err := ns.Generate(app.Namespace())
+ derivedValues, err := deriveValues(values, app.Schema(), CreateNetworks(globalConfig))
if err != nil {
return err
}
- nms = nms + suffix
- if err := m.nsCreator.Create(nms); err != nil {
- return err
- }
- globalConfig, err := m.repoIO.ReadConfig()
- if err != nil {
- return err
- }
- derivedValues, err := deriveValues(config, app.Schema(), CreateNetworks(globalConfig))
- if err != nil {
- fmt.Println(err)
- return err
- }
derived := Derived{
Global: globalConfig.Values,
Values: derivedValues,
}
- derived.Release.Namespace = nms
- fmt.Printf("%+v\n", derived)
- err = m.repoIO.InstallApp(
- app,
- filepath.Join(appDir, app.Name()+suffix),
- config,
- derived,
- )
- fmt.Println(err)
- return err
+ return InstallApp(m.repoIO, m.nsCreator, app, appDir, namespace, values, derived)
}
func (m *AppManager) Update(app App, instanceId string, config map[string]any) error {
if err := m.repoIO.Pull(); err != nil {
return err
}
- globalConfig, err := m.repoIO.ReadConfig()
+ globalConfig, err := m.Config()
if err != nil {
return err
}
instanceDir := filepath.Join(appDir, instanceId)
instanceConfigPath := filepath.Join(instanceDir, configFileName)
- appConfig, err := m.repoIO.ReadAppConfig(instanceConfigPath)
+ appConfig, err := m.appConfig(instanceConfigPath)
if err != nil {
return err
}
@@ -115,14 +252,24 @@
Release: appConfig.Derived.Release,
Values: derivedValues,
}
- return m.repoIO.InstallApp(app, instanceDir, config, derived)
+ return InstallApp(m.repoIO, m.nsCreator, app, instanceDir, appConfig.Derived.Release.Namespace, config, derived)
}
func (m *AppManager) Remove(instanceId string) error {
if err := m.repoIO.Pull(); err != nil {
return err
}
- return m.repoIO.RemoveApp(filepath.Join(appDir, instanceId))
+ return m.repoIO.Atomic(func(r RepoFS) (string, error) {
+ r.RemoveDir(filepath.Join(appDir, instanceId))
+ kustPath := filepath.Join(appDir, "kustomization.yaml")
+ kust, err := ReadKustomization(r, kustPath)
+ if err != nil {
+ return "", err
+ }
+ kust.RemoveResources(instanceId)
+ WriteYaml(r, kustPath, kust)
+ return fmt.Sprintf("uninstall: %s", instanceId), nil
+ })
}
// TODO(gio): deduplicate with cue definition in app.go, this one should be removed.