blob: adc8bebfa84aa2c8bf1c5f3873d0b3dccdee8274 [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{}
gio0710c6b2024-11-04 19:08:30 +0400163 log := log.Default()
164 log.SetOutput(io.Discard)
165 softClient, err := cg.Get(dodoAppFlags.repoAddr, sshKey, log)
gio0eaf2712024-04-14 13:08:46 +0400166 if err != nil {
167 return err
168 }
giof8843412024-05-22 16:38:05 +0400169 jc, err := newJobCreator()
170 if err != nil {
171 return err
172 }
gio33059762024-07-05 13:19:07 +0400173 nsc, err := newNSCreator()
174 if err != nil {
gio266c04f2024-07-03 14:18:45 +0400175 return err
176 }
gio59946282024-10-07 12:55:51 +0400177 if ok, err := softClient.RepoExists(dodoapp.ConfigRepoName); err != nil {
gioa60f0de2024-07-08 10:49:48 +0400178 return err
179 } else if !ok {
gio59946282024-10-07 12:55:51 +0400180 if err := softClient.AddRepository(dodoapp.ConfigRepoName); err != nil {
gioa60f0de2024-07-08 10:49:48 +0400181 return err
182 }
183 }
gio59946282024-10-07 12:55:51 +0400184 configRepo, err := softClient.GetRepo(dodoapp.ConfigRepoName)
gioa60f0de2024-07-08 10:49:48 +0400185 if err != nil {
186 return err
187 }
188 db, err := sql.Open("sqlite3", dodoAppFlags.db)
189 if err != nil {
190 return err
191 }
gio59946282024-10-07 12:55:51 +0400192 st, err := dodoapp.NewStore(configRepo, db)
gioa60f0de2024-07-08 10:49:48 +0400193 if err != nil {
194 return err
195 }
gio59946282024-10-07 12:55:51 +0400196 var nf dodoapp.NetworkFilter
gio11617ac2024-07-15 16:09:04 +0400197 if len(dodoAppFlags.networks) == 0 {
gio59946282024-10-07 12:55:51 +0400198 nf = dodoapp.NewNoNetworkFilter()
gio11617ac2024-07-15 16:09:04 +0400199 } else {
gio59946282024-10-07 12:55:51 +0400200 nf = dodoapp.NewAllowListFilter(dodoAppFlags.networks)
gio11617ac2024-07-15 16:09:04 +0400201 }
202 if dodoAppFlags.external {
gio59946282024-10-07 12:55:51 +0400203 nf = dodoapp.NewCombinedFilter(dodoapp.NewNetworkFilterByOwner(st), nf)
gio11617ac2024-07-15 16:09:04 +0400204 }
gio59946282024-10-07 12:55:51 +0400205 var ug dodoapp.UserGetter
gio11617ac2024-07-15 16:09:04 +0400206 if dodoAppFlags.external {
gio59946282024-10-07 12:55:51 +0400207 ug = dodoapp.NewExternalUserGetter()
gio11617ac2024-07-15 16:09:04 +0400208 } else {
gio59946282024-10-07 12:55:51 +0400209 ug = dodoapp.NewInternalUserGetter()
gio11617ac2024-07-15 16:09:04 +0400210 }
gio43b0f422024-08-21 10:40:13 +0400211 reconciler := &tasks.SequentialReconciler{
212 []tasks.Reconciler{
213 &tasks.SourceGitReconciler{},
214 // &tasks.KustomizationReconciler{},
215 },
216 }
gio36b23b32024-08-25 12:20:54 +0400217 vpnKeyGen := installer.NewHeadscaleAPIClient(dodoAppFlags.headscaleAPIAddr)
giof15b9da2024-09-19 06:59:16 +0400218 cnc := &proxyConfigurator{dodoAppFlags.envAppManagerAddr}
gio59946282024-10-07 12:55:51 +0400219 s, err := dodoapp.NewServer(
gioa60f0de2024-07-08 10:49:48 +0400220 st,
gio11617ac2024-07-15 16:09:04 +0400221 nf,
222 ug,
gio33059762024-07-05 13:19:07 +0400223 dodoAppFlags.port,
gioa60f0de2024-07-08 10:49:48 +0400224 dodoAppFlags.apiPort,
gio33059762024-07-05 13:19:07 +0400225 dodoAppFlags.self,
gioc81a8472024-09-24 13:06:19 +0200226 dodoAppFlags.selfPublic,
gio11617ac2024-07-15 16:09:04 +0400227 dodoAppFlags.repoPublicAddr,
gio33059762024-07-05 13:19:07 +0400228 string(sshKey),
229 dodoAppFlags.gitRepoPublicKey,
230 softClient,
231 dodoAppFlags.namespace,
giocb34ad22024-07-11 08:01:13 +0400232 dodoAppFlags.envAppManagerAddr,
gio33059762024-07-05 13:19:07 +0400233 nsc,
234 jc,
gio36b23b32024-08-25 12:20:54 +0400235 vpnKeyGen,
giof6ad2982024-08-23 17:42:49 +0400236 cnc,
gio33059762024-07-05 13:19:07 +0400237 env,
giocafd4e62024-07-31 10:53:40 +0400238 dodoAppFlags.external,
239 dodoAppFlags.fetchUsersAddr,
gio43b0f422024-08-21 10:40:13 +0400240 reconciler,
gio33059762024-07-05 13:19:07 +0400241 )
gio9d66f322024-07-06 13:45:10 +0400242 if err != nil {
243 return err
244 }
gio0eaf2712024-04-14 13:08:46 +0400245 return s.Start()
246}
giof15b9da2024-09-19 06:59:16 +0400247
248type proxyConfigurator struct {
249 apiAddr string
250}
251
252func (pc *proxyConfigurator) AddCluster(name string, ingressIP net.IP) error {
253 return fmt.Errorf("NOT IMPLEMENTED")
254}
255
256func (pc *proxyConfigurator) RemoveCluster(name string, ingressIP net.IP) error {
257 return fmt.Errorf("NOT IMPLEMENTED")
258}
259
260type proxyPair struct {
261 From string `json:"from"`
262 To string `json:"to"`
263}
264
265func (pc *proxyConfigurator) AddProxy(src, dst string) error {
266 var buf bytes.Buffer
267 if err := json.NewEncoder(&buf).Encode(proxyPair{src, dst}); err != nil {
268 return err
269 }
270 resp, err := http.Post(fmt.Sprintf("%s/api/proxy/add", pc.apiAddr), "application/json", &buf)
271 if err != nil {
272 return err
273 }
274 if resp.StatusCode != http.StatusOK {
275 var buf bytes.Buffer
276 io.Copy(&buf, resp.Body)
277 return fmt.Errorf(buf.String())
278 }
279 return nil
280}
281
282func (pc *proxyConfigurator) RemoveProxy(src, dst string) error {
283 var buf bytes.Buffer
284 if err := json.NewEncoder(&buf).Encode(proxyPair{src, dst}); err != nil {
285 return err
286 }
287 resp, err := http.Post(fmt.Sprintf("%s/api/proxy/remove", pc.apiAddr), "application/json", &buf)
288 if err != nil {
289 return err
290 }
291 if resp.StatusCode != http.StatusOK {
292 var buf bytes.Buffer
293 io.Copy(&buf, resp.Body)
294 return fmt.Errorf(buf.String())
295 }
296 return nil
297}