blob: bcd8a687ecf9ce182143a538af8dd6d1e6113e44 [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"
15 "github.com/giolekva/pcloud/core/installer/soft"
gio43b0f422024-08-21 10:40:13 +040016 "github.com/giolekva/pcloud/core/installer/tasks"
gio0eaf2712024-04-14 13:08:46 +040017 "github.com/giolekva/pcloud/core/installer/welcome"
18
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
gio11617ac2024-07-15 16:09:04 +040032 repoPublicAddr string
giocb34ad22024-07-11 08:01:13 +040033 namespace string
34 envAppManagerAddr string
35 envConfig string
giocb34ad22024-07-11 08:01:13 +040036 gitRepoPublicKey string
37 db string
gio23bdc1b2024-07-11 16:07:47 +040038 networks []string
giocafd4e62024-07-31 10:53:40 +040039 fetchUsersAddr string
gio36b23b32024-08-25 12:20:54 +040040 headscaleAPIAddr string
gio0eaf2712024-04-14 13:08:46 +040041}
42
43func dodoAppCmd() *cobra.Command {
44 cmd := &cobra.Command{
45 Use: "dodo-app",
46 RunE: dodoAppCmdRun,
47 }
gio11617ac2024-07-15 16:09:04 +040048 cmd.Flags().BoolVar(
49 &dodoAppFlags.external,
50 "external",
51 false,
52 "",
53 )
gio0eaf2712024-04-14 13:08:46 +040054 cmd.Flags().IntVar(
55 &dodoAppFlags.port,
56 "port",
57 8080,
58 "",
59 )
gioa60f0de2024-07-08 10:49:48 +040060 cmd.Flags().IntVar(
61 &dodoAppFlags.apiPort,
62 "api-port",
63 8081,
64 "",
65 )
66 cmd.Flags().StringVar(
67 &dodoAppFlags.db,
68 "db",
69 "",
70 "",
71 )
gio0eaf2712024-04-14 13:08:46 +040072 cmd.Flags().StringVar(
73 &dodoAppFlags.repoAddr,
74 "repo-addr",
75 "",
76 "",
77 )
78 cmd.Flags().StringVar(
79 &dodoAppFlags.sshKey,
80 "ssh-key",
81 "",
82 "",
83 )
84 cmd.Flags().StringVar(
85 &dodoAppFlags.self,
86 "self",
87 "",
88 "",
89 )
90 cmd.Flags().StringVar(
giocafd4e62024-07-31 10:53:40 +040091 &dodoAppFlags.fetchUsersAddr,
92 "fetch-users-addr",
93 "",
94 "",
95 )
96 cmd.Flags().StringVar(
gio11617ac2024-07-15 16:09:04 +040097 &dodoAppFlags.repoPublicAddr,
98 "repo-public-addr",
99 "",
100 "",
101 )
102 cmd.Flags().StringVar(
gio0eaf2712024-04-14 13:08:46 +0400103 &dodoAppFlags.namespace,
104 "namespace",
105 "",
106 "",
107 )
108 cmd.Flags().StringVar(
giocb34ad22024-07-11 08:01:13 +0400109 &dodoAppFlags.envAppManagerAddr,
110 "env-app-manager-addr",
111 "",
112 "",
113 )
114 cmd.Flags().StringVar(
gio0eaf2712024-04-14 13:08:46 +0400115 &dodoAppFlags.envConfig,
116 "env-config",
117 "",
118 "",
119 )
gio266c04f2024-07-03 14:18:45 +0400120 cmd.Flags().StringVar(
gio266c04f2024-07-03 14:18:45 +0400121 &dodoAppFlags.gitRepoPublicKey,
122 "git-repo-public-key",
123 "",
124 "",
125 )
gio23bdc1b2024-07-11 16:07:47 +0400126 cmd.Flags().StringSliceVar(
127 &dodoAppFlags.networks,
128 "networks",
129 []string{},
130 "",
131 )
gio36b23b32024-08-25 12:20:54 +0400132 cmd.Flags().StringVar(
133 &dodoAppFlags.headscaleAPIAddr,
134 "headscale-api-addr",
135 "",
136 "",
137 )
gio0eaf2712024-04-14 13:08:46 +0400138 return cmd
139}
140
141func dodoAppCmdRun(cmd *cobra.Command, args []string) error {
gioa60f0de2024-07-08 10:49:48 +0400142 sshKey, err := os.ReadFile(dodoAppFlags.sshKey)
143 if err != nil {
144 return err
145 }
gio0eaf2712024-04-14 13:08:46 +0400146 envConfig, err := os.Open(dodoAppFlags.envConfig)
147 if err != nil {
148 return err
149 }
150 defer envConfig.Close()
151 var env installer.EnvConfig
152 if err := json.NewDecoder(envConfig).Decode(&env); err != nil {
153 return err
154 }
gio33059762024-07-05 13:19:07 +0400155 cg := soft.RealClientGetter{}
156 softClient, err := cg.Get(dodoAppFlags.repoAddr, sshKey, log.Default())
gio0eaf2712024-04-14 13:08:46 +0400157 if err != nil {
158 return err
159 }
giof8843412024-05-22 16:38:05 +0400160 jc, err := newJobCreator()
161 if err != nil {
162 return err
163 }
gio33059762024-07-05 13:19:07 +0400164 nsc, err := newNSCreator()
165 if err != nil {
gio266c04f2024-07-03 14:18:45 +0400166 return err
167 }
gioa60f0de2024-07-08 10:49:48 +0400168 if ok, err := softClient.RepoExists(welcome.ConfigRepoName); err != nil {
169 return err
170 } else if !ok {
171 if err := softClient.AddRepository(welcome.ConfigRepoName); err != nil {
172 return err
173 }
174 }
175 configRepo, err := softClient.GetRepo(welcome.ConfigRepoName)
176 if err != nil {
177 return err
178 }
179 db, err := sql.Open("sqlite3", dodoAppFlags.db)
180 if err != nil {
181 return err
182 }
183 st, err := welcome.NewStore(configRepo, db)
184 if err != nil {
185 return err
186 }
gio11617ac2024-07-15 16:09:04 +0400187 var nf welcome.NetworkFilter
188 if len(dodoAppFlags.networks) == 0 {
189 nf = welcome.NewNoNetworkFilter()
190 } else {
191 nf = welcome.NewAllowListFilter(dodoAppFlags.networks)
192 }
193 if dodoAppFlags.external {
194 nf = welcome.NewCombinedFilter(welcome.NewNetworkFilterByOwner(st), nf)
195 }
196 var ug welcome.UserGetter
197 if dodoAppFlags.external {
198 ug = welcome.NewExternalUserGetter()
199 } else {
200 ug = welcome.NewInternalUserGetter()
201 }
gio43b0f422024-08-21 10:40:13 +0400202 reconciler := &tasks.SequentialReconciler{
203 []tasks.Reconciler{
204 &tasks.SourceGitReconciler{},
205 // &tasks.KustomizationReconciler{},
206 },
207 }
gio36b23b32024-08-25 12:20:54 +0400208 vpnKeyGen := installer.NewHeadscaleAPIClient(dodoAppFlags.headscaleAPIAddr)
giof15b9da2024-09-19 06:59:16 +0400209 cnc := &proxyConfigurator{dodoAppFlags.envAppManagerAddr}
gio9d66f322024-07-06 13:45:10 +0400210 s, err := welcome.NewDodoAppServer(
gioa60f0de2024-07-08 10:49:48 +0400211 st,
gio11617ac2024-07-15 16:09:04 +0400212 nf,
213 ug,
gio33059762024-07-05 13:19:07 +0400214 dodoAppFlags.port,
gioa60f0de2024-07-08 10:49:48 +0400215 dodoAppFlags.apiPort,
gio33059762024-07-05 13:19:07 +0400216 dodoAppFlags.self,
gio11617ac2024-07-15 16:09:04 +0400217 dodoAppFlags.repoPublicAddr,
gio33059762024-07-05 13:19:07 +0400218 string(sshKey),
219 dodoAppFlags.gitRepoPublicKey,
220 softClient,
221 dodoAppFlags.namespace,
giocb34ad22024-07-11 08:01:13 +0400222 dodoAppFlags.envAppManagerAddr,
gio33059762024-07-05 13:19:07 +0400223 nsc,
224 jc,
gio36b23b32024-08-25 12:20:54 +0400225 vpnKeyGen,
giof6ad2982024-08-23 17:42:49 +0400226 cnc,
gio33059762024-07-05 13:19:07 +0400227 env,
giocafd4e62024-07-31 10:53:40 +0400228 dodoAppFlags.external,
229 dodoAppFlags.fetchUsersAddr,
gio43b0f422024-08-21 10:40:13 +0400230 reconciler,
gio33059762024-07-05 13:19:07 +0400231 )
gio9d66f322024-07-06 13:45:10 +0400232 if err != nil {
233 return err
234 }
gio0eaf2712024-04-14 13:08:46 +0400235 return s.Start()
236}
giof15b9da2024-09-19 06:59:16 +0400237
238type proxyConfigurator struct {
239 apiAddr string
240}
241
242func (pc *proxyConfigurator) AddCluster(name string, ingressIP net.IP) error {
243 return fmt.Errorf("NOT IMPLEMENTED")
244}
245
246func (pc *proxyConfigurator) RemoveCluster(name string, ingressIP net.IP) error {
247 return fmt.Errorf("NOT IMPLEMENTED")
248}
249
250type proxyPair struct {
251 From string `json:"from"`
252 To string `json:"to"`
253}
254
255func (pc *proxyConfigurator) AddProxy(src, dst string) error {
256 var buf bytes.Buffer
257 if err := json.NewEncoder(&buf).Encode(proxyPair{src, dst}); err != nil {
258 return err
259 }
260 resp, err := http.Post(fmt.Sprintf("%s/api/proxy/add", pc.apiAddr), "application/json", &buf)
261 if err != nil {
262 return err
263 }
264 if resp.StatusCode != http.StatusOK {
265 var buf bytes.Buffer
266 io.Copy(&buf, resp.Body)
267 return fmt.Errorf(buf.String())
268 }
269 return nil
270}
271
272func (pc *proxyConfigurator) RemoveProxy(src, dst string) error {
273 var buf bytes.Buffer
274 if err := json.NewEncoder(&buf).Encode(proxyPair{src, dst}); err != nil {
275 return err
276 }
277 resp, err := http.Post(fmt.Sprintf("%s/api/proxy/remove", pc.apiAddr), "application/json", &buf)
278 if err != nil {
279 return err
280 }
281 if resp.StatusCode != http.StatusOK {
282 var buf bytes.Buffer
283 io.Copy(&buf, resp.Body)
284 return fmt.Errorf(buf.String())
285 }
286 return nil
287}