cli: add --relative-path option

To be able running the node from any working directory by simply
pointing the relative-path as prefix for relative parameters set in
config.

Closes #3179.

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
This commit is contained in:
Ekaterina Pavlova 2023-11-23 21:41:50 +04:00
parent 195bb742ca
commit f457d50773
6 changed files with 58 additions and 11 deletions

View file

@ -73,6 +73,13 @@ var ConfigFile = cli.StringFlag{
Usage: "path to the node configuration file (overrides --config-path option)", Usage: "path to the node configuration file (overrides --config-path option)",
} }
// RelativePath is a flag for commands that use node configuration and provide
// a prefix to all relative paths in config files.
var RelativePath = cli.StringFlag{
Name: "relative-path",
Usage: "a prefix to all relative paths in the node configuration file",
}
// Debug is a flag for commands that allow node in debug mode usage. // Debug is a flag for commands that allow node in debug mode usage.
var Debug = cli.BoolFlag{ var Debug = cli.BoolFlag{
Name: "debug, d", Name: "debug, d",
@ -159,15 +166,18 @@ func GetRPCWithInvoker(gctx context.Context, ctx *cli.Context, signers []transac
// GetConfigFromContext looks at the path and the mode flags in the given config and // GetConfigFromContext looks at the path and the mode flags in the given config and
// returns an appropriate config. // returns an appropriate config.
func GetConfigFromContext(ctx *cli.Context) (config.Config, error) { func GetConfigFromContext(ctx *cli.Context) (config.Config, error) {
var configFile = ctx.String("config-file") var (
configFile = ctx.String("config-file")
relativePath = ctx.String("relative-path")
)
if len(configFile) != 0 { if len(configFile) != 0 {
return config.LoadFile(configFile) return config.LoadFile(configFile, relativePath)
} }
var configPath = "./config" var configPath = "./config"
if argCp := ctx.String("config-path"); argCp != "" { if argCp := ctx.String("config-path"); argCp != "" {
configPath = argCp configPath = argCp
} }
return config.Load(configPath, GetNetwork(ctx)) return config.Load(configPath, GetNetwork(ctx), relativePath)
} }
var ( var (

View file

@ -34,7 +34,7 @@ import (
// NewCommands returns 'node' command. // NewCommands returns 'node' command.
func NewCommands() []cli.Command { func NewCommands() []cli.Command {
cfgFlags := []cli.Flag{options.Config, options.ConfigFile} cfgFlags := []cli.Flag{options.Config, options.ConfigFile, options.RelativePath}
cfgFlags = append(cfgFlags, options.Network...) cfgFlags = append(cfgFlags, options.Network...)
var cfgWithCountFlags = make([]cli.Flag, len(cfgFlags)) var cfgWithCountFlags = make([]cli.Flag, len(cfgFlags))
copy(cfgWithCountFlags, cfgFlags) copy(cfgWithCountFlags, cfgFlags)

View file

@ -49,6 +49,18 @@ func TestGetConfigFromContext(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, netmode.TestNet, cfg.ProtocolConfiguration.Magic) require.Equal(t, netmode.TestNet, cfg.ProtocolConfiguration.Magic)
}) })
t.Run("relative-path", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String("relative-path", "../../config", "")
set.Bool("testnet", true, "")
set.String("config-file", "../../config/protocol.testnet.yml", "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
cfg, err := options.GetConfigFromContext(ctx)
require.NoError(t, err)
require.Equal(t, "../../config/chains/testnet", cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath)
require.Equal(t, "/cn_wallet.json", cfg.ApplicationConfiguration.Consensus.UnlockWallet.Path)
require.Equal(t, "/notary_wallet.json", cfg.ApplicationConfiguration.P2PNotary.UnlockWallet.Path)
})
} }
func TestHandleLoggingParams(t *testing.T) { func TestHandleLoggingParams(t *testing.T) {

View file

@ -95,8 +95,10 @@ func newTestVMCLIWithLogoAndCustomConfig(t *testing.T, printLogo bool, cfg *conf
if cfg == nil { if cfg == nil {
configPath := "../../config/protocol.unit_testnet.single.yml" configPath := "../../config/protocol.unit_testnet.single.yml"
var err error var err error
c, err = config.LoadFile(configPath) c, err = config.LoadFile(configPath, "../../config")
require.NoError(t, err, "could not load chain config") require.NoError(t, err, "could not load chain config")
require.Equal(t, "../../testdata/wallet1_solo.json", c.ApplicationConfiguration.Consensus.UnlockWallet.Path)
require.Equal(t, "/notary_wallet.json", c.ApplicationConfiguration.P2PNotary.UnlockWallet.Path)
c.ApplicationConfiguration.DBConfiguration.Type = dbconfig.InMemoryDB c.ApplicationConfiguration.DBConfiguration.Type = dbconfig.InMemoryDB
} else { } else {
c = *cfg c = *cfg

View file

@ -13,7 +13,7 @@ import (
// NewCommands returns 'vm' command. // NewCommands returns 'vm' command.
func NewCommands() []cli.Command { func NewCommands() []cli.Command {
cfgFlags := []cli.Flag{options.Config, options.ConfigFile} cfgFlags := []cli.Flag{options.Config, options.ConfigFile, options.RelativePath}
cfgFlags = append(cfgFlags, options.Network...) cfgFlags = append(cfgFlags, options.Network...)
return []cli.Command{{ return []cli.Command{{
Name: "vm", Name: "vm",

View file

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"os" "os"
"path/filepath"
"time" "time"
"github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/config/netmode"
@ -57,15 +58,17 @@ func (c Config) Blockchain() Blockchain {
} }
// Load attempts to load the config from the given // Load attempts to load the config from the given
// path for the given netMode. // path for the given netMode. If relativePath is not empty, relative paths in the
func Load(path string, netMode netmode.Magic) (Config, error) { // config will be updated based on the provided relative path.
func Load(path string, netMode netmode.Magic, relativePath ...string) (Config, error) {
configPath := fmt.Sprintf("%s/protocol.%s.yml", path, netMode) configPath := fmt.Sprintf("%s/protocol.%s.yml", path, netMode)
return LoadFile(configPath) return LoadFile(configPath, relativePath...)
} }
// LoadFile loads config from the provided path. It also applies backwards compatibility // LoadFile loads config from the provided path. It also applies backwards compatibility
// fixups if necessary. // fixups if necessary. If relativePath is not empty, relative paths in the config will
func LoadFile(configPath string) (Config, error) { // be updated based on the provided relative path.
func LoadFile(configPath string, relativePath ...string) (Config, error) {
if _, err := os.Stat(configPath); os.IsNotExist(err) { if _, err := os.Stat(configPath); os.IsNotExist(err) {
return Config{}, fmt.Errorf("config '%s' doesn't exist", configPath) return Config{}, fmt.Errorf("config '%s' doesn't exist", configPath)
} }
@ -89,6 +92,9 @@ func LoadFile(configPath string) (Config, error) {
if err != nil { if err != nil {
return Config{}, fmt.Errorf("failed to unmarshal config YAML: %w", err) return Config{}, fmt.Errorf("failed to unmarshal config YAML: %w", err)
} }
if len(relativePath) == 1 && relativePath[0] != "" {
updateRelativePaths(relativePath[0], &config)
}
err = config.ProtocolConfiguration.Validate() err = config.ProtocolConfiguration.Validate()
if err != nil { if err != nil {
@ -97,3 +103,20 @@ func LoadFile(configPath string) (Config, error) {
return config, nil return config, nil
} }
// updateRelativePaths updates relative paths in the config structure based on the provided relative path.
func updateRelativePaths(relativePath string, config *Config) {
updatePath := func(path *string) {
if *path != "" && !filepath.IsAbs(*path) {
*path = filepath.Join(relativePath, *path)
}
}
updatePath(&config.ApplicationConfiguration.LogPath)
updatePath(&config.ApplicationConfiguration.DBConfiguration.BoltDBOptions.FilePath)
updatePath(&config.ApplicationConfiguration.DBConfiguration.LevelDBOptions.DataDirectoryPath)
updatePath(&config.ApplicationConfiguration.Consensus.UnlockWallet.Path)
updatePath(&config.ApplicationConfiguration.P2PNotary.UnlockWallet.Path)
updatePath(&config.ApplicationConfiguration.Oracle.UnlockWallet.Path)
updatePath(&config.ApplicationConfiguration.StateRoot.UnlockWallet.Path)
}