blob: 907bba09cb260a05f5be72717673b6d2583060c7 [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
gio134be722025-07-20 19:01:17 +040013type group struct {
14 Id string `json:"id"`
15 Title string `json:"title"`
16 Description string `json:"description"`
17}
18
19var initGroups = []group{
20 group{
21 "admin",
22 "Admin",
23 "Administrators",
24 },
25}
Giorgi Lekveishvilid542b732024-03-25 18:17:39 +040026
gioe72b54f2024-04-22 10:44:41 +040027func CreateRepoClient(env installer.EnvConfig, st *state) Task {
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +040028 t := newLeafTask("Create repo client", func() error {
gioe72b54f2024-04-22 10:44:41 +040029 r, err := st.ssClient.GetRepo("config")
gio3af43942024-04-16 08:13:50 +040030 if err != nil {
31 return err
32 }
giof6ad2982024-08-23 17:42:49 +040033 appManager, err := installer.NewAppManager(r, st.nsCreator, st.jc, st.hf, nil, nil, "/apps")
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +040034 if err != nil {
35 return err
36 }
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +040037 st.appManager = appManager
38 st.appsRepo = installer.NewInMemoryAppRepository(installer.CreateAllApps())
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +040039 return nil
40 })
Giorgi Lekveishviliab7ff6e2024-03-29 13:11:30 +040041 t.beforeStart = func() {
42 st.infoListener("Setting up core infrastructure services.")
43 }
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +040044 return &t
45}
46
gioe72b54f2024-04-22 10:44:41 +040047func SetupInfra(env installer.EnvConfig, st *state) Task {
gio7841f4f2024-07-26 19:53:49 +040048 tasks := []Task{
gioe72b54f2024-04-22 10:44:41 +040049 SetupNetwork(env, st),
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +040050 SetupCertificateIssuers(env, st),
51 SetupAuth(env, st),
52 SetupGroupMemberships(env, st),
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +040053 SetupWelcome(env, st),
54 SetupAppStore(env, st),
Davit Tabidze56f86a42024-04-09 19:15:25 +040055 SetupLauncher(env, st),
gio7841f4f2024-07-26 19:53:49 +040056 }
57 if env.PrivateDomain != "" {
58 tasks = append(tasks, SetupHeadscale(env, st))
59 }
60 return newConcurrentParentTask(
61 "Setup core services",
62 true,
63 tasks...,
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +040064 )
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +040065}
66
gioe72b54f2024-04-22 10:44:41 +040067func CommitEnvironmentConfiguration(env installer.EnvConfig, st *state) Task {
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +040068 t := newLeafTask("commit config", func() error {
gioe72b54f2024-04-22 10:44:41 +040069 r, err := st.ssClient.GetRepo("config")
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +040070 if err != nil {
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +040071 return err
72 }
gioe72b54f2024-04-22 10:44:41 +040073 r.Do(func(r soft.RepoFS) (string, error) {
74 if err := soft.WriteYaml(r, "config.yaml", env); err != nil {
75 return "", err
gio3af43942024-04-16 08:13:50 +040076 }
gioe72b54f2024-04-22 10:44:41 +040077 rootKust, err := soft.ReadKustomization(r, "kustomization.yaml")
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +040078 if err != nil {
gio3af43942024-04-16 08:13:50 +040079 return "", err
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +040080 }
gioe72b54f2024-04-22 10:44:41 +040081 if err := soft.WriteYaml(r, "kustomization.yaml", rootKust); err != nil {
gio3af43942024-04-16 08:13:50 +040082 return "", err
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +040083 }
gio3af43942024-04-16 08:13:50 +040084 return "configure charts repo", nil
85 })
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +040086 return nil
87 })
88 return &t
89}
90
Giorgi Lekveishvilid542b732024-03-25 18:17:39 +040091type firstAccount struct {
gio134be722025-07-20 19:01:17 +040092 Created bool `json:"created"`
93 Domain string `json:"domain"`
94 Groups []group `json:"groups"`
Giorgi Lekveishvilid542b732024-03-25 18:17:39 +040095}
96
gioe72b54f2024-04-22 10:44:41 +040097func ConfigureFirstAccount(env installer.EnvConfig, st *state) Task {
Giorgi Lekveishvilid542b732024-03-25 18:17:39 +040098 t := newLeafTask("Configure first account settings", func() error {
gioe72b54f2024-04-22 10:44:41 +040099 r, err := st.ssClient.GetRepo("config")
Giorgi Lekveishvilid542b732024-03-25 18:17:39 +0400100 if err != nil {
101 return err
102 }
giob4a3a192024-08-19 09:55:47 +0400103 _, err = r.Do(func(r soft.RepoFS) (string, error) {
gio2728e402024-08-01 18:14:21 +0400104 fa := firstAccount{false, env.Domain, initGroups}
gioe72b54f2024-04-22 10:44:41 +0400105 if err := soft.WriteYaml(r, "first-account.yaml", fa); err != nil {
gio3af43942024-04-16 08:13:50 +0400106 return "", err
107 }
108 return "first account membership configuration", nil
109 })
giob4a3a192024-08-19 09:55:47 +0400110 return err
Giorgi Lekveishvilid542b732024-03-25 18:17:39 +0400111 })
112 return &t
113}
114
gioe72b54f2024-04-22 10:44:41 +0400115func SetupNetwork(env installer.EnvConfig, st *state) Task {
gio7841f4f2024-07-26 19:53:49 +0400116 t := newLeafTask("Setup networks", func() error {
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400117 {
gio3cdee592024-04-17 10:15:56 +0400118 app, err := installer.FindEnvApp(st.appsRepo, "metallb-ipaddresspool")
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400119 if err != nil {
120 return err
121 }
gio3af43942024-04-16 08:13:50 +0400122 {
gio44f621b2024-04-29 09:44:38 +0400123 instanceId := fmt.Sprintf("%s-ingress-private", app.Slug())
gio3cdee592024-04-17 10:15:56 +0400124 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400125 namespace := fmt.Sprintf("%s%s-ingress-private", env.NamespacePrefix, app.Namespace())
gio778577f2024-04-29 09:44:38 +0400126 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
gioe72b54f2024-04-22 10:44:41 +0400127 "name": fmt.Sprintf("%s-ingress-private", env.Id),
128 "from": env.Network.Ingress.String(),
129 "to": env.Network.Ingress.String(),
gio3af43942024-04-16 08:13:50 +0400130 "autoAssign": false,
131 "namespace": "metallb-system",
132 }); err != nil {
133 return err
134 }
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400135 }
gio3af43942024-04-16 08:13:50 +0400136 {
gio44f621b2024-04-29 09:44:38 +0400137 instanceId := fmt.Sprintf("%s-headscale", app.Slug())
gio3cdee592024-04-17 10:15:56 +0400138 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400139 namespace := fmt.Sprintf("%s%s-ingress-private", env.NamespacePrefix, app.Namespace())
gio778577f2024-04-29 09:44:38 +0400140 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
gioe72b54f2024-04-22 10:44:41 +0400141 "name": fmt.Sprintf("%s-headscale", env.Id),
142 "from": env.Network.Headscale.String(),
143 "to": env.Network.Headscale.String(),
gio3af43942024-04-16 08:13:50 +0400144 "autoAssign": false,
145 "namespace": "metallb-system",
146 }); err != nil {
147 return err
148 }
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400149 }
gio3af43942024-04-16 08:13:50 +0400150 {
gio44f621b2024-04-29 09:44:38 +0400151 instanceId := app.Slug()
gio3cdee592024-04-17 10:15:56 +0400152 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400153 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
gio778577f2024-04-29 09:44:38 +0400154 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
gioe72b54f2024-04-22 10:44:41 +0400155 "name": env.Id,
156 "from": env.Network.ServicesFrom.String(),
157 "to": env.Network.ServicesTo.String(),
gio3af43942024-04-16 08:13:50 +0400158 "autoAssign": false,
159 "namespace": "metallb-system",
160 }); err != nil {
161 return err
162 }
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400163 }
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +0400164 }
gio7841f4f2024-07-26 19:53:49 +0400165 if env.PrivateDomain != "" {
Giorgi Lekveishvilib59b7c22024-04-03 22:17:50 +0400166 keys, err := installer.NewSSHKeyPair("port-allocator")
167 if err != nil {
168 return err
169 }
gioe72b54f2024-04-22 10:44:41 +0400170 user := fmt.Sprintf("%s-port-allocator", env.Id)
Giorgi Lekveishvilib59b7c22024-04-03 22:17:50 +0400171 if err := st.ssClient.AddUser(user, keys.AuthorizedKey()); err != nil {
172 return err
173 }
174 if err := st.ssClient.AddReadWriteCollaborator("config", user); err != nil {
175 return err
176 }
gio3cdee592024-04-17 10:15:56 +0400177 app, err := installer.FindEnvApp(st.appsRepo, "private-network")
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400178 if err != nil {
179 return err
180 }
gio44f621b2024-04-29 09:44:38 +0400181 instanceId := app.Slug()
gio3cdee592024-04-17 10:15:56 +0400182 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400183 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
gio778577f2024-04-29 09:44:38 +0400184 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +0400185 "privateNetwork": map[string]any{
186 "hostname": "private-network-proxy",
187 "username": "private-network-proxy",
gioe72b54f2024-04-22 10:44:41 +0400188 "ipSubnet": fmt.Sprintf("%s.0/24", strings.Join(strings.Split(env.Network.DNS.String(), ".")[:3], ".")),
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400189 },
Giorgi Lekveishvilib59b7c22024-04-03 22:17:50 +0400190 "sshPrivateKey": string(keys.RawPrivateKey()),
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400191 }); err != nil {
192 return err
193 }
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +0400194 }
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400195 return nil
196 })
197 return &t
198}
199
gioe72b54f2024-04-22 10:44:41 +0400200func SetupCertificateIssuers(env installer.EnvConfig, st *state) Task {
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400201 pub := newLeafTask(fmt.Sprintf("Public %s", env.Domain), func() error {
gio3cdee592024-04-17 10:15:56 +0400202 app, err := installer.FindEnvApp(st.appsRepo, "certificate-issuer-public")
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400203 if err != nil {
204 return err
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +0400205 }
gio44f621b2024-04-29 09:44:38 +0400206 instanceId := app.Slug()
gio3cdee592024-04-17 10:15:56 +0400207 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400208 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
gio7841f4f2024-07-26 19:53:49 +0400209 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
210 "network": "Public",
211 }); err != nil {
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400212 return err
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +0400213 }
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400214 return nil
215 })
gio7841f4f2024-07-26 19:53:49 +0400216 tasks := []Task{&pub}
217 if env.PrivateDomain != "" {
218 priv := newLeafTask(fmt.Sprintf("Private p.%s", env.Domain), func() error {
219 app, err := installer.FindEnvApp(st.appsRepo, "certificate-issuer-private")
220 if err != nil {
221 return err
222 }
223 instanceId := app.Slug()
224 appDir := fmt.Sprintf("/apps/%s", instanceId)
225 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
226 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{}); err != nil {
227 return err
228 }
229 return nil
230 })
231 tasks = append(tasks, &priv)
232 }
233 return newSequentialParentTask("Configure TLS certificate issuers", false, tasks...)
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400234}
235
gioe72b54f2024-04-22 10:44:41 +0400236func SetupAuth(env installer.EnvConfig, st *state) Task {
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400237 t := newLeafTask("Setup", func() error {
gio3cdee592024-04-17 10:15:56 +0400238 app, err := installer.FindEnvApp(st.appsRepo, "core-auth")
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400239 if err != nil {
240 return err
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +0400241 }
gio44f621b2024-04-29 09:44:38 +0400242 instanceId := app.Slug()
gio3cdee592024-04-17 10:15:56 +0400243 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400244 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
gio778577f2024-04-29 09:44:38 +0400245 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
gio7841f4f2024-07-26 19:53:49 +0400246 "network": "Public",
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +0400247 "subdomain": "test", // TODO(giolekva): make core-auth chart actually use this
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400248 }); err != nil {
249 return err
250 }
251 return nil
252 })
253 return newSequentialParentTask(
254 "Authentication services",
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400255 false,
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400256 &t,
gioe72b54f2024-04-22 10:44:41 +0400257 waitForAddr(st.httpClient, fmt.Sprintf("https://accounts-ui.%s", env.Domain)),
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400258 )
259}
260
gioe72b54f2024-04-22 10:44:41 +0400261func SetupGroupMemberships(env installer.EnvConfig, st *state) Task {
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400262 t := newLeafTask("Setup", func() error {
gio3cdee592024-04-17 10:15:56 +0400263 app, err := installer.FindEnvApp(st.appsRepo, "memberships")
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400264 if err != nil {
265 return err
266 }
gio44f621b2024-04-29 09:44:38 +0400267 instanceId := app.Slug()
gio3cdee592024-04-17 10:15:56 +0400268 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400269 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
gio7841f4f2024-07-26 19:53:49 +0400270 network := "Public"
271 if env.PrivateDomain != "" {
272 network = "Private"
273 }
gio778577f2024-04-29 09:44:38 +0400274 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
giod17ad3e2024-10-03 13:09:55 +0400275 "network": network,
276 // NOTE(gio): Everyone has access to memberships service, so that
277 // they can edit their own profile, request memberships, ...
278 "authGroups": "",
Giorgi Lekveishvilid542b732024-03-25 18:17:39 +0400279 }); err != nil {
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400280 return err
281 }
282 return nil
283 })
giob79db3a2024-08-01 14:20:42 +0400284 var addr string
285 if env.PrivateDomain != "" {
286 addr = fmt.Sprintf("https://memberships.%s", env.PrivateDomain)
287 } else {
288 addr = fmt.Sprintf("https://memberships.%s", env.Domain)
289 }
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400290 return newSequentialParentTask(
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400291 "Group membership",
292 false,
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400293 &t,
giob79db3a2024-08-01 14:20:42 +0400294 waitForAddr(st.httpClient, addr),
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400295 )
296}
297
gio09a3e5b2024-04-26 14:11:06 +0400298func SetupLauncher(env installer.EnvConfig, st *state) Task {
299 t := newLeafTask("Setup", func() error {
300 user := fmt.Sprintf("%s-launcher", env.Id)
301 keys, err := installer.NewSSHKeyPair(user)
302 if err != nil {
303 return err
304 }
305 if err := st.ssClient.AddUser(user, keys.AuthorizedKey()); err != nil {
306 return err
307 }
308 if err := st.ssClient.AddReadWriteCollaborator("config", user); err != nil {
309 return err
310 }
311 app, err := installer.FindEnvApp(st.appsRepo, "launcher")
312 if err != nil {
313 return err
314 }
315 instanceId := app.Slug()
316 appDir := fmt.Sprintf("/apps/%s", instanceId)
317 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
318 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
gio7841f4f2024-07-26 19:53:49 +0400319 "network": "Public",
gio09a3e5b2024-04-26 14:11:06 +0400320 "repoAddr": st.ssClient.GetRepoAddress("config"),
321 "sshPrivateKey": string(keys.RawPrivateKey()),
322 }); err != nil {
323 return err
324 }
325 return nil
326 })
327 return newSequentialParentTask(
328 "Launcher",
329 false,
330 &t,
331 waitForAddr(st.httpClient, fmt.Sprintf("https://launcher.%s", env.Domain)),
332 )
333}
334
gioe72b54f2024-04-22 10:44:41 +0400335func SetupHeadscale(env installer.EnvConfig, st *state) Task {
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400336 t := newLeafTask("Setup", func() error {
gio3cdee592024-04-17 10:15:56 +0400337 app, err := installer.FindEnvApp(st.appsRepo, "headscale")
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400338 if err != nil {
339 return err
340 }
gio44f621b2024-04-29 09:44:38 +0400341 instanceId := app.Slug()
gio3cdee592024-04-17 10:15:56 +0400342 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400343 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
gio778577f2024-04-29 09:44:38 +0400344 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
gio7841f4f2024-07-26 19:53:49 +0400345 "network": "Public",
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +0400346 "subdomain": "headscale",
gioe72b54f2024-04-22 10:44:41 +0400347 "ipSubnet": fmt.Sprintf("%s/24", env.Network.DNS.String()),
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400348 }); err != nil {
349 return err
350 }
351 return nil
352 })
353 return newSequentialParentTask(
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400354 "Setup mesh VPN",
355 false,
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400356 &t,
gioe72b54f2024-04-22 10:44:41 +0400357 waitForAddr(st.httpClient, fmt.Sprintf("https://headscale.%s/apple", env.Domain)),
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400358 )
359}
360
gioe72b54f2024-04-22 10:44:41 +0400361func SetupWelcome(env installer.EnvConfig, st *state) Task {
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400362 t := newLeafTask("Setup", func() error {
363 keys, err := installer.NewSSHKeyPair("welcome")
364 if err != nil {
365 return err
366 }
gioe72b54f2024-04-22 10:44:41 +0400367 user := fmt.Sprintf("%s-welcome", env.Id)
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400368 if err := st.ssClient.AddUser(user, keys.AuthorizedKey()); err != nil {
369 return err
370 }
371 if err := st.ssClient.AddReadWriteCollaborator("config", user); err != nil {
372 return err
373 }
gio3cdee592024-04-17 10:15:56 +0400374 app, err := installer.FindEnvApp(st.appsRepo, "welcome")
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400375 if err != nil {
376 return err
377 }
gio44f621b2024-04-29 09:44:38 +0400378 instanceId := app.Slug()
gio3cdee592024-04-17 10:15:56 +0400379 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400380 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
gio778577f2024-04-29 09:44:38 +0400381 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
gio7841f4f2024-07-26 19:53:49 +0400382 "network": "Public",
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +0400383 "repoAddr": st.ssClient.GetRepoAddress("config"),
384 "sshPrivateKey": string(keys.RawPrivateKey()),
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400385 }); err != nil {
386 return err
387 }
388 return nil
389 })
390 return newSequentialParentTask(
391 "Welcome service",
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400392 false,
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400393 &t,
gioe72b54f2024-04-22 10:44:41 +0400394 waitForAddr(st.httpClient, fmt.Sprintf("https://welcome.%s", env.Domain)),
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400395 )
396}
397
gioe72b54f2024-04-22 10:44:41 +0400398func SetupAppStore(env installer.EnvConfig, st *state) Task {
giob79db3a2024-08-01 14:20:42 +0400399 t := newLeafTask("Setup", func() error {
gioe72b54f2024-04-22 10:44:41 +0400400 user := fmt.Sprintf("%s-appmanager", env.Id)
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400401 keys, err := installer.NewSSHKeyPair(user)
402 if err != nil {
403 return err
404 }
405 if err := st.ssClient.AddUser(user, keys.AuthorizedKey()); err != nil {
406 return err
407 }
408 if err := st.ssClient.AddReadWriteCollaborator("config", user); err != nil {
409 return err
410 }
gio3cdee592024-04-17 10:15:56 +0400411 app, err := installer.FindEnvApp(st.appsRepo, "app-manager") // TODO(giolekva): configure
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400412 if err != nil {
413 return err
414 }
gio44f621b2024-04-29 09:44:38 +0400415 instanceId := app.Slug()
gio3cdee592024-04-17 10:15:56 +0400416 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400417 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
gio7841f4f2024-07-26 19:53:49 +0400418 network := "Public"
419 if env.PrivateDomain != "" {
420 network = "Private"
421 }
gio134be722025-07-20 19:01:17 +0400422 var initGroupIds []string
423 for _, g := range initGroups {
424 initGroupIds = append(initGroupIds, g.Id)
425 }
gio778577f2024-04-29 09:44:38 +0400426 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
gio7841f4f2024-07-26 19:53:49 +0400427 "network": network,
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +0400428 "repoAddr": st.ssClient.GetRepoAddress("config"),
429 "sshPrivateKey": string(keys.RawPrivateKey()),
gio134be722025-07-20 19:01:17 +0400430 "authGroups": strings.Join(initGroupIds, ","),
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400431 }); err != nil {
432 return err
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +0400433 }
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400434 return nil
435 })
giob79db3a2024-08-01 14:20:42 +0400436 var addr string
437 if env.PrivateDomain != "" {
438 addr = fmt.Sprintf("https://apps.%s", env.PrivateDomain)
439 } else {
440 addr = fmt.Sprintf("https://apps.%s", env.Domain)
441 }
442 return newSequentialParentTask(
443 "Application marketplace",
444 false,
445 &t,
446 waitForAddr(st.httpClient, addr),
447 )
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +0400448}
449
gioe72b54f2024-04-22 10:44:41 +0400450// TODO(gio-dns): remove
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +0400451type DNSSecKey struct {
452 Basename string `json:"basename,omitempty"`
453 Key []byte `json:"key,omitempty"`
454 Private []byte `json:"private,omitempty"`
455 DS []byte `json:"ds,omitempty"`
456}
457
458func newDNSSecKey(zone string) (DNSSecKey, error) {
459 key := &dns.DNSKEY{
460 Hdr: dns.RR_Header{Name: dns.Fqdn(zone), Class: dns.ClassINET, Ttl: 3600, Rrtype: dns.TypeDNSKEY},
461 Algorithm: dns.ECDSAP256SHA256, Flags: 257, Protocol: 3,
462 }
463 priv, err := key.Generate(256)
464 if err != nil {
465 return DNSSecKey{}, err
466 }
467 return DNSSecKey{
468 Basename: fmt.Sprintf("K%s+%03d+%05d", key.Header().Name, key.Algorithm, key.KeyTag()),
469 Key: []byte(key.String()),
470 Private: []byte(key.PrivateKeyString(priv)),
471 DS: []byte(key.ToDS(dns.SHA256).String()),
472 }, nil
473}