blob: c581f420b945f507ecae5697ac1f7823eaca5bbb [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 }
gio3cdee592024-04-17 10:15:56 +040093 mgr, err := NewInfraAppManager(repoIO, b.ns)
94 if err != nil {
95 return err
96 }
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +040097 fmt.Println("Configuring main repo")
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +040098 if err := configureMainRepo(repoIO, env); err != nil {
99 return err
100 }
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400101 fmt.Println("Installing infrastructure services")
gio3cdee592024-04-17 10:15:56 +0400102 if err := b.installInfrastructureServices(mgr, env); err != nil {
103 return err
104 }
105 fmt.Println("Installing public ingress")
106 if err := b.installIngressPublic(mgr, ss, env); err != nil {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400107 return err
108 }
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400109 fmt.Println("Installing DNS Zone Manager")
gio3cdee592024-04-17 10:15:56 +0400110 if err := b.installDNSZoneManager(mgr, env); err != nil {
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400111 return err
112 }
Giorgi Lekveishvili2df23db2023-12-14 07:55:22 +0400113 fmt.Println("Installing Fluxcd Reconciler")
gio3cdee592024-04-17 10:15:56 +0400114 if err := b.installFluxcdReconciler(mgr, ss, env); err != nil {
Giorgi Lekveishvili2df23db2023-12-14 07:55:22 +0400115 return err
116 }
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400117 fmt.Println("Installing env manager")
gio3cdee592024-04-17 10:15:56 +0400118 if err := b.installEnvManager(mgr, ss, env); err != nil {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400119 return err
120 }
Giorgi Lekveishvili925f0de2024-03-14 18:51:56 +0400121 fmt.Println("Installing Ory Hydra Maester")
gio3cdee592024-04-17 10:15:56 +0400122 if err := b.installOryHydraMaester(mgr, env); err != nil {
Giorgi Lekveishvili925f0de2024-03-14 18:51:56 +0400123 return err
124 }
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400125 fmt.Println("Environment ready to use")
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400126 return nil
127}
128
gio3cdee592024-04-17 10:15:56 +0400129func (b Bootstrapper) installMetallb(env BootstrapConfig) error {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400130 if err := b.installMetallbNamespace(env); err != nil {
131 return err
132 }
133 if err := b.installMetallbService(); err != nil {
134 return err
135 }
136 if err := b.installMetallbIPAddressPool(IPAddressPoolLocal, true, env.ServiceIPs.From, env.ServiceIPs.To); err != nil {
137 return err
138 }
139 if err := b.installMetallbIPAddressPool(IPAddressPoolConfigRepo, false, env.ServiceIPs.ConfigRepo, env.ServiceIPs.ConfigRepo); err != nil {
140 return err
141 }
142 if err := b.installMetallbIPAddressPool(IPAddressPoolIngressPublic, false, env.ServiceIPs.IngressPublic, env.ServiceIPs.IngressPublic); err != nil {
143 return err
144 }
145 return nil
146}
147
gio3cdee592024-04-17 10:15:56 +0400148func (b Bootstrapper) installMetallbNamespace(env BootstrapConfig) error {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400149 fmt.Println("Installing metallb namespace")
gio3cdee592024-04-17 10:15:56 +0400150 config, err := b.ha.New(env.InfraName)
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400151 if err != nil {
152 return err
153 }
154 chart, err := b.cl.Load("namespace")
155 if err != nil {
156 return err
157 }
158 values := map[string]any{
159 "namespace": "metallb-system",
160 "labels": []string{
161 "pod-security.kubernetes.io/audit: privileged",
162 "pod-security.kubernetes.io/enforce: privileged",
163 "pod-security.kubernetes.io/warn: privileged",
164 },
165 }
166 installer := action.NewInstall(config)
gio3cdee592024-04-17 10:15:56 +0400167 installer.Namespace = env.InfraName
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400168 installer.ReleaseName = "metallb-ns"
169 installer.Wait = true
170 installer.WaitForJobs = true
171 if _, err := installer.RunWithContext(context.TODO(), chart, values); err != nil {
172 return err
173 }
174 return nil
175}
176
177func (b Bootstrapper) installMetallbService() error {
178 fmt.Println("Installing metallb")
179 config, err := b.ha.New("metallb-system")
180 if err != nil {
181 return err
182 }
183 chart, err := b.cl.Load("metallb")
184 if err != nil {
185 return err
186 }
187 values := map[string]any{ // TODO(giolekva): add loadBalancerClass?
188 "controller": map[string]any{
189 "image": map[string]any{
190 "repository": "quay.io/metallb/controller",
Giorgi Lekveishvilic06164d2023-11-22 13:51:29 +0400191 "tag": "v0.13.12",
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400192 "pullPolicy": "IfNotPresent",
193 },
194 "logLevel": "info",
195 },
196 "speaker": map[string]any{
197 "image": map[string]any{
198 "repository": "quay.io/metallb/speaker",
Giorgi Lekveishvilic06164d2023-11-22 13:51:29 +0400199 "tag": "v0.13.12",
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400200 "pullPolicy": "IfNotPresent",
201 },
202 "logLevel": "info",
203 },
204 }
205 installer := action.NewInstall(config)
206 installer.Namespace = "metallb-system"
207 installer.CreateNamespace = true
208 installer.ReleaseName = "metallb"
209 installer.IncludeCRDs = true
210 installer.Wait = true
211 installer.WaitForJobs = true
212 installer.Timeout = 20 * time.Minute
213 if _, err := installer.RunWithContext(context.TODO(), chart, values); err != nil {
214 return err
215 }
216 return nil
217}
218
219func (b Bootstrapper) installMetallbIPAddressPool(name string, autoAssign bool, from, to netip.Addr) error {
220 fmt.Printf("Installing metallb-ipaddresspool: %s\n", name)
221 config, err := b.ha.New("metallb-system")
222 if err != nil {
223 return err
224 }
225 chart, err := b.cl.Load("metallb-ipaddresspool")
226 if err != nil {
227 return err
228 }
229 values := map[string]any{
230 "name": name,
231 "autoAssign": autoAssign,
232 "from": from.String(),
233 "to": to.String(),
234 }
235 installer := action.NewInstall(config)
236 installer.Namespace = "metallb-system"
237 installer.CreateNamespace = true
238 installer.ReleaseName = name
239 installer.Wait = true
240 installer.WaitForJobs = true
241 installer.Timeout = 20 * time.Minute
242 if _, err := installer.RunWithContext(context.TODO(), chart, values); err != nil {
243 return err
244 }
245 return nil
246}
247
248func (b Bootstrapper) installLonghorn(envName string, storageDir string, volumeDefaultReplicaCount int) error {
249 fmt.Println("Installing Longhorn")
250 config, err := b.ha.New(envName)
251 if err != nil {
252 return err
253 }
254 chart, err := b.cl.Load("longhorn")
255 if err != nil {
256 return err
257 }
258 values := map[string]any{
259 "defaultSettings": map[string]any{
260 "defaultDataPath": storageDir,
261 },
262 "persistence": map[string]any{
263 "defaultClassReplicaCount": volumeDefaultReplicaCount,
264 },
265 "service": map[string]any{
266 "ui": map[string]any{
267 "type": "LoadBalancer",
268 },
269 },
270 "ingress": map[string]any{
271 "enabled": false,
272 },
273 }
274 installer := action.NewInstall(config)
275 installer.Namespace = "longhorn-system"
276 installer.CreateNamespace = true
277 installer.ReleaseName = "longhorn"
278 installer.Wait = true
279 installer.WaitForJobs = true
280 installer.Timeout = 20 * time.Minute
281 if _, err := installer.RunWithContext(context.TODO(), chart, values); err != nil {
282 return err
283 }
284 return nil
285}
286
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400287func (b Bootstrapper) installSoftServe(adminPublicKey string, namespace string, repoIP netip.Addr) error {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400288 fmt.Println("Installing SoftServe")
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400289 keys, err := NewSSHKeyPair("soft-serve")
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400290 if err != nil {
291 return err
292 }
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400293 config, err := b.ha.New(namespace)
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400294 if err != nil {
295 return err
296 }
297 chart, err := b.cl.Load("soft-serve")
298 if err != nil {
299 return err
300 }
301 values := map[string]any{
302 "image": map[string]any{
303 "repository": "charmcli/soft-serve",
Giorgi Lekveishvilic06164d2023-11-22 13:51:29 +0400304 "tag": "v0.7.1",
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400305 "pullPolicy": "IfNotPresent",
306 },
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400307 "privateKey": string(keys.RawPrivateKey()),
308 "publicKey": string(keys.RawAuthorizedKey()),
309 "adminKey": adminPublicKey,
310 "reservedIP": repoIP.String(),
311 "serviceType": "LoadBalancer",
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400312 }
313 installer := action.NewInstall(config)
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400314 installer.Namespace = namespace
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400315 installer.CreateNamespace = true
316 installer.ReleaseName = "soft-serve"
317 installer.Wait = true
318 installer.WaitForJobs = true
319 installer.Timeout = 20 * time.Minute
320 if _, err := installer.RunWithContext(context.TODO(), chart, values); err != nil {
321 return err
322 }
323 return nil
324}
325
gioe72b54f2024-04-22 10:44:41 +0400326func (b Bootstrapper) installFluxcd(ss soft.Client, envName string) error {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400327 keys, err := NewSSHKeyPair("fluxcd")
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400328 if err != nil {
329 return err
330 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400331 if err := ss.AddUser("flux", keys.AuthorizedKey()); err != nil {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400332 return err
333 }
334 if err := ss.MakeUserAdmin("flux"); err != nil {
335 return err
336 }
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400337 if err := ss.AddRepository("config"); err != nil {
338 return err
339 }
gioe72b54f2024-04-22 10:44:41 +0400340 repoIO, err := ss.GetRepo("config")
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400341 if err != nil {
342 return err
343 }
gioe72b54f2024-04-22 10:44:41 +0400344 if err := repoIO.Do(func(r soft.RepoFS) (string, error) {
gio3af43942024-04-16 08:13:50 +0400345 w, err := r.Writer("README.md")
346 if err != nil {
347 return "", err
348 }
349 if _, err := fmt.Fprintf(w, "# %s systems", envName); err != nil {
350 return "", err
351 }
352 return "readme", nil
353 }); err != nil {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400354 return err
355 }
356 fmt.Println("Installing Flux")
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400357 ssPublicKeys, err := ss.GetPublicKeys()
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400358 if err != nil {
359 return err
360 }
gioe72b54f2024-04-22 10:44:41 +0400361 host := strings.Split(ss.Address(), ":")[0]
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400362 if err := b.installFluxBootstrap(
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400363 ss.GetRepoAddress("config"),
364 host,
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400365 ssPublicKeys,
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400366 string(keys.RawPrivateKey()),
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400367 envName,
368 ); err != nil {
369 return err
370 }
371 return nil
372}
373
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400374func (b Bootstrapper) installFluxBootstrap(repoAddr, repoHost string, repoHostPubKeys []string, privateKey, envName string) error {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400375 config, err := b.ha.New(envName)
376 if err != nil {
377 return err
378 }
379 chart, err := b.cl.Load("flux-bootstrap")
380 if err != nil {
381 return err
382 }
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400383 var lines []string
384 for _, k := range repoHostPubKeys {
385 lines = append(lines, fmt.Sprintf("%s %s", repoHost, k))
386 }
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400387 values := map[string]any{
388 "image": map[string]any{
Giorgi Lekveishvilic06164d2023-11-22 13:51:29 +0400389 "repository": "fluxcd/flux-cli",
390 "tag": "v2.1.2",
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400391 "pullPolicy": "IfNotPresent",
392 },
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400393 "repositoryAddress": repoAddr,
394 "repositoryHost": repoHost,
395 "repositoryHostPublicKeys": strings.Join(lines, "\n"),
396 "privateKey": privateKey,
397 "installationNamespace": fmt.Sprintf("%s-flux", envName),
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400398 }
399 installer := action.NewInstall(config)
400 installer.Namespace = envName
401 installer.CreateNamespace = true
402 installer.ReleaseName = "flux"
403 installer.Wait = true
404 installer.WaitForJobs = true
405 installer.Timeout = 20 * time.Minute
406 if _, err := installer.RunWithContext(context.TODO(), chart, values); err != nil {
407 return err
408 }
409 return nil
410}
411
gio3cdee592024-04-17 10:15:56 +0400412func (b Bootstrapper) installInfrastructureServices(mgr *InfraAppManager, env BootstrapConfig) error {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400413 install := func(name string) error {
Giorgi Lekveishvili5c1b06e2024-03-28 15:19:44 +0400414 fmt.Printf("Installing infrastructure service %s\n", name)
gio3cdee592024-04-17 10:15:56 +0400415 app, err := b.findApp(name)
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400416 if err != nil {
417 return err
418 }
gio3cdee592024-04-17 10:15:56 +0400419 namespace := fmt.Sprintf("%s-%s", env.InfraName, app.Namespace())
gio44f621b2024-04-29 09:44:38 +0400420 appDir := filepath.Join("/infrastructure", app.Slug())
gio778577f2024-04-29 09:44:38 +0400421 _, err = mgr.Install(app, appDir, namespace, map[string]any{})
422 return err
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400423 }
424 appsToInstall := []string{
425 "resource-renderer-controller",
426 "headscale-controller",
427 "csi-driver-smb",
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400428 "cert-manager",
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400429 }
430 for _, name := range appsToInstall {
431 if err := install(name); err != nil {
432 return err
433 }
434 }
435 return nil
436}
437
gioe72b54f2024-04-22 10:44:41 +0400438func configureMainRepo(repo soft.RepoIO, bootstrap BootstrapConfig) error {
439 return repo.Do(func(r soft.RepoFS) (string, error) {
440 if err := soft.WriteYaml(r, "bootstrap-config.yaml", bootstrap); err != nil {
gio3cdee592024-04-17 10:15:56 +0400441 return "", err
442 }
443 infra := InfraConfig{
444 Name: bootstrap.InfraName,
445 PublicIP: bootstrap.PublicIP,
446 InfraNamespacePrefix: bootstrap.NamespacePrefix,
447 InfraAdminPublicKey: bootstrap.AdminPublicKey,
448 }
gioe72b54f2024-04-22 10:44:41 +0400449 if err := soft.WriteYaml(r, "config.yaml", infra); err != nil {
gio3af43942024-04-16 08:13:50 +0400450 return "", err
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400451 }
gioe72b54f2024-04-22 10:44:41 +0400452 if err := soft.WriteYaml(r, "env-cidrs.yaml", EnvCIDRs{}); err != nil {
gio3af43942024-04-16 08:13:50 +0400453 return "", err
454 }
gioe72b54f2024-04-22 10:44:41 +0400455 kust := io.NewKustomization()
gio3af43942024-04-16 08:13:50 +0400456 kust.AddResources(
gio3cdee592024-04-17 10:15:56 +0400457 fmt.Sprintf("%s-flux", bootstrap.InfraName),
gio3af43942024-04-16 08:13:50 +0400458 "infrastructure",
459 "environments",
460 )
gioe72b54f2024-04-22 10:44:41 +0400461 if err := soft.WriteYaml(r, "kustomization.yaml", kust); err != nil {
gio3af43942024-04-16 08:13:50 +0400462 return "", err
463 }
464 {
465 out, err := r.Writer("infrastructure/pcloud-charts.yaml")
466 if err != nil {
467 return "", err
468 }
469 defer out.Close()
470 _, err = out.Write([]byte(fmt.Sprintf(`
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400471apiVersion: source.toolkit.fluxcd.io/v1
472kind: GitRepository
473metadata:
474 name: pcloud # TODO(giolekva): use more generic name
475 namespace: %s
476spec:
477 interval: 1m0s
478 url: https://github.com/giolekva/pcloud
479 ref:
Giorgi Lekveishvili024757c2024-03-14 13:27:29 +0400480 branch: main
gio3cdee592024-04-17 10:15:56 +0400481`, bootstrap.InfraName)))
gio3af43942024-04-16 08:13:50 +0400482 if err != nil {
483 return "", err
484 }
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400485 }
gioe72b54f2024-04-22 10:44:41 +0400486 infraKust := io.NewKustomization()
gio3af43942024-04-16 08:13:50 +0400487 infraKust.AddResources("pcloud-charts.yaml")
gioe72b54f2024-04-22 10:44:41 +0400488 if err := soft.WriteYaml(r, "infrastructure/kustomization.yaml", infraKust); err != nil {
gio3af43942024-04-16 08:13:50 +0400489 return "", err
490 }
gioe72b54f2024-04-22 10:44:41 +0400491 if err := soft.WriteYaml(r, "environments/kustomization.yaml", io.NewKustomization()); err != nil {
gio3af43942024-04-16 08:13:50 +0400492 return "", err
493 }
494 return "initialize pcloud directory structure", nil
495 })
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400496}
497
gioe72b54f2024-04-22 10:44:41 +0400498func (b Bootstrapper) installEnvManager(mgr *InfraAppManager, ss soft.Client, env BootstrapConfig) error {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400499 keys, err := NewSSHKeyPair("env-manager")
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400500 if err != nil {
501 return err
502 }
gio3cdee592024-04-17 10:15:56 +0400503 user := fmt.Sprintf("%s-env-manager", env.InfraName)
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400504 if err := ss.AddUser(user, keys.AuthorizedKey()); err != nil {
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400505 return err
506 }
507 if err := ss.MakeUserAdmin(user); err != nil {
508 return err
509 }
gio3cdee592024-04-17 10:15:56 +0400510 app, err := b.findApp("env-manager")
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400511 if err != nil {
512 return err
513 }
gio3cdee592024-04-17 10:15:56 +0400514 namespace := fmt.Sprintf("%s-%s", env.InfraName, app.Namespace())
gio44f621b2024-04-29 09:44:38 +0400515 appDir := filepath.Join("/infrastructure", app.Slug())
gio778577f2024-04-29 09:44:38 +0400516 _, err = mgr.Install(app, appDir, namespace, map[string]any{
gio3cdee592024-04-17 10:15:56 +0400517 "repoIP": env.ServiceIPs.ConfigRepo,
518 "repoPort": 22,
519 "repoName": "config",
520 "sshPrivateKey": string(keys.RawPrivateKey()),
521 })
gio778577f2024-04-29 09:44:38 +0400522 return err
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400523}
524
gioe72b54f2024-04-22 10:44:41 +0400525func (b Bootstrapper) installIngressPublic(mgr *InfraAppManager, ss soft.Client, env BootstrapConfig) error {
gio3cdee592024-04-17 10:15:56 +0400526 keys, err := NewSSHKeyPair("port-allocator")
Giorgi Lekveishvili925f0de2024-03-14 18:51:56 +0400527 if err != nil {
528 return err
529 }
gio3cdee592024-04-17 10:15:56 +0400530 user := fmt.Sprintf("%s-port-allocator", env.InfraName)
531 if err := ss.AddUser(user, keys.AuthorizedKey()); err != nil {
532 return err
Giorgi Lekveishvili925f0de2024-03-14 18:51:56 +0400533 }
gio3cdee592024-04-17 10:15:56 +0400534 if err := ss.AddReadWriteCollaborator("config", user); err != nil {
535 return err
536 }
537 app, err := b.findApp("ingress-public")
538 if err != nil {
539 return err
540 }
541 namespace := fmt.Sprintf("%s-%s", env.InfraName, app.Namespace())
gio44f621b2024-04-29 09:44:38 +0400542 appDir := filepath.Join("/infrastructure", app.Slug())
gio778577f2024-04-29 09:44:38 +0400543 _, err = mgr.Install(app, appDir, namespace, map[string]any{
gio3cdee592024-04-17 10:15:56 +0400544 "sshPrivateKey": string(keys.RawPrivateKey()),
545 })
gio778577f2024-04-29 09:44:38 +0400546 return err
Giorgi Lekveishvili925f0de2024-03-14 18:51:56 +0400547}
548
gio3cdee592024-04-17 10:15:56 +0400549func (b Bootstrapper) installOryHydraMaester(mgr *InfraAppManager, env BootstrapConfig) error {
550 app, err := b.findApp("hydra-maester")
551 if err != nil {
552 return err
553 }
554 namespace := fmt.Sprintf("%s-%s", env.InfraName, app.Namespace())
gio44f621b2024-04-29 09:44:38 +0400555 appDir := filepath.Join("/infrastructure", app.Slug())
gio778577f2024-04-29 09:44:38 +0400556 _, err = mgr.Install(app, appDir, namespace, map[string]any{})
557 return err
gio3cdee592024-04-17 10:15:56 +0400558}
559
560func (b Bootstrapper) installDNSZoneManager(mgr *InfraAppManager, env BootstrapConfig) error {
gioe72b54f2024-04-22 10:44:41 +0400561 app, err := b.findApp("dns-gateway")
Giorgi Lekveishvili106a9352023-12-04 11:20:11 +0400562 if err != nil {
563 return err
564 }
gio3cdee592024-04-17 10:15:56 +0400565 namespace := fmt.Sprintf("%s-%s", env.InfraName, app.Namespace())
gio44f621b2024-04-29 09:44:38 +0400566 appDir := filepath.Join("/infrastructure", app.Slug())
gio778577f2024-04-29 09:44:38 +0400567 _, err = mgr.Install(app, appDir, namespace, map[string]any{
gioe72b54f2024-04-22 10:44:41 +0400568 "servers": []EnvDNS{},
gio3cdee592024-04-17 10:15:56 +0400569 })
gio778577f2024-04-29 09:44:38 +0400570 return err
gio3af43942024-04-16 08:13:50 +0400571}
572
gioe72b54f2024-04-22 10:44:41 +0400573func (b Bootstrapper) installFluxcdReconciler(mgr *InfraAppManager, ss soft.Client, env BootstrapConfig) error {
gio3cdee592024-04-17 10:15:56 +0400574 app, err := b.findApp("fluxcd-reconciler")
gio3af43942024-04-16 08:13:50 +0400575 if err != nil {
Giorgi Lekveishvili2df23db2023-12-14 07:55:22 +0400576 return err
577 }
gio3cdee592024-04-17 10:15:56 +0400578 namespace := fmt.Sprintf("%s-%s", env.InfraName, app.Namespace())
gio44f621b2024-04-29 09:44:38 +0400579 appDir := filepath.Join("/infrastructure", app.Slug())
gio778577f2024-04-29 09:44:38 +0400580 _, err = mgr.Install(app, appDir, namespace, map[string]any{})
581 return err
Giorgi Lekveishvili2df23db2023-12-14 07:55:22 +0400582}
583
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400584type HelmActionConfigFactory interface {
585 New(namespace string) (*action.Configuration, error)
586}
587
588type ChartLoader interface {
589 Load(name string) (*chart.Chart, error)
590}
591
592type fsChartLoader struct {
593 baseDir string
594}
595
596func NewFSChartLoader(baseDir string) ChartLoader {
597 return &fsChartLoader{baseDir}
598}
599
600func (l *fsChartLoader) Load(name string) (*chart.Chart, error) {
601 return loader.Load(filepath.Join(l.baseDir, name))
602}