Skip to content

Commit

Permalink
feat(storagemarket): support all cid recording option
Browse files Browse the repository at this point in the history
Provide an option to record locations of all CIDs in a piece, so that we can support retrieval not
only of root cid but all cids inside of a piece

Also cleanup file store maintainence so temporary files are always
deleted

Also build better filestore and piecestore test infrastructure
  • Loading branch information
hannahhoward committed Mar 24, 2020
1 parent 9703112 commit 1d267a4
Show file tree
Hide file tree
Showing 14 changed files with 794 additions and 239 deletions.
3 changes: 0 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,6 @@ github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJ
github.com/ipfs/go-blockservice v0.1.0/go.mod h1:hzmMScl1kXHg3M2BjTymbVPjv627N7sYcvYaKbop39M=
github.com/ipfs/go-blockservice v0.1.3-0.20190908200855-f22eea50656c h1:lN5IQA07VtLiTLAp/Scezp1ljFhXErC6yq4O1cu+yJ0=
github.com/ipfs/go-blockservice v0.1.3-0.20190908200855-f22eea50656c/go.mod h1:t+411r7psEUhLueM8C7aPA7cxCclv4O3VsUVxt9kz2I=
github.com/ipfs/go-car v0.0.4 h1:zLhxykvk4SFU4oIpgcIoiolVL3jqcK0hjqcQfUSs4dk=
github.com/ipfs/go-car v0.0.4/go.mod h1:eZX0EppfsvSQN8IsJnx57bheogWMgQjJVWU/fDA7ySQ=
github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
Expand Down Expand Up @@ -231,7 +229,6 @@ github.com/ipfs/go-unixfs v0.2.2-0.20190827150610-868af2e9e5cb h1:tmWYgjltxwM7PD
github.com/ipfs/go-unixfs v0.2.2-0.20190827150610-868af2e9e5cb/go.mod h1:IwAAgul1UQIcNZzKPYZWOCijryFBeCV79cNubPzol+k=
github.com/ipfs/go-verifcid v0.0.1 h1:m2HI7zIuR5TFyQ1b79Da5N9dnnCP1vcu2QqawmWlK2E=
github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZc0g37pY0=
github.com/ipld/go-car v0.0.4/go.mod h1:kgp82Cpd0TgoxgCsr7yndUXMkMgNP/aAwVBtJtsNR6g=
github.com/ipld/go-car v0.0.5-0.20200316204026-3e2cf7af0fab h1:+3Y6Jb3IBmG3t6e3r6TItnuciOaMOuGW7QIVEUa5vy4=
github.com/ipld/go-car v0.0.5-0.20200316204026-3e2cf7af0fab/go.mod h1:yR5AsJ38xTwwgwGpbh60ICtdLPp5lGfuH28PAAzaEhM=
github.com/ipld/go-ipld-prime v0.0.2-0.20191108012745-28a82f04c785 h1:fASnkvtR+SmB2y453RxmDD3Uvd4LonVUgFGk9JoDaZs=
Expand Down
176 changes: 176 additions & 0 deletions shared_testutil/test_filestore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package shared_testutil

import (
"bytes"
"errors"
"math/rand"
"testing"

"github.com/stretchr/testify/require"

"github.com/filecoin-project/go-fil-markets/filestore"
)

var TestErrNotFound = errors.New("file not found")
var TestErrTempFile = errors.New("temp file creation failed")

// TestFileStoreParams are parameters for a test file store
type TestFileStoreParams struct {
Files []filestore.File
AvailableTempFiles []filestore.File
ExpectedDeletions []filestore.Path
ExpectedOpens []filestore.Path
}

// TestFileStore is a mocked file store that can provide programmed returns
// and test expectations
type TestFileStore struct {
files []filestore.File
availableTempFiles []filestore.File
expectedDeletions map[filestore.Path]struct{}
expectedOpens map[filestore.Path]struct{}
deletedFiles map[filestore.Path]struct{}
openedFiles map[filestore.Path]struct{}
}

// NewTestFileStore returns a new test file store from the given parameters
func NewTestFileStore(params TestFileStoreParams) *TestFileStore {
fs := &TestFileStore{
files: params.Files,
availableTempFiles: params.AvailableTempFiles,
expectedDeletions: make(map[filestore.Path]struct{}),
expectedOpens: make(map[filestore.Path]struct{}),
deletedFiles: make(map[filestore.Path]struct{}),
openedFiles: make(map[filestore.Path]struct{}),
}
for _, path := range params.ExpectedDeletions {
fs.expectedDeletions[path] = struct{}{}
}
for _, path := range params.ExpectedOpens {
fs.expectedOpens[path] = struct{}{}
}
return fs
}

