pomerium/config/config_source.go
Caleb Doxsey d3a7ee38be
options refactor (#1088)
* refactor config loading

* wip

* move autocert to its own config source

* refactor options updaters

* fix stuttering

* fix autocert validate check
2020-07-16 14:30:15 -06:00

103 lines
2.4 KiB
Go

package config
import (
"reflect"
"sync"
"github.com/fsnotify/fsnotify"
"github.com/mitchellh/copystructure"
"github.com/spf13/viper"
)
// Config holds pomerium configuration options.
type Config struct {
Options *Options
}
// Clone creates a deep clone of the config.
func (cfg *Config) Clone() *Config {
return copystructure.Must(copystructure.Config{
Copiers: map[reflect.Type]copystructure.CopierFunc{
reflect.TypeOf((*viper.Viper)(nil)): func(i interface{}) (interface{}, error) {
return i, nil
},
},
}.Copy(cfg)).(*Config)
}
// A ChangeListener is called when configuration changes.
type ChangeListener = func(*Config)
// A ChangeDispatcher manages listeners on config changes.
type ChangeDispatcher struct {
sync.Mutex
onConfigChangeListeners []ChangeListener
}
// Trigger triggers a change.
func (dispatcher *ChangeDispatcher) Trigger(cfg *Config) {
dispatcher.Lock()
defer dispatcher.Unlock()
for _, li := range dispatcher.onConfigChangeListeners {
li(cfg)
}
}
// OnConfigChange adds a listener.
func (dispatcher *ChangeDispatcher) OnConfigChange(li ChangeListener) {
dispatcher.Lock()
defer dispatcher.Unlock()
dispatcher.onConfigChangeListeners = append(dispatcher.onConfigChangeListeners, li)
}
// A Source gets configuration.
type Source interface {
GetConfig() *Config
OnConfigChange(ChangeListener)
}
// A FileOrEnvironmentSource retrieves config options from a file or the environment.
type FileOrEnvironmentSource struct {
configFile string
mu sync.RWMutex
config *Config
ChangeDispatcher
}
// NewFileOrEnvironmentSource creates a new FileOrEnvironmentSource.
func NewFileOrEnvironmentSource(configFile string) (*FileOrEnvironmentSource, error) {
options, err := newOptionsFromConfig(configFile)
if err != nil {
return nil, err
}
src := &FileOrEnvironmentSource{
configFile: configFile,
config: &Config{Options: options},
}
options.viper.OnConfigChange(src.onConfigChange)
go options.viper.WatchConfig()
return src, nil
}
func (src *FileOrEnvironmentSource) onConfigChange(evt fsnotify.Event) {
src.mu.Lock()
newOptions := handleConfigUpdate(src.configFile, src.config.Options)
cfg := &Config{Options: newOptions}
src.config = cfg
src.mu.Unlock()
src.Trigger(cfg)
}
// GetConfig gets the config.
func (src *FileOrEnvironmentSource) GetConfig() *Config {
src.mu.RLock()
defer src.mu.RUnlock()
return src.config
}