blob: 7f1957d47ce320b9a8feb4fad0fa36d175f18e1b [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"
giob7df27f2026-07-28 10:36:17 +04007 "errors"
giof15b9da2024-09-19 06:59:16 +04008 "fmt"
9 "io"
gio0eaf2712024-04-14 13:08:46 +040010 "log"
giof15b9da2024-09-19 06:59:16 +040011 "net"
12 "net/http"
gio0eaf2712024-04-14 13:08:46 +040013 "os"
14
15 "github.com/giolekva/pcloud/core/installer"
gio59946282024-10-07 12:55:51 +040016 dodoapp "github.com/giolekva/pcloud/core/installer/server/dodo-app"
gio0eaf2712024-04-14 13:08:46 +040017 "github.com/giolekva/pcloud/core/installer/soft"
gio43b0f422024-08-21 10:40:13 +040018 "github.com/giolekva/pcloud/core/installer/tasks"
gio0eaf2712024-04-14 13:08:46 +040019
gioa60f0de2024-07-08 10:49:48 +040020 _ "github.com/ncruces/go-sqlite3"
21 _ "github.com/ncruces/go-sqlite3/driver"
22 _ "github.com/ncruces/go-sqlite3/embed"
gio0eaf2712024-04-14 13:08:46 +040023 "github.com/spf13/cobra"
24)
25
26var dodoAppFlags struct {
gio11617ac2024-07-15 16:09:04 +040027 external bool
giocb34ad22024-07-11 08:01:13 +040028 port int
29 apiPort int
30 sshKey string
31 repoAddr string
32 self string
gioc81a8472024-09-24 13:06:19 +020033 selfPublic string
gio11617ac2024-07-15 16:09:04 +040034 repoPublicAddr string
giocb34ad22024-07-11 08:01:13 +040035 namespace string
36 envAppManagerAddr string
37 envConfig string
giocb34ad22024-07-11 08:01:13 +040038 gitRepoPublicKey string
39 db string
gio23bdc1b2024-07-11 16:07:47 +040040 networks []string
giocafd4e62024-07-31 10:53:40 +040041 fetchUsersAddr string
gio36b23b32024-08-25 12:20:54 +040042 headscaleAPIAddr string
gio0eaf2712024-04-14 13:08:46 +040043}
44
45func dodoAppCmd() *cobra.Command {
46 cmd := &cobra.Command{
47 Use: "dodo-app",
48 RunE: dodoAppCmdRun,
49 }
gio11617ac2024-07-15 16:09:04 +040050 cmd.Flags().BoolVar(
51 &dodoAppFlags.external,
52 "external",
53 false,
54 "",
55 )
gio0eaf2712024-04-14 13:08:46 +040056 cmd.Flags().IntVar(
57 &dodoAppFlags.port,
58 "port",
59 8080,
60 "",
61 )
gioa60f0de2024-07-08 10:49:48 +040062 cmd.Flags().IntVar(
63 &dodoAppFlags.apiPort,
64 "api-port",
65 8081,
66 "",
67 )
68 cmd.Flags().StringVar(
69 &dodoAppFlags.db,
70 "db",
71 "",
72 "",
73 )
gio0eaf2712024-04-14 13:08:46 +040074 cmd.Flags().StringVar(
75 &dodoAppFlags.repoAddr,
76 "repo-addr",
77 "",
78 "",
79 )
80 cmd.Flags().StringVar(
81 &dodoAppFlags.sshKey,
82 "ssh-key",
83 "",
84 "",
85 )
86 cmd.Flags().StringVar(
87 &dodoAppFlags.self,
88 "self",
89 "",
90 "",
91 )
92 cmd.Flags().StringVar(
gioc81a8472024-09-24 13:06:19 +020093 &dodoAppFlags.selfPublic,
94 "self-public",
95 "",
96 "",
97 )
98 cmd.Flags().StringVar(
giocafd4e62024-07-31 10:53:40 +040099 &dodoAppFlags.fetchUsersAddr,
100 "fetch-users-addr",
101 "",
102 "",
103 )
104 cmd.Flags().StringVar(
gio11617ac2024-07-15 16:09:04 +0400105 &dodoAppFlags.repoPublicAddr,
106 "repo-public-addr",
107 "",
108 "",
109 )
110 cmd.Flags().StringVar(
gio0eaf2712024-04-14 13:08:46 +0400111 &dodoAppFlags.namespace,
112 "namespace",
113 "",
114 "",
115 )
116 cmd.Flags().StringVar(
giocb34ad22024-07-11 08:01:13 +0400117 &dodoAppFlags.envAppManagerAddr,
118 "env-app-manager-addr",
119 "",
120 "",
121 )
122 cmd.Flags().StringVar(
gio0eaf2712024-04-14 13:08:46 +0400123 &dodoAppFlags.envConfig,
124 "env-config",
125 "",
126 "",
127 )
gio266c04f2024-07-03 14:18:45 +0400128 cmd.Flags().StringVar(
gio266c04f2024-07-03 14:18:45 +0400129 &dodoAppFlags.gitRepoPublicKey,
130 "git-repo-public-key",
131 "",
132 "",
133 )
gio23bdc1b2024-07-11 16:07:47 +0400134 cmd.Flags().StringSliceVar(
135 &dodoAppFlags.networks,
136 "networks",
137 []string{},
138 "",
139 )
gio36b23b32024-08-25 12:20:54 +0400140 cmd.Flags().StringVar(
141 &dodoAppFlags.headscaleAPIAddr,
142 "headscale-api-addr",
143 "",
144 "",
145 )
gio0eaf2712024-04-14 13:08:46 +0400146 return cmd
147}
148
149func dodoAppCmdRun(cmd *cobra.Command, args []string) error {
gioa60f0de2024-07-08 10:49:48 +0400150 sshKey, err := os.ReadFile(dodoAppFlags.sshKey)
151 if err != nil {
152 return err
153 }
gio0eaf2712024-04-14 13:08:46 +0400154 envConfig, err := os.Open(dodoAppFlags.envConfig)
155 if err != nil {
156 return err
157 }
158 defer envConfig.Close()
159 var env installer.EnvConfig
160 if err := json.NewDecoder(envConfig).Decode(&env); err != nil {
161 return err
162 }
gio33059762024-07-05 13:19:07 +0400163 cg := soft.RealClientGetter{}
gio0710c6b2024-11-04 19:08:30 +0400164 log := log.Default()
165 log.SetOutput(io.Discard)
166 softClient, err := cg.Get(dodoAppFlags.repoAddr, sshKey, log)
gio0eaf2712024-04-14 13:08:46 +0400167 if err != nil {
168 return err
169 }
giof8843412024-05-22 16:38:05 +0400170 jc, err := newJobCreator()
171 if err != nil {
172 return err
173 }
gio33059762024-07-05 13:19:07 +0400174 nsc, err := newNSCreator()
175 if err != nil {
gio266c04f2024-07-03 14:18:45 +0400176 return err
177 }
gio59946282024-10-07 12:55:51 +0400178 if ok, err := softClient.RepoExists(dodoapp.ConfigRepoName); err != nil {
gioa60f0de2024-07-08 10:49:48 +0400179 return err
180 } else if !ok {
gio59946282024-10-07 12:55:51 +0400181 if err := softClient.AddRepository(dodoapp.ConfigRepoName); err != nil {
gioa60f0de2024-07-08 10:49:48 +0400182 return err
183 }
184 }
gio59946282024-10-07 12:55:51 +0400185 configRepo, err := softClient.GetRepo(dodoapp.ConfigRepoName)
gioa60f0de2024-07-08 10:49:48 +0400186 if err != nil {
187 return err
188 }
189 db, err := sql.Open("sqlite3", dodoAppFlags.db)
190 if err != nil {
191 return err
192 }
gio59946282024-10-07 12:55:51 +0400193 st, err := dodoapp.NewStore(configRepo, db)
gioa60f0de2024-07-08 10:49:48 +0400194 if err != nil {
195 return err
196 }
gio59946282024-10-07 12:55:51 +0400197 var nf dodoapp.NetworkFilter
gio11617ac2024-07-15 16:09:04 +0400198 if len(dodoAppFlags.networks) == 0 {
gio59946282024-10-07 12:55:51 +0400199 nf = dodoapp.NewNoNetworkFilter()
gio11617ac2024-07-15 16:09:04 +0400200 } else {
gio59946282024-10-07 12:55:51 +0400201 nf = dodoapp.NewAllowListFilter(dodoAppFlags.networks)
gio11617ac2024-07-15 16:09:04 +0400202 }
203 if dodoAppFlags.external {
gio59946282024-10-07 12:55:51 +0400204 nf = dodoapp.NewCombinedFilter(dodoapp.NewNetworkFilterByOwner(st), nf)
gio11617ac2024-07-15 16:09:04 +0400205 }
gio59946282024-10-07 12:55:51 +0400206 var ug dodoapp.UserGetter
gio11617ac2024-07-15 16:09:04 +0400207 if dodoAppFlags.external {
gio59946282024-10-07 12:55:51 +0400208 ug = dodoapp.NewExternalUserGetter()
gio11617ac2024-07-15 16:09:04 +0400209 } else {
gio59946282024-10-07 12:55:51 +0400210 ug = dodoapp.NewInternalUserGetter()
gio11617ac2024-07-15 16:09:04 +0400211 }
gio43b0f422024-08-21 10:40:13 +0400212 reconciler := &tasks.SequentialReconciler{
213 []tasks.Reconciler{
214 &tasks.SourceGitReconciler{},
215 // &tasks.KustomizationReconciler{},
216 },
217 }
gio36b23b32024-08-25 12:20:54 +0400218 vpnKeyGen := installer.NewHeadscaleAPIClient(dodoAppFlags.headscaleAPIAddr)
giof15b9da2024-09-19 06:59:16 +0400219 cnc := &proxyConfigurator{dodoAppFlags.envAppManagerAddr}
gio59946282024-10-07 12:55:51 +0400220 s, err := dodoapp.NewServer(
gioa60f0de2024-07-08 10:49:48 +0400221 st,
gio11617ac2024-07-15 16:09:04 +0400222 nf,
223 ug,
gio33059762024-07-05 13:19:07 +0400224 dodoAppFlags.port,
gioa60f0de2024-07-08 10:49:48 +0400225 dodoAppFlags.apiPort,
gio33059762024-07-05 13:19:07 +0400226 dodoAppFlags.self,
gioc81a8472024-09-24 13:06:19 +0200227 dodoAppFlags.selfPublic,
gio11617ac2024-07-15 16:09:04 +0400228 dodoAppFlags.repoPublicAddr,
gio33059762024-07-05 13:19:07 +0400229 string(sshKey),
230 dodoAppFlags.gitRepoPublicKey,
231 softClient,
232 dodoAppFlags.namespace,
giocb34ad22024-07-11 08:01:13 +0400233 dodoAppFlags.envAppManagerAddr,
gio33059762024-07-05 13:19:07 +0400234 nsc,
235 jc,
gio36b23b32024-08-25 12:20:54 +0400236 vpnKeyGen,
giof6ad2982024-08-23 17:42:49 +0400237 cnc,
gio33059762024-07-05 13:19:07 +0400238 env,
giocafd4e62024-07-31 10:53:40 +0400239 dodoAppFlags.external,
240 dodoAppFlags.fetchUsersAddr,
gio43b0f422024-08-21 10:40:13 +0400241 reconciler,
gio33059762024-07-05 13:19:07 +0400242 )
gio9d66f322024-07-06 13:45:10 +0400243 if err != nil {
244 return err
245 }
gio0eaf2712024-04-14 13:08:46 +0400246 return s.Start()
247}
giof15b9da2024-09-19 06:59:16 +0400248
gio721c0042025-04-03 11:56:36 +0400249// TODO(gio): this should be removed, all of app installtions including dodo app
250// must be done directly by main app manager.
giof15b9da2024-09-19 06:59:16 +0400251type proxyConfigurator struct {
252 apiAddr string
253}
254
255func (pc *proxyConfigurator) AddCluster(name string, ingressIP net.IP) error {
256 return fmt.Errorf("NOT IMPLEMENTED")
257}
258
259func (pc *proxyConfigurator) RemoveCluster(name string, ingressIP net.IP) error {
260 return fmt.Errorf("NOT IMPLEMENTED")
261}
262
263type proxyPair struct {
264 From string `json:"from"`
265 To string `json:"to"`
266}
267
gio721c0042025-04-03 11:56:36 +0400268func (pc *proxyConfigurator) AddIngressProxy(src, dst string) error {
giof15b9da2024-09-19 06:59:16 +0400269 var buf bytes.Buffer
270 if err := json.NewEncoder(&buf).Encode(proxyPair{src, dst}); err != nil {
271 return err
272 }
273 resp, err := http.Post(fmt.Sprintf("%s/api/proxy/add", pc.apiAddr), "application/json", &buf)
274 if err != nil {
275 return err
276 }
277 if resp.StatusCode != http.StatusOK {
278 var buf bytes.Buffer
279 io.Copy(&buf, resp.Body)
giob7df27f2026-07-28 10:36:17 +0400280 return errors.New(buf.String())
giof15b9da2024-09-19 06:59:16 +0400281 }
282 return nil
283}
284
gio721c0042025-04-03 11:56:36 +0400285func (pc *proxyConfigurator) AddProxy(src int, dst string, protocol installer.Protocol) (string, error) {
286 // TODO(gio): implement
287 return "", fmt.Errorf("NOT IMPLEMENTED")
288}
289
290func (pc *proxyConfigurator) RemoveProxy(src int, dst string, protocol installer.Protocol) error {
291 // TODO(gio): implement
292 return fmt.Errorf("NOT IMPLEMENTED")
293}
294
295func (pc *proxyConfigurator) RemoveIngressProxy(src, dst string) error {
giof15b9da2024-09-19 06:59:16 +0400296 var buf bytes.Buffer
297 if err := json.NewEncoder(&buf).Encode(proxyPair{src, dst}); err != nil {
298 return err
299 }
300 resp, err := http.Post(fmt.Sprintf("%s/api/proxy/remove", pc.apiAddr), "application/json", &buf)
301 if err != nil {
302 return err
303 }
304 if resp.StatusCode != http.StatusOK {
305 var buf bytes.Buffer
306 io.Copy(&buf, resp.Body)
giob7df27f2026-07-28 10:36:17 +0400307 return errors.New(buf.String())
giof15b9da2024-09-19 06:59:16 +0400308 }
309 return nil
310}