diff options
Diffstat (limited to 'utils/config.go')
-rw-r--r-- | utils/config.go | 257 |
1 files changed, 60 insertions, 197 deletions
diff --git a/utils/config.go b/utils/config.go index e2622cff9..bcc2054b3 100644 --- a/utils/config.go +++ b/utils/config.go @@ -4,7 +4,6 @@ package utils import ( - "crypto/md5" "encoding/json" "fmt" "io" @@ -14,10 +13,10 @@ import ( "path/filepath" "strconv" "strings" - "sync" l4g "github.com/alecthomas/log4go" "github.com/fsnotify/fsnotify" + "github.com/pkg/errors" "github.com/spf13/viper" "net/http" @@ -34,15 +33,6 @@ const ( LOG_FILENAME = "mattermost.log" ) -var cfgMutex = &sync.Mutex{} -var watcher *fsnotify.Watcher -var Cfg *model.Config = &model.Config{} -var CfgDiagnosticId = "" -var CfgHash = "" -var ClientCfgHash = "" -var CfgFileName string = "" -var CfgDisableConfigWatch = false -var ClientCfg map[string]string = map[string]string{} var originalDisableDebugLvl l4g.Level = l4g.DEBUG var siteURL = "" @@ -54,22 +44,6 @@ func SetSiteURL(url string) { siteURL = strings.TrimRight(url, "/") } -var cfgListeners = map[string]func(*model.Config, *model.Config){} - -// Registers a function with a given to be called when the config is reloaded and may have changed. The function -// will be called with two arguments: the old config and the new config. AddConfigListener returns a unique ID -// for the listener that can later be used to remove it. -func AddConfigListener(listener func(*model.Config, *model.Config)) string { - id := model.NewId() - cfgListeners[id] = listener - return id -} - -// Removes a listener function by the unique ID returned when AddConfigListener was called -func RemoveConfigListener(id string) { - delete(cfgListeners, id) -} - // FindConfigFile attempts to find an existing configuration file. fileName can be an absolute or // relative path or name such as "/opt/mattermost/config.json" or simply "config.json". An empty // string is returned if no configuration is found. @@ -107,8 +81,6 @@ func FindDir(dir string) (string, bool) { } func DisableDebugLogForTest() { - cfgMutex.Lock() - defer cfgMutex.Unlock() if l4g.Global["stdout"] != nil { originalDisableDebugLvl = l4g.Global["stdout"].Level l4g.Global["stdout"].Level = l4g.ERROR @@ -116,8 +88,6 @@ func DisableDebugLogForTest() { } func EnableDebugLogForTest() { - cfgMutex.Lock() - defer cfgMutex.Unlock() if l4g.Global["stdout"] != nil { l4g.Global["stdout"].Level = originalDisableDebugLvl } @@ -127,12 +97,12 @@ func ConfigureCmdLineLog() { ls := model.LogSettings{} ls.EnableConsole = true ls.ConsoleLevel = "WARN" - configureLog(&ls) + ConfigureLog(&ls) } // TODO: this code initializes console and file logging. It will eventually be replaced by JSON logging in logger/logger.go // See PLT-3893 for more information -func configureLog(s *model.LogSettings) { +func ConfigureLog(s *model.LogSettings) { l4g.Close() @@ -186,9 +156,6 @@ func GetLogFileLocation(fileLocation string) string { } func SaveConfig(fileName string, config *model.Config) *model.AppError { - cfgMutex.Lock() - defer cfgMutex.Unlock() - b, err := json.MarshalIndent(config, "", " ") if err != nil { return model.NewAppError("SaveConfig", "utils.config.save_config.saving.app_error", @@ -204,82 +171,61 @@ func SaveConfig(fileName string, config *model.Config) *model.AppError { return nil } -func InitializeConfigWatch() { - cfgMutex.Lock() - defer cfgMutex.Unlock() +type ConfigWatcher struct { + watcher *fsnotify.Watcher + close chan struct{} + closed chan struct{} +} - if CfgDisableConfigWatch { - return +func NewConfigWatcher(cfgFileName string, f func()) (*ConfigWatcher, error) { + watcher, err := fsnotify.NewWatcher() + if err != nil { + return nil, errors.Wrapf(err, "failed to create config watcher for file: "+cfgFileName) } - if watcher == nil { - var err error - watcher, err = fsnotify.NewWatcher() - if err != nil { - l4g.Error(fmt.Sprintf("Failed to watch config file at %v with err=%v", CfgFileName, err.Error())) - } + configFile := filepath.Clean(cfgFileName) + configDir, _ := filepath.Split(configFile) + watcher.Add(configDir) - go func() { - configFile := filepath.Clean(CfgFileName) - - for { - select { - case event := <-watcher.Events: - // we only care about the config file - if filepath.Clean(event.Name) == configFile { - if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create { - l4g.Info(fmt.Sprintf("Config file watcher detected a change reloading %v", CfgFileName)) - - if _, configReadErr := ReadConfigFile(CfgFileName, true); configReadErr == nil { - LoadGlobalConfig(CfgFileName) - } else { - l4g.Error(fmt.Sprintf("Failed to read while watching config file at %v with err=%v", CfgFileName, configReadErr.Error())) - } - } - } - case err := <-watcher.Errors: - l4g.Error(fmt.Sprintf("Failed while watching config file at %v with err=%v", CfgFileName, err.Error())) - } - } - }() + ret := &ConfigWatcher{ + watcher: watcher, + close: make(chan struct{}), + closed: make(chan struct{}), } -} -func EnableConfigWatch() { - cfgMutex.Lock() - defer cfgMutex.Unlock() + go func() { + defer close(ret.closed) + defer watcher.Close() - if watcher != nil { - configFile := filepath.Clean(CfgFileName) - configDir, _ := filepath.Split(configFile) + for { + select { + case event := <-watcher.Events: + // we only care about the config file + if filepath.Clean(event.Name) == configFile { + if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create { + l4g.Info(fmt.Sprintf("Config file watcher detected a change reloading %v", cfgFileName)) - if watcher != nil { - watcher.Add(configDir) + if _, configReadErr := ReadConfigFile(cfgFileName, true); configReadErr == nil { + f() + } else { + l4g.Error(fmt.Sprintf("Failed to read while watching config file at %v with err=%v", cfgFileName, configReadErr.Error())) + } + } + } + case err := <-watcher.Errors: + l4g.Error(fmt.Sprintf("Failed while watching config file at %v with err=%v", cfgFileName, err.Error())) + case <-ret.close: + return + } } - } -} + }() -func DisableConfigWatch() { - cfgMutex.Lock() - defer cfgMutex.Unlock() - - if watcher != nil { - configFile := filepath.Clean(CfgFileName) - configDir, _ := filepath.Split(configFile) - watcher.Remove(configDir) - } + return ret, nil } -func InitAndLoadConfig(filename string) error { - if err := TranslationsPreInit(); err != nil { - return err - } - - LoadGlobalConfig(filename) - InitializeConfigWatch() - EnableConfigWatch() - - return nil +func (w *ConfigWatcher) Close() { + close(w.close) + <-w.closed } // ReadConfig reads and parses the given configuration. @@ -344,26 +290,16 @@ func EnsureConfigFile(fileName string) (string, error) { return "", fmt.Errorf("no config file found") } -// LoadGlobalConfig will try to search around for the corresponding config file. It will search +// LoadConfig will try to search around for the corresponding config file. It will search // /tmp/fileName then attempt ./config/fileName, then ../config/fileName and last it will look at -// fileName -// -// XXX: This is deprecated. -func LoadGlobalConfig(fileName string) *model.Config { - cfgMutex.Lock() - defer cfgMutex.Unlock() - - // Cfg should never be null - oldConfig := *Cfg - - var configPath string +// fileName. +func LoadConfig(fileName string) (config *model.Config, configPath string, appErr *model.AppError) { if fileName != filepath.Base(fileName) { configPath = fileName } else { if path, err := EnsureConfigFile(fileName); err != nil { - errMsg := T("utils.config.load_config.opening.panic", map[string]interface{}{"Filename": fileName, "Error": err.Error()}) - fmt.Fprintln(os.Stderr, errMsg) - os.Exit(1) + appErr = model.NewAppError("LoadConfig", "utils.config.load_config.opening.panic", map[string]interface{}{"Filename": fileName, "Error": err.Error()}, "", 0) + return } else { configPath = path } @@ -371,42 +307,31 @@ func LoadGlobalConfig(fileName string) *model.Config { config, err := ReadConfigFile(configPath, true) if err != nil { - errMsg := T("utils.config.load_config.decoding.panic", map[string]interface{}{"Filename": fileName, "Error": err.Error()}) - fmt.Fprintln(os.Stderr, errMsg) - os.Exit(1) + appErr = model.NewAppError("LoadConfig", "utils.config.load_config.decoding.panic", map[string]interface{}{"Filename": fileName, "Error": err.Error()}, "", 0) + return } - CfgFileName = configPath - needSave := len(config.SqlSettings.AtRestEncryptKey) == 0 || len(*config.FileSettings.PublicLinkSalt) == 0 || len(config.EmailSettings.InviteSalt) == 0 config.SetDefaults() if err := config.IsValid(); err != nil { - panic(T(err.Id)) + return nil, "", err } if needSave { - cfgMutex.Unlock() - if err := SaveConfig(CfgFileName, config); err != nil { - err.Translate(T) + if err := SaveConfig(configPath, config); err != nil { l4g.Warn(err.Error()) } - cfgMutex.Lock() } if err := ValidateLocales(config); err != nil { - cfgMutex.Unlock() - if err := SaveConfig(CfgFileName, config); err != nil { - err.Translate(T) + if err := SaveConfig(configPath, config); err != nil { l4g.Warn(err.Error()) } - cfgMutex.Lock() } - configureLog(&config.LogSettings) - if *config.FileSettings.DriverName == model.IMAGE_DRIVER_LOCAL { dir := config.FileSettings.Directory if len(dir) > 0 && dir[len(dir)-1:] != "/" { @@ -414,30 +339,10 @@ func LoadGlobalConfig(fileName string) *model.Config { } } - Cfg = config - CfgHash = fmt.Sprintf("%x", md5.Sum([]byte(Cfg.ToJson()))) - ClientCfg = getClientConfig(Cfg) - clientCfgJson, _ := json.Marshal(ClientCfg) - ClientCfgHash = fmt.Sprintf("%x", md5.Sum(clientCfgJson)) - - SetSiteURL(*Cfg.ServiceSettings.SiteURL) - - InvokeGlobalConfigListeners(&oldConfig, config) - - return config -} - -func InvokeGlobalConfigListeners(old, current *model.Config) { - for _, listener := range cfgListeners { - listener(old, current) - } + return config, configPath, nil } -func RegenerateClientConfig() { - ClientCfg = getClientConfig(Cfg) -} - -func getClientConfig(c *model.Config) map[string]string { +func GenerateClientConfig(c *model.Config, diagnosticId string) map[string]string { props := make(map[string]string) props["Version"] = model.CurrentVersion @@ -491,6 +396,7 @@ func getClientConfig(c *model.Config) map[string]string { props["CloseUnusedDirectMessages"] = strconv.FormatBool(*c.ServiceSettings.CloseUnusedDirectMessages) props["EnablePreviewFeatures"] = strconv.FormatBool(*c.ServiceSettings.EnablePreviewFeatures) props["EnableTutorial"] = strconv.FormatBool(*c.ServiceSettings.EnableTutorial) + props["ExperimentalEnableDefaultChannelLeaveJoinMessages"] = strconv.FormatBool(*c.ServiceSettings.ExperimentalEnableDefaultChannelLeaveJoinMessages) props["SendEmailNotifications"] = strconv.FormatBool(c.EmailSettings.SendEmailNotifications) props["SendPushNotifications"] = strconv.FormatBool(*c.EmailSettings.SendPushNotifications) @@ -544,7 +450,7 @@ func getClientConfig(c *model.Config) map[string]string { props["EnableUserTypingMessages"] = strconv.FormatBool(*c.ServiceSettings.EnableUserTypingMessages) props["EnableChannelViewedMessages"] = strconv.FormatBool(*c.ServiceSettings.EnableChannelViewedMessages) - props["DiagnosticId"] = CfgDiagnosticId + props["DiagnosticId"] = diagnosticId props["DiagnosticsEnabled"] = strconv.FormatBool(*c.LogSettings.EnableDiagnostics) props["PluginsEnabled"] = strconv.FormatBool(*c.PluginSettings.Enable) @@ -690,46 +596,3 @@ func ValidateLocales(cfg *model.Config) *model.AppError { return err } - -func Desanitize(cfg *model.Config) { - if cfg.LdapSettings.BindPassword != nil && *cfg.LdapSettings.BindPassword == model.FAKE_SETTING { - *cfg.LdapSettings.BindPassword = *Cfg.LdapSettings.BindPassword - } - - if *cfg.FileSettings.PublicLinkSalt == model.FAKE_SETTING { - *cfg.FileSettings.PublicLinkSalt = *Cfg.FileSettings.PublicLinkSalt - } - if cfg.FileSettings.AmazonS3SecretAccessKey == model.FAKE_SETTING { - cfg.FileSettings.AmazonS3SecretAccessKey = Cfg.FileSettings.AmazonS3SecretAccessKey - } - - if cfg.EmailSettings.InviteSalt == model.FAKE_SETTING { - cfg.EmailSettings.InviteSalt = Cfg.EmailSettings.InviteSalt - } - if cfg.EmailSettings.SMTPPassword == model.FAKE_SETTING { - cfg.EmailSettings.SMTPPassword = Cfg.EmailSettings.SMTPPassword - } - - if cfg.GitLabSettings.Secret == model.FAKE_SETTING { - cfg.GitLabSettings.Secret = Cfg.GitLabSettings.Secret - } - - if *cfg.SqlSettings.DataSource == model.FAKE_SETTING { - *cfg.SqlSettings.DataSource = *Cfg.SqlSettings.DataSource - } - if cfg.SqlSettings.AtRestEncryptKey == model.FAKE_SETTING { - cfg.SqlSettings.AtRestEncryptKey = Cfg.SqlSettings.AtRestEncryptKey - } - - if *cfg.ElasticsearchSettings.Password == model.FAKE_SETTING { - *cfg.ElasticsearchSettings.Password = *Cfg.ElasticsearchSettings.Password - } - - for i := range cfg.SqlSettings.DataSourceReplicas { - cfg.SqlSettings.DataSourceReplicas[i] = Cfg.SqlSettings.DataSourceReplicas[i] - } - - for i := range cfg.SqlSettings.DataSourceSearchReplicas { - cfg.SqlSettings.DataSourceSearchReplicas[i] = Cfg.SqlSettings.DataSourceSearchReplicas[i] - } -} |