github.com/pafomin-at-avito/mattermost-server@v5.11.1+incompatible/config/common.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package config
     5  
     6  import (
     7  	"io"
     8  	"sync"
     9  
    10  	"github.com/mattermost/mattermost-server/model"
    11  	"github.com/pkg/errors"
    12  )
    13  
    14  // commonStore enables code sharing between different backing implementations
    15  type commonStore struct {
    16  	emitter
    17  
    18  	configLock           sync.RWMutex
    19  	config               *model.Config
    20  	environmentOverrides map[string]interface{}
    21  }
    22  
    23  // Get fetches the current, cached configuration.
    24  func (cs *commonStore) Get() *model.Config {
    25  	cs.configLock.RLock()
    26  	defer cs.configLock.RUnlock()
    27  
    28  	return cs.config
    29  }
    30  
    31  // GetEnvironmentOverrides fetches the configuration fields overridden by environment variables.
    32  func (cs *commonStore) GetEnvironmentOverrides() map[string]interface{} {
    33  	cs.configLock.RLock()
    34  	defer cs.configLock.RUnlock()
    35  
    36  	return cs.environmentOverrides
    37  }
    38  
    39  // set replaces the current configuration in its entirety, and updates the backing store
    40  // using the persist function argument.
    41  //
    42  // This function assumes no lock has been acquired, as it acquires a write lock itself.
    43  func (cs *commonStore) set(newCfg *model.Config, validate func(*model.Config) error, persist func(*model.Config) error) (*model.Config, error) {
    44  	cs.configLock.Lock()
    45  	var unlockOnce sync.Once
    46  	defer unlockOnce.Do(cs.configLock.Unlock)
    47  
    48  	oldCfg := cs.config
    49  
    50  	// TODO: disallow attempting to save a directly modified config (comparing pointers). This
    51  	// wouldn't be an exhaustive check, given the use of pointers throughout the data
    52  	// structure, but might prevent common mistakes. Requires upstream changes first.
    53  	// if newCfg == oldCfg {
    54  	// 	return nil, errors.New("old configuration modified instead of cloning")
    55  	// }
    56  
    57  	newCfg = newCfg.Clone()
    58  	newCfg.SetDefaults()
    59  
    60  	// Sometimes the config is received with "fake" data in sensitive fields. Apply the real
    61  	// data from the existing config as necessary.
    62  	desanitize(oldCfg, newCfg)
    63  
    64  	if validate != nil {
    65  		if err := validate(newCfg); err != nil {
    66  			return nil, errors.Wrap(err, "new configuration is invalid")
    67  		}
    68  	}
    69  
    70  	if err := persist(newCfg); err != nil {
    71  		return nil, errors.Wrap(err, "failed to persist")
    72  	}
    73  
    74  	cs.config = newCfg
    75  
    76  	unlockOnce.Do(cs.configLock.Unlock)
    77  
    78  	// Notify listeners synchronously. Ideally, this would be asynchronous, but existing code
    79  	// assumes this and there would be increased complexity to avoid racing updates.
    80  	cs.invokeConfigListeners(oldCfg, newCfg)
    81  
    82  	return oldCfg, nil
    83  }
    84  
    85  // load updates the current configuration from the given io.ReadCloser.
    86  //
    87  // This function assumes no lock has been acquired, as it acquires a write lock itself.
    88  func (cs *commonStore) load(f io.ReadCloser, needsSave bool, validate func(*model.Config) error, persist func(*model.Config) error) error {
    89  	allowEnvironmentOverrides := true
    90  	loadedCfg, environmentOverrides, err := unmarshalConfig(f, allowEnvironmentOverrides)
    91  	if err != nil {
    92  		return errors.Wrapf(err, "failed to unmarshal config")
    93  	}
    94  
    95  	// SetDefaults generates various keys and salts if not previously configured. Determine if
    96  	// such a change will be made before invoking.
    97  	needsSave = needsSave || loadedCfg.SqlSettings.AtRestEncryptKey == nil || len(*loadedCfg.SqlSettings.AtRestEncryptKey) == 0
    98  	needsSave = needsSave || loadedCfg.FileSettings.PublicLinkSalt == nil || len(*loadedCfg.FileSettings.PublicLinkSalt) == 0
    99  
   100  	loadedCfg.SetDefaults()
   101  
   102  	if validate != nil {
   103  		if err = validate(loadedCfg); err != nil {
   104  			return errors.Wrap(err, "invalid config")
   105  		}
   106  	}
   107  
   108  	if changed := fixConfig(loadedCfg); changed {
   109  		needsSave = true
   110  	}
   111  
   112  	cs.configLock.Lock()
   113  	var unlockOnce sync.Once
   114  	defer unlockOnce.Do(cs.configLock.Unlock)
   115  
   116  	if needsSave && persist != nil {
   117  		if err = persist(loadedCfg); err != nil {
   118  			return errors.Wrap(err, "failed to persist required changes after load")
   119  		}
   120  	}
   121  
   122  	oldCfg := cs.config
   123  	cs.config = loadedCfg
   124  	cs.environmentOverrides = environmentOverrides
   125  
   126  	unlockOnce.Do(cs.configLock.Unlock)
   127  
   128  	// Notify listeners synchronously. Ideally, this would be asynchronous, but existing code
   129  	// assumes this and there would be increased complexity to avoid racing updates.
   130  	cs.invokeConfigListeners(oldCfg, loadedCfg)
   131  
   132  	return nil
   133  }
   134  
   135  // validate checks if the given configuration is valid
   136  func (cs *commonStore) validate(cfg *model.Config) error {
   137  	if err := cfg.IsValid(); err != nil {
   138  		return err
   139  	}
   140  
   141  	return nil
   142  }