package config import ( "fmt" "strings" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/config" "github.com/spf13/viper" ) // Config represents a group of named values structured // by tree type. // // Sub-trees are named configuration sub-sections, // leaves are named configuration values. // Names are of string type. type Config struct { v *viper.Viper opts opts defaultPath []string path []string } const ( separator = "." // EnvPrefix is a prefix of ENV variables related // to storage node configuration. EnvPrefix = "FROSTFS" // EnvSeparator is a section separator in ENV variables. EnvSeparator = "_" ) // Prm groups required parameters of the Config. type Prm struct{} // New creates a new Config instance. // // If file option is provided (WithConfigFile), // configuration values are read from it. // Otherwise, Config is a degenerate tree. func New(_ Prm, opts ...Option) *Config { v := viper.New() o := defaultOpts() for i := range opts { opts[i](o) } if o.envPrefix != "" { v.SetEnvPrefix(o.envPrefix) v.AutomaticEnv() v.SetEnvKeyReplacer(strings.NewReplacer(separator, EnvSeparator)) } if o.path != "" { v.SetConfigFile(o.path) err := v.ReadInConfig() if err != nil { panic(fmt.Errorf("failed to read config: %w", err)) } } 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, } } // Reload reads configuration path if it was provided to New. func (x *Config) Reload() error { if x.opts.path != "" { err := x.v.ReadInConfig() if err != nil { return fmt.Errorf("rereading configuration file: %w", err) } } 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 }