diff --git a/app/audit.go b/app/audit.go index a1a8837094..1c1026c8db 100644 --- a/app/audit.go +++ b/app/audit.go @@ -111,7 +111,7 @@ func (s *Server) configureAudit(adt *audit.Audit, bAllowAdvancedLogging bool) er dsn := *s.Config().ExperimentalAuditSettings.AdvancedLoggingConfig if bAllowAdvancedLogging && dsn != "" { var err error - logConfigSrc, err = config.NewLogConfigSrc(dsn, s.configStore) + logConfigSrc, err = config.NewLogConfigSrc(dsn, s.configStore.Store) if err != nil { return fmt.Errorf("invalid config source for audit, %w", err) } diff --git a/app/channels.go b/app/channels.go index 672223920c..fffbb6b95d 100644 --- a/app/channels.go +++ b/app/channels.go @@ -4,6 +4,7 @@ package app import ( + "fmt" "runtime" "strings" "sync" @@ -20,9 +21,26 @@ import ( "github.com/pkg/errors" ) +// configSvc is a consumer interface to work +// with any config related task with the server. +type configSvc interface { + Config() *model.Config + AddConfigListener(listener func(*model.Config, *model.Config)) string + RemoveConfigListener(id string) + UpdateConfig(f func(*model.Config)) + SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bool) (*model.Config, *model.Config, *model.AppError) +} + +// namer is an interface which enforces that +// all services can return their names. +type namer interface { + Name() ServiceKey +} + // Channels contains all channels related state. type Channels struct { - srv *Server + srv *Server + cfgSvc configSvc postActionCookieSecret []byte @@ -65,17 +83,44 @@ type Channels struct { } func init() { - RegisterProduct("channels", func(s *Server) (Product, error) { - return NewChannels(s) + RegisterProduct("channels", func(s *Server, services map[ServiceKey]interface{}) (Product, error) { + return NewChannels(s, services) }) } -func NewChannels(s *Server) (*Channels, error) { +func NewChannels(s *Server, services map[ServiceKey]interface{}) (*Channels, error) { + ch := &Channels{ srv: s, imageProxy: imageproxy.MakeImageProxy(s, s.httpService, s.Log), uploadLockMap: map[string]bool{}, } + + // To get another service: + // 1. Prepare the service interface + // 2. Add the field to *Channels + // 3. Add the service key to the slice. + // 4. Add a new case in the switch statement. + requiredServices := []ServiceKey{ConfigKey} + for _, svcKey := range requiredServices { + svc, ok := services[svcKey] + if !ok { + return nil, fmt.Errorf("Service %s not passed", svcKey) + } + switch svcKey { + // Keep adding more services here + case ConfigKey: + cfgSvc, ok := svc.(configSvc) + if !ok { + return nil, errors.New("Config service did not satisfy ConfigSvc interface") + } + _, ok = svc.(namer) + if !ok { + return nil, errors.New("Config service does not contain Name method") + } + ch.cfgSvc = cfgSvc + } + } // We are passing a partially filled Channels struct so that the enterprise // methods can have access to app methods. // Otherwise, passing server would mean it has to call s.Channels(), @@ -119,7 +164,7 @@ func NewChannels(s *Server) (*Channels, error) { func (ch *Channels) Start() error { // Start plugins ctx := request.EmptyContext() - ch.initPlugins(ctx, *ch.srv.Config().PluginSettings.Directory, *ch.srv.Config().PluginSettings.ClientDirectory) + ch.initPlugins(ctx, *ch.cfgSvc.Config().PluginSettings.Directory, *ch.cfgSvc.Config().PluginSettings.ClientDirectory) ch.AddConfigListener(func(prevCfg, cfg *model.Config) { // We compute the difference between configs @@ -142,7 +187,7 @@ func (ch *Channels) Start() error { // Do only if some plugin related settings has changed. if hasDiff { if *cfg.PluginSettings.Enable { - ch.initPlugins(ctx, *cfg.PluginSettings.Directory, *ch.srv.Config().PluginSettings.ClientDirectory) + ch.initPlugins(ctx, *cfg.PluginSettings.Directory, *ch.cfgSvc.Config().PluginSettings.ClientDirectory) } else { ch.ShutDownPlugins() } diff --git a/app/config.go b/app/config.go index 4aafe7323f..236bf2b02e 100644 --- a/app/config.go +++ b/app/config.go @@ -31,8 +31,82 @@ const ( ErrorTermsOfServiceNoRowsFound = "app.terms_of_service.get.no_rows.app_error" ) +// configWrapper is an adapter struct that only exposes the +// config related functionality to be passed down to other products. +type configWrapper struct { + srv *Server + *config.Store +} + +func (w *configWrapper) Name() ServiceKey { + return ConfigKey +} + +func (w *configWrapper) Config() *model.Config { + return w.Store.Get() +} + +func (w *configWrapper) AddConfigListener(listener func(*model.Config, *model.Config)) string { + return w.Store.AddListener(listener) +} + +func (w *configWrapper) RemoveConfigListener(id string) { + w.Store.RemoveListener(id) +} + +func (w *configWrapper) UpdateConfig(f func(*model.Config)) { + if w.Store.IsReadOnly() { + return + } + old := w.Config() + updated := old.Clone() + f(updated) + if _, _, err := w.Store.Set(updated); err != nil { + mlog.Error("Failed to update config", mlog.Err(err)) + } +} + +func (w *configWrapper) SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bool) (*model.Config, *model.Config, *model.AppError) { + oldCfg, newCfg, err := w.Store.Set(newCfg) + if errors.Cause(err) == config.ErrReadOnlyConfiguration { + return nil, nil, model.NewAppError("saveConfig", "ent.cluster.save_config.error", nil, err.Error(), http.StatusForbidden) + } else if err != nil { + return nil, nil, model.NewAppError("saveConfig", "app.save_config.app_error", nil, err.Error(), http.StatusInternalServerError) + } + + if w.srv.startMetrics && *w.Config().MetricsSettings.Enable { + if w.srv.Metrics != nil { + w.srv.Metrics.Register() + } + w.srv.SetupMetricsServer() + } else { + w.srv.StopMetricsServer() + } + + if w.srv.Cluster != nil { + err := w.srv.Cluster.ConfigChanged(w.Store.RemoveEnvironmentOverrides(oldCfg), + w.Store.RemoveEnvironmentOverrides(newCfg), sendConfigChangeClusterMessage) + if err != nil { + return nil, nil, err + } + } + + return oldCfg, newCfg, nil +} + +func (w *configWrapper) ReloadConfig() error { + if err := w.Store.Load(); err != nil { + return err + } + return nil +} + func (s *Server) Config() *model.Config { - return s.configStore.Get() + return s.configStore.Config() +} + +func (s *Server) ConfigStore() *configWrapper { + return s.configStore } func (a *App) Config() *model.Config { @@ -48,15 +122,7 @@ func (a *App) EnvironmentConfig(filter func(reflect.StructField) bool) map[strin } func (s *Server) UpdateConfig(f func(*model.Config)) { - if s.configStore.IsReadOnly() { - return - } - old := s.Config() - updated := old.Clone() - f(updated) - if _, _, err := s.configStore.Set(updated); err != nil { - mlog.Error("Failed to update config", mlog.Err(err)) - } + s.configStore.UpdateConfig(f) } func (a *App) UpdateConfig(f func(*model.Config)) { @@ -64,10 +130,7 @@ func (a *App) UpdateConfig(f func(*model.Config)) { } func (s *Server) ReloadConfig() error { - if err := s.configStore.Load(); err != nil { - return err - } - return nil + return s.configStore.ReloadConfig() } func (a *App) ReloadConfig() error { @@ -90,7 +153,7 @@ func (a *App) LimitedClientConfig() map[string]string { // 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 (s *Server) AddConfigListener(listener func(*model.Config, *model.Config)) string { - return s.configStore.AddListener(listener) + return s.configStore.AddConfigListener(listener) } func (a *App) AddConfigListener(listener func(*model.Config, *model.Config)) string { @@ -99,7 +162,7 @@ func (a *App) AddConfigListener(listener func(*model.Config, *model.Config)) str // Removes a listener function by the unique ID returned when AddConfigListener was called func (s *Server) RemoveConfigListener(id string) { - s.configStore.RemoveListener(id) + s.configStore.RemoveConfigListener(id) } func (a *App) RemoveConfigListener(id string) { @@ -303,8 +366,8 @@ func (a *App) PostActionCookieSecret() []byte { } func (ch *Channels) regenerateClientConfig() { - clientConfig := config.GenerateClientConfig(ch.srv.Config(), ch.srv.TelemetryId(), ch.srv.License()) - limitedClientConfig := config.GenerateLimitedClientConfig(ch.srv.Config(), ch.srv.TelemetryId(), ch.srv.License()) + clientConfig := config.GenerateClientConfig(ch.cfgSvc.Config(), ch.srv.TelemetryId(), ch.srv.License()) + limitedClientConfig := config.GenerateLimitedClientConfig(ch.cfgSvc.Config(), ch.srv.TelemetryId(), ch.srv.License()) if clientConfig["EnableCustomTermsOfService"] == "true" { termsOfService, err := ch.srv.Store.TermsOfService().GetLatest(true) @@ -402,31 +465,7 @@ func (a *App) GetEnvironmentConfig(filter func(reflect.StructField) bool) map[st // SaveConfig replaces the active configuration, optionally notifying cluster peers. // It returns both the previous and current configs. func (s *Server) SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bool) (*model.Config, *model.Config, *model.AppError) { - oldCfg, newCfg, err := s.configStore.Set(newCfg) - if errors.Cause(err) == config.ErrReadOnlyConfiguration { - return nil, nil, model.NewAppError("saveConfig", "ent.cluster.save_config.error", nil, err.Error(), http.StatusForbidden) - } else if err != nil { - return nil, nil, model.NewAppError("saveConfig", "app.save_config.app_error", nil, err.Error(), http.StatusInternalServerError) - } - - if s.startMetrics && *s.Config().MetricsSettings.Enable { - if s.Metrics != nil { - s.Metrics.Register() - } - s.SetupMetricsServer() - } else { - s.StopMetricsServer() - } - - if s.Cluster != nil { - err := s.Cluster.ConfigChanged(s.configStore.RemoveEnvironmentOverrides(oldCfg), - s.configStore.RemoveEnvironmentOverrides(newCfg), sendConfigChangeClusterMessage) - if err != nil { - return nil, nil, err - } - } - - return oldCfg, newCfg, nil + return s.configStore.SaveConfig(newCfg, sendConfigChangeClusterMessage) } // SaveConfig replaces the active configuration, optionally notifying cluster peers. diff --git a/app/notification_push_test.go b/app/notification_push_test.go index 3351a4715c..237e841853 100644 --- a/app/notification_push_test.go +++ b/app/notification_push_test.go @@ -1403,12 +1403,15 @@ func TestPushNotificationRace(t *testing.T) { Return(&model.Preference{Value: "test"}, nil) mockStore.On("Preference").Return(&mockPreferenceStore) s := &Server{ - configStore: memoryStore, - Store: mockStore, - products: make(map[string]Product), - Router: mux.NewRouter(), + Store: mockStore, + products: make(map[string]Product), + Router: mux.NewRouter(), } - ch, err := NewChannels(s) + s.configStore = &configWrapper{srv: s, Store: memoryStore} + serviceMap := map[ServiceKey]interface{}{ + ConfigKey: s.configStore, + } + ch, err := NewChannels(s, serviceMap) require.NoError(t, err) s.products["channels"] = ch diff --git a/app/options.go b/app/options.go index 1dd117cfae..a88edb51f3 100644 --- a/app/options.go +++ b/app/options.go @@ -50,7 +50,7 @@ func Config(dsn string, readOnly bool, configDefaults *model.Config) Option { return errors.Wrap(err, "failed to apply Config option") } - s.configStore = configStore + s.configStore = &configWrapper{srv: s, Store: configStore} return nil } } @@ -58,7 +58,7 @@ func Config(dsn string, readOnly bool, configDefaults *model.Config) Option { // ConfigStore applies the given config store, typically to replace the traditional sources with a memory store for testing. func ConfigStore(configStore *config.Store) Option { return func(s *Server) error { - s.configStore = configStore + s.configStore = &configWrapper{srv: s, Store: configStore} return nil } diff --git a/app/plugin.go b/app/plugin.go index abbfd99d56..8ad6e81bd6 100644 --- a/app/plugin.go +++ b/app/plugin.go @@ -43,7 +43,7 @@ type pluginSignaturePath struct { // To get the plugins environment when the plugins are disabled, manually acquire the plugins // lock instead. func (ch *Channels) GetPluginsEnvironment() *plugin.Environment { - if !*ch.srv.Config().PluginSettings.Enable { + if !*ch.cfgSvc.Config().PluginSettings.Enable { return nil } @@ -79,7 +79,7 @@ func (ch *Channels) syncPluginsActiveState() { return } - config := ch.srv.Config().PluginSettings + config := ch.cfgSvc.Config().PluginSettings if *config.Enable { availablePlugins, err := pluginsEnvironment.Available() @@ -100,7 +100,7 @@ func (ch *Channels) syncPluginsActiveState() { // Tie Apps proxy disabled status to the feature flag. if pluginID == "com.mattermost.apps" { - if !ch.srv.Config().FeatureFlags.AppsEnabled { + if !ch.cfgSvc.Config().FeatureFlags.AppsEnabled { pluginEnabled = false } } @@ -174,10 +174,10 @@ func (ch *Channels) initPlugins(c *request.Context, pluginDir, webappPluginDir s ch.pluginsLock.RLock() pluginsEnvironment := ch.pluginsEnvironment ch.pluginsLock.RUnlock() - if pluginsEnvironment != nil || !*ch.srv.Config().PluginSettings.Enable { + if pluginsEnvironment != nil || !*ch.cfgSvc.Config().PluginSettings.Enable { ch.syncPluginsActiveState() if pluginsEnvironment != nil { - pluginsEnvironment.TogglePluginHealthCheckJob(*ch.srv.Config().PluginSettings.EnableHealthCheck) + pluginsEnvironment.TogglePluginHealthCheckJob(*ch.cfgSvc.Config().PluginSettings.EnableHealthCheck) } return } @@ -207,7 +207,7 @@ func (ch *Channels) initPlugins(c *request.Context, pluginDir, webappPluginDir s ch.pluginsEnvironment = env ch.pluginsLock.Unlock() - ch.pluginsEnvironment.TogglePluginHealthCheckJob(*ch.srv.Config().PluginSettings.EnableHealthCheck) + ch.pluginsEnvironment.TogglePluginHealthCheckJob(*ch.cfgSvc.Config().PluginSettings.EnableHealthCheck) if err := ch.syncPlugins(); err != nil { mlog.Error("Failed to sync plugins from the file store", mlog.Err(err)) @@ -274,7 +274,7 @@ func (ch *Channels) syncPlugins() *model.AppError { go func(pluginID string) { defer wg.Done() // Only handle managed plugins with .filestore flag file. - _, err := os.Stat(filepath.Join(*ch.srv.Config().PluginSettings.Directory, pluginID, managedPluginFileName)) + _, err := os.Stat(filepath.Join(*ch.cfgSvc.Config().PluginSettings.Directory, pluginID, managedPluginFileName)) if os.IsNotExist(err) { mlog.Warn("Skipping sync for unmanaged plugin", mlog.String("plugin_id", pluginID)) } else if err != nil { @@ -307,7 +307,7 @@ func (ch *Channels) syncPlugins() *model.AppError { defer reader.Close() var signature filestore.ReadCloseSeeker - if *ch.srv.Config().PluginSettings.RequirePluginSignature { + if *ch.cfgSvc.Config().PluginSettings.RequirePluginSignature { signature, appErr = ch.srv.fileReader(plugin.signaturePath) if appErr != nil { mlog.Error("Failed to open plugin signature from file store.", mlog.Err(appErr)) @@ -401,12 +401,12 @@ func (ch *Channels) enablePlugin(id string) *model.AppError { return model.NewAppError("EnablePlugin", "app.plugin.not_installed.app_error", nil, "", http.StatusNotFound) } - ch.srv.UpdateConfig(func(cfg *model.Config) { + ch.cfgSvc.UpdateConfig(func(cfg *model.Config) { cfg.PluginSettings.PluginStates[id] = &model.PluginState{Enable: true} }) // This call will implicitly invoke SyncPluginsActiveState which will activate enabled plugins. - if _, _, err := ch.srv.SaveConfig(ch.srv.Config(), true); err != nil { + if _, _, err := ch.cfgSvc.SaveConfig(ch.cfgSvc.Config(), true); err != nil { if err.Id == "ent.cluster.save_config.error" { return model.NewAppError("EnablePlugin", "app.plugin.cluster.save_config.app_error", nil, "", http.StatusInternalServerError) } @@ -447,13 +447,13 @@ func (ch *Channels) disablePlugin(id string) *model.AppError { return model.NewAppError("DisablePlugin", "app.plugin.not_installed.app_error", nil, "", http.StatusNotFound) } - ch.srv.UpdateConfig(func(cfg *model.Config) { + ch.cfgSvc.UpdateConfig(func(cfg *model.Config) { cfg.PluginSettings.PluginStates[id] = &model.PluginState{Enable: false} }) ch.unregisterPluginCommands(id) // This call will implicitly invoke SyncPluginsActiveState which will deactivate disabled plugins. - if _, _, err := ch.srv.SaveConfig(ch.srv.Config(), true); err != nil { + if _, _, err := ch.cfgSvc.SaveConfig(ch.cfgSvc.Config(), true); err != nil { return model.NewAppError("DisablePlugin", "app.plugin.config.app_error", nil, err.Error(), http.StatusInternalServerError) } @@ -561,7 +561,7 @@ func (ch *Channels) getPrepackagedPlugin(pluginID, version string) (*plugin.Prep // If version is empty, the latest compatible version is used. func (ch *Channels) getRemoteMarketplacePlugin(pluginID, version string) (*model.BaseMarketplacePlugin, *model.AppError) { marketplaceClient, err := marketplace.NewClient( - *ch.srv.Config().PluginSettings.MarketplaceURL, + *ch.cfgSvc.Config().PluginSettings.MarketplaceURL, ch.srv.HTTPService(), ) if err != nil { @@ -835,8 +835,8 @@ func (ch *Channels) getPluginsFromFilePaths(fileStorePaths []string) map[string] pluginSignaturePathMap := make(map[string]*pluginSignaturePath) fsPrefix := "" - if *ch.srv.Config().FileSettings.DriverName == model.ImageDriverS3 { - ptr := ch.srv.Config().FileSettings.AmazonS3PathPrefix + if *ch.cfgSvc.Config().FileSettings.DriverName == model.ImageDriverS3 { + ptr := ch.cfgSvc.Config().FileSettings.AmazonS3PathPrefix if ptr != nil && *ptr != "" { fsPrefix = *ptr + "/" } @@ -936,12 +936,12 @@ func (ch *Channels) processPrepackagedPlugin(pluginPath *pluginSignaturePath) (* } // Skip installing the plugin at all if automatic prepackaged plugins is disabled - if !*ch.srv.Config().PluginSettings.AutomaticPrepackagedPlugins { + if !*ch.cfgSvc.Config().PluginSettings.AutomaticPrepackagedPlugins { return plugin, nil } // Skip installing if the plugin is has not been previously enabled. - pluginState := ch.srv.Config().PluginSettings.PluginStates[plugin.Manifest.Id] + pluginState := ch.cfgSvc.Config().PluginSettings.PluginStates[plugin.Manifest.Id] if pluginState == nil || !pluginState.Enable { return plugin, nil } @@ -956,16 +956,16 @@ func (ch *Channels) processPrepackagedPlugin(pluginPath *pluginSignaturePath) (* // installFeatureFlagPlugins handles the automatic installation/upgrade of plugins from feature flags func (ch *Channels) installFeatureFlagPlugins() { - ffControledPlugins := ch.srv.Config().FeatureFlags.Plugins() + ffControledPlugins := ch.cfgSvc.Config().FeatureFlags.Plugins() // Respect the automatic prepackaged disable setting - if !*ch.srv.Config().PluginSettings.AutomaticPrepackagedPlugins { + if !*ch.cfgSvc.Config().PluginSettings.AutomaticPrepackagedPlugins { return } for pluginID, version := range ffControledPlugins { // Skip installing if the plugin has been previously disabled. - pluginState := ch.srv.Config().PluginSettings.PluginStates[pluginID] + pluginState := ch.cfgSvc.Config().PluginSettings.PluginStates[pluginID] if pluginState != nil && !pluginState.Enable { ch.srv.Log.Debug("Not auto installing/upgrade because plugin was disabled", mlog.String("plugin_id", pluginID), mlog.String("version", version)) continue diff --git a/app/plugin_install.go b/app/plugin_install.go index 179f52ff4d..6bda8e7848 100644 --- a/app/plugin_install.go +++ b/app/plugin_install.go @@ -83,7 +83,7 @@ func (ch *Channels) installPluginFromData(data model.PluginEventData) { defer reader.Close() var signature filestore.ReadCloseSeeker - if *ch.srv.Config().PluginSettings.RequirePluginSignature { + if *ch.cfgSvc.Config().PluginSettings.RequirePluginSignature { signature, appErr = ch.srv.fileReader(plugin.signaturePath) if appErr != nil { mlog.Error("Failed to open plugin signature from file store.", mlog.Err(appErr)) @@ -196,7 +196,7 @@ func (ch *Channels) InstallMarketplacePlugin(request *model.InstallMarketplacePl signatureFile = bytes.NewReader(prepackagedPlugin.Signature) } - if *ch.srv.Config().PluginSettings.EnableRemoteMarketplace && pluginFile == nil { + if *ch.cfgSvc.Config().PluginSettings.EnableRemoteMarketplace && pluginFile == nil { var plugin *model.BaseMarketplacePlugin plugin, appErr = ch.getRemoteMarketplacePlugin(request.Id, request.Version) if appErr != nil { @@ -353,7 +353,7 @@ func (ch *Channels) installExtractedPlugin(manifest *model.Manifest, fromPluginD } } - pluginPath := filepath.Join(*ch.srv.Config().PluginSettings.Directory, manifest.Id) + pluginPath := filepath.Join(*ch.cfgSvc.Config().PluginSettings.Directory, manifest.Id) err = utils.CopyDir(fromPluginDir, pluginPath) if err != nil { return nil, model.NewAppError("installExtractedPlugin", "app.plugin.mvdir.app_error", nil, err.Error(), http.StatusInternalServerError) @@ -375,9 +375,9 @@ func (ch *Channels) installExtractedPlugin(manifest *model.Manifest, fromPluginD } // Activate the plugin if enabled. - pluginState := ch.srv.Config().PluginSettings.PluginStates[manifest.Id] + pluginState := ch.cfgSvc.Config().PluginSettings.PluginStates[manifest.Id] if pluginState != nil && pluginState.Enable { - if manifest.Id == "com.mattermost.apps" && !ch.srv.Config().FeatureFlags.AppsEnabled { + if manifest.Id == "com.mattermost.apps" && !ch.cfgSvc.Config().FeatureFlags.AppsEnabled { return manifest, nil } updatedManifest, _, err := pluginsEnvironment.Activate(manifest.Id) diff --git a/app/plugin_requests.go b/app/plugin_requests.go index 49b66262a1..142ec94a14 100644 --- a/app/plugin_requests.go +++ b/app/plugin_requests.go @@ -118,7 +118,7 @@ func (ch *Channels) servePluginRequest(w http.ResponseWriter, r *http.Request, h token := "" context := &plugin.Context{ RequestId: model.NewId(), - IPAddress: utils.GetIPAddress(r, ch.srv.Config().ServiceSettings.TrustedProxyIPHeader), + IPAddress: utils.GetIPAddress(r, ch.cfgSvc.Config().ServiceSettings.TrustedProxyIPHeader), AcceptLanguage: r.Header.Get("Accept-Language"), UserAgent: r.UserAgent(), } @@ -183,7 +183,7 @@ func (ch *Channels) servePluginRequest(w http.ResponseWriter, r *http.Request, h mlog.String("user_id", userID), } - if *ch.srv.Config().ServiceSettings.ExperimentalStrictCSRFEnforcement { + if *ch.cfgSvc.Config().ServiceSettings.ExperimentalStrictCSRFEnforcement { mlog.Warn(csrfErrorMessage, fields...) } else { mlog.Debug(csrfErrorMessage, fields...) @@ -212,7 +212,7 @@ func (ch *Channels) servePluginRequest(w http.ResponseWriter, r *http.Request, h params := mux.Vars(r) - subpath, _ := utils.GetSubpathFromConfig(ch.srv.Config()) + subpath, _ := utils.GetSubpathFromConfig(ch.cfgSvc.Config()) newQuery := r.URL.Query() newQuery.Del("access_token") diff --git a/app/plugin_signature.go b/app/plugin_signature.go index eec57807bc..2acf4990d1 100644 --- a/app/plugin_signature.go +++ b/app/plugin_signature.go @@ -81,7 +81,7 @@ func (ch *Channels) verifyPlugin(plugin, signature io.ReadSeeker) *model.AppErro if err := verifySignature(bytes.NewReader(mattermostPluginPublicKey), plugin, signature); err == nil { return nil } - publicKeys := ch.srv.Config().PluginSettings.SignaturePublicKeyFiles + publicKeys := ch.cfgSvc.Config().PluginSettings.SignaturePublicKeyFiles for _, pk := range publicKeys { pkBytes, appErr := ch.srv.getPublicKey(pk) if appErr != nil { diff --git a/app/plugin_statuses.go b/app/plugin_statuses.go index db982f814a..540ab7c4ac 100644 --- a/app/plugin_statuses.go +++ b/app/plugin_statuses.go @@ -80,7 +80,7 @@ func (ch *Channels) getClusterPluginStatuses() (model.PluginStatuses, *model.App return nil, err } - if ch.srv.Cluster != nil && *ch.srv.Config().ClusterSettings.Enable { + if ch.srv.Cluster != nil && *ch.cfgSvc.Config().ClusterSettings.Enable { clusterPluginStatuses, err := ch.srv.Cluster.GetPluginStatuses() if err != nil { return nil, model.NewAppError("GetClusterPluginStatuses", "app.plugin.get_cluster_plugin_statuses.app_error", nil, err.Error(), http.StatusInternalServerError) diff --git a/app/product.go b/app/product.go index cc6c4f55a0..e45e6faee7 100644 --- a/app/product.go +++ b/app/product.go @@ -8,8 +8,8 @@ type Product interface { Stop() error } -var products = make(map[string]func(*Server) (Product, error)) +var products = make(map[string]func(*Server, map[ServiceKey]interface{}) (Product, error)) -func RegisterProduct(name string, f func(*Server) (Product, error)) { +func RegisterProduct(name string, f func(*Server, map[ServiceKey]interface{}) (Product, error)) { products[name] = f } diff --git a/app/server.go b/app/server.go index 4979e08220..6acbf26f15 100644 --- a/app/server.go +++ b/app/server.go @@ -86,6 +86,12 @@ import ( // declaring this as var to allow overriding in tests var SentryDSN = "placeholder_sentry_dsn" +type ServiceKey string + +const ( + ConfigKey ServiceKey = "config" +) + type Server struct { sqlStore *sqlstore.SqlStore Store store.Store @@ -150,7 +156,7 @@ type Server struct { searchConfigListenerId string searchLicenseListenerId string loggerLicenseListenerId string - configStore *config.Store + configStore *configWrapper telemetryService *telemetry.TelemetryService userService *users.UserService @@ -231,7 +237,7 @@ func NewServer(options ...Option) (*Server, error) { return nil, errors.Wrap(err, "failed to load config") } - s.configStore = configStore + s.configStore = &configWrapper{srv: s, Store: configStore} } // Step 2: Logging @@ -250,10 +256,13 @@ func NewServer(options ...Option) (*Server, error) { s.httpService = httpservice.MakeHTTPService(s) + serviceMap := map[ServiceKey]interface{}{ + ConfigKey: s.configStore, + } // Step 3: Initialize products. // Depends on s.httpService. for name, initializer := range products { - prod, err2 := initializer(s) + prod, err2 := initializer(s, serviceMap) if err2 != nil { return nil, errors.Wrapf(err2, "error initializing product: %s", name) } @@ -755,7 +764,7 @@ func (s *Server) initLogging() error { s.NotificationsLog = l.With(mlog.String("logSource", "notifications")) } - if err := s.configureLogger("logging", s.Log, &s.Config().LogSettings, s.configStore, config.GetLogFileLocation); err != nil { + if err := s.configureLogger("logging", s.Log, &s.Config().LogSettings, s.configStore.Store, config.GetLogFileLocation); err != nil { // if the config is locked then a unit test has already configured and locked the logger; not an error. if !errors.Is(err, mlog.ErrConfigurationLock) { // revert to default logger if the config is invalid @@ -771,7 +780,7 @@ func (s *Server) initLogging() error { mlog.InitGlobalLogger(s.Log) notificationLogSettings := config.GetLogSettingsFromNotificationsLogSettings(&s.Config().NotificationLogSettings) - if err := s.configureLogger("notification logging", s.NotificationsLog, notificationLogSettings, s.configStore, config.GetNotificationsLogFileLocation); err != nil { + if err := s.configureLogger("notification logging", s.NotificationsLog, notificationLogSettings, s.configStore.Store, config.GetNotificationsLogFileLocation); err != nil { if !errors.Is(err, mlog.ErrConfigurationLock) { mlog.Error("Error configuring notification logger", mlog.Err(err)) return err diff --git a/app/server_test.go b/app/server_test.go index 4f9ca6536c..05c573d49d 100644 --- a/app/server_test.go +++ b/app/server_test.go @@ -85,7 +85,7 @@ func TestReadReplicaDisabledBasedOnLicense(t *testing.T) { s, err := NewServer(func(server *Server) error { configStore := config.NewTestMemoryStore() configStore.Set(&cfg) - server.configStore = configStore + server.configStore = &configWrapper{srv: server, Store: configStore} return nil }) require.NoError(t, err) @@ -111,7 +111,7 @@ func TestReadReplicaDisabledBasedOnLicense(t *testing.T) { s, err := NewServer(func(server *Server) error { configStore := config.NewTestMemoryStore() configStore.Set(&cfg) - server.configStore = configStore + server.configStore = &configWrapper{srv: server, Store: configStore} return nil }) require.NoError(t, err) @@ -124,7 +124,7 @@ func TestReadReplicaDisabledBasedOnLicense(t *testing.T) { s, err := NewServer(func(server *Server) error { configStore := config.NewTestMemoryStore() configStore.Set(&cfg) - server.configStore = configStore + server.configStore = &configWrapper{srv: server, Store: configStore} server.licenseValue.Store(model.NewTestLicense()) return nil }) @@ -169,7 +169,7 @@ func TestStartServerNoS3Bucket(t *testing.T) { s, err := NewServer(func(server *Server) error { configStore, _ := config.NewFileStore("config.json", true) store, _ := config.NewStoreFromBacking(configStore, nil, false) - server.configStore = store + server.configStore = &configWrapper{srv: server, Store: store} server.UpdateConfig(func(cfg *model.Config) { cfg.FileSettings = model.FileSettings{ DriverName: model.NewString(model.ImageDriverS3),