From f457d507734acc1144a8a55c2205f264177f5049 Mon Sep 17 00:00:00 2001 From: Ekaterina Pavlova Date: Thu, 23 Nov 2023 21:41:50 +0400 Subject: [PATCH] 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 --- cli/options/options.go | 16 +++++++++++++--- cli/server/server.go | 2 +- cli/server/server_test.go | 12 ++++++++++++ cli/vm/cli_test.go | 4 +++- cli/vm/vm.go | 2 +- pkg/config/config.go | 33 ++++++++++++++++++++++++++++----- 6 files changed, 58 insertions(+), 11 deletions(-) diff --git a/cli/options/options.go b/cli/options/options.go index 09bb08572..adafb6166 100644 --- a/cli/options/options.go +++ b/cli/options/options.go @@ -73,6 +73,13 @@ var ConfigFile = cli.StringFlag{ 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. var Debug = cli.BoolFlag{ 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 // returns an appropriate config. 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 { - return config.LoadFile(configFile) + return config.LoadFile(configFile, relativePath) } var configPath = "./config" if argCp := ctx.String("config-path"); argCp != "" { configPath = argCp } - return config.Load(configPath, GetNetwork(ctx)) + return config.Load(configPath, GetNetwork(ctx), relativePath) } var ( diff --git a/cli/server/server.go b/cli/server/server.go index 7e705d433..f2670d599 100644 --- a/cli/server/server.go +++ b/cli/server/server.go @@ -34,7 +34,7 @@ import ( // NewCommands returns 'node' 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...) var cfgWithCountFlags = make([]cli.Flag, len(cfgFlags)) copy(cfgWithCountFlags, cfgFlags) diff --git a/cli/server/server_test.go b/cli/server/server_test.go index 2757ae6bb..d19278d91 100644 --- a/cli/server/server_test.go +++ b/cli/server/server_test.go @@ -49,6 +49,18 @@ func TestGetConfigFromContext(t *testing.T) { require.NoError(t, err) 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) { diff --git a/cli/vm/cli_test.go b/cli/vm/cli_test.go index 8e7d22887..24bdbe7cc 100644 --- a/cli/vm/cli_test.go +++ b/cli/vm/cli_test.go @@ -95,8 +95,10 @@ func newTestVMCLIWithLogoAndCustomConfig(t *testing.T, printLogo bool, cfg *conf if cfg == nil { configPath := "../../config/protocol.unit_testnet.single.yml" var err error - c, err = config.LoadFile(configPath) + c, err = config.LoadFile(configPath, "../../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 } else { c = *cfg diff --git a/cli/vm/vm.go b/cli/vm/vm.go index 89dd351bb..e9e1e2a58 100644 --- a/cli/vm/vm.go +++ b/cli/vm/vm.go @@ -13,7 +13,7 @@ import ( // NewCommands returns 'vm' 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...) return []cli.Command{{ Name: "vm", diff --git a/pkg/config/config.go b/pkg/config/config.go index d30a30e61..06b3d3754 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "os" + "path/filepath" "time" "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 -// path for the given netMode. -func Load(path string, netMode netmode.Magic) (Config, error) { +// path for the given netMode. If relativePath is not empty, relative paths in the +// 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) - return LoadFile(configPath) + return LoadFile(configPath, relativePath...) } // LoadFile loads config from the provided path. It also applies backwards compatibility -// fixups if necessary. -func LoadFile(configPath string) (Config, error) { +// fixups if necessary. If relativePath is not empty, relative paths in the config will +// be updated based on the provided relative path. +func LoadFile(configPath string, relativePath ...string) (Config, error) { if _, err := os.Stat(configPath); os.IsNotExist(err) { return Config{}, fmt.Errorf("config '%s' doesn't exist", configPath) } @@ -89,6 +92,9 @@ func LoadFile(configPath string) (Config, error) { if err != nil { 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() if err != nil { @@ -97,3 +103,20 @@ func LoadFile(configPath string) (Config, error) { 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) +}