[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
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
77da23e84b
Коммит
9c9fbe6dd9
@@ -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
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user