Skip to content

Commit

Permalink
feat: upgrade frisbii for bifrost-gateway support, add full e2e+bifro…
Browse files Browse the repository at this point in the history
…st-gateway test
  • Loading branch information
rvagg committed Oct 6, 2023
1 parent e8103a2 commit a056c4c
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 23 deletions.
181 changes: 181 additions & 0 deletions cmd/booster-http/e2e_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package main

import (
"context"
"fmt"
"io"
"math/rand"
"net/http"
"os"
"path/filepath"
"testing"
"time"

"github.com/filecoin-project/boost/itests/framework"
"github.com/filecoin-project/boost/testutil"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/itests/kit"
"github.com/ipld/go-car/v2"
"github.com/ipld/go-car/v2/storage"
unixfsgen "github.com/ipld/go-fixtureplate/generator"
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
"github.com/ipni/storetheindex/test"
"github.com/stretchr/testify/require"
)

// Test a full deal -> booster-http serve via lassie trustless and fronted with
// bifrost-gateway with curl-style trusted file fetch

func TestE2E(t *testing.T) {
req := require.New(t)

ctx, cancel := context.WithCancel(context.Background())
defer cancel()
tr := test.NewTestIpniRunner(t, ctx, t.TempDir())

t.Log("Running in test directory:", tr.Dir)

kit.QuietMiningLogs()
framework.SetLogLevel()

t.Log("Starting boost and miner")
boostAndMiner := framework.NewTestFramework(ctx, t, framework.EnableLegacyDeals(true))
req.NoError(boostAndMiner.Start(framework.WithMaxStagingDealsBytes(40000000)))
defer boostAndMiner.Stop()

req.NoError(boostAndMiner.AddClientProviderBalance(abi.NewTokenAmount(1e15)))

// Get the listen address of the miner
minerApiInfo, err := boostAndMiner.LotusMinerApiInfo()
req.NoError(err)
fullNodeApiInfo, err := boostAndMiner.LotusFullNodeApiInfo()
req.NoError(err)

boosterHttpPort, err := testutil.FreePort()
require.NoError(t, err)

t.Log("Starting booster-http")
runAndWaitForBoosterHttp(ctx, t, []string{minerApiInfo}, fullNodeApiInfo, boosterHttpPort, "--serve-pieces=false", "--serve-cars=true")

cwd, err := os.Getwd()
req.NoError(err)
err = os.Chdir(tr.Dir)
req.NoError(err)

rndSeed := time.Now().UTC().UnixNano()
t.Logf("Random seed: %d", rndSeed)
var rndReader io.Reader = rand.New(rand.NewSource(rndSeed))

t.Log("Generate a CAR file with some content")
carFilepath := filepath.Join(tr.Dir, "test.car")
carFile, err := os.Create(carFilepath)
req.NoError(err)
store, err := storage.NewReadableWritable(carFile, nil, car.WriteAsCarV1(true))
req.NoError(err)
lsys := cidlink.DefaultLinkSystem()
lsys.TrustedStorage = true
lsys.SetReadStorage(store)
lsys.SetWriteStorage(store)
entity, err := unixfsgen.Parse(`dir(dir{name:"foo"}(dir{name:"bar"}(file:6MiB{name:"baz.txt"},file:1KiB{name:"boom.png"},file:1{name:"fizz.mov"})),file:1KiB{name:"qux.html"})`)
req.NoError(err)
t.Logf("Generating: %s", entity.Describe(""))
rootEnt, err := entity.Generate(lsys, rndReader)
req.NoError(err)
req.NoError(carFile.Close())

dealTestCarInParts(ctx, t, boostAndMiner, carFilepath, rootEnt.Root)

bifrostGateway := filepath.Join(tr.Dir, "bifrost-gateway")
tr.Run("go", "install", "github.com/ipfs/bifrost-gateway@latest")

t.Log("Install lassie to perform a fetch of our content")
lassie := filepath.Join(tr.Dir, "lassie")
tr.Run("go", "install", "github.com/filecoin-project/lassie/cmd/lassie@latest")

t.Log("Start bifrost-gateway")
bifrostPort, err := testutil.FreePort()
req.NoError(err)
bifrostMetricsPort, err := testutil.FreePort()
req.NoError(err)
bifrostReady := test.NewStdoutWatcher("Path gateway listening on ")
tr.Env = append(tr.Env, fmt.Sprintf("PROXY_GATEWAY_URL=http://localhost:%d", boosterHttpPort))

cmdBifrost := tr.Start(test.NewExecution(bifrostGateway,
"--gateway-port", fmt.Sprintf("%d", bifrostPort),
"--metrics-port", fmt.Sprintf("%d", bifrostMetricsPort),
).WithWatcher(bifrostReady))

select {
case <-bifrostReady.Signal:
case <-ctx.Done():
t.Fatal("timed out waiting for bifrost-gateway to start")
}

// we don't have a clear stdout signal for bifrost-gateway, so we need to
// probe for it
t.Logf("Waiting for bifrost-gateway server to fully come up on port %d...", bifrostPort)
start := time.Now()
waitForHttp(ctx, t, bifrostPort, http.StatusNotFound, 20*time.Second)
t.Logf("bifrost-gateway is up after %s", time.Since(start))

t.Log("Perform some curl requests to bifrost-gateway")
for _, fetch := range []struct {
path string
expect []byte
expectType string
}{
{"/foo/bar/baz.txt", rootEnt.Children[0].Children[0].Children[0].Content, "text/plain; charset=utf-8"},
{"/foo/bar/boom.png", rootEnt.Children[0].Children[0].Children[1].Content, "image/png"},
{"/foo/bar/fizz.mov", rootEnt.Children[0].Children[0].Children[2].Content, "video/quicktime"},
{"/qux.html", rootEnt.Children[1].Content, "text/html"},
} {
byts, ct, code, err := curl(fmt.Sprintf("http://localhost:%d/ipfs/%s%s", bifrostPort, rootEnt.Root.String(), fetch.path))
req.NoError(err)
req.Equal(http.StatusOK, code)
req.Equal(fetch.expect, byts)
req.Equal(fetch.expectType, ct)
}

t.Log("Perform some curl requests to bifrost-gateway that should fail")

byts, ct, code, err := curl(fmt.Sprintf("http://localhost:%d/ipfs/%s/nope", bifrostPort, rootEnt.Root.String()))
req.NoError(err)
req.Equal(http.StatusNotFound, code)
req.Contains(string(byts), "failed to resolve")
req.Equal("text/plain; charset=utf-8", ct)

byts, ct, code, err = curl(fmt.Sprintf("http://localhost:%d/ipfs/bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi", bifrostPort))
req.NoError(err)
req.Equal(http.StatusBadGateway, code)
req.Contains(string(byts), "failed to resolve")
req.Equal("text/plain; charset=utf-8", ct)

t.Log("Perform a direct CAR fetch with lassie")
tr.Run(lassie,
"fetch",
"--provider", fmt.Sprintf("http://localhost:%d", boosterHttpPort),
"--output", "/tmp/lassie.car",
rootEnt.Link().String()+"/foo/bar/baz",
)

t.Log("Cleanup ...")

tr.Stop(cmdBifrost, time.Second)

err = os.Chdir(cwd)
req.NoError(err)
}

