Move config store into Platform Service (#20718)

* update config methods
Этот коммит содержится в:
Ibrahim Serdar Acikgoz
2022-08-08 16:52:31 +03:00
коммит произвёл GitHub
родитель b826421716
Коммит f0e4794cda
43 изменённых файлов: 764 добавлений и 519 удалений

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

@@ -29,7 +29,7 @@ func (s *Server) GetLogs(page, perPage int) ([]string, *model.AppError) {
var lines []string
license := s.License()
if license != nil && *license.Features.Cluster && s.Cluster != nil && *s.Config().ClusterSettings.Enable {
if license != nil && *license.Features.Cluster && s.Cluster != nil && *s.platform.Config().ClusterSettings.Enable {
if info := s.Cluster.GetMyClusterInfo(); info != nil {
lines = append(lines, "-----------------------------------------------------------------------------------------------------------")
lines = append(lines, "-----------------------------------------------------------------------------------------------------------")
@@ -48,7 +48,7 @@ func (s *Server) GetLogs(page, perPage int) ([]string, *model.AppError) {
lines = append(lines, melines...)
if s.Cluster != nil && *s.Config().ClusterSettings.Enable {
if s.Cluster != nil && *s.platform.Config().ClusterSettings.Enable {
clines, err := s.Cluster.GetLogs(page, perPage)
if err != nil {
return nil, err
@@ -67,9 +67,9 @@ func (a *App) GetLogs(page, perPage int) ([]string, *model.AppError) {
func (s *Server) GetLogsSkipSend(page, perPage int) ([]string, *model.AppError) {
var lines []string
if *s.Config().LogSettings.EnableFile {
if *s.platform.Config().LogSettings.EnableFile {
s.Log.Flush()
logFile := config.GetLogFileLocation(*s.Config().LogSettings.FileLocation)
logFile := config.GetLogFileLocation(*s.platform.Config().LogSettings.FileLocation)
file, err := os.Open(logFile)
if err != nil {
return nil, model.NewAppError("getLogs", "api.admin.file_read_error", nil, err.Error(), http.StatusInternalServerError)

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

@@ -283,6 +283,8 @@ type AppIface interface {
// PromoteGuestToUser Convert user's roles and all his membership's roles from
// guest roles to regular user roles.
PromoteGuestToUser(c *request.Context, user *model.User, requestorId string) *model.AppError
// Removes a listener function by the unique ID returned when AddConfigListener was called
RemoveConfigListener(id string)
// RenameChannel is used to rename the channel Name and the DisplayName fields
RenameChannel(c request.CTX, channel *model.Channel, newChannelName string, newDisplayName string) (*model.Channel, *model.AppError)
// RenameTeam is used to rename the team Name and the DisplayName fields
@@ -941,7 +943,6 @@ type AppIface interface {
ReloadConfig() error
RemoveAllDeactivatedMembersFromChannel(c request.CTX, channel *model.Channel) *model.AppError
RemoveChannelsFromRetentionPolicy(policyID string, channelIDs []string) *model.AppError
RemoveConfigListener(id string)
RemoveCustomStatus(c request.CTX, userID string) *model.AppError
RemoveDirectory(path string) *model.AppError
RemoveFile(path string) *model.AppError

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

@@ -109,10 +109,10 @@ func (s *Server) configureAudit(adt *audit.Audit, bAllowAdvancedLogging bool) er
adt.OnError = s.onAuditError
var logConfigSrc config.LogConfigSrc
dsn := *s.Config().ExperimentalAuditSettings.AdvancedLoggingConfig
dsn := *s.platform.Config().ExperimentalAuditSettings.AdvancedLoggingConfig
if bAllowAdvancedLogging && dsn != "" {
var err error
logConfigSrc, err = config.NewLogConfigSrc(dsn, s.configStore.Store)
logConfigSrc, err = config.NewLogConfigSrc(dsn, s.platform.GetConfigStore())
if err != nil {
return fmt.Errorf("invalid config source for audit, %w", err)
}
@@ -120,7 +120,7 @@ func (s *Server) configureAudit(adt *audit.Audit, bAllowAdvancedLogging bool) er
}
// ExperimentalAuditSettings provides basic file audit (E0, E10); logConfigSrc provides advanced config (E20).
cfg, err := config.MloggerConfigFromAuditConfig(s.Config().ExperimentalAuditSettings, logConfigSrc)
cfg, err := config.MloggerConfigFromAuditConfig(s.platform.Config().ExperimentalAuditSettings, logConfigSrc)
if err != nil {
return fmt.Errorf("invalid config for audit, %w", err)
}

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

@@ -2056,7 +2056,7 @@ func TestMarkChannelsAsViewedPanic(t *testing.T) {
UserStore: &mockUserStore,
SessionStore: &mockSessionStore,
OAuthStore: &mockOAuthStore,
ConfigFn: th.App.ch.srv.Config,
ConfigFn: th.App.ch.srv.platform.Config,
LicenseFn: th.App.ch.srv.License,
})
require.NoError(t, err)

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

@@ -31,12 +31,6 @@ type licenseSvc interface {
RequestTrialLicense(requesterID string, users int, termsAccepted bool, receiveEmailsAccepted bool) *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
@@ -107,7 +101,7 @@ func init() {
func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
ch := &Channels{
srv: s,
imageProxy: imageproxy.MakeImageProxy(s, s.httpService, s.Log),
imageProxy: imageproxy.MakeImageProxy(s.platform, s.httpService, s.Log),
uploadLockMap: map[string]bool{},
}
@@ -133,10 +127,6 @@ func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
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
case FilestoreKey:
filestore, ok := svc.(filestore.FileBackend)
@@ -149,10 +139,6 @@ func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
if !ok {
return nil, errors.New("License service did not satisfy licenseSvc interface")
}
_, ok = svc.(namer)
if !ok {
return nil, errors.New("License service does not contain Name method")
}
ch.licenseSvc = svc
}
}

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

@@ -83,7 +83,7 @@ func (cds *ClusterDiscoveryService) Stop() {
}
func (s *Server) IsLeader() bool {
if s.License() != nil && *s.Config().ClusterSettings.Enable && s.Cluster != nil {
if s.License() != nil && *s.platform.Config().ClusterSettings.Enable && s.Cluster != nil {
return s.Cluster.IsLeader()
}
return true

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

@@ -12,7 +12,6 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"net/url"
"reflect"
"strconv"
@@ -22,7 +21,6 @@ import (
"github.com/mattermost/mattermost-server/v6/config"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/product"
"github.com/mattermost/mattermost-server/v6/shared/mail"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
"github.com/mattermost/mattermost-server/v6/utils"
@@ -32,113 +30,24 @@ const (
ErrorTermsOfServiceNoRowsFound = "app.terms_of_service.get.no_rows.app_error"
)
// ensure the config wrapper implements `product.ConfigService`
var _ product.ConfigService = (*configWrapper)(nil)
// 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.GetMetrics() != nil {
w.srv.GetMetrics().Register()
}
w.srv.platform.RestartMetrics() // TODO: remove when this moved to the platform service
} else {
w.srv.platform.ShutdownMetrics() // TODO: remove when this moved to the platform service
}
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.Config()
}
func (s *Server) ConfigStore() *configWrapper {
return s.configStore
return s.platform.Config()
}
func (a *App) Config() *model.Config {
return a.ch.cfgSvc.Config()
}
func (s *Server) EnvironmentConfig(filter func(reflect.StructField) bool) map[string]any {
return s.configStore.GetEnvironmentOverridesWithFilter(filter)
}
func (a *App) EnvironmentConfig(filter func(reflect.StructField) bool) map[string]any {
return a.Srv().EnvironmentConfig(filter)
}
func (s *Server) UpdateConfig(f func(*model.Config)) {
s.configStore.UpdateConfig(f)
return a.Srv().platform.GetEnvironmentOverridesWithFilter(filter)
}
func (a *App) UpdateConfig(f func(*model.Config)) {
a.Srv().UpdateConfig(f)
}
func (s *Server) ReloadConfig() error {
return s.configStore.ReloadConfig()
a.Srv().platform.UpdateConfig(f)
}
func (a *App) ReloadConfig() error {
return a.Srv().ReloadConfig()
return a.Srv().platform.ReloadConfig()
}
func (a *App) ClientConfig() map[string]string {
@@ -153,24 +62,13 @@ func (a *App) LimitedClientConfig() map[string]string {
return a.ch.limitedClientConfig.Load().(map[string]string)
}
// Registers a function with a given listener 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 (s *Server) AddConfigListener(listener func(*model.Config, *model.Config)) string {
return s.configStore.AddConfigListener(listener)
}
func (a *App) AddConfigListener(listener func(*model.Config, *model.Config)) string {
return a.Srv().AddConfigListener(listener)
return a.Srv().platform.AddConfigListener(listener)
}
// Removes a listener function by the unique ID returned when AddConfigListener was called
func (s *Server) RemoveConfigListener(id string) {
s.configStore.RemoveConfigListener(id)
}
func (a *App) RemoveConfigListener(id string) {
a.Srv().RemoveConfigListener(id)
a.Srv().platform.RemoveConfigListener(id)
}
// ensurePostActionCookieSecret ensures that the key for encrypting PostActionCookie exists
@@ -449,7 +347,7 @@ func (a *App) LimitedClientConfigWithComputed() map[string]string {
// GetConfigFile proxies access to the given configuration file to the underlying config store.
func (a *App) GetConfigFile(name string) ([]byte, error) {
data, err := a.Srv().configStore.GetFile(name)
data, err := a.Srv().platform.GetConfigFile(name)
if err != nil {
return nil, errors.Wrapf(err, "failed to get config file %s", name)
}
@@ -471,15 +369,9 @@ func (a *App) GetEnvironmentConfig(filter func(reflect.StructField) bool) map[st
return a.EnvironmentConfig(filter)
}
// 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) {
return s.configStore.SaveConfig(newCfg, sendConfigChangeClusterMessage)
}
// SaveConfig replaces the active configuration, optionally notifying cluster peers.
func (a *App) SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bool) (*model.Config, *model.Config, *model.AppError) {
return a.Srv().SaveConfig(newCfg, sendConfigChangeClusterMessage)
return a.Srv().platform.SaveConfig(newCfg, sendConfigChangeClusterMessage)
}
func (a *App) HandleMessageExportConfig(cfg *model.Config, appCfg *model.Config) {
@@ -499,8 +391,8 @@ func (a *App) HandleMessageExportConfig(cfg *model.Config, appCfg *model.Config)
}
func (s *Server) MailServiceConfig() *mail.SMTPConfig {
emailSettings := s.Config().EmailSettings
hostname := utils.GetHostnameFromSiteURL(*s.Config().ServiceSettings.SiteURL)
emailSettings := s.platform.Config().EmailSettings
hostname := utils.GetHostnameFromSiteURL(*s.platform.Config().ServiceSettings.SiteURL)
cfg := mail.SMTPConfig{
Hostname: hostname,
ConnectionSecurity: *emailSettings.ConnectionSecurity,

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

@@ -16,41 +16,6 @@ import (
"github.com/mattermost/mattermost-server/v6/utils"
)
func TestConfigListener(t *testing.T) {
th := Setup(t)
defer th.TearDown()
originalSiteName := th.App.Config().TeamSettings.SiteName
listenerCalled := false
listener := func(oldConfig *model.Config, newConfig *model.Config) {
assert.False(t, listenerCalled, "listener called twice")
assert.Equal(t, *originalSiteName, *oldConfig.TeamSettings.SiteName, "old config contains incorrect site name")
assert.Equal(t, "test123", *newConfig.TeamSettings.SiteName, "new config contains incorrect site name")
listenerCalled = true
}
listenerId := th.App.AddConfigListener(listener)
defer th.App.RemoveConfigListener(listenerId)
listener2Called := false
listener2 := func(oldConfig *model.Config, newConfig *model.Config) {
assert.False(t, listener2Called, "listener2 called twice")
listener2Called = true
}
listener2Id := th.App.AddConfigListener(listener2)
defer th.App.RemoveConfigListener(listener2Id)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.TeamSettings.SiteName = "test123"
})
assert.True(t, listenerCalled, "listener should've been called")
assert.True(t, listener2Called, "listener 2 should've been called")
}
func TestAsymmetricSigningKey(t *testing.T) {
th := SetupWithStoreMock(t)
defer th.TearDown()

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

@@ -35,7 +35,7 @@ func (s *Server) downloadFromURL(downloadURL string) ([]byte, error) {
if err != nil {
return nil, errors.Errorf("failed to parse url %s", downloadURL)
}
if !*s.Config().PluginSettings.AllowInsecureDownloadURL && u.Scheme != "https" {
if !*s.platform.Config().PluginSettings.AllowInsecureDownloadURL && u.Scheme != "https" {
return nil, errors.Errorf("insecure url not allowed %s", downloadURL)
}

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

@@ -87,9 +87,9 @@ func RegisterCloudInterface(f func(*Server) einterfaces.CloudInterface) {
cloudInterface = f
}
var metricsInterface func(*Server) einterfaces.MetricsInterface
var metricsInterface func(*Server, string, string) einterfaces.MetricsInterface
func RegisterMetricsInterface(f func(*Server) einterfaces.MetricsInterface) {
func RegisterMetricsInterface(f func(*Server, string, string) einterfaces.MetricsInterface) {
metricsInterface = f
}

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

@@ -1,118 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package app
import (
"encoding/json"
"os"
"time"
"github.com/mattermost/mattermost-server/v6/app/featureflag"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)
// setupFeatureFlags called on startup and when the cluster leader changes.
// Starts or stops the synchronization of feature flags from upstream management.
func (s *Server) setupFeatureFlags() {
s.featureFlagSynchronizerMutex.Lock()
defer s.featureFlagSynchronizerMutex.Unlock()
splitKey := *s.Config().ServiceSettings.SplitKey
splitConfigured := splitKey != ""
syncFeatureFlags := splitConfigured && s.IsLeader()
s.configStore.SetReadOnlyFF(!splitConfigured)
if syncFeatureFlags {
if err := s.startFeatureFlagUpdateJob(); err != nil {
s.Log.Warn("Unable to setup synchronization with feature flag management. Will fallback to cache.", mlog.Err(err))
}
} else {
s.stopFeatureFlagUpdateJob()
}
if err := s.configStore.Load(); err != nil {
s.Log.Warn("Unable to load config store after feature flag setup.", mlog.Err(err))
}
}
func (s *Server) updateFeatureFlagValuesFromManagement() {
newCfg := s.configStore.GetNoEnv().Clone()
oldFlags := *newCfg.FeatureFlags
newFlags := s.featureFlagSynchronizer.UpdateFeatureFlagValues(oldFlags)
oldFlagsBytes, _ := json.Marshal(oldFlags)
newFlagsBytes, _ := json.Marshal(newFlags)
s.Log.Debug("Checking feature flags from management service", mlog.String("old_flags", string(oldFlagsBytes)), mlog.String("new_flags", string(newFlagsBytes)))
if oldFlags != newFlags {
s.Log.Debug("Feature flag change detected, updating config")
*newCfg.FeatureFlags = newFlags
s.SaveConfig(newCfg, true)
}
}
func (s *Server) startFeatureFlagUpdateJob() error {
// Can be run multiple times
if s.featureFlagSynchronizer != nil {
return nil
}
var log *mlog.Logger
if *s.Config().ServiceSettings.DebugSplit {
log = s.Log
}
attributes := map[string]any{}
// if we are part of a cloud installation, add its installation and group id
if installationId := os.Getenv("MM_CLOUD_INSTALLATION_ID"); installationId != "" {
attributes["installation_id"] = installationId
}
if groupId := os.Getenv("MM_CLOUD_GROUP_ID"); groupId != "" {
attributes["group_id"] = groupId
}
synchronizer, err := featureflag.NewSynchronizer(featureflag.SyncParams{
ServerID: s.TelemetryId(),
SplitKey: *s.Config().ServiceSettings.SplitKey,
Log: log,
Attributes: attributes,
})
if err != nil {
return err
}
s.featureFlagStop = make(chan struct{})
s.featureFlagStopped = make(chan struct{})
s.featureFlagSynchronizer = synchronizer
syncInterval := *s.Config().ServiceSettings.FeatureFlagSyncIntervalSeconds
go func() {
ticker := time.NewTicker(time.Duration(syncInterval) * time.Second)
defer ticker.Stop()
defer close(s.featureFlagStopped)
if err := synchronizer.EnsureReady(); err != nil {
s.Log.Warn("Problem connecting to feature flag management. Will fallback to cloud cache.", mlog.Err(err))
return
}
s.updateFeatureFlagValuesFromManagement()
for {
select {
case <-s.featureFlagStop:
return
case <-ticker.C:
s.updateFeatureFlagValuesFromManagement()
}
}
}()
return nil
}
func (s *Server) stopFeatureFlagUpdateJob() {
if s.featureFlagSynchronizer != nil {
close(s.featureFlagStop)
<-s.featureFlagStopped
s.featureFlagSynchronizer.Close()
s.featureFlagSynchronizer = nil
}
}

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

@@ -191,7 +191,7 @@ func (a *App) writeLdapFile(filename string, fileData *multipart.FileHeader) *mo
return model.NewAppError("AddLdapCertificate", "api.admin.add_certificate.saving.app_error", nil, err.Error(), http.StatusInternalServerError)
}
err = a.Srv().configStore.SetFile(filename, data)
err = a.Srv().platform.SetConfigFile(filename, data)
if err != nil {
return model.NewAppError("AddLdapCertificate", "api.admin.add_certificate.saving.app_error", nil, err.Error(), http.StatusInternalServerError)
}
@@ -234,7 +234,7 @@ func (a *App) AddLdapPrivateCertificate(fileData *multipart.FileHeader) *model.A
}
func (a *App) removeLdapFile(filename string) *model.AppError {
if err := a.Srv().configStore.RemoveFile(filename); err != nil {
if err := a.Srv().platform.RemoveConfigFile(filename); err != nil {
return model.NewAppError("RemoveLdapFile", "api.admin.remove_certificate.delete.app_error", map[string]any{"Filename": filename}, err.Error(), http.StatusInternalServerError)
}
return nil

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

@@ -48,7 +48,7 @@ func (w *licenseWrapper) GetLicense() *model.License {
}
func (w *licenseWrapper) RequestTrialLicense(requesterID string, users int, termsAccepted bool, receiveEmailsAccepted bool) *model.AppError {
if *w.srv.Config().ExperimentalSettings.RestrictSystemAdmin {
if *w.srv.platform.Config().ExperimentalSettings.RestrictSystemAdmin {
return model.NewAppError("RequestTrialLicense", "api.restricted_system_admin", nil, "", http.StatusForbidden)
}
@@ -75,8 +75,8 @@ func (w *licenseWrapper) RequestTrialLicense(requesterID string, users int, term
ServerID: w.srv.TelemetryId(),
Name: requester.GetDisplayName(model.ShowFullName),
Email: requester.Email,
SiteName: *w.srv.Config().TeamSettings.SiteName,
SiteURL: *w.srv.Config().ServiceSettings.SiteURL,
SiteName: *w.srv.platform.Config().TeamSettings.SiteName,
SiteURL: *w.srv.platform.Config().ServiceSettings.SiteURL,
Users: users,
TermsAccepted: termsAccepted,
ReceiveEmailsAccepted: receiveEmailsAccepted,
@@ -93,6 +93,11 @@ type JWTClaims struct {
jwt.StandardClaims
}
func (s *Server) License() *model.License {
license, _ := s.licenseValue.Load().(*model.License)
return license
}
func (s *Server) LoadLicense() {
// ENV var overrides all other sources of license.
licenseStr := os.Getenv(LicenseEnv)
@@ -131,7 +136,7 @@ func (s *Server) LoadLicense() {
if !model.IsValidId(licenseId) {
// Lets attempt to load the file from disk since it was missing from the DB
license, licenseBytes := utils.GetAndValidateLicenseFileFromDisk(*s.Config().ServiceSettings.LicenseFileLocation)
license, licenseBytes := utils.GetAndValidateLicenseFileFromDisk(*s.platform.Config().ServiceSettings.LicenseFileLocation)
if license != nil {
if _, err := s.SaveLicense(licenseBytes); err != nil {
@@ -177,13 +182,13 @@ func (s *Server) SaveLicense(licenseBytes []byte) (*model.License, *model.AppErr
return nil, model.NewAppError("addLicense", model.ExpiredLicenseError, nil, "", http.StatusBadRequest)
}
if *s.Config().JobSettings.RunJobs && s.Jobs != nil {
if *s.platform.Config().JobSettings.RunJobs && s.Jobs != nil {
if err := s.Jobs.StopWorkers(); err != nil && !errors.Is(err, jobs.ErrWorkersNotRunning) {
mlog.Warn("Stopping job server workers failed", mlog.Err(err))
}
}
if *s.Config().JobSettings.RunScheduler && s.Jobs != nil {
if *s.platform.Config().JobSettings.RunScheduler && s.Jobs != nil {
if err := s.Jobs.StopSchedulers(); err != nil && !errors.Is(err, jobs.ErrSchedulersNotRunning) {
mlog.Error("Stopping job server schedulers failed", mlog.Err(err))
}
@@ -193,12 +198,12 @@ func (s *Server) SaveLicense(licenseBytes []byte) (*model.License, *model.AppErr
// restart job server workers - this handles the edge case where a license file is uploaded, but the job server
// doesn't start until the server is restarted, which prevents the 'run job now' buttons in system console from
// functioning as expected
if *s.Config().JobSettings.RunJobs && s.Jobs != nil {
if *s.platform.Config().JobSettings.RunJobs && s.Jobs != nil {
if err := s.Jobs.StartWorkers(); err != nil {
mlog.Error("Starting job server workers failed", mlog.Err(err))
}
}
if *s.Config().JobSettings.RunScheduler && s.Jobs != nil {
if *s.platform.Config().JobSettings.RunScheduler && s.Jobs != nil {
if err := s.Jobs.StartSchedulers(); err != nil && !errors.Is(err, jobs.ErrSchedulersRunning) {
mlog.Error("Starting job server schedulers failed", mlog.Err(err))
}
@@ -233,7 +238,7 @@ func (s *Server) SaveLicense(licenseBytes []byte) (*model.License, *model.AppErr
return nil, model.NewAppError("addLicense", "api.license.add_license.save_active.app_error", nil, "", http.StatusInternalServerError)
}
s.ReloadConfig()
s.platform.ReloadConfig()
s.InvalidateAllCaches()
return &license, nil
@@ -256,12 +261,20 @@ func (s *Server) SetLicense(license *model.License) bool {
license.Features.SetDefaults()
s.licenseValue.Store(license)
if s.platform != nil {
s.platform.SetLicense(license)
}
s.clientLicenseValue.Store(utils.GetClientLicense(license))
return true
}
s.licenseValue.Store((*model.License)(nil))
s.clientLicenseValue.Store(map[string]string(nil))
if s.platform != nil {
s.platform.SetLicense((*model.License)(nil))
}
return false
}
@@ -307,7 +320,7 @@ func (s *Server) RemoveLicense() *model.AppError {
}
s.SetLicense(nil)
s.ReloadConfig()
s.platform.ReloadConfig()
s.InvalidateAllCaches()
return nil
@@ -360,7 +373,7 @@ func (s *Server) RequestTrialLicense(trialRequest *model.TrialLicenseRequest) *m
return err
}
s.ReloadConfig()
s.platform.ReloadConfig()
s.InvalidateAllCaches()
return nil

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

@@ -69,9 +69,9 @@ func (s *Server) doAdvancedPermissionsMigration() {
return
}
config := s.Config()
config := s.platform.Config()
*config.ServiceSettings.PostEditTimeLimit = -1
if _, _, err := s.SaveConfig(config, true); err != nil {
if _, _, err := s.platform.SaveConfig(config, true); err != nil {
mlog.Error("Failed to update config in Advanced Permissions Phase 1 Migration.", mlog.Err(err))
}
@@ -327,7 +327,7 @@ func (s *Server) doContentExtractionConfigDefaultTrueMigration() {
return
}
s.UpdateConfig(func(config *model.Config) {
s.platform.UpdateConfig(func(config *model.Config) {
config.FileSettings.ExtractContent = model.NewBool(true)
})
@@ -474,7 +474,7 @@ const existingInstallationPostsThreshold = 10
func (s *Server) doFirstAdminSetupCompleteMigration() {
// Don't run the migration until the flag is turned on.
if !s.Config().FeatureFlags.UseCaseOnboarding {
if !s.platform.Config().FeatureFlags.UseCaseOnboarding {
return
}

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

@@ -290,7 +290,7 @@ func (a *App) UpdateMobileAppBadge(userID string) {
}
func (s *Server) createPushNotificationsHub(c request.CTX) {
buffer := *s.Config().EmailSettings.PushNotificationBuffer
buffer := *s.platform.Config().EmailSettings.PushNotificationBuffer
hub := PushNotificationsHub{
notificationsChan: make(chan PushNotification, buffer),
app: New(ServerConnector(s.Channels())),

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

@@ -17,6 +17,7 @@ import (
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost-server/v6/app/platform"
"github.com/mattermost/mattermost-server/v6/config"
"github.com/mattermost/mattermost-server/v6/model"
fmocks "github.com/mattermost/mattermost-server/v6/shared/filestore/mocks"
@@ -1443,9 +1444,13 @@ func TestPushNotificationRace(t *testing.T) {
Router: mux.NewRouter(),
filestore: &fmocks.FileBackend{},
}
s.configStore = &configWrapper{srv: s, Store: memoryStore}
var err error
s.platform, err = platform.New(platform.ServiceConfig{
ConfigStore: memoryStore,
})
require.NoError(t, err)
serviceMap := map[ServiceKey]any{
ConfigKey: s.configStore,
ConfigKey: s.platform,
LicenseKey: &licenseWrapper{s},
FilestoreKey: s.filestore,
}

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

@@ -6,6 +6,7 @@ package app
import (
"github.com/pkg/errors"
"github.com/mattermost/mattermost-server/v6/app/platform"
"github.com/mattermost/mattermost-server/v6/config"
"github.com/mattermost/mattermost-server/v6/einterfaces"
"github.com/mattermost/mattermost-server/v6/model"
@@ -52,7 +53,22 @@ func Config(dsn string, readOnly bool, configDefaults *model.Config) Option {
return errors.Wrap(err, "failed to apply Config option")
}
s.configStore = &configWrapper{srv: s, Store: configStore}
platformCfg := platform.ServiceConfig{
ConfigStore: configStore,
Logger: s.Log,
StartMetrics: s.startMetrics,
Cluster: s.Cluster,
}
if metricsInterface != nil {
platformCfg.Metrics = metricsInterface(s, *configStore.Get().SqlSettings.DriverName, *configStore.Get().SqlSettings.DataSource)
}
ps, sErr := platform.New(platformCfg)
if sErr != nil {
return errors.Wrap(sErr, "failed to initialize platform")
}
s.platform = ps
return nil
}
}
@@ -60,7 +76,21 @@ 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 = &configWrapper{srv: s, Store: configStore}
platformCfg := platform.ServiceConfig{
ConfigStore: configStore,
Logger: s.Log,
StartMetrics: s.startMetrics,
Cluster: s.Cluster,
}
if metricsInterface != nil {
platformCfg.Metrics = metricsInterface(s, *configStore.Get().SqlSettings.DriverName, *configStore.Get().SqlSettings.DataSource)
}
ps, sErr := platform.New(platformCfg)
if sErr != nil {
return errors.Wrap(sErr, "failed to initialize platform")
}
s.platform = ps
return nil
}

12
app/platform/cluster.go Обычный файл
Просмотреть файл

@@ -0,0 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package platform
func (ps *PlatformService) IsLeader() bool {
if ps.License() != nil && *ps.Config().ClusterSettings.Enable && ps.cluster != nil {
return ps.cluster.IsLeader()
}
return true
}

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

@@ -5,9 +5,14 @@ package platform
import (
"errors"
"fmt"
"net/http"
"reflect"
"github.com/mattermost/mattermost-server/v6/config"
"github.com/mattermost/mattermost-server/v6/einterfaces"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/product"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)
@@ -30,7 +35,145 @@ func (c *ServiceConfig) validate() error {
}
if c.Logger == nil {
return errors.New("Logger is required")
var err error
// If Logger is not set, use a default logger temporarily.
// this should be removed once the logger is properly configured with the service config.
// MM-45841
c.Logger, err = mlog.NewLogger()
if err != nil {
return err
}
}
return nil
}
// ensure the config wrapper implements `product.ConfigService`
var _ product.ConfigService = (*PlatformService)(nil)
func (ps *PlatformService) Config() *model.Config {
return ps.configStore.Get()
}
// Registers a function with a given listener 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 (ps *PlatformService) AddConfigListener(listener func(*model.Config, *model.Config)) string {
return ps.configStore.AddListener(listener)
}
func (ps *PlatformService) RemoveConfigListener(id string) {
ps.configStore.RemoveListener(id)
}
func (ps *PlatformService) UpdateConfig(f func(*model.Config)) {
if ps.configStore.IsReadOnly() {
return
}
old := ps.Config()
updated := old.Clone()
f(updated)
if _, _, err := ps.configStore.Set(updated); err != nil {
ps.logger.Error("Failed to update config", mlog.Err(err))
}
}
// SaveConfig replaces the active configuration, optionally notifying cluster peers.
// It returns both the previous and current configs.
func (ps *PlatformService) SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bool) (*model.Config, *model.Config, *model.AppError) {
oldCfg, newCfg, err := ps.configStore.Set(newCfg)
if errors.Is(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 ps.serviceConfig.StartMetrics && *ps.Config().MetricsSettings.Enable {
ps.RestartMetrics()
} else {
ps.ShutdownMetrics()
}
if ps.cluster != nil {
err := ps.cluster.ConfigChanged(ps.configStore.RemoveEnvironmentOverrides(oldCfg),
ps.configStore.RemoveEnvironmentOverrides(newCfg), sendConfigChangeClusterMessage)
if err != nil {
return nil, nil, err
}
}
return oldCfg, newCfg, nil
}
func (ps *PlatformService) ReloadConfig() error {
if err := ps.configStore.Load(); err != nil {
return err
}
return nil
}
func (ps *PlatformService) GetEnvironmentOverridesWithFilter(filter func(reflect.StructField) bool) map[string]interface{} {
return ps.configStore.GetEnvironmentOverridesWithFilter(filter)
}
func (ps *PlatformService) GetEnvironmentOverrides() map[string]interface{} {
return ps.configStore.GetEnvironmentOverrides()
}
func (ps *PlatformService) DescribeConfig() string {
return ps.configStore.String()
}
func (ps *PlatformService) CleanUpConfig() error {
return ps.configStore.CleanUp()
}
// ConfigureLogger applies the specified configuration to a logger.
func (ps *PlatformService) ConfigureLogger(name string, logger *mlog.Logger, logSettings *model.LogSettings, getPath func(string) string) error {
// Advanced logging is E20 only, however logging must be initialized before the license
// file is loaded. If no valid E20 license exists then advanced logging will be
// shutdown once license is loaded/checked.
var err error
dsn := *logSettings.AdvancedLoggingConfig
var logConfigSrc config.LogConfigSrc
if dsn != "" {
logConfigSrc, err = config.NewLogConfigSrc(dsn, ps.configStore)
if err != nil {
return fmt.Errorf("invalid config source for %s, %w", name, err)
}
ps.logger.Info("Loaded configuration for "+name, mlog.String("source", dsn))
}
cfg, err := config.MloggerConfigFromLoggerConfig(logSettings, logConfigSrc, getPath)
if err != nil {
return fmt.Errorf("invalid config source for %s, %w", name, err)
}
if err := logger.ConfigureTargets(cfg, nil); err != nil {
return fmt.Errorf("invalid config for %s, %w", name, err)
}
return nil
}
func (ps *PlatformService) GetConfigStore() *config.Store {
return ps.configStore
}
func (ps *PlatformService) GetConfigFile(name string) ([]byte, error) {
return ps.configStore.GetFile(name)
}
func (ps *PlatformService) SetConfigFile(name string, data []byte) error {
return ps.configStore.SetFile(name, data)
}
func (ps *PlatformService) RemoveConfigFile(name string) error {
return ps.configStore.RemoveFile(name)
}
func (ps *PlatformService) HasConfigFile(name string) (bool, error) {
return ps.configStore.HasFile(name)
}
func (ps *PlatformService) SetConfigReadOnlyFF(readOnly bool) {
ps.configStore.SetReadOnlyFF(readOnly)
}

47
app/platform/config_test.go Обычный файл
Просмотреть файл

@@ -0,0 +1,47 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package platform
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/mattermost/mattermost-server/v6/model"
)
func TestConfigListener(t *testing.T) {
th := Setup(t)
defer th.TearDown()
originalSiteName := th.Service.Config().TeamSettings.SiteName
listenerCalled := false
listener := func(oldConfig *model.Config, newConfig *model.Config) {
assert.False(t, listenerCalled, "listener called twice")
assert.Equal(t, *originalSiteName, *oldConfig.TeamSettings.SiteName, "old config contains incorrect site name")
assert.Equal(t, "test123", *newConfig.TeamSettings.SiteName, "new config contains incorrect site name")
listenerCalled = true
}
listenerId := th.Service.AddConfigListener(listener)
defer th.Service.RemoveConfigListener(listenerId)
listener2Called := false
listener2 := func(oldConfig *model.Config, newConfig *model.Config) {
assert.False(t, listener2Called, "listener2 called twice")
listener2Called = true
}
listener2Id := th.Service.AddConfigListener(listener2)
defer th.Service.RemoveConfigListener(listener2Id)
th.Service.UpdateConfig(func(cfg *model.Config) {
*cfg.TeamSettings.SiteName = "test123"
})
assert.True(t, listenerCalled, "listener should've been called")
assert.True(t, listener2Called, "listener 2 should've been called")
}

118
app/platform/feature_flags.go Обычный файл
Просмотреть файл

@@ -0,0 +1,118 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package platform
import (
"encoding/json"
"os"
"time"
"github.com/mattermost/mattermost-server/v6/app/featureflag"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)
// SetupFeatureFlags called on startup and when the cluster leader changes.
// Starts or stops the synchronization of feature flags from upstream management.
func (ps *PlatformService) SetupFeatureFlags() {
ps.featureFlagSynchronizerMutex.Lock()
defer ps.featureFlagSynchronizerMutex.Unlock()
splitKey := *ps.Config().ServiceSettings.SplitKey
splitConfigured := splitKey != ""
syncFeatureFlags := splitConfigured && ps.IsLeader()
ps.configStore.SetReadOnlyFF(!splitConfigured)
if syncFeatureFlags {
if err := ps.startFeatureFlagUpdateJob(); err != nil {
ps.logger.Warn("Unable to setup synchronization with feature flag management. Will fallback to cache.", mlog.Err(err))
}
} else {
ps.StopFeatureFlagUpdateJob()
}
if err := ps.configStore.Load(); err != nil {
ps.logger.Warn("Unable to load config store after feature flag setup.", mlog.Err(err))
}
}
func (ps *PlatformService) updateFeatureFlagValuesFromManagement() {
newCfg := ps.configStore.GetNoEnv().Clone()
oldFlags := *newCfg.FeatureFlags
newFlags := ps.featureFlagSynchronizer.UpdateFeatureFlagValues(oldFlags)
oldFlagsBytes, _ := json.Marshal(oldFlags)
newFlagsBytes, _ := json.Marshal(newFlags)
ps.logger.Debug("Checking feature flags from management service", mlog.String("old_flags", string(oldFlagsBytes)), mlog.String("new_flags", string(newFlagsBytes)))
if oldFlags != newFlags {
ps.logger.Debug("Feature flag change detected, updating config")
*newCfg.FeatureFlags = newFlags
ps.SaveConfig(newCfg, true)
}
}
func (ps *PlatformService) startFeatureFlagUpdateJob() error {
// Can be run multiple times
if ps.featureFlagSynchronizer != nil {
return nil
}
var log *mlog.Logger
if *ps.Config().ServiceSettings.DebugSplit {
log = ps.logger
}
attributes := map[string]any{}
// if we are part of a cloud installation, add its installation and group id
if installationId := os.Getenv("MM_CLOUD_INSTALLATION_ID"); installationId != "" {
attributes["installation_id"] = installationId
}
if groupId := os.Getenv("MM_CLOUD_GROUP_ID"); groupId != "" {
attributes["group_id"] = groupId
}
synchronizer, err := featureflag.NewSynchronizer(featureflag.SyncParams{
ServerID: ps.telemetryId,
SplitKey: *ps.Config().ServiceSettings.SplitKey,
Log: log,
Attributes: attributes,
})
if err != nil {
return err
}
ps.featureFlagStop = make(chan struct{})
ps.featureFlagStopped = make(chan struct{})
ps.featureFlagSynchronizer = synchronizer
syncInterval := *ps.Config().ServiceSettings.FeatureFlagSyncIntervalSeconds
go func() {
ticker := time.NewTicker(time.Duration(syncInterval) * time.Second)
defer ticker.Stop()
defer close(ps.featureFlagStopped)
if err := synchronizer.EnsureReady(); err != nil {
ps.logger.Warn("Problem connecting to feature flag management. Will fallback to cloud cache.", mlog.Err(err))
return
}
ps.updateFeatureFlagValuesFromManagement()
for {
select {
case <-ps.featureFlagStop:
return
case <-ticker.C:
ps.updateFeatureFlagValuesFromManagement()
}
}
}()
return nil
}
func (ps *PlatformService) StopFeatureFlagUpdateJob() {
if ps.featureFlagSynchronizer != nil {
close(ps.featureFlagStop)
<-ps.featureFlagStopped
ps.featureFlagSynchronizer.Close()
ps.featureFlagSynchronizer = nil
}
}

87
app/platform/helper_test.go Обычный файл
Просмотреть файл

@@ -0,0 +1,87 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package platform
import (
"io/ioutil"
"path/filepath"
"testing"
"github.com/mattermost/mattermost-server/v6/config"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/store"
)
type TestHelper struct {
Service *PlatformService
}
func Setup(tb testing.TB) *TestHelper {
if testing.Short() {
tb.SkipNow()
}
dbStore := mainHelper.GetStore()
dbStore.DropAllTables()
dbStore.MarkSystemRanUnitTests()
mainHelper.PreloadMigrations()
return setupTestHelper(dbStore, false, true, tb)
}
func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer bool, tb testing.TB) *TestHelper {
tempWorkspace, err := ioutil.TempDir("", "apptest")
if err != nil {
panic(err)
}
configStore := config.NewTestMemoryStore()
memoryConfig := configStore.Get()
*memoryConfig.PluginSettings.Directory = filepath.Join(tempWorkspace, "plugins")
*memoryConfig.PluginSettings.ClientDirectory = filepath.Join(tempWorkspace, "webapp")
*memoryConfig.PluginSettings.AutomaticPrepackagedPlugins = false
*memoryConfig.LogSettings.EnableSentry = false // disable error reporting during tests
*memoryConfig.AnnouncementSettings.AdminNoticesEnabled = false
*memoryConfig.AnnouncementSettings.UserNoticesEnabled = false
configStore.Set(memoryConfig)
ps, err := New(ServiceConfig{
ConfigStore: configStore,
})
if err != nil {
panic(err)
}
th := &TestHelper{
Service: ps,
}
// Share same configuration with app.TestHelper
th.Service.UpdateConfig(func(cfg *model.Config) {
*cfg.TeamSettings.MaxUsersPerTeam = 50
*cfg.RateLimitSettings.Enable = false
*cfg.TeamSettings.EnableOpenServer = true
})
// Disable strict password requirements for test
th.Service.UpdateConfig(func(cfg *model.Config) {
*cfg.PasswordSettings.MinimumLength = 5
*cfg.PasswordSettings.Lowercase = false
*cfg.PasswordSettings.Uppercase = false
*cfg.PasswordSettings.Symbol = false
*cfg.PasswordSettings.Number = false
})
if enterprise {
th.Service.SetLicense(model.NewTestLicense())
} else {
th.Service.SetLicense(nil)
}
return th
}
func (th *TestHelper) TearDown() {
// Add cleaning code here
}

32
app/platform/main_test.go Обычный файл
Просмотреть файл

@@ -0,0 +1,32 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package platform
import (
"flag"
"testing"
"github.com/mattermost/mattermost-server/v6/testlib"
)
var mainHelper *testlib.MainHelper
var replicaFlag bool
func TestMain(m *testing.M) {
if f := flag.Lookup("mysql-replica"); f == nil {
flag.BoolVar(&replicaFlag, "mysql-replica", false, "")
flag.Parse()
}
var options = testlib.HelperOptions{
EnableStore: true,
EnableResources: true,
WithReadReplica: replicaFlag,
}
mainHelper = testlib.NewMainHelperWithOptions(&options)
defer mainHelper.Close()
mainHelper.Main(m)
}

19
app/platform/server_license.go Обычный файл
Просмотреть файл

@@ -0,0 +1,19 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package platform
import (
"github.com/mattermost/mattermost-server/v6/model"
)
// License returns the license stored in the server struct.
// This should be removed with MM-45839
func (ps *PlatformService) License() *model.License {
license, _ := ps.licenseValue.Load().(*model.License)
return license
}
func (ps *PlatformService) SetLicense(license *model.License) {
ps.licenseValue.Store(license)
}

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

@@ -4,6 +4,11 @@
package platform
import (
"fmt"
"sync"
"sync/atomic"
"github.com/mattermost/mattermost-server/v6/app/featureflag"
"github.com/mattermost/mattermost-server/v6/config"
"github.com/mattermost/mattermost-server/v6/einterfaces"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
@@ -19,6 +24,14 @@ type PlatformService struct {
metrics *platformMetrics
featureFlagSynchronizerMutex sync.Mutex
featureFlagSynchronizer *featureflag.Synchronizer
featureFlagStop chan struct{}
featureFlagStopped chan struct{}
licenseValue atomic.Value
telemetryId string
cluster einterfaces.ClusterInterface
}
@@ -49,3 +62,18 @@ func (ps *PlatformService) ShutdownMetrics() error {
return nil
}
func (ps *PlatformService) ShutdownConfig() error {
if ps.configStore != nil {
err := ps.configStore.Close()
if err != nil {
return fmt.Errorf("failed to close config store: %w", err)
}
}
return nil
}
func (ps *PlatformService) SetTelemetryId(id string) {
ps.telemetryId = id
}

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

@@ -25,7 +25,7 @@ func (a *App) GetPublicKey(name string) ([]byte, *model.AppError) {
}
func (s *Server) getPublicKey(name string) ([]byte, *model.AppError) {
data, err := s.configStore.GetFile(name)
data, err := s.platform.GetConfigFile(name)
if err != nil {
return nil, model.NewAppError("GetPublicKey", "app.plugin.get_public_key.get_file.app_error", nil, err.Error(), http.StatusInternalServerError)
}
@@ -41,7 +41,7 @@ func (a *App) AddPublicKey(name string, key io.Reader) *model.AppError {
if err != nil {
return model.NewAppError("AddPublicKey", "app.plugin.write_file.read.app_error", nil, err.Error(), http.StatusInternalServerError)
}
err = a.Srv().configStore.SetFile(name, data)
err = a.Srv().platform.SetConfigFile(name, data)
if err != nil {
return model.NewAppError("AddPublicKey", "app.plugin.write_file.saving.app_error", nil, err.Error(), http.StatusInternalServerError)
}
@@ -61,7 +61,7 @@ func (a *App) DeletePublicKey(name string) *model.AppError {
return model.NewAppError("AddPublicKey", "app.plugin.modify_saml.app_error", nil, "", http.StatusInternalServerError)
}
filename := filepath.Base(name)
if err := a.Srv().configStore.RemoveFile(filename); err != nil {
if err := a.Srv().platform.RemoveConfigFile(filename); err != nil {
return model.NewAppError("DeletePublicKey", "app.plugin.delete_public_key.delete.app_error", nil, err.Error(), http.StatusInternalServerError)
}

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

@@ -41,7 +41,7 @@ var linkCache = cache.NewLRU(cache.LRUOptions{
func (s *Server) initPostMetadata() {
// Dump any cached links if the proxy settings have changed so image URLs can be updated
s.AddConfigListener(func(before, after *model.Config) {
s.platform.AddConfigListener(func(before, after *model.Config) {
if (before.ImageProxySettings.Enable != after.ImageProxySettings.Enable) ||
(before.ImageProxySettings.ImageProxyType != after.ImageProxySettings.ImageProxyType) ||
(before.ImageProxySettings.RemoteImageProxyURL != after.ImageProxySettings.RemoteImageProxyURL) ||

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

@@ -796,7 +796,7 @@ func TestPreparePostForClientWithImageProxy(t *testing.T) {
*cfg.ImageProxySettings.RemoteImageProxyOptions = "foo"
})
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server, th.Server.HTTPService(), th.Server.Log)
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log)
return th
}

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

@@ -472,7 +472,7 @@ func TestImageProxy(t *testing.T) {
*cfg.ServiceSettings.SiteURL = "http://mymattermost.com"
})
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server, th.Server.HTTPService(), th.Server.Log)
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log)
for name, tc := range map[string]struct {
ProxyType string
@@ -686,7 +686,7 @@ func TestCreatePost(t *testing.T) {
*cfg.ImageProxySettings.RemoteImageProxyOptions = "foo"
})
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server, th.Server.HTTPService(), th.Server.Log)
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log)
imageURL := "http://mydomain.com/myimage"
proxiedImageURL := "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage"
@@ -956,7 +956,7 @@ func TestPatchPost(t *testing.T) {
*cfg.ImageProxySettings.RemoteImageProxyOptions = "foo"
})
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server, th.Server.HTTPService(), th.Server.Log)
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log)
imageURL := "http://mydomain.com/myimage"
proxiedImageURL := "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage"
@@ -1252,7 +1252,7 @@ func TestUpdatePost(t *testing.T) {
*cfg.ImageProxySettings.RemoteImageProxyOptions = "foo"
})
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server, th.Server.HTTPService(), th.Server.Log)
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log)
imageURL := "http://mydomain.com/myimage"
proxiedImageURL := "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage"

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

@@ -90,8 +90,8 @@ func TestGetTopReactionsForTeamSince(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
th.Server.configStore.SetReadOnlyFF(false)
defer th.Server.configStore.SetReadOnlyFF(true)
th.Server.platform.SetConfigReadOnlyFF(false)
defer th.Server.platform.SetConfigReadOnlyFF(true)
userId := th.BasicUser.Id
user2Id := th.BasicUser2.Id
@@ -261,8 +261,8 @@ func TestGetTopReactionsForUserSince(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
th.Server.configStore.SetReadOnlyFF(false)
defer th.Server.configStore.SetReadOnlyFF(true)
th.Server.platform.SetConfigReadOnlyFF(false)
defer th.Server.platform.SetConfigReadOnlyFF(true)
userId := th.BasicUser.Id

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

@@ -47,7 +47,7 @@ func (a *App) writeSamlFile(filename string, fileData *multipart.FileHeader) *mo
return model.NewAppError("AddSamlCertificate", "api.admin.add_certificate.saving.app_error", nil, err.Error(), http.StatusInternalServerError)
}
err = a.Srv().configStore.SetFile(filename, data)
err = a.Srv().platform.SetConfigFile(filename, data)
if err != nil {
return model.NewAppError("AddSamlCertificate", "api.admin.add_certificate.saving.app_error", nil, err.Error(), http.StatusInternalServerError)
}
@@ -107,7 +107,7 @@ func (a *App) AddSamlIdpCertificate(fileData *multipart.FileHeader) *model.AppEr
}
func (a *App) removeSamlFile(filename string) *model.AppError {
if err := a.Srv().configStore.RemoveFile(filename); err != nil {
if err := a.Srv().platform.RemoveConfigFile(filename); err != nil {
return model.NewAppError("RemoveSamlFile", "api.admin.remove_certificate.delete.app_error", map[string]any{"Filename": filename}, err.Error(), http.StatusInternalServerError)
}
@@ -171,9 +171,9 @@ func (a *App) RemoveSamlIdpCertificate() *model.AppError {
func (a *App) GetSamlCertificateStatus() *model.SamlCertificateStatus {
status := &model.SamlCertificateStatus{}
status.IdpCertificateFile, _ = a.Srv().configStore.HasFile(*a.Config().SamlSettings.IdpCertificateFile)
status.PrivateKeyFile, _ = a.Srv().configStore.HasFile(*a.Config().SamlSettings.PrivateKeyFile)
status.PublicCertificateFile, _ = a.Srv().configStore.HasFile(*a.Config().SamlSettings.PublicCertificateFile)
status.IdpCertificateFile, _ = a.Srv().platform.HasConfigFile(*a.Config().SamlSettings.IdpCertificateFile)
status.PrivateKeyFile, _ = a.Srv().platform.HasConfigFile(*a.Config().SamlSettings.PrivateKeyFile)
status.PublicCertificateFile, _ = a.Srv().platform.HasConfigFile(*a.Config().SamlSettings.PublicCertificateFile)
return status
}
@@ -267,7 +267,7 @@ func (a *App) SetSamlIdpCertificateFromMetadata(data []byte) *model.AppError {
Bytes: block.Bytes,
})
if err := a.Srv().configStore.SetFile(SamlIdpCertificateName, data); err != nil {
if err := a.Srv().platform.SetConfigFile(SamlIdpCertificateName, data); err != nil {
return model.NewAppError("SetSamlIdpCertificateFromMetadata", "api.admin.saml.failure_save_idp_certificate_file.app_error", nil, err.Error(), http.StatusInternalServerError)
}

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

@@ -33,7 +33,7 @@ const (
)
func (s *Server) DoSecurityUpdateCheck() {
if !*s.Config().ServiceSettings.EnableSecurityFixAlert {
if !*s.platform.Config().ServiceSettings.EnableSecurityFixAlert {
return
}
@@ -53,7 +53,7 @@ func (s *Server) DoSecurityUpdateCheck() {
v.Set(PropSecurityID, s.TelemetryId())
v.Set(PropSecurityBuild, model.CurrentVersion+"."+model.BuildNumber)
v.Set(PropSecurityEnterpriseReady, model.BuildEnterpriseReady)
v.Set(PropSecurityDatabase, *s.Config().SqlSettings.DriverName)
v.Set(PropSecurityDatabase, *s.platform.Config().SqlSettings.DriverName)
v.Set(PropSecurityOS, runtime.GOOS)
if props[model.SystemRanUnitTests] != "" {

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

@@ -31,7 +31,6 @@ import (
"golang.org/x/crypto/acme/autocert"
"github.com/mattermost/mattermost-server/v6/app/email"
"github.com/mattermost/mattermost-server/v6/app/featureflag"
"github.com/mattermost/mattermost-server/v6/app/platform"
"github.com/mattermost/mattermost-server/v6/app/request"
"github.com/mattermost/mattermost-server/v6/app/teams"
@@ -168,7 +167,6 @@ type Server struct {
searchConfigListenerId string
searchLicenseListenerId string
loggerLicenseListenerId string
configStore *configWrapper
filestore filestore.FileBackend
platform *platform.PlatformService
@@ -201,11 +199,6 @@ type Server struct {
tracer *tracing.Tracer
featureFlagSynchronizer *featureflag.Synchronizer
featureFlagStop chan struct{}
featureFlagStopped chan struct{}
featureFlagSynchronizerMutex sync.Mutex
products map[string]Product
}
@@ -237,7 +230,7 @@ func NewServer(options ...Option) (*Server, error) {
// and has dependency requirements with the previous step.
//
// Step 1: Config.
if s.configStore == nil {
if s.platform == nil {
innerStore, err := config.NewFileStore("config.json", true)
if err != nil {
return nil, errors.Wrap(err, "failed to load config")
@@ -247,7 +240,25 @@ func NewServer(options ...Option) (*Server, error) {
return nil, errors.Wrap(err, "failed to load config")
}
s.configStore = &configWrapper{srv: s, Store: configStore}
platformCfg := platform.ServiceConfig{
ConfigStore: configStore,
Logger: s.Log,
StartMetrics: s.startMetrics,
Cluster: s.Cluster,
}
if metricsInterface != nil {
platformCfg.Metrics = metricsInterface(s, *configStore.Get().SqlSettings.DriverName, *configStore.Get().SqlSettings.DataSource)
}
ps, sErr := platform.New(platformCfg)
if sErr != nil {
return nil, errors.Wrap(sErr, "failed to initialize platform")
}
s.platform = ps
if s.licenseValue.Load() != nil {
ps.SetLicense(s.licenseValue.Load().(*model.License)) // in case license is set in server options
}
}
// Step 2: Logging
@@ -255,7 +266,7 @@ func NewServer(options ...Option) (*Server, error) {
mlog.Error("Could not initiate logging", mlog.Err(err))
}
subpath, err := utils.GetSubpathFromConfig(s.Config())
subpath, err := utils.GetSubpathFromConfig(s.platform.Config())
if err != nil {
return nil, errors.Wrap(err, "failed to parse SiteURL subpath")
}
@@ -264,12 +275,12 @@ func NewServer(options ...Option) (*Server, error) {
// This is called after initLogging() to avoid a race condition.
mlog.Info("Server is initializing...", mlog.String("go_version", runtime.Version()))
s.httpService = httpservice.MakeHTTPService(s)
s.httpService = httpservice.MakeHTTPService(s.platform)
// Step 3: Search Engine
// Depends on Step 1 (config).
searchEngine := searchengine.NewBroker(s.Config())
bleveEngine := bleveengine.NewBleveEngine(s.Config())
searchEngine := searchengine.NewBroker(s.platform.Config())
bleveEngine := bleveengine.NewBleveEngine(s.platform.Config())
if err := bleveEngine.Start(); err != nil {
return nil, err
}
@@ -280,22 +291,6 @@ func NewServer(options ...Option) (*Server, error) {
// Depends on step 3 (s.SearchEngine must be non-nil)
s.initEnterprise()
platformCfg := platform.ServiceConfig{
ConfigStore: s.configStore.Store,
Logger: s.Log,
StartMetrics: s.startMetrics,
Cluster: s.Cluster,
}
if metricsInterface != nil {
platformCfg.Metrics = metricsInterface(s)
}
ps, sErr := platform.New(platformCfg)
if sErr != nil {
return nil, errors.Wrap(sErr, "failed to initialize platform")
}
s.platform = ps
// Step 5: Cache provider.
// At the moment we only have this implementation
// in the future the cache provider will be built based on the loaded config
@@ -308,7 +303,7 @@ func NewServer(options ...Option) (*Server, error) {
// Depends on Step 1 (config), 4 (metrics, cluster) and 5 (cacheProvider).
if s.newStore == nil {
s.newStore = func() (store.Store, error) {
s.sqlStore = sqlstore.New(s.Config().SqlSettings, s.GetMetrics())
s.sqlStore = sqlstore.New(s.platform.Config().SqlSettings, s.GetMetrics())
lcl, err2 := localcachelayer.NewLocalCacheLayer(
retrylayer.New(s.sqlStore),
@@ -323,10 +318,10 @@ func NewServer(options ...Option) (*Server, error) {
searchStore := searchlayer.NewSearchLayer(
lcl,
s.SearchEngine,
s.Config(),
s.platform.Config(),
)
s.AddConfigListener(func(prevCfg, cfg *model.Config) {
s.platform.AddConfigListener(func(prevCfg, cfg *model.Config) {
searchStore.UpdateConfig(cfg)
})
@@ -352,7 +347,7 @@ func NewServer(options ...Option) (*Server, error) {
UserStore: s.Store.User(),
SessionStore: s.Store.Session(),
OAuthStore: s.Store.OAuth(),
ConfigFn: s.Config,
ConfigFn: s.platform.Config,
Metrics: s.GetMetrics(),
Cluster: s.Cluster,
LicenseFn: s.License,
@@ -376,9 +371,9 @@ func NewServer(options ...Option) (*Server, error) {
}
license := s.License()
insecure := s.Config().ServiceSettings.EnableInsecureOutgoingConnections
insecure := s.platform.Config().ServiceSettings.EnableInsecureOutgoingConnections
// Step 7: Initialize filestore
backend, err := filestore.NewFileBackend(s.Config().FileSettings.ToFileBackendSettings(license != nil && *license.Features.Compliance, insecure != nil && *insecure))
backend, err := filestore.NewFileBackend(s.platform.Config().FileSettings.ToFileBackendSettings(license != nil && *license.Features.Compliance, insecure != nil && *insecure))
if err != nil {
return nil, errors.Wrap(err, "failed to initialize filebackend")
}
@@ -398,7 +393,7 @@ func NewServer(options ...Option) (*Server, error) {
GroupStore: s.Store.Group(),
Users: s.userService,
WebHub: s,
ConfigFn: s.Config,
ConfigFn: s.platform.Config,
LicenseFn: s.License,
})
if err != nil {
@@ -410,7 +405,7 @@ func NewServer(options ...Option) (*Server, error) {
serviceMap := map[ServiceKey]any{
ChannelKey: &channelsWrapper{srv: s},
ConfigKey: s.configStore,
ConfigKey: s.platform,
LicenseKey: s.licenseWrapper,
FilestoreKey: s.filestore,
FileInfoStoreKey: &fileInfoWrapper{srv: s},
@@ -441,7 +436,7 @@ func NewServer(options ...Option) (*Server, error) {
// below this. Otherwise, please add it to Channels struct in app/channels.go.
// -------------------------------------------------------------------------
if *s.Config().LogSettings.EnableDiagnostics && *s.Config().LogSettings.EnableSentry {
if *s.platform.Config().LogSettings.EnableDiagnostics && *s.platform.Config().LogSettings.EnableSentry {
if strings.Contains(SentryDSN, "placeholder") {
mlog.Warn("Sentry reporting is enabled, but SENTRY_DSN is not set. Disabling reporting.")
} else {
@@ -468,7 +463,7 @@ func NewServer(options ...Option) (*Server, error) {
}
}
if *s.Config().ServiceSettings.EnableOpenTracing {
if *s.platform.Config().ServiceSettings.EnableOpenTracing {
tracer, err2 := tracing.New()
if err2 != nil {
return nil, err2
@@ -496,7 +491,7 @@ func NewServer(options ...Option) (*Server, error) {
s.createPushNotificationsHub(request.EmptyContext(s.GetLogger()))
if err2 := i18n.InitTranslations(*s.Config().LocalizationSettings.DefaultServerLocale, *s.Config().LocalizationSettings.DefaultClientLocale); err2 != nil {
if err2 := i18n.InitTranslations(*s.platform.Config().LocalizationSettings.DefaultServerLocale, *s.platform.Config().LocalizationSettings.DefaultClientLocale); err2 != nil {
return nil, errors.Wrapf(err2, "unable to load Mattermost translation files")
}
@@ -515,7 +510,7 @@ func NewServer(options ...Option) (*Server, error) {
})
s.htmlTemplateWatcher = htmlTemplateWatcher
s.configListenerId = s.AddConfigListener(func(_, _ *model.Config) {
s.configListenerId = s.platform.AddConfigListener(func(_, _ *model.Config) {
ch := s.Channels()
ch.regenerateClientConfig()
@@ -544,9 +539,10 @@ func NewServer(options ...Option) (*Server, error) {
})
s.telemetryService = telemetry.New(New(ServerConnector(s.Channels())), s.Store, s.SearchEngine, s.Log)
s.platform.SetTelemetryId(s.TelemetryId()) // TODO: move this into platform once telemetry service moved to platform.
emailService, err := email.NewService(email.ServiceConfig{
ConfigFn: s.Config,
ConfigFn: s.platform.Config,
LicenseFn: s.License,
GoFn: s.Go,
TemplatesContainer: s.TemplatesContainer(),
@@ -558,7 +554,7 @@ func NewServer(options ...Option) (*Server, error) {
}
s.EmailService = emailService
s.setupFeatureFlags()
s.platform.SetupFeatureFlags()
s.initJobs()
@@ -567,7 +563,7 @@ func NewServer(options ...Option) (*Server, error) {
if s.Jobs != nil {
s.Jobs.HandleClusterLeaderChange(s.IsLeader())
}
s.setupFeatureFlags()
s.platform.SetupFeatureFlags()
})
// If configured with a subpath, redirect 404s at the root back into the subpath.
@@ -578,12 +574,12 @@ func NewServer(options ...Option) (*Server, error) {
})
}
if _, err = url.ParseRequestURI(*s.Config().ServiceSettings.SiteURL); err != nil {
if _, err = url.ParseRequestURI(*s.platform.Config().ServiceSettings.SiteURL); err != nil {
mlog.Error("SiteURL must be set. Some features will operate incorrectly if the SiteURL is not set. See documentation for details: https://docs.mattermost.com/configure/configuration-settings.html#site-url")
}
// Start email batching because it's not like the other jobs
s.AddConfigListener(func(_, _ *model.Config) {
s.platform.AddConfigListener(func(_, _ *model.Config) {
s.EmailService.InitEmailBatching()
})
@@ -604,7 +600,7 @@ func NewServer(options ...Option) (*Server, error) {
pwd, _ := os.Getwd()
mlog.Info("Printing current working", mlog.String("directory", pwd))
mlog.Info("Loaded config", mlog.String("source", s.configStore.String()))
mlog.Info("Loaded config", mlog.String("source", s.platform.DescribeConfig()))
allowAdvancedLogging := license != nil && *license.Features.AdvancedLogging
@@ -626,7 +622,7 @@ func NewServer(options ...Option) (*Server, error) {
// Enable developer settings if this is a "dev" build
if model.BuildNumber == "dev" {
s.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableDeveloper = true })
s.platform.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableDeveloper = true })
}
if s.startMetrics {
@@ -649,13 +645,13 @@ func NewServer(options ...Option) (*Server, error) {
}
})
s.SearchEngine.UpdateConfig(s.Config())
s.SearchEngine.UpdateConfig(s.platform.Config())
searchConfigListenerId, searchLicenseListenerId := s.StartSearchEngine()
s.searchConfigListenerId = searchConfigListenerId
s.searchLicenseListenerId = searchLicenseListenerId
// if enabled - perform initial product notices fetch
if *s.Config().AnnouncementSettings.AdminNoticesEnabled || *s.Config().AnnouncementSettings.UserNoticesEnabled {
if *s.platform.Config().AnnouncementSettings.AdminNoticesEnabled || *s.platform.Config().AnnouncementSettings.UserNoticesEnabled {
go func() {
appInstance := New(ServerConnector(s.Channels()))
if err := appInstance.UpdateProductNotices(); err != nil {
@@ -668,7 +664,7 @@ func NewServer(options ...Option) (*Server, error) {
return s, nil
}
s.AddConfigListener(func(old, new *model.Config) {
s.platform.AddConfigListener(func(old, new *model.Config) {
appInstance := New(ServerConnector(s.Channels()))
if *old.GuestAccountsSettings.Enable && !*new.GuestAccountsSettings.Enable {
c := request.EmptyContext(s.GetLogger())
@@ -679,7 +675,7 @@ func NewServer(options ...Option) (*Server, error) {
})
// Disable active guest accounts on first run if guest accounts are disabled
if !*s.Config().GuestAccountsSettings.Enable {
if !*s.platform.Config().GuestAccountsSettings.Enable {
appInstance := New(ServerConnector(s.Channels()))
c := request.EmptyContext(s.GetLogger())
if appErr := appInstance.DeactivateGuests(c); appErr != nil {
@@ -703,7 +699,7 @@ func NewServer(options ...Option) (*Server, error) {
s.initPostMetadata()
// Dump the image cache if the proxy settings have changed. (need switch URLs to the correct proxy)
s.AddConfigListener(func(oldCfg, newCfg *model.Config) {
s.platform.AddConfigListener(func(oldCfg, newCfg *model.Config) {
if (oldCfg.ImageProxySettings.Enable != newCfg.ImageProxySettings.Enable) ||
(oldCfg.ImageProxySettings.ImageProxyType != newCfg.ImageProxySettings.ImageProxyType) ||
(oldCfg.ImageProxySettings.RemoteImageProxyURL != newCfg.ImageProxySettings.RemoteImageProxyURL) ||
@@ -755,18 +751,18 @@ func (s *Server) runJobs() {
complianceI.StartComplianceDailyJob()
}
if *s.Config().JobSettings.RunJobs && s.Jobs != nil {
if *s.platform.Config().JobSettings.RunJobs && s.Jobs != nil {
if err := s.Jobs.StartWorkers(); err != nil {
mlog.Error("Failed to start job server workers", mlog.Err(err))
}
}
if *s.Config().JobSettings.RunScheduler && s.Jobs != nil {
if *s.platform.Config().JobSettings.RunScheduler && s.Jobs != nil {
if err := s.Jobs.StartSchedulers(); err != nil {
mlog.Error("Failed to start job server schedulers", mlog.Err(err))
}
}
if *s.Config().ServiceSettings.EnableAWSMetering {
if *s.platform.Config().ServiceSettings.EnableAWSMetering {
runReportToAWSMeterJob(s)
}
}
@@ -786,7 +782,7 @@ func (s *Server) Channels() *Channels {
// Return Database type (postgres or mysql) and current version of the schema
func (s *Server) DatabaseTypeAndSchemaVersion() (string, string) {
schemaVersion, _ := s.Store.GetDBSchemaVersion()
return *s.Config().SqlSettings.DriverName, strconv.Itoa(schemaVersion)
return *s.platform.Config().SqlSettings.DriverName, strconv.Itoa(schemaVersion)
}
// initLogging initializes and configures the logger(s). This may be called more than once.
@@ -809,7 +805,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.Store, config.GetLogFileLocation); err != nil {
if err := s.platform.ConfigureLogger("logging", s.Log, &s.platform.Config().LogSettings, 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
@@ -824,8 +820,8 @@ func (s *Server) initLogging() error {
// Use the app logger as the global logger (eventually remove all instances of global logging).
mlog.InitGlobalLogger(s.Log)
notificationLogSettings := config.GetLogSettingsFromNotificationsLogSettings(&s.Config().NotificationLogSettings)
if err := s.configureLogger("notification logging", s.NotificationsLog, notificationLogSettings, s.configStore.Store, config.GetNotificationsLogFileLocation); err != nil {
notificationLogSettings := config.GetLogSettingsFromNotificationsLogSettings(&s.platform.Config().NotificationLogSettings)
if err := s.platform.ConfigureLogger("notification logging", s.NotificationsLog, notificationLogSettings, config.GetNotificationsLogFileLocation); err != nil {
if !errors.Is(err, mlog.ErrConfigurationLock) {
mlog.Error("Error configuring notification logger", mlog.Err(err))
return err
@@ -834,33 +830,6 @@ func (s *Server) initLogging() error {
return nil
}
// configureLogger applies the specified configuration to a logger.
func (s *Server) configureLogger(name string, logger *mlog.Logger, logSettings *model.LogSettings, configStore *config.Store, getPath func(string) string) error {
// Advanced logging is E20 only, however logging must be initialized before the license
// file is loaded. If no valid E20 license exists then advanced logging will be
// shutdown once license is loaded/checked.
var err error
dsn := *logSettings.AdvancedLoggingConfig
var logConfigSrc config.LogConfigSrc
if dsn != "" {
logConfigSrc, err = config.NewLogConfigSrc(dsn, configStore)
if err != nil {
return fmt.Errorf("invalid config source for %s, %w", name, err)
}
mlog.Info("Loaded configuration for "+name, mlog.String("source", dsn))
}
cfg, err := config.MloggerConfigFromLoggerConfig(logSettings, logConfigSrc, getPath)
if err != nil {
return fmt.Errorf("invalid config source for %s, %w", name, err)
}
if err := logger.ConfigureTargets(cfg, nil); err != nil {
return fmt.Errorf("invalid config for %s, %w", name, err)
}
return nil
}
// removeUnlicensedLogTargets removes any unlicensed log target types.
func (s *Server) removeUnlicensedLogTargets(license *model.License) {
if license != nil && *license.Features.AdvancedLogging {
@@ -895,7 +864,7 @@ func (s *Server) startInterClusterServices(license *model.License) error {
}
// Config check
if !*s.Config().ExperimentalSettings.EnableRemoteClusterService {
if !*s.platform.Config().ExperimentalSettings.EnableRemoteClusterService {
mlog.Debug("Remote Cluster Service disabled via config")
return nil
}
@@ -924,7 +893,7 @@ func (s *Server) startInterClusterServices(license *model.License) error {
}
// Config check
if !*s.Config().ExperimentalSettings.EnableSharedChannels {
if !*s.platform.Config().ExperimentalSettings.EnableSharedChannels {
mlog.Debug("Shared Channels Service disabled via config")
return nil
}
@@ -1029,14 +998,16 @@ func (s *Server) Shutdown() {
s.WaitForGoroutines()
s.RemoveConfigListener(s.configListenerId)
s.platform.RemoveConfigListener(s.configListenerId)
s.stopSearchEngine()
s.Audit.Shutdown()
s.stopFeatureFlagUpdateJob()
s.platform.StopFeatureFlagUpdateJob()
s.configStore.Close()
if err = s.platform.ShutdownConfig(); err != nil {
s.Log.Warn("Failed to shut down config store", mlog.Err(err))
}
if s.Cluster != nil {
s.Cluster.StopInterNodeCommunication()
@@ -1239,23 +1210,23 @@ func (s *Server) Start() error {
s.checkPushNotificationServerURL()
s.ReloadConfig()
s.platform.ReloadConfig()
mlog.Info("Starting Server...")
var handler http.Handler = s.RootRouter
if *s.Config().LogSettings.EnableDiagnostics && *s.Config().LogSettings.EnableSentry && !strings.Contains(SentryDSN, "placeholder") {
if *s.platform.Config().LogSettings.EnableDiagnostics && *s.platform.Config().LogSettings.EnableSentry && !strings.Contains(SentryDSN, "placeholder") {
sentryHandler := sentryhttp.New(sentryhttp.Options{
Repanic: true,
})
handler = sentryHandler.Handle(handler)
}
if allowedOrigins := *s.Config().ServiceSettings.AllowCorsFrom; allowedOrigins != "" {
exposedCorsHeaders := *s.Config().ServiceSettings.CorsExposedHeaders
allowCredentials := *s.Config().ServiceSettings.CorsAllowCredentials
debug := *s.Config().ServiceSettings.CorsDebug
if allowedOrigins := *s.platform.Config().ServiceSettings.AllowCorsFrom; allowedOrigins != "" {
exposedCorsHeaders := *s.platform.Config().ServiceSettings.CorsExposedHeaders
allowCredentials := *s.platform.Config().ServiceSettings.CorsAllowCredentials
debug := *s.platform.Config().ServiceSettings.CorsDebug
corsWrapper := cors.New(cors.Options{
AllowedOrigins: strings.Fields(allowedOrigins),
AllowedMethods: corsAllowedMethods,
@@ -1274,10 +1245,10 @@ func (s *Server) Start() error {
handler = corsWrapper.Handler(handler)
}
if *s.Config().RateLimitSettings.Enable {
if *s.platform.Config().RateLimitSettings.Enable {
mlog.Info("RateLimiter is enabled")
rateLimiter, err2 := NewRateLimiter(&s.Config().RateLimitSettings, s.Config().ServiceSettings.TrustedProxyIPHeader)
rateLimiter, err2 := NewRateLimiter(&s.platform.Config().RateLimitSettings, s.platform.Config().ServiceSettings.TrustedProxyIPHeader)
if err2 != nil {
return err2
}
@@ -1292,15 +1263,15 @@ func (s *Server) Start() error {
s.Server = &http.Server{
Handler: handler,
ReadTimeout: time.Duration(*s.Config().ServiceSettings.ReadTimeout) * time.Second,
WriteTimeout: time.Duration(*s.Config().ServiceSettings.WriteTimeout) * time.Second,
IdleTimeout: time.Duration(*s.Config().ServiceSettings.IdleTimeout) * time.Second,
ReadTimeout: time.Duration(*s.platform.Config().ServiceSettings.ReadTimeout) * time.Second,
WriteTimeout: time.Duration(*s.platform.Config().ServiceSettings.WriteTimeout) * time.Second,
IdleTimeout: time.Duration(*s.platform.Config().ServiceSettings.IdleTimeout) * time.Second,
ErrorLog: errStdLog,
}
addr := *s.Config().ServiceSettings.ListenAddress
addr := *s.platform.Config().ServiceSettings.ListenAddress
if addr == "" {
if *s.Config().ServiceSettings.ConnectionSecurity == model.ConnSecurityTLS {
if *s.platform.Config().ServiceSettings.ConnectionSecurity == model.ConnSecurityTLS {
addr = ":https"
} else {
addr = ":http"
@@ -1317,11 +1288,11 @@ func (s *Server) Start() error {
mlog.Info(logListeningPort, mlog.String("address", listener.Addr().String()))
m := &autocert.Manager{
Cache: autocert.DirCache(*s.Config().ServiceSettings.LetsEncryptCertificateCacheFile),
Cache: autocert.DirCache(*s.platform.Config().ServiceSettings.LetsEncryptCertificateCacheFile),
Prompt: autocert.AcceptTOS,
}
if *s.Config().ServiceSettings.Forward80To443 {
if *s.platform.Config().ServiceSettings.Forward80To443 {
if host, port, err := net.SplitHostPort(addr); err != nil {
mlog.Error("Unable to setup forwarding", mlog.Err(err))
} else if port != "443" {
@@ -1329,7 +1300,7 @@ func (s *Server) Start() error {
} else {
httpListenAddress := net.JoinHostPort(host, "http")
if *s.Config().ServiceSettings.UseLetsEncrypt {
if *s.platform.Config().ServiceSettings.UseLetsEncrypt {
server := &http.Server{
Addr: httpListenAddress,
Handler: m.HTTPHandler(nil),
@@ -1353,21 +1324,21 @@ func (s *Server) Start() error {
}()
}
}
} else if *s.Config().ServiceSettings.UseLetsEncrypt {
} else if *s.platform.Config().ServiceSettings.UseLetsEncrypt {
return errors.New(i18n.T("api.server.start_server.forward80to443.disabled_while_using_lets_encrypt"))
}
s.didFinishListen = make(chan struct{})
go func() {
var err error
if *s.Config().ServiceSettings.ConnectionSecurity == model.ConnSecurityTLS {
if *s.platform.Config().ServiceSettings.ConnectionSecurity == model.ConnSecurityTLS {
tlsConfig := &tls.Config{
PreferServerCipherSuites: true,
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
}
switch *s.Config().ServiceSettings.TLSMinVer {
switch *s.platform.Config().ServiceSettings.TLSMinVer {
case "1.0":
tlsConfig.MinVersion = tls.VersionTLS10
case "1.1":
@@ -1385,11 +1356,11 @@ func (s *Server) Start() error {
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
}
if len(s.Config().ServiceSettings.TLSOverwriteCiphers) == 0 {
if len(s.platform.Config().ServiceSettings.TLSOverwriteCiphers) == 0 {
tlsConfig.CipherSuites = defaultCiphers
} else {
var cipherSuites []uint16
for _, cipher := range s.Config().ServiceSettings.TLSOverwriteCiphers {
for _, cipher := range s.platform.Config().ServiceSettings.TLSOverwriteCiphers {
value, ok := model.ServerTLSSupportedCiphers[cipher]
if !ok {
@@ -1411,12 +1382,12 @@ func (s *Server) Start() error {
certFile := ""
keyFile := ""
if *s.Config().ServiceSettings.UseLetsEncrypt {
if *s.platform.Config().ServiceSettings.UseLetsEncrypt {
tlsConfig.GetCertificate = m.GetCertificate
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2")
} else {
certFile = *s.Config().ServiceSettings.TLSCertFile
keyFile = *s.Config().ServiceSettings.TLSKeyFile
certFile = *s.platform.Config().ServiceSettings.TLSCertFile
keyFile = *s.platform.Config().ServiceSettings.TLSKeyFile
}
s.Server.TLSConfig = tlsConfig
@@ -1433,7 +1404,7 @@ func (s *Server) Start() error {
close(s.didFinishListen)
}()
if *s.Config().ServiceSettings.EnableLocalMode {
if *s.platform.Config().ServiceSettings.EnableLocalMode {
if err := s.startLocalModeServer(); err != nil {
mlog.Critical(err.Error())
}
@@ -1451,7 +1422,7 @@ func (s *Server) startLocalModeServer() error {
Handler: s.LocalRouter,
}
socket := *s.configStore.Get().ServiceSettings.LocalModeSocketLocation
socket := *s.platform.Config().ServiceSettings.LocalModeSocketLocation
if err := os.RemoveAll(socket); err != nil {
return errors.Wrapf(err, i18n.T("api.server.start_server.starting.critical"), err)
}
@@ -1495,7 +1466,7 @@ func (a *App) OriginChecker() func(*http.Request) bool {
}
func (s *Server) checkPushNotificationServerURL() {
notificationServer := *s.Config().EmailSettings.PushNotificationServer
notificationServer := *s.platform.Config().EmailSettings.PushNotificationServer
if strings.HasPrefix(notificationServer, "http://") {
mlog.Warn("Your push notification server is configured with HTTP. For improved security, update to HTTPS in your configuration.")
}
@@ -1563,7 +1534,7 @@ func runReportToAWSMeterJob(s *Server) {
}
func doReportUsageToAWSMeteringService(s *Server) {
awsMeter := awsmeter.New(s.Store, s.Config())
awsMeter := awsmeter.New(s.Store, s.platform.Config())
if awsMeter == nil {
mlog.Error("Cannot obtain instance of AWS Metering Service.")
return
@@ -1604,12 +1575,12 @@ func doSessionCleanup(s *Server) {
}
func doJobsCleanup(s *Server) {
if *s.Config().JobSettings.CleanupJobsThresholdDays < 0 {
if *s.platform.Config().JobSettings.CleanupJobsThresholdDays < 0 {
return
}
mlog.Debug("Cleaning up jobs store.")
dur := time.Duration(*s.Config().JobSettings.CleanupJobsThresholdDays) * time.Hour * 24
dur := time.Duration(*s.platform.Config().JobSettings.CleanupJobsThresholdDays) * time.Hour * 24
expiry := model.GetMillisForTime(time.Now().Add(-dur))
err := s.Store.Job().Cleanup(expiry, jobsCleanupBatchSize)
if err != nil {
@@ -1618,12 +1589,12 @@ func doJobsCleanup(s *Server) {
}
func doConfigCleanup(s *Server) {
if *s.Config().JobSettings.CleanupConfigThresholdDays < 0 || !config.IsDatabaseDSN(s.ConfigStore().Store.String()) {
if *s.platform.Config().JobSettings.CleanupConfigThresholdDays < 0 || !config.IsDatabaseDSN(s.platform.DescribeConfig()) {
return
}
mlog.Info("Cleaning up configuration store.")
if err := s.ConfigStore().Store.CleanUp(); err != nil {
if err := s.platform.CleanUpConfig(); err != nil {
mlog.Warn("Error while cleaning up configurations", mlog.Err(err))
}
}
@@ -1654,7 +1625,7 @@ func (s *Server) sendLicenseUpForRenewalEmail(users map[string]*model.User, lice
if name == "" {
name = user.Username
}
if err := s.EmailService.SendLicenseUpForRenewalEmail(user.Email, name, user.Locale, *s.Config().ServiceSettings.SiteURL, renewalLink, daysToExpiration); err != nil {
if err := s.EmailService.SendLicenseUpForRenewalEmail(user.Email, name, user.Locale, *s.platform.Config().ServiceSettings.SiteURL, renewalLink, daysToExpiration); err != nil {
mlog.Error("Error sending license up for renewal email to", mlog.String("user_email", user.Email), mlog.Err(err))
countNotOks++
}
@@ -1735,7 +1706,7 @@ func (s *Server) doLicenseExpirationCheck() {
mlog.Debug("Sending license expired email.", mlog.String("user_email", user.Email))
s.Go(func() {
if err := s.SendRemoveExpiredLicenseEmail(user.Email, renewalLink, user.Locale, *s.Config().ServiceSettings.SiteURL); err != nil {
if err := s.SendRemoveExpiredLicenseEmail(user.Email, renewalLink, user.Locale, *s.platform.Config().ServiceSettings.SiteURL); err != nil {
mlog.Error("Error while sending the license expired email.", mlog.String("user_email", user.Email), mlog.Err(err))
}
})
@@ -1765,7 +1736,7 @@ func (s *Server) StartSearchEngine() (string, string) {
})
}
configListenerId := s.AddConfigListener(func(oldConfig *model.Config, newConfig *model.Config) {
configListenerId := s.platform.AddConfigListener(func(oldConfig *model.Config, newConfig *model.Config) {
if s.SearchEngine == nil {
return
}
@@ -1824,7 +1795,7 @@ func (s *Server) StartSearchEngine() (string, string) {
}
func (s *Server) stopSearchEngine() {
s.RemoveConfigListener(s.searchConfigListenerId)
s.platform.RemoveConfigListener(s.searchConfigListenerId)
s.RemoveLicenseListener(s.searchLicenseListenerId)
if s.SearchEngine != nil && s.SearchEngine.ElasticsearchEngine != nil && s.SearchEngine.ElasticsearchEngine.IsActive() {
s.SearchEngine.ElasticsearchEngine.Stop()
@@ -1858,7 +1829,7 @@ func (ch *Channels) ClientConfigHash() string {
}
func (s *Server) initJobs() {
s.Jobs = jobs.NewJobServer(s, s.Store, s.GetMetrics())
s.Jobs = jobs.NewJobServer(s.platform, s.Store, s.GetMetrics())
if jobsDataRetentionJobInterface != nil {
builder := jobsDataRetentionJobInterface(s)
@@ -2031,7 +2002,7 @@ func (s *Server) SetSharedChannelSyncService(sharedChannelService SharedChannelS
}
func (s *Server) GetProfileImage(user *model.User) ([]byte, bool, *model.AppError) {
if *s.Config().FileSettings.DriverName == "" {
if *s.platform.Config().FileSettings.DriverName == "" {
img, appErr := s.GetDefaultProfileImage(user)
if appErr != nil {
return nil, false, appErr
@@ -2141,3 +2112,8 @@ func (a *App) GetAppliedSchemaMigrations() ([]model.AppliedMigration, *model.App
}
return table, nil
}
// Expose platform service from server, this should be replaced with server itself in time.
func (s *Server) Platform() *platform.PlatformService {
return s.platform
}

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

@@ -17,17 +17,17 @@ const inactivityEmailSent = "INACTIVITY"
func (s *Server) doInactivityCheck() {
if *s.Config().ServiceSettings.EnableDeveloper {
if *s.platform.Config().ServiceSettings.EnableDeveloper {
mlog.Info("No activity check because developer mode is enabled")
return
}
if !*s.Config().EmailSettings.EnableInactivityEmail {
if !*s.platform.Config().EmailSettings.EnableInactivityEmail {
mlog.Info("No activity check because EnableInactivityEmail is false")
return
}
if !s.Config().FeatureFlags.EnableInactivityCheckJob {
if !s.platform.Config().FeatureFlags.EnableInactivityCheckJob {
mlog.Info("No activity check because EnableInactivityCheckJob feature flag is disabled")
return
}
@@ -70,7 +70,7 @@ func (s *Server) doInactivityCheck() {
}
func (s *Server) takeInactivityAction() {
siteURL := *s.Config().ServiceSettings.SiteURL
siteURL := *s.platform.Config().ServiceSettings.SiteURL
if siteURL == "" {
mlog.Warn("No SiteURL configured")
}

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

@@ -1,13 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package app
import (
"github.com/mattermost/mattermost-server/v6/model"
)
func (s *Server) License() *model.License {
license, _ := s.licenseValue.Load().(*model.License)
return license
}

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

@@ -23,6 +23,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost-server/v6/app/platform"
"github.com/mattermost/mattermost-server/v6/config"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/shared/filestore"
@@ -83,54 +84,70 @@ func TestReadReplicaDisabledBasedOnLicense(t *testing.T) {
s, err := NewServer(func(server *Server) error {
configStore := config.NewTestMemoryStore()
configStore.Set(&cfg)
server.configStore = &configWrapper{srv: server, Store: configStore}
var err error
server.platform, err = platform.New(platform.ServiceConfig{
ConfigStore: configStore,
})
require.NoError(t, err)
return nil
})
require.NoError(t, err)
defer s.Shutdown()
require.Same(t, s.sqlStore.GetMasterX(), s.sqlStore.GetReplicaX())
require.Len(t, s.Config().SqlSettings.DataSourceReplicas, 1)
require.Len(t, s.platform.Config().SqlSettings.DataSourceReplicas, 1)
})
t.Run("Read Replicas With License", func(t *testing.T) {
s, err := NewServer(func(server *Server) error {
configStore := config.NewTestMemoryStore()
configStore.Set(&cfg)
server.configStore = &configWrapper{srv: server, Store: configStore}
var err error
server.platform, err = platform.New(platform.ServiceConfig{
ConfigStore: configStore,
})
require.NoError(t, err)
server.licenseValue.Store(model.NewTestLicense())
return nil
})
require.NoError(t, err)
defer s.Shutdown()
require.NotSame(t, s.sqlStore.GetMasterX(), s.sqlStore.GetReplicaX())
require.Len(t, s.Config().SqlSettings.DataSourceReplicas, 1)
require.Len(t, s.platform.Config().SqlSettings.DataSourceReplicas, 1)
})
t.Run("Search Replicas with no License", func(t *testing.T) {
s, err := NewServer(func(server *Server) error {
configStore := config.NewTestMemoryStore()
configStore.Set(&cfg)
server.configStore = &configWrapper{srv: server, Store: configStore}
var err error
server.platform, err = platform.New(platform.ServiceConfig{
ConfigStore: configStore,
})
require.NoError(t, err)
return nil
})
require.NoError(t, err)
defer s.Shutdown()
require.Same(t, s.sqlStore.GetMasterX(), s.sqlStore.GetSearchReplicaX())
require.Len(t, s.Config().SqlSettings.DataSourceSearchReplicas, 1)
require.Len(t, s.platform.Config().SqlSettings.DataSourceSearchReplicas, 1)
})
t.Run("Search Replicas With License", func(t *testing.T) {
s, err := NewServer(func(server *Server) error {
configStore := config.NewTestMemoryStore()
configStore.Set(&cfg)
server.configStore = &configWrapper{srv: server, Store: configStore}
var err error
server.platform, err = platform.New(platform.ServiceConfig{
ConfigStore: configStore,
})
require.NoError(t, err)
server.licenseValue.Store(model.NewTestLicense())
return nil
})
require.NoError(t, err)
defer s.Shutdown()
require.NotSame(t, s.sqlStore.GetMasterX(), s.sqlStore.GetSearchReplicaX())
require.Len(t, s.Config().SqlSettings.DataSourceSearchReplicas, 1)
require.Len(t, s.platform.Config().SqlSettings.DataSourceSearchReplicas, 1)
})
}
@@ -143,7 +160,7 @@ func TestStartServerPortUnavailable(t *testing.T) {
require.NoError(t, err)
// Attempt to listen on the port used above.
s.UpdateConfig(func(cfg *model.Config) {
s.platform.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ListenAddress = listener.Addr().String()
})
@@ -168,8 +185,12 @@ 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 = &configWrapper{srv: server, Store: store}
server.UpdateConfig(func(cfg *model.Config) {
var err error
server.platform, err = platform.New(platform.ServiceConfig{
ConfigStore: store,
})
require.NoError(t, err)
server.platform.UpdateConfig(func(cfg *model.Config) {
cfg.FileSettings = model.FileSettings{
DriverName: model.NewString(model.ImageDriverS3),
AmazonS3AccessKeyId: model.NewString(model.MinioAccessKey),
@@ -393,7 +414,7 @@ func TestPanicLog(t *testing.T) {
})
testDir, _ := fileutils.FindDir("tests")
s.UpdateConfig(func(cfg *model.Config) {
s.platform.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ListenAddress = ":0"
*cfg.ServiceSettings.ConnectionSecurity = "TLS"
*cfg.ServiceSettings.TLSKeyFile = path.Join(testDir, "tls_test_key.pem")

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

@@ -104,7 +104,7 @@ func TestCustomStatusErrors(t *testing.T) {
UserStore: &mockUserStore,
SessionStore: &mockSessionStore,
OAuthStore: &mockOAuthStore,
ConfigFn: th.App.ch.srv.Config,
ConfigFn: th.App.ch.srv.platform.Config,
LicenseFn: th.App.ch.srv.License,
})
require.NoError(t, err)

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

@@ -1044,7 +1044,7 @@ func TestLeaveTeamPanic(t *testing.T) {
UserStore: &mockUserStore,
SessionStore: &mocks.SessionStore{},
OAuthStore: &mocks.OAuthStore{},
ConfigFn: th.App.ch.srv.Config,
ConfigFn: th.App.ch.srv.platform.Config,
LicenseFn: th.App.ch.srv.License,
})
require.NoError(t, err)
@@ -1088,7 +1088,7 @@ func TestLeaveTeamPanic(t *testing.T) {
GroupStore: &mocks.GroupStore{},
Users: th.App.ch.srv.userService,
WebHub: th.App.ch.srv,
ConfigFn: th.App.ch.srv.Config,
ConfigFn: th.App.ch.srv.platform.Config,
LicenseFn: th.App.ch.srv.License,
})
require.NoError(t, err)

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

@@ -1712,7 +1712,7 @@ func TestUpdateThreadReadForUser(t *testing.T) {
UserStore: &mockUserStore,
SessionStore: &storemocks.SessionStore{},
OAuthStore: &storemocks.OAuthStore{},
ConfigFn: th.App.ch.srv.Config,
ConfigFn: th.App.ch.srv.platform.Config,
LicenseFn: th.App.ch.srv.License,
})
require.NoError(t, err)
@@ -1749,8 +1749,8 @@ func TestCreateUserWithInitialPreferences(t *testing.T) {
})
t.Run("successfully create a user with insights feature flag disabled", func(t *testing.T) {
th.Server.configStore.SetReadOnlyFF(false)
defer th.Server.configStore.SetReadOnlyFF(true)
th.Server.platform.SetConfigReadOnlyFF(false)
defer th.Server.platform.SetConfigReadOnlyFF(true)
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = false })
defer th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
testUser := th.CreateUser()
@@ -1769,8 +1769,8 @@ func TestCreateUserWithInitialPreferences(t *testing.T) {
})
t.Run("successfully create a guest user with initial tutorial, insights and recommended steps preferences", func(t *testing.T) {
th.Server.configStore.SetReadOnlyFF(false)
defer th.Server.configStore.SetReadOnlyFF(true)
th.Server.platform.SetConfigReadOnlyFF(false)
defer th.Server.platform.SetConfigReadOnlyFF(true)
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
testUser := th.CreateGuest()
defer th.App.PermanentDeleteUser(th.Context, testUser)

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

@@ -167,7 +167,7 @@ func TestHubSessionRevokeRace(t *testing.T) {
UserStore: &mockUserStore,
SessionStore: &mockSessionStore,
OAuthStore: &mockOAuthStore,
ConfigFn: th.App.ch.srv.Config,
ConfigFn: th.App.ch.srv.platform.Config,
Metrics: th.App.Metrics(),
Cluster: th.App.Cluster(),
LicenseFn: th.App.ch.srv.License,

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

@@ -42,9 +42,10 @@ func initDBCommandContext(configDSN string, readOnlyConfigStore bool) (*app.App,
model.AppErrorInit(i18n.T)
s, err := app.NewServer(
// The option order is important as app.Config option reads app.StartMetrics option.
app.StartMetrics,
app.Config(configDSN, readOnlyConfigStore, nil),
app.StartSearchEngine,
app.StartMetrics,
)
if err != nil {
return nil, err

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

@@ -65,11 +65,12 @@ func runServer(configStore *config.Store, interruptChan chan os.Signal) error {
debug.SetTraceback("crash")
options := []app.Option{
// The option order is important as app.Config option reads app.StartMetrics option.
app.StartMetrics,
app.ConfigStore(configStore),
app.RunEssentialJobs,
app.JoinCluster,
app.StartSearchEngine,
app.StartMetrics,
}
server, err := app.NewServer(options...)
if err != nil {

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

@@ -102,16 +102,17 @@ func setupTestHelper(tb testing.TB, includeCacheLayer bool) *TestHelper {
}
}
a := app.New(app.ServerConnector(s.Channels()))
prevListenAddress := *s.Config().ServiceSettings.ListenAddress
s.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" })
a.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" })
serverErr := s.Start()
if serverErr != nil {
panic(serverErr)
}
s.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress })
a.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress })
// Disable strict password requirements for test
s.UpdateConfig(func(cfg *model.Config) {
a.UpdateConfig(func(cfg *model.Config) {
*cfg.PasswordSettings.MinimumLength = 5
*cfg.PasswordSettings.Lowercase = false
*cfg.PasswordSettings.Uppercase = false
@@ -119,15 +120,13 @@ func setupTestHelper(tb testing.TB, includeCacheLayer bool) *TestHelper {
*cfg.PasswordSettings.Number = false
})
a := app.New(app.ServerConnector(s.Channels()))
web := New(s)
URL = fmt.Sprintf("http://localhost:%v", s.ListenAddr.Port)
apiClient = model.NewAPIv4Client(URL)
s.Store.MarkSystemRanUnitTests()
s.UpdateConfig(func(cfg *model.Config) {
a.UpdateConfig(func(cfg *model.Config) {
*cfg.TeamSettings.EnableOpenServer = true
})