From f25521aeb9394160a5ec1476959111dfabd68598 Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Mon, 6 Feb 2023 16:53:16 +0300 Subject: [PATCH 1/5] [#44] node: Support multiple configs Signed-off-by: Denis Kirillov --- cmd/frostfs-node/config/config.go | 13 +++++++ cmd/frostfs-node/config/opts.go | 11 +++++- cmd/frostfs-node/main.go | 3 +- config/example/README.md | 61 +++++++++++++++++++++++++++++++ pkg/util/config/dir.go | 55 ++++++++++++++++++++++++++++ 5 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 pkg/util/config/dir.go diff --git a/cmd/frostfs-node/config/config.go b/cmd/frostfs-node/config/config.go index 6f06a374e..8b6069761 100644 --- a/cmd/frostfs-node/config/config.go +++ b/cmd/frostfs-node/config/config.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/internal" + "github.com/TrueCloudLab/frostfs-node/pkg/util/config" "github.com/spf13/viper" ) @@ -54,6 +55,12 @@ func New(_ Prm, opts ...Option) *Config { } } + if o.configDir != "" { + if err := config.ReadConfigDir(v, o.configDir); err != nil { + panic(fmt.Errorf("failed to read config dir: %w", err)) + } + } + return &Config{ v: v, opts: *o, @@ -69,5 +76,11 @@ func (x *Config) Reload() error { } } + if x.opts.configDir != "" { + if err := config.ReadConfigDir(x.v, x.opts.configDir); err != nil { + return fmt.Errorf("rereading configuration dir: %w", err) + } + } + return nil } diff --git a/cmd/frostfs-node/config/opts.go b/cmd/frostfs-node/config/opts.go index f7f7685f7..009c6efef 100644 --- a/cmd/frostfs-node/config/opts.go +++ b/cmd/frostfs-node/config/opts.go @@ -1,7 +1,8 @@ package config type opts struct { - path string + path string + configDir string } func defaultOpts() *opts { @@ -18,3 +19,11 @@ func WithConfigFile(path string) Option { o.path = path } } + +// WithConfigDir returns an option to set the system path +// to the directory with configuration files. +func WithConfigDir(path string) Option { + return func(o *opts) { + o.configDir = path + } +} diff --git a/cmd/frostfs-node/main.go b/cmd/frostfs-node/main.go index d6345d3b8..11ee7a97b 100644 --- a/cmd/frostfs-node/main.go +++ b/cmd/frostfs-node/main.go @@ -34,6 +34,7 @@ func fatalOnErrDetails(details string, err error) { func main() { configFile := flag.String("config", "", "path to config") + configDir := flag.String("config-dir", "", "path to config directory") versionFlag := flag.Bool("version", false, "frostfs node version") dryRunFlag := flag.Bool("check", false, "validate configuration and exit") flag.Parse() @@ -44,7 +45,7 @@ func main() { os.Exit(SuccessReturnCode) } - appCfg := config.New(config.Prm{}, config.WithConfigFile(*configFile)) + appCfg := config.New(config.Prm{}, config.WithConfigFile(*configFile), config.WithConfigDir(*configDir)) err := validateConfig(appCfg) fatalOnErr(err) diff --git a/config/example/README.md b/config/example/README.md index 48b614bae..cbca83731 100644 --- a/config/example/README.md +++ b/config/example/README.md @@ -16,6 +16,67 @@ It is not recommended transferring these configs for real application launches. - CLI - YAML: `cli.yaml` +### Multiple configs + +You can split your configuration to several files. +For example, you can use separate yaml file for each shard or each service (pprof, prometheus). +You must use `--config-dir` flag to process several configs: + +```shell +$ ./bin/frotsfs-node --config ./config/example/node.yaml --config-dir ./dir/with/additional/configs +``` + +When the `--config-dir` flag set, the application: +* reads all `*.y[a]ml` files from provided directory, +* use Viper's [MergeConfig](https://pkg.go.dev/github.com/spf13/viper#MergeConfig) functionality to produce the final configuration, +* files are being processing in alphanumerical order so that `01.yaml` may be extended with contents of `02.yaml`. + +So if we have the following files: +```yaml +# 00.yaml +logger: + level: debug +pprof: + enabled: true + address: localhost:6060 +prometheus: + enabled: true + address: localhost:9090 +``` + +```yaml +# dir/01.yaml +logger: + level: info +pprof: + enabled: false +``` + +```yaml +# dir/02.yaml +logger: + level: warn +prometheus: + address: localhost:9091 +``` + +and provide the following flags: +```shell +$ ./bin/frotsfs-node --config 00.yaml --config-dir dir +``` + +result config will be: +```yaml +logger: + level: warn +pprof: + enabled: false + address: localhost:6060 +prometheus: + enabled: true + address: localhost:9091 +``` + ## Environment variables - Storage node: `node.env` diff --git a/pkg/util/config/dir.go b/pkg/util/config/dir.go new file mode 100644 index 000000000..a74992d19 --- /dev/null +++ b/pkg/util/config/dir.go @@ -0,0 +1,55 @@ +package config + +import ( + "os" + "path" + + "github.com/spf13/viper" +) + +// ReadConfigDir reads all config files from provided directory in alphabetical order +// and merge its content with current viper configuration. +func ReadConfigDir(v *viper.Viper, configDir string) error { + entries, err := os.ReadDir(configDir) + if err != nil { + return err + } + + for _, entry := range entries { + if entry.IsDir() { + continue + } + ext := path.Ext(entry.Name()) + if ext != ".yaml" && ext != ".yml" { + continue + } + + if err = mergeConfig(v, path.Join(configDir, entry.Name())); err != nil { + return err + } + } + + return nil +} + +// mergeConfig reads config file and merge its content with current viper. +func mergeConfig(v *viper.Viper, fileName string) (err error) { + var cfgFile *os.File + cfgFile, err = os.Open(fileName) + if err != nil { + return err + } + + defer func() { + errClose := cfgFile.Close() + if err == nil { + err = errClose + } + }() + + if err = v.MergeConfig(cfgFile); err != nil { + return err + } + + return nil +} -- 2.45.2 From 891f884af1840fd7326c7a79fe70ca0673db3e90 Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Mon, 6 Feb 2023 17:54:06 +0300 Subject: [PATCH 2/5] [#44] ir: Support multiple configs Signed-off-by: Denis Kirillov --- cmd/frostfs-ir/defaults.go | 11 +++++++++-- cmd/frostfs-ir/main.go | 3 ++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/cmd/frostfs-ir/defaults.go b/cmd/frostfs-ir/defaults.go index f2f416e00..1e09c75d8 100644 --- a/cmd/frostfs-ir/defaults.go +++ b/cmd/frostfs-ir/defaults.go @@ -4,10 +4,11 @@ import ( "strings" "time" + "github.com/TrueCloudLab/frostfs-node/pkg/util/config" "github.com/spf13/viper" ) -func newConfig(path string) (*viper.Viper, error) { +func newConfig(path, directory string) (*viper.Viper, error) { const envPrefix = "FROSTFS_IR" var ( @@ -28,7 +29,13 @@ func newConfig(path string) (*viper.Viper, error) { } else { v.SetConfigType("yml") } - err = v.ReadInConfig() + if err = v.ReadInConfig(); err != nil { + return v, err + } + } + + if directory != "" { + err = config.ReadConfigDir(v, directory) } return v, err diff --git a/cmd/frostfs-ir/main.go b/cmd/frostfs-ir/main.go index f2afd7b8a..2094265fa 100644 --- a/cmd/frostfs-ir/main.go +++ b/cmd/frostfs-ir/main.go @@ -35,6 +35,7 @@ func exitErr(err error) { func main() { configFile := flag.String("config", "", "path to config") + configDir := flag.String("config-dir", "", "path to config directory") versionFlag := flag.Bool("version", false, "frostfs-ir node version") flag.Parse() @@ -44,7 +45,7 @@ func main() { os.Exit(SuccessReturnCode) } - cfg, err := newConfig(*configFile) + cfg, err := newConfig(*configFile, *configDir) exitErr(err) var logPrm logger.Prm -- 2.45.2 From 7ee00a3452459670e74ff1ceff249632f4070de6 Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Mon, 6 Feb 2023 17:54:20 +0300 Subject: [PATCH 3/5] [#44] cli: Support multiple configs Signed-off-by: Denis Kirillov --- cmd/frostfs-cli/modules/root.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cmd/frostfs-cli/modules/root.go b/cmd/frostfs-cli/modules/root.go index a981f6971..4bc317ba0 100644 --- a/cmd/frostfs-cli/modules/root.go +++ b/cmd/frostfs-cli/modules/root.go @@ -19,6 +19,7 @@ import ( utilCli "github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/modules/util" commonCmd "github.com/TrueCloudLab/frostfs-node/cmd/internal/common" "github.com/TrueCloudLab/frostfs-node/misc" + "github.com/TrueCloudLab/frostfs-node/pkg/util/config" "github.com/TrueCloudLab/frostfs-node/pkg/util/gendoc" "github.com/mitchellh/go-homedir" "github.com/spf13/cobra" @@ -32,6 +33,7 @@ const ( // Global scope flags. var ( cfgFile string + cfgDir string ) // rootCmd represents the base command when called without any subcommands. @@ -64,6 +66,7 @@ func init() { // Cobra supports persistent flags, which, if defined here, // will be global for your application. rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "Config file (default is $HOME/.config/frostfs-cli/config.yaml)") + rootCmd.PersistentFlags().StringVar(&cfgDir, "config-dir", "", "Config directory") rootCmd.PersistentFlags().BoolP(commonflags.Verbose, commonflags.VerboseShorthand, false, commonflags.VerboseUsage) @@ -121,4 +124,10 @@ func initConfig() { if err := viper.ReadInConfig(); err == nil { common.PrintVerbose(rootCmd, "Using config file: %s", viper.ConfigFileUsed()) } + + if cfgDir != "" { + if err := config.ReadConfigDir(viper.GetViper(), cfgDir); err != nil { + commonCmd.ExitOnErr(rootCmd, "failed to read config dir: %w", err) + } + } } -- 2.45.2 From e5c83b2805c412feae7fa21f4d71449d19ea8064 Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Mon, 6 Feb 2023 17:54:49 +0300 Subject: [PATCH 4/5] [#44] adm: Support multiple configs Signed-off-by: Denis Kirillov --- cmd/frostfs-adm/internal/commonflags/flags.go | 3 +++ cmd/frostfs-adm/internal/modules/root.go | 21 +++++++++++++++---- config/example/README.md | 3 ++- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/cmd/frostfs-adm/internal/commonflags/flags.go b/cmd/frostfs-adm/internal/commonflags/flags.go index b31b13255..2f1ae4cc5 100644 --- a/cmd/frostfs-adm/internal/commonflags/flags.go +++ b/cmd/frostfs-adm/internal/commonflags/flags.go @@ -5,6 +5,9 @@ const ( ConfigFlagShorthand = "c" ConfigFlagUsage = "Config file" + ConfigDirFlag = "config-dir" + ConfigDirFlagUsage = "Config directory" + Verbose = "verbose" VerboseShorthand = "v" VerboseUsage = "Verbose output" diff --git a/cmd/frostfs-adm/internal/modules/root.go b/cmd/frostfs-adm/internal/modules/root.go index 4ac9a9b20..67584e921 100644 --- a/cmd/frostfs-adm/internal/modules/root.go +++ b/cmd/frostfs-adm/internal/modules/root.go @@ -9,6 +9,7 @@ import ( "github.com/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/storagecfg" "github.com/TrueCloudLab/frostfs-node/misc" "github.com/TrueCloudLab/frostfs-node/pkg/util/autocomplete" + utilConfig "github.com/TrueCloudLab/frostfs-node/pkg/util/config" "github.com/TrueCloudLab/frostfs-node/pkg/util/gendoc" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -34,6 +35,7 @@ func init() { rootCmd.SetOut(os.Stdout) rootCmd.PersistentFlags().StringP(commonflags.ConfigFlag, commonflags.ConfigFlagShorthand, "", commonflags.ConfigFlagUsage) + rootCmd.PersistentFlags().String(commonflags.ConfigDirFlag, "", commonflags.ConfigDirFlagUsage) rootCmd.PersistentFlags().BoolP(commonflags.Verbose, commonflags.VerboseShorthand, false, commonflags.VerboseUsage) _ = viper.BindPFlag(commonflags.Verbose, rootCmd.PersistentFlags().Lookup(commonflags.Verbose)) rootCmd.Flags().Bool("version", false, "Application version") @@ -62,11 +64,22 @@ func entryPoint(cmd *cobra.Command, args []string) error { func initConfig(cmd *cobra.Command) { configFile, err := cmd.Flags().GetString(commonflags.ConfigFlag) - if err != nil || configFile == "" { + if err != nil { return } - viper.SetConfigType("yml") - viper.SetConfigFile(configFile) - _ = viper.ReadInConfig() // if config file is set but unavailable, ignore it + if configFile != "" { + viper.SetConfigType("yml") + viper.SetConfigFile(configFile) + _ = viper.ReadInConfig() // if config file is set but unavailable, ignore it + } + + configDir, err := cmd.Flags().GetString(commonflags.ConfigDirFlag) + if err != nil { + return + } + + if configDir != "" { + _ = utilConfig.ReadConfigDir(viper.GetViper(), configDir) // if config files cannot be read, ignore it + } } diff --git a/config/example/README.md b/config/example/README.md index cbca83731..afb594b14 100644 --- a/config/example/README.md +++ b/config/example/README.md @@ -29,7 +29,8 @@ $ ./bin/frotsfs-node --config ./config/example/node.yaml --config-dir ./dir/with When the `--config-dir` flag set, the application: * reads all `*.y[a]ml` files from provided directory, * use Viper's [MergeConfig](https://pkg.go.dev/github.com/spf13/viper#MergeConfig) functionality to produce the final configuration, -* files are being processing in alphanumerical order so that `01.yaml` may be extended with contents of `02.yaml`. +* files are being processing in alphanumerical order so that `01.yaml` may be extended with contents of `02.yaml`, so +if a field is specified in multiple files, the latest occurrence takes effect. So if we have the following files: ```yaml -- 2.45.2 From fc1f1e1826fb212d8b74a4340218deb266c013ca Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Tue, 7 Feb 2023 09:44:43 +0300 Subject: [PATCH 5/5] [#44] Update Changelog Signed-off-by: Denis Kirillov --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b448e265..04542c452 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Changelog for FrostFS Node - Add command `frostfs-adm morph netmap-candidates` (#1889) - `object.delete.tombstone_lifetime` config parameter to set tombstone lifetime in the DELETE service (#2246) - Reload config for pprof and metrics on SIGHUP in `neofs-node` (#1868) +- Multiple configs support (#44) ### Changed - Change `frostfs_node_engine_container_size` to counting sizes of logical objects -- 2.45.2