Installer: Clean up RepoIO interface
Change-Id: If80d7be1460c725b7df9d1d27c9354cb9141acfe
diff --git a/core/installer/tasks/activate.go b/core/installer/tasks/activate.go
index 1333f28..dffdfbe 100644
--- a/core/installer/tasks/activate.go
+++ b/core/installer/tasks/activate.go
@@ -8,6 +8,8 @@
"path"
"strings"
"text/template"
+
+ "github.com/giolekva/pcloud/core/installer"
)
//go:embed env-tmpl
@@ -29,45 +31,43 @@
return err
}
repoHost := strings.Split(st.ssClient.Addr, ":")[0]
- kust, err := st.repo.ReadKustomization("environments/kustomization.yaml")
- if err != nil {
- return err
- }
- kust.AddResources(env.Name)
- tmpls, err := template.ParseFS(filesTmpls, "env-tmpl/*.yaml")
- if err != nil {
- return err
- }
- var knownHosts bytes.Buffer
- for _, key := range ssPublicKeys {
- fmt.Fprintf(&knownHosts, "%s %s\n", repoHost, key)
- }
- for _, tmpl := range tmpls.Templates() { // TODO(gio): migrate to cue
- dstPath := path.Join("environments", env.Name, tmpl.Name())
- dst, err := st.repo.Writer(dstPath)
+ return st.repo.Atomic(func(r installer.RepoFS) (string, error) {
+ kust, err := installer.ReadKustomization(r, "environments/kustomization.yaml")
if err != nil {
- return err
+ return "", err
}
- defer dst.Close()
-
- if err := tmpl.Execute(dst, map[string]string{
- "Name": env.Name,
- "PrivateKey": base64.StdEncoding.EncodeToString(st.keys.RawPrivateKey()),
- "PublicKey": base64.StdEncoding.EncodeToString(st.keys.RawAuthorizedKey()),
- "RepoHost": repoHost,
- "RepoName": "config",
- "KnownHosts": base64.StdEncoding.EncodeToString(knownHosts.Bytes()),
- }); err != nil {
- return err
+ kust.AddResources(env.Name)
+ tmpls, err := template.ParseFS(filesTmpls, "env-tmpl/*.yaml")
+ if err != nil {
+ return "", err
}
- }
- if err := st.repo.WriteKustomization("environments/kustomization.yaml", *kust); err != nil {
- return err
- }
- if err := st.repo.CommitAndPush(fmt.Sprintf("%s: initialize environment", env.Name)); err != nil {
- return err
- }
- return nil
+ var knownHosts bytes.Buffer
+ for _, key := range ssPublicKeys {
+ fmt.Fprintf(&knownHosts, "%s %s\n", repoHost, key)
+ }
+ for _, tmpl := range tmpls.Templates() { // TODO(gio): migrate to cue
+ dstPath := path.Join("environments", env.Name, tmpl.Name())
+ dst, err := r.Writer(dstPath)
+ if err != nil {
+ return "", err
+ }
+ defer dst.Close()
+ if err := tmpl.Execute(dst, map[string]string{
+ "Name": env.Name,
+ "PrivateKey": base64.StdEncoding.EncodeToString(st.keys.RawPrivateKey()),
+ "PublicKey": base64.StdEncoding.EncodeToString(st.keys.RawAuthorizedKey()),
+ "RepoHost": repoHost,
+ "RepoName": "config",
+ "KnownHosts": base64.StdEncoding.EncodeToString(knownHosts.Bytes()),
+ }); err != nil {
+ return "", err
+ }
+ }
+ if err := installer.WriteYaml(r, "environments/kustomization.yaml", kust); err != nil {
+ return "", err
+ }
+ return fmt.Sprintf("%s: initialize environment", env.Name), nil
+ })
})
return &t
}
diff --git a/core/installer/tasks/dns.go b/core/installer/tasks/dns.go
index 13c4b58..65ef787 100644
--- a/core/installer/tasks/dns.go
+++ b/core/installer/tasks/dns.go
@@ -38,22 +38,26 @@
st *state,
) Task {
t := newLeafTask("Generate and publish DNS records", func() error {
+ key, err := newDNSSecKey(env.Domain)
+ if err != nil {
+ return err
+ }
repo, err := st.ssClient.GetRepo("config")
if err != nil {
return err
}
- r := installer.NewRepoIO(repo, st.ssClient.Signer)
- {
- key, err := newDNSSecKey(env.Domain)
- if err != nil {
- return err
- }
- out, err := r.Writer("dns-zone.yaml")
- if err != nil {
- return err
- }
- defer out.Close()
- dnsZoneTmpl, err := template.New("config").Funcs(sprig.TxtFuncMap()).Parse(`
+ r, err := installer.NewRepoIO(repo, st.ssClient.Signer)
+ if err != nil {
+ return err
+ }
+ return r.Atomic(func(r installer.RepoFS) (string, error) {
+ {
+ out, err := r.Writer("dns-zone.yaml")
+ if err != nil {
+ return "", err
+ }
+ defer out.Close()
+ dnsZoneTmpl, err := template.New("config").Funcs(sprig.TxtFuncMap()).Parse(`
apiVersion: dodo.cloud.dodo.cloud/v1
kind: DNSZone
metadata:
@@ -86,31 +90,29 @@
private: {{ .dnssec.Private | toString | b64enc }}
ds: {{ .dnssec.DS | toString | b64enc }}
`)
- if err != nil {
- return err
+ if err != nil {
+ return "", err
+ }
+ if err := dnsZoneTmpl.Execute(out, map[string]any{
+ "namespace": env.Name,
+ "zone": env.Domain,
+ "dnssec": key,
+ "publicIPs": st.publicIPs,
+ "ingressIP": ingressIP.String(),
+ }); err != nil {
+ return "", err
+ }
+ rootKust, err := installer.ReadKustomization(r, "kustomization.yaml")
+ if err != nil {
+ return "", err
+ }
+ rootKust.AddResources("dns-zone.yaml")
+ if err := installer.WriteYaml(r, "kustomization.yaml", rootKust); err != nil {
+ return "", err
+ }
+ return "configure dns zone", nil
}
- if err := dnsZoneTmpl.Execute(out, map[string]any{
- "namespace": env.Name,
- "zone": env.Domain,
- "dnssec": key,
- "publicIPs": st.publicIPs,
- "ingressIP": ingressIP.String(),
- }); err != nil {
- return err
- }
- rootKust, err := r.ReadKustomization("kustomization.yaml")
- if err != nil {
- return err
- }
- rootKust.AddResources("dns-zone.yaml")
- if err := r.WriteKustomization("kustomization.yaml", *rootKust); err != nil {
- return err
- }
- if err := r.CommitAndPush("configure dns zone"); err != nil {
- return err
- }
- }
- return nil
+ })
})
return &t
}
diff --git a/core/installer/tasks/env.go b/core/installer/tasks/env.go
index 1d1eaba..7eaf0da 100644
--- a/core/installer/tasks/env.go
+++ b/core/installer/tasks/env.go
@@ -12,26 +12,25 @@
)
type state struct {
- infoListener EnvInfoListener
- publicIPs []net.IP
- nsCreator installer.NamespaceCreator
- repo installer.RepoIO
- ssAdminKeys *keygen.KeyPair
- ssClient *soft.Client
- fluxUserName string
- keys *keygen.KeyPair
- appManager *installer.AppManager
- appsRepo installer.AppRepository
- nsGen installer.NamespaceGenerator
- emptySuffixGen installer.SuffixGenerator
+ infoListener EnvInfoListener
+ publicIPs []net.IP
+ nsCreator installer.NamespaceCreator
+ repo installer.RepoIO
+ ssAdminKeys *keygen.KeyPair
+ ssClient *soft.Client
+ fluxUserName string
+ keys *keygen.KeyPair
+ appManager *installer.AppManager
+ appsRepo installer.AppRepository
}
type Env struct {
- PCloudEnvName string
- Name string
- ContactEmail string
- Domain string
- AdminPublicKey string
+ PCloudEnvName string
+ Name string
+ ContactEmail string
+ Domain string
+ AdminPublicKey string
+ NamespacePrefix string
}
type EnvInfoListener func(string)
diff --git a/core/installer/tasks/infra.go b/core/installer/tasks/infra.go
index 914307c..d718428 100644
--- a/core/installer/tasks/infra.go
+++ b/core/installer/tasks/infra.go
@@ -19,15 +19,16 @@
if err != nil {
return err
}
- r := installer.NewRepoIO(repo, st.ssClient.Signer)
+ r, err := installer.NewRepoIO(repo, st.ssClient.Signer)
+ if err != nil {
+ return err
+ }
appManager, err := installer.NewAppManager(r, st.nsCreator)
if err != nil {
return err
}
st.appManager = appManager
st.appsRepo = installer.NewInMemoryAppRepository(installer.CreateAllApps())
- st.nsGen = installer.NewPrefixGenerator(env.Name + "-")
- st.emptySuffixGen = installer.NewEmptySuffixGenerator()
return nil
})
t.beforeStart = func() {
@@ -56,28 +57,31 @@
if err != nil {
return err
}
- r := installer.NewRepoIO(repo, st.ssClient.Signer)
- {
- // TODO(giolekva): private domain can be configurable as well
- config := installer.Config{
- Values: installer.Values{
- PCloudEnvName: env.PCloudEnvName,
- Id: env.Name,
- ContactEmail: env.ContactEmail,
- Domain: env.Domain,
- PrivateDomain: fmt.Sprintf("p.%s", env.Domain),
- PublicIP: st.publicIPs[0].String(),
- NamespacePrefix: fmt.Sprintf("%s-", env.Name),
- },
- }
- if err := r.WriteYaml("config.yaml", config); err != nil {
- return err
- }
+ r, err := installer.NewRepoIO(repo, st.ssClient.Signer)
+ if err != nil {
+ return err
}
- {
+ r.Atomic(func(r installer.RepoFS) (string, error) {
+ {
+ // TODO(giolekva): private domain can be configurable as well
+ config := installer.Config{
+ Values: installer.Values{
+ PCloudEnvName: env.PCloudEnvName,
+ Id: env.Name,
+ ContactEmail: env.ContactEmail,
+ Domain: env.Domain,
+ PrivateDomain: fmt.Sprintf("p.%s", env.Domain),
+ PublicIP: st.publicIPs[0].String(),
+ NamespacePrefix: fmt.Sprintf("%s-", env.Name),
+ },
+ }
+ if err := installer.WriteYaml(r, "config.yaml", config); err != nil {
+ return "", err
+ }
+ }
out, err := r.Writer("pcloud-charts.yaml")
if err != nil {
- return err
+ return "", err
}
defer out.Close()
_, err = fmt.Fprintf(out, `
@@ -93,18 +97,18 @@
branch: ingress-port-allocator
`, env.Name)
if err != nil {
- return err
+ return "", err
}
- rootKust, err := r.ReadKustomization("kustomization.yaml")
+ rootKust, err := installer.ReadKustomization(r, "kustomization.yaml")
if err != nil {
- return err
+ return "", err
}
rootKust.AddResources("pcloud-charts.yaml")
- if err := r.WriteKustomization("kustomization.yaml", *rootKust); err != nil {
- return err
+ if err := installer.WriteYaml(r, "kustomization.yaml", rootKust); err != nil {
+ return "", err
}
- r.CommitAndPush("configure charts repo")
- }
+ return "configure charts repo", nil
+ })
return nil
})
return &t
@@ -121,12 +125,17 @@
if err != nil {
return err
}
- r := installer.NewRepoIO(repo, st.ssClient.Signer)
- fa := firstAccount{false, initGroups}
- if err := r.WriteYaml("first-account.yaml", fa); err != nil {
+ r, err := installer.NewRepoIO(repo, st.ssClient.Signer)
+ if err != nil {
return err
}
- return r.CommitAndPush("first account membership configuration")
+ return r.Atomic(func(r installer.RepoFS) (string, error) {
+ fa := firstAccount{false, initGroups}
+ if err := installer.WriteYaml(r, "first-account.yaml", fa); err != nil {
+ return "", err
+ }
+ return "first account membership configuration", nil
+ })
})
return &t
}
@@ -161,32 +170,44 @@
if err != nil {
return err
}
- if err := st.appManager.Install(app, st.nsGen, installer.NewSuffixGenerator("-ingress-private"), map[string]any{
- "name": fmt.Sprintf("%s-ingress-private", env.Name),
- "from": ingressPrivateIP.String(),
- "to": ingressPrivateIP.String(),
- "autoAssign": false,
- "namespace": "metallb-system",
- }); err != nil {
- return err
+ {
+ appDir := fmt.Sprintf("/apps/%s-ingress-private", app.Name())
+ namespace := fmt.Sprintf("%s%s-ingress-private", env.NamespacePrefix, app.Namespace())
+ if err := st.appManager.Install(app, appDir, namespace, map[string]any{
+ "name": fmt.Sprintf("%s-ingress-private", env.Name),
+ "from": ingressPrivateIP.String(),
+ "to": ingressPrivateIP.String(),
+ "autoAssign": false,
+ "namespace": "metallb-system",
+ }); err != nil {
+ return err
+ }
}
- if err := st.appManager.Install(app, st.nsGen, installer.NewSuffixGenerator("-headscale"), map[string]any{
- "name": fmt.Sprintf("%s-headscale", env.Name),
- "from": headscaleIP.String(),
- "to": headscaleIP.String(),
- "autoAssign": false,
- "namespace": "metallb-system",
- }); err != nil {
- return err
+ {
+ appDir := fmt.Sprintf("/apps/%s-headscale", app.Name())
+ namespace := fmt.Sprintf("%s%s-ingress-private", env.NamespacePrefix, app.Namespace())
+ if err := st.appManager.Install(app, appDir, namespace, map[string]any{
+ "name": fmt.Sprintf("%s-headscale", env.Name),
+ "from": headscaleIP.String(),
+ "to": headscaleIP.String(),
+ "autoAssign": false,
+ "namespace": "metallb-system",
+ }); err != nil {
+ return err
+ }
}
- if err := st.appManager.Install(app, st.nsGen, st.emptySuffixGen, map[string]any{
- "name": env.Name,
- "from": fromIP.String(),
- "to": toIP.String(),
- "autoAssign": false,
- "namespace": "metallb-system",
- }); err != nil {
- return err
+ {
+ appDir := fmt.Sprintf("/apps/%s", app.Name())
+ namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
+ if err := st.appManager.Install(app, appDir, namespace, map[string]any{
+ "name": env.Name,
+ "from": fromIP.String(),
+ "to": toIP.String(),
+ "autoAssign": false,
+ "namespace": "metallb-system",
+ }); err != nil {
+ return err
+ }
}
}
{
@@ -205,7 +226,9 @@
if err != nil {
return err
}
- if err := st.appManager.Install(app, st.nsGen, st.emptySuffixGen, map[string]any{
+ appDir := fmt.Sprintf("/apps/%s", app.Name())
+ namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
+ if err := st.appManager.Install(app, appDir, namespace, map[string]any{
"privateNetwork": map[string]any{
"hostname": "private-network-proxy",
"username": "private-network-proxy",
@@ -227,7 +250,9 @@
if err != nil {
return err
}
- if err := st.appManager.Install(app, st.nsGen, st.emptySuffixGen, map[string]any{}); err != nil {
+ appDir := fmt.Sprintf("/apps/%s", app.Name())
+ namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
+ if err := st.appManager.Install(app, appDir, namespace, map[string]any{}); err != nil {
return err
}
return nil
@@ -237,7 +262,9 @@
if err != nil {
return err
}
- if err := st.appManager.Install(app, st.nsGen, st.emptySuffixGen, map[string]any{
+ appDir := fmt.Sprintf("/apps/%s", app.Name())
+ namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
+ if err := st.appManager.Install(app, appDir, namespace, map[string]any{
"apiConfigMap": map[string]any{
"name": "api-config", // TODO(gio): take from global pcloud config
"namespace": fmt.Sprintf("%s-dns-zone-manager", env.PCloudEnvName),
@@ -256,7 +283,9 @@
if err != nil {
return err
}
- if err := st.appManager.Install(app, st.nsGen, st.emptySuffixGen, map[string]any{
+ appDir := fmt.Sprintf("/apps/%s", app.Name())
+ namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
+ if err := st.appManager.Install(app, appDir, namespace, map[string]any{
"subdomain": "test", // TODO(giolekva): make core-auth chart actually use this
}); err != nil {
return err
@@ -277,7 +306,9 @@
if err != nil {
return err
}
- if err := st.appManager.Install(app, st.nsGen, st.emptySuffixGen, map[string]any{
+ appDir := fmt.Sprintf("/apps/%s", app.Name())
+ namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
+ if err := st.appManager.Install(app, appDir, namespace, map[string]any{
"authGroups": strings.Join(initGroups, ","),
}); err != nil {
return err
@@ -298,7 +329,9 @@
if err != nil {
return err
}
- if err := st.appManager.Install(app, st.nsGen, st.emptySuffixGen, map[string]any{
+ appDir := fmt.Sprintf("/apps/%s", app.Name())
+ namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
+ if err := st.appManager.Install(app, appDir, namespace, map[string]any{
"subdomain": "headscale",
"ipSubnet": fmt.Sprintf("%s/24", startIP),
}); err != nil {
@@ -331,7 +364,9 @@
if err != nil {
return err
}
- if err := st.appManager.Install(app, st.nsGen, st.emptySuffixGen, map[string]any{
+ appDir := fmt.Sprintf("/apps/%s", app.Name())
+ namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
+ if err := st.appManager.Install(app, appDir, namespace, map[string]any{
"repoAddr": st.ssClient.GetRepoAddress("config"),
"sshPrivateKey": string(keys.RawPrivateKey()),
}); err != nil {
@@ -364,7 +399,9 @@
if err != nil {
return err
}
- if err := st.appManager.Install(app, st.nsGen, st.emptySuffixGen, map[string]any{
+ appDir := fmt.Sprintf("/apps/%s", app.Name())
+ namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
+ if err := st.appManager.Install(app, appDir, namespace, map[string]any{
"repoAddr": st.ssClient.GetRepoAddress("config"),
"sshPrivateKey": string(keys.RawPrivateKey()),
"authGroups": strings.Join(initGroups, ","),
diff --git a/core/installer/tasks/init.go b/core/installer/tasks/init.go
index cb546c1..4ed7176 100644
--- a/core/installer/tasks/init.go
+++ b/core/installer/tasks/init.go
@@ -51,28 +51,18 @@
if err != nil {
return err
}
- ssValues := map[string]any{
- "privateKey": string(ssKeys.RawPrivateKey()),
- "publicKey": string(ssKeys.RawAuthorizedKey()),
- "adminKey": string(ssAdminKeys.RawAuthorizedKey()),
- }
derived := installer.Derived{
Global: installer.Values{
Id: env.Name,
PCloudEnvName: env.PCloudEnvName,
},
- Release: installer.Release{
- Namespace: env.Name,
+ Values: map[string]any{
+ "privateKey": string(ssKeys.RawPrivateKey()),
+ "publicKey": string(ssKeys.RawAuthorizedKey()),
+ "adminKey": string(ssAdminKeys.RawAuthorizedKey()),
},
- Values: ssValues,
}
- if err := st.nsCreator.Create(env.Name); err != nil {
- return err
- }
- if err := st.repo.InstallApp(ssApp, filepath.Join("/environments", env.Name, "config-repo"), ssValues, derived); err != nil {
- return err
- }
- return nil
+ return installer.InstallApp(st.repo, st.nsCreator, ssApp, filepath.Join("/environments", env.Name, "config-repo"), env.Name, derived.Values, derived)
})
return &t
}
@@ -116,24 +106,24 @@
if err != nil {
return err
}
- repoIO := installer.NewRepoIO(repo, st.ssClient.Signer)
- if err := func() error {
- w, err := repoIO.Writer("README.md")
+ repoIO, err := installer.NewRepoIO(repo, st.ssClient.Signer)
+ if err != nil {
+ return err
+ }
+ if err := repoIO.Atomic(func(r installer.RepoFS) (string, error) {
+ w, err := r.Writer("README.md")
if err != nil {
- return err
+ return "", err
}
defer w.Close()
if _, err := fmt.Fprintf(w, "# %s PCloud environment", env.Name); err != nil {
- return err
+ return "", err
}
- return nil
- }(); err != nil {
- return err
- }
- if err := repoIO.WriteKustomization("kustomization.yaml", installer.NewKustomization()); err != nil {
- return err
- }
- if err := repoIO.CommitAndPush("init"); err != nil {
+ if err := installer.WriteYaml(r, "kustomization.yaml", installer.NewKustomization()); err != nil {
+ return "", err
+ }
+ return "init", nil
+ }); err != nil {
return err
}
if err := st.ssClient.AddUser(st.fluxUserName, keys.AuthorizedKey()); err != nil {