blob: 33b40589e56fb758c7b01f3fccf41af54f72bedc [file] [log] [blame]
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +04001package main
2
3import (
4 "encoding/base64"
5 "encoding/json"
6 "fmt"
7 "log"
8 "os"
9 "path"
10 "text/template"
11
12 "github.com/labstack/echo/v4"
13 "github.com/spf13/cobra"
14
15 "github.com/giolekva/pcloud/core/installer"
16 "github.com/giolekva/pcloud/core/installer/soft"
17)
18
19var envManagerFlags struct {
20 repoIP string
21 repoPort int
22 sshKey string
23 port int
24}
25
26func envManagerCmd() *cobra.Command {
27 cmd := &cobra.Command{
28 Use: "envmanager",
29 RunE: envManagerCmdRun,
30 }
31 cmd.Flags().StringVar(
32 &envManagerFlags.repoIP,
33 "repo-ip",
34 "",
35 "",
36 )
37 cmd.Flags().IntVar(
38 &envManagerFlags.repoPort,
39 "repo-port",
40 22,
41 "",
42 )
43 cmd.Flags().StringVar(
44 &envManagerFlags.sshKey,
45 "ssh-key",
46 "",
47 "",
48 )
49 cmd.Flags().IntVar(
50 &envManagerFlags.port,
51 "port",
52 8080,
53 "",
54 )
55 return cmd
56}
57
58func envManagerCmdRun(cmd *cobra.Command, args []string) error {
59 sshKey, err := os.ReadFile(envManagerFlags.sshKey)
60 if err != nil {
61 return err
62 }
63 fmt.Println(string(sshKey))
64 ss, err := soft.NewClient(envManagerFlags.repoIP, envManagerFlags.repoPort, sshKey, log.Default())
65 if err != nil {
66 return err
67 }
68 b, err := ss.GetPublicKey()
69 if err != nil {
70 return err
71 }
72 fmt.Println(string(b))
73 fmt.Println(111)
74 repo, err := ss.GetRepo("pcloud")
75 fmt.Println(222)
76 if err != nil {
77 return err
78 }
79 fmt.Println(333)
80 repoIO := installer.NewRepoIO(repo, ss.Signer)
81 s := &envServer{
82 port: envManagerFlags.port,
83 ss: ss,
84 repo: repoIO,
85 }
86 s.start()
87 return nil
88}
89
90type envServer struct {
91 port int
92 ss *soft.Client
93 repo installer.RepoIO
94}
95
96func (s *envServer) start() {
97 e := echo.New()
98 e.POST("/env", s.createEnv)
99 log.Fatal(e.Start(fmt.Sprintf(":%d", s.port)))
100}
101
102type createEnvReq struct {
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400103 Name string `json:"name"`
104 ContactEmail string `json:"contactEmail"`
105 Domain string `json:"domain"`
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400106}
107
108func (s *envServer) createEnv(c echo.Context) error {
109 var req createEnvReq
110 if err := json.NewDecoder(c.Request().Body).Decode(&req); err != nil {
111 return err
112 }
113 keys, err := installer.NewSSHKeyPair()
114 if err != nil {
115 return err
116 }
117 {
118 readme := fmt.Sprintf("# %s PCloud environment", req.Name)
119 if err := s.ss.AddRepository(req.Name, readme); err != nil {
120 return err
121 }
122 fluxUserName := fmt.Sprintf("flux-%s", req.Name)
123 if err := s.ss.AddUser(fluxUserName, keys.Public); err != nil {
124 return err
125 }
126 if err := s.ss.AddCollaborator(req.Name, fluxUserName); err != nil {
127 return err
128 }
129 }
130 {
131 repo, err := s.ss.GetRepo(req.Name)
132 if repo == nil {
133 return err
134 }
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400135 if err := initNewEnv(s.ss, installer.NewRepoIO(repo, s.ss.Signer), req); err != nil {
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400136 return err
137 }
138 }
139 {
140 repo, err := s.ss.GetRepo("pcloud")
141 if err != nil {
142 return err
143 }
144 ssPubKey, err := s.ss.GetPublicKey()
145 if err != nil {
146 return err
147 }
148 if err := addNewEnv(
149 installer.NewRepoIO(repo, s.ss.Signer),
150 req,
151 keys,
152 ssPubKey,
153 ); err != nil {
154 return err
155 }
156 }
157 return nil
158}
159
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400160func initNewEnv(ss *soft.Client, r installer.RepoIO, req createEnvReq) error {
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400161 appManager, err := installer.NewAppManager(r)
162 if err != nil {
163 return err
164 }
165 appsRepo := installer.NewInMemoryAppRepository(installer.CreateAllApps())
166 // TODO(giolekva): env name and ip should come from pcloud repo config.yaml
167 // TODO(giolekva): private domain can be configurable as well
168 config := installer.Config{
169 Values: installer.Values{
170 PCloudEnvName: "pcloud",
171 Id: req.Name,
172 ContactEmail: req.ContactEmail,
173 Domain: req.Domain,
174 PrivateDomain: fmt.Sprintf("p.%s", req.Domain),
175 PublicIP: "46.49.35.44",
176 NamespacePrefix: fmt.Sprintf("%s-", req.Name),
177 },
178 }
179 if err := r.WriteYaml("config.yaml", config); err != nil {
180 return err
181 }
182 {
183 out, err := r.Writer("pcloud-charts.yaml")
184 if err != nil {
185 return err
186 }
187 defer out.Close()
188 _, err = out.Write([]byte(`
189apiVersion: source.toolkit.fluxcd.io/v1beta2
190kind: GitRepository
191metadata:
192 name: pcloud
193 namespace: lekva
194spec:
195 interval: 1m0s
196 url: https://github.com/giolekva/pcloud
197 ref:
198 branch: main
199`))
200 if err != nil {
201 return err
202 }
203 }
204 rootKust := installer.NewKustomization()
205 rootKust.AddResources("pcloud-charts.yaml", "apps")
206 if err := r.WriteKustomization("kustomization.yaml", rootKust); err != nil {
207 return err
208 }
209 appsKust := installer.NewKustomization()
210 if err := r.WriteKustomization("apps/kustomization.yaml", appsKust); err != nil {
211 return err
212 }
213 r.CommitAndPush("initialize config")
214 {
215 app, err := appsRepo.Find("metallb-config-env")
216 if err != nil {
217 return err
218 }
219 if err := appManager.Install(*app, map[string]any{
220 "IngressPrivate": "10.1.0.1",
221 "Headscale": "10.1.0.2",
222 "SoftServe": "10.1.0.3",
223 "Rest": map[string]any{
224 "From": "10.1.0.100",
225 "To": "10.1.0.255",
226 },
227 }); err != nil {
228 return err
229 }
230 }
231 {
232 app, err := appsRepo.Find("ingress-private")
233 if err != nil {
234 return err
235 }
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400236 if err := appManager.Install(*app, map[string]any{}); err != nil {
237 return err
238 }
239 }
240 {
241 app, err := appsRepo.Find("certificate-issuer-public")
242 if err != nil {
243 return err
244 }
245 if err := appManager.Install(*app, map[string]any{}); err != nil {
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400246 return err
247 }
248 }
249 {
250 app, err := appsRepo.Find("core-auth")
251 if err != nil {
252 return err
253 }
254 if err := appManager.Install(*app, map[string]any{
255 "Subdomain": "test", // TODO(giolekva): make core-auth chart actually use this
256 }); err != nil {
257 return err
258 }
259 }
260 {
261 app, err := appsRepo.Find("headscale")
262 if err != nil {
263 return err
264 }
265 if err := appManager.Install(*app, map[string]any{
266 "Subdomain": "headscale",
267 }); err != nil {
268 return err
269 }
270 }
271 {
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400272 keys, err := installer.NewSSHKeyPair()
273 if err != nil {
274 return err
275 }
276 user := fmt.Sprintf("%s-welcome", req.Name)
277 if err := ss.AddUser(user, keys.Public); err != nil {
278 return err
279 }
280 if err := ss.AddCollaborator(req.Name, user); err != nil {
281 return err
282 }
283 app, err := appsRepo.Find("welcome")
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400284 if err != nil {
285 return err
286 }
287 if err := appManager.Install(*app, map[string]any{
Giorgi Lekveishvili12850ee2023-06-22 13:11:17 +0400288 "RepoAddr": ss.GetRepoAddress(req.Name),
289 "SSHPrivateKey": keys.Private,
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400290 }); err != nil {
291 return err
292 }
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400293 }
Giorgi Lekveishvili0ccd1482023-06-21 15:02:24 +0400294 return nil
295}
296
297func addNewEnv(
298 repoIO installer.RepoIO,
299 req createEnvReq,
300 keys installer.KeyPair,
301 pcloudRepoPublicKey []byte,
302) error {
303 kust, err := repoIO.ReadKustomization("environments/kustomization.yaml")
304 if err != nil {
305 return err
306 }
307 kust.AddResources(req.Name)
308 tmpls, err := template.ParseFS(filesTmpls, "env-tmpl/*.yaml")
309 if err != nil {
310 return err
311 }
312 for _, tmpl := range tmpls.Templates() {
313 dstPath := path.Join("environments", req.Name, tmpl.Name())
314 dst, err := repoIO.Writer(dstPath)
315 if err != nil {
316 return err
317 }
318 defer dst.Close()
319 if err := tmpl.Execute(dst, map[string]string{
320 "Name": req.Name,
321 "PrivateKey": base64.StdEncoding.EncodeToString([]byte(keys.Private)),
322 "PublicKey": base64.StdEncoding.EncodeToString([]byte(keys.Public)),
323 "GitHost": envManagerFlags.repoIP,
324 "KnownHosts": base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s %s", envManagerFlags.repoIP, pcloudRepoPublicKey))),
325 }); err != nil {
326 return err
327 }
328 }
329 if err := repoIO.WriteKustomization("environments/kustomization.yaml", *kust); err != nil {
330 return err
331 }
332 if err := repoIO.CommitAndPush(fmt.Sprintf("%s: initialize environment", req.Name)); err != nil {
333 return err
334 }
335 return nil
336}