// Open will open a file if it's in the file store
func (fs *TestFileStore) Open(p filestore.Path) (filestore.File, error) {
var foundFile filestore.File
for _, file := range fs.files {
if p == file.Path() {
foundFile = file
break
}
}
if foundFile == nil {
return nil, TestErrNotFound
}
fs.openedFiles[p] = struct{}{}
return foundFile, nil
}

// Create is not implement
func (fs *TestFileStore) Create(p filestore.Path) (filestore.File, error) {
panic("not implemented")
}

// Store is not implemented
func (fs *TestFileStore) Store(p filestore.Path, f filestore.File) (filestore.Path, error) {
panic("not implemented")
}

// Delete will delete a file if it is in the file store
func (fs *TestFileStore) Delete(p filestore.Path) error {
var foundFile filestore.File
for i, file := range fs.files {
if p == file.Path() {
foundFile = file
fs.files[i] = fs.files[len(fs.files)-1]
fs.files[len(fs.files)-1] = nil
fs.files = fs.files[:len(fs.files)-1]
break
}
}
if foundFile == nil {
return TestErrNotFound
}
fs.deletedFiles[p] = struct{}{}
return nil
}

// CreateTemp will create a temporary file from the provided set of temporary files
func (fs *TestFileStore) CreateTemp() (filestore.File, error) {
if len(fs.availableTempFiles) == 0 {
return nil, TestErrTempFile
}
var tempFile filestore.File
tempFile, fs.availableTempFiles = fs.availableTempFiles[0], fs.availableTempFiles[1:]
fs.files = append(fs.files, tempFile)
return tempFile, nil
}

// VerifyExpectations will verify that the correct files were opened and deleted
func (fs *TestFileStore) VerifyExpectations(t *testing.T) {
require.Equal(t, fs.openedFiles, fs.expectedOpens)
require.Equal(t, fs.deletedFiles, fs.expectedDeletions)
}

// TestFileParams are parameters for a test file
type TestFileParams struct {
Buffer *bytes.Buffer
Size int64
Path filestore.Path
}

// NewTestFile generates a mocked filestore.File that has programmed returns
func NewTestFile(params TestFileParams) *TestFile {
tf := &TestFile{
Buffer: params.Buffer,
size: params.Size,
path: params.Path,
}
if tf.Buffer == nil {
tf.Buffer = new(bytes.Buffer)
}
if tf.size == 0 {
tf.size = rand.Int63()
}
if tf.path == filestore.Path("") {
buf := make([]byte, 16)
_, _ = rand.Read(buf)
tf.path = filestore.Path(buf)
}
return tf
}

// TestFile is a mocked version of filestore.File with preset returns
// and a byte buffer for read/writes
type TestFile struct {
*bytes.Buffer
size int64
path filestore.Path
}

// Path returns the preset path
func (f *TestFile) Path() filestore.Path {
return f.path
}

// OsPath is not implemented
func (f *TestFile) OsPath() filestore.OsPath {
panic("not implemented")
}

// Size returns the preset size
func (f *TestFile) Size() int64 {
return f.size
}

// Close does nothing
func (f *TestFile) Close() error {
return nil
}

// Seek is not implemented
func (f *TestFile) Seek(offset int64, whence int) (int64, error) {
panic("not implemented")
}
18 changes: 18 additions & 0 deletions shared_testutil/test_ipld_tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ import (

blocks "github.com/ipfs/go-block-format"
"github.com/ipfs/go-cid"
"github.com/ipld/go-car"
"github.com/ipld/go-ipld-prime"

// to register multicodec
_ "github.com/ipld/go-ipld-prime/encoding/dagjson"
"github.com/ipld/go-ipld-prime/fluent"
ipldfree "github.com/ipld/go-ipld-prime/impl/free"
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
"github.com/ipld/go-ipld-prime/traversal/selector"
"github.com/ipld/go-ipld-prime/traversal/selector/builder"
)

// TestIPLDTree is a set of IPLD Data that forms a tree spread across some blocks
Expand Down Expand Up @@ -130,3 +133,18 @@ func (tt TestIPLDTree) Get(c cid.Cid) (blocks.Block, error) {
}
return blocks.NewBlockWithCid(data, c)
}

// DumpToCar puts the tree into a car file, with user configured functions
func (tt TestIPLDTree) DumpToCar(out io.Writer, userOnNewCarBlocks ...car.OnNewCarBlockFunc) error {
ssb := builder.NewSelectorSpecBuilder(ipldfree.NodeBuilder())
node := ssb.ExploreRecursive(selector.RecursionLimitNone(), ssb.ExploreAll(ssb.ExploreRecursiveEdge())).Node()
ctx := context.Background()
sc := car.NewSelectiveCar(ctx, tt, []car.Dag{
car.Dag{
Root: tt.RootNodeLnk.(cidlink.Link).Cid,
Selector: node,
},
})

return sc.Write(out, userOnNewCarBlocks...)
}
47 changes: 33 additions & 14 deletions shared_testutil/test_piecestore.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,40 @@ import (

// TestPieceStore is piecestore who's query results are mocked
type TestPieceStore struct {
piecesStubbed map[cid.Cid]piecestore.PieceInfo
piecesExpected map[cid.Cid]struct{}
piecesReceived map[cid.Cid]struct{}
cidInfosStubbed map[cid.Cid]piecestore.CIDInfo
cidInfosExpected map[cid.Cid]struct{}
cidInfosReceived map[cid.Cid]struct{}
addPieceBlockLocationsError error
addDealForPieceError error
piecesStubbed map[cid.Cid]piecestore.PieceInfo
piecesExpected map[cid.Cid]struct{}
piecesReceived map[cid.Cid]struct{}
cidInfosStubbed map[cid.Cid]piecestore.CIDInfo
cidInfosExpected map[cid.Cid]struct{}
cidInfosReceived map[cid.Cid]struct{}
}

// TestPieceStoreParams sets parameters for a piece store
type TestPieceStoreParams struct {
AddDealForPieceError error
AddPieceBlockLocationsError error
}

var _ piecestore.PieceStore = &TestPieceStore{}

// NewTestPieceStore creates a TestPieceStore
func NewTestPieceStore() *TestPieceStore {
return NewTestPieceStoreWithParams(TestPieceStoreParams{})
}

// NewTestPieceStoreWithParams creates a TestPieceStore with the given parameters
func NewTestPieceStoreWithParams(params TestPieceStoreParams) *TestPieceStore {
return &TestPieceStore{
piecesStubbed: make(map[cid.Cid]piecestore.PieceInfo),
piecesExpected: make(map[cid.Cid]struct{}),
piecesReceived: make(map[cid.Cid]struct{}),
cidInfosStubbed: make(map[cid.Cid]piecestore.CIDInfo),
cidInfosExpected: make(map[cid.Cid]struct{}),
cidInfosReceived: make(map[cid.Cid]struct{}),
addDealForPieceError: params.AddDealForPieceError,
addPieceBlockLocationsError: params.AddPieceBlockLocationsError,
piecesStubbed: make(map[cid.Cid]piecestore.PieceInfo),
piecesExpected: make(map[cid.Cid]struct{}),
piecesReceived: make(map[cid.Cid]struct{}),
cidInfosStubbed: make(map[cid.Cid]piecestore.CIDInfo),
cidInfosExpected: make(map[cid.Cid]struct{}),
cidInfosReceived: make(map[cid.Cid]struct{}),
}
}

Expand Down Expand Up @@ -75,14 +90,17 @@ func (tps *TestPieceStore) VerifyExpectations(t *testing.T) {
require.Equal(t, tps.cidInfosExpected, tps.cidInfosReceived)
}

// AddDealForPiece returns a preprogrammed error
func (tps *TestPieceStore) AddDealForPiece(pieceCID cid.Cid, dealInfo piecestore.DealInfo) error {
panic("not implemented")
return tps.addDealForPieceError
}

// AddPieceBlockLocations returns a preprogrammed error
func (tps *TestPieceStore) AddPieceBlockLocations(pieceCID cid.Cid, blockLocations map[cid.Cid]piecestore.BlockLocation) error {
panic("not implemented")
return tps.addPieceBlockLocationsError
}

// GetPieceInfo returns a piece info if it's been stubbed
func (tps *TestPieceStore) GetPieceInfo(pieceCID cid.Cid) (piecestore.PieceInfo, error) {
tps.piecesReceived[pieceCID] = struct{}{}

Expand All @@ -97,6 +115,7 @@ func (tps *TestPieceStore) GetPieceInfo(pieceCID cid.Cid) (piecestore.PieceInfo,
return piecestore.PieceInfoUndefined, errors.New("GetPieceInfo failed")
}

// GetCIDInfo returns cid info if it's been stubbed
func (tps *TestPieceStore) GetCIDInfo(c cid.Cid) (piecestore.CIDInfo, error) {
tps.cidInfosReceived[c] = struct{}{}

Expand Down
Loading

0 comments on commit 1d267a4

Please sign in to comment.