// simulate a curl to the url and return the body bytes, content type and status code
func curl(to string) ([]byte, string, int, error) {
req, err := http.Get(to)
if err != nil {
return nil, "", 0, err
}
defer req.Body.Close()
byts, err := io.ReadAll(req.Body)
if err != nil {
return nil, "", 0, err
}
return byts, req.Header.Get("Content-Type"), req.StatusCode, nil
}
6 changes: 3 additions & 3 deletions cmd/booster-http/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const testFile = "test/test_file"

func TestNewHttpServer(t *testing.T) {
// Create a new mock Http server
port, err := testutil.OpenPort()
port, err := testutil.FreePort()
require.NoError(t, err)
ctrl := gomock.NewController(t)
httpServer := NewHttpServer("", "0.0.0.0", port, mocks_booster_http.NewMockHttpServerApi(ctrl), nil)
Expand Down Expand Up @@ -53,7 +53,7 @@ func TestNewHttpServer(t *testing.T) {

func TestHttpGzipResponse(t *testing.T) {
// Create a new mock Http server with custom functions
port, err := testutil.OpenPort()
port, err := testutil.FreePort()
require.NoError(t, err)
ctrl := gomock.NewController(t)
mockHttpServer := mocks_booster_http.NewMockHttpServerApi(ctrl)
Expand Down Expand Up @@ -113,7 +113,7 @@ func TestHttpGzipResponse(t *testing.T) {
func TestHttpInfo(t *testing.T) {
var v apiVersion

port, err := testutil.OpenPort()
port, err := testutil.FreePort()
require.NoError(t, err)
// Create a new mock Http server
ctrl := gomock.NewController(t)
Expand Down
26 changes: 15 additions & 11 deletions cmd/booster-http/multiminer_retrieval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"io"
"net/http"
"strings"
"testing"
"time"

Expand All @@ -27,7 +28,7 @@ func TestMultiMinerHttpRetrieval(t *testing.T) {
fullNode2ApiInfo, err := rt.BoostAndMiner2.LotusFullNodeApiInfo()
require.NoError(t, err)

port, err := testutil.OpenPort()
port, err := testutil.FreePort()
require.NoError(t, err)

runAndWaitForBoosterHttp(ctx, t, []string{miner1ApiInfo, miner2ApiInfo}, fullNode2ApiInfo, port)
Expand Down Expand Up @@ -64,20 +65,23 @@ func TestMultiMinerHttpRetrieval(t *testing.T) {
}

func runAndWaitForBoosterHttp(ctx context.Context, t *testing.T, minerApiInfo []string, fullNodeApiInfo string, port int, args ...string) {
req := require.New(t)

args = append(args, fmt.Sprintf("--port=%d", port))

go func() {
_ = runBoosterHttp(ctx, t.TempDir(), minerApiInfo, fullNodeApiInfo, "ws://localhost:8042", args...)
_ = runBoosterHttp(ctx, t, minerApiInfo, fullNodeApiInfo, "ws://localhost:8042", args...)
}()

t.Logf("waiting for booster-http server to come up on port %d...", port)
start := time.Now()
req.Eventually(func() bool {
waitForHttp(ctx, t, port, http.StatusOK, 1*time.Minute)
t.Logf("booster-http is up after %s", time.Since(start))
}

func waitForHttp(ctx context.Context, t *testing.T, port int, waitForCode int, waitFor time.Duration) {
require.Eventually(t, func() bool {
// Wait for server to come up
resp, err := http.Get(fmt.Sprintf("http://localhost:%d", port))
if err == nil && resp != nil && resp.StatusCode == 200 {
if err == nil && resp != nil && resp.StatusCode == waitForCode {
return true
}
msg := "Waiting for http server to come up: "
Expand All @@ -86,18 +90,17 @@ func runAndWaitForBoosterHttp(ctx context.Context, t *testing.T, minerApiInfo []
}
if resp != nil {
respBody, err := io.ReadAll(resp.Body)
req.NoError(err)
require.NoError(t, err)
msg += " / Resp: " + resp.Status + "\n" + string(respBody)
}
t.Log(msg)
return false
}, 30*time.Second, 1*time.Second)
t.Logf("booster-http is up after %s", time.Since(start))
}, waitFor, 5*time.Second)
}

func runBoosterHttp(ctx context.Context, repo string, minerApiInfo []string, fullNodeApiInfo string, lidApiInfo string, args ...string) error {
func runBoosterHttp(ctx context.Context, t *testing.T, minerApiInfo []string, fullNodeApiInfo string, lidApiInfo string, args ...string) error {
args = append([]string{"booster-http",
"--repo=" + repo,
"--repo=" + t.TempDir(),
"run",
"--api-fullnode=" + fullNodeApiInfo,
"--api-lid=" + lidApiInfo,
Expand All @@ -106,6 +109,7 @@ func runBoosterHttp(ctx context.Context, repo string, minerApiInfo []string, ful
for _, apiInfo := range minerApiInfo {
args = append(args, "--api-storage="+apiInfo)
}
t.Logf("Running: booster-http %s", strings.Join(args, " "))
// new app per call
app := cli.NewApp()
app.Name = "booster-http"
Expand Down
4 changes: 2 additions & 2 deletions cmd/booster-http/trustless_gateway_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func TestTrustlessGateway(t *testing.T) {

dealTestCarInParts(ctx, t, boostAndMiner, carFilepath, rootCid)

port, err := testutil.OpenPort()
port, err := testutil.FreePort()
require.NoError(t, err)

runAndWaitForBoosterHttp(ctx, t, []string{minerApiInfo}, fullNodeApiInfo, port, "--serve-pieces=false", "--serve-cars=true")
Expand Down Expand Up @@ -137,8 +137,8 @@ func dealTestCarInParts(ctx context.Context, t *testing.T, boostAndMiner *framew
// LID and store the data on the first miner
res, err := boostAndMiner.MakeDummyDeal(dealUuid, file.Name(), rootCid, server.URL+"/"+filepath.Base(file.Name()), false)
req.NoError(err)
req.True(res.Result.Accepted)
t.Logf("created MarketDummyDeal %s", spew.Sdump(res))
req.True(res.Result.Accepted)
req.NoError(boostAndMiner.WaitForDealAddedToSector(dealUuid))
}
}
Expand Down
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -336,10 +336,12 @@ require (
github.com/filecoin-project/lotus v1.23.4-rc1
github.com/ipfs/boxo v0.12.0
github.com/ipfs/kubo v0.22.0
github.com/ipld/frisbii v0.2.1
github.com/ipld/frisbii v0.2.2-0.20231006085121-7d48d11641db
github.com/ipld/go-fixtureplate v0.0.2
github.com/ipld/ipld/specs v0.0.0-20230927010225-ef4dbd703269
github.com/ipni/go-libipni v0.5.2
github.com/ipni/ipni-cli v0.1.1
github.com/ipni/storetheindex v0.8.1
github.com/schollz/progressbar/v3 v3.13.1
)

Expand All @@ -361,7 +363,7 @@ require (
github.com/hashicorp/golang-lru/arc/v2 v2.0.5 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect
github.com/ipfs/go-ipfs-blockstore v1.3.0 // indirect
github.com/ipld/go-trustless-utils v0.3.1 // indirect
github.com/ipld/go-trustless-utils v0.3.2-0.20231006083052-c8beabd68ce4 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.11.0 // indirect
github.com/jackc/pgio v1.0.0 // indirect
Expand Down
13 changes: 9 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -898,8 +898,8 @@ github.com/ipfs/go-verifcid v0.0.2 h1:XPnUv0XmdH+ZIhLGKg6U2vaPaRDXb9urMyNVCE7uvT
github.com/ipfs/go-verifcid v0.0.2/go.mod h1:40cD9x1y4OWnFXbLNJYRe7MpNvWlMn3LZAG5Wb4xnPU=
github.com/ipfs/kubo v0.22.0 h1:HxYkvtFqlF+qQMTxHW+xBhrIWykWm+WbEuQpw1d67mM=
github.com/ipfs/kubo v0.22.0/go.mod h1:Sn3hp55POjH9Ni0lEd/+smXpkZ0J1gKlm0Fx+E1LE60=
github.com/ipld/frisbii v0.2.1 h1:DHdDrYFkIoFIjkXHfj3OMJFN17zh9DBMCHjRqumnEOQ=
github.com/ipld/frisbii v0.2.1/go.mod h1:BFeqnuQ+8n2JEyNVmFl7Y5BqsiEDqmS752ZHDdfKq4o=
github.com/ipld/frisbii v0.2.2-0.20231006085121-7d48d11641db h1:94GghLok3xtbxprTSEsGlzT9BeqOTgvaW7ptKBcteqA=
github.com/ipld/frisbii v0.2.2-0.20231006085121-7d48d11641db/go.mod h1:veapLCF7yc/lFr49vxU6DxNVfxEo6nSESHSecgm8Gpg=
github.com/ipld/go-car v0.1.0/go.mod h1:RCWzaUh2i4mOEkB3W45Vc+9jnS/M6Qay5ooytiBHl3g=
github.com/ipld/go-car v0.6.1 h1:blWbEHf1j62JMWFIqWE//YR0m7k5ZMw0AuUOU5hjrH8=
github.com/ipld/go-car v0.6.1/go.mod h1:oEGXdwp6bmxJCZ+rARSkDliTeYnVzv3++eXajZ+Bmr8=
Expand All @@ -912,6 +912,8 @@ github.com/ipld/go-codec-dagpb v1.3.0/go.mod h1:ga4JTU3abYApDC3pZ00BC2RSvC3qfBb9
github.com/ipld/go-codec-dagpb v1.3.1/go.mod h1:ErNNglIi5KMur/MfFE/svtgQthzVvf+43MrzLbpcIZY=
github.com/ipld/go-codec-dagpb v1.6.0 h1:9nYazfyu9B1p3NAgfVdpRco3Fs2nFC72DqVsMj6rOcc=
github.com/ipld/go-codec-dagpb v1.6.0/go.mod h1:ANzFhfP2uMJxRBr8CE+WQWs5UsNa0pYtmKZ+agnUw9s=
github.com/ipld/go-fixtureplate v0.0.2 h1:q7dSdIXeyY5v4AAFvB6M1nPWuRzTeYcgpbeqGygSLu8=
github.com/ipld/go-fixtureplate v0.0.2/go.mod h1:z7AnsVOetEl6sQ7j5iMyqVGrS9BgGGE4u69TtsLhJGU=
github.com/ipld/go-ipld-adl-hamt v0.0.0-20220616142416-9004dbd839e0 h1:QAI/Ridj0+foHD6epbxmB4ugxz9B4vmNdYSmQLGa05E=
github.com/ipld/go-ipld-adl-hamt v0.0.0-20220616142416-9004dbd839e0/go.mod h1:odxGcpiQZLzP5+yGu84Ljo8y3EzCvNAQKEodHNsHLXA=
github.com/ipld/go-ipld-prime v0.0.2-0.20191108012745-28a82f04c785/go.mod h1:bDDSvVz7vaK12FNvMeRYnpRFkSUPNQOiCYQezMD/P3w=
Expand All @@ -929,8 +931,8 @@ github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20211210234204-ce2a1c70cd
github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20230102063945-1a409dc236dd h1:gMlw/MhNr2Wtp5RwGdsW23cs+yCuj9k2ON7i9MiJlRo=
github.com/ipld/go-ipld-selector-text-lite v0.0.1 h1:lNqFsQpBHc3p5xHob2KvEg/iM5dIFn6iw4L/Hh+kS1Y=
github.com/ipld/go-ipld-selector-text-lite v0.0.1/go.mod h1:U2CQmFb+uWzfIEF3I1arrDa5rwtj00PrpiwwCO+k1RM=
github.com/ipld/go-trustless-utils v0.3.1 h1:i7lPoo7HbThj93wJO1aow3UKdL6AV/jeTLXzTEBZDsE=
github.com/ipld/go-trustless-utils v0.3.1/go.mod h1:SQR5abLVb2YcZiy9QEsBhNyIPj6ubWISJnrpwNBdmpA=
github.com/ipld/go-trustless-utils v0.3.2-0.20231006083052-c8beabd68ce4 h1:D976Ll2XBjjfwbSASRfK6xNMrljvN0FCdaT+vFD/01A=
github.com/ipld/go-trustless-utils v0.3.2-0.20231006083052-c8beabd68ce4/go.mod h1:SQR5abLVb2YcZiy9QEsBhNyIPj6ubWISJnrpwNBdmpA=
github.com/ipld/ipld/specs v0.0.0-20230927010225-ef4dbd703269 h1:MXBxKsw8geRJitw8f1dr3EfwbEV0WXVbEH7e/o3p+NI=
github.com/ipld/ipld/specs v0.0.0-20230927010225-ef4dbd703269/go.mod h1:WcT0DfRe+e2QFY0kcbsOnuT6jL5Q0JNZ83I5DHIdStg=
github.com/ipni/go-libipni v0.5.2 h1:9vaYOnR4dskd8p88NOboqI6yVqBwYPNCQ/zOaRSr59I=
Expand All @@ -939,6 +941,8 @@ github.com/ipni/index-provider v0.14.2 h1:daA3IFnI2n2x/mL0K91SQHNLq6Vvfp5q4uFX9G
github.com/ipni/index-provider v0.14.2/go.mod h1:mArx7Ou3Y62fIDSj9a1Neh5G14xQcwXGbfEbf47vyuM=
github.com/ipni/ipni-cli v0.1.1 h1:TjYAf5CrVx/loQtWQnwEnIYjW7hvRJDRyIibT7WbHjE=
github.com/ipni/ipni-cli v0.1.1/go.mod h1:TAOkJwc9OBsx5gRy8iyoWgb8AbtJcT482cJUGDTxnHg=
github.com/ipni/storetheindex v0.8.1 h1:3uHclkcQWlIXQx+We4tbGF/XzoZYERz3so34xQbUmZE=
github.com/ipni/storetheindex v0.8.1/go.mod h1:K4AR2bRll46YCWeGvob5foN/Z/kuovPdlUeJKOHVQHo=
github.com/ipsn/go-secp256k1 v0.0.0-20180726113642-9d62b9f0bc52 h1:QG4CGBqCeuBo6aZlGAamSkxWdgWfZGeE49eUOWJPA4c=
github.com/ipsn/go-secp256k1 v0.0.0-20180726113642-9d62b9f0bc52/go.mod h1:fdg+/X9Gg4AsAIzWpEHwnqd+QY3b7lajxyjE1m4hkq4=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
Expand Down Expand Up @@ -1777,6 +1781,7 @@ github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpP
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE=
github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
Expand Down
2 changes: 1 addition & 1 deletion testutil/port.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package testutil

import "net"

func OpenPort() (int, error) {
func FreePort() (int, error) {
l, err := net.Listen("tcp", ":0")
if err != nil {
return 0, err
Expand Down

0 comments on commit a056c4c

Please sign in to comment.