[MM-32234] Fix config.json getting reset (#16805)

* Add support for read-only config stores

* Allow read/write config store for plugin commands that require it
Этот коммит содержится в:
Claudio Costa
2021-01-28 20:04:17 +01:00
коммит произвёл GitHub
родитель 77da23e84b
Коммит 9c9fbe6dd9
22 изменённых файлов: 211 добавлений и 92 удалений

Просмотреть файл

@@ -14,6 +14,12 @@ import (
"github.com/mattermost/mattermost-server/v5/utils/jsonutils"
)
var (
// ErrReadOnlyStore is returned when an attempt to modify a read-only
// configuration store is made.
ErrReadOnlyStore = errors.New("configuration store is read-only")
)
// Listener is a callback function invoked when the configuration changes.
type Listener func(oldConfig *model.Config, newConfig *model.Config)
@@ -48,13 +54,13 @@ type BackingStore interface {
}
// NewStore creates a database or file store given a data source name by which to connect.
func NewStore(dsn string, watch bool, customDefaults *model.Config) (*Store, error) {
func NewStore(dsn string, watch, readOnly bool, customDefaults *model.Config) (*Store, error) {
backingStore, err := getBackingStore(dsn, watch)
if err != nil {
return nil, err
}
store, err := NewStoreFromBacking(backingStore, customDefaults)
store, err := NewStoreFromBacking(backingStore, customDefaults, readOnly)
if err != nil {
backingStore.Close()
return nil, errors.Wrap(err, "failed to create store")
@@ -63,10 +69,11 @@ func NewStore(dsn string, watch bool, customDefaults *model.Config) (*Store, err
return store, nil
}
func NewStoreFromBacking(backingStore BackingStore, customDefaults *model.Config) (*Store, error) {
func NewStoreFromBacking(backingStore BackingStore, customDefaults *model.Config, readOnly bool) (*Store, error) {
store := &Store{
backingStore: backingStore,
configCustomDefaults: customDefaults,
readOnly: readOnly,
}
if err := store.Load(); err != nil {
@@ -96,7 +103,7 @@ func NewTestMemoryStore() *Store {
panic("failed to initialize memory store: " + err.Error())
}
configStore, err := NewStoreFromBacking(memoryStore, nil)
configStore, err := NewStoreFromBacking(memoryStore, nil, false)
if err != nil {
panic("failed to initialize config store: " + err.Error())
}
@@ -114,6 +121,7 @@ type Store struct {
configCustomDefaults *model.Config
persistFeatureFlags bool
readOnly bool
}
// Get fetches the current, cached configuration.
@@ -156,6 +164,10 @@ func (s *Store) Set(newCfg *model.Config) (*model.Config, error) {
var unlockOnce sync.Once
defer unlockOnce.Do(s.configLock.Unlock)
if s.readOnly {
return nil, ErrReadOnlyStore
}
oldCfg := s.config.Clone()
// Really just for some tests we need to set defaults here
@@ -236,7 +248,7 @@ func (s *Store) loadLockedWithOld(oldCfg *model.Config, unlockOnce *sync.Once) e
if err != nil {
return errors.Wrap(err, "failed to marshal loaded config")
}
if len(configBytes) == 0 || !bytes.Equal(oldCfgBytes, newCfgBytes) {
if !s.readOnly && (len(configBytes) == 0 || !bytes.Equal(oldCfgBytes, newCfgBytes)) {
if err := s.backingStore.Set(s.configNoEnv); err != nil {
if !errors.Is(err, ErrReadOnlyConfiguration) {
return errors.Wrap(err, "failed to persist")
@@ -276,6 +288,9 @@ func (s *Store) GetFile(name string) ([]byte, error) {
func (s *Store) SetFile(name string, data []byte) error {
s.configLock.Lock()
defer s.configLock.Unlock()
if s.readOnly {
return ErrReadOnlyStore
}
return s.backingStore.SetFile(name, data)
}
@@ -290,6 +305,9 @@ func (s *Store) HasFile(name string) (bool, error) {
func (s *Store) RemoveFile(name string) error {
s.configLock.Lock()
defer s.configLock.Unlock()
if s.readOnly {
return ErrReadOnlyStore
}
return s.backingStore.RemoveFile(name)
}
@@ -304,3 +322,10 @@ func (s *Store) Close() error {
defer s.configLock.Unlock()
return s.backingStore.Close()
}
// IsReadOnly returns whether or not the store is read-only.
func (s *Store) IsReadOnly() bool {
s.configLock.RLock()
defer s.configLock.RUnlock()
return s.readOnly
}