Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat:config:force existing users to opt into new defaults #10488

Merged
merged 13 commits into from
Mar 20, 2023
4 changes: 2 additions & 2 deletions cmd/lotus/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ var configDefaultCmd = &cli.Command{
Action: func(cctx *cli.Context) error {
c := config.DefaultFullNode()

cb, err := config.ConfigUpdate(c, nil, !cctx.Bool("no-comment"))
cb, err := config.ConfigUpdate(c, nil, !cctx.Bool("no-comment"), config.KeepUncommented(config.MatchEnableSplitstoreField))
ZenGround0 marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return err
}
Expand Down Expand Up @@ -83,7 +83,7 @@ var configUpdateCmd = &cli.Command{

cfgDef := config.DefaultFullNode()

updated, err := config.ConfigUpdate(cfgNode, cfgDef, !cctx.Bool("no-comment"))
updated, err := config.ConfigUpdate(cfgNode, cfgDef, !cctx.Bool("no-comment"), config.KeepUncommented(config.MatchEnableSplitstoreField))
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion documentation/en/default-lotus-config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@
[Chainstore]
# type: bool
# env var: LOTUS_CHAINSTORE_ENABLESPLITSTORE
#EnableSplitstore = true
EnableSplitstore = true

[Chainstore.Splitstore]
# ColdStoreType specifies the type of the coldstore.
Expand Down
101 changes: 95 additions & 6 deletions node/config/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"reflect"
"regexp"
Expand All @@ -17,17 +18,40 @@ import (

// FromFile loads config from a specified file overriding defaults specified in
// the def parameter. If file does not exist or is empty defaults are assumed.
func FromFile(path string, def interface{}) (interface{}, error) {
func FromFile(path string, def interface{}, opts ...LoadCfgOpt) (interface{}, error) {
var loadOpts cfgLoadOpts
for _, opt := range opts {
if err := opt(&loadOpts); err != nil {
return nil, xerrors.Errorf("failed to apply load cfg option: %w", err)
}
}

// check for loadability
file, err := os.Open(path)
defer file.Close() //nolint:errcheck,staticcheck // The file is RO
switch {
case os.IsNotExist(err):
if loadOpts.canFallbackOnDefault != nil {
if err := loadOpts.canFallbackOnDefault(); err != nil {
return nil, err
}
}
return def, nil
ZenGround0 marked this conversation as resolved.
Show resolved Hide resolved
case err != nil:
return nil, err
}

defer file.Close() //nolint:errcheck // The file is RO
return FromReader(file, def)
var buf bytes.Buffer
tee := io.TeeReader(file, &buf)
cfgBs, err := ioutil.ReadAll(tee)
if err != nil {
return nil, xerrors.Errorf("failed to read config for loadability checks %w", err)
}
s := string(cfgBs)
if err := loadOpts.validate(s); err != nil {
return nil, xerrors.Errorf("config failed loadability check: %w", err)
}
return FromReader(&buf, def)
ZenGround0 marked this conversation as resolved.
Show resolved Hide resolved
}

// FromReader loads config from a reader instance.
Expand All @@ -46,7 +70,70 @@ func FromReader(reader io.Reader, def interface{}) (interface{}, error) {
return cfg, nil
}

func ConfigUpdate(cfgCur, cfgDef interface{}, comment bool) ([]byte, error) {
type cfgLoadOpts struct {
canFallbackOnDefault func() error
validate func(string) error
}

type LoadCfgOpt func(opts *cfgLoadOpts) error

func SetCanFallbackOnDefault(f func() error) LoadCfgOpt {
return func(opts *cfgLoadOpts) error {
opts.canFallbackOnDefault = f
return nil
}
}

func SetValidate(f func(string) error) LoadCfgOpt {
return func(opts *cfgLoadOpts) error {
opts.validate = f
return nil
}
}

func NoDefaultForSplitstoreTransition() error {
return xerrors.Errorf("FullNode config not found and fallback to default disallowed while we transition to splitstore discard default. Use `lotus config default` to set this repo up with a default config. Be sure to set `EnableSplitstore` to `false` if you are running a full archive node")
}

func ValidateSplitstoreSet(cfgRaw string) error {
explicitSplitstoreRx := regexp.MustCompile("\n[\t\n\f\r ]*EnableSplitstore =")
ZenGround0 marked this conversation as resolved.
Show resolved Hide resolved
if match := explicitSplitstoreRx.FindString(cfgRaw); match == "" {
ZenGround0 marked this conversation as resolved.
Show resolved Hide resolved
return xerrors.Errorf("Config does not contain explicit set of EnableSplitstore field, refusing to load. Please explicitly set EnableSplitstore. Set it to false if you are running a full archival node")
}
return nil
}

type cfgUpdateOpts struct {
keepUncommented func(string) bool
}

// UpdateCfgOpt is a functional option for updating the config
type UpdateCfgOpt func(opts *cfgUpdateOpts) error

// KeepUncommented sets a function for matching default valeus that should remain uncommented during
// a config update that comments out default values.
func KeepUncommented(f func(string) bool) UpdateCfgOpt {
return func(opts *cfgUpdateOpts) error {
opts.keepUncommented = f
return nil
}
}

// Match the EnableSplitstore field
func MatchEnableSplitstoreField(s string) bool {
enableSplitstoreRx := regexp.MustCompile("^[\t\n\f\r ]*EnableSplitstore =")
match := enableSplitstoreRx.FindString(s)
return match != ""
}

// ConfigUpdate takes in a config and a default config and optionally comments out default values
func ConfigUpdate(cfgCur, cfgDef interface{}, comment bool, opts ...UpdateCfgOpt) ([]byte, error) {
ZenGround0 marked this conversation as resolved.
Show resolved Hide resolved
var updateOpts cfgUpdateOpts
for _, opt := range opts {
if err := opt(&updateOpts); err != nil {
return nil, xerrors.Errorf("failed to apply update cfg option to ConfigUpdate's config: %w", err)
}
}
var nodeStr, defStr string
if cfgDef != nil {
buf := new(bytes.Buffer)
Expand Down Expand Up @@ -130,8 +217,10 @@ func ConfigUpdate(cfgCur, cfgDef interface{}, comment bool) ([]byte, error) {
}
}

// filter lines from options
optsFilter := updateOpts.keepUncommented != nil && updateOpts.keepUncommented(line)
// if there is the same line in the default config, comment it out it output
if _, found := defaults[strings.TrimSpace(nodeLines[i])]; (cfgDef == nil || found) && len(line) > 0 {
if _, found := defaults[strings.TrimSpace(nodeLines[i])]; (cfgDef == nil || found) && len(line) > 0 && !optsFilter {
line = pad + "#" + line[len(pad):]
}
outLines = append(outLines, line)
Expand Down Expand Up @@ -159,5 +248,5 @@ func ConfigUpdate(cfgCur, cfgDef interface{}, comment bool) ([]byte, error) {
}

func ConfigComment(t interface{}) ([]byte, error) {
return ConfigUpdate(t, nil, true)
return ConfigUpdate(t, nil, true, KeepUncommented(MatchEnableSplitstoreField))
}
7 changes: 6 additions & 1 deletion node/repo/fsrepo.go
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,12 @@ func (fsr *fsLockedRepo) Config() (interface{}, error) {
}

func (fsr *fsLockedRepo) loadConfigFromDisk() (interface{}, error) {
return config.FromFile(fsr.configPath, fsr.repoType.Config())
var opts []config.LoadCfgOpt
if fsr.repoType == FullNode {
opts = append(opts, config.SetCanFallbackOnDefault(config.NoDefaultForSplitstoreTransition))
opts = append(opts, config.SetValidate(config.ValidateSplitstoreSet))
}
return config.FromFile(fsr.configPath, fsr.repoType.Config(), opts...)
}

func (fsr *fsLockedRepo) SetConfig(c func(interface{})) error {
Expand Down