blob: d69ba3a0b81a958ade12aba8a35b420dfb476e9c [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 }
giof6ad2982024-08-23 17:42:49 +040021 appManager, err := installer.NewAppManager(r, st.nsCreator, st.jc, st.hf, nil, nil, "/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{
giod17ad3e2024-10-03 13:09:55 +0400263 "network": network,
264 // NOTE(gio): Everyone has access to memberships service, so that
265 // they can edit their own profile, request memberships, ...
266 "authGroups": "",
Giorgi Lekveishvilid542b732024-03-25 18:17:39 +0400267 }); err != nil {
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400268 return err
269 }
270 return nil
271 })
giob79db3a2024-08-01 14:20:42 +0400272 var addr string
273 if env.PrivateDomain != "" {
274 addr = fmt.Sprintf("https://memberships.%s", env.PrivateDomain)
275 } else {
276 addr = fmt.Sprintf("https://memberships.%s", env.Domain)
277 }
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400278 return newSequentialParentTask(
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400279 "Group membership",
280 false,
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400281 &t,
giob79db3a2024-08-01 14:20:42 +0400282 waitForAddr(st.httpClient, addr),
Giorgi Lekveishvilia09fad72024-03-21 15:24:35 +0400283 )
284}
285
gio09a3e5b2024-04-26 14:11:06 +0400286func SetupLauncher(env installer.EnvConfig, st *state) Task {
287 t := newLeafTask("Setup", func() error {
288 user := fmt.Sprintf("%s-launcher", env.Id)
289 keys, err := installer.NewSSHKeyPair(user)
290 if err != nil {
291 return err
292 }
293 if err := st.ssClient.AddUser(user, keys.AuthorizedKey()); err != nil {
294 return err
295 }
296 if err := st.ssClient.AddReadWriteCollaborator("config", user); err != nil {
297 return err
298 }
299 app, err := installer.FindEnvApp(st.appsRepo, "launcher")
300 if err != nil {
301 return err
302 }
303 instanceId := app.Slug()
304 appDir := fmt.Sprintf("/apps/%s", instanceId)
305 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
306 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
gio7841f4f2024-07-26 19:53:49 +0400307 "network": "Public",
gio09a3e5b2024-04-26 14:11:06 +0400308 "repoAddr": st.ssClient.GetRepoAddress("config"),
309 "sshPrivateKey": string(keys.RawPrivateKey()),
310 }); err != nil {
311 return err
312 }
313 return nil
314 })
315 return newSequentialParentTask(
316 "Launcher",
317 false,
318 &t,
319 waitForAddr(st.httpClient, fmt.Sprintf("https://launcher.%s", env.Domain)),
320 )
321}
322
gioe72b54f2024-04-22 10:44:41 +0400323func SetupHeadscale(env installer.EnvConfig, st *state) Task {
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400324 t := newLeafTask("Setup", func() error {
gio3cdee592024-04-17 10:15:56 +0400325 app, err := installer.FindEnvApp(st.appsRepo, "headscale")
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400326 if err != nil {
327 return err
328 }
gio44f621b2024-04-29 09:44:38 +0400329 instanceId := app.Slug()
gio3cdee592024-04-17 10:15:56 +0400330 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400331 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
gio778577f2024-04-29 09:44:38 +0400332 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
gio7841f4f2024-07-26 19:53:49 +0400333 "network": "Public",
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +0400334 "subdomain": "headscale",
gioe72b54f2024-04-22 10:44:41 +0400335 "ipSubnet": fmt.Sprintf("%s/24", env.Network.DNS.String()),
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400336 }); err != nil {
337 return err
338 }
339 return nil
340 })
341 return newSequentialParentTask(
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400342 "Setup mesh VPN",
343 false,
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400344 &t,
gioe72b54f2024-04-22 10:44:41 +0400345 waitForAddr(st.httpClient, fmt.Sprintf("https://headscale.%s/apple", env.Domain)),
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400346 )
347}
348
gioe72b54f2024-04-22 10:44:41 +0400349func SetupWelcome(env installer.EnvConfig, st *state) Task {
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400350 t := newLeafTask("Setup", func() error {
351 keys, err := installer.NewSSHKeyPair("welcome")
352 if err != nil {
353 return err
354 }
gioe72b54f2024-04-22 10:44:41 +0400355 user := fmt.Sprintf("%s-welcome", env.Id)
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400356 if err := st.ssClient.AddUser(user, keys.AuthorizedKey()); err != nil {
357 return err
358 }
359 if err := st.ssClient.AddReadWriteCollaborator("config", user); err != nil {
360 return err
361 }
gio3cdee592024-04-17 10:15:56 +0400362 app, err := installer.FindEnvApp(st.appsRepo, "welcome")
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400363 if err != nil {
364 return err
365 }
gio44f621b2024-04-29 09:44:38 +0400366 instanceId := app.Slug()
gio3cdee592024-04-17 10:15:56 +0400367 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400368 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
gio778577f2024-04-29 09:44:38 +0400369 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
gio7841f4f2024-07-26 19:53:49 +0400370 "network": "Public",
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +0400371 "repoAddr": st.ssClient.GetRepoAddress("config"),
372 "sshPrivateKey": string(keys.RawPrivateKey()),
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400373 }); err != nil {
374 return err
375 }
376 return nil
377 })
378 return newSequentialParentTask(
379 "Welcome service",
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400380 false,
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400381 &t,
gioe72b54f2024-04-22 10:44:41 +0400382 waitForAddr(st.httpClient, fmt.Sprintf("https://welcome.%s", env.Domain)),
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400383 )
384}
385
gioe72b54f2024-04-22 10:44:41 +0400386func SetupAppStore(env installer.EnvConfig, st *state) Task {
giob79db3a2024-08-01 14:20:42 +0400387 t := newLeafTask("Setup", func() error {
gioe72b54f2024-04-22 10:44:41 +0400388 user := fmt.Sprintf("%s-appmanager", env.Id)
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400389 keys, err := installer.NewSSHKeyPair(user)
390 if err != nil {
391 return err
392 }
393 if err := st.ssClient.AddUser(user, keys.AuthorizedKey()); err != nil {
394 return err
395 }
396 if err := st.ssClient.AddReadWriteCollaborator("config", user); err != nil {
397 return err
398 }
gio3cdee592024-04-17 10:15:56 +0400399 app, err := installer.FindEnvApp(st.appsRepo, "app-manager") // TODO(giolekva): configure
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400400 if err != nil {
401 return err
402 }
gio44f621b2024-04-29 09:44:38 +0400403 instanceId := app.Slug()
gio3cdee592024-04-17 10:15:56 +0400404 appDir := fmt.Sprintf("/apps/%s", instanceId)
gio3af43942024-04-16 08:13:50 +0400405 namespace := fmt.Sprintf("%s%s", env.NamespacePrefix, app.Namespace())
gio7841f4f2024-07-26 19:53:49 +0400406 network := "Public"
407 if env.PrivateDomain != "" {
408 network = "Private"
409 }
gio778577f2024-04-29 09:44:38 +0400410 if _, err := st.appManager.Install(app, instanceId, appDir, namespace, map[string]any{
gio7841f4f2024-07-26 19:53:49 +0400411 "network": network,
Giorgi Lekveishvilie009a5d2024-01-05 14:10:11 +0400412 "repoAddr": st.ssClient.GetRepoAddress("config"),
413 "sshPrivateKey": string(keys.RawPrivateKey()),
Giorgi Lekveishvili3c91e8b2024-03-25 20:20:14 +0400414 "authGroups": strings.Join(initGroups, ","),
Giorgi Lekveishvili378ea882023-12-12 13:59:18 +0400415 }); err != nil {
416 return err
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +0400417 }
Giorgi Lekveishvili77ee2dc2023-12-11 16:51:10 +0400418 return nil
419 })
giob79db3a2024-08-01 14:20:42 +0400420 var addr string
421 if env.PrivateDomain != "" {
422 addr = fmt.Sprintf("https://apps.%s", env.PrivateDomain)
423 } else {
424 addr = fmt.Sprintf("https://apps.%s", env.Domain)
425 }
426 return newSequentialParentTask(
427 "Application marketplace",
428 false,
429 &t,
430 waitForAddr(st.httpClient, addr),
431 )
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +0400432}
433
gioe72b54f2024-04-22 10:44:41 +0400434// TODO(gio-dns): remove
Giorgi Lekveishvili46743d42023-12-10 15:47:23 +0400435type DNSSecKey struct {
436 Basename string `json:"basename,omitempty"`
437 Key []byte `json:"key,omitempty"`
438 Private []byte `json:"private,omitempty"`
439 DS []byte `json:"ds,omitempty"`
440}
441
442func newDNSSecKey(zone string) (DNSSecKey, error) {
443 key := &dns.DNSKEY{
444 Hdr: dns.RR_Header{Name: dns.Fqdn(zone), Class: dns.ClassINET, Ttl: 3600, Rrtype: dns.TypeDNSKEY},
445 Algorithm: dns.ECDSAP256SHA256, Flags: 257, Protocol: 3,
446 }
447 priv, err := key.Generate(256)
448 if err != nil {
449 return DNSSecKey{}, err
450 }
451 return DNSSecKey{
452 Basename: fmt.Sprintf("K%s+%03d+%05d", key.Header().Name, key.Algorithm, key.KeyTag()),
453 Key: []byte(key.String()),
454 Private: []byte(key.PrivateKeyString(priv)),
455 DS: []byte(key.ToDS(dns.SHA256).String()),
456 }, nil
457}