blob: 9b5da30aa96c66e5dcafb3d7c1a5450068243335 [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
gio721c0042025-04-03 11:56:36 +0400248// TODO(gio): this should be removed, all of app installtions including dodo app
249// must be done directly by main app manager.
giof15b9da2024-09-19 06:59:16 +0400250type proxyConfigurator struct {
251 apiAddr string
252}
253
254func (pc *proxyConfigurator) AddCluster(name string, ingressIP net.IP) error {
255 return fmt.Errorf("NOT IMPLEMENTED")
256}
257
258func (pc *proxyConfigurator) RemoveCluster(name string, ingressIP net.IP) error {
259 return fmt.Errorf("NOT IMPLEMENTED")
260}
261
262type proxyPair struct {
263 From string `json:"from"`
264 To string `json:"to"`
265}
266
gio721c0042025-04-03 11:56:36 +0400267func (pc *proxyConfigurator) AddIngressProxy(src, dst string) error {
giof15b9da2024-09-19 06:59:16 +0400268 var buf bytes.Buffer
269 if err := json.NewEncoder(&buf).Encode(proxyPair{src, dst}); err != nil {
270 return err
271 }
272 resp, err := http.Post(fmt.Sprintf("%s/api/proxy/add", pc.apiAddr), "application/json", &buf)
273 if err != nil {
274 return err
275 }
276 if resp.StatusCode != http.StatusOK {
277 var buf bytes.Buffer
278 io.Copy(&buf, resp.Body)
279 return fmt.Errorf(buf.String())
280 }
281 return nil
282}
283
gio721c0042025-04-03 11:56:36 +0400284func (pc *proxyConfigurator) AddProxy(src int, dst string, protocol installer.Protocol) (string, error) {
285 // TODO(gio): implement
286 return "", fmt.Errorf("NOT IMPLEMENTED")
287}
288
289func (pc *proxyConfigurator) RemoveProxy(src int, dst string, protocol installer.Protocol) error {
290 // TODO(gio): implement
291 return fmt.Errorf("NOT IMPLEMENTED")
292}
293
294func (pc *proxyConfigurator) RemoveIngressProxy(src, dst string) error {
giof15b9da2024-09-19 06:59:16 +0400295 var buf bytes.Buffer
296 if err := json.NewEncoder(&buf).Encode(proxyPair{src, dst}); err != nil {
297 return err
298 }
299 resp, err := http.Post(fmt.Sprintf("%s/api/proxy/remove", pc.apiAddr), "application/json", &buf)
300 if err != nil {
301 return err
302 }
303 if resp.StatusCode != http.StatusOK {
304 var buf bytes.Buffer
305 io.Copy(&buf, resp.Body)
306 return fmt.Errorf(buf.String())
307 }
308 return nil
309}