add support for replacing config after node starts

- TODO: does a "locked repo" need fine-grained (i.e. field-level) locking?
This commit is contained in:
laser 2020-06-09 16:37:18 -07:00
parent 2d6b2e3811
commit cf321f7667
5 changed files with 62 additions and 7 deletions

View File

@ -412,7 +412,7 @@ func Repo(r repo.Repo) Option {
if err != nil { if err != nil {
return err return err
} }
c, err := lr.GetConfig() c, err := lr.Config()
if err != nil { if err != nil {
return err return err
} }

View File

@ -11,6 +11,7 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/BurntSushi/toml"
"github.com/ipfs/go-datastore" "github.com/ipfs/go-datastore"
fslock "github.com/ipfs/go-fs-lock" fslock "github.com/ipfs/go-fs-lock"
logging "github.com/ipfs/go-log/v2" logging "github.com/ipfs/go-log/v2"
@ -271,6 +272,29 @@ func (fsr *fsLockedRepo) Config() (interface{}, error) {
return config.FromFile(fsr.join(fsConfig), defConfForType(fsr.repoType)) return config.FromFile(fsr.join(fsConfig), defConfForType(fsr.repoType))
} }
func (fsr *fsLockedRepo) SetConfig(cfg interface{}) error {
if err := fsr.stillValid(); err != nil {
return err
}
tmp, err := ioutil.TempFile("", "lotus-config-temp")
if err != nil {
return err
}
err = toml.NewEncoder(tmp).Encode(cfg)
if err != nil {
return err
}
err = os.Rename(tmp.Name(), fsr.join(fsConfig))
if err != nil {
return err
}
return nil
}
func (fsr *fsLockedRepo) GetStorage() (stores.StorageConfig, error) { func (fsr *fsLockedRepo) GetStorage() (stores.StorageConfig, error) {
fsr.storageLk.Lock() fsr.storageLk.Lock()
defer fsr.storageLk.Unlock() defer fsr.storageLk.Unlock()

View File

@ -37,7 +37,8 @@ type LockedRepo interface {
Datastore(namespace string) (datastore.Batching, error) Datastore(namespace string) (datastore.Batching, error)
// Returns config in this repo // Returns config in this repo
GetConfig() (interface{}, error) Config() (interface{}, error)
SetConfig(interface{}) error
GetStorage() (stores.StorageConfig, error) GetStorage() (stores.StorageConfig, error)
SetStorage(func(*stores.StorageConfig)) error SetStorage(func(*stores.StorageConfig)) error

View File

@ -30,8 +30,13 @@ type MemRepo struct {
token *byte token *byte
datastore datastore.Datastore datastore datastore.Datastore
configF func(t RepoType) interface{}
keystore map[string]types.KeyInfo keystore map[string]types.KeyInfo
// given a repo type, produce the default config
configF func(t RepoType) interface{}
// holds the current config value
config interface{}
} }
type lockedMemRepo struct { type lockedMemRepo struct {
@ -217,11 +222,22 @@ func (lmem *lockedMemRepo) Datastore(ns string) (datastore.Batching, error) {
return namespace.Wrap(lmem.mem.datastore, datastore.NewKey(ns)), nil return namespace.Wrap(lmem.mem.datastore, datastore.NewKey(ns)), nil
} }
func (lmem *lockedMemRepo) GetConfig() (interface{}, error) { func (lmem *lockedMemRepo) Config() (interface{}, error) {
if err := lmem.checkToken(); err != nil { if err := lmem.checkToken(); err != nil {
return nil, err return nil, err
} }
return lmem.mem.configF(lmem.t), nil
if lmem.mem.config == nil {
lmem.mem.config = lmem.mem.configF(lmem.t)
}
return lmem.mem.config, nil
}
func (lmem *lockedMemRepo) SetConfig(cfg interface{}) error {
lmem.mem.config = cfg
return nil
} }
func (lmem *lockedMemRepo) Storage() (stores.StorageConfig, error) { func (lmem *lockedMemRepo) Storage() (stores.StorageConfig, error) {

View File

@ -9,6 +9,8 @@ import (
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/node/config" "github.com/filecoin-project/lotus/node/config"
"github.com/stretchr/testify/require"
) )
func basicTest(t *testing.T, repo Repo) { func basicTest(t *testing.T, repo Repo) {
@ -47,10 +49,22 @@ func basicTest(t *testing.T, repo Repo) {
assert.NoError(t, err, "setting multiaddr shouldn't error") assert.NoError(t, err, "setting multiaddr shouldn't error")
assert.Equal(t, ma, apima, "returned API multiaddr should be the same") assert.Equal(t, ma, apima, "returned API multiaddr should be the same")
cfg, err := lrepo.Config() c1, err := lrepo.Config()
assert.Equal(t, config.DefaultFullNode(), cfg, "there should be a default config") assert.Equal(t, config.DefaultFullNode(), c1, "there should be a default config")
assert.NoError(t, err, "config should not error") assert.NoError(t, err, "config should not error")
// mutate config and persist back to repo
cfg1 := c1.(*config.FullNode)
cfg1.Client.IpfsMAddr = "duvall"
err = lrepo.SetConfig(cfg1)
assert.NoError(t, err)
// load config and verify changes
c2, err := lrepo.Config()
require.NoError(t, err)
cfg2 := c2.(*config.FullNode)
require.Equal(t, cfg2.Client.IpfsMAddr, "duvall")
err = lrepo.Close() err = lrepo.Close()
assert.NoError(t, err, "should be able to close") assert.NoError(t, err, "should be able to close")