lotus/node/repo/memrepo.go

411 lines
8.8 KiB
Go
Raw Normal View History

package repo
import (
"context"
"encoding/json"
2022-01-14 13:11:04 +00:00
"github.com/filecoin-project/lotus/node/config"
"github.com/google/uuid"
2019-11-12 17:59:38 +00:00
"io/ioutil"
"os"
"path/filepath"
"sync"
"github.com/ipfs/go-datastore"
2019-07-10 15:38:35 +00:00
"github.com/ipfs/go-datastore/namespace"
dssync "github.com/ipfs/go-datastore/sync"
"github.com/multiformats/go-multiaddr"
"golang.org/x/xerrors"
"github.com/filecoin-project/lotus/blockstore"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/extern/sector-storage/fsutil"
"github.com/filecoin-project/lotus/extern/sector-storage/stores"
)
type MemRepo struct {
api struct {
sync.Mutex
2019-07-23 18:49:09 +00:00
ma multiaddr.Multiaddr
token []byte
}
repoLock chan struct{}
token *byte
datastore datastore.Datastore
keystore map[string]types.KeyInfo
blockstore blockstore.Blockstore
2022-01-14 13:11:04 +00:00
sc *stores.StorageConfig
tempDir string
// given a repo type, produce the default config
configF func(t RepoType) interface{}
// holds the current config value
config struct {
sync.Mutex
val interface{}
}
}
type lockedMemRepo struct {
mem *MemRepo
t RepoType
sync.RWMutex
2022-01-14 13:11:04 +00:00
token *byte
}
func (lmem *lockedMemRepo) GetStorage() (stores.StorageConfig, error) {
if err := lmem.checkToken(); err != nil {
return stores.StorageConfig{}, err
}
2022-01-14 13:11:04 +00:00
if lmem.mem.sc == nil {
lmem.mem.sc = &stores.StorageConfig{StoragePaths: []stores.LocalPath{
2020-03-09 06:21:42 +00:00
{Path: lmem.Path()},
}}
}
2022-01-14 13:11:04 +00:00
return *lmem.mem.sc, nil
2020-03-03 22:19:22 +00:00
}
func (lmem *lockedMemRepo) SetStorage(c func(*stores.StorageConfig)) error {
if err := lmem.checkToken(); err != nil {
return err
}
2020-03-09 19:22:30 +00:00
_, _ = lmem.GetStorage()
2022-01-14 13:11:04 +00:00
c(lmem.mem.sc)
2020-03-09 06:21:42 +00:00
return nil
2020-03-03 22:19:22 +00:00
}
2020-07-08 15:23:27 +00:00
func (lmem *lockedMemRepo) Stat(path string) (fsutil.FsStat, error) {
return fsutil.Statfs(path)
}
func (lmem *lockedMemRepo) DiskUsage(path string) (int64, error) {
si, err := fsutil.FileSize(path)
if err != nil {
return 0, err
}
return si.OnDisk, nil
}
2019-07-12 09:59:18 +00:00
func (lmem *lockedMemRepo) Path() string {
lmem.Lock()
defer lmem.Unlock()
2022-01-14 13:11:04 +00:00
if lmem.mem.tempDir != "" {
return lmem.mem.tempDir
}
2019-11-12 17:59:38 +00:00
t, err := ioutil.TempDir(os.TempDir(), "lotus-memrepo-temp-")
if err != nil {
panic(err) // only used in tests, probably fine
}
if lmem.t == StorageMiner {
// this is required due to the method makeDealStaging from cmd/lotus-storage-miner/init.go
// deal-staging is the directory deal files are staged in before being sealed into sectors
// for offline deal flow.
if err := os.MkdirAll(filepath.Join(t, "deal-staging"), 0755); err != nil {
panic(err)
}
2022-01-14 13:11:04 +00:00
}
if lmem.t == StorageMiner || lmem.t == Worker {
lmem.initSectorStore(t)
}
2022-01-14 13:11:04 +00:00
lmem.mem.tempDir = t
return t
}
2022-01-14 13:11:04 +00:00
func (lmem *lockedMemRepo) initSectorStore(t string) {
if err := config.WriteStorageFile(filepath.Join(t, fsStorageConfig), stores.StorageConfig{
StoragePaths: []stores.LocalPath{
{Path: t},
}}); err != nil {
panic(err)
}
2022-01-14 13:11:04 +00:00
b, err := json.MarshalIndent(&stores.LocalStorageMeta{
ID: stores.ID(uuid.New().String()),
Weight: 10,
CanSeal: true,
CanStore: true,
}, "", " ")
if err != nil {
panic(err)
}
if err := ioutil.WriteFile(filepath.Join(t, "sectorstore.json"), b, 0644); err != nil {
panic(err)
}
2019-07-12 09:59:18 +00:00
}
var _ Repo = &MemRepo{}
// MemRepoOptions contains options for memory repo
type MemRepoOptions struct {
2019-10-03 00:02:06 +00:00
Ds datastore.Datastore
ConfigF func(RepoType) interface{}
2019-10-03 00:02:06 +00:00
KeyStore map[string]types.KeyInfo
}
// NewMemory creates new memory based repo with provided options.
// opts can be nil, it will be replaced with defaults.
// Any field in opts can be nil, they will be replaced by defaults.
func NewMemory(opts *MemRepoOptions) *MemRepo {
if opts == nil {
opts = &MemRepoOptions{}
}
if opts.ConfigF == nil {
opts.ConfigF = defConfForType
}
if opts.Ds == nil {
opts.Ds = dssync.MutexWrap(datastore.NewMapDatastore())
}
if opts.KeyStore == nil {
opts.KeyStore = make(map[string]types.KeyInfo)
}
return &MemRepo{
repoLock: make(chan struct{}, 1),
2021-01-29 23:17:25 +00:00
blockstore: blockstore.WrapIDStore(blockstore.NewMemorySync()),
datastore: opts.Ds,
configF: opts.ConfigF,
keystore: opts.KeyStore,
}
}
func (mem *MemRepo) APIEndpoint() (multiaddr.Multiaddr, error) {
mem.api.Lock()
defer mem.api.Unlock()
if mem.api.ma == nil {
return nil, ErrNoAPIEndpoint
}
return mem.api.ma, nil
}
2019-07-23 18:49:09 +00:00
func (mem *MemRepo) APIToken() ([]byte, error) {
mem.api.Lock()
defer mem.api.Unlock()
if mem.api.ma == nil {
return nil, ErrNoAPIToken
}
return mem.api.token, nil
}
func (mem *MemRepo) Lock(t RepoType) (LockedRepo, error) {
select {
case mem.repoLock <- struct{}{}:
default:
return nil, ErrRepoAlreadyLocked
}
mem.token = new(byte)
return &lockedMemRepo{
mem: mem,
t: t,
token: mem.token,
}, nil
}
2022-01-14 13:11:04 +00:00
func (mem *MemRepo) Cleanup() {
mem.api.Lock()
defer mem.api.Unlock()
if mem.tempDir != "" {
if err := os.RemoveAll(mem.tempDir); err != nil {
log.Errorw("cleanup test memrepo", "error", err)
}
mem.tempDir = ""
}
}
func (lmem *lockedMemRepo) Readonly() bool {
return false
}
func (lmem *lockedMemRepo) checkToken() error {
lmem.RLock()
defer lmem.RUnlock()
if lmem.mem.token != lmem.token {
return ErrClosedRepo
}
return nil
}
func (lmem *lockedMemRepo) Close() error {
if err := lmem.checkToken(); err != nil {
return err
}
lmem.Lock()
defer lmem.Unlock()
if lmem.mem.token != lmem.token {
return ErrClosedRepo
}
lmem.mem.token = nil
lmem.mem.api.Lock()
lmem.mem.api.ma = nil
lmem.mem.api.Unlock()
<-lmem.mem.repoLock // unlock
return nil
}
2021-01-26 10:25:34 +00:00
func (lmem *lockedMemRepo) Datastore(_ context.Context, ns string) (datastore.Batching, error) {
if err := lmem.checkToken(); err != nil {
return nil, err
}
2019-07-10 15:38:35 +00:00
return namespace.Wrap(lmem.mem.datastore, datastore.NewKey(ns)), nil
}
func (lmem *lockedMemRepo) Blockstore(ctx context.Context, domain BlockstoreDomain) (blockstore.Blockstore, error) {
if domain != UniversalBlockstore {
return nil, ErrInvalidBlockstoreDomain
}
return lmem.mem.blockstore, nil
}
func (lmem *lockedMemRepo) SplitstorePath() (string, error) {
return ioutil.TempDir("", "splitstore.*")
}
2020-07-06 23:39:30 +00:00
func (lmem *lockedMemRepo) ListDatastores(ns string) ([]int64, error) {
return nil, nil
}
2020-07-06 20:03:37 +00:00
func (lmem *lockedMemRepo) DeleteDatastore(ns string) error {
/** poof **/
return nil
}
func (lmem *lockedMemRepo) Config() (interface{}, error) {
if err := lmem.checkToken(); err != nil {
return nil, err
}
lmem.mem.config.Lock()
defer lmem.mem.config.Unlock()
if lmem.mem.config.val == nil {
lmem.mem.config.val = lmem.mem.configF(lmem.t)
}
return lmem.mem.config.val, nil
}
func (lmem *lockedMemRepo) SetConfig(c func(interface{})) error {
if err := lmem.checkToken(); err != nil {
return err
}
lmem.mem.config.Lock()
defer lmem.mem.config.Unlock()
if lmem.mem.config.val == nil {
lmem.mem.config.val = lmem.mem.configF(lmem.t)
}
c(lmem.mem.config.val)
return nil
2020-03-03 22:19:22 +00:00
}
func (lmem *lockedMemRepo) SetAPIEndpoint(ma multiaddr.Multiaddr) error {
if err := lmem.checkToken(); err != nil {
return err
}
lmem.mem.api.Lock()
lmem.mem.api.ma = ma
lmem.mem.api.Unlock()
return nil
}
2019-07-23 18:49:09 +00:00
func (lmem *lockedMemRepo) SetAPIToken(token []byte) error {
if err := lmem.checkToken(); err != nil {
return err
}
lmem.mem.api.Lock()
lmem.mem.api.token = token
lmem.mem.api.Unlock()
return nil
}
func (lmem *lockedMemRepo) KeyStore() (types.KeyStore, error) {
if err := lmem.checkToken(); err != nil {
return nil, err
}
return lmem, nil
}
// Implement KeyStore on the same instance
// List lists all the keys stored in the KeyStore
func (lmem *lockedMemRepo) List() ([]string, error) {
if err := lmem.checkToken(); err != nil {
return nil, err
}
lmem.RLock()
defer lmem.RUnlock()
res := make([]string, 0, len(lmem.mem.keystore))
for k := range lmem.mem.keystore {
res = append(res, k)
}
return res, nil
}
// Get gets a key out of keystore and returns types.KeyInfo coresponding to named key
func (lmem *lockedMemRepo) Get(name string) (types.KeyInfo, error) {
if err := lmem.checkToken(); err != nil {
return types.KeyInfo{}, err
}
lmem.RLock()
defer lmem.RUnlock()
key, ok := lmem.mem.keystore[name]
if !ok {
return types.KeyInfo{}, xerrors.Errorf("getting key '%s': %w", name, types.ErrKeyInfoNotFound)
}
return key, nil
}
// Put saves key info under given name
func (lmem *lockedMemRepo) Put(name string, key types.KeyInfo) error {
if err := lmem.checkToken(); err != nil {
return err
}
lmem.Lock()
defer lmem.Unlock()
_, isThere := lmem.mem.keystore[name]
if isThere {
2019-10-18 11:39:31 +00:00
return xerrors.Errorf("putting key '%s': %w", name, types.ErrKeyExists)
}
lmem.mem.keystore[name] = key
return nil
}
func (lmem *lockedMemRepo) Delete(name string) error {
if err := lmem.checkToken(); err != nil {
return err
}
lmem.Lock()
defer lmem.Unlock()
_, isThere := lmem.mem.keystore[name]
if !isThere {
return xerrors.Errorf("deleting key '%s': %w", name, types.ErrKeyInfoNotFound)
}
delete(lmem.mem.keystore, name)
return nil
}