test http handler
This commit is contained in:
parent
db5c88196d
commit
759d8f090b
@ -364,7 +364,7 @@ var runCmd = &cli.Command{
|
|||||||
|
|
||||||
remote := stores.NewRemote(localStore, nodeApi, sminfo.AuthHeader(), cctx.Int("parallel-fetch-limit"))
|
remote := stores.NewRemote(localStore, nodeApi, sminfo.AuthHeader(), cctx.Int("parallel-fetch-limit"))
|
||||||
|
|
||||||
fh := &stores.FetchHandler{Local: localStore}
|
fh := &stores.FetchHandler{Local: localStore, PfHandler: &stores.DefaultPartialFileHandler{}}
|
||||||
remoteHandler := func(w http.ResponseWriter, r *http.Request) {
|
remoteHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||||
if !auth.HasPerm(r.Context(), nil, api.PermAdmin) {
|
if !auth.HasPerm(r.Context(), nil, api.PermAdmin) {
|
||||||
w.WriteHeader(401)
|
w.WriteHeader(401)
|
||||||
|
2
extern/sector-storage/manager.go
vendored
2
extern/sector-storage/manager.go
vendored
@ -114,7 +114,7 @@ func New(ctx context.Context, lstor *stores.Local, stor *stores.Remote, ls store
|
|||||||
ls: ls,
|
ls: ls,
|
||||||
storage: stor,
|
storage: stor,
|
||||||
localStore: lstor,
|
localStore: lstor,
|
||||||
remoteHnd: &stores.FetchHandler{Local: lstor},
|
remoteHnd: &stores.FetchHandler{Local: lstor, PfHandler: &stores.DefaultPartialFileHandler{}},
|
||||||
index: si,
|
index: si,
|
||||||
|
|
||||||
sched: newScheduler(),
|
sched: newScheduler(),
|
||||||
|
38
extern/sector-storage/stores/http_handler.go
vendored
38
extern/sector-storage/stores/http_handler.go
vendored
@ -21,8 +21,23 @@ import (
|
|||||||
|
|
||||||
var log = logging.Logger("stores")
|
var log = logging.Logger("stores")
|
||||||
|
|
||||||
|
var _ partialFileHandler = &DefaultPartialFileHandler{}
|
||||||
|
|
||||||
|
// DefaultPartialFileHandler is the default implementation of the partialFileHandler interface.
|
||||||
|
// This is probably the only implementation we'll ever use because the purpose of the
|
||||||
|
// interface to is to mock out partial file related functionality during testing.
|
||||||
|
type DefaultPartialFileHandler struct{}
|
||||||
|
|
||||||
|
func (d *DefaultPartialFileHandler) OpenPartialFile(maxPieceSize abi.PaddedPieceSize, path string) (*partialfile.PartialFile, error) {
|
||||||
|
return partialfile.OpenPartialFile(maxPieceSize, path)
|
||||||
|
}
|
||||||
|
func (d *DefaultPartialFileHandler) HasAllocated(pf *partialfile.PartialFile, offset storiface.UnpaddedByteIndex, size abi.UnpaddedPieceSize) (bool, error) {
|
||||||
|
return pf.HasAllocated(offset, size)
|
||||||
|
}
|
||||||
|
|
||||||
type FetchHandler struct {
|
type FetchHandler struct {
|
||||||
*Local
|
Local Store
|
||||||
|
PfHandler partialFileHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *FetchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // /remote/
|
func (handler *FetchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // /remote/
|
||||||
@ -88,7 +103,7 @@ func (handler *FetchHandler) remoteGetSector(w http.ResponseWriter, r *http.Requ
|
|||||||
|
|
||||||
paths, _, err := handler.Local.AcquireSector(r.Context(), si, ft, storiface.FTNone, storiface.PathStorage, storiface.AcquireMove)
|
paths, _, err := handler.Local.AcquireSector(r.Context(), si, ft, storiface.FTNone, storiface.PathStorage, storiface.AcquireMove)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("%+v", err)
|
log.Errorf("AcquireSector: %+v", err)
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -104,7 +119,7 @@ func (handler *FetchHandler) remoteGetSector(w http.ResponseWriter, r *http.Requ
|
|||||||
|
|
||||||
stat, err := os.Stat(path)
|
stat, err := os.Stat(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("%+v", err)
|
log.Errorf("os.Stat: %+v", err)
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -131,6 +146,7 @@ func (handler *FetchHandler) remoteGetSector(w http.ResponseWriter, r *http.Requ
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
w.Header().Set("Content-Type", "application/octet-stream")
|
w.Header().Set("Content-Type", "application/octet-stream")
|
||||||
|
w.WriteHeader(200)
|
||||||
// will do a ranged read over the file at the given path if the caller has asked for a ranged read in the request headers.
|
// will do a ranged read over the file at the given path if the caller has asked for a ranged read in the request headers.
|
||||||
http.ServeFile(w, r, path)
|
http.ServeFile(w, r, path)
|
||||||
}
|
}
|
||||||
@ -156,7 +172,7 @@ func (handler *FetchHandler) remoteDeleteSector(w http.ResponseWriter, r *http.R
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := handler.Remove(r.Context(), id, ft, false); err != nil {
|
if err := handler.Local.Remove(r.Context(), id, ft, false); err != nil {
|
||||||
log.Errorf("%+v", err)
|
log.Errorf("%+v", err)
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
return
|
return
|
||||||
@ -172,14 +188,14 @@ func (handler *FetchHandler) remoteGetAllocated(w http.ResponseWriter, r *http.R
|
|||||||
|
|
||||||
id, err := storiface.ParseSectorID(vars["id"])
|
id, err := storiface.ParseSectorID(vars["id"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("%+v", err)
|
log.Errorf("parsing sectorID: %+v", err)
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ft, err := ftFromString(vars["type"])
|
ft, err := ftFromString(vars["type"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("%+v", err)
|
log.Errorf("ftFromString: %+v", err)
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -198,7 +214,7 @@ func (handler *FetchHandler) remoteGetAllocated(w http.ResponseWriter, r *http.R
|
|||||||
spt := abi.RegisteredSealProof(spti)
|
spt := abi.RegisteredSealProof(spti)
|
||||||
ssize, err := spt.SectorSize()
|
ssize, err := spt.SectorSize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("%+v", err)
|
log.Errorf("spt.SectorSize(): %+v", err)
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -211,7 +227,7 @@ func (handler *FetchHandler) remoteGetAllocated(w http.ResponseWriter, r *http.R
|
|||||||
}
|
}
|
||||||
szi, err := strconv.ParseInt(vars["size"], 10, 64)
|
szi, err := strconv.ParseInt(vars["size"], 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("parsing spt: %+v", err)
|
log.Errorf("parsing size: %+v", err)
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -228,7 +244,7 @@ func (handler *FetchHandler) remoteGetAllocated(w http.ResponseWriter, r *http.R
|
|||||||
// return error if we do NOT have it.
|
// return error if we do NOT have it.
|
||||||
paths, _, err := handler.Local.AcquireSector(r.Context(), si, ft, storiface.FTNone, storiface.PathStorage, storiface.AcquireMove)
|
paths, _, err := handler.Local.AcquireSector(r.Context(), si, ft, storiface.FTNone, storiface.PathStorage, storiface.AcquireMove)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("%+v", err)
|
log.Errorf("AcquireSector: %+v", err)
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -241,7 +257,7 @@ func (handler *FetchHandler) remoteGetAllocated(w http.ResponseWriter, r *http.R
|
|||||||
}
|
}
|
||||||
|
|
||||||
// open the Unsealed file and check if it has the Unsealed sector for the piece at the given offset and size.
|
// open the Unsealed file and check if it has the Unsealed sector for the piece at the given offset and size.
|
||||||
pf, err := partialfile.OpenPartialFile(abi.PaddedPieceSize(ssize), path)
|
pf, err := handler.PfHandler.OpenPartialFile(abi.PaddedPieceSize(ssize), path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("opening partial file: ", err)
|
log.Error("opening partial file: ", err)
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
@ -253,7 +269,7 @@ func (handler *FetchHandler) remoteGetAllocated(w http.ResponseWriter, r *http.R
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
has, err := pf.HasAllocated(storiface.UnpaddedByteIndex(offi), abi.UnpaddedPieceSize(szi))
|
has, err := handler.PfHandler.HasAllocated(pf, storiface.UnpaddedByteIndex(offi), abi.UnpaddedPieceSize(szi))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("has allocated: ", err)
|
log.Error("has allocated: ", err)
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
|
437
extern/sector-storage/stores/http_handler_test.go
vendored
Normal file
437
extern/sector-storage/stores/http_handler_test.go
vendored
Normal file
@ -0,0 +1,437 @@
|
|||||||
|
package stores_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/filecoin-project/lotus/extern/sector-storage/partialfile"
|
||||||
|
"github.com/filecoin-project/lotus/extern/sector-storage/stores"
|
||||||
|
"github.com/filecoin-project/lotus/extern/sector-storage/stores/mocks"
|
||||||
|
"github.com/filecoin-project/lotus/extern/sector-storage/storiface"
|
||||||
|
"github.com/filecoin-project/specs-storage/storage"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"golang.org/x/xerrors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRemoteGetAllocated(t *testing.T) {
|
||||||
|
|
||||||
|
emptyPartialFile := &partialfile.PartialFile{}
|
||||||
|
pfPath := "path"
|
||||||
|
expectedSectorRef := storage.SectorRef{
|
||||||
|
ID: abi.SectorID{
|
||||||
|
123,
|
||||||
|
123,
|
||||||
|
},
|
||||||
|
ProofType: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
validSectorName := fmt.Sprintf("s-t0%d-%d", 123, 123)
|
||||||
|
validSectorFileType := storiface.FTUnsealed.String()
|
||||||
|
validSectorType := "1"
|
||||||
|
sectorSize := abi.SealProofInfos[1].SectorSize
|
||||||
|
|
||||||
|
validOffset := "100"
|
||||||
|
validOffsetInt := 100
|
||||||
|
|
||||||
|
validSize := "1000"
|
||||||
|
validSizeInt := 1000
|
||||||
|
|
||||||
|
type pieceInfo struct {
|
||||||
|
sectorName string
|
||||||
|
fileType string
|
||||||
|
sectorType string
|
||||||
|
|
||||||
|
// piece info
|
||||||
|
offset string
|
||||||
|
size string
|
||||||
|
}
|
||||||
|
validPieceInfo := pieceInfo{
|
||||||
|
sectorName: validSectorName,
|
||||||
|
fileType: validSectorFileType,
|
||||||
|
sectorType: validSectorType,
|
||||||
|
offset: validOffset,
|
||||||
|
size: validSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
tcs := map[string]struct {
|
||||||
|
piFnc func(pi *pieceInfo)
|
||||||
|
storeFnc func(s *mocks.Store)
|
||||||
|
pfFunc func(s *mocks.PartialFileHandler)
|
||||||
|
|
||||||
|
// expectation
|
||||||
|
expectedStatusCode int
|
||||||
|
}{
|
||||||
|
"fails when sector name is invalid": {
|
||||||
|
piFnc: func(pi *pieceInfo) {
|
||||||
|
pi.sectorName = "invalid"
|
||||||
|
},
|
||||||
|
expectedStatusCode: http.StatusInternalServerError,
|
||||||
|
},
|
||||||
|
"fails when file type is invalid": {
|
||||||
|
piFnc: func(pi *pieceInfo) {
|
||||||
|
pi.fileType = "invalid"
|
||||||
|
},
|
||||||
|
expectedStatusCode: http.StatusInternalServerError,
|
||||||
|
},
|
||||||
|
"fails when sector proof type is invalid": {
|
||||||
|
piFnc: func(pi *pieceInfo) {
|
||||||
|
pi.sectorType = "invalid"
|
||||||
|
},
|
||||||
|
expectedStatusCode: http.StatusInternalServerError,
|
||||||
|
},
|
||||||
|
"fails when offset is invalid": {
|
||||||
|
piFnc: func(pi *pieceInfo) {
|
||||||
|
pi.offset = "invalid"
|
||||||
|
},
|
||||||
|
expectedStatusCode: http.StatusInternalServerError,
|
||||||
|
},
|
||||||
|
"fails when size is invalid": {
|
||||||
|
piFnc: func(pi *pieceInfo) {
|
||||||
|
pi.size = "invalid"
|
||||||
|
},
|
||||||
|
expectedStatusCode: http.StatusInternalServerError,
|
||||||
|
},
|
||||||
|
"fails when errors out during acquiring unsealed sector file": {
|
||||||
|
expectedStatusCode: http.StatusInternalServerError,
|
||||||
|
storeFnc: func(l *mocks.Store) {
|
||||||
|
l.On("AcquireSector", mock.Anything, expectedSectorRef, storiface.FTUnsealed,
|
||||||
|
storiface.FTNone, storiface.PathStorage, storiface.AcquireMove).Return(storiface.SectorPaths{
|
||||||
|
Unsealed: "path",
|
||||||
|
},
|
||||||
|
storiface.SectorPaths{}, xerrors.New("some error"))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"fails when unsealed sector file is not found locally": {
|
||||||
|
expectedStatusCode: http.StatusInternalServerError,
|
||||||
|
storeFnc: func(l *mocks.Store) {
|
||||||
|
|
||||||
|
l.On("AcquireSector", mock.Anything, expectedSectorRef, storiface.FTUnsealed,
|
||||||
|
storiface.FTNone, storiface.PathStorage, storiface.AcquireMove).Return(storiface.SectorPaths{},
|
||||||
|
storiface.SectorPaths{}, nil)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"fails when partial file is not found locally": {
|
||||||
|
expectedStatusCode: http.StatusInternalServerError,
|
||||||
|
storeFnc: func(l *mocks.Store) {
|
||||||
|
// will return emppty paths
|
||||||
|
|
||||||
|
l.On("AcquireSector", mock.Anything, expectedSectorRef, storiface.FTUnsealed,
|
||||||
|
storiface.FTNone, storiface.PathStorage, storiface.AcquireMove).Return(storiface.SectorPaths{
|
||||||
|
Unsealed: pfPath,
|
||||||
|
},
|
||||||
|
storiface.SectorPaths{}, nil)
|
||||||
|
},
|
||||||
|
|
||||||
|
pfFunc: func(pf *mocks.PartialFileHandler) {
|
||||||
|
//OpenPartialFile(maxPieceSize abi.PaddedPieceSize, path string)
|
||||||
|
pf.On("OpenPartialFile", abi.PaddedPieceSize(sectorSize), pfPath).Return(&partialfile.PartialFile{},
|
||||||
|
xerrors.New("some error"))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"fails when determining partial file allocation returns an error": {
|
||||||
|
expectedStatusCode: http.StatusInternalServerError,
|
||||||
|
storeFnc: func(l *mocks.Store) {
|
||||||
|
// will return emppty paths
|
||||||
|
|
||||||
|
l.On("AcquireSector", mock.Anything, expectedSectorRef, storiface.FTUnsealed,
|
||||||
|
storiface.FTNone, storiface.PathStorage, storiface.AcquireMove).Return(storiface.SectorPaths{
|
||||||
|
Unsealed: pfPath,
|
||||||
|
},
|
||||||
|
storiface.SectorPaths{}, nil)
|
||||||
|
},
|
||||||
|
|
||||||
|
pfFunc: func(pf *mocks.PartialFileHandler) {
|
||||||
|
pf.On("OpenPartialFile", abi.PaddedPieceSize(sectorSize), pfPath).Return(emptyPartialFile,
|
||||||
|
nil)
|
||||||
|
pf.On("HasAllocated", emptyPartialFile, storiface.UnpaddedByteIndex(validOffsetInt),
|
||||||
|
abi.UnpaddedPieceSize(validSizeInt)).Return(true, xerrors.New("some error"))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"StatusRequestedRangeNotSatisfiable when piece is NOT allocated in partial file": {
|
||||||
|
expectedStatusCode: http.StatusRequestedRangeNotSatisfiable,
|
||||||
|
storeFnc: func(l *mocks.Store) {
|
||||||
|
// will return emppty paths
|
||||||
|
|
||||||
|
l.On("AcquireSector", mock.Anything, expectedSectorRef, storiface.FTUnsealed,
|
||||||
|
storiface.FTNone, storiface.PathStorage, storiface.AcquireMove).Return(storiface.SectorPaths{
|
||||||
|
Unsealed: pfPath,
|
||||||
|
},
|
||||||
|
storiface.SectorPaths{}, nil)
|
||||||
|
},
|
||||||
|
|
||||||
|
pfFunc: func(pf *mocks.PartialFileHandler) {
|
||||||
|
pf.On("OpenPartialFile", abi.PaddedPieceSize(sectorSize), pfPath).Return(emptyPartialFile,
|
||||||
|
nil)
|
||||||
|
pf.On("HasAllocated", emptyPartialFile, storiface.UnpaddedByteIndex(validOffsetInt),
|
||||||
|
abi.UnpaddedPieceSize(validSizeInt)).Return(false, nil)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"OK when piece is allocated in partial file": {
|
||||||
|
expectedStatusCode: http.StatusOK,
|
||||||
|
storeFnc: func(l *mocks.Store) {
|
||||||
|
// will return emppty paths
|
||||||
|
|
||||||
|
l.On("AcquireSector", mock.Anything, expectedSectorRef, storiface.FTUnsealed,
|
||||||
|
storiface.FTNone, storiface.PathStorage, storiface.AcquireMove).Return(storiface.SectorPaths{
|
||||||
|
Unsealed: pfPath,
|
||||||
|
},
|
||||||
|
storiface.SectorPaths{}, nil)
|
||||||
|
},
|
||||||
|
|
||||||
|
pfFunc: func(pf *mocks.PartialFileHandler) {
|
||||||
|
pf.On("OpenPartialFile", abi.PaddedPieceSize(sectorSize), pfPath).Return(emptyPartialFile,
|
||||||
|
nil)
|
||||||
|
pf.On("HasAllocated", emptyPartialFile, storiface.UnpaddedByteIndex(validOffsetInt),
|
||||||
|
abi.UnpaddedPieceSize(validSizeInt)).Return(true, nil)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range tcs {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
lstore := &mocks.Store{}
|
||||||
|
pfhandler := &mocks.PartialFileHandler{}
|
||||||
|
|
||||||
|
handler := &stores.FetchHandler{
|
||||||
|
lstore,
|
||||||
|
pfhandler,
|
||||||
|
}
|
||||||
|
|
||||||
|
// run http server
|
||||||
|
ts := httptest.NewServer(handler)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
pi := validPieceInfo
|
||||||
|
if tc.piFnc != nil {
|
||||||
|
tc.piFnc(&pi)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.storeFnc != nil {
|
||||||
|
tc.storeFnc(lstore)
|
||||||
|
}
|
||||||
|
if tc.pfFunc != nil {
|
||||||
|
tc.pfFunc(pfhandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// call remoteGetAllocated
|
||||||
|
url := fmt.Sprintf("%s/remote/%s/%s/%s/allocated/%s/%s",
|
||||||
|
ts.URL,
|
||||||
|
pi.fileType,
|
||||||
|
pi.sectorName,
|
||||||
|
pi.sectorType,
|
||||||
|
pi.offset,
|
||||||
|
pi.size)
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// assert expected status code
|
||||||
|
require.Equal(t, tc.expectedStatusCode, resp.StatusCode)
|
||||||
|
|
||||||
|
// assert expectations on the mocks
|
||||||
|
lstore.AssertExpectations(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoteGetSector(t *testing.T) {
|
||||||
|
str := "hello-world"
|
||||||
|
fileBytes := []byte(str)
|
||||||
|
|
||||||
|
validSectorName := fmt.Sprintf("s-t0%d-%d", 123, 123)
|
||||||
|
validSectorFileType := storiface.FTUnsealed.String()
|
||||||
|
expectedSectorRef := storage.SectorRef{
|
||||||
|
ID: abi.SectorID{
|
||||||
|
123,
|
||||||
|
123,
|
||||||
|
},
|
||||||
|
ProofType: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
type sectorInfo struct {
|
||||||
|
sectorName string
|
||||||
|
fileType string
|
||||||
|
}
|
||||||
|
validSectorInfo := sectorInfo{
|
||||||
|
sectorName: validSectorName,
|
||||||
|
fileType: validSectorFileType,
|
||||||
|
}
|
||||||
|
|
||||||
|
tcs := map[string]struct {
|
||||||
|
siFnc func(pi *sectorInfo)
|
||||||
|
storeFnc func(s *mocks.Store, path string)
|
||||||
|
|
||||||
|
// reading a file or a dir
|
||||||
|
isDir bool
|
||||||
|
|
||||||
|
// expectation
|
||||||
|
noResponseBytes bool
|
||||||
|
expectedContentType string
|
||||||
|
expectedStatusCode int
|
||||||
|
expectedResponseBytes []byte
|
||||||
|
}{
|
||||||
|
"fails when sector name is invalid": {
|
||||||
|
siFnc: func(si *sectorInfo) {
|
||||||
|
si.sectorName = "invalid"
|
||||||
|
},
|
||||||
|
expectedStatusCode: http.StatusInternalServerError,
|
||||||
|
noResponseBytes: true,
|
||||||
|
},
|
||||||
|
"fails when file type is invalid": {
|
||||||
|
siFnc: func(si *sectorInfo) {
|
||||||
|
si.fileType = "invalid"
|
||||||
|
},
|
||||||
|
expectedStatusCode: http.StatusInternalServerError,
|
||||||
|
noResponseBytes: true,
|
||||||
|
},
|
||||||
|
"fails when error while acquiring sector file": {
|
||||||
|
storeFnc: func(l *mocks.Store, _ string) {
|
||||||
|
l.On("AcquireSector", mock.Anything, expectedSectorRef, storiface.FTUnsealed,
|
||||||
|
storiface.FTNone, storiface.PathStorage, storiface.AcquireMove).Return(storiface.SectorPaths{
|
||||||
|
Unsealed: "path",
|
||||||
|
},
|
||||||
|
storiface.SectorPaths{}, xerrors.New("some error"))
|
||||||
|
},
|
||||||
|
expectedStatusCode: http.StatusInternalServerError,
|
||||||
|
noResponseBytes: true,
|
||||||
|
},
|
||||||
|
"fails when acquired sector file path is empty": {
|
||||||
|
expectedStatusCode: http.StatusInternalServerError,
|
||||||
|
storeFnc: func(l *mocks.Store, _ string) {
|
||||||
|
|
||||||
|
l.On("AcquireSector", mock.Anything, expectedSectorRef, storiface.FTUnsealed,
|
||||||
|
storiface.FTNone, storiface.PathStorage, storiface.AcquireMove).Return(storiface.SectorPaths{},
|
||||||
|
storiface.SectorPaths{}, nil)
|
||||||
|
},
|
||||||
|
noResponseBytes: true,
|
||||||
|
},
|
||||||
|
"fails when acquired file does not exist": {
|
||||||
|
expectedStatusCode: http.StatusInternalServerError,
|
||||||
|
storeFnc: func(l *mocks.Store, _ string) {
|
||||||
|
|
||||||
|
l.On("AcquireSector", mock.Anything, expectedSectorRef, storiface.FTUnsealed,
|
||||||
|
storiface.FTNone, storiface.PathStorage, storiface.AcquireMove).Return(storiface.SectorPaths{
|
||||||
|
Unsealed: "path",
|
||||||
|
},
|
||||||
|
storiface.SectorPaths{}, nil)
|
||||||
|
},
|
||||||
|
noResponseBytes: true,
|
||||||
|
},
|
||||||
|
"successfully read a sector file": {
|
||||||
|
storeFnc: func(l *mocks.Store, path string) {
|
||||||
|
|
||||||
|
l.On("AcquireSector", mock.Anything, expectedSectorRef, storiface.FTUnsealed,
|
||||||
|
storiface.FTNone, storiface.PathStorage, storiface.AcquireMove).Return(storiface.SectorPaths{
|
||||||
|
Unsealed: path,
|
||||||
|
},
|
||||||
|
storiface.SectorPaths{}, nil)
|
||||||
|
},
|
||||||
|
|
||||||
|
noResponseBytes: false,
|
||||||
|
expectedContentType: "application/octet-stream",
|
||||||
|
expectedStatusCode: 200,
|
||||||
|
expectedResponseBytes: fileBytes,
|
||||||
|
},
|
||||||
|
"successfully read a sector dir": {
|
||||||
|
storeFnc: func(l *mocks.Store, path string) {
|
||||||
|
|
||||||
|
l.On("AcquireSector", mock.Anything, expectedSectorRef, storiface.FTUnsealed,
|
||||||
|
storiface.FTNone, storiface.PathStorage, storiface.AcquireMove).Return(storiface.SectorPaths{
|
||||||
|
Unsealed: path,
|
||||||
|
},
|
||||||
|
storiface.SectorPaths{}, nil)
|
||||||
|
},
|
||||||
|
|
||||||
|
isDir: true,
|
||||||
|
noResponseBytes: false,
|
||||||
|
expectedContentType: "application/x-tar",
|
||||||
|
expectedStatusCode: 200,
|
||||||
|
expectedResponseBytes: fileBytes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range tcs {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
var path string
|
||||||
|
|
||||||
|
if !tc.isDir {
|
||||||
|
// create file
|
||||||
|
tempFile, err := ioutil.TempFile("", "TestRemoteGetSector-")
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer os.Remove(tempFile.Name())
|
||||||
|
_, err = tempFile.Write(fileBytes)
|
||||||
|
require.NoError(t, err)
|
||||||
|
path = tempFile.Name()
|
||||||
|
} else {
|
||||||
|
// create dir with a file
|
||||||
|
tempFile2, err := ioutil.TempFile("", "TestRemoteGetSector-")
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer os.Remove(tempFile2.Name())
|
||||||
|
stat, err := os.Stat(tempFile2.Name())
|
||||||
|
require.NoError(t, err)
|
||||||
|
tempDir, err := ioutil.TempDir("", "TestRemoteGetSector-")
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer os.RemoveAll(tempDir)
|
||||||
|
require.NoError(t, os.Rename(tempFile2.Name(), filepath.Join(tempDir, stat.Name())))
|
||||||
|
|
||||||
|
path = tempDir
|
||||||
|
}
|
||||||
|
|
||||||
|
lstore := &mocks.Store{}
|
||||||
|
pfhandler := &mocks.PartialFileHandler{}
|
||||||
|
|
||||||
|
handler := &stores.FetchHandler{
|
||||||
|
lstore,
|
||||||
|
pfhandler,
|
||||||
|
}
|
||||||
|
|
||||||
|
// run http server
|
||||||
|
ts := httptest.NewServer(handler)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
si := validSectorInfo
|
||||||
|
if tc.siFnc != nil {
|
||||||
|
tc.siFnc(&si)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.storeFnc != nil {
|
||||||
|
tc.storeFnc(lstore, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// call remoteGetAllocated
|
||||||
|
url := fmt.Sprintf("%s/remote/%s/%s",
|
||||||
|
ts.URL,
|
||||||
|
si.fileType,
|
||||||
|
si.sectorName,
|
||||||
|
)
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
bz, err := ioutil.ReadAll(resp.Body)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// assert expected status code
|
||||||
|
require.Equal(t, tc.expectedStatusCode, resp.StatusCode)
|
||||||
|
|
||||||
|
if !tc.noResponseBytes {
|
||||||
|
if !tc.isDir {
|
||||||
|
require.EqualValues(t, tc.expectedResponseBytes, bz)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
require.Equal(t, tc.expectedContentType, resp.Header.Get("Content-Type"))
|
||||||
|
|
||||||
|
// assert expectations on the mocks
|
||||||
|
lstore.AssertExpectations(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
12
extern/sector-storage/stores/interface.go
vendored
12
extern/sector-storage/stores/interface.go
vendored
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-state-types/abi"
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/filecoin-project/lotus/extern/sector-storage/partialfile"
|
||||||
|
|
||||||
"github.com/filecoin-project/specs-storage/storage"
|
"github.com/filecoin-project/specs-storage/storage"
|
||||||
|
|
||||||
@ -11,6 +12,17 @@ import (
|
|||||||
"github.com/filecoin-project/lotus/extern/sector-storage/storiface"
|
"github.com/filecoin-project/lotus/extern/sector-storage/storiface"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// PartialFileHandler helps mock out the partial file functionality during testing.
|
||||||
|
type partialFileHandler interface {
|
||||||
|
// OpenPartialFile opens and returns a partial file at the given path and also verifies it has the given
|
||||||
|
// size
|
||||||
|
OpenPartialFile(maxPieceSize abi.PaddedPieceSize, path string) (*partialfile.PartialFile, error)
|
||||||
|
|
||||||
|
// HasAllocated returns true if the given partialfile has an unsealed piece starting at the given offset with the given size.
|
||||||
|
// returns false otherwise.
|
||||||
|
HasAllocated(pf *partialfile.PartialFile, offset storiface.UnpaddedByteIndex, size abi.UnpaddedPieceSize) (bool, error)
|
||||||
|
}
|
||||||
|
|
||||||
type Store interface {
|
type Store interface {
|
||||||
AcquireSector(ctx context.Context, s storage.SectorRef, existing storiface.SectorFileType, allocate storiface.SectorFileType, sealing storiface.PathType, op storiface.AcquireMode) (paths storiface.SectorPaths, stores storiface.SectorPaths, err error)
|
AcquireSector(ctx context.Context, s storage.SectorRef, existing storiface.SectorFileType, allocate storiface.SectorFileType, sealing storiface.PathType, op storiface.AcquireMode) (paths storiface.SectorPaths, stores storiface.SectorPaths, err error)
|
||||||
Remove(ctx context.Context, s abi.SectorID, types storiface.SectorFileType, force bool) error
|
Remove(ctx context.Context, s abi.SectorID, types storiface.SectorFileType, force bool) error
|
||||||
|
61
extern/sector-storage/stores/mocks/PartialFileHandler.go
vendored
Normal file
61
extern/sector-storage/stores/mocks/PartialFileHandler.go
vendored
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Code generated by mockery 2.7.5. DO NOT EDIT.
|
||||||
|
|
||||||
|
package mocks
|
||||||
|
|
||||||
|
import (
|
||||||
|
abi "github.com/filecoin-project/go-state-types/abi"
|
||||||
|
mock "github.com/stretchr/testify/mock"
|
||||||
|
|
||||||
|
partialfile "github.com/filecoin-project/lotus/extern/sector-storage/partialfile"
|
||||||
|
|
||||||
|
storiface "github.com/filecoin-project/lotus/extern/sector-storage/storiface"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PartialFileHandler is an autogenerated mock type for the PartialFileHandler type
|
||||||
|
type PartialFileHandler struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasAllocated provides a mock function with given fields: pf, offset, size
|
||||||
|
func (_m *PartialFileHandler) HasAllocated(pf *partialfile.PartialFile, offset storiface.UnpaddedByteIndex, size abi.UnpaddedPieceSize) (bool, error) {
|
||||||
|
ret := _m.Called(pf, offset, size)
|
||||||
|
|
||||||
|
var r0 bool
|
||||||
|
if rf, ok := ret.Get(0).(func(*partialfile.PartialFile, storiface.UnpaddedByteIndex, abi.UnpaddedPieceSize) bool); ok {
|
||||||
|
r0 = rf(pf, offset, size)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(*partialfile.PartialFile, storiface.UnpaddedByteIndex, abi.UnpaddedPieceSize) error); ok {
|
||||||
|
r1 = rf(pf, offset, size)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenPartialFile provides a mock function with given fields: maxPieceSize, path
|
||||||
|
func (_m *PartialFileHandler) OpenPartialFile(maxPieceSize abi.PaddedPieceSize, path string) (*partialfile.PartialFile, error) {
|
||||||
|
ret := _m.Called(maxPieceSize, path)
|
||||||
|
|
||||||
|
var r0 *partialfile.PartialFile
|
||||||
|
if rf, ok := ret.Get(0).(func(abi.PaddedPieceSize, string) *partialfile.PartialFile); ok {
|
||||||
|
r0 = rf(maxPieceSize, path)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*partialfile.PartialFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(abi.PaddedPieceSize, string) error); ok {
|
||||||
|
r1 = rf(maxPieceSize, path)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
115
extern/sector-storage/stores/mocks/Store.go
vendored
Normal file
115
extern/sector-storage/stores/mocks/Store.go
vendored
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
// Code generated by mockery 2.7.5. DO NOT EDIT.
|
||||||
|
|
||||||
|
package mocks
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
|
||||||
|
abi "github.com/filecoin-project/go-state-types/abi"
|
||||||
|
|
||||||
|
fsutil "github.com/filecoin-project/lotus/extern/sector-storage/fsutil"
|
||||||
|
|
||||||
|
mock "github.com/stretchr/testify/mock"
|
||||||
|
|
||||||
|
storage "github.com/filecoin-project/specs-storage/storage"
|
||||||
|
|
||||||
|
stores "github.com/filecoin-project/lotus/extern/sector-storage/stores"
|
||||||
|
|
||||||
|
storiface "github.com/filecoin-project/lotus/extern/sector-storage/storiface"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Store is an autogenerated mock type for the Store type
|
||||||
|
type Store struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// AcquireSector provides a mock function with given fields: ctx, s, existing, allocate, sealing, op
|
||||||
|
func (_m *Store) AcquireSector(ctx context.Context, s storage.SectorRef, existing storiface.SectorFileType, allocate storiface.SectorFileType, sealing storiface.PathType, op storiface.AcquireMode) (storiface.SectorPaths, storiface.SectorPaths, error) {
|
||||||
|
ret := _m.Called(ctx, s, existing, allocate, sealing, op)
|
||||||
|
|
||||||
|
var r0 storiface.SectorPaths
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, storage.SectorRef, storiface.SectorFileType, storiface.SectorFileType, storiface.PathType, storiface.AcquireMode) storiface.SectorPaths); ok {
|
||||||
|
r0 = rf(ctx, s, existing, allocate, sealing, op)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(storiface.SectorPaths)
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 storiface.SectorPaths
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, storage.SectorRef, storiface.SectorFileType, storiface.SectorFileType, storiface.PathType, storiface.AcquireMode) storiface.SectorPaths); ok {
|
||||||
|
r1 = rf(ctx, s, existing, allocate, sealing, op)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Get(1).(storiface.SectorPaths)
|
||||||
|
}
|
||||||
|
|
||||||
|
var r2 error
|
||||||
|
if rf, ok := ret.Get(2).(func(context.Context, storage.SectorRef, storiface.SectorFileType, storiface.SectorFileType, storiface.PathType, storiface.AcquireMode) error); ok {
|
||||||
|
r2 = rf(ctx, s, existing, allocate, sealing, op)
|
||||||
|
} else {
|
||||||
|
r2 = ret.Error(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1, r2
|
||||||
|
}
|
||||||
|
|
||||||
|
// FsStat provides a mock function with given fields: ctx, id
|
||||||
|
func (_m *Store) FsStat(ctx context.Context, id stores.ID) (fsutil.FsStat, error) {
|
||||||
|
ret := _m.Called(ctx, id)
|
||||||
|
|
||||||
|
var r0 fsutil.FsStat
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, stores.ID) fsutil.FsStat); ok {
|
||||||
|
r0 = rf(ctx, id)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(fsutil.FsStat)
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, stores.ID) error); ok {
|
||||||
|
r1 = rf(ctx, id)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// MoveStorage provides a mock function with given fields: ctx, s, types
|
||||||
|
func (_m *Store) MoveStorage(ctx context.Context, s storage.SectorRef, types storiface.SectorFileType) error {
|
||||||
|
ret := _m.Called(ctx, s, types)
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, storage.SectorRef, storiface.SectorFileType) error); ok {
|
||||||
|
r0 = rf(ctx, s, types)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove provides a mock function with given fields: ctx, s, types, force
|
||||||
|
func (_m *Store) Remove(ctx context.Context, s abi.SectorID, types storiface.SectorFileType, force bool) error {
|
||||||
|
ret := _m.Called(ctx, s, types, force)
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, abi.SectorID, storiface.SectorFileType, bool) error); ok {
|
||||||
|
r0 = rf(ctx, s, types, force)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveCopies provides a mock function with given fields: ctx, s, types
|
||||||
|
func (_m *Store) RemoveCopies(ctx context.Context, s abi.SectorID, types storiface.SectorFileType) error {
|
||||||
|
ret := _m.Called(ctx, s, types)
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, abi.SectorID, storiface.SectorFileType) error); ok {
|
||||||
|
r0 = rf(ctx, s, types)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user