blob: 8a83eea180af3e7287f48b2f8a60986ac46c5129 [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
gioe72b54f2024-04-22 10:44:41 +040017 "github.com/giolekva/pcloud/core/installer/io"
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040018 "github.com/giolekva/pcloud/core/installer/soft"
19)
20
21const IPAddressPoolLocal = "local"
22const IPAddressPoolConfigRepo = "config-repo"
23const IPAddressPoolIngressPublic = "ingress-public"
24
Giorgi Lekveishvili5c2c0b92023-12-07 17:35:40 +040025const dnsAPIConfigMapName = "api-config"
26
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040027type Bootstrapper struct {
gioe72b54f2024-04-22 10:44:41 +040028 cl ChartLoader
29 ns NamespaceCreator
30 ha HelmActionConfigFactory
31 appRepo AppRepository
32 repoClient soft.ClientGetter
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040033}
34
gioe72b54f2024-04-22 10:44:41 +040035func NewBootstrapper(cl ChartLoader, ns NamespaceCreator, ha HelmActionConfigFactory, appRepo AppRepository, repoClient soft.ClientGetter) Bootstrapper {
36 return Bootstrapper{cl, ns, ha, appRepo, repoClient}
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040037}
38
gio3cdee592024-04-17 10:15:56 +040039func (b Bootstrapper) findApp(name string) (InfraApp, error) {
40 app, err := b.appRepo.Find(name)
41 if err != nil {
42 return nil, err
43 }
44 if a, ok := app.(InfraApp); ok {
45 return a, nil
46 } else {
47 return nil, fmt.Errorf("not found")
48 }
49}
50
51func (b Bootstrapper) Run(env BootstrapConfig) error {
52 if err := b.ns.Create(env.InfraName); err != nil {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040053 return err
54 }
55 if err := b.installMetallb(env); err != nil {
56 return err
57 }
gio3cdee592024-04-17 10:15:56 +040058 if err := b.installLonghorn(env.InfraName, env.StorageDir, env.VolumeDefaultReplicaCount); err != nil {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040059 return err
60 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +040061 bootstrapJobKeys, err := NewSSHKeyPair("bootstrapper")
62 if err != nil {
63 return err
64 }
gio3cdee592024-04-17 10:15:56 +040065 if err := b.installSoftServe(bootstrapJobKeys.AuthorizedKey(), env.InfraName, env.ServiceIPs.ConfigRepo); err != nil {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040066 return err
67 }
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +040068 time.Sleep(30 * time.Second)
gioe72b54f2024-04-22 10:44:41 +040069 ss, err := b.repoClient.Get(
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +040070 netip.AddrPortFrom(env.ServiceIPs.ConfigRepo, 22).String(),
71 bootstrapJobKeys.RawPrivateKey(),
72 log.Default())
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040073 if err != nil {
74 return err
75 }
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +040076 defer func() {
77 if ss.RemovePublicKey("admin", bootstrapJobKeys.AuthorizedKey()); err != nil {
78 fmt.Printf("Failed to remove admin public key: %s\n", err.Error())
79 }
80 }()
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040081 if ss.AddPublicKey("admin", string(env.AdminPublicKey)); err != nil {
82 return err
83 }
gio3cdee592024-04-17 10:15:56 +040084 if err := b.installFluxcd(ss, env.InfraName); err != nil {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040085 return err
86 }
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +040087 fmt.Println("Fluxcd installed")
gioe72b54f2024-04-22 10:44:41 +040088 repoIO, err := ss.GetRepo("config")
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040089 if err != nil {
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +040090 fmt.Println("Failed to get config repo")
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040091 return err
92 }
giof8843412024-05-22 16:38:05 +040093 hf := NewGitHelmFetcher()
94 lg := NewInfraLocalChartGenerator()
95 mgr, err := NewInfraAppManager(repoIO, b.ns, hf, lg)
gio3cdee592024-04-17 10:15:56 +040096 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
gioe72b54f2024-04-22 10:44:41 +0400328func (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 }
gioe72b54f2024-04-22 10:44:41 +0400342 repoIO, err := ss.GetRepo("config")
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400343 if err != nil {
344 return err
345 }
giob4a3a192024-08-19 09:55:47 +0400346 if _, err := repoIO.Do(func(r soft.RepoFS) (string, error) {
gio3af43942024-04-16 08:13:50 +0400347 w, err := r.Writer("README.md")
348 if err != nil {
349 return "", err
350 }
351 if _, err := fmt.Fprintf(w, "# %s systems", envName); err != nil {
352 return "", err
353 }
354 return "readme", nil
355 }); err != nil {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400356 return err
357 }
358 fmt.Println("Installing Flux")
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400359 ssPublicKeys, err := ss.GetPublicKeys()
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400360 if err != nil {
361 return err
362 }
gioe72b54f2024-04-22 10:44:41 +0400363 host := strings.Split(ss.Address(), ":")[0]
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400364 if err := b.installFluxBootstrap(
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400365 ss.GetRepoAddress("config"),
366 host,
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400367 ssPublicKeys,
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400368 string(keys.RawPrivateKey()),
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400369 envName,
370 ); err != nil {
371 return err
372 }
373 return nil
374}
375
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400376func (b Bootstrapper) installFluxBootstrap(repoAddr, repoHost string, repoHostPubKeys []string, privateKey, envName string) error {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400377 config, err := b.ha.New(envName)
378 if err != nil {
379 return err
380 }
381 chart, err := b.cl.Load("flux-bootstrap")
382 if err != nil {
383 return err
384 }
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400385 var lines []string
386 for _, k := range repoHostPubKeys {
387 lines = append(lines, fmt.Sprintf("%s %s", repoHost, k))
388 }
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400389 values := map[string]any{
390 "image": map[string]any{
Giorgi Lekveishvilic06164d2023-11-22 13:51:29 +0400391 "repository": "fluxcd/flux-cli",
392 "tag": "v2.1.2",
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400393 "pullPolicy": "IfNotPresent",
394 },
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400395 "repositoryAddress": repoAddr,
396 "repositoryHost": repoHost,
397 "repositoryHostPublicKeys": strings.Join(lines, "\n"),
398 "privateKey": privateKey,
399 "installationNamespace": fmt.Sprintf("%s-flux", envName),
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400400 }
401 installer := action.NewInstall(config)
402 installer.Namespace = envName
403 installer.CreateNamespace = true
404 installer.ReleaseName = "flux"
405 installer.Wait = true
406 installer.WaitForJobs = true
407 installer.Timeout = 20 * time.Minute
408 if _, err := installer.RunWithContext(context.TODO(), chart, values); err != nil {
409 return err
410 }
411 return nil
412}
413
gio3cdee592024-04-17 10:15:56 +0400414func (b Bootstrapper) installInfrastructureServices(mgr *InfraAppManager, env BootstrapConfig) error {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400415 install := func(name string) error {
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400416 fmt.Printf("Installing infrastructure service %s\n", name)
gio3cdee592024-04-17 10:15:56 +0400417 app, err := b.findApp(name)
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400418 if err != nil {
419 return err
420 }
gio3cdee592024-04-17 10:15:56 +0400421 namespace := fmt.Sprintf("%s-%s", env.InfraName, app.Namespace())
gio44f621b2024-04-29 09:44:38 +0400422 appDir := filepath.Join("/infrastructure", app.Slug())
gio778577f2024-04-29 09:44:38 +0400423 _, err = mgr.Install(app, appDir, namespace, map[string]any{})
424 return err
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400425 }
426 appsToInstall := []string{
427 "resource-renderer-controller",
428 "headscale-controller",
429 "csi-driver-smb",
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400430 "cert-manager",
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400431 }
432 for _, name := range appsToInstall {
433 if err := install(name); err != nil {
434 return err
435 }
436 }
437 return nil
438}
439
gioe72b54f2024-04-22 10:44:41 +0400440func configureMainRepo(repo soft.RepoIO, bootstrap BootstrapConfig) error {
giob4a3a192024-08-19 09:55:47 +0400441 _, err := repo.Do(func(r soft.RepoFS) (string, error) {
gioe72b54f2024-04-22 10:44:41 +0400442 if err := soft.WriteYaml(r, "bootstrap-config.yaml", bootstrap); err != nil {
gio3cdee592024-04-17 10:15:56 +0400443 return "", err
444 }
445 infra := InfraConfig{
446 Name: bootstrap.InfraName,
447 PublicIP: bootstrap.PublicIP,
448 InfraNamespacePrefix: bootstrap.NamespacePrefix,
449 InfraAdminPublicKey: bootstrap.AdminPublicKey,
450 }
gioe72b54f2024-04-22 10:44:41 +0400451 if err := soft.WriteYaml(r, "config.yaml", infra); err != nil {
gio3af43942024-04-16 08:13:50 +0400452 return "", err
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400453 }
gioe72b54f2024-04-22 10:44:41 +0400454 if err := soft.WriteYaml(r, "env-cidrs.yaml", EnvCIDRs{}); err != nil {
gio3af43942024-04-16 08:13:50 +0400455 return "", err
456 }
gioe72b54f2024-04-22 10:44:41 +0400457 kust := io.NewKustomization()
gio3af43942024-04-16 08:13:50 +0400458 kust.AddResources(
gio3cdee592024-04-17 10:15:56 +0400459 fmt.Sprintf("%s-flux", bootstrap.InfraName),
gio3af43942024-04-16 08:13:50 +0400460 "infrastructure",
461 "environments",
462 )
gioe72b54f2024-04-22 10:44:41 +0400463 if err := soft.WriteYaml(r, "kustomization.yaml", kust); err != nil {
gio3af43942024-04-16 08:13:50 +0400464 return "", err
465 }
466 {
467 out, err := r.Writer("infrastructure/pcloud-charts.yaml")
468 if err != nil {
469 return "", err
470 }
471 defer out.Close()
472 _, err = out.Write([]byte(fmt.Sprintf(`
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400473apiVersion: source.toolkit.fluxcd.io/v1
474kind: GitRepository
475metadata:
476 name: pcloud # TODO(giolekva): use more generic name
477 namespace: %s
478spec:
479 interval: 1m0s
480 url: https://github.com/giolekva/pcloud
481 ref:
Giorgi Lekveishvili024757c2024-03-14 13:27:29 +0400482 branch: main
gio3cdee592024-04-17 10:15:56 +0400483`, bootstrap.InfraName)))
gio3af43942024-04-16 08:13:50 +0400484 if err != nil {
485 return "", err
486 }
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400487 }
gioe72b54f2024-04-22 10:44:41 +0400488 infraKust := io.NewKustomization()
gio3af43942024-04-16 08:13:50 +0400489 infraKust.AddResources("pcloud-charts.yaml")
gioe72b54f2024-04-22 10:44:41 +0400490 if err := soft.WriteYaml(r, "infrastructure/kustomization.yaml", infraKust); err != nil {
gio3af43942024-04-16 08:13:50 +0400491 return "", err
492 }
gioe72b54f2024-04-22 10:44:41 +0400493 if err := soft.WriteYaml(r, "environments/kustomization.yaml", io.NewKustomization()); err != nil {
gio3af43942024-04-16 08:13:50 +0400494 return "", err
495 }
496 return "initialize pcloud directory structure", nil
497 })
giob4a3a192024-08-19 09:55:47 +0400498 return err
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400499}
500
gioe72b54f2024-04-22 10:44:41 +0400501func (b Bootstrapper) installEnvManager(mgr *InfraAppManager, ss soft.Client, env BootstrapConfig) error {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400502 keys, err := NewSSHKeyPair("env-manager")
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400503 if err != nil {
504 return err
505 }
gio3cdee592024-04-17 10:15:56 +0400506 user := fmt.Sprintf("%s-env-manager", env.InfraName)
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400507 if err := ss.AddUser(user, keys.AuthorizedKey()); err != nil {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400508 return err
509 }
510 if err := ss.MakeUserAdmin(user); err != nil {
511 return err
512 }
gio3cdee592024-04-17 10:15:56 +0400513 app, err := b.findApp("env-manager")
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400514 if err != nil {
515 return err
516 }
gio3cdee592024-04-17 10:15:56 +0400517 namespace := fmt.Sprintf("%s-%s", env.InfraName, app.Namespace())
gio44f621b2024-04-29 09:44:38 +0400518 appDir := filepath.Join("/infrastructure", app.Slug())
gio778577f2024-04-29 09:44:38 +0400519 _, err = mgr.Install(app, appDir, namespace, map[string]any{
gio3cdee592024-04-17 10:15:56 +0400520 "repoIP": env.ServiceIPs.ConfigRepo,
521 "repoPort": 22,
522 "repoName": "config",
523 "sshPrivateKey": string(keys.RawPrivateKey()),
524 })
gio778577f2024-04-29 09:44:38 +0400525 return err
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400526}
527
gioe72b54f2024-04-22 10:44:41 +0400528func (b Bootstrapper) installIngressPublic(mgr *InfraAppManager, ss soft.Client, env BootstrapConfig) error {
gio3cdee592024-04-17 10:15:56 +0400529 keys, err := NewSSHKeyPair("port-allocator")
Giorgi Lekveishvili925f0de2024-03-14 18:51:56 +0400530 if err != nil {
531 return err
532 }
gio3cdee592024-04-17 10:15:56 +0400533 user := fmt.Sprintf("%s-port-allocator", env.InfraName)
534 if err := ss.AddUser(user, keys.AuthorizedKey()); err != nil {
535 return err
Giorgi Lekveishvili925f0de2024-03-14 18:51:56 +0400536 }
gio3cdee592024-04-17 10:15:56 +0400537 if err := ss.AddReadWriteCollaborator("config", user); err != nil {
538 return err
539 }
540 app, err := b.findApp("ingress-public")
541 if err != nil {
542 return err
543 }
544 namespace := fmt.Sprintf("%s-%s", env.InfraName, app.Namespace())
gio44f621b2024-04-29 09:44:38 +0400545 appDir := filepath.Join("/infrastructure", app.Slug())
gio778577f2024-04-29 09:44:38 +0400546 _, err = mgr.Install(app, appDir, namespace, map[string]any{
gio3cdee592024-04-17 10:15:56 +0400547 "sshPrivateKey": string(keys.RawPrivateKey()),
548 })
gio778577f2024-04-29 09:44:38 +0400549 return err
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())
gio44f621b2024-04-29 09:44:38 +0400558 appDir := filepath.Join("/infrastructure", app.Slug())
gio778577f2024-04-29 09:44:38 +0400559 _, err = mgr.Install(app, appDir, namespace, map[string]any{})
560 return err
gio3cdee592024-04-17 10:15:56 +0400561}
562
563func (b Bootstrapper) installDNSZoneManager(mgr *InfraAppManager, env BootstrapConfig) error {
gioe72b54f2024-04-22 10:44:41 +0400564 app, err := b.findApp("dns-gateway")
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400565 if err != nil {
566 return err
567 }
gio3cdee592024-04-17 10:15:56 +0400568 namespace := fmt.Sprintf("%s-%s", env.InfraName, app.Namespace())
gio44f621b2024-04-29 09:44:38 +0400569 appDir := filepath.Join("/infrastructure", app.Slug())
gio778577f2024-04-29 09:44:38 +0400570 _, err = mgr.Install(app, appDir, namespace, map[string]any{
gioe72b54f2024-04-22 10:44:41 +0400571 "servers": []EnvDNS{},
gio3cdee592024-04-17 10:15:56 +0400572 })
gio778577f2024-04-29 09:44:38 +0400573 return err
gio3af43942024-04-16 08:13:50 +0400574}
575
gioe72b54f2024-04-22 10:44:41 +0400576func (b Bootstrapper) installFluxcdReconciler(mgr *InfraAppManager, ss soft.Client, env BootstrapConfig) error {
gio3cdee592024-04-17 10:15:56 +0400577 app, err := b.findApp("fluxcd-reconciler")
gio3af43942024-04-16 08:13:50 +0400578 if err != nil {
Giorgi Lekveishvili2df23db2023-12-14 07:55:22 +0400579 return err
580 }
gio3cdee592024-04-17 10:15:56 +0400581 namespace := fmt.Sprintf("%s-%s", env.InfraName, app.Namespace())
gio44f621b2024-04-29 09:44:38 +0400582 appDir := filepath.Join("/infrastructure", app.Slug())
gio778577f2024-04-29 09:44:38 +0400583 _, err = mgr.Install(app, appDir, namespace, map[string]any{})
584 return err
Giorgi Lekveishvili2df23db2023-12-14 07:55:22 +0400585}
586
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400587type HelmActionConfigFactory interface {
588 New(namespace string) (*action.Configuration, error)
589}
590
591type ChartLoader interface {
592 Load(name string) (*chart.Chart, error)
593}
594
595type fsChartLoader struct {
596 baseDir string
597}
598
599func NewFSChartLoader(baseDir string) ChartLoader {
600 return &fsChartLoader{baseDir}
601}
602
603func (l *fsChartLoader) Load(name string) (*chart.Chart, error) {
604 return loader.Load(filepath.Join(l.baseDir, name))
605}