[#125] cmd: Refactor internal/common/viper
Add `opts.WithViper`, set opts struct as private. Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
This commit is contained in:
parent
e61aec4a7d
commit
ef222e2487
7 changed files with 80 additions and 53 deletions
|
@ -14,7 +14,9 @@ import (
|
|||
type Config struct {
|
||||
v *viper.Viper
|
||||
|
||||
opts configViper.Opts
|
||||
configFile string
|
||||
configDir string
|
||||
envPrefix string
|
||||
|
||||
defaultPath []string
|
||||
path []string
|
||||
|
@ -28,23 +30,31 @@ const (
|
|||
|
||||
// New creates a new Config instance.
|
||||
//
|
||||
// If file option is provided (WithConfigFile),
|
||||
// If file option is provided,
|
||||
// configuration values are read from it.
|
||||
// Otherwise, Config is a degenerate tree.
|
||||
func New(opts ...configViper.Option) *Config {
|
||||
v, o, err := configViper.CreateViper(opts...)
|
||||
func New(configFile, configDir, envPrefix string) *Config {
|
||||
v, err := configViper.CreateViper(
|
||||
configViper.WithConfigFile(configFile),
|
||||
configViper.WithConfigDir(configDir),
|
||||
configViper.WithEnvPrefix(envPrefix))
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &Config{
|
||||
v: v,
|
||||
opts: *o,
|
||||
v: v,
|
||||
configFile: configFile,
|
||||
configDir: configDir,
|
||||
envPrefix: envPrefix,
|
||||
}
|
||||
}
|
||||
|
||||
// Reload reads configuration path if it was provided to New.
|
||||
func (x *Config) Reload() error {
|
||||
return configViper.ReloadViper(x.v, x.opts)
|
||||
return configViper.ReloadViper(
|
||||
configViper.WithViper(x.v), configViper.WithConfigFile(x.configFile),
|
||||
configViper.WithConfigDir(x.configDir),
|
||||
configViper.WithEnvPrefix(x.envPrefix))
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"path"
|
||||
"testing"
|
||||
|
||||
configViper "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common/config"
|
||||
"github.com/spf13/cast"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -19,7 +18,7 @@ func TestConfigDir(t *testing.T) {
|
|||
require.NoError(t, os.WriteFile(cfgFileName0, []byte(`{"storage":{"shard_pool_size":15}}`), 0777))
|
||||
require.NoError(t, os.WriteFile(cfgFileName1, []byte("logger:\n level: debug"), 0777))
|
||||
|
||||
c := New(configViper.WithConfigDir(dir))
|
||||
c := New("", dir, "")
|
||||
require.Equal(t, "debug", cast.ToString(c.Sub("logger").Value("level")))
|
||||
require.EqualValues(t, 15, cast.ToUint32(c.Sub("storage").Value("shard_pool_size")))
|
||||
}
|
||||
|
|
|
@ -7,22 +7,19 @@ import (
|
|||
"testing"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config"
|
||||
configViper "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common/config"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func fromFile(path string) *config.Config {
|
||||
os.Clearenv() // ENVs have priority over config files, so we do this in tests
|
||||
|
||||
return config.New(
|
||||
configViper.WithConfigFile(path),
|
||||
)
|
||||
return config.New(path, "", "")
|
||||
}
|
||||
|
||||
func fromEnvFile(t testing.TB, path string) *config.Config {
|
||||
loadEnv(t, path) // github.com/joho/godotenv can do that as well
|
||||
|
||||
return config.New(configViper.WithEnvPrefix(config.EnvPrefix))
|
||||
return config.New("", "", config.EnvPrefix)
|
||||
}
|
||||
|
||||
func forEachFile(paths []string, f func(*config.Config)) {
|
||||
|
@ -48,7 +45,7 @@ func ForEnvFileType(t testing.TB, pref string, f func(*config.Config)) {
|
|||
|
||||
// EmptyConfig returns config without any values and sections.
|
||||
func EmptyConfig() *config.Config {
|
||||
return config.New(configViper.WithEnvPrefix(config.EnvPrefix))
|
||||
return config.New("", "", config.EnvPrefix)
|
||||
}
|
||||
|
||||
// loadEnv reads .env file, parses `X=Y` records and sets OS ENVs.
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"os"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config"
|
||||
configViper "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common/config"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/misc"
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/control"
|
||||
|
@ -47,9 +46,7 @@ func main() {
|
|||
os.Exit(SuccessReturnCode)
|
||||
}
|
||||
|
||||
appCfg := config.New(
|
||||
configViper.WithConfigFile(*configFile), configViper.WithConfigDir(*configDir),
|
||||
configViper.WithEnvPrefix(config.EnvPrefix))
|
||||
appCfg := config.New(*configFile, *configDir, config.EnvPrefix)
|
||||
|
||||
err := validateConfig(appCfg)
|
||||
fatalOnErr(err)
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
|
||||
"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config"
|
||||
configtest "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-node/config/test"
|
||||
configViper "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common/config"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -27,13 +26,13 @@ func TestValidate(t *testing.T) {
|
|||
t.Run("mainnet", func(t *testing.T) {
|
||||
os.Clearenv() // ENVs have priority over config files, so we do this in tests
|
||||
p := filepath.Join(exampleConfigPrefix, "mainnet/config.yml")
|
||||
c := config.New(configViper.WithConfigFile(p))
|
||||
c := config.New(p, "", config.EnvPrefix)
|
||||
require.NoError(t, validateConfig(c))
|
||||
})
|
||||
t.Run("testnet", func(t *testing.T) {
|
||||
os.Clearenv() // ENVs have priority over config files, so we do this in tests
|
||||
p := filepath.Join(exampleConfigPrefix, "testnet/config.yml")
|
||||
c := config.New(configViper.WithConfigFile(p))
|
||||
c := config.New(p, "", config.EnvPrefix)
|
||||
require.NoError(t, validateConfig(c))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,38 +1,49 @@
|
|||
package config
|
||||
|
||||
type Opts struct {
|
||||
Path string
|
||||
ConfigDir string
|
||||
EnvPrefix string
|
||||
import "github.com/spf13/viper"
|
||||
|
||||
type opts struct {
|
||||
path string
|
||||
configDir string
|
||||
envPrefix string
|
||||
v *viper.Viper
|
||||
}
|
||||
|
||||
func DefaultOpts() *Opts {
|
||||
return new(Opts)
|
||||
func defaultOpts() *opts {
|
||||
return new(opts)
|
||||
}
|
||||
|
||||
// Option allows to set an optional parameter of the Config.
|
||||
type Option func(*Opts)
|
||||
type Option func(*opts)
|
||||
|
||||
// WithConfigFile returns an option to set the system path
|
||||
// to the configuration file.
|
||||
func WithConfigFile(path string) Option {
|
||||
return func(o *Opts) {
|
||||
o.Path = path
|
||||
return func(o *opts) {
|
||||
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
|
||||
return func(o *opts) {
|
||||
o.configDir = path
|
||||
}
|
||||
}
|
||||
|
||||
// WithEnvPrefix returns an option to defines
|
||||
// a prefix that ENVIRONMENT variables will use.
|
||||
func WithEnvPrefix(envPrefix string) Option {
|
||||
return func(o *Opts) {
|
||||
o.EnvPrefix = envPrefix
|
||||
return func(o *opts) {
|
||||
o.envPrefix = envPrefix
|
||||
}
|
||||
}
|
||||
|
||||
// WithViper returns an option to defines
|
||||
// a predefined viper.Viper.
|
||||
func WithViper(v *viper.Viper) Option {
|
||||
return func(o *opts) {
|
||||
o.v = v
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,48 +15,62 @@ const (
|
|||
EnvSeparator = "_"
|
||||
)
|
||||
|
||||
func CreateViper(opts ...Option) (*viper.Viper, *Opts, error) {
|
||||
v := viper.New()
|
||||
|
||||
o := DefaultOpts()
|
||||
func CreateViper(opts ...Option) (*viper.Viper, error) {
|
||||
o := defaultOpts()
|
||||
for i := range opts {
|
||||
opts[i](o)
|
||||
}
|
||||
|
||||
if o.EnvPrefix != "" {
|
||||
v.SetEnvPrefix(o.EnvPrefix)
|
||||
var v *viper.Viper
|
||||
if o.v != nil {
|
||||
v = o.v
|
||||
} else {
|
||||
v = viper.New()
|
||||
}
|
||||
|
||||
if o.envPrefix != "" {
|
||||
v.SetEnvPrefix(o.envPrefix)
|
||||
v.AutomaticEnv()
|
||||
v.SetEnvKeyReplacer(strings.NewReplacer(Separator, EnvSeparator))
|
||||
}
|
||||
|
||||
if o.Path != "" {
|
||||
v.SetConfigFile(o.Path)
|
||||
if o.path != "" {
|
||||
v.SetConfigFile(o.path)
|
||||
|
||||
err := v.ReadInConfig()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to read config: %w", err)
|
||||
return nil, fmt.Errorf("failed to read config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if o.ConfigDir != "" {
|
||||
if err := config.ReadConfigDir(v, o.ConfigDir); err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to read config dir: %w", err)
|
||||
if o.configDir != "" {
|
||||
if err := config.ReadConfigDir(v, o.configDir); err != nil {
|
||||
return nil, fmt.Errorf("failed to read config dir: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return v, o, nil
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func ReloadViper(v *viper.Viper, o Opts) error {
|
||||
if o.Path != "" {
|
||||
err := v.ReadInConfig()
|
||||
func ReloadViper(opts ...Option) error {
|
||||
o := defaultOpts()
|
||||
for i := range opts {
|
||||
opts[i](o)
|
||||
}
|
||||
|
||||
if o.v == nil {
|
||||
return fmt.Errorf("provide viper in opts")
|
||||
}
|
||||
|
||||
if o.path != "" {
|
||||
err := o.v.ReadInConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("rereading configuration file: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if o.ConfigDir != "" {
|
||||
if err := config.ReadConfigDir(v, o.ConfigDir); err != nil {
|
||||
if o.configDir != "" {
|
||||
if err := config.ReadConfigDir(o.v, o.configDir); err != nil {
|
||||
return fmt.Errorf("rereading configuration dir: %w", err)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue