blob: 0d61af8a3cddaf7c35ed59bdfa38fb47f64c78cf [file] [log] [blame]
gioe72b54f2024-04-22 10:44:41 +04001package welcome
2
3import (
4 "bytes"
5 "encoding/json"
6 "golang.org/x/crypto/ssh"
7 "io"
8 "io/fs"
9 "log"
10 "net"
11 "net/http"
12 "strings"
13 "sync"
14 "testing"
15
16 "github.com/go-git/go-billy/v5"
17 "github.com/go-git/go-billy/v5/memfs"
18 "github.com/go-git/go-billy/v5/util"
19 // "github.com/go-git/go-git/v5"
20 // "github.com/go-git/go-git/v5/storage/memory"
21
22 "github.com/giolekva/pcloud/core/installer"
23 "github.com/giolekva/pcloud/core/installer/soft"
24)
25
26type fakeNSCreator struct {
27 t *testing.T
28}
29
30func (f fakeNSCreator) Create(name string) error {
31 f.t.Logf("Create namespace: %s", name)
32 return nil
33}
34
35type fakeZoneStatusFetcher struct {
36 t *testing.T
37}
38
39func (f fakeZoneStatusFetcher) Fetch(addr string) (string, error) {
40 f.t.Logf("Fetching status: %s", addr)
41 return addr, nil
42}
43
44type mockRepoIO struct {
45 soft.RepoFS
46 addr string
47 t *testing.T
48 l sync.Locker
49}
50
51func (r mockRepoIO) FullAddress() string {
52 return r.addr
53}
54
55func (r mockRepoIO) Pull() error {
56 r.t.Logf("Pull: %s", r.addr)
57 return nil
58}
59
60func (r mockRepoIO) CommitAndPush(message string) error {
61 r.t.Logf("Commit and push: %s", message)
62 return nil
63}
64
65func (r mockRepoIO) Do(op soft.DoFn, _ ...soft.DoOption) error {
66 r.l.Lock()
67 defer r.l.Unlock()
68 msg, err := op(r)
69 if err != nil {
70 return err
71 }
72 return r.CommitAndPush(msg)
73}
74
75type fakeSoftServeClient struct {
76 t *testing.T
77 envFS billy.Filesystem
78}
79
80func (f fakeSoftServeClient) Address() string {
81 return ""
82}
83
84func (f fakeSoftServeClient) Signer() ssh.Signer {
85 return nil
86}
87
88func (f fakeSoftServeClient) GetPublicKeys() ([]string, error) {
89 return []string{}, nil
90}
91
92func (f fakeSoftServeClient) GetRepo(name string) (soft.RepoIO, error) {
93 var l sync.Mutex
94 return mockRepoIO{soft.NewBillyRepoFS(f.envFS), "foo.bar", f.t, &l}, nil
95}
96
97func (f fakeSoftServeClient) GetRepoAddress(name string) string {
98 return ""
99}
100
101func (f fakeSoftServeClient) AddRepository(name string) error {
102 return nil
103}
104
105func (f fakeSoftServeClient) AddUser(name, pubKey string) error {
106 return nil
107}
108
109func (f fakeSoftServeClient) AddPublicKey(user string, pubKey string) error {
110 return nil
111}
112
113func (f fakeSoftServeClient) RemovePublicKey(user string, pubKey string) error {
114 return nil
115}
116
117func (f fakeSoftServeClient) MakeUserAdmin(name string) error {
118 return nil
119}
120
121func (f fakeSoftServeClient) AddReadWriteCollaborator(repo, user string) error {
122 return nil
123}
124
125func (f fakeSoftServeClient) AddReadOnlyCollaborator(repo, user string) error {
126 return nil
127}
128
129type fakeClientGetter struct {
130 t *testing.T
131 envFS billy.Filesystem
132}
133
134func (f fakeClientGetter) Get(addr string, clientPrivateKey []byte, log *log.Logger) (soft.Client, error) {
135 return fakeSoftServeClient{f.t, f.envFS}, nil
136}
137
138const infraConfig = `
139infraAdminPublicKey: Zm9vYmFyCg==
140namespacePrefix: infra-
141pcloudEnvName: infra
142publicIP:
143- 1.1.1.1
144- 2.2.2.2
145`
146
147const envCidrs = ``
148
149type fixedNameGenerator struct{}
150
151func (f fixedNameGenerator) Generate() (string, error) {
152 return "test", nil
153}
154
155type fakeHttpClient struct {
156 t *testing.T
157 counts map[string]int
158}
159
160func (f fakeHttpClient) Get(addr string) (*http.Response, error) {
161 f.t.Logf("HTTP GET: %s", addr)
162 cnt, ok := f.counts[addr]
163 if !ok {
164 cnt = 0
165 }
166 f.counts[addr] = cnt + 1
167 return &http.Response{
168 Status: "200 OK",
169 StatusCode: http.StatusOK,
170 Proto: "HTTP/1.0",
171 ProtoMajor: 1,
172 ProtoMinor: 0,
173 Body: io.NopCloser(strings.NewReader("ok")),
174 }, nil
175}
176
177type fakeDnsClient struct {
178 t *testing.T
179 counts map[string]int
180}
181
182func (f fakeDnsClient) Lookup(host string) ([]net.IP, error) {
183 f.t.Logf("HTTP GET: %s", host)
184 return []net.IP{net.ParseIP("1.1.1.1"), net.ParseIP("2.2.2.2")}, nil
185}
186
187func TestCreateNewEnv(t *testing.T) {
188 apps := installer.NewInMemoryAppRepository(installer.CreateAllApps())
189 infraFS := memfs.New()
190 envFS := memfs.New()
191 nsCreator := fakeNSCreator{t}
192 infraRepo := mockRepoIO{soft.NewBillyRepoFS(infraFS), "foo.bar", t, &sync.Mutex{}}
193 infraMgr, err := installer.NewInfraAppManager(infraRepo, nsCreator)
194 if err != nil {
195 t.Fatal(err)
196 }
197 if err := util.WriteFile(infraFS, "config.yaml", []byte(infraConfig), fs.ModePerm); err != nil {
198 t.Fatal(err)
199 }
200 if err := util.WriteFile(infraFS, "env-cidrs.yaml", []byte(envCidrs), fs.ModePerm); err != nil {
201 t.Fatal(err)
202 }
203 {
204 app, err := installer.FindInfraApp(apps, "dns-gateway")
205 if err != nil {
206 t.Fatal(err)
207 }
gio778577f2024-04-29 09:44:38 +0400208 if _, err := infraMgr.Install(app, "/infrastructure/dns-gateway", "dns-gateway", map[string]any{
gioe72b54f2024-04-22 10:44:41 +0400209 "servers": []installer.EnvDNS{},
210 }); err != nil {
211 t.Fatal(err)
212 }
213 }
214 cg := fakeClientGetter{t, envFS}
215 httpClient := fakeHttpClient{t, make(map[string]int)}
216 dnsClient := fakeDnsClient{t, make(map[string]int)}
217 s := NewEnvServer(
218 8181,
219 fakeSoftServeClient{t, envFS},
220 infraRepo,
221 cg,
222 nsCreator,
223 fakeZoneStatusFetcher{t},
224 fixedNameGenerator{},
225 httpClient,
226 dnsClient,
227 )
228 go s.Start()
229 req := createEnvReq{
230 Name: "test",
231 ContactEmail: "test@test.t",
232 Domain: "test.t",
233 AdminPublicKey: "test",
234 SecretToken: "test",
235 }
236 var buf bytes.Buffer
237 if err := json.NewEncoder(&buf).Encode(req); err != nil {
238 t.Fatal(err)
239 }
240 resp, err := http.Post("http://localhost:8181/", "application/json", &buf)
241 var done sync.WaitGroup
242 done.Add(1)
243 var taskErr error
244 s.Tasks["test"].OnDone(func(err error) {
245 taskErr = err
246 done.Done()
247 })
248 if err != nil {
249 t.Fatal(err)
250 }
251 if resp.StatusCode != http.StatusOK {
252 var buf bytes.Buffer
253 io.Copy(&buf, resp.Body)
254 t.Fatal(buf.String())
255 }
256 done.Wait()
257 http.Get("http://localhost:8181/env/test")
258 debugFS(infraFS, t, "/infrastructure/dns-gateway/resources/coredns.yaml")
259 debugFS(envFS, t)
260 if taskErr != nil {
261 t.Fatal(taskErr)
262 }
263 expected := []string{
264 "https://accounts-ui.test.t",
265 "https://welcome.test.t",
266 "https://memberships.p.test.t",
gio09a3e5b2024-04-26 14:11:06 +0400267 "https://launcher.test.t",
gioe72b54f2024-04-22 10:44:41 +0400268 "https://headscale.test.t/apple",
269 }
270 for _, e := range expected {
271 if cnt, ok := httpClient.counts[e]; !ok || cnt != 1 {
272 t.Fatal(httpClient.counts)
273 }
274 }
gio09a3e5b2024-04-26 14:11:06 +0400275 if len(httpClient.counts) != 5 {
gioe72b54f2024-04-22 10:44:41 +0400276 t.Fatal(httpClient.counts)
277 }
278}
279
280func debugFS(bfs billy.Filesystem, t *testing.T, files ...string) {
281 f := map[string]struct{}{}
282 for _, i := range files {
283 f[i] = struct{}{}
284 }
285 t.Log("----- START ------")
286 err := util.Walk(bfs, "/", func(path string, info fs.FileInfo, err error) error {
287 t.Logf("%s %t\n", path, info.IsDir())
288 if _, ok := f[path]; ok && !info.IsDir() {
289 contents, err := util.ReadFile(bfs, path)
290 if err != nil {
291 return err
292 }
293 t.Log(string(contents))
294 }
295 return nil
296 })
297 if err != nil {
298 t.Fatal(err)
299 }
300 t.Log("----- END ------")
301}