[TrueCloudLab#12] Support multiple configs

Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
Denis Kirillov 2023-02-03 12:46:25 +03:00 committed by Alex Vanin
parent ed983f8ad0
commit 6be8d47d92
6 changed files with 94 additions and 14 deletions

View file

@ -182,6 +182,23 @@ $ frostfs-http-gw --config your-config.yaml
See [config](./config/config.yaml) and [defaults](./docs/gate-configuration.md) for example. See [config](./config/config.yaml) and [defaults](./docs/gate-configuration.md) for example.
#### Multiple configs
You can use several config files when running application. It allows you to split configuration into parts.
For example, you can use separate yaml file for pprof and prometheus section in config (see [config examples](./config)).
You can either provide several files with repeating `--config` flag or provide path to the dir that contains all configs using `--config-dir` flag.
Also, you can combine these flags:
```shell
$ frostfs-http-gw --config ./config/config.yaml --config /your/partial/config.yaml --config-dir ./config/dir
```
**Note:** next file in `--config` flag overwrites values from the previous one.
Files from `--config-dir` directory overwrite values from `--config` files.
So the command above run `frostfs-http-gw` to listen on `0.0.0.0:8080` address (value from `./config/config.yaml`),
applies parameters from `/your/partial/config.yaml`,
enable pprof (value from `./config/dir/pprof.yaml`) and prometheus (value from `./config/dir/prometheus.yaml`).
## HTTP API provided ## HTTP API provided
This gateway intentionally provides limited feature set and doesn't try to This gateway intentionally provides limited feature set and doesn't try to

5
app.go
View file

@ -380,14 +380,15 @@ LOOP:
func (a *app) configReload() { func (a *app) configReload() {
a.log.Info("SIGHUP config reload started") a.log.Info("SIGHUP config reload started")
if !a.cfg.IsSet(cmdConfig) { if !a.cfg.IsSet(cmdConfig) && !a.cfg.IsSet(cmdConfigDir) {
a.log.Warn("failed to reload config because it's missed") a.log.Warn("failed to reload config because it's missed")
return return
} }
if err := readConfig(a.cfg); err != nil { if err := readInConfig(a.cfg); err != nil {
a.log.Warn("failed to reload config", zap.Error(err)) a.log.Warn("failed to reload config", zap.Error(err))
return return
} }
if lvl, err := getLogLevel(a.cfg); err != nil { if lvl, err := getLogLevel(a.cfg); err != nil {
a.log.Warn("log level won't be updated", zap.Error(err)) a.log.Warn("log level won't be updated", zap.Error(err))
} else { } else {

View file

@ -4,10 +4,10 @@ wallet:
passphrase: pwd # Passphrase to decrypt wallet. If you're using a wallet without a password, place '' here. passphrase: pwd # Passphrase to decrypt wallet. If you're using a wallet without a password, place '' here.
pprof: pprof:
enabled: true # Enable pprof. enabled: false # Enable pprof.
address: localhost:8083 address: localhost:8083
prometheus: prometheus:
enabled: true # Enable metrics. enabled: false # Enable metrics.
address: localhost:8084 address: localhost:8084
logger: logger:

3
config/dir/pprof.yaml Normal file
View file

@ -0,0 +1,3 @@
pprof:
enabled: true
address: localhost:8083

View file

@ -0,0 +1,3 @@
prometheus:
enabled: true
address: localhost:8084

View file

@ -3,6 +3,7 @@ package main
import ( import (
"fmt" "fmt"
"os" "os"
"path"
"runtime" "runtime"
"sort" "sort"
"strconv" "strconv"
@ -84,6 +85,7 @@ const (
cmdWallet = "wallet" cmdWallet = "wallet"
cmdAddress = "address" cmdAddress = "address"
cmdConfig = "config" cmdConfig = "config"
cmdConfigDir = "config-dir"
cmdListenAddress = "listen_address" cmdListenAddress = "listen_address"
) )
@ -114,7 +116,8 @@ func settings() *viper.Viper {
flags.StringP(cmdWallet, "w", "", `path to the wallet`) flags.StringP(cmdWallet, "w", "", `path to the wallet`)
flags.String(cmdAddress, "", `address of wallet account`) flags.String(cmdAddress, "", `address of wallet account`)
flags.String(cmdConfig, "", "config path") flags.StringArray(cmdConfig, nil, "config paths")
flags.String(cmdConfigDir, "", "config dir path")
flags.Duration(cfgConTimeout, defaultConnectTimeout, "gRPC connect timeout") flags.Duration(cfgConTimeout, defaultConnectTimeout, "gRPC connect timeout")
flags.Duration(cfgStreamTimeout, defaultStreamTimeout, "gRPC individual message timeout") flags.Duration(cfgStreamTimeout, defaultStreamTimeout, "gRPC individual message timeout")
flags.Duration(cfgReqTimeout, defaultRequestTimeout, "gRPC request timeout") flags.Duration(cfgReqTimeout, defaultRequestTimeout, "gRPC request timeout")
@ -233,10 +236,8 @@ func settings() *viper.Viper {
os.Exit(0) os.Exit(0)
} }
if v.IsSet(cmdConfig) { if err := readInConfig(v); err != nil {
if err := readConfig(v); err != nil { panic(err)
panic(err)
}
} }
if peers != nil && len(*peers) > 0 { if peers != nil && len(*peers) > 0 {
@ -250,17 +251,72 @@ func settings() *viper.Viper {
return v return v
} }
func readConfig(v *viper.Viper) error { func readInConfig(v *viper.Viper) error {
cfgFileName := v.GetString(cmdConfig) if v.IsSet(cmdConfig) {
cfgFile, err := os.Open(cfgFileName) if err := readConfig(v); err != nil {
return err
}
}
if v.IsSet(cmdConfigDir) {
if err := readConfigDir(v); err != nil {
return err
}
}
return nil
}
func readConfigDir(v *viper.Viper) error {
cfgSubConfigDir := v.GetString(cmdConfigDir)
entries, err := os.ReadDir(cfgSubConfigDir)
if err != nil { if err != nil {
return err return err
} }
if err = v.ReadConfig(cfgFile); err != nil {
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(cfgSubConfigDir, entry.Name())); err != nil {
return err
}
}
return nil
}
func readConfig(v *viper.Viper) error {
for _, fileName := range v.GetStringSlice(cmdConfig) {
if err := mergeConfig(v, fileName); err != nil {
return err
}
}
return nil
}
func mergeConfig(v *viper.Viper, fileName string) error {
cfgFile, err := os.Open(fileName)
if err != nil {
return err return err
} }
return cfgFile.Close() defer func() {
if errClose := cfgFile.Close(); errClose != nil {
panic(errClose)
}
}()
if err = v.MergeConfig(cfgFile); err != nil {
return err
}
return nil
} }
// newLogger constructs a zap.Logger instance for current application. // newLogger constructs a zap.Logger instance for current application.