installer-env: wait for services to be reachable
diff --git a/core/installer/tasks/infra.go b/core/installer/tasks/infra.go
index 06de0dc..995744c 100644
--- a/core/installer/tasks/infra.go
+++ b/core/installer/tasks/infra.go
@@ -7,27 +7,10 @@
"github.com/miekg/dns"
"github.com/giolekva/pcloud/core/installer"
- "github.com/giolekva/pcloud/core/installer/soft"
)
-type setupInfraAppsTask struct {
- basicTask
- env Env
- st *state
-}
-
-func (t *setupInfraAppsTask) initNewEnv(
- ss *soft.Client,
- r installer.RepoIO,
- nsCreator installer.NamespaceCreator,
- pcloudEnvName string,
- pcloudPublicIP string,
-) error {
- return nil
-}
-
-func NewSetupInfraAppsTask(env Env, st *state) Task {
- t := newLeafTask("Configure environment infrastructure", func() error {
+func SetupInfra(env Env, st *state) []Task {
+ t := newLeafTask("Create client", func() error {
repo, err := st.ssClient.GetRepo("config")
if err != nil {
return err
@@ -37,22 +20,51 @@
if err != nil {
return err
}
- appsRepo := installer.NewInMemoryAppRepository(installer.CreateAllApps())
- // 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 {
+ st.appManager = appManager
+ st.appsRepo = installer.NewInMemoryAppRepository(installer.CreateAllApps())
+ st.nsGen = installer.NewPrefixGenerator(env.Name + "-")
+ st.emptySuffixGen = installer.NewEmptySuffixGenerator()
+ return nil
+ })
+ return []Task{
+ CommitEnvironmentConfiguration(env, st),
+ &t,
+ newConcurrentParentTask(
+ "Core services",
+ SetupNetwork(env, st),
+ SetupCertificateIssuers(env, st),
+ SetupAuth(env, st),
+ SetupHeadscale(env, st),
+ SetupWelcome(env, st),
+ SetupAppStore(env, st),
+ ),
+ }
+}
+
+func CommitEnvironmentConfiguration(env Env, st *state) Task {
+ t := newLeafTask("Configure environment infrastructure", func() error {
+ repo, err := st.ssClient.GetRepo("config")
+ 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
+ }
+ }
{
out, err := r.Writer("pcloud-charts.yaml")
if err != nil {
@@ -74,29 +86,34 @@
if err != nil {
return err
}
+ rootKust, err := r.ReadKustomization("kustomization.yaml")
+ if err != nil {
+ return err
+ }
+ rootKust.AddResources("pcloud-charts.yaml")
+ if err := r.WriteKustomization("kustomization.yaml", *rootKust); err != nil {
+ return err
+ }
+ r.CommitAndPush("configure charts repo")
}
- rootKust, err := r.ReadKustomization("kustomization.yaml")
- if err != nil {
- return err
- }
- rootKust.AddResources("pcloud-charts.yaml")
- if err := r.WriteKustomization("kustomization.yaml", *rootKust); err != nil {
- return err
- }
- r.CommitAndPush("configure charts repo")
- nsGen := installer.NewPrefixGenerator(env.Name + "-")
- emptySuffixGen := installer.NewEmptySuffixGenerator()
+ return nil
+ })
+ return &t
+}
+
+func SetupNetwork(env Env, st *state) Task {
+ t := newLeafTask("Setup network", func() error {
ingressPrivateIP, err := netip.ParseAddr("10.1.0.1")
if err != nil {
return err
}
{
headscaleIP := ingressPrivateIP.Next()
- app, err := appsRepo.Find("metallb-ipaddresspool")
+ app, err := st.appsRepo.Find("metallb-ipaddresspool")
if err != nil {
return err
}
- if err := appManager.Install(*app, nsGen, installer.NewSuffixGenerator("-ingress-private"), map[string]any{
+ 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(),
@@ -105,7 +122,7 @@
}); err != nil {
return err
}
- if err := appManager.Install(*app, nsGen, installer.NewSuffixGenerator("-headscale"), map[string]any{
+ 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(),
@@ -114,7 +131,7 @@
}); err != nil {
return err
}
- if err := appManager.Install(*app, nsGen, emptySuffixGen, map[string]any{
+ if err := st.appManager.Install(*app, st.nsGen, st.emptySuffixGen, map[string]any{
"Name": env.Name,
"From": "10.1.0.100", // TODO(gio): auto-generate
"To": "10.1.0.254",
@@ -125,11 +142,11 @@
}
}
{
- app, err := appsRepo.Find("private-network")
+ app, err := st.appsRepo.Find("private-network")
if err != nil {
return err
}
- if err := appManager.Install(*app, nsGen, emptySuffixGen, map[string]any{
+ if err := st.appManager.Install(*app, st.nsGen, st.emptySuffixGen, map[string]any{
"PrivateNetwork": map[string]any{
"Hostname": "private-network-proxy",
"Username": "private-network-proxy",
@@ -139,96 +156,134 @@
return err
}
}
- {
- app, err := appsRepo.Find("certificate-issuer-public")
- if err != nil {
- return err
- }
- if err := appManager.Install(*app, nsGen, emptySuffixGen, map[string]any{}); err != nil {
- return err
- }
+ return nil
+ })
+ return &t
+}
+
+func SetupCertificateIssuers(env Env, st *state) Task {
+ pub := newLeafTask(fmt.Sprintf("Public %s", env.Domain), func() error {
+ app, err := st.appsRepo.Find("certificate-issuer-public")
+ if err != nil {
+ return err
}
- {
- app, err := appsRepo.Find("certificate-issuer-private")
- if err != nil {
- return err
- }
- if err := appManager.Install(*app, nsGen, emptySuffixGen, 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),
- },
- }); err != nil {
- return err
- }
+ if err := st.appManager.Install(*app, st.nsGen, st.emptySuffixGen, map[string]any{}); err != nil {
+ return err
}
- {
- app, err := appsRepo.Find("core-auth")
- if err != nil {
- return err
- }
- if err := appManager.Install(*app, nsGen, emptySuffixGen, map[string]any{
- "Subdomain": "test", // TODO(giolekva): make core-auth chart actually use this
- }); err != nil {
- return err
- }
+ return nil
+ })
+ priv := newLeafTask(fmt.Sprintf("Private p.%s", env.Domain), func() error {
+ app, err := st.appsRepo.Find("certificate-issuer-private")
+ if err != nil {
+ return err
}
- {
- app, err := appsRepo.Find("headscale")
- if err != nil {
- return err
- }
- if err := appManager.Install(*app, nsGen, emptySuffixGen, map[string]any{
- "Subdomain": "headscale",
- }); err != nil {
- return err
- }
+ if err := st.appManager.Install(*app, st.nsGen, st.emptySuffixGen, 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),
+ },
+ }); err != nil {
+ return err
}
- {
- keys, err := installer.NewSSHKeyPair("welcome")
- if err != nil {
- return err
- }
- user := fmt.Sprintf("%s-welcome", env.Name)
- if err := st.ssClient.AddUser(user, keys.AuthorizedKey()); err != nil {
- return err
- }
- if err := st.ssClient.AddReadWriteCollaborator("config", user); err != nil {
- return err
- }
- app, err := appsRepo.Find("welcome")
- if err != nil {
- return err
- }
- if err := appManager.Install(*app, nsGen, emptySuffixGen, map[string]any{
- "RepoAddr": st.ssClient.GetRepoAddress("config"),
- "SSHPrivateKey": string(keys.RawPrivateKey()),
- }); err != nil {
- return err
- }
+ return nil
+ })
+ return newSequentialParentTask("Configure TLS certificate issuers", &pub, &priv)
+}
+
+func SetupAuth(env Env, st *state) Task {
+ t := newLeafTask("Setup", func() error {
+ app, err := st.appsRepo.Find("core-auth")
+ if err != nil {
+ return err
}
- {
- user := fmt.Sprintf("%s-appmanager", env.Name)
- keys, err := installer.NewSSHKeyPair(user)
- if err != nil {
- return err
- }
- if err := st.ssClient.AddUser(user, keys.AuthorizedKey()); err != nil {
- return err
- }
- if err := st.ssClient.AddReadWriteCollaborator("config", user); err != nil {
- return err
- }
- app, err := appsRepo.Find("app-manager") // TODO(giolekva): configure
- if err != nil {
- return err
- }
- if err := appManager.Install(*app, nsGen, emptySuffixGen, map[string]any{
- "RepoAddr": st.ssClient.GetRepoAddress("config"),
- "SSHPrivateKey": string(keys.RawPrivateKey()),
- }); err != nil {
- return err
- }
+ if err := st.appManager.Install(*app, st.nsGen, st.emptySuffixGen, map[string]any{
+ "Subdomain": "test", // TODO(giolekva): make core-auth chart actually use this
+ }); err != nil {
+ return err
+ }
+ return nil
+ })
+ return newSequentialParentTask(
+ "Authentication services",
+ &t,
+ waitForAddr(fmt.Sprintf("https://accounts-ui.%s", env.Domain)),
+ )
+}
+
+func SetupHeadscale(env Env, st *state) Task {
+ t := newLeafTask("Setup", func() error {
+ app, err := st.appsRepo.Find("headscale")
+ if err != nil {
+ return err
+ }
+ if err := st.appManager.Install(*app, st.nsGen, st.emptySuffixGen, map[string]any{
+ "Subdomain": "headscale",
+ }); err != nil {
+ return err
+ }
+ return nil
+ })
+ return newSequentialParentTask(
+ "Headscale service",
+ &t,
+ waitForAddr(fmt.Sprintf("https://headscale.%s/apple", env.Domain)),
+ )
+}
+
+func SetupWelcome(env Env, st *state) Task {
+ t := newLeafTask("Setup", func() error {
+ keys, err := installer.NewSSHKeyPair("welcome")
+ if err != nil {
+ return err
+ }
+ user := fmt.Sprintf("%s-welcome", env.Name)
+ if err := st.ssClient.AddUser(user, keys.AuthorizedKey()); err != nil {
+ return err
+ }
+ if err := st.ssClient.AddReadWriteCollaborator("config", user); err != nil {
+ return err
+ }
+ app, err := st.appsRepo.Find("welcome")
+ if err != nil {
+ return err
+ }
+ if err := st.appManager.Install(*app, st.nsGen, st.emptySuffixGen, map[string]any{
+ "RepoAddr": st.ssClient.GetRepoAddress("config"),
+ "SSHPrivateKey": string(keys.RawPrivateKey()),
+ }); err != nil {
+ return err
+ }
+ return nil
+ })
+ return newSequentialParentTask(
+ "Welcome service",
+ &t,
+ waitForAddr(fmt.Sprintf("https://welcome.%s", env.Domain)),
+ )
+}
+
+func SetupAppStore(env Env, st *state) Task {
+ t := newLeafTask("Application marketplace", func() error {
+ user := fmt.Sprintf("%s-appmanager", env.Name)
+ keys, err := installer.NewSSHKeyPair(user)
+ if err != nil {
+ return err
+ }
+ if err := st.ssClient.AddUser(user, keys.AuthorizedKey()); err != nil {
+ return err
+ }
+ if err := st.ssClient.AddReadWriteCollaborator("config", user); err != nil {
+ return err
+ }
+ app, err := st.appsRepo.Find("app-manager") // TODO(giolekva): configure
+ if err != nil {
+ return err
+ }
+ if err := st.appManager.Install(*app, st.nsGen, st.emptySuffixGen, map[string]any{
+ "RepoAddr": st.ssClient.GetRepoAddress("config"),
+ "SSHPrivateKey": string(keys.RawPrivateKey()),
+ }); err != nil {
+ return err
}
return nil
})