blob: 029f6df21163143538aca49c1cd2d093ae33c61f [file] [log] [blame]
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +04001package welcome
2
3import (
4 "embed"
5 "encoding/base64"
6 "encoding/json"
7 "fmt"
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +04008 htemplate "html/template"
9 "io"
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +040010 "log"
11 "net/http"
12 "path"
13 "text/template"
14
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +040015 "github.com/charmbracelet/keygen"
16 "github.com/gorilla/mux"
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +040017
18 "github.com/giolekva/pcloud/core/installer"
19 "github.com/giolekva/pcloud/core/installer/soft"
20)
21
22//go:embed env-tmpl
23var filesTmpls embed.FS
24
25//go:embed create-env.html
26var createEnvFormHtml string
27
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +040028//go:embed env-created.html
29var envCreatedHtml string
30
31type Status string
32
33const (
34 StatusActive Status = "ACTIVE"
35 StatusAccepted Status = "ACCEPTED"
36)
37
38// TODO(giolekva): add CreatedAt and ValidUntil
39type invitation struct {
40 Token string `json:"token"`
41 Status Status `json:"status"`
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +040042}
43
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +040044type EnvServer struct {
45 port int
46 ss *soft.Client
47 repo installer.RepoIO
48 nsCreator installer.NamespaceCreator
49 nameGenerator installer.NameGenerator
50}
51
52func NewEnvServer(port int, ss *soft.Client, repo installer.RepoIO, nsCreator installer.NamespaceCreator, nameGenerator installer.NameGenerator) *EnvServer {
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +040053 return &EnvServer{
54 port,
55 ss,
56 repo,
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +040057 nsCreator,
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +040058 nameGenerator,
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +040059 }
60}
61
62func (s *EnvServer) Start() {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +040063 r := mux.NewRouter()
64 r.PathPrefix("/static/").Handler(http.FileServer(http.FS(staticAssets)))
65 r.Path("/env").Methods("GET").HandlerFunc(s.createEnvForm)
66 r.Path("/env").Methods("POST").HandlerFunc(s.createEnv)
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +040067 r.Path("/create-invitation").Methods("GET").HandlerFunc(s.createInvitation)
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +040068 http.Handle("/", r)
69 log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", s.port), nil))
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +040070}
71
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +040072func (s *EnvServer) createEnvForm(w http.ResponseWriter, r *http.Request) {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +040073 if _, err := w.Write([]byte(createEnvFormHtml)); err != nil {
74 http.Error(w, err.Error(), http.StatusInternalServerError)
75 }
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +040076}
77
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +040078func (s *EnvServer) createInvitation(w http.ResponseWriter, r *http.Request) {
79 invitations, err := s.readInvitations()
80 if err != nil {
81 http.Error(w, err.Error(), http.StatusInternalServerError)
82 return
83 }
84 token, err := installer.NewFixedLengthRandomNameGenerator(100).Generate() // TODO(giolekva): use cryptographic tokens
85 if err != nil {
86 http.Error(w, err.Error(), http.StatusInternalServerError)
87 return
88
89 }
90 invitations = append(invitations, invitation{token, StatusActive})
91 if err := s.writeInvitations(invitations); err != nil {
92 http.Error(w, err.Error(), http.StatusInternalServerError)
93 return
94 }
95 if _, err := w.Write([]byte("OK")); err != nil {
96 http.Error(w, err.Error(), http.StatusInternalServerError)
97 return
98 }
99}
100
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400101type createEnvReq struct {
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +0400102 Name string
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400103 ContactEmail string `json:"contactEmail"`
104 Domain string `json:"domain"`
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +0400105 SecretToken string `json:"secretToken"`
106}
107
108func (s *EnvServer) readInvitations() ([]invitation, error) {
109 r, err := s.repo.Reader("invitations")
110 if err != nil {
111 return nil, err
112 }
113 defer r.Close()
114 dec := json.NewDecoder(r)
115 invitations := make([]invitation, 0)
116 for {
117 var i invitation
118 if err := dec.Decode(&i); err == io.EOF {
119 break
120 }
121 invitations = append(invitations, i)
122 }
123 return invitations, nil
124}
125
126func (s *EnvServer) writeInvitations(invitations []invitation) error {
127 w, err := s.repo.Writer("invitations")
128 if err != nil {
129 return err
130 }
131 defer w.Close()
132 enc := json.NewEncoder(w)
133 for _, i := range invitations {
134 if err := enc.Encode(i); err != nil {
135 return err
136 }
137 }
138 return s.repo.CommitAndPush("Generated new invitation")
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400139}
140
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400141func (s *EnvServer) createEnv(w http.ResponseWriter, r *http.Request) {
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400142 var req createEnvReq
143 if err := func() error {
144 var err error
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400145 if err = r.ParseForm(); err != nil {
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400146 return err
147 }
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +0400148 if req.SecretToken, err = getFormValue(r.PostForm, "secret-token"); err != nil {
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400149 return err
150 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400151 if req.Domain, err = getFormValue(r.PostForm, "domain"); err != nil {
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400152 return err
153 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400154 if req.ContactEmail, err = getFormValue(r.PostForm, "contact-email"); err != nil {
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400155 return err
156 }
157 return nil
158 }(); err != nil {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400159 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
160 http.Error(w, err.Error(), http.StatusInternalServerError)
161 return
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400162 }
163 }
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +0400164 invitations, err := s.readInvitations()
165 if err != nil {
166 http.Error(w, err.Error(), http.StatusInternalServerError)
167 return
168 }
169 found := false
170 for _, i := range invitations {
171 if i.Token == req.SecretToken && i.Status == StatusActive {
172 i.Status = StatusAccepted
173 found = true
174 break
175 }
176 }
177 if !found {
178 http.Error(w, "Invalid invitation", http.StatusNotFound)
179 return
180 }
181 if err := s.writeInvitations(invitations); err != nil {
182 http.Error(w, err.Error(), http.StatusInternalServerError)
183 return
184 }
185 if name, err := s.nameGenerator.Generate(); err != nil {
186 http.Error(w, err.Error(), http.StatusInternalServerError)
187 return
188 } else {
189 req.Name = name
190 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400191 fluxUserName := fmt.Sprintf("flux-%s", req.Name)
192 keys, err := installer.NewSSHKeyPair(fluxUserName)
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400193 if err != nil {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400194 http.Error(w, err.Error(), http.StatusInternalServerError)
195 return
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400196 }
197 {
198 readme := fmt.Sprintf("# %s PCloud environment", req.Name)
199 if err := s.ss.AddRepository(req.Name, readme); err != nil {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400200 http.Error(w, err.Error(), http.StatusInternalServerError)
201 return
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400202 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400203 if err := s.ss.AddUser(fluxUserName, keys.AuthorizedKey()); err != nil {
204 http.Error(w, err.Error(), http.StatusInternalServerError)
205 return
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400206 }
207 if err := s.ss.AddCollaborator(req.Name, fluxUserName); err != nil {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400208 http.Error(w, err.Error(), http.StatusInternalServerError)
209 return
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400210 }
211 }
212 {
213 repo, err := s.ss.GetRepo(req.Name)
Giorgi Lekveishvili57dffb32023-08-07 15:45:43 +0400214 if err != nil {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400215 http.Error(w, err.Error(), http.StatusInternalServerError)
216 return
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400217 }
Giorgi Lekveishvili57dffb32023-08-07 15:45:43 +0400218 var env installer.EnvConfig
219 r, err := s.repo.Reader("config.yaml")
220 if err != nil {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400221 http.Error(w, err.Error(), http.StatusInternalServerError)
222 return
Giorgi Lekveishvili57dffb32023-08-07 15:45:43 +0400223 }
224 defer r.Close()
225 if err := installer.ReadYaml(r, &env); err != nil {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400226 http.Error(w, err.Error(), http.StatusInternalServerError)
227 return
Giorgi Lekveishvili57dffb32023-08-07 15:45:43 +0400228 }
229 if err := initNewEnv(s.ss, installer.NewRepoIO(repo, s.ss.Signer), s.nsCreator, req, env); err != nil {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400230 http.Error(w, err.Error(), http.StatusInternalServerError)
231 return
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400232 }
233 }
234 {
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400235 ssPubKey, err := s.ss.GetPublicKey()
236 if err != nil {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400237 http.Error(w, err.Error(), http.StatusInternalServerError)
238 return
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400239 }
240 if err := addNewEnv(
Giorgi Lekveishvili57dffb32023-08-07 15:45:43 +0400241 s.repo,
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400242 req,
243 keys,
244 ssPubKey,
245 ); err != nil {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400246 http.Error(w, err.Error(), http.StatusInternalServerError)
247 return
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400248 }
249 }
Giorgi Lekveishvili081f18f2023-11-07 14:58:10 +0400250 tmpl, err := htemplate.New("response").Parse(envCreatedHtml)
251 if err != nil {
252 http.Error(w, err.Error(), http.StatusInternalServerError)
253 return
254 }
255 if err := tmpl.Execute(w, map[string]any{
256 "Domain": req.Domain,
257 }); err != nil {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400258 http.Error(w, err.Error(), http.StatusInternalServerError)
259 return
260 }
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400261}
262
Giorgi Lekveishvili57dffb32023-08-07 15:45:43 +0400263func initNewEnv(
264 ss *soft.Client,
265 r installer.RepoIO,
266 nsCreator installer.NamespaceCreator,
267 req createEnvReq,
268 env installer.EnvConfig,
269) error {
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400270 appManager, err := installer.NewAppManager(r, nsCreator)
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400271 if err != nil {
272 return err
273 }
274 appsRepo := installer.NewInMemoryAppRepository(installer.CreateAllApps())
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400275 // TODO(giolekva): private domain can be configurable as well
276 config := installer.Config{
277 Values: installer.Values{
Giorgi Lekveishvili57dffb32023-08-07 15:45:43 +0400278 PCloudEnvName: env.Name,
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400279 Id: req.Name,
280 ContactEmail: req.ContactEmail,
281 Domain: req.Domain,
282 PrivateDomain: fmt.Sprintf("p.%s", req.Domain),
Giorgi Lekveishvili57dffb32023-08-07 15:45:43 +0400283 PublicIP: env.PublicIP,
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400284 NamespacePrefix: fmt.Sprintf("%s-", req.Name),
285 },
286 }
287 if err := r.WriteYaml("config.yaml", config); err != nil {
288 return err
289 }
290 {
291 out, err := r.Writer("pcloud-charts.yaml")
292 if err != nil {
293 return err
294 }
295 defer out.Close()
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400296 _, err = out.Write([]byte(fmt.Sprintf(`
Giorgi Lekveishvilibf1e6e82023-07-12 11:57:24 +0400297apiVersion: source.toolkit.fluxcd.io/v1
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400298kind: GitRepository
299metadata:
300 name: pcloud
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400301 namespace: %s
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400302spec:
303 interval: 1m0s
304 url: https://github.com/giolekva/pcloud
305 ref:
306 branch: main
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400307`, req.Name)))
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400308 if err != nil {
309 return err
310 }
311 }
312 rootKust := installer.NewKustomization()
313 rootKust.AddResources("pcloud-charts.yaml", "apps")
314 if err := r.WriteKustomization("kustomization.yaml", rootKust); err != nil {
315 return err
316 }
317 appsKust := installer.NewKustomization()
318 if err := r.WriteKustomization("apps/kustomization.yaml", appsKust); err != nil {
319 return err
320 }
321 r.CommitAndPush("initialize config")
Giorgi Lekveishvili7fb28bf2023-06-24 19:51:16 +0400322 nsGen := installer.NewPrefixGenerator(req.Name + "-")
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400323 emptySuffixGen := installer.NewEmptySuffixGenerator()
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400324 {
Giorgi Lekveishvili41563dc2023-07-20 10:37:35 +0400325 app, err := appsRepo.Find("metallb-ipaddresspool")
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400326 if err != nil {
327 return err
328 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400329 if err := appManager.Install(*app, nsGen, installer.NewSuffixGenerator("-ingress-private"), map[string]any{
Giorgi Lekveishvili41563dc2023-07-20 10:37:35 +0400330 "Name": fmt.Sprintf("%s-ingress-private", req.Name),
331 "From": "10.1.0.1",
332 "To": "10.1.0.1",
333 "AutoAssign": false,
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400334 "Namespace": "metallb-system",
Giorgi Lekveishvili41563dc2023-07-20 10:37:35 +0400335 }); err != nil {
336 return err
337 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400338 if err := appManager.Install(*app, nsGen, installer.NewSuffixGenerator("-headscale"), map[string]any{
Giorgi Lekveishvili41563dc2023-07-20 10:37:35 +0400339 "Name": fmt.Sprintf("%s-headscale", req.Name),
340 "From": "10.1.0.2",
341 "To": "10.1.0.2",
342 "AutoAssign": false,
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400343 "Namespace": "metallb-system",
Giorgi Lekveishvili41563dc2023-07-20 10:37:35 +0400344 }); err != nil {
345 return err
346 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400347 if err := appManager.Install(*app, nsGen, installer.NewSuffixGenerator("-soft-serve"), map[string]any{
Giorgi Lekveishvili41563dc2023-07-20 10:37:35 +0400348 "Name": fmt.Sprintf("%s-soft-serve", req.Name), // TODO(giolekva): rename to config repo
349 "From": "10.1.0.3",
350 "To": "10.1.0.3",
351 "AutoAssign": false,
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400352 "Namespace": "metallb-system",
Giorgi Lekveishvili41563dc2023-07-20 10:37:35 +0400353 }); err != nil {
354 return err
355 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400356 if err := appManager.Install(*app, nsGen, emptySuffixGen, map[string]any{
Giorgi Lekveishvili41563dc2023-07-20 10:37:35 +0400357 "Name": req.Name,
358 "From": "10.1.0.100",
359 "To": "10.1.0.254",
360 "AutoAssign": false,
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400361 "Namespace": "metallb-system",
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400362 }); err != nil {
363 return err
364 }
365 }
366 {
367 app, err := appsRepo.Find("ingress-private")
368 if err != nil {
369 return err
370 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400371 if err := appManager.Install(*app, nsGen, emptySuffixGen, map[string]any{}); err != nil {
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400372 return err
373 }
374 }
375 {
376 app, err := appsRepo.Find("certificate-issuer-public")
377 if err != nil {
378 return err
379 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400380 if err := appManager.Install(*app, nsGen, emptySuffixGen, map[string]any{}); err != nil {
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400381 return err
382 }
383 }
384 {
385 app, err := appsRepo.Find("core-auth")
386 if err != nil {
387 return err
388 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400389 if err := appManager.Install(*app, nsGen, emptySuffixGen, map[string]any{
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400390 "Subdomain": "test", // TODO(giolekva): make core-auth chart actually use this
391 }); err != nil {
392 return err
393 }
394 }
395 {
396 app, err := appsRepo.Find("headscale")
397 if err != nil {
398 return err
399 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400400 if err := appManager.Install(*app, nsGen, emptySuffixGen, map[string]any{
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400401 "Subdomain": "headscale",
402 }); err != nil {
403 return err
404 }
405 }
406 {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400407 keys, err := installer.NewSSHKeyPair("welcome")
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400408 if err != nil {
409 return err
410 }
411 user := fmt.Sprintf("%s-welcome", req.Name)
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400412 if err := ss.AddUser(user, keys.AuthorizedKey()); err != nil {
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400413 return err
414 }
415 if err := ss.AddCollaborator(req.Name, user); err != nil {
416 return err
417 }
418 app, err := appsRepo.Find("welcome")
419 if err != nil {
420 return err
421 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400422 if err := appManager.Install(*app, nsGen, emptySuffixGen, map[string]any{
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400423 "RepoAddr": ss.GetRepoAddress(req.Name),
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400424 "SSHPrivateKey": string(keys.RawPrivateKey()),
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400425 }); err != nil {
426 return err
427 }
428 }
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400429 {
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400430 user := fmt.Sprintf("%s-appmanager", req.Name)
431 keys, err := installer.NewSSHKeyPair(user)
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400432 if err != nil {
433 return err
434 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400435 if err := ss.AddUser(user, keys.AuthorizedKey()); err != nil {
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400436 return err
437 }
438 if err := ss.AddCollaborator(req.Name, user); err != nil {
439 return err
440 }
441 app, err := appsRepo.Find("app-manager") // TODO(giolekva): configure
442 if err != nil {
443 return err
444 }
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400445 if err := appManager.Install(*app, nsGen, emptySuffixGen, map[string]any{
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400446 "RepoAddr": ss.GetRepoAddress(req.Name),
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400447 "SSHPrivateKey": string(keys.RawPrivateKey()),
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400448 }); err != nil {
449 return err
450 }
451 }
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400452 return nil
453}
454
455func addNewEnv(
456 repoIO installer.RepoIO,
457 req createEnvReq,
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400458 keys *keygen.KeyPair,
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400459 pcloudRepoPublicKey []byte,
460) error {
461 kust, err := repoIO.ReadKustomization("environments/kustomization.yaml")
462 if err != nil {
463 return err
464 }
465 kust.AddResources(req.Name)
466 tmpls, err := template.ParseFS(filesTmpls, "env-tmpl/*.yaml")
467 if err != nil {
468 return err
469 }
Giorgi Lekveishvili94cda9d2023-07-20 10:16:09 +0400470 repoIP := repoIO.Addr().Addr().String()
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400471 for _, tmpl := range tmpls.Templates() {
472 dstPath := path.Join("environments", req.Name, tmpl.Name())
473 dst, err := repoIO.Writer(dstPath)
474 if err != nil {
475 return err
476 }
477 defer dst.Close()
478 if err := tmpl.Execute(dst, map[string]string{
479 "Name": req.Name,
Giorgi Lekveishvilia1e77902023-11-06 14:48:27 +0400480 "PrivateKey": base64.StdEncoding.EncodeToString(keys.RawPrivateKey()),
481 "PublicKey": base64.StdEncoding.EncodeToString(keys.RawAuthorizedKey()),
Giorgi Lekveishvilib4a9c982023-06-22 15:17:02 +0400482 "GitHost": repoIP,
483 "KnownHosts": base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s %s", repoIP, pcloudRepoPublicKey))),
484 }); err != nil {
485 return err
486 }
487 }
488 if err := repoIO.WriteKustomization("environments/kustomization.yaml", *kust); err != nil {
489 return err
490 }
491 if err := repoIO.CommitAndPush(fmt.Sprintf("%s: initialize environment", req.Name)); err != nil {
492 return err
493 }
494 return nil
495}