blob: 562627c87ddc3404e3e81442a4eb9ff8ef2f8f29 [file] [log] [blame]
gio0eaf2712024-04-14 13:08:46 +04001package main
2
3import (
giof15b9da2024-09-19 06:59:16 +04004 "bytes"
gioa60f0de2024-07-08 10:49:48 +04005 "database/sql"
gio0eaf2712024-04-14 13:08:46 +04006 "encoding/json"
giof15b9da2024-09-19 06:59:16 +04007 "fmt"
8 "io"
gio0eaf2712024-04-14 13:08:46 +04009 "log"
giof15b9da2024-09-19 06:59:16 +040010 "net"
11 "net/http"
gio0eaf2712024-04-14 13:08:46 +040012 "os"
13
14 "github.com/giolekva/pcloud/core/installer"
gio59946282024-10-07 12:55:51 +040015 dodoapp "github.com/giolekva/pcloud/core/installer/server/dodo-app"
gio0eaf2712024-04-14 13:08:46 +040016 "github.com/giolekva/pcloud/core/installer/soft"
gio43b0f422024-08-21 10:40:13 +040017 "github.com/giolekva/pcloud/core/installer/tasks"
gio0eaf2712024-04-14 13:08:46 +040018
gioa60f0de2024-07-08 10:49:48 +040019 _ "github.com/ncruces/go-sqlite3"
20 _ "github.com/ncruces/go-sqlite3/driver"
21 _ "github.com/ncruces/go-sqlite3/embed"
gio0eaf2712024-04-14 13:08:46 +040022 "github.com/spf13/cobra"
23)
24
25var dodoAppFlags struct {
gio11617ac2024-07-15 16:09:04 +040026 external bool
giocb34ad22024-07-11 08:01:13 +040027 port int
28 apiPort int
29 sshKey string
30 repoAddr string
31 self string
gioc81a8472024-09-24 13:06:19 +020032 selfPublic string
gio11617ac2024-07-15 16:09:04 +040033 repoPublicAddr string
giocb34ad22024-07-11 08:01:13 +040034 namespace string
35 envAppManagerAddr string
36 envConfig string
giocb34ad22024-07-11 08:01:13 +040037 gitRepoPublicKey string
38 db string
gio23bdc1b2024-07-11 16:07:47 +040039 networks []string
giocafd4e62024-07-31 10:53:40 +040040 fetchUsersAddr string
gio36b23b32024-08-25 12:20:54 +040041 headscaleAPIAddr string
gio0eaf2712024-04-14 13:08:46 +040042}
43
44func dodoAppCmd() *cobra.Command {
45 cmd := &cobra.Command{
46 Use: "dodo-app",
47 RunE: dodoAppCmdRun,
48 }
gio11617ac2024-07-15 16:09:04 +040049 cmd.Flags().BoolVar(
50 &dodoAppFlags.external,
51 "external",
52 false,
53 "",
54 )
gio0eaf2712024-04-14 13:08:46 +040055 cmd.Flags().IntVar(
56 &dodoAppFlags.port,
57 "port",
58 8080,
59 "",
60 )
gioa60f0de2024-07-08 10:49:48 +040061 cmd.Flags().IntVar(
62 &dodoAppFlags.apiPort,
63 "api-port",
64 8081,
65 "",
66 )
67 cmd.Flags().StringVar(
68 &dodoAppFlags.db,
69 "db",
70 "",
71 "",
72 )
gio0eaf2712024-04-14 13:08:46 +040073 cmd.Flags().StringVar(
74 &dodoAppFlags.repoAddr,
75 "repo-addr",
76 "",
77 "",
78 )
79 cmd.Flags().StringVar(
80 &dodoAppFlags.sshKey,
81 "ssh-key",
82 "",
83 "",
84 )
85 cmd.Flags().StringVar(
86 &dodoAppFlags.self,
87 "self",
88 "",
89 "",
90 )
91 cmd.Flags().StringVar(
gioc81a8472024-09-24 13:06:19 +020092 &dodoAppFlags.selfPublic,
93 "self-public",
94 "",
95 "",
96 )
97 cmd.Flags().StringVar(
giocafd4e62024-07-31 10:53:40 +040098 &dodoAppFlags.fetchUsersAddr,
99 "fetch-users-addr",
100 "",
101 "",
102 )
103 cmd.Flags().StringVar(
gio11617ac2024-07-15 16:09:04 +0400104 &dodoAppFlags.repoPublicAddr,
105 "repo-public-addr",
106 "",
107 "",
108 )
109 cmd.Flags().StringVar(
gio0eaf2712024-04-14 13:08:46 +0400110 &dodoAppFlags.namespace,
111 "namespace",
112 "",
113 "",
114 )
115 cmd.Flags().StringVar(
giocb34ad22024-07-11 08:01:13 +0400116 &dodoAppFlags.envAppManagerAddr,
117 "env-app-manager-addr",
118 "",
119 "",
120 )
121 cmd.Flags().StringVar(
gio0eaf2712024-04-14 13:08:46 +0400122 &dodoAppFlags.envConfig,
123 "env-config",
124 "",
125 "",
126 )
gio266c04f2024-07-03 14:18:45 +0400127 cmd.Flags().StringVar(
gio266c04f2024-07-03 14:18:45 +0400128 &dodoAppFlags.gitRepoPublicKey,
129 "git-repo-public-key",
130 "",
131 "",
132 )
gio23bdc1b2024-07-11 16:07:47 +0400133 cmd.Flags().StringSliceVar(
134 &dodoAppFlags.networks,
135 "networks",
136 []string{},
137 "",
138 )
gio36b23b32024-08-25 12:20:54 +0400139 cmd.Flags().StringVar(
140 &dodoAppFlags.headscaleAPIAddr,
141 "headscale-api-addr",
142 "",
143 "",
144 )
gio0eaf2712024-04-14 13:08:46 +0400145 return cmd
146}
147
148func dodoAppCmdRun(cmd *cobra.Command, args []string) error {
gioa60f0de2024-07-08 10:49:48 +0400149 sshKey, err := os.ReadFile(dodoAppFlags.sshKey)
150 if err != nil {
151 return err
152 }
gio0eaf2712024-04-14 13:08:46 +0400153 envConfig, err := os.Open(dodoAppFlags.envConfig)
154 if err != nil {
155 return err
156 }
157 defer envConfig.Close()
158 var env installer.EnvConfig
159 if err := json.NewDecoder(envConfig).Decode(&env); err != nil {
160 return err
161 }
gio33059762024-07-05 13:19:07 +0400162 cg := soft.RealClientGetter{}
163 softClient, err := cg.Get(dodoAppFlags.repoAddr, sshKey, log.Default())
gio0eaf2712024-04-14 13:08:46 +0400164 if err != nil {
165 return err
166 }
giof8843412024-05-22 16:38:05 +0400167 jc, err := newJobCreator()
168 if err != nil {
169 return err
170 }
gio33059762024-07-05 13:19:07 +0400171 nsc, err := newNSCreator()
172 if err != nil {
gio266c04f2024-07-03 14:18:45 +0400173 return err
174 }
gio59946282024-10-07 12:55:51 +0400175 if ok, err := softClient.RepoExists(dodoapp.ConfigRepoName); err != nil {
gioa60f0de2024-07-08 10:49:48 +0400176 return err
177 } else if !ok {
gio59946282024-10-07 12:55:51 +0400178 if err := softClient.AddRepository(dodoapp.ConfigRepoName); err != nil {
gioa60f0de2024-07-08 10:49:48 +0400179 return err
180 }
181 }
gio59946282024-10-07 12:55:51 +0400182 configRepo, err := softClient.GetRepo(dodoapp.ConfigRepoName)
gioa60f0de2024-07-08 10:49:48 +0400183 if err != nil {
184 return err
185 }
186 db, err := sql.Open("sqlite3", dodoAppFlags.db)
187 if err != nil {
188 return err
189 }
gio59946282024-10-07 12:55:51 +0400190 st, err := dodoapp.NewStore(configRepo, db)
gioa60f0de2024-07-08 10:49:48 +0400191 if err != nil {
192 return err
193 }
gio59946282024-10-07 12:55:51 +0400194 var nf dodoapp.NetworkFilter
gio11617ac2024-07-15 16:09:04 +0400195 if len(dodoAppFlags.networks) == 0 {
gio59946282024-10-07 12:55:51 +0400196 nf = dodoapp.NewNoNetworkFilter()
gio11617ac2024-07-15 16:09:04 +0400197 } else {
gio59946282024-10-07 12:55:51 +0400198 nf = dodoapp.NewAllowListFilter(dodoAppFlags.networks)
gio11617ac2024-07-15 16:09:04 +0400199 }
200 if dodoAppFlags.external {
gio59946282024-10-07 12:55:51 +0400201 nf = dodoapp.NewCombinedFilter(dodoapp.NewNetworkFilterByOwner(st), nf)
gio11617ac2024-07-15 16:09:04 +0400202 }
gio59946282024-10-07 12:55:51 +0400203 var ug dodoapp.UserGetter
gio11617ac2024-07-15 16:09:04 +0400204 if dodoAppFlags.external {
gio59946282024-10-07 12:55:51 +0400205 ug = dodoapp.NewExternalUserGetter()
gio11617ac2024-07-15 16:09:04 +0400206 } else {
gio59946282024-10-07 12:55:51 +0400207 ug = dodoapp.NewInternalUserGetter()
gio11617ac2024-07-15 16:09:04 +0400208 }
gio43b0f422024-08-21 10:40:13 +0400209 reconciler := &tasks.SequentialReconciler{
210 []tasks.Reconciler{
211 &tasks.SourceGitReconciler{},
212 // &tasks.KustomizationReconciler{},
213 },
214 }
gio36b23b32024-08-25 12:20:54 +0400215 vpnKeyGen := installer.NewHeadscaleAPIClient(dodoAppFlags.headscaleAPIAddr)
giof15b9da2024-09-19 06:59:16 +0400216 cnc := &proxyConfigurator{dodoAppFlags.envAppManagerAddr}
gio59946282024-10-07 12:55:51 +0400217 s, err := dodoapp.NewServer(
gioa60f0de2024-07-08 10:49:48 +0400218 st,
gio11617ac2024-07-15 16:09:04 +0400219 nf,
220 ug,
gio33059762024-07-05 13:19:07 +0400221 dodoAppFlags.port,
gioa60f0de2024-07-08 10:49:48 +0400222 dodoAppFlags.apiPort,
gio33059762024-07-05 13:19:07 +0400223 dodoAppFlags.self,
gioc81a8472024-09-24 13:06:19 +0200224 dodoAppFlags.selfPublic,
gio11617ac2024-07-15 16:09:04 +0400225 dodoAppFlags.repoPublicAddr,
gio33059762024-07-05 13:19:07 +0400226 string(sshKey),
227 dodoAppFlags.gitRepoPublicKey,
228 softClient,
229 dodoAppFlags.namespace,
giocb34ad22024-07-11 08:01:13 +0400230 dodoAppFlags.envAppManagerAddr,
gio33059762024-07-05 13:19:07 +0400231 nsc,
232 jc,
gio36b23b32024-08-25 12:20:54 +0400233 vpnKeyGen,
giof6ad2982024-08-23 17:42:49 +0400234 cnc,
gio33059762024-07-05 13:19:07 +0400235 env,
giocafd4e62024-07-31 10:53:40 +0400236 dodoAppFlags.external,
237 dodoAppFlags.fetchUsersAddr,
gio43b0f422024-08-21 10:40:13 +0400238 reconciler,
gio33059762024-07-05 13:19:07 +0400239 )
gio9d66f322024-07-06 13:45:10 +0400240 if err != nil {
241 return err
242 }
gio0eaf2712024-04-14 13:08:46 +0400243 return s.Start()
244}
giof15b9da2024-09-19 06:59:16 +0400245
246type proxyConfigurator struct {
247 apiAddr string
248}
249
250func (pc *proxyConfigurator) AddCluster(name string, ingressIP net.IP) error {
251 return fmt.Errorf("NOT IMPLEMENTED")
252}
253
254func (pc *proxyConfigurator) RemoveCluster(name string, ingressIP net.IP) error {
255 return fmt.Errorf("NOT IMPLEMENTED")
256}
257
258type proxyPair struct {
259 From string `json:"from"`
260 To string `json:"to"`
261}
262
263func (pc *proxyConfigurator) AddProxy(src, dst string) error {
264 var buf bytes.Buffer
265 if err := json.NewEncoder(&buf).Encode(proxyPair{src, dst}); err != nil {
266 return err
267 }
268 resp, err := http.Post(fmt.Sprintf("%s/api/proxy/add", pc.apiAddr), "application/json", &buf)
269 if err != nil {
270 return err
271 }
272 if resp.StatusCode != http.StatusOK {
273 var buf bytes.Buffer
274 io.Copy(&buf, resp.Body)
275 return fmt.Errorf(buf.String())
276 }
277 return nil
278}
279
280func (pc *proxyConfigurator) RemoveProxy(src, dst string) error {
281 var buf bytes.Buffer
282 if err := json.NewEncoder(&buf).Encode(proxyPair{src, dst}); err != nil {
283 return err
284 }
285 resp, err := http.Post(fmt.Sprintf("%s/api/proxy/remove", pc.apiAddr), "application/json", &buf)
286 if err != nil {
287 return err
288 }
289 if resp.StatusCode != http.StatusOK {
290 var buf bytes.Buffer
291 io.Copy(&buf, resp.Body)
292 return fmt.Errorf(buf.String())
293 }
294 return nil
295}