pomerium/config/config_source.go
Caleb Doxsey f822c9a5d2
config: allow reloading of telemetry settings (#1255)
* metrics: support dynamic configuration settings

* add test

* trace: update configuration when settings change

* config: allow logging options to be configured when settings change

* envoy: allow changing log settings

* fix unexpected doc change

* fix tests

* pick a port at random

* update based on review
2020-08-12 08:14:15 -06:00

127 lines
2.8 KiB
Go

package config
import (
"sync"
"github.com/fsnotify/fsnotify"
)
// Config holds pomerium configuration options.
type Config struct {
Options *Options
}
// Clone creates a clone of the config.
func (cfg *Config) Clone() *Config {
newOptions := new(Options)
*newOptions = *cfg.Options
return &Config{
Options: newOptions,
}
}
// 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 StaticSource always returns the same config. Useful for testing.
type StaticSource struct {
cfg *Config
lis []ChangeListener
}
// NewStaticSource creates a new StaticSource.
func NewStaticSource(cfg *Config) *StaticSource {
return &StaticSource{cfg: cfg}
}
// GetConfig gets the config.
func (src *StaticSource) GetConfig() *Config {
return src.cfg
}
// SetConfig sets the config.
func (src *StaticSource) SetConfig(cfg *Config) {
src.cfg = cfg
for _, li := range src.lis {
li(cfg)
}
}
// OnConfigChange is ignored for the StaticSource.
func (src *StaticSource) OnConfigChange(li ChangeListener) {
src.lis = append(src.lis, li)
}
// 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
}