blob: 6b65096a987713500d20ca80cb9aaa2376589a57 [file] [log] [blame]
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +04001package installer
2
3import (
4 "context"
5 _ "embed"
6 "fmt"
7 "log"
8 "net/netip"
9 "path/filepath"
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +040010 "strings"
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040011 "time"
12
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040013 "helm.sh/helm/v3/pkg/action"
14 "helm.sh/helm/v3/pkg/chart"
15 "helm.sh/helm/v3/pkg/chart/loader"
16
17 "github.com/giolekva/pcloud/core/installer/soft"
18)
19
20const IPAddressPoolLocal = "local"
21const IPAddressPoolConfigRepo = "config-repo"
22const IPAddressPoolIngressPublic = "ingress-public"
23
Giorgi Lekveishvili5c2c0b92023-12-07 17:35:40 +040024const dnsAPIConfigMapName = "api-config"
25
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040026type Bootstrapper struct {
gio3af43942024-04-16 08:13:50 +040027 cl ChartLoader
28 ns NamespaceCreator
29 ha HelmActionConfigFactory
30 appRepo AppRepository
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040031}
32
gio3af43942024-04-16 08:13:50 +040033func NewBootstrapper(cl ChartLoader, ns NamespaceCreator, ha HelmActionConfigFactory, appRepo AppRepository) Bootstrapper {
34 return Bootstrapper{cl, ns, ha, appRepo}
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040035}
36
gio3cdee592024-04-17 10:15:56 +040037func (b Bootstrapper) findApp(name string) (InfraApp, error) {
38 app, err := b.appRepo.Find(name)
39 if err != nil {
40 return nil, err
41 }
42 if a, ok := app.(InfraApp); ok {
43 return a, nil
44 } else {
45 return nil, fmt.Errorf("not found")
46 }
47}
48
49func (b Bootstrapper) Run(env BootstrapConfig) error {
50 if err := b.ns.Create(env.InfraName); err != nil {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040051 return err
52 }
53 if err := b.installMetallb(env); err != nil {
54 return err
55 }
gio3cdee592024-04-17 10:15:56 +040056 if err := b.installLonghorn(env.InfraName, env.StorageDir, env.VolumeDefaultReplicaCount); err != nil {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040057 return err
58 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +040059 bootstrapJobKeys, err := NewSSHKeyPair("bootstrapper")
60 if err != nil {
61 return err
62 }
gio3cdee592024-04-17 10:15:56 +040063 if err := b.installSoftServe(bootstrapJobKeys.AuthorizedKey(), env.InfraName, env.ServiceIPs.ConfigRepo); err != nil {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040064 return err
65 }
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +040066 time.Sleep(30 * time.Second)
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +040067 ss, err := soft.WaitForClient(
68 netip.AddrPortFrom(env.ServiceIPs.ConfigRepo, 22).String(),
69 bootstrapJobKeys.RawPrivateKey(),
70 log.Default())
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040071 if err != nil {
72 return err
73 }
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +040074 defer func() {
75 if ss.RemovePublicKey("admin", bootstrapJobKeys.AuthorizedKey()); err != nil {
76 fmt.Printf("Failed to remove admin public key: %s\n", err.Error())
77 }
78 }()
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040079 if ss.AddPublicKey("admin", string(env.AdminPublicKey)); err != nil {
80 return err
81 }
gio3cdee592024-04-17 10:15:56 +040082 if err := b.installFluxcd(ss, env.InfraName); err != nil {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040083 return err
84 }
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +040085 fmt.Println("Fluxcd installed")
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +040086 repo, err := ss.GetRepo("config")
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040087 if err != nil {
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +040088 fmt.Println("Failed to get config repo")
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040089 return err
90 }
gio3af43942024-04-16 08:13:50 +040091 repoIO, err := NewRepoIO(repo, ss.Signer)
92 if err != nil {
93 return err
94 }
gio3cdee592024-04-17 10:15:56 +040095 mgr, err := NewInfraAppManager(repoIO, b.ns)
96 if err != nil {
97 return err
98 }
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +040099 fmt.Println("Configuring main repo")
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400100 if err := configureMainRepo(repoIO, env); err != nil {
101 return err
102 }
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400103 fmt.Println("Installing infrastructure services")
gio3cdee592024-04-17 10:15:56 +0400104 if err := b.installInfrastructureServices(mgr, env); err != nil {
105 return err
106 }
107 fmt.Println("Installing public ingress")
108 if err := b.installIngressPublic(mgr, ss, env); err != nil {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400109 return err
110 }
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400111 fmt.Println("Installing DNS Zone Manager")
gio3cdee592024-04-17 10:15:56 +0400112 if err := b.installDNSZoneManager(mgr, env); err != nil {
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400113 return err
114 }
Giorgi Lekveishvili2df23db2023-12-14 07:55:22 +0400115 fmt.Println("Installing Fluxcd Reconciler")
gio3cdee592024-04-17 10:15:56 +0400116 if err := b.installFluxcdReconciler(mgr, ss, env); err != nil {
Giorgi Lekveishvili2df23db2023-12-14 07:55:22 +0400117 return err
118 }
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400119 fmt.Println("Installing env manager")
gio3cdee592024-04-17 10:15:56 +0400120 if err := b.installEnvManager(mgr, ss, env); err != nil {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400121 return err
122 }
Giorgi Lekveishvili925f0de2024-03-14 18:51:56 +0400123 fmt.Println("Installing Ory Hydra Maester")
gio3cdee592024-04-17 10:15:56 +0400124 if err := b.installOryHydraMaester(mgr, env); err != nil {
Giorgi Lekveishvili925f0de2024-03-14 18:51:56 +0400125 return err
126 }
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400127 fmt.Println("Environment ready to use")
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400128 return nil
129}
130
gio3cdee592024-04-17 10:15:56 +0400131func (b Bootstrapper) installMetallb(env BootstrapConfig) error {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400132 if err := b.installMetallbNamespace(env); err != nil {
133 return err
134 }
135 if err := b.installMetallbService(); err != nil {
136 return err
137 }
138 if err := b.installMetallbIPAddressPool(IPAddressPoolLocal, true, env.ServiceIPs.From, env.ServiceIPs.To); err != nil {
139 return err
140 }
141 if err := b.installMetallbIPAddressPool(IPAddressPoolConfigRepo, false, env.ServiceIPs.ConfigRepo, env.ServiceIPs.ConfigRepo); err != nil {
142 return err
143 }
144 if err := b.installMetallbIPAddressPool(IPAddressPoolIngressPublic, false, env.ServiceIPs.IngressPublic, env.ServiceIPs.IngressPublic); err != nil {
145 return err
146 }
147 return nil
148}
149
gio3cdee592024-04-17 10:15:56 +0400150func (b Bootstrapper) installMetallbNamespace(env BootstrapConfig) error {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400151 fmt.Println("Installing metallb namespace")
gio3cdee592024-04-17 10:15:56 +0400152 config, err := b.ha.New(env.InfraName)
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400153 if err != nil {
154 return err
155 }
156 chart, err := b.cl.Load("namespace")
157 if err != nil {
158 return err
159 }
160 values := map[string]any{
161 "namespace": "metallb-system",
162 "labels": []string{
163 "pod-security.kubernetes.io/audit: privileged",
164 "pod-security.kubernetes.io/enforce: privileged",
165 "pod-security.kubernetes.io/warn: privileged",
166 },
167 }
168 installer := action.NewInstall(config)
gio3cdee592024-04-17 10:15:56 +0400169 installer.Namespace = env.InfraName
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400170 installer.ReleaseName = "metallb-ns"
171 installer.Wait = true
172 installer.WaitForJobs = true
173 if _, err := installer.RunWithContext(context.TODO(), chart, values); err != nil {
174 return err
175 }
176 return nil
177}
178
179func (b Bootstrapper) installMetallbService() error {
180 fmt.Println("Installing metallb")
181 config, err := b.ha.New("metallb-system")
182 if err != nil {
183 return err
184 }
185 chart, err := b.cl.Load("metallb")
186 if err != nil {
187 return err
188 }
189 values := map[string]any{ // TODO(giolekva): add loadBalancerClass?
190 "controller": map[string]any{
191 "image": map[string]any{
192 "repository": "quay.io/metallb/controller",
Giorgi Lekveishvilic06164d2023-11-22 13:51:29 +0400193 "tag": "v0.13.12",
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400194 "pullPolicy": "IfNotPresent",
195 },
196 "logLevel": "info",
197 },
198 "speaker": map[string]any{
199 "image": map[string]any{
200 "repository": "quay.io/metallb/speaker",
Giorgi Lekveishvilic06164d2023-11-22 13:51:29 +0400201 "tag": "v0.13.12",
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400202 "pullPolicy": "IfNotPresent",
203 },
204 "logLevel": "info",
205 },
206 }
207 installer := action.NewInstall(config)
208 installer.Namespace = "metallb-system"
209 installer.CreateNamespace = true
210 installer.ReleaseName = "metallb"
211 installer.IncludeCRDs = true
212 installer.Wait = true
213 installer.WaitForJobs = true
214 installer.Timeout = 20 * time.Minute
215 if _, err := installer.RunWithContext(context.TODO(), chart, values); err != nil {
216 return err
217 }
218 return nil
219}
220
221func (b Bootstrapper) installMetallbIPAddressPool(name string, autoAssign bool, from, to netip.Addr) error {
222 fmt.Printf("Installing metallb-ipaddresspool: %s\n", name)
223 config, err := b.ha.New("metallb-system")
224 if err != nil {
225 return err
226 }
227 chart, err := b.cl.Load("metallb-ipaddresspool")
228 if err != nil {
229 return err
230 }
231 values := map[string]any{
232 "name": name,
233 "autoAssign": autoAssign,
234 "from": from.String(),
235 "to": to.String(),
236 }
237 installer := action.NewInstall(config)
238 installer.Namespace = "metallb-system"
239 installer.CreateNamespace = true
240 installer.ReleaseName = name
241 installer.Wait = true
242 installer.WaitForJobs = true
243 installer.Timeout = 20 * time.Minute
244 if _, err := installer.RunWithContext(context.TODO(), chart, values); err != nil {
245 return err
246 }
247 return nil
248}
249
250func (b Bootstrapper) installLonghorn(envName string, storageDir string, volumeDefaultReplicaCount int) error {
251 fmt.Println("Installing Longhorn")
252 config, err := b.ha.New(envName)
253 if err != nil {
254 return err
255 }
256 chart, err := b.cl.Load("longhorn")
257 if err != nil {
258 return err
259 }
260 values := map[string]any{
261 "defaultSettings": map[string]any{
262 "defaultDataPath": storageDir,
263 },
264 "persistence": map[string]any{
265 "defaultClassReplicaCount": volumeDefaultReplicaCount,
266 },
267 "service": map[string]any{
268 "ui": map[string]any{
269 "type": "LoadBalancer",
270 },
271 },
272 "ingress": map[string]any{
273 "enabled": false,
274 },
275 }
276 installer := action.NewInstall(config)
277 installer.Namespace = "longhorn-system"
278 installer.CreateNamespace = true
279 installer.ReleaseName = "longhorn"
280 installer.Wait = true
281 installer.WaitForJobs = true
282 installer.Timeout = 20 * time.Minute
283 if _, err := installer.RunWithContext(context.TODO(), chart, values); err != nil {
284 return err
285 }
286 return nil
287}
288
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400289func (b Bootstrapper) installSoftServe(adminPublicKey string, namespace string, repoIP netip.Addr) error {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400290 fmt.Println("Installing SoftServe")
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400291 keys, err := NewSSHKeyPair("soft-serve")
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400292 if err != nil {
293 return err
294 }
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400295 config, err := b.ha.New(namespace)
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400296 if err != nil {
297 return err
298 }
299 chart, err := b.cl.Load("soft-serve")
300 if err != nil {
301 return err
302 }
303 values := map[string]any{
304 "image": map[string]any{
305 "repository": "charmcli/soft-serve",
Giorgi Lekveishvilic06164d2023-11-22 13:51:29 +0400306 "tag": "v0.7.1",
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400307 "pullPolicy": "IfNotPresent",
308 },
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400309 "privateKey": string(keys.RawPrivateKey()),
310 "publicKey": string(keys.RawAuthorizedKey()),
311 "adminKey": adminPublicKey,
312 "reservedIP": repoIP.String(),
313 "serviceType": "LoadBalancer",
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400314 }
315 installer := action.NewInstall(config)
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400316 installer.Namespace = namespace
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400317 installer.CreateNamespace = true
318 installer.ReleaseName = "soft-serve"
319 installer.Wait = true
320 installer.WaitForJobs = true
321 installer.Timeout = 20 * time.Minute
322 if _, err := installer.RunWithContext(context.TODO(), chart, values); err != nil {
323 return err
324 }
325 return nil
326}
327
328func (b Bootstrapper) installFluxcd(ss *soft.Client, envName string) error {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400329 keys, err := NewSSHKeyPair("fluxcd")
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400330 if err != nil {
331 return err
332 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400333 if err := ss.AddUser("flux", keys.AuthorizedKey()); err != nil {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400334 return err
335 }
336 if err := ss.MakeUserAdmin("flux"); err != nil {
337 return err
338 }
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400339 if err := ss.AddRepository("config"); err != nil {
340 return err
341 }
342 repo, err := ss.GetRepo("config")
343 if err != nil {
344 return err
345 }
gio3af43942024-04-16 08:13:50 +0400346 repoIO, err := NewRepoIO(repo, ss.Signer)
347 if err != nil {
348 return err
349 }
350 if err := repoIO.Atomic(func(r RepoFS) (string, error) {
351 w, err := r.Writer("README.md")
352 if err != nil {
353 return "", err
354 }
355 if _, err := fmt.Fprintf(w, "# %s systems", envName); err != nil {
356 return "", err
357 }
358 return "readme", nil
359 }); err != nil {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400360 return err
361 }
362 fmt.Println("Installing Flux")
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400363 ssPublicKeys, err := ss.GetPublicKeys()
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400364 if err != nil {
365 return err
366 }
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400367 host := strings.Split(ss.Addr, ":")[0]
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400368 if err := b.installFluxBootstrap(
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400369 ss.GetRepoAddress("config"),
370 host,
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400371 ssPublicKeys,
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400372 string(keys.RawPrivateKey()),
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400373 envName,
374 ); err != nil {
375 return err
376 }
377 return nil
378}
379
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400380func (b Bootstrapper) installFluxBootstrap(repoAddr, repoHost string, repoHostPubKeys []string, privateKey, envName string) error {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400381 config, err := b.ha.New(envName)
382 if err != nil {
383 return err
384 }
385 chart, err := b.cl.Load("flux-bootstrap")
386 if err != nil {
387 return err
388 }
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400389 var lines []string
390 for _, k := range repoHostPubKeys {
391 lines = append(lines, fmt.Sprintf("%s %s", repoHost, k))
392 }
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400393 values := map[string]any{
394 "image": map[string]any{
Giorgi Lekveishvilic06164d2023-11-22 13:51:29 +0400395 "repository": "fluxcd/flux-cli",
396 "tag": "v2.1.2",
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400397 "pullPolicy": "IfNotPresent",
398 },
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400399 "repositoryAddress": repoAddr,
400 "repositoryHost": repoHost,
401 "repositoryHostPublicKeys": strings.Join(lines, "\n"),
402 "privateKey": privateKey,
403 "installationNamespace": fmt.Sprintf("%s-flux", envName),
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400404 }
405 installer := action.NewInstall(config)
406 installer.Namespace = envName
407 installer.CreateNamespace = true
408 installer.ReleaseName = "flux"
409 installer.Wait = true
410 installer.WaitForJobs = true
411 installer.Timeout = 20 * time.Minute
412 if _, err := installer.RunWithContext(context.TODO(), chart, values); err != nil {
413 return err
414 }
415 return nil
416}
417
gio3cdee592024-04-17 10:15:56 +0400418func (b Bootstrapper) installInfrastructureServices(mgr *InfraAppManager, env BootstrapConfig) error {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400419 install := func(name string) error {
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400420 fmt.Printf("Installing infrastructure service %s\n", name)
gio3cdee592024-04-17 10:15:56 +0400421 app, err := b.findApp(name)
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400422 if err != nil {
423 return err
424 }
gio3cdee592024-04-17 10:15:56 +0400425 namespace := fmt.Sprintf("%s-%s", env.InfraName, app.Namespace())
426 appDir := filepath.Join("/infrastructure", app.Name())
427 return mgr.Install(app, appDir, namespace, map[string]any{})
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400428 }
429 appsToInstall := []string{
430 "resource-renderer-controller",
431 "headscale-controller",
432 "csi-driver-smb",
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400433 "cert-manager",
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400434 }
435 for _, name := range appsToInstall {
436 if err := install(name); err != nil {
437 return err
438 }
439 }
440 return nil
441}
442
gio3cdee592024-04-17 10:15:56 +0400443func configureMainRepo(repo RepoIO, bootstrap BootstrapConfig) error {
gio3af43942024-04-16 08:13:50 +0400444 return repo.Atomic(func(r RepoFS) (string, error) {
gio3cdee592024-04-17 10:15:56 +0400445 if err := WriteYaml(r, "bootstrap-config.yaml", bootstrap); err != nil {
446 return "", err
447 }
448 infra := InfraConfig{
449 Name: bootstrap.InfraName,
450 PublicIP: bootstrap.PublicIP,
451 InfraNamespacePrefix: bootstrap.NamespacePrefix,
452 InfraAdminPublicKey: bootstrap.AdminPublicKey,
453 }
454 if err := WriteYaml(r, "config.yaml", infra); err != nil {
gio3af43942024-04-16 08:13:50 +0400455 return "", err
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400456 }
gio3af43942024-04-16 08:13:50 +0400457 if err := WriteYaml(r, "env-cidrs.yaml", EnvCIDRs{}); err != nil {
458 return "", err
459 }
460 kust := NewKustomization()
461 kust.AddResources(
gio3cdee592024-04-17 10:15:56 +0400462 fmt.Sprintf("%s-flux", bootstrap.InfraName),
gio3af43942024-04-16 08:13:50 +0400463 "infrastructure",
464 "environments",
465 )
466 if err := WriteYaml(r, "kustomization.yaml", kust); err != nil {
467 return "", err
468 }
469 {
470 out, err := r.Writer("infrastructure/pcloud-charts.yaml")
471 if err != nil {
472 return "", err
473 }
474 defer out.Close()
475 _, err = out.Write([]byte(fmt.Sprintf(`
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400476apiVersion: source.toolkit.fluxcd.io/v1
477kind: GitRepository
478metadata:
479 name: pcloud # TODO(giolekva): use more generic name
480 namespace: %s
481spec:
482 interval: 1m0s
483 url: https://github.com/giolekva/pcloud
484 ref:
Giorgi Lekveishvili024757c2024-03-14 13:27:29 +0400485 branch: main
gio3cdee592024-04-17 10:15:56 +0400486`, bootstrap.InfraName)))
gio3af43942024-04-16 08:13:50 +0400487 if err != nil {
488 return "", err
489 }
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400490 }
gio3af43942024-04-16 08:13:50 +0400491 infraKust := NewKustomization()
492 infraKust.AddResources("pcloud-charts.yaml")
493 if err := WriteYaml(r, "infrastructure/kustomization.yaml", infraKust); err != nil {
494 return "", err
495 }
496 if err := WriteYaml(r, "environments/kustomization.yaml", NewKustomization()); err != nil {
497 return "", err
498 }
499 return "initialize pcloud directory structure", nil
500 })
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400501}
502
gio3cdee592024-04-17 10:15:56 +0400503func (b Bootstrapper) installEnvManager(mgr *InfraAppManager, ss *soft.Client, env BootstrapConfig) error {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400504 keys, err := NewSSHKeyPair("env-manager")
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400505 if err != nil {
506 return err
507 }
gio3cdee592024-04-17 10:15:56 +0400508 user := fmt.Sprintf("%s-env-manager", env.InfraName)
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400509 if err := ss.AddUser(user, keys.AuthorizedKey()); err != nil {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400510 return err
511 }
512 if err := ss.MakeUserAdmin(user); err != nil {
513 return err
514 }
gio3cdee592024-04-17 10:15:56 +0400515 app, err := b.findApp("env-manager")
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400516 if err != nil {
517 return err
518 }
gio3cdee592024-04-17 10:15:56 +0400519 namespace := fmt.Sprintf("%s-%s", env.InfraName, app.Namespace())
520 appDir := filepath.Join("/infrastructure", app.Name())
521 return mgr.Install(app, appDir, namespace, map[string]any{
522 "repoIP": env.ServiceIPs.ConfigRepo,
523 "repoPort": 22,
524 "repoName": "config",
525 "sshPrivateKey": string(keys.RawPrivateKey()),
526 })
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400527}
528
gio3cdee592024-04-17 10:15:56 +0400529func (b Bootstrapper) installIngressPublic(mgr *InfraAppManager, ss *soft.Client, env BootstrapConfig) error {
530 keys, err := NewSSHKeyPair("port-allocator")
Giorgi Lekveishvili925f0de2024-03-14 18:51:56 +0400531 if err != nil {
532 return err
533 }
gio3cdee592024-04-17 10:15:56 +0400534 user := fmt.Sprintf("%s-port-allocator", env.InfraName)
535 if err := ss.AddUser(user, keys.AuthorizedKey()); err != nil {
536 return err
Giorgi Lekveishvili925f0de2024-03-14 18:51:56 +0400537 }
gio3cdee592024-04-17 10:15:56 +0400538 if err := ss.AddReadWriteCollaborator("config", user); err != nil {
539 return err
540 }
541 app, err := b.findApp("ingress-public")
542 if err != nil {
543 return err
544 }
545 namespace := fmt.Sprintf("%s-%s", env.InfraName, app.Namespace())
546 appDir := filepath.Join("/infrastructure", app.Name())
547 return mgr.Install(app, appDir, namespace, map[string]any{
548 "sshPrivateKey": string(keys.RawPrivateKey()),
549 })
Giorgi Lekveishvili925f0de2024-03-14 18:51:56 +0400550}
551
gio3cdee592024-04-17 10:15:56 +0400552func (b Bootstrapper) installOryHydraMaester(mgr *InfraAppManager, env BootstrapConfig) error {
553 app, err := b.findApp("hydra-maester")
554 if err != nil {
555 return err
556 }
557 namespace := fmt.Sprintf("%s-%s", env.InfraName, app.Namespace())
558 appDir := filepath.Join("/infrastructure", app.Name())
559 return mgr.Install(app, appDir, namespace, map[string]any{})
560}
561
562func (b Bootstrapper) installDNSZoneManager(mgr *InfraAppManager, env BootstrapConfig) error {
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400563 const (
564 volumeClaimName = "dns-zone-configs"
565 volumeMountPath = "/etc/pcloud/dns-zone-configs"
566 )
gio3cdee592024-04-17 10:15:56 +0400567 app, err := b.findApp("dns-zone-manager")
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400568 if err != nil {
569 return err
570 }
gio3cdee592024-04-17 10:15:56 +0400571 namespace := fmt.Sprintf("%s-%s", env.InfraName, app.Namespace())
572 appDir := filepath.Join("/infrastructure", app.Name())
573 return mgr.Install(app, appDir, namespace, map[string]any{
574 "volume": map[string]any{
575 "claimName": volumeClaimName,
576 "mountPath": volumeMountPath,
577 "size": "1Gi",
Giorgi Lekveishvili2df23db2023-12-14 07:55:22 +0400578 },
gio3cdee592024-04-17 10:15:56 +0400579 "apiConfigMapName": dnsAPIConfigMapName,
580 })
gio3af43942024-04-16 08:13:50 +0400581}
582
gio3cdee592024-04-17 10:15:56 +0400583func (b Bootstrapper) installFluxcdReconciler(mgr *InfraAppManager, ss *soft.Client, env BootstrapConfig) error {
584 app, err := b.findApp("fluxcd-reconciler")
gio3af43942024-04-16 08:13:50 +0400585 if err != nil {
Giorgi Lekveishvili2df23db2023-12-14 07:55:22 +0400586 return err
587 }
gio3cdee592024-04-17 10:15:56 +0400588 namespace := fmt.Sprintf("%s-%s", env.InfraName, app.Namespace())
589 appDir := filepath.Join("/infrastructure", app.Name())
590 return mgr.Install(app, appDir, namespace, map[string]any{})
Giorgi Lekveishvili2df23db2023-12-14 07:55:22 +0400591}
592
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400593type HelmActionConfigFactory interface {
594 New(namespace string) (*action.Configuration, error)
595}
596
597type ChartLoader interface {
598 Load(name string) (*chart.Chart, error)
599}
600
601type fsChartLoader struct {
602 baseDir string
603}
604
605func NewFSChartLoader(baseDir string) ChartLoader {
606 return &fsChartLoader{baseDir}
607}
608
609func (l *fsChartLoader) Load(name string) (*chart.Chart, error) {
610 return loader.Load(filepath.Join(l.baseDir, name))
611}