add viperconfig
This commit is contained in:
parent
6b39120f6d
commit
f9fc3be716
2 changed files with 134 additions and 0 deletions
108
viperconfig/configuration_manager.go
Normal file
108
viperconfig/configuration_manager.go
Normal file
|
@ -0,0 +1,108 @@
|
|||
package configuration_manager
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// ConfigurationManager[T any] manages Viper for the specified configuration struct
|
||||
type ConfigurationManager[T any] struct {
|
||||
name string
|
||||
config T
|
||||
defaults []byte
|
||||
lock *sync.RWMutex
|
||||
vc *viper.Viper
|
||||
}
|
||||
|
||||
// New[T any] creates a new configuration manager for the specified configuration struct
|
||||
func New[T any](name string, defaults []byte) *ConfigurationManager[T] {
|
||||
// Initialize
|
||||
p := &ConfigurationManager[T]{
|
||||
name: name,
|
||||
defaults: defaults,
|
||||
lock: &sync.RWMutex{},
|
||||
vc: viper.New(),
|
||||
}
|
||||
|
||||
// Load defaults
|
||||
vd := viper.New()
|
||||
buf := bytes.NewBuffer(p.defaults)
|
||||
if err := vd.ReadConfig(buf); err != nil {
|
||||
slog.Error("Error loading default configuration", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Create Viper instance
|
||||
p.vc = viper.New()
|
||||
p.vc.AddConfigPath("/secrets")
|
||||
p.vc.AddConfigPath("/etc/joco")
|
||||
p.vc.AddConfigPath("/etc")
|
||||
p.vc.AddConfigPath(".")
|
||||
p.vc.SetConfigName(p.name)
|
||||
p.vc.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
p.vc.SetEnvPrefix(p.name)
|
||||
p.vc.AutomaticEnv()
|
||||
p.vc.OnConfigChange(p.onConfigChange)
|
||||
|
||||
// Set defaults from embedded configuration file
|
||||
for k, v := range vd.AllSettings() {
|
||||
p.vc.SetDefault(k, v)
|
||||
}
|
||||
|
||||
// Load the configuration (ignore file not found)
|
||||
if err := p.load(); err != nil {
|
||||
slog.Error("Error loading configuration", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
p.vc.BindPFlags(pflag.CommandLine)
|
||||
|
||||
// Monitor changes to the configuration
|
||||
p.vc.WatchConfig()
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *ConfigurationManager[T]) Get() T {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
|
||||
return p.config
|
||||
}
|
||||
|
||||
func (p *ConfigurationManager[T]) load() error {
|
||||
if err := p.vc.ReadInConfig(); err != nil {
|
||||
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var cfg T
|
||||
if err := viper.Unmarshal(&cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.lock.Lock()
|
||||
p.config = cfg
|
||||
p.lock.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ConfigurationManager[T]) onConfigChange(in fsnotify.Event) {
|
||||
if err := p.load(); err != nil {
|
||||
slog.Error("Error re-loading configuration", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
// SaveToFile saves the embedded default configuration to the specified file
|
||||
func (p *ConfigurationManager[T]) SaveToFile(path string) error {
|
||||
return os.WriteFile(path, p.defaults, os.ModeExclusive)
|
||||
}
|
26
viperconfig/context.go
Normal file
26
viperconfig/context.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package configuration_manager
|
||||
|
||||
import "context"
|
||||
|
||||
type contextKey struct{}
|
||||
|
||||
var (
|
||||
ConfigurationKey = contextKey{}
|
||||
)
|
||||
|
||||
// GetConfigFromContext[T any] returns the current configuration extracted from the passed context
|
||||
func GetConfigFromContext[T any](ctx context.Context) T {
|
||||
cm := GetConfigMgrFromContext[T](ctx)
|
||||
|
||||
return cm.Get()
|
||||
}
|
||||
|
||||
// GetConfigMgrFromContext[T any] returns the configuration manager stored in the passed context
|
||||
func GetConfigMgrFromContext[T any](ctx context.Context) ConfigurationManager[T] {
|
||||
return ctx.Value(ConfigurationKey).(ConfigurationManager[T])
|
||||
}
|
||||
|
||||
// SetConfigMgrOnContext[T any] stores the passed configuration manager on the passed context
|
||||
func SetConfigMgrOnContext[T any](ctx context.Context, value T) context.Context {
|
||||
return context.WithValue(ctx, ConfigurationKey, value)
|
||||
}
|
Loading…
Reference in a new issue