blob: 9d0c011b8b30535b35c699bd1e33bc03d0673064 [file] [log] [blame]
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +04001package tasks
2
3import (
4 "fmt"
Giorgi Lekveishvilid542b732024-03-25 18:17:39 +04005 "strings"
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +04006
7 "github.com/miekg/dns"
8
9 "github.com/giolekva/pcloud/core/installer"
gioe72b54f2024-04-22 10:44:41 +040010 "github.com/giolekva/pcloud/core/installer/soft"
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +040011)
12
Giorgi Lekveishvilid542b732024-03-25 18:17:39 +040013var initGroups = []string{"admin"}
14
gioe72b54f2024-04-22 10:44:41 +040015func CreateRepoClient(env installer.EnvConfig, st *state) Task {
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +040016 t := newLeafTask("Create repo client", func() error {
gioe72b54f2024-04-22 10:44:41 +040017 r, err := st.ssClient.GetRepo("config")
gio3af43942024-04-16 08:13:50 +040018 if err != nil {
19 return err
20 }
giof8843412024-05-22 16:38:05 +040021 appManager, err := installer.NewAppManager(r, st.nsCreator, st.jc, st.hf, "/apps")
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +040022 if err != nil {
23 return err
24 }
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +040025 st.appManager = appManager
26 st.appsRepo = installer.NewInMemoryAppRepository(installer.CreateAllApps())
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +040027 return nil
28 })
Giorgi Lekveishviliab7ff6e2024-03-29 13:11:30 +040029 t.beforeStart = func() {
30 st.infoListener("Setting up core infrastructure services.")
31 }
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +040032 return &t
33}
34
gioe72b54f2024-04-22 10:44:41 +040035func SetupInfra(env installer.EnvConfig, st *state) Task {
gio7841f4f2024-07-26 19:53:49 +040036 tasks := []Task{
gioe72b54f2024-04-22 10:44:41 +040037 SetupNetwork(env, st),
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +040038 SetupCertificateIssuers(env, st),
39 SetupAuth(env, st),
40 SetupGroupMemberships(env, st),
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +040041 SetupWelcome(env, st),
42 SetupAppStore(env, st),
Davit Tabidze56f86a42024-04-09 19:15:25 +040043 SetupLauncher(env, st),
gio7841f4f2024-07-26 19:53:49 +040044 }
45 if env.PrivateDomain != "" {
46 tasks = append(tasks, SetupHeadscale(env, st))
47 }
48 return newConcurrentParentTask(
49 "Setup core services",
50 true,
51 tasks...,
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +040052 )
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +040053}
54
gioe72b54f2024-04-22 10:44:41 +040055func CommitEnvironmentConfiguration(env installer.EnvConfig, st *state) Task {
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +040056 t := newLeafTask("commit config", func() error {
gioe72b54f2024-04-22 10:44:41 +040057 r, err := st.ssClient.GetRepo("config")
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +040058 if err != nil {
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +040059 return err
60 }
gioe72b54f2024-04-22 10:44:41 +040061 r.Do(func(r soft.RepoFS) (string, error) {
62 if err := soft.WriteYaml(r, "config.yaml", env); err != nil {
63 return "", err
gio3af43942024-04-16 08:13:50 +040064 }
gioe72b54f2024-04-22 10:44:41 +040065 rootKust, err := soft.ReadKustomization(r, "kustomization.yaml")
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +040066 if err != nil {
gio3af43942024-04-16 08:13:50 +040067 return "", err
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +040068 }
gioe72b54f2024-04-22 10:44:41 +040069 if err := soft.WriteYaml(r, "kustomization.yaml", rootKust); err != nil {
gio3af43942024-04-16 08:13:50 +040070 return "", err
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +040071 }
gio3af43942024-04-16 08:13:50 +040072 return "configure charts repo", nil
73 })
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +040074 return nil
75 })
76 return &t
77}
78
Giorgi Lekveishvilid542b732024-03-25 18:17:39 +040079type firstAccount struct {
80 Created bool `json:"created"`
gio2728e402024-08-01 18:14:21 +040081 Domain string `json:"domain"`
Giorgi Lekveishvilid542b732024-03-25 18:17:39 +040082 Groups []string `json:"groups"`
83}
84
gioe72b54f2024-04-22 10:44:41 +040085func ConfigureFirstAccount(env installer.EnvConfig, st *state) Task {
Giorgi Lekveishvilid542b732024-03-25 18:17:39 +040086 t := newLeafTask("Configure first account settings", func() error {
gioe72b54f2024-04-22 10:44:41 +040087 r, err := st.ssClient.GetRepo("config")
Giorgi Lekveishvilid542b732024-03-25 18:17:39 +040088 if err != nil {
89 return err
90 }
giob4a3a192024-08-19 09:55:47 +040091 _, err = r.Do(func(r soft.RepoFS) (string, error) {
gio2728e402024-08-01 18:14:21 +040092 fa := firstAccount{false, env.Domain, initGroups}
gioe72b54f2024-04-22 10:44:41 +040093 if err := soft.WriteYaml(r, "first-account.yaml", fa); err != nil {
gio3af43942024-04-16 08:13:50 +040094 return "", err
95 }
96 return "first account membership configuration", nil
97 })
giob4a3a192024-08-19 09:55:47 +040098 return err
Giorgi Lekveishvilid542b732024-03-25 18:17:39 +040099 })
100 return &t
101}
102
gioe72b54f2024-04-22 10:44:41 +0400103func SetupNetwork(env installer.EnvConfig, st *state) Task {
gio7841f4f2024-07-26 19:53:49 +0400104 t := newLeafTask("Setup networks", func() error {
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400105 {
gio3cdee592024-04-17 10:15:56 +0400106 app, err := installer.FindEnvApp(st.appsRepo, "metallb-ipaddresspool")
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400107 if err != nil {
108 return err
109 }
gio3af43942024-04-16 08:13:50 +0400110 {
gio44f621b2024-04-29 09:44:38 +0400111 instanceId := fmt.Sprintf("%s-ingress-private", app.Slug())
gio3cdee592024-04-17 10:15:56 +0400112 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400113 namespace := fmt.Sprintf("%s%s-ingress-private", env.NamespacePrefix, app.Namespace())
gio778577f2024-04-29 09:44:38 +0400114 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
gioe72b54f2024-04-22 10:44:41 +0400115 "name": fmt.Sprintf("%s-ingress-private", env.Id),
116 "from": env.Network.Ingress.String(),
117 "to": env.Network.Ingress.String(),
gio3af43942024-04-16 08:13:50 +0400118 "autoAssign": false,
119 "namespace": "metallb-system",
120 }); err != nil {
121 return err
122 }
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400123 }
gio3af43942024-04-16 08:13:50 +0400124 {
gio44f621b2024-04-29 09:44:38 +0400125 instanceId := fmt.Sprintf("%s-headscale", app.Slug())
gio3cdee592024-04-17 10:15:56 +0400126 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400127 namespace := fmt.Sprintf("%s%s-ingress-private", env.NamespacePrefix, app.Namespace())
gio778577f2024-04-29 09:44:38 +0400128 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
gioe72b54f2024-04-22 10:44:41 +0400129 "name": fmt.Sprintf("%s-headscale", env.Id),
130 "from": env.Network.Headscale.String(),
131 "to": env.Network.Headscale.String(),
gio3af43942024-04-16 08:13:50 +0400132 "autoAssign": false,
133 "namespace": "metallb-system",
134 }); err != nil {
135 return err
136 }
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400137 }
gio3af43942024-04-16 08:13:50 +0400138 {
gio44f621b2024-04-29 09:44:38 +0400139 instanceId := app.Slug()
gio3cdee592024-04-17 10:15:56 +0400140 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400141 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
gio778577f2024-04-29 09:44:38 +0400142 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
gioe72b54f2024-04-22 10:44:41 +0400143 "name": env.Id,
144 "from": env.Network.ServicesFrom.String(),
145 "to": env.Network.ServicesTo.String(),
gio3af43942024-04-16 08:13:50 +0400146 "autoAssign": false,
147 "namespace": "metallb-system",
148 }); err != nil {
149 return err
150 }
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400151 }
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +0400152 }
gio7841f4f2024-07-26 19:53:49 +0400153 if env.PrivateDomain != "" {
Giorgi Lekveishvilib59b7c22024-04-03 22:17:50 +0400154 keys, err := installer.NewSSHKeyPair("port-allocator")
155 if err != nil {
156 return err
157 }
gioe72b54f2024-04-22 10:44:41 +0400158 user := fmt.Sprintf("%s-port-allocator", env.Id)
Giorgi Lekveishvilib59b7c22024-04-03 22:17:50 +0400159 if err := st.ssClient.AddUser(user, keys.AuthorizedKey()); err != nil {
160 return err
161 }
162 if err := st.ssClient.AddReadWriteCollaborator("config", user); err != nil {
163 return err
164 }
gio3cdee592024-04-17 10:15:56 +0400165 app, err := installer.FindEnvApp(st.appsRepo, "private-network")
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400166 if err != nil {
167 return err
168 }
gio44f621b2024-04-29 09:44:38 +0400169 instanceId := app.Slug()
gio3cdee592024-04-17 10:15:56 +0400170 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400171 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
gio778577f2024-04-29 09:44:38 +0400172 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +0400173 "privateNetwork": map[string]any{
174 "hostname": "private-network-proxy",
175 "username": "private-network-proxy",
gioe72b54f2024-04-22 10:44:41 +0400176 "ipSubnet": fmt.Sprintf("%s.0/24", strings.Join(strings.Split(env.Network.DNS.String(), ".")[:3], ".")),
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400177 },
Giorgi Lekveishvilib59b7c22024-04-03 22:17:50 +0400178 "sshPrivateKey": string(keys.RawPrivateKey()),
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400179 }); err != nil {
180 return err
181 }
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +0400182 }
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400183 return nil
184 })
185 return &t
186}
187
gioe72b54f2024-04-22 10:44:41 +0400188func SetupCertificateIssuers(env installer.EnvConfig, st *state) Task {
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400189 pub := newLeafTask(fmt.Sprintf("Public %s", env.Domain), func() error {
gio3cdee592024-04-17 10:15:56 +0400190 app, err := installer.FindEnvApp(st.appsRepo, "certificate-issuer-public")
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400191 if err != nil {
192 return err
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +0400193 }
gio44f621b2024-04-29 09:44:38 +0400194 instanceId := app.Slug()
gio3cdee592024-04-17 10:15:56 +0400195 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400196 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
gio7841f4f2024-07-26 19:53:49 +0400197 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
198 "network": "Public",
199 }); err != nil {
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400200 return err
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +0400201 }
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400202 return nil
203 })
gio7841f4f2024-07-26 19:53:49 +0400204 tasks := []Task{&pub}
205 if env.PrivateDomain != "" {
206 priv := newLeafTask(fmt.Sprintf("Private p.%s", env.Domain), func() error {
207 app, err := installer.FindEnvApp(st.appsRepo, "certificate-issuer-private")
208 if err != nil {
209 return err
210 }
211 instanceId := app.Slug()
212 appDir := fmt.Sprintf("/apps/%s", instanceId)
213 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
214 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{}); err != nil {
215 return err
216 }
217 return nil
218 })
219 tasks = append(tasks, &priv)
220 }
221 return newSequentialParentTask("Configure TLS certificate issuers", false, tasks...)
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400222}
223
gioe72b54f2024-04-22 10:44:41 +0400224func SetupAuth(env installer.EnvConfig, st *state) Task {
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400225 t := newLeafTask("Setup", func() error {
gio3cdee592024-04-17 10:15:56 +0400226 app, err := installer.FindEnvApp(st.appsRepo, "core-auth")
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400227 if err != nil {
228 return err
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +0400229 }
gio44f621b2024-04-29 09:44:38 +0400230 instanceId := app.Slug()
gio3cdee592024-04-17 10:15:56 +0400231 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400232 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
gio778577f2024-04-29 09:44:38 +0400233 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
gio7841f4f2024-07-26 19:53:49 +0400234 "network": "Public",
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +0400235 "subdomain": "test", // TODO(giolekva): make core-auth chart actually use this
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400236 }); err != nil {
237 return err
238 }
239 return nil
240 })
241 return newSequentialParentTask(
242 "Authentication services",
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400243 false,
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400244 &t,
gioe72b54f2024-04-22 10:44:41 +0400245 waitForAddr(st.httpClient, fmt.Sprintf("https://accounts-ui.%s", env.Domain)),
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400246 )
247}
248
gioe72b54f2024-04-22 10:44:41 +0400249func SetupGroupMemberships(env installer.EnvConfig, st *state) Task {
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400250 t := newLeafTask("Setup", func() error {
gio3cdee592024-04-17 10:15:56 +0400251 app, err := installer.FindEnvApp(st.appsRepo, "memberships")
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400252 if err != nil {
253 return err
254 }
gio44f621b2024-04-29 09:44:38 +0400255 instanceId := app.Slug()
gio3cdee592024-04-17 10:15:56 +0400256 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400257 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
gio7841f4f2024-07-26 19:53:49 +0400258 network := "Public"
259 if env.PrivateDomain != "" {
260 network = "Private"
261 }
gio778577f2024-04-29 09:44:38 +0400262 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
gio7841f4f2024-07-26 19:53:49 +0400263 "network": network,
Giorgi Lekveishvilid542b732024-03-25 18:17:39 +0400264 "authGroups": strings.Join(initGroups, ","),
265 }); err != nil {
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400266 return err
267 }
268 return nil
269 })
giob79db3a2024-08-01 14:20:42 +0400270 var addr string
271 if env.PrivateDomain != "" {
272 addr = fmt.Sprintf("https://memberships.%s", env.PrivateDomain)
273 } else {
274 addr = fmt.Sprintf("https://memberships.%s", env.Domain)
275 }
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400276 return newSequentialParentTask(
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400277 "Group membership",
278 false,
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400279 &t,
giob79db3a2024-08-01 14:20:42 +0400280 waitForAddr(st.httpClient, addr),
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400281 )
282}
283
gio09a3e5b2024-04-26 14:11:06 +0400284func SetupLauncher(env installer.EnvConfig, st *state) Task {
285 t := newLeafTask("Setup", func() error {
286 user := fmt.Sprintf("%s-launcher", env.Id)
287 keys, err := installer.NewSSHKeyPair(user)
288 if err != nil {
289 return err
290 }
291 if err := st.ssClient.AddUser(user, keys.AuthorizedKey()); err != nil {
292 return err
293 }
294 if err := st.ssClient.AddReadWriteCollaborator("config", user); err != nil {
295 return err
296 }
297 app, err := installer.FindEnvApp(st.appsRepo, "launcher")
298 if err != nil {
299 return err
300 }
301 instanceId := app.Slug()
302 appDir := fmt.Sprintf("/apps/%s", instanceId)
303 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
304 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
gio7841f4f2024-07-26 19:53:49 +0400305 "network": "Public",
gio09a3e5b2024-04-26 14:11:06 +0400306 "repoAddr": st.ssClient.GetRepoAddress("config"),
307 "sshPrivateKey": string(keys.RawPrivateKey()),
308 }); err != nil {
309 return err
310 }
311 return nil
312 })
313 return newSequentialParentTask(
314 "Launcher",
315 false,
316 &t,
317 waitForAddr(st.httpClient, fmt.Sprintf("https://launcher.%s", env.Domain)),
318 )
319}
320
gioe72b54f2024-04-22 10:44:41 +0400321func SetupHeadscale(env installer.EnvConfig, st *state) Task {
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400322 t := newLeafTask("Setup", func() error {
gio3cdee592024-04-17 10:15:56 +0400323 app, err := installer.FindEnvApp(st.appsRepo, "headscale")
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400324 if err != nil {
325 return err
326 }
gio44f621b2024-04-29 09:44:38 +0400327 instanceId := app.Slug()
gio3cdee592024-04-17 10:15:56 +0400328 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400329 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
gio778577f2024-04-29 09:44:38 +0400330 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
gio7841f4f2024-07-26 19:53:49 +0400331 "network": "Public",
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +0400332 "subdomain": "headscale",
gioe72b54f2024-04-22 10:44:41 +0400333 "ipSubnet": fmt.Sprintf("%s/24", env.Network.DNS.String()),
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400334 }); err != nil {
335 return err
336 }
337 return nil
338 })
339 return newSequentialParentTask(
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400340 "Setup mesh VPN",
341 false,
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400342 &t,
gioe72b54f2024-04-22 10:44:41 +0400343 waitForAddr(st.httpClient, fmt.Sprintf("https://headscale.%s/apple", env.Domain)),
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400344 )
345}
346
gioe72b54f2024-04-22 10:44:41 +0400347func SetupWelcome(env installer.EnvConfig, st *state) Task {
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400348 t := newLeafTask("Setup", func() error {
349 keys, err := installer.NewSSHKeyPair("welcome")
350 if err != nil {
351 return err
352 }
gioe72b54f2024-04-22 10:44:41 +0400353 user := fmt.Sprintf("%s-welcome", env.Id)
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400354 if err := st.ssClient.AddUser(user, keys.AuthorizedKey()); err != nil {
355 return err
356 }
357 if err := st.ssClient.AddReadWriteCollaborator("config", user); err != nil {
358 return err
359 }
gio3cdee592024-04-17 10:15:56 +0400360 app, err := installer.FindEnvApp(st.appsRepo, "welcome")
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400361 if err != nil {
362 return err
363 }
gio44f621b2024-04-29 09:44:38 +0400364 instanceId := app.Slug()
gio3cdee592024-04-17 10:15:56 +0400365 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400366 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
gio778577f2024-04-29 09:44:38 +0400367 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
gio7841f4f2024-07-26 19:53:49 +0400368 "network": "Public",
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +0400369 "repoAddr": st.ssClient.GetRepoAddress("config"),
370 "sshPrivateKey": string(keys.RawPrivateKey()),
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400371 }); err != nil {
372 return err
373 }
374 return nil
375 })
376 return newSequentialParentTask(
377 "Welcome service",
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400378 false,
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400379 &t,
gioe72b54f2024-04-22 10:44:41 +0400380 waitForAddr(st.httpClient, fmt.Sprintf("https://welcome.%s", env.Domain)),
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400381 )
382}
383
gioe72b54f2024-04-22 10:44:41 +0400384func SetupAppStore(env installer.EnvConfig, st *state) Task {
giob79db3a2024-08-01 14:20:42 +0400385 t := newLeafTask("Setup", func() error {
gioe72b54f2024-04-22 10:44:41 +0400386 user := fmt.Sprintf("%s-appmanager", env.Id)
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400387 keys, err := installer.NewSSHKeyPair(user)
388 if err != nil {
389 return err
390 }
391 if err := st.ssClient.AddUser(user, keys.AuthorizedKey()); err != nil {
392 return err
393 }
394 if err := st.ssClient.AddReadWriteCollaborator("config", user); err != nil {
395 return err
396 }
gio3cdee592024-04-17 10:15:56 +0400397 app, err := installer.FindEnvApp(st.appsRepo, "app-manager") // TODO(giolekva): configure
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400398 if err != nil {
399 return err
400 }
gio44f621b2024-04-29 09:44:38 +0400401 instanceId := app.Slug()
gio3cdee592024-04-17 10:15:56 +0400402 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400403 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
gio7841f4f2024-07-26 19:53:49 +0400404 network := "Public"
405 if env.PrivateDomain != "" {
406 network = "Private"
407 }
gio778577f2024-04-29 09:44:38 +0400408 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
gio7841f4f2024-07-26 19:53:49 +0400409 "network": network,
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +0400410 "repoAddr": st.ssClient.GetRepoAddress("config"),
411 "sshPrivateKey": string(keys.RawPrivateKey()),
Giorgi Lekveishvili3c91e8b2024-03-25 20:20:14 +0400412 "authGroups": strings.Join(initGroups, ","),
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400413 }); err != nil {
414 return err
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +0400415 }
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400416 return nil
417 })
giob79db3a2024-08-01 14:20:42 +0400418 var addr string
419 if env.PrivateDomain != "" {
420 addr = fmt.Sprintf("https://apps.%s", env.PrivateDomain)
421 } else {
422 addr = fmt.Sprintf("https://apps.%s", env.Domain)
423 }
424 return newSequentialParentTask(
425 "Application marketplace",
426 false,
427 &t,
428 waitForAddr(st.httpClient, addr),
429 )
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +0400430}
431
gioe72b54f2024-04-22 10:44:41 +0400432// TODO(gio-dns): remove
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +0400433type DNSSecKey struct {
434 Basename string `json:"basename,omitempty"`
435 Key []byte `json:"key,omitempty"`
436 Private []byte `json:"private,omitempty"`
437 DS []byte `json:"ds,omitempty"`
438}
439
440func newDNSSecKey(zone string) (DNSSecKey, error) {
441 key := &dns.DNSKEY{
442 Hdr: dns.RR_Header{Name: dns.Fqdn(zone), Class: dns.ClassINET, Ttl: 3600, Rrtype: dns.TypeDNSKEY},
443 Algorithm: dns.ECDSAP256SHA256, Flags: 257, Protocol: 3,
444 }
445 priv, err := key.Generate(256)
446 if err != nil {
447 return DNSSecKey{}, err
448 }
449 return DNSSecKey{
450 Basename: fmt.Sprintf("K%s+%03d+%05d", key.Header().Name, key.Algorithm, key.KeyTag()),
451 Key: []byte(key.String()),
452 Private: []byte(key.PrivateKeyString(priv)),
453 DS: []byte(key.ToDS(dns.SHA256).String()),
454 }, nil
455}