master
go 231 lines 5.4 KB
Raw
1 package migrations
2
3 import (
4 "bytes"
5 "context"
6 "fmt"
7 "io"
8 "net/http/httptest"
9 "os"
10 "path"
11 "path/filepath"
12 "strings"
13 "testing"
14
15 "github.com/ipfs/boxo/blockservice"
16 "github.com/ipfs/boxo/exchange/offline"
17 "github.com/ipfs/boxo/gateway"
18 blocks "github.com/ipfs/go-block-format"
19 "github.com/ipfs/go-cid"
20 "github.com/ipfs/go-unixfsnode/data/builder"
21 "github.com/ipld/go-car/v2"
22 carblockstore "github.com/ipld/go-car/v2/blockstore"
23 "github.com/ipld/go-ipld-prime"
24 cidlink "github.com/ipld/go-ipld-prime/linking/cid"
25 "github.com/multiformats/go-multicodec"
26 "github.com/multiformats/go-multihash"
27 )
28
29 var (
30 testIpfsDist string
31 testServer *httptest.Server
32 )
33
34 func TestMain(m *testing.M) {
35 t := &testing.T{}
36
37 // Setup test data
38 testDataDir := makeTestData(t)
39
40 testCar := makeTestCar(testDataDir)
41 defer os.RemoveAll(testCar)
42
43 // Setup test gateway
44 fd := setupTestGateway(testCar)
45 defer fd.Close()
46
47 // Run tests
48 os.Exit(m.Run())
49 }
50
51 func makeTestData(t testing.TB) string {
52 tempDir := t.TempDir()
53
54 versions := []string{"v1.0.0", "v1.1.0", "v1.1.2", "v2.0.0-rc1", "2.0.0", "v2.0.1"}
55 packages := []string{"kubo", "go-ipfs", "fs-repo-migrations", "fs-repo-1-to-2", "fs-repo-2-to-3", "fs-repo-9-to-10", "fs-repo-10-to-11"}
56
57 // Generate fake data
58 for _, name := range packages {
59 err := os.MkdirAll(filepath.Join(tempDir, name), 0777)
60 if err != nil {
61 panic(err)
62 }
63
64 err = os.WriteFile(filepath.Join(tempDir, name, "versions"), []byte(strings.Join(versions, "\n")+"\n"), 0666)
65 if err != nil {
66 panic(err)
67 }
68
69 for _, version := range versions {
70 filename, archName := makeArchivePath(name, name, version, "tar.gz")
71 createFakeArchive(filepath.Join(tempDir, filename), archName, false)
72
73 filename, archName = makeArchivePath(name, name, version, "zip")
74 createFakeArchive(filepath.Join(tempDir, filename), archName, true)
75 }
76 }
77
78 return tempDir
79 }
80
81 func createFakeArchive(archName, name string, archZip bool) {
82 err := os.MkdirAll(filepath.Dir(archName), 0777)
83 if err != nil {
84 panic(err)
85 }
86
87 fileName := strings.Split(path.Base(name), "_")[0]
88 root := fileName
89
90 // Simulate fetching go-ipfs, which has "ipfs" as the name in the archive.
91 if fileName == "go-ipfs" || fileName == "kubo" {
92 fileName = "ipfs"
93 }
94 fileName = ExeName(fileName)
95
96 if archZip {
97 err = writeZipFile(archName, root, fileName, "FAKE DATA")
98 } else {
99 err = writeTarGzipFile(archName, root, fileName, "FAKE DATA")
100 }
101 if err != nil {
102 panic(err)
103 }
104 }
105
106 // makeTestCar makes a CAR file with the directory [testData]. This code is mostly
107 // sourced from https://github.com/ipld/go-car/blob/1e2f0bd2c44ee31f48a8f602b25b5671cc0c4687/cmd/car/create.go
108 func makeTestCar(testData string) string {
109 // make a cid with the right length that we eventually will patch with the root.
110 hasher, err := multihash.GetHasher(multihash.SHA2_256)
111 if err != nil {
112 panic(err)
113 }
114 digest := hasher.Sum([]byte{})
115 hash, err := multihash.Encode(digest, multihash.SHA2_256)
116 if err != nil {
117 panic(err)
118 }
119 proxyRoot := cid.NewCidV1(uint64(multicodec.DagPb), hash)
120
121 // Make CAR file
122 fd, err := os.CreateTemp("", "kubo-migrations-test-*.car")
123 if err != nil {
124 panic(err)
125 }
126 defer fd.Close()
127 filename := fd.Name()
128
129 rw, err := carblockstore.OpenReadWriteFile(fd, []cid.Cid{proxyRoot}, carblockstore.WriteAsCarV1(true))
130 if err != nil {
131 panic(err)
132 }
133 defer rw.Close()
134
135 ctx := context.Background()
136
137 ls := cidlink.DefaultLinkSystem()
138 ls.TrustedStorage = true
139 ls.StorageReadOpener = func(_ ipld.LinkContext, l ipld.Link) (io.Reader, error) {
140 cl, ok := l.(cidlink.Link)
141 if !ok {
142 return nil, fmt.Errorf("not a cidlink")
143 }
144 blk, err := rw.Get(ctx, cl.Cid)
145 if err != nil {
146 return nil, err
147 }
148 return bytes.NewBuffer(blk.RawData()), nil
149 }
150 ls.StorageWriteOpener = func(_ ipld.LinkContext) (io.Writer, ipld.BlockWriteCommitter, error) {
151 buf := bytes.NewBuffer(nil)
152 return buf, func(l ipld.Link) error {
153 cl, ok := l.(cidlink.Link)
154 if !ok {
155 return fmt.Errorf("not a cidlink")
156 }
157 blk, err := blocks.NewBlockWithCid(buf.Bytes(), cl.Cid)
158 if err != nil {
159 return err
160 }
161 return rw.Put(ctx, blk)
162 }, nil
163 }
164
165 l, _, err := builder.BuildUnixFSRecursive(testData, &ls)
166 if err != nil {
167 panic(err)
168 }
169
170 rcl, ok := l.(cidlink.Link)
171 if !ok {
172 panic(fmt.Errorf("could not interpret %s", l))
173 }
174
175 if err := rw.Finalize(); err != nil {
176 panic(err)
177 }
178 // re-open/finalize with the final root.
179 err = car.ReplaceRootsInFile(filename, []cid.Cid{rcl.Cid})
180 if err != nil {
181 panic(err)
182 }
183
184 return filename
185 }
186
187 func setupTestGateway(testCar string) io.Closer {
188 blockService, roots, fd, err := newBlockServiceFromCAR(testCar)
189 if err != nil {
190 panic(err)
191 }
192
193 if len(roots) != 1 {
194 panic("expected car with 1 root")
195 }
196
197 backend, err := gateway.NewBlocksBackend(blockService)
198 if err != nil {
199 panic(err)
200 }
201 conf := gateway.Config{
202 NoDNSLink: false,
203 DeserializedResponses: false,
204 }
205
206 testIpfsDist = "/ipfs/" + roots[0].String()
207 testServer = httptest.NewServer(gateway.NewHandler(conf, backend))
208
209 return fd
210 }
211
212 func newBlockServiceFromCAR(filepath string) (blockservice.BlockService, []cid.Cid, io.Closer, error) {
213 r, err := os.Open(filepath)
214 if err != nil {
215 return nil, nil, nil, err
216 }
217
218 bs, err := carblockstore.NewReadOnly(r, nil)
219 if err != nil {
220 _ = r.Close()
221 return nil, nil, nil, err
222 }
223
224 roots, err := bs.Roots()
225 if err != nil {
226 return nil, nil, nil, err
227 }
228
229 blockService := blockservice.New(bs, offline.Exchange(bs))
230 return blockService, roots, r, nil
231 }