[#44] node: Support multiple configs

Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
Denis Kirillov 2023-02-06 16:53:16 +03:00 committed by fyrchik
parent 337049b2ce
commit 87e69b9349
5 changed files with 141 additions and 2 deletions

View file

@ -5,6 +5,7 @@ import (
"strings" "strings"
"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/internal" "github.com/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/internal"
"github.com/TrueCloudLab/frostfs-node/pkg/util/config"
"github.com/spf13/viper" "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{ return &Config{
v: v, v: v,
opts: *o, 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 return nil
} }

View file

@ -2,6 +2,7 @@ package config
type opts struct { type opts struct {
path string path string
configDir string
} }
func defaultOpts() *opts { func defaultOpts() *opts {
@ -18,3 +19,11 @@ func WithConfigFile(path string) Option {
o.path = path 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
}
}

View file

@ -34,6 +34,7 @@ func fatalOnErrDetails(details string, err error) {
func main() { func main() {
configFile := flag.String("config", "", "path to config") configFile := flag.String("config", "", "path to config")
configDir := flag.String("config-dir", "", "path to config directory")
versionFlag := flag.Bool("version", false, "frostfs node version") versionFlag := flag.Bool("version", false, "frostfs node version")
dryRunFlag := flag.Bool("check", false, "validate configuration and exit") dryRunFlag := flag.Bool("check", false, "validate configuration and exit")
flag.Parse() flag.Parse()
@ -44,7 +45,7 @@ func main() {
os.Exit(SuccessReturnCode) 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) err := validateConfig(appCfg)
fatalOnErr(err) fatalOnErr(err)

View file

@ -16,6 +16,67 @@ It is not recommended transferring these configs for real application launches.
- CLI - CLI
- YAML: `cli.yaml` - 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 ## Environment variables
- Storage node: `node.env` - Storage node: `node.env`

55
pkg/util/config/dir.go Normal file
View file

@ -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
}