blob: 3e4f6bd57a99346c7d7183d6f4aa29fc527cdf84 [file] [log] [blame]
gio0eaf2712024-04-14 13:08:46 +04001package main
2
3import (
4 "encoding/json"
5 "errors"
6 "fmt"
7 "log"
8 "os"
9
10 "github.com/giolekva/pcloud/core/installer"
11 "github.com/giolekva/pcloud/core/installer/soft"
12 "github.com/giolekva/pcloud/core/installer/welcome"
13
14 "github.com/spf13/cobra"
15)
16
17var dodoAppFlags struct {
gio266c04f2024-07-03 14:18:45 +040018 port int
19 sshKey string
20 repoAddr string
21 self string
22 namespace string
23 envConfig string
24 appAdminKey string
25 gitRepoPublicKey string
gio0eaf2712024-04-14 13:08:46 +040026}
27
28func dodoAppCmd() *cobra.Command {
29 cmd := &cobra.Command{
30 Use: "dodo-app",
31 RunE: dodoAppCmdRun,
32 }
33 cmd.Flags().IntVar(
34 &dodoAppFlags.port,
35 "port",
36 8080,
37 "",
38 )
39 cmd.Flags().StringVar(
40 &dodoAppFlags.repoAddr,
41 "repo-addr",
42 "",
43 "",
44 )
45 cmd.Flags().StringVar(
46 &dodoAppFlags.sshKey,
47 "ssh-key",
48 "",
49 "",
50 )
51 cmd.Flags().StringVar(
52 &dodoAppFlags.self,
53 "self",
54 "",
55 "",
56 )
57 cmd.Flags().StringVar(
58 &dodoAppFlags.namespace,
59 "namespace",
60 "",
61 "",
62 )
63 cmd.Flags().StringVar(
64 &dodoAppFlags.envConfig,
65 "env-config",
66 "",
67 "",
68 )
gio266c04f2024-07-03 14:18:45 +040069 cmd.Flags().StringVar(
70 &dodoAppFlags.appAdminKey,
71 "app-admin-key",
72 "",
73 "",
74 )
75 cmd.Flags().StringVar(
76 &dodoAppFlags.gitRepoPublicKey,
77 "git-repo-public-key",
78 "",
79 "",
80 )
gio0eaf2712024-04-14 13:08:46 +040081 return cmd
82}
83
84func dodoAppCmdRun(cmd *cobra.Command, args []string) error {
85 envConfig, err := os.Open(dodoAppFlags.envConfig)
86 if err != nil {
87 return err
88 }
89 defer envConfig.Close()
90 var env installer.EnvConfig
91 if err := json.NewDecoder(envConfig).Decode(&env); err != nil {
92 return err
93 }
94 sshKey, err := os.ReadFile(dodoAppFlags.sshKey)
95 if err != nil {
96 return err
97 }
98 softClient, err := soft.NewClient(dodoAppFlags.repoAddr, sshKey, log.Default())
99 if err != nil {
100 return err
101 }
giof8843412024-05-22 16:38:05 +0400102 jc, err := newJobCreator()
103 if err != nil {
104 return err
105 }
gio266c04f2024-07-03 14:18:45 +0400106 if err := softClient.AddRepository("config"); err == nil {
107 repo, err := softClient.GetRepo("config")
108 if err != nil {
109 return err
110 }
111 appRepo := installer.NewInMemoryAppRepository(installer.CreateAllApps())
112 app, err := installer.FindEnvApp(appRepo, "dodo-app-instance")
113 if err != nil {
114 return err
115 }
116 nsc := installer.NewNoOpNamespaceCreator()
117 if err != nil {
118 return err
119 }
120 hf := installer.NewGitHelmFetcher()
121 m, err := installer.NewAppManager(repo, nsc, jc, hf, "/")
122 if err != nil {
123 return err
124 }
125 if _, err := m.Install(app, "app", "/app", dodoAppFlags.namespace, map[string]any{
126 "appName": "app",
127 "repoAddr": softClient.GetRepoAddress("app"),
128 "gitRepoPublicKey": dodoAppFlags.gitRepoPublicKey,
129 }, installer.WithConfig(&env)); err != nil {
130 return err
131 }
132 if cfg, err := m.FindInstance("app"); err != nil {
133 return err
134 } else {
135 fluxKeys, ok := cfg.Input["fluxKeys"]
136 if !ok {
137 return fmt.Errorf("Fluxcd keys not found")
138 }
139 fluxPublicKey, ok := fluxKeys.(map[string]any)["public"]
140 if !ok {
141 return fmt.Errorf("Fluxcd keys not found")
142 }
143 if err := softClient.AddUser("fluxcd", fluxPublicKey.(string)); err != nil {
144 return err
145 }
146 if err := softClient.AddReadOnlyCollaborator("app", "fluxcd"); err != nil {
147 return err
148 }
149 }
150 } else if !errors.Is(err, soft.ErrorAlreadyExists) {
151 return err
152 }
gio0eaf2712024-04-14 13:08:46 +0400153 if err := softClient.AddRepository("app"); err == nil {
154 repo, err := softClient.GetRepo("app")
155 if err != nil {
156 return err
157 }
158 if err := initRepo(repo); err != nil {
159 return err
160 }
gio266c04f2024-07-03 14:18:45 +0400161 if err := welcome.UpdateDodoApp("app", softClient, dodoAppFlags.namespace, string(sshKey), jc, &env); err != nil {
gio0eaf2712024-04-14 13:08:46 +0400162 return err
163 }
164 if err := softClient.AddWebhook("app", fmt.Sprintf("http://%s/update", dodoAppFlags.self), "--active=true", "--events=push", "--content-type=json"); err != nil {
165 return err
166 }
gio266c04f2024-07-03 14:18:45 +0400167 if err := softClient.AddUser("app", dodoAppFlags.appAdminKey); err != nil {
168 return err
169 }
170 if err := softClient.AddReadWriteCollaborator("app", "app"); err != nil {
171 return err
172 }
gio0eaf2712024-04-14 13:08:46 +0400173 } else if !errors.Is(err, soft.ErrorAlreadyExists) {
174 return err
175 }
giof8843412024-05-22 16:38:05 +0400176 s := welcome.NewDodoAppServer(dodoAppFlags.port, string(sshKey), softClient, dodoAppFlags.namespace, jc, env)
gio0eaf2712024-04-14 13:08:46 +0400177 return s.Start()
178}
179
180const goMod = `module dodo.app
181
182go 1.18
183`
184
185const mainGo = `package main
186
187import (
188 "flag"
189 "fmt"
190 "log"
191 "net/http"
192)
193
194var port = flag.Int("port", 8080, "Port to listen on")
195
196func handler(w http.ResponseWriter, r *http.Request) {
197 fmt.Fprintln(w, "Hello from Dodo App!")
198}
199
200func main() {
201 flag.Parse()
202 http.HandleFunc("/", handler)
203 log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil))
204}
205`
206
207const appCue = `app: {
208 type: "golang:1.22.0"
209 run: "main.go"
210 ingress: {
211 network: "Private" // or Public
212 subdomain: "testapp"
213 auth: enabled: false
214 }
215}
216`
217
218func initRepo(repo soft.RepoIO) error {
219 return repo.Do(func(fs soft.RepoFS) (string, error) {
220 {
221 w, err := fs.Writer("go.mod")
222 if err != nil {
223 return "", err
224 }
225 defer w.Close()
226 fmt.Fprint(w, goMod)
227 }
228 {
229 w, err := fs.Writer("main.go")
230 if err != nil {
231 return "", err
232 }
233 defer w.Close()
234 fmt.Fprintf(w, "%s", mainGo)
235 }
236 {
237 w, err := fs.Writer("app.cue")
238 if err != nil {
239 return "", err
240 }
241 defer w.Close()
242 fmt.Fprint(w, appCue)
243 }
244 return "go web app template", nil
245 })
246}