blob: aa3e061696f1794cdc8cc4e7ec30197db130ace9 [file] [log] [blame]
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +04001package welcome
2
3import (
4 "embed"
5 "encoding/base64"
6 "encoding/json"
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +04007 "errors"
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +04008 "fmt"
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +04009 htemplate "html/template"
10 "io"
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +040011 "io/fs"
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +040012 "log"
13 "net/http"
14 "path"
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +040015 "path/filepath"
16 "strings"
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +040017 "text/template"
18
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +040019 "github.com/charmbracelet/keygen"
20 "github.com/gorilla/mux"
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +040021
22 "github.com/giolekva/pcloud/core/installer"
23 "github.com/giolekva/pcloud/core/installer/soft"
24)
25
26//go:embed env-tmpl
27var filesTmpls embed.FS
28
29//go:embed create-env.html
30var createEnvFormHtml string
31
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +040032//go:embed env-created.html
33var envCreatedHtml string
34
35type Status string
36
37const (
38 StatusActive Status = "ACTIVE"
39 StatusAccepted Status = "ACCEPTED"
40)
41
42// TODO(giolekva): add CreatedAt and ValidUntil
43type invitation struct {
44 Token string `json:"token"`
45 Status Status `json:"status"`
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +040046}
47
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +040048type EnvServer struct {
49 port int
50 ss *soft.Client
51 repo installer.RepoIO
52 nsCreator installer.NamespaceCreator
53 nameGenerator installer.NameGenerator
54}
55
56func NewEnvServer(port int, ss *soft.Client, repo installer.RepoIO, nsCreator installer.NamespaceCreator, nameGenerator installer.NameGenerator) *EnvServer {
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +040057 return &EnvServer{
58 port,
59 ss,
60 repo,
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +040061 nsCreator,
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +040062 nameGenerator,
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +040063 }
64}
65
66func (s *EnvServer) Start() {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +040067 r := mux.NewRouter()
68 r.PathPrefix("/static/").Handler(http.FileServer(http.FS(staticAssets)))
69 r.Path("/env").Methods("GET").HandlerFunc(s.createEnvForm)
70 r.Path("/env").Methods("POST").HandlerFunc(s.createEnv)
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +040071 r.Path("/create-invitation").Methods("GET").HandlerFunc(s.createInvitation)
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +040072 http.Handle("/", r)
73 log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", s.port), nil))
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +040074}
75
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +040076func (s *EnvServer) createEnvForm(w http.ResponseWriter, r *http.Request) {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +040077 if _, err := w.Write([]byte(createEnvFormHtml)); err != nil {
78 http.Error(w, err.Error(), http.StatusInternalServerError)
79 }
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +040080}
81
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +040082func (s *EnvServer) createInvitation(w http.ResponseWriter, r *http.Request) {
83 invitations, err := s.readInvitations()
84 if err != nil {
85 http.Error(w, err.Error(), http.StatusInternalServerError)
86 return
87 }
88 token, err := installer.NewFixedLengthRandomNameGenerator(100).Generate() // TODO(giolekva): use cryptographic tokens
89 if err != nil {
90 http.Error(w, err.Error(), http.StatusInternalServerError)
91 return
92
93 }
94 invitations = append(invitations, invitation{token, StatusActive})
95 if err := s.writeInvitations(invitations); err != nil {
96 http.Error(w, err.Error(), http.StatusInternalServerError)
97 return
98 }
99 if _, err := w.Write([]byte("OK")); err != nil {
100 http.Error(w, err.Error(), http.StatusInternalServerError)
101 return
102 }
103}
104
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400105type createEnvReq struct {
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400106 Name string
107 ContactEmail string `json:"contactEmail"`
108 Domain string `json:"domain"`
109 AdminPublicKey string `json:"adminPublicKey"`
110 SecretToken string `json:"secretToken"`
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +0400111}
112
113func (s *EnvServer) readInvitations() ([]invitation, error) {
114 r, err := s.repo.Reader("invitations")
115 if err != nil {
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400116 if errors.Is(err, fs.ErrNotExist) {
117 return make([]invitation, 0), nil
118 }
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +0400119 return nil, err
120 }
121 defer r.Close()
122 dec := json.NewDecoder(r)
123 invitations := make([]invitation, 0)
124 for {
125 var i invitation
126 if err := dec.Decode(&i); err == io.EOF {
127 break
128 }
129 invitations = append(invitations, i)
130 }
131 return invitations, nil
132}
133
134func (s *EnvServer) writeInvitations(invitations []invitation) error {
135 w, err := s.repo.Writer("invitations")
136 if err != nil {
137 return err
138 }
139 defer w.Close()
140 enc := json.NewEncoder(w)
141 for _, i := range invitations {
142 if err := enc.Encode(i); err != nil {
143 return err
144 }
145 }
146 return s.repo.CommitAndPush("Generated new invitation")
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400147}
148
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400149func extractRequest(r *http.Request) (createEnvReq, error) {
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400150 var req createEnvReq
151 if err := func() error {
152 var err error
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400153 if err = r.ParseForm(); err != nil {
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400154 return err
155 }
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +0400156 if req.SecretToken, err = getFormValue(r.PostForm, "secret-token"); err != nil {
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400157 return err
158 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400159 if req.Domain, err = getFormValue(r.PostForm, "domain"); err != nil {
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400160 return err
161 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400162 if req.ContactEmail, err = getFormValue(r.PostForm, "contact-email"); err != nil {
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400163 return err
164 }
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400165 if req.AdminPublicKey, err = getFormValue(r.PostForm, "admin-public-key"); err != nil {
166 return err
167 }
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400168 return nil
169 }(); err != nil {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400170 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400171 return createEnvReq{}, err
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400172 }
173 }
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400174 return req, nil
175}
176
177func (s *EnvServer) acceptInvitation(token string) error {
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +0400178 invitations, err := s.readInvitations()
179 if err != nil {
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400180 return err
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +0400181 }
182 found := false
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400183 for i := range invitations {
184 if invitations[i].Token == token && invitations[i].Status == StatusActive {
185 invitations[i].Status = StatusAccepted
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +0400186 found = true
187 break
188 }
189 }
190 if !found {
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400191 return fmt.Errorf("Invitation not found")
192 }
193 return s.writeInvitations(invitations)
194}
195
196func (s *EnvServer) createEnv(w http.ResponseWriter, r *http.Request) {
197 req, err := extractRequest(r)
198 if err != nil {
199 http.Error(w, err.Error(), http.StatusInternalServerError)
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +0400200 return
201 }
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400202 var env installer.EnvConfig
203 cr, err := s.repo.Reader("config.yaml")
204 if err != nil {
205 http.Error(w, err.Error(), http.StatusInternalServerError)
206 return
207 }
208 defer cr.Close()
209 if err := installer.ReadYaml(cr, &env); err != nil {
210 http.Error(w, err.Error(), http.StatusInternalServerError)
211 return
212 }
213 if err := s.acceptInvitation(req.SecretToken); err != nil {
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +0400214 http.Error(w, err.Error(), http.StatusInternalServerError)
215 return
216 }
217 if name, err := s.nameGenerator.Generate(); err != nil {
218 http.Error(w, err.Error(), http.StatusInternalServerError)
219 return
220 } else {
221 req.Name = name
222 }
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400223 appsRepo := installer.NewInMemoryAppRepository(installer.CreateAllApps())
224 ssApp, err := appsRepo.Find("soft-serve")
225 if err != nil {
226 http.Error(w, err.Error(), http.StatusInternalServerError)
227 return
228 }
229 ssAdminKeys, err := installer.NewSSHKeyPair(fmt.Sprintf("%s-config-repo-admin-keys", req.Name))
230 if err != nil {
231 http.Error(w, err.Error(), http.StatusInternalServerError)
232 return
233 }
234 ssKeys, err := installer.NewSSHKeyPair(fmt.Sprintf("%s-config-repo-keys", req.Name))
235 if err != nil {
236 http.Error(w, err.Error(), http.StatusInternalServerError)
237 return
238 }
239 ssValues := map[string]any{
240 "ChartRepositoryNamespace": env.Name,
241 "ServiceType": "ClusterIP",
242 "PrivateKey": string(ssKeys.RawPrivateKey()),
243 "PublicKey": string(ssKeys.RawAuthorizedKey()),
244 "AdminKey": string(ssAdminKeys.RawAuthorizedKey()),
245 "Ingress": map[string]any{
246 "Enabled": false,
247 },
248 }
249 derived := installer.Derived{
250 Global: installer.Values{
251 Id: req.Name,
252 PCloudEnvName: env.Name,
253 },
254 Release: installer.Release{
255 Namespace: req.Name,
256 },
257 Values: ssValues,
258 }
259 if err := s.nsCreator.Create(req.Name); err != nil {
260 http.Error(w, err.Error(), http.StatusInternalServerError)
261 return
262 }
263 if err := s.repo.InstallApp(*ssApp, filepath.Join("/environments", req.Name, "config-repo"), ssValues, derived); err != nil {
264 http.Error(w, err.Error(), http.StatusInternalServerError)
265 return
266 }
267 k := installer.NewKustomization()
268 k.AddResources("config-repo")
269 if err := s.repo.WriteKustomization(filepath.Join("/environments", req.Name, "kustomization.yaml"), k); err != nil {
270 http.Error(w, err.Error(), http.StatusInternalServerError)
271 return
272 }
273 ssClient, err := soft.WaitForClient(
274 fmt.Sprintf("soft-serve.%s.svc.cluster.local:%d", req.Name, 22),
275 ssAdminKeys.RawPrivateKey(),
276 log.Default())
277 if err != nil {
278 http.Error(w, err.Error(), http.StatusInternalServerError)
279 return
280 }
281 if err := ssClient.AddPublicKey("admin", req.AdminPublicKey); err != nil {
282 http.Error(w, err.Error(), http.StatusInternalServerError)
283 return
284 }
285 defer func() {
286 if err := ssClient.RemovePublicKey("admin", string(ssAdminKeys.RawAuthorizedKey())); err != nil {
287 http.Error(w, err.Error(), http.StatusInternalServerError)
288 return
289 }
290 }()
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400291 fluxUserName := fmt.Sprintf("flux-%s", req.Name)
292 keys, err := installer.NewSSHKeyPair(fluxUserName)
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400293 if err != nil {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400294 http.Error(w, err.Error(), http.StatusInternalServerError)
295 return
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400296 }
297 {
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400298 if err := ssClient.AddRepository("config"); err != nil {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400299 http.Error(w, err.Error(), http.StatusInternalServerError)
300 return
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400301 }
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400302 repo, err := ssClient.GetRepo("config")
303 if err != nil {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400304 http.Error(w, err.Error(), http.StatusInternalServerError)
305 return
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400306 }
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400307 repoIO := installer.NewRepoIO(repo, ssClient.Signer)
308 if err := repoIO.WriteCommitAndPush("README.md", fmt.Sprintf("# %s PCloud environment", req.Name), "readme"); err != nil {
309 http.Error(w, err.Error(), http.StatusInternalServerError)
310 return
311 }
312 if err := ssClient.AddUser(fluxUserName, keys.AuthorizedKey()); err != nil {
313 http.Error(w, err.Error(), http.StatusInternalServerError)
314 return
315 }
316 if err := ssClient.AddReadOnlyCollaborator("config", fluxUserName); err != nil {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400317 http.Error(w, err.Error(), http.StatusInternalServerError)
318 return
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400319 }
320 }
321 {
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400322 repo, err := ssClient.GetRepo("config")
Giorgi Lekveishvili57dffb32023-08-07 15:45:43 +0400323 if err != nil {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400324 http.Error(w, err.Error(), http.StatusInternalServerError)
325 return
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400326 }
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400327 if err := initNewEnv(ssClient, installer.NewRepoIO(repo, ssClient.Signer), s.nsCreator, req, env.Name, env.PublicIP); err != nil {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400328 http.Error(w, err.Error(), http.StatusInternalServerError)
329 return
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400330 }
331 }
332 {
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400333 ssPubKey, err := ssClient.GetPublicKey()
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400334 if err != nil {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400335 http.Error(w, err.Error(), http.StatusInternalServerError)
336 return
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400337 }
338 if err := addNewEnv(
Giorgi Lekveishvili57dffb32023-08-07 15:45:43 +0400339 s.repo,
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400340 req,
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400341 strings.Split(ssClient.Addr, ":")[0],
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400342 keys,
343 ssPubKey,
344 ); err != nil {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400345 http.Error(w, err.Error(), http.StatusInternalServerError)
346 return
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400347 }
348 }
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +0400349 tmpl, err := htemplate.New("response").Parse(envCreatedHtml)
350 if err != nil {
351 http.Error(w, err.Error(), http.StatusInternalServerError)
352 return
353 }
354 if err := tmpl.Execute(w, map[string]any{
355 "Domain": req.Domain,
356 }); err != nil {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400357 http.Error(w, err.Error(), http.StatusInternalServerError)
358 return
359 }
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400360}
361
Giorgi Lekveishvili57dffb32023-08-07 15:45:43 +0400362func initNewEnv(
363 ss *soft.Client,
364 r installer.RepoIO,
365 nsCreator installer.NamespaceCreator,
366 req createEnvReq,
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400367 pcloudEnvName string,
368 pcloudPublicIP string,
Giorgi Lekveishvili57dffb32023-08-07 15:45:43 +0400369) error {
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400370 appManager, err := installer.NewAppManager(r, nsCreator)
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400371 if err != nil {
372 return err
373 }
374 appsRepo := installer.NewInMemoryAppRepository(installer.CreateAllApps())
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400375 // TODO(giolekva): private domain can be configurable as well
376 config := installer.Config{
377 Values: installer.Values{
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400378 PCloudEnvName: pcloudEnvName,
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400379 Id: req.Name,
380 ContactEmail: req.ContactEmail,
381 Domain: req.Domain,
382 PrivateDomain: fmt.Sprintf("p.%s", req.Domain),
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400383 PublicIP: pcloudPublicIP,
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400384 NamespacePrefix: fmt.Sprintf("%s-", req.Name),
385 },
386 }
387 if err := r.WriteYaml("config.yaml", config); err != nil {
388 return err
389 }
390 {
391 out, err := r.Writer("pcloud-charts.yaml")
392 if err != nil {
393 return err
394 }
395 defer out.Close()
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400396 _, err = out.Write([]byte(fmt.Sprintf(`
Giorgi Lekveishvilibf1e6e82023-07-12 11:57:24 +0400397apiVersion: source.toolkit.fluxcd.io/v1
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400398kind: GitRepository
399metadata:
400 name: pcloud
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400401 namespace: %s
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400402spec:
403 interval: 1m0s
404 url: https://github.com/giolekva/pcloud
405 ref:
406 branch: main
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400407`, req.Name)))
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400408 if err != nil {
409 return err
410 }
411 }
412 rootKust := installer.NewKustomization()
413 rootKust.AddResources("pcloud-charts.yaml", "apps")
414 if err := r.WriteKustomization("kustomization.yaml", rootKust); err != nil {
415 return err
416 }
417 appsKust := installer.NewKustomization()
418 if err := r.WriteKustomization("apps/kustomization.yaml", appsKust); err != nil {
419 return err
420 }
421 r.CommitAndPush("initialize config")
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400422 nsGen := installer.NewPrefixGenerator(req.Name + "-")
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400423 emptySuffixGen := installer.NewEmptySuffixGenerator()
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400424 {
Giorgi Lekveishvili41563dc2023-07-20 10:37:35 +0400425 app, err := appsRepo.Find("metallb-ipaddresspool")
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400426 if err != nil {
427 return err
428 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400429 if err := appManager.Install(*app, nsGen, installer.NewSuffixGenerator("-ingress-private"), map[string]any{
Giorgi Lekveishvili41563dc2023-07-20 10:37:35 +0400430 "Name": fmt.Sprintf("%s-ingress-private", req.Name),
431 "From": "10.1.0.1",
432 "To": "10.1.0.1",
433 "AutoAssign": false,
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400434 "Namespace": "metallb-system",
Giorgi Lekveishvili41563dc2023-07-20 10:37:35 +0400435 }); err != nil {
436 return err
437 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400438 if err := appManager.Install(*app, nsGen, installer.NewSuffixGenerator("-headscale"), map[string]any{
Giorgi Lekveishvili41563dc2023-07-20 10:37:35 +0400439 "Name": fmt.Sprintf("%s-headscale", req.Name),
440 "From": "10.1.0.2",
441 "To": "10.1.0.2",
442 "AutoAssign": false,
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400443 "Namespace": "metallb-system",
Giorgi Lekveishvili41563dc2023-07-20 10:37:35 +0400444 }); err != nil {
445 return err
446 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400447 if err := appManager.Install(*app, nsGen, emptySuffixGen, map[string]any{
Giorgi Lekveishvili41563dc2023-07-20 10:37:35 +0400448 "Name": req.Name,
449 "From": "10.1.0.100",
450 "To": "10.1.0.254",
451 "AutoAssign": false,
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400452 "Namespace": "metallb-system",
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400453 }); err != nil {
454 return err
455 }
456 }
457 {
458 app, err := appsRepo.Find("ingress-private")
459 if err != nil {
460 return err
461 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400462 if err := appManager.Install(*app, nsGen, emptySuffixGen, map[string]any{}); err != nil {
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400463 return err
464 }
465 }
466 {
467 app, err := appsRepo.Find("certificate-issuer-public")
468 if err != nil {
469 return err
470 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400471 if err := appManager.Install(*app, nsGen, emptySuffixGen, map[string]any{}); err != nil {
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400472 return err
473 }
474 }
475 {
476 app, err := appsRepo.Find("core-auth")
477 if err != nil {
478 return err
479 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400480 if err := appManager.Install(*app, nsGen, emptySuffixGen, map[string]any{
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400481 "Subdomain": "test", // TODO(giolekva): make core-auth chart actually use this
482 }); err != nil {
483 return err
484 }
485 }
486 {
487 app, err := appsRepo.Find("headscale")
488 if err != nil {
489 return err
490 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400491 if err := appManager.Install(*app, nsGen, emptySuffixGen, map[string]any{
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400492 "Subdomain": "headscale",
493 }); err != nil {
494 return err
495 }
496 }
497 {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400498 keys, err := installer.NewSSHKeyPair("welcome")
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400499 if err != nil {
500 return err
501 }
502 user := fmt.Sprintf("%s-welcome", req.Name)
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400503 if err := ss.AddUser(user, keys.AuthorizedKey()); err != nil {
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400504 return err
505 }
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400506 if err := ss.AddReadWriteCollaborator("config", user); err != nil {
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400507 return err
508 }
509 app, err := appsRepo.Find("welcome")
510 if err != nil {
511 return err
512 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400513 if err := appManager.Install(*app, nsGen, emptySuffixGen, map[string]any{
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400514 "RepoAddr": ss.GetRepoAddress("config"),
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400515 "SSHPrivateKey": string(keys.RawPrivateKey()),
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400516 }); err != nil {
517 return err
518 }
519 }
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400520 {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400521 user := fmt.Sprintf("%s-appmanager", req.Name)
522 keys, err := installer.NewSSHKeyPair(user)
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400523 if err != nil {
524 return err
525 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400526 if err := ss.AddUser(user, keys.AuthorizedKey()); err != nil {
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400527 return err
528 }
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400529 if err := ss.AddReadWriteCollaborator("config", user); err != nil {
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400530 return err
531 }
532 app, err := appsRepo.Find("app-manager") // TODO(giolekva): configure
533 if err != nil {
534 return err
535 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400536 if err := appManager.Install(*app, nsGen, emptySuffixGen, map[string]any{
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400537 "RepoAddr": ss.GetRepoAddress("config"),
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400538 "SSHPrivateKey": string(keys.RawPrivateKey()),
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400539 }); err != nil {
540 return err
541 }
542 }
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400543 return nil
544}
545
546func addNewEnv(
547 repoIO installer.RepoIO,
548 req createEnvReq,
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400549 repoHost string,
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400550 keys *keygen.KeyPair,
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400551 pcloudRepoPublicKey []byte,
552) error {
553 kust, err := repoIO.ReadKustomization("environments/kustomization.yaml")
554 if err != nil {
555 return err
556 }
557 kust.AddResources(req.Name)
558 tmpls, err := template.ParseFS(filesTmpls, "env-tmpl/*.yaml")
559 if err != nil {
560 return err
561 }
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400562 for _, tmpl := range tmpls.Templates() {
563 dstPath := path.Join("environments", req.Name, tmpl.Name())
564 dst, err := repoIO.Writer(dstPath)
565 if err != nil {
566 return err
567 }
568 defer dst.Close()
569 if err := tmpl.Execute(dst, map[string]string{
570 "Name": req.Name,
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400571 "PrivateKey": base64.StdEncoding.EncodeToString(keys.RawPrivateKey()),
572 "PublicKey": base64.StdEncoding.EncodeToString(keys.RawAuthorizedKey()),
Giorgi Lekveishvili724885f2023-11-29 16:18:42 +0400573 "RepoHost": repoHost,
574 "RepoName": "config",
575 "KnownHosts": base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s %s", repoHost, pcloudRepoPublicKey))),
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400576 }); err != nil {
577 return err
578 }
579 }
580 if err := repoIO.WriteKustomization("environments/kustomization.yaml", *kust); err != nil {
581 return err
582 }
583 if err := repoIO.CommitAndPush(fmt.Sprintf("%s: initialize environment", req.Name)); err != nil {
584 return err
585 }
586 return nil
587}