MM-11262: database config store (#10281)
* vendor github.com/jmoiron/sqlx * MM-11262: introduce a database store * revert unnecessary fmt.Errorf * simplify unit test helper methods * remote TODO re: retry * relocate initializeConfigurationsTable for clarity * factor out a commonStore * acquire database config lock on close for safety * add missing header * fix lock comment
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
898a3a289c
Коммит
9bf5687311
122
config/file.go
122
config/file.go
@@ -10,7 +10,6 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
@@ -25,14 +24,11 @@ var (
|
||||
|
||||
// FileStore is a config store backed by a file such as config/config.json.
|
||||
type FileStore struct {
|
||||
emitter
|
||||
commonStore
|
||||
|
||||
config *model.Config
|
||||
environmentOverrides map[string]interface{}
|
||||
configLock sync.RWMutex
|
||||
path string
|
||||
watch bool
|
||||
watcher *watcher
|
||||
path string
|
||||
watch bool
|
||||
watcher *watcher
|
||||
}
|
||||
|
||||
// NewFileStore creates a new instance of a config store backed by the given file path.
|
||||
@@ -90,68 +86,15 @@ func resolveConfigFilePath(path string) (string, error) {
|
||||
return "", fmt.Errorf("failed to find config file %s", path)
|
||||
}
|
||||
|
||||
// Get fetches the current, cached configuration.
|
||||
func (fs *FileStore) Get() *model.Config {
|
||||
fs.configLock.RLock()
|
||||
defer fs.configLock.RUnlock()
|
||||
|
||||
return fs.config
|
||||
}
|
||||
|
||||
// GetEnvironmentOverrides fetches the configuration fields overridden by environment variables.
|
||||
func (fs *FileStore) GetEnvironmentOverrides() map[string]interface{} {
|
||||
fs.configLock.RLock()
|
||||
defer fs.configLock.RUnlock()
|
||||
|
||||
return fs.environmentOverrides
|
||||
}
|
||||
|
||||
// Set replaces the current configuration in its entirety, without updating the backing store.
|
||||
func (fs *FileStore) Set(newCfg *model.Config) (*model.Config, error) {
|
||||
fs.configLock.Lock()
|
||||
var unlockOnce sync.Once
|
||||
defer unlockOnce.Do(fs.configLock.Unlock)
|
||||
return fs.commonStore.set(newCfg, func(cfg *model.Config) error {
|
||||
if *fs.config.ClusterSettings.Enable && *fs.config.ClusterSettings.ReadOnlyConfig {
|
||||
return ErrReadOnlyConfiguration
|
||||
}
|
||||
|
||||
oldCfg := fs.config
|
||||
|
||||
// TODO: disallow attempting to save a directly modified config (comparing pointers). This
|
||||
// wouldn't be an exhaustive check, given the use of pointers throughout the data
|
||||
// structure, but might prevent common mistakes. Requires upstream changes first.
|
||||
// if newCfg == oldCfg {
|
||||
// return nil, errors.New("old configuration modified instead of cloning")
|
||||
// }
|
||||
|
||||
newCfg = newCfg.Clone()
|
||||
newCfg.SetDefaults()
|
||||
|
||||
// Sometimes the config is received with "fake" data in sensitive fields. Apply the real
|
||||
// data from the existing config as necessary.
|
||||
desanitize(oldCfg, newCfg)
|
||||
|
||||
if err := newCfg.IsValid(); err != nil {
|
||||
return nil, errors.Wrap(err, "new configuration is invalid")
|
||||
}
|
||||
|
||||
if *oldCfg.ClusterSettings.Enable && *oldCfg.ClusterSettings.ReadOnlyConfig {
|
||||
return nil, ErrReadOnlyConfiguration
|
||||
}
|
||||
|
||||
// Ideally, Set would persist automatically and abstract this completely away from the
|
||||
// client. Doing so requires a few upstream changes first, so for now an explicit Save()
|
||||
// remains required.
|
||||
// if err := fs.persist(newCfg); err != nil {
|
||||
// return nil, errors.Wrap(err, "failed to persist")
|
||||
// }
|
||||
|
||||
fs.config = newCfg
|
||||
|
||||
unlockOnce.Do(fs.configLock.Unlock)
|
||||
|
||||
// Notify listeners synchronously. Ideally, this would be asynchronous, but existing code
|
||||
// assumes this and there would be increased complexity to avoid racing updates.
|
||||
fs.invokeConfigListeners(oldCfg, newCfg)
|
||||
|
||||
return oldCfg, nil
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// persist writes the configuration to the configured file.
|
||||
@@ -206,50 +149,7 @@ func (fs *FileStore) Load() (err error) {
|
||||
}
|
||||
}()
|
||||
|
||||
allowEnvironmentOverrides := true
|
||||
loadedCfg, environmentOverrides, err := unmarshalConfig(f, allowEnvironmentOverrides)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to unmarshal config from %s", fs.path)
|
||||
}
|
||||
|
||||
// SetDefaults generates various keys and salts if not previously configured. Determine if
|
||||
// such a change will be made before invoking. This method will not effect the save: that
|
||||
// remains the responsibility of the caller.
|
||||
needsSave = needsSave || loadedCfg.SqlSettings.AtRestEncryptKey == nil || len(*loadedCfg.SqlSettings.AtRestEncryptKey) == 0
|
||||
needsSave = needsSave || loadedCfg.FileSettings.PublicLinkSalt == nil || len(*loadedCfg.FileSettings.PublicLinkSalt) == 0
|
||||
needsSave = needsSave || loadedCfg.EmailSettings.InviteSalt == nil || len(*loadedCfg.EmailSettings.InviteSalt) == 0
|
||||
|
||||
loadedCfg.SetDefaults()
|
||||
|
||||
if err := loadedCfg.IsValid(); err != nil {
|
||||
return errors.Wrap(err, "invalid config")
|
||||
}
|
||||
|
||||
if changed := fixConfig(loadedCfg); changed {
|
||||
needsSave = true
|
||||
}
|
||||
|
||||
fs.configLock.Lock()
|
||||
var unlockOnce sync.Once
|
||||
defer unlockOnce.Do(fs.configLock.Unlock)
|
||||
|
||||
if needsSave {
|
||||
if err = fs.persist(loadedCfg); err != nil {
|
||||
return errors.Wrap(err, "failed to persist required changes after load")
|
||||
}
|
||||
}
|
||||
|
||||
oldCfg := fs.config
|
||||
fs.config = loadedCfg
|
||||
fs.environmentOverrides = environmentOverrides
|
||||
|
||||
unlockOnce.Do(fs.configLock.Unlock)
|
||||
|
||||
// Notify listeners synchronously. Ideally, this would be asynchronous, but existing code
|
||||
// assumes this and there would be increased complexity to avoid racing updates.
|
||||
fs.invokeConfigListeners(oldCfg, loadedCfg)
|
||||
|
||||
return nil
|
||||
return fs.commonStore.load(f, needsSave, fs.persist)
|
||||
}
|
||||
|
||||
// Save writes the current configuration to the backing store.
|
||||
|
||||
Ссылка в новой задаче
Block a user