master
go 382 lines 8.84 KB
Raw
1 package migrations
2
3 import (
4 "context"
5 "fmt"
6 "log"
7 "os"
8 "path/filepath"
9 "strings"
10 "testing"
11
12 config "github.com/ipfs/kubo/config"
13 )
14
15 func TestFindMigrations(t *testing.T) {
16 tmpDir := t.TempDir()
17
18 ctx := t.Context()
19
20 migs, bins, err := findMigrations(ctx, 0, 5)
21 if err != nil {
22 t.Fatal(err)
23 }
24 if len(migs) != 5 {
25 t.Fatal("expected 5 migrations")
26 }
27 if len(bins) != 0 {
28 t.Fatal("should not have found migrations")
29 }
30
31 for i := 1; i < 6; i++ {
32 createFakeBin(i-1, i, tmpDir)
33 }
34
35 t.Setenv("PATH", tmpDir)
36
37 migs, bins, err = findMigrations(ctx, 0, 5)
38 if err != nil {
39 t.Fatal(err)
40 }
41 if len(migs) != 5 {
42 t.Fatal("expected 5 migrations")
43 }
44 if len(bins) != len(migs) {
45 t.Fatal("missing", len(migs)-len(bins), "migrations")
46 }
47
48 os.Remove(bins[migs[2]])
49
50 migs, bins, err = findMigrations(ctx, 0, 5)
51 if err != nil {
52 t.Fatal(err)
53 }
54 if len(bins) != len(migs)-1 {
55 t.Fatal("should be missing one migration bin")
56 }
57 }
58
59 func TestFindMigrationsReverse(t *testing.T) {
60 tmpDir := t.TempDir()
61
62 ctx := t.Context()
63
64 migs, bins, err := findMigrations(ctx, 5, 0)
65 if err != nil {
66 t.Fatal(err)
67 }
68 if len(migs) != 5 {
69 t.Fatal("expected 5 migrations")
70 }
71 if len(bins) != 0 {
72 t.Fatal("should not have found migrations")
73 }
74
75 for i := 1; i < 6; i++ {
76 createFakeBin(i-1, i, tmpDir)
77 }
78
79 t.Setenv("PATH", tmpDir)
80
81 migs, bins, err = findMigrations(ctx, 5, 0)
82 if err != nil {
83 t.Fatal(err)
84 }
85 if len(migs) != 5 {
86 t.Fatal("expected 5 migrations")
87 }
88 if len(bins) != len(migs) {
89 t.Fatal("missing", len(migs)-len(bins), "migrations:", migs)
90 }
91
92 os.Remove(bins[migs[2]])
93
94 migs, bins, err = findMigrations(ctx, 5, 0)
95 if err != nil {
96 t.Fatal(err)
97 }
98 if len(bins) != len(migs)-1 {
99 t.Fatal("should be missing one migration bin")
100 }
101 }
102
103 func TestFetchMigrations(t *testing.T) {
104 ctx := t.Context()
105
106 fetcher := NewHttpFetcher(testIpfsDist, testServer.URL, "", 0)
107
108 tmpDir := t.TempDir()
109
110 needed := []string{"fs-repo-1-to-2", "fs-repo-2-to-3"}
111 buf := new(strings.Builder)
112 buf.Grow(256)
113 logger := log.New(buf, "", 0)
114 fetched, err := fetchMigrations(ctx, fetcher, needed, tmpDir, logger)
115 if err != nil {
116 t.Fatal(err)
117 }
118
119 for _, bin := range fetched {
120 _, err = os.Stat(bin)
121 if os.IsNotExist(err) {
122 t.Error("expected file to exist:", bin)
123 }
124 }
125
126 // Check expected log output
127 for _, mig := range needed {
128 logOut := fmt.Sprintf("Downloading migration: %s", mig)
129 if !strings.Contains(buf.String(), logOut) {
130 t.Fatalf("did not find expected log output %q", logOut)
131 }
132 logOut = fmt.Sprintf("Downloaded and unpacked migration: %s", filepath.Join(tmpDir, mig))
133 if !strings.Contains(buf.String(), logOut) {
134 t.Fatalf("did not find expected log output %q", logOut)
135 }
136 }
137 }
138
139 func TestRunMigrations(t *testing.T) {
140 fakeIpfs := filepath.Join(t.TempDir(), ".ipfs")
141 t.Setenv(config.EnvDir, fakeIpfs)
142
143 err := os.Mkdir(fakeIpfs, os.ModePerm)
144 if err != nil {
145 panic(err)
146 }
147
148 testVer := 11
149 err = WriteRepoVersion(fakeIpfs, testVer)
150 if err != nil {
151 t.Fatal(err)
152 }
153
154 fetcher := NewHttpFetcher(testIpfsDist, testServer.URL, "", 0)
155
156 ctx := t.Context()
157
158 targetVer := 9
159
160 err = RunMigration(ctx, fetcher, targetVer, fakeIpfs, false)
161 if err == nil || !strings.HasPrefix(err.Error(), "downgrade not allowed") {
162 t.Fatal("expected 'downgrade not allowed' error")
163 }
164
165 err = RunMigration(ctx, fetcher, targetVer, fakeIpfs, true)
166 if err != nil {
167 if !strings.HasPrefix(err.Error(), "migration fs-repo-10-to-11 failed") {
168 t.Fatal(err)
169 }
170 }
171 }
172
173 func createFakeBin(from, to int, tmpDir string) {
174 migPath := filepath.Join(tmpDir, ExeName(migrationName(from, to)))
175 emptyFile, err := os.Create(migPath)
176 if err != nil {
177 panic(err)
178 }
179 emptyFile.Close()
180 err = os.Chmod(migPath, 0o755)
181 if err != nil {
182 panic(err)
183 }
184 }
185
186 var testConfig = `
187 {
188 "Bootstrap": [
189 "/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt",
190 "/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ"
191 ],
192 "Migration": {
193 "DownloadSources": ["IPFS", "HTTP", "127.0.0.1", "https://127.0.1.1"],
194 "Keep": "cache"
195 },
196 "Peering": {
197 "Peers": [
198 {
199 "ID": "12D3KooWGC6TvWhfapngX6wvJHMYvKpDMXPb3ZnCZ6dMoaMtimQ5",
200 "Addrs": ["/ip4/127.0.0.1/tcp/4001", "/ip4/127.0.0.1/udp/4001/quic"]
201 }
202 ]
203 }
204 }
205 `
206
207 func TestReadMigrationConfigDefaults(t *testing.T) {
208 tmpDir := makeConfig(t, "{}")
209
210 cfg, err := ReadMigrationConfig(tmpDir, "")
211 if err != nil {
212 t.Fatal(err)
213 }
214
215 if cfg.Keep != config.DefaultMigrationKeep {
216 t.Error("expected default value for Keep")
217 }
218
219 if len(cfg.DownloadSources) != len(config.DefaultMigrationDownloadSources) {
220 t.Fatal("expected default number of download sources")
221 }
222 for i, src := range config.DefaultMigrationDownloadSources {
223 if cfg.DownloadSources[i] != src {
224 t.Errorf("wrong DownloadSource: %s", cfg.DownloadSources[i])
225 }
226 }
227 }
228
229 func TestReadMigrationConfigErrors(t *testing.T) {
230 tmpDir := makeConfig(t, `{"Migration": {"Keep": "badvalue"}}`)
231
232 _, err := ReadMigrationConfig(tmpDir, "")
233 if err == nil {
234 t.Fatal("expected error")
235 }
236 if !strings.HasPrefix(err.Error(), "unknown") {
237 t.Fatal("did not get expected error:", err)
238 }
239
240 os.RemoveAll(tmpDir)
241 _, err = ReadMigrationConfig(tmpDir, "")
242 if err == nil {
243 t.Fatal("expected error")
244 }
245
246 tmpDir = makeConfig(t, `}{`)
247 _, err = ReadMigrationConfig(tmpDir, "")
248 if err == nil {
249 t.Fatal("expected error")
250 }
251 }
252
253 func TestReadMigrationConfig(t *testing.T) {
254 tmpDir := makeConfig(t, testConfig)
255
256 cfg, err := ReadMigrationConfig(tmpDir, "")
257 if err != nil {
258 t.Fatal(err)
259 }
260
261 if len(cfg.DownloadSources) != 4 {
262 t.Fatal("wrong number of DownloadSources")
263 }
264 expect := []string{"IPFS", "HTTP", "127.0.0.1", "https://127.0.1.1"}
265 for i := range expect {
266 if cfg.DownloadSources[i] != expect[i] {
267 t.Errorf("wrong DownloadSource at %d", i)
268 }
269 }
270
271 if cfg.Keep != "cache" {
272 t.Error("wrong value for Keep")
273 }
274 }
275
276 type mockIpfsFetcher struct{}
277
278 var _ Fetcher = (*mockIpfsFetcher)(nil)
279
280 func (m *mockIpfsFetcher) Fetch(ctx context.Context, filePath string) ([]byte, error) {
281 return nil, nil
282 }
283
284 func (m *mockIpfsFetcher) Close() error {
285 return nil
286 }
287
288 func TestGetMigrationFetcher(t *testing.T) {
289 var f Fetcher
290 var err error
291
292 newIpfsFetcher := func(distPath string) Fetcher {
293 return &mockIpfsFetcher{}
294 }
295
296 downloadSources := []string{"ftp://bad.gateway.io"}
297 _, err = GetMigrationFetcher(downloadSources, "", newIpfsFetcher)
298 if err == nil || !strings.HasPrefix(err.Error(), "bad gateway addr") {
299 t.Fatal("Expected bad gateway address error, got:", err)
300 }
301
302 downloadSources = []string{"::bad.gateway.io"}
303 _, err = GetMigrationFetcher(downloadSources, "", newIpfsFetcher)
304 if err == nil || !strings.HasPrefix(err.Error(), "bad gateway addr") {
305 t.Fatal("Expected bad gateway address error, got:", err)
306 }
307
308 downloadSources = []string{"http://localhost"}
309 f, err = GetMigrationFetcher(downloadSources, "", newIpfsFetcher)
310 if err != nil {
311 t.Fatal(err)
312 }
313 if _, ok := f.(*HttpFetcher); !ok {
314 t.Fatalf("expected HttpFetcher, got %T", f)
315 }
316
317 downloadSources = []string{"ipfs"}
318 _, err = GetMigrationFetcher(downloadSources, "", newIpfsFetcher)
319 if err == nil || !strings.Contains(err.Error(), "IPFS downloads are not supported for legacy migrations") {
320 t.Fatal("Expected IPFS downloads error, got:", err)
321 }
322
323 downloadSources = []string{"http"}
324 f, err = GetMigrationFetcher(downloadSources, "", newIpfsFetcher)
325 if err != nil {
326 t.Fatal(err)
327 }
328 mf, ok := f.(*MultiFetcher)
329 if !ok {
330 t.Fatal("expected MultiFetcher for HTTPS alias expansion")
331 }
332 if mf.Len() != len(defaultMigrationGateways) {
333 t.Fatalf("expected %d fetchers from HTTPS alias, got %d", len(defaultMigrationGateways), mf.Len())
334 }
335
336 downloadSources = []string{"IPFS", "HTTPS"}
337 _, err = GetMigrationFetcher(downloadSources, "", newIpfsFetcher)
338 if err == nil || !strings.Contains(err.Error(), "IPFS downloads are not supported for legacy migrations") {
339 t.Fatal("Expected IPFS downloads error, got:", err)
340 }
341
342 downloadSources = []string{"https", "some.domain.io"}
343 f, err = GetMigrationFetcher(downloadSources, "", newIpfsFetcher)
344 if err != nil {
345 t.Fatal(err)
346 }
347 mf, ok = f.(*MultiFetcher)
348 if !ok {
349 t.Fatal("expected MultiFetcher")
350 }
351 if mf.Len() != len(defaultMigrationGateways)+1 {
352 t.Fatalf("expected %d fetchers in MultiFetcher, got %d", len(defaultMigrationGateways)+1, mf.Len())
353 }
354
355 downloadSources = nil
356 _, err = GetMigrationFetcher(downloadSources, "", newIpfsFetcher)
357 if err == nil {
358 t.Fatal("expected error when no sources specified")
359 }
360
361 downloadSources = []string{"", ""}
362 _, err = GetMigrationFetcher(downloadSources, "", newIpfsFetcher)
363 if err == nil {
364 t.Fatal("expected error when empty string fetchers specified")
365 }
366 }
367
368 func makeConfig(t *testing.T, configData string) string {
369 tmpDir := t.TempDir()
370
371 cfgFile, err := os.Create(filepath.Join(tmpDir, "config"))
372 if err != nil {
373 t.Fatal(err)
374 }
375 if _, err = cfgFile.Write([]byte(configData)); err != nil {
376 t.Fatal(err)
377 }
378 if err = cfgFile.Close(); err != nil {
379 t.Fatal(err)
380 }
381 return tmpDir
382 }