коммит произвёл
GitHub
родитель
b826421716
Коммит
f0e4794cda
@@ -29,7 +29,7 @@ func (s *Server) GetLogs(page, perPage int) ([]string, *model.AppError) {
|
|||||||
var lines []string
|
var lines []string
|
||||||
|
|
||||||
license := s.License()
|
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 {
|
if info := s.Cluster.GetMyClusterInfo(); info != nil {
|
||||||
lines = append(lines, "-----------------------------------------------------------------------------------------------------------")
|
lines = append(lines, "-----------------------------------------------------------------------------------------------------------")
|
||||||
lines = append(lines, "-----------------------------------------------------------------------------------------------------------")
|
lines = append(lines, "-----------------------------------------------------------------------------------------------------------")
|
||||||
@@ -48,7 +48,7 @@ func (s *Server) GetLogs(page, perPage int) ([]string, *model.AppError) {
|
|||||||
|
|
||||||
lines = append(lines, melines...)
|
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)
|
clines, err := s.Cluster.GetLogs(page, perPage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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) {
|
func (s *Server) GetLogsSkipSend(page, perPage int) ([]string, *model.AppError) {
|
||||||
var lines []string
|
var lines []string
|
||||||
|
|
||||||
if *s.Config().LogSettings.EnableFile {
|
if *s.platform.Config().LogSettings.EnableFile {
|
||||||
s.Log.Flush()
|
s.Log.Flush()
|
||||||
logFile := config.GetLogFileLocation(*s.Config().LogSettings.FileLocation)
|
logFile := config.GetLogFileLocation(*s.platform.Config().LogSettings.FileLocation)
|
||||||
file, err := os.Open(logFile)
|
file, err := os.Open(logFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, model.NewAppError("getLogs", "api.admin.file_read_error", nil, err.Error(), http.StatusInternalServerError)
|
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
|
// PromoteGuestToUser Convert user's roles and all his membership's roles from
|
||||||
// guest roles to regular user roles.
|
// guest roles to regular user roles.
|
||||||
PromoteGuestToUser(c *request.Context, user *model.User, requestorId string) *model.AppError
|
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 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)
|
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
|
// RenameTeam is used to rename the team Name and the DisplayName fields
|
||||||
@@ -941,7 +943,6 @@ type AppIface interface {
|
|||||||
ReloadConfig() error
|
ReloadConfig() error
|
||||||
RemoveAllDeactivatedMembersFromChannel(c request.CTX, channel *model.Channel) *model.AppError
|
RemoveAllDeactivatedMembersFromChannel(c request.CTX, channel *model.Channel) *model.AppError
|
||||||
RemoveChannelsFromRetentionPolicy(policyID string, channelIDs []string) *model.AppError
|
RemoveChannelsFromRetentionPolicy(policyID string, channelIDs []string) *model.AppError
|
||||||
RemoveConfigListener(id string)
|
|
||||||
RemoveCustomStatus(c request.CTX, userID string) *model.AppError
|
RemoveCustomStatus(c request.CTX, userID string) *model.AppError
|
||||||
RemoveDirectory(path string) *model.AppError
|
RemoveDirectory(path string) *model.AppError
|
||||||
RemoveFile(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
|
adt.OnError = s.onAuditError
|
||||||
|
|
||||||
var logConfigSrc config.LogConfigSrc
|
var logConfigSrc config.LogConfigSrc
|
||||||
dsn := *s.Config().ExperimentalAuditSettings.AdvancedLoggingConfig
|
dsn := *s.platform.Config().ExperimentalAuditSettings.AdvancedLoggingConfig
|
||||||
if bAllowAdvancedLogging && dsn != "" {
|
if bAllowAdvancedLogging && dsn != "" {
|
||||||
var err error
|
var err error
|
||||||
logConfigSrc, err = config.NewLogConfigSrc(dsn, s.configStore.Store)
|
logConfigSrc, err = config.NewLogConfigSrc(dsn, s.platform.GetConfigStore())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid config source for audit, %w", err)
|
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).
|
// 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 {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid config for audit, %w", err)
|
return fmt.Errorf("invalid config for audit, %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2056,7 +2056,7 @@ func TestMarkChannelsAsViewedPanic(t *testing.T) {
|
|||||||
UserStore: &mockUserStore,
|
UserStore: &mockUserStore,
|
||||||
SessionStore: &mockSessionStore,
|
SessionStore: &mockSessionStore,
|
||||||
OAuthStore: &mockOAuthStore,
|
OAuthStore: &mockOAuthStore,
|
||||||
ConfigFn: th.App.ch.srv.Config,
|
ConfigFn: th.App.ch.srv.platform.Config,
|
||||||
LicenseFn: th.App.ch.srv.License,
|
LicenseFn: th.App.ch.srv.License,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -31,12 +31,6 @@ type licenseSvc interface {
|
|||||||
RequestTrialLicense(requesterID string, users int, termsAccepted bool, receiveEmailsAccepted bool) *model.AppError
|
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.
|
// Channels contains all channels related state.
|
||||||
type Channels struct {
|
type Channels struct {
|
||||||
srv *Server
|
srv *Server
|
||||||
@@ -107,7 +101,7 @@ func init() {
|
|||||||
func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
|
func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
|
||||||
ch := &Channels{
|
ch := &Channels{
|
||||||
srv: s,
|
srv: s,
|
||||||
imageProxy: imageproxy.MakeImageProxy(s, s.httpService, s.Log),
|
imageProxy: imageproxy.MakeImageProxy(s.platform, s.httpService, s.Log),
|
||||||
uploadLockMap: map[string]bool{},
|
uploadLockMap: map[string]bool{},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,10 +127,6 @@ func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("Config service did not satisfy ConfigSvc interface")
|
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
|
ch.cfgSvc = cfgSvc
|
||||||
case FilestoreKey:
|
case FilestoreKey:
|
||||||
filestore, ok := svc.(filestore.FileBackend)
|
filestore, ok := svc.(filestore.FileBackend)
|
||||||
@@ -149,10 +139,6 @@ func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("License service did not satisfy licenseSvc interface")
|
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
|
ch.licenseSvc = svc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ func (cds *ClusterDiscoveryService) Stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) IsLeader() bool {
|
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 s.Cluster.IsLeader()
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|||||||
128
app/config.go
128
app/config.go
@@ -12,7 +12,6 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -22,7 +21,6 @@ import (
|
|||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/config"
|
"github.com/mattermost/mattermost-server/v6/config"
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"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/mail"
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||||
"github.com/mattermost/mattermost-server/v6/utils"
|
"github.com/mattermost/mattermost-server/v6/utils"
|
||||||
@@ -32,113 +30,24 @@ const (
|
|||||||
ErrorTermsOfServiceNoRowsFound = "app.terms_of_service.get.no_rows.app_error"
|
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 {
|
func (s *Server) Config() *model.Config {
|
||||||
return s.configStore.Config()
|
return s.platform.Config()
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) ConfigStore() *configWrapper {
|
|
||||||
return s.configStore
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) Config() *model.Config {
|
func (a *App) Config() *model.Config {
|
||||||
return a.ch.cfgSvc.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 {
|
func (a *App) EnvironmentConfig(filter func(reflect.StructField) bool) map[string]any {
|
||||||
return a.Srv().EnvironmentConfig(filter)
|
return a.Srv().platform.GetEnvironmentOverridesWithFilter(filter)
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) UpdateConfig(f func(*model.Config)) {
|
|
||||||
s.configStore.UpdateConfig(f)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) UpdateConfig(f func(*model.Config)) {
|
func (a *App) UpdateConfig(f func(*model.Config)) {
|
||||||
a.Srv().UpdateConfig(f)
|
a.Srv().platform.UpdateConfig(f)
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) ReloadConfig() error {
|
|
||||||
return s.configStore.ReloadConfig()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) ReloadConfig() error {
|
func (a *App) ReloadConfig() error {
|
||||||
return a.Srv().ReloadConfig()
|
return a.Srv().platform.ReloadConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) ClientConfig() map[string]string {
|
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)
|
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 {
|
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
|
// 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) {
|
func (a *App) RemoveConfigListener(id string) {
|
||||||
a.Srv().RemoveConfigListener(id)
|
a.Srv().platform.RemoveConfigListener(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensurePostActionCookieSecret ensures that the key for encrypting PostActionCookie exists
|
// 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.
|
// GetConfigFile proxies access to the given configuration file to the underlying config store.
|
||||||
func (a *App) GetConfigFile(name string) ([]byte, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "failed to get config file %s", name)
|
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)
|
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.
|
// SaveConfig replaces the active configuration, optionally notifying cluster peers.
|
||||||
func (a *App) SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bool) (*model.Config, *model.Config, *model.AppError) {
|
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) {
|
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 {
|
func (s *Server) MailServiceConfig() *mail.SMTPConfig {
|
||||||
emailSettings := s.Config().EmailSettings
|
emailSettings := s.platform.Config().EmailSettings
|
||||||
hostname := utils.GetHostnameFromSiteURL(*s.Config().ServiceSettings.SiteURL)
|
hostname := utils.GetHostnameFromSiteURL(*s.platform.Config().ServiceSettings.SiteURL)
|
||||||
cfg := mail.SMTPConfig{
|
cfg := mail.SMTPConfig{
|
||||||
Hostname: hostname,
|
Hostname: hostname,
|
||||||
ConnectionSecurity: *emailSettings.ConnectionSecurity,
|
ConnectionSecurity: *emailSettings.ConnectionSecurity,
|
||||||
|
|||||||
@@ -16,41 +16,6 @@ import (
|
|||||||
"github.com/mattermost/mattermost-server/v6/utils"
|
"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) {
|
func TestAsymmetricSigningKey(t *testing.T) {
|
||||||
th := SetupWithStoreMock(t)
|
th := SetupWithStoreMock(t)
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func (s *Server) downloadFromURL(downloadURL string) ([]byte, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Errorf("failed to parse url %s", downloadURL)
|
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)
|
return nil, errors.Errorf("insecure url not allowed %s", downloadURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,9 +87,9 @@ func RegisterCloudInterface(f func(*Server) einterfaces.CloudInterface) {
|
|||||||
cloudInterface = f
|
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
|
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)
|
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 {
|
if err != nil {
|
||||||
return model.NewAppError("AddLdapCertificate", "api.admin.add_certificate.saving.app_error", nil, err.Error(), http.StatusInternalServerError)
|
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 {
|
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 model.NewAppError("RemoveLdapFile", "api.admin.remove_certificate.delete.app_error", map[string]any{"Filename": filename}, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
return nil
|
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 {
|
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)
|
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(),
|
ServerID: w.srv.TelemetryId(),
|
||||||
Name: requester.GetDisplayName(model.ShowFullName),
|
Name: requester.GetDisplayName(model.ShowFullName),
|
||||||
Email: requester.Email,
|
Email: requester.Email,
|
||||||
SiteName: *w.srv.Config().TeamSettings.SiteName,
|
SiteName: *w.srv.platform.Config().TeamSettings.SiteName,
|
||||||
SiteURL: *w.srv.Config().ServiceSettings.SiteURL,
|
SiteURL: *w.srv.platform.Config().ServiceSettings.SiteURL,
|
||||||
Users: users,
|
Users: users,
|
||||||
TermsAccepted: termsAccepted,
|
TermsAccepted: termsAccepted,
|
||||||
ReceiveEmailsAccepted: receiveEmailsAccepted,
|
ReceiveEmailsAccepted: receiveEmailsAccepted,
|
||||||
@@ -93,6 +93,11 @@ type JWTClaims struct {
|
|||||||
jwt.StandardClaims
|
jwt.StandardClaims
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) License() *model.License {
|
||||||
|
license, _ := s.licenseValue.Load().(*model.License)
|
||||||
|
return license
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) LoadLicense() {
|
func (s *Server) LoadLicense() {
|
||||||
// ENV var overrides all other sources of license.
|
// ENV var overrides all other sources of license.
|
||||||
licenseStr := os.Getenv(LicenseEnv)
|
licenseStr := os.Getenv(LicenseEnv)
|
||||||
@@ -131,7 +136,7 @@ func (s *Server) LoadLicense() {
|
|||||||
|
|
||||||
if !model.IsValidId(licenseId) {
|
if !model.IsValidId(licenseId) {
|
||||||
// Lets attempt to load the file from disk since it was missing from the DB
|
// 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 license != nil {
|
||||||
if _, err := s.SaveLicense(licenseBytes); err != 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)
|
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) {
|
if err := s.Jobs.StopWorkers(); err != nil && !errors.Is(err, jobs.ErrWorkersNotRunning) {
|
||||||
mlog.Warn("Stopping job server workers failed", mlog.Err(err))
|
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) {
|
if err := s.Jobs.StopSchedulers(); err != nil && !errors.Is(err, jobs.ErrSchedulersNotRunning) {
|
||||||
mlog.Error("Stopping job server schedulers failed", mlog.Err(err))
|
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
|
// 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
|
// doesn't start until the server is restarted, which prevents the 'run job now' buttons in system console from
|
||||||
// functioning as expected
|
// 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 {
|
if err := s.Jobs.StartWorkers(); err != nil {
|
||||||
mlog.Error("Starting job server workers failed", mlog.Err(err))
|
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) {
|
if err := s.Jobs.StartSchedulers(); err != nil && !errors.Is(err, jobs.ErrSchedulersRunning) {
|
||||||
mlog.Error("Starting job server schedulers failed", mlog.Err(err))
|
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)
|
return nil, model.NewAppError("addLicense", "api.license.add_license.save_active.app_error", nil, "", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.ReloadConfig()
|
s.platform.ReloadConfig()
|
||||||
s.InvalidateAllCaches()
|
s.InvalidateAllCaches()
|
||||||
|
|
||||||
return &license, nil
|
return &license, nil
|
||||||
@@ -256,12 +261,20 @@ func (s *Server) SetLicense(license *model.License) bool {
|
|||||||
license.Features.SetDefaults()
|
license.Features.SetDefaults()
|
||||||
|
|
||||||
s.licenseValue.Store(license)
|
s.licenseValue.Store(license)
|
||||||
|
if s.platform != nil {
|
||||||
|
s.platform.SetLicense(license)
|
||||||
|
}
|
||||||
|
|
||||||
s.clientLicenseValue.Store(utils.GetClientLicense(license))
|
s.clientLicenseValue.Store(utils.GetClientLicense(license))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
s.licenseValue.Store((*model.License)(nil))
|
s.licenseValue.Store((*model.License)(nil))
|
||||||
s.clientLicenseValue.Store(map[string]string(nil))
|
s.clientLicenseValue.Store(map[string]string(nil))
|
||||||
|
if s.platform != nil {
|
||||||
|
s.platform.SetLicense((*model.License)(nil))
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,7 +320,7 @@ func (s *Server) RemoveLicense() *model.AppError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.SetLicense(nil)
|
s.SetLicense(nil)
|
||||||
s.ReloadConfig()
|
s.platform.ReloadConfig()
|
||||||
s.InvalidateAllCaches()
|
s.InvalidateAllCaches()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -360,7 +373,7 @@ func (s *Server) RequestTrialLicense(trialRequest *model.TrialLicenseRequest) *m
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.ReloadConfig()
|
s.platform.ReloadConfig()
|
||||||
s.InvalidateAllCaches()
|
s.InvalidateAllCaches()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -69,9 +69,9 @@ func (s *Server) doAdvancedPermissionsMigration() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
config := s.Config()
|
config := s.platform.Config()
|
||||||
*config.ServiceSettings.PostEditTimeLimit = -1
|
*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))
|
mlog.Error("Failed to update config in Advanced Permissions Phase 1 Migration.", mlog.Err(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,7 +327,7 @@ func (s *Server) doContentExtractionConfigDefaultTrueMigration() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.UpdateConfig(func(config *model.Config) {
|
s.platform.UpdateConfig(func(config *model.Config) {
|
||||||
config.FileSettings.ExtractContent = model.NewBool(true)
|
config.FileSettings.ExtractContent = model.NewBool(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -474,7 +474,7 @@ const existingInstallationPostsThreshold = 10
|
|||||||
func (s *Server) doFirstAdminSetupCompleteMigration() {
|
func (s *Server) doFirstAdminSetupCompleteMigration() {
|
||||||
// Don't run the migration until the flag is turned on.
|
// Don't run the migration until the flag is turned on.
|
||||||
|
|
||||||
if !s.Config().FeatureFlags.UseCaseOnboarding {
|
if !s.platform.Config().FeatureFlags.UseCaseOnboarding {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -290,7 +290,7 @@ func (a *App) UpdateMobileAppBadge(userID string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) createPushNotificationsHub(c request.CTX) {
|
func (s *Server) createPushNotificationsHub(c request.CTX) {
|
||||||
buffer := *s.Config().EmailSettings.PushNotificationBuffer
|
buffer := *s.platform.Config().EmailSettings.PushNotificationBuffer
|
||||||
hub := PushNotificationsHub{
|
hub := PushNotificationsHub{
|
||||||
notificationsChan: make(chan PushNotification, buffer),
|
notificationsChan: make(chan PushNotification, buffer),
|
||||||
app: New(ServerConnector(s.Channels())),
|
app: New(ServerConnector(s.Channels())),
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/require"
|
"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/config"
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"github.com/mattermost/mattermost-server/v6/model"
|
||||||
fmocks "github.com/mattermost/mattermost-server/v6/shared/filestore/mocks"
|
fmocks "github.com/mattermost/mattermost-server/v6/shared/filestore/mocks"
|
||||||
@@ -1443,9 +1444,13 @@ func TestPushNotificationRace(t *testing.T) {
|
|||||||
Router: mux.NewRouter(),
|
Router: mux.NewRouter(),
|
||||||
filestore: &fmocks.FileBackend{},
|
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{
|
serviceMap := map[ServiceKey]any{
|
||||||
ConfigKey: s.configStore,
|
ConfigKey: s.platform,
|
||||||
LicenseKey: &licenseWrapper{s},
|
LicenseKey: &licenseWrapper{s},
|
||||||
FilestoreKey: s.filestore,
|
FilestoreKey: s.filestore,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package app
|
|||||||
import (
|
import (
|
||||||
"github.com/pkg/errors"
|
"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/config"
|
||||||
"github.com/mattermost/mattermost-server/v6/einterfaces"
|
"github.com/mattermost/mattermost-server/v6/einterfaces"
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"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")
|
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
|
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.
|
// ConfigStore applies the given config store, typically to replace the traditional sources with a memory store for testing.
|
||||||
func ConfigStore(configStore *config.Store) Option {
|
func ConfigStore(configStore *config.Store) Option {
|
||||||
return func(s *Server) error {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
12
app/platform/cluster.go
Обычный файл
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 (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/config"
|
"github.com/mattermost/mattermost-server/v6/config"
|
||||||
"github.com/mattermost/mattermost-server/v6/einterfaces"
|
"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"
|
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,7 +35,145 @@ func (c *ServiceConfig) validate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.Logger == nil {
|
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
|
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
Обычный файл
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
Обычный файл
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
Обычный файл
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
Обычный файл
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
Обычный файл
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
|
package platform
|
||||||
|
|
||||||
import (
|
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/config"
|
||||||
"github.com/mattermost/mattermost-server/v6/einterfaces"
|
"github.com/mattermost/mattermost-server/v6/einterfaces"
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||||
@@ -19,6 +24,14 @@ type PlatformService struct {
|
|||||||
|
|
||||||
metrics *platformMetrics
|
metrics *platformMetrics
|
||||||
|
|
||||||
|
featureFlagSynchronizerMutex sync.Mutex
|
||||||
|
featureFlagSynchronizer *featureflag.Synchronizer
|
||||||
|
featureFlagStop chan struct{}
|
||||||
|
featureFlagStopped chan struct{}
|
||||||
|
|
||||||
|
licenseValue atomic.Value
|
||||||
|
telemetryId string
|
||||||
|
|
||||||
cluster einterfaces.ClusterInterface
|
cluster einterfaces.ClusterInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,3 +62,18 @@ func (ps *PlatformService) ShutdownMetrics() error {
|
|||||||
|
|
||||||
return nil
|
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) {
|
func (s *Server) getPublicKey(name string) ([]byte, *model.AppError) {
|
||||||
data, err := s.configStore.GetFile(name)
|
data, err := s.platform.GetConfigFile(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, model.NewAppError("GetPublicKey", "app.plugin.get_public_key.get_file.app_error", nil, err.Error(), http.StatusInternalServerError)
|
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 {
|
if err != nil {
|
||||||
return model.NewAppError("AddPublicKey", "app.plugin.write_file.read.app_error", nil, err.Error(), http.StatusInternalServerError)
|
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 {
|
if err != nil {
|
||||||
return model.NewAppError("AddPublicKey", "app.plugin.write_file.saving.app_error", nil, err.Error(), http.StatusInternalServerError)
|
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)
|
return model.NewAppError("AddPublicKey", "app.plugin.modify_saml.app_error", nil, "", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
filename := filepath.Base(name)
|
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)
|
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() {
|
func (s *Server) initPostMetadata() {
|
||||||
// Dump any cached links if the proxy settings have changed so image URLs can be updated
|
// 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) ||
|
if (before.ImageProxySettings.Enable != after.ImageProxySettings.Enable) ||
|
||||||
(before.ImageProxySettings.ImageProxyType != after.ImageProxySettings.ImageProxyType) ||
|
(before.ImageProxySettings.ImageProxyType != after.ImageProxySettings.ImageProxyType) ||
|
||||||
(before.ImageProxySettings.RemoteImageProxyURL != after.ImageProxySettings.RemoteImageProxyURL) ||
|
(before.ImageProxySettings.RemoteImageProxyURL != after.ImageProxySettings.RemoteImageProxyURL) ||
|
||||||
|
|||||||
@@ -796,7 +796,7 @@ func TestPreparePostForClientWithImageProxy(t *testing.T) {
|
|||||||
*cfg.ImageProxySettings.RemoteImageProxyOptions = "foo"
|
*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
|
return th
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -472,7 +472,7 @@ func TestImageProxy(t *testing.T) {
|
|||||||
*cfg.ServiceSettings.SiteURL = "http://mymattermost.com"
|
*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 {
|
for name, tc := range map[string]struct {
|
||||||
ProxyType string
|
ProxyType string
|
||||||
@@ -686,7 +686,7 @@ func TestCreatePost(t *testing.T) {
|
|||||||
*cfg.ImageProxySettings.RemoteImageProxyOptions = "foo"
|
*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"
|
imageURL := "http://mydomain.com/myimage"
|
||||||
proxiedImageURL := "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage"
|
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"
|
*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"
|
imageURL := "http://mydomain.com/myimage"
|
||||||
proxiedImageURL := "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage"
|
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"
|
*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"
|
imageURL := "http://mydomain.com/myimage"
|
||||||
proxiedImageURL := "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage"
|
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()
|
th := Setup(t).InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
|
|
||||||
th.Server.configStore.SetReadOnlyFF(false)
|
th.Server.platform.SetConfigReadOnlyFF(false)
|
||||||
defer th.Server.configStore.SetReadOnlyFF(true)
|
defer th.Server.platform.SetConfigReadOnlyFF(true)
|
||||||
|
|
||||||
userId := th.BasicUser.Id
|
userId := th.BasicUser.Id
|
||||||
user2Id := th.BasicUser2.Id
|
user2Id := th.BasicUser2.Id
|
||||||
@@ -261,8 +261,8 @@ func TestGetTopReactionsForUserSince(t *testing.T) {
|
|||||||
th := Setup(t).InitBasic()
|
th := Setup(t).InitBasic()
|
||||||
defer th.TearDown()
|
defer th.TearDown()
|
||||||
|
|
||||||
th.Server.configStore.SetReadOnlyFF(false)
|
th.Server.platform.SetConfigReadOnlyFF(false)
|
||||||
defer th.Server.configStore.SetReadOnlyFF(true)
|
defer th.Server.platform.SetConfigReadOnlyFF(true)
|
||||||
|
|
||||||
userId := th.BasicUser.Id
|
userId := th.BasicUser.Id
|
||||||
|
|
||||||
|
|||||||
12
app/saml.go
12
app/saml.go
@@ -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)
|
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 {
|
if err != nil {
|
||||||
return model.NewAppError("AddSamlCertificate", "api.admin.add_certificate.saving.app_error", nil, err.Error(), http.StatusInternalServerError)
|
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 {
|
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)
|
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 {
|
func (a *App) GetSamlCertificateStatus() *model.SamlCertificateStatus {
|
||||||
status := &model.SamlCertificateStatus{}
|
status := &model.SamlCertificateStatus{}
|
||||||
|
|
||||||
status.IdpCertificateFile, _ = a.Srv().configStore.HasFile(*a.Config().SamlSettings.IdpCertificateFile)
|
status.IdpCertificateFile, _ = a.Srv().platform.HasConfigFile(*a.Config().SamlSettings.IdpCertificateFile)
|
||||||
status.PrivateKeyFile, _ = a.Srv().configStore.HasFile(*a.Config().SamlSettings.PrivateKeyFile)
|
status.PrivateKeyFile, _ = a.Srv().platform.HasConfigFile(*a.Config().SamlSettings.PrivateKeyFile)
|
||||||
status.PublicCertificateFile, _ = a.Srv().configStore.HasFile(*a.Config().SamlSettings.PublicCertificateFile)
|
status.PublicCertificateFile, _ = a.Srv().platform.HasConfigFile(*a.Config().SamlSettings.PublicCertificateFile)
|
||||||
|
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
@@ -267,7 +267,7 @@ func (a *App) SetSamlIdpCertificateFromMetadata(data []byte) *model.AppError {
|
|||||||
Bytes: block.Bytes,
|
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)
|
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() {
|
func (s *Server) DoSecurityUpdateCheck() {
|
||||||
if !*s.Config().ServiceSettings.EnableSecurityFixAlert {
|
if !*s.platform.Config().ServiceSettings.EnableSecurityFixAlert {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ func (s *Server) DoSecurityUpdateCheck() {
|
|||||||
v.Set(PropSecurityID, s.TelemetryId())
|
v.Set(PropSecurityID, s.TelemetryId())
|
||||||
v.Set(PropSecurityBuild, model.CurrentVersion+"."+model.BuildNumber)
|
v.Set(PropSecurityBuild, model.CurrentVersion+"."+model.BuildNumber)
|
||||||
v.Set(PropSecurityEnterpriseReady, model.BuildEnterpriseReady)
|
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)
|
v.Set(PropSecurityOS, runtime.GOOS)
|
||||||
|
|
||||||
if props[model.SystemRanUnitTests] != "" {
|
if props[model.SystemRanUnitTests] != "" {
|
||||||
|
|||||||
236
app/server.go
236
app/server.go
@@ -31,7 +31,6 @@ import (
|
|||||||
"golang.org/x/crypto/acme/autocert"
|
"golang.org/x/crypto/acme/autocert"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/app/email"
|
"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/platform"
|
||||||
"github.com/mattermost/mattermost-server/v6/app/request"
|
"github.com/mattermost/mattermost-server/v6/app/request"
|
||||||
"github.com/mattermost/mattermost-server/v6/app/teams"
|
"github.com/mattermost/mattermost-server/v6/app/teams"
|
||||||
@@ -168,7 +167,6 @@ type Server struct {
|
|||||||
searchConfigListenerId string
|
searchConfigListenerId string
|
||||||
searchLicenseListenerId string
|
searchLicenseListenerId string
|
||||||
loggerLicenseListenerId string
|
loggerLicenseListenerId string
|
||||||
configStore *configWrapper
|
|
||||||
filestore filestore.FileBackend
|
filestore filestore.FileBackend
|
||||||
|
|
||||||
platform *platform.PlatformService
|
platform *platform.PlatformService
|
||||||
@@ -201,11 +199,6 @@ type Server struct {
|
|||||||
|
|
||||||
tracer *tracing.Tracer
|
tracer *tracing.Tracer
|
||||||
|
|
||||||
featureFlagSynchronizer *featureflag.Synchronizer
|
|
||||||
featureFlagStop chan struct{}
|
|
||||||
featureFlagStopped chan struct{}
|
|
||||||
featureFlagSynchronizerMutex sync.Mutex
|
|
||||||
|
|
||||||
products map[string]Product
|
products map[string]Product
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,7 +230,7 @@ func NewServer(options ...Option) (*Server, error) {
|
|||||||
// and has dependency requirements with the previous step.
|
// and has dependency requirements with the previous step.
|
||||||
//
|
//
|
||||||
// Step 1: Config.
|
// Step 1: Config.
|
||||||
if s.configStore == nil {
|
if s.platform == nil {
|
||||||
innerStore, err := config.NewFileStore("config.json", true)
|
innerStore, err := config.NewFileStore("config.json", true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to load config")
|
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")
|
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
|
// Step 2: Logging
|
||||||
@@ -255,7 +266,7 @@ func NewServer(options ...Option) (*Server, error) {
|
|||||||
mlog.Error("Could not initiate logging", mlog.Err(err))
|
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 {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to parse SiteURL subpath")
|
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.
|
// This is called after initLogging() to avoid a race condition.
|
||||||
mlog.Info("Server is initializing...", mlog.String("go_version", runtime.Version()))
|
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
|
// Step 3: Search Engine
|
||||||
// Depends on Step 1 (config).
|
// Depends on Step 1 (config).
|
||||||
searchEngine := searchengine.NewBroker(s.Config())
|
searchEngine := searchengine.NewBroker(s.platform.Config())
|
||||||
bleveEngine := bleveengine.NewBleveEngine(s.Config())
|
bleveEngine := bleveengine.NewBleveEngine(s.platform.Config())
|
||||||
if err := bleveEngine.Start(); err != nil {
|
if err := bleveEngine.Start(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -280,22 +291,6 @@ func NewServer(options ...Option) (*Server, error) {
|
|||||||
// Depends on step 3 (s.SearchEngine must be non-nil)
|
// Depends on step 3 (s.SearchEngine must be non-nil)
|
||||||
s.initEnterprise()
|
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.
|
// Step 5: Cache provider.
|
||||||
// At the moment we only have this implementation
|
// At the moment we only have this implementation
|
||||||
// in the future the cache provider will be built based on the loaded config
|
// 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).
|
// Depends on Step 1 (config), 4 (metrics, cluster) and 5 (cacheProvider).
|
||||||
if s.newStore == nil {
|
if s.newStore == nil {
|
||||||
s.newStore = func() (store.Store, error) {
|
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(
|
lcl, err2 := localcachelayer.NewLocalCacheLayer(
|
||||||
retrylayer.New(s.sqlStore),
|
retrylayer.New(s.sqlStore),
|
||||||
@@ -323,10 +318,10 @@ func NewServer(options ...Option) (*Server, error) {
|
|||||||
searchStore := searchlayer.NewSearchLayer(
|
searchStore := searchlayer.NewSearchLayer(
|
||||||
lcl,
|
lcl,
|
||||||
s.SearchEngine,
|
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)
|
searchStore.UpdateConfig(cfg)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -352,7 +347,7 @@ func NewServer(options ...Option) (*Server, error) {
|
|||||||
UserStore: s.Store.User(),
|
UserStore: s.Store.User(),
|
||||||
SessionStore: s.Store.Session(),
|
SessionStore: s.Store.Session(),
|
||||||
OAuthStore: s.Store.OAuth(),
|
OAuthStore: s.Store.OAuth(),
|
||||||
ConfigFn: s.Config,
|
ConfigFn: s.platform.Config,
|
||||||
Metrics: s.GetMetrics(),
|
Metrics: s.GetMetrics(),
|
||||||
Cluster: s.Cluster,
|
Cluster: s.Cluster,
|
||||||
LicenseFn: s.License,
|
LicenseFn: s.License,
|
||||||
@@ -376,9 +371,9 @@ func NewServer(options ...Option) (*Server, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
license := s.License()
|
license := s.License()
|
||||||
insecure := s.Config().ServiceSettings.EnableInsecureOutgoingConnections
|
insecure := s.platform.Config().ServiceSettings.EnableInsecureOutgoingConnections
|
||||||
// Step 7: Initialize filestore
|
// 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 {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to initialize filebackend")
|
return nil, errors.Wrap(err, "failed to initialize filebackend")
|
||||||
}
|
}
|
||||||
@@ -398,7 +393,7 @@ func NewServer(options ...Option) (*Server, error) {
|
|||||||
GroupStore: s.Store.Group(),
|
GroupStore: s.Store.Group(),
|
||||||
Users: s.userService,
|
Users: s.userService,
|
||||||
WebHub: s,
|
WebHub: s,
|
||||||
ConfigFn: s.Config,
|
ConfigFn: s.platform.Config,
|
||||||
LicenseFn: s.License,
|
LicenseFn: s.License,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -410,7 +405,7 @@ func NewServer(options ...Option) (*Server, error) {
|
|||||||
|
|
||||||
serviceMap := map[ServiceKey]any{
|
serviceMap := map[ServiceKey]any{
|
||||||
ChannelKey: &channelsWrapper{srv: s},
|
ChannelKey: &channelsWrapper{srv: s},
|
||||||
ConfigKey: s.configStore,
|
ConfigKey: s.platform,
|
||||||
LicenseKey: s.licenseWrapper,
|
LicenseKey: s.licenseWrapper,
|
||||||
FilestoreKey: s.filestore,
|
FilestoreKey: s.filestore,
|
||||||
FileInfoStoreKey: &fileInfoWrapper{srv: s},
|
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.
|
// 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") {
|
if strings.Contains(SentryDSN, "placeholder") {
|
||||||
mlog.Warn("Sentry reporting is enabled, but SENTRY_DSN is not set. Disabling reporting.")
|
mlog.Warn("Sentry reporting is enabled, but SENTRY_DSN is not set. Disabling reporting.")
|
||||||
} else {
|
} 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()
|
tracer, err2 := tracing.New()
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
return nil, err2
|
return nil, err2
|
||||||
@@ -496,7 +491,7 @@ func NewServer(options ...Option) (*Server, error) {
|
|||||||
|
|
||||||
s.createPushNotificationsHub(request.EmptyContext(s.GetLogger()))
|
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")
|
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.htmlTemplateWatcher = htmlTemplateWatcher
|
||||||
|
|
||||||
s.configListenerId = s.AddConfigListener(func(_, _ *model.Config) {
|
s.configListenerId = s.platform.AddConfigListener(func(_, _ *model.Config) {
|
||||||
ch := s.Channels()
|
ch := s.Channels()
|
||||||
ch.regenerateClientConfig()
|
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.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{
|
emailService, err := email.NewService(email.ServiceConfig{
|
||||||
ConfigFn: s.Config,
|
ConfigFn: s.platform.Config,
|
||||||
LicenseFn: s.License,
|
LicenseFn: s.License,
|
||||||
GoFn: s.Go,
|
GoFn: s.Go,
|
||||||
TemplatesContainer: s.TemplatesContainer(),
|
TemplatesContainer: s.TemplatesContainer(),
|
||||||
@@ -558,7 +554,7 @@ func NewServer(options ...Option) (*Server, error) {
|
|||||||
}
|
}
|
||||||
s.EmailService = emailService
|
s.EmailService = emailService
|
||||||
|
|
||||||
s.setupFeatureFlags()
|
s.platform.SetupFeatureFlags()
|
||||||
|
|
||||||
s.initJobs()
|
s.initJobs()
|
||||||
|
|
||||||
@@ -567,7 +563,7 @@ func NewServer(options ...Option) (*Server, error) {
|
|||||||
if s.Jobs != nil {
|
if s.Jobs != nil {
|
||||||
s.Jobs.HandleClusterLeaderChange(s.IsLeader())
|
s.Jobs.HandleClusterLeaderChange(s.IsLeader())
|
||||||
}
|
}
|
||||||
s.setupFeatureFlags()
|
s.platform.SetupFeatureFlags()
|
||||||
})
|
})
|
||||||
|
|
||||||
// If configured with a subpath, redirect 404s at the root back into the subpath.
|
// 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")
|
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
|
// 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()
|
s.EmailService.InitEmailBatching()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -604,7 +600,7 @@ func NewServer(options ...Option) (*Server, error) {
|
|||||||
|
|
||||||
pwd, _ := os.Getwd()
|
pwd, _ := os.Getwd()
|
||||||
mlog.Info("Printing current working", mlog.String("directory", pwd))
|
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
|
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
|
// Enable developer settings if this is a "dev" build
|
||||||
if model.BuildNumber == "dev" {
|
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 {
|
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()
|
searchConfigListenerId, searchLicenseListenerId := s.StartSearchEngine()
|
||||||
s.searchConfigListenerId = searchConfigListenerId
|
s.searchConfigListenerId = searchConfigListenerId
|
||||||
s.searchLicenseListenerId = searchLicenseListenerId
|
s.searchLicenseListenerId = searchLicenseListenerId
|
||||||
|
|
||||||
// if enabled - perform initial product notices fetch
|
// 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() {
|
go func() {
|
||||||
appInstance := New(ServerConnector(s.Channels()))
|
appInstance := New(ServerConnector(s.Channels()))
|
||||||
if err := appInstance.UpdateProductNotices(); err != nil {
|
if err := appInstance.UpdateProductNotices(); err != nil {
|
||||||
@@ -668,7 +664,7 @@ func NewServer(options ...Option) (*Server, error) {
|
|||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
s.AddConfigListener(func(old, new *model.Config) {
|
s.platform.AddConfigListener(func(old, new *model.Config) {
|
||||||
appInstance := New(ServerConnector(s.Channels()))
|
appInstance := New(ServerConnector(s.Channels()))
|
||||||
if *old.GuestAccountsSettings.Enable && !*new.GuestAccountsSettings.Enable {
|
if *old.GuestAccountsSettings.Enable && !*new.GuestAccountsSettings.Enable {
|
||||||
c := request.EmptyContext(s.GetLogger())
|
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
|
// 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()))
|
appInstance := New(ServerConnector(s.Channels()))
|
||||||
c := request.EmptyContext(s.GetLogger())
|
c := request.EmptyContext(s.GetLogger())
|
||||||
if appErr := appInstance.DeactivateGuests(c); appErr != nil {
|
if appErr := appInstance.DeactivateGuests(c); appErr != nil {
|
||||||
@@ -703,7 +699,7 @@ func NewServer(options ...Option) (*Server, error) {
|
|||||||
s.initPostMetadata()
|
s.initPostMetadata()
|
||||||
|
|
||||||
// Dump the image cache if the proxy settings have changed. (need switch URLs to the correct proxy)
|
// 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) ||
|
if (oldCfg.ImageProxySettings.Enable != newCfg.ImageProxySettings.Enable) ||
|
||||||
(oldCfg.ImageProxySettings.ImageProxyType != newCfg.ImageProxySettings.ImageProxyType) ||
|
(oldCfg.ImageProxySettings.ImageProxyType != newCfg.ImageProxySettings.ImageProxyType) ||
|
||||||
(oldCfg.ImageProxySettings.RemoteImageProxyURL != newCfg.ImageProxySettings.RemoteImageProxyURL) ||
|
(oldCfg.ImageProxySettings.RemoteImageProxyURL != newCfg.ImageProxySettings.RemoteImageProxyURL) ||
|
||||||
@@ -755,18 +751,18 @@ func (s *Server) runJobs() {
|
|||||||
complianceI.StartComplianceDailyJob()
|
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 {
|
if err := s.Jobs.StartWorkers(); err != nil {
|
||||||
mlog.Error("Failed to start job server workers", mlog.Err(err))
|
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 {
|
if err := s.Jobs.StartSchedulers(); err != nil {
|
||||||
mlog.Error("Failed to start job server schedulers", mlog.Err(err))
|
mlog.Error("Failed to start job server schedulers", mlog.Err(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if *s.Config().ServiceSettings.EnableAWSMetering {
|
if *s.platform.Config().ServiceSettings.EnableAWSMetering {
|
||||||
runReportToAWSMeterJob(s)
|
runReportToAWSMeterJob(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -786,7 +782,7 @@ func (s *Server) Channels() *Channels {
|
|||||||
// Return Database type (postgres or mysql) and current version of the schema
|
// Return Database type (postgres or mysql) and current version of the schema
|
||||||
func (s *Server) DatabaseTypeAndSchemaVersion() (string, string) {
|
func (s *Server) DatabaseTypeAndSchemaVersion() (string, string) {
|
||||||
schemaVersion, _ := s.Store.GetDBSchemaVersion()
|
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.
|
// 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"))
|
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 the config is locked then a unit test has already configured and locked the logger; not an error.
|
||||||
if !errors.Is(err, mlog.ErrConfigurationLock) {
|
if !errors.Is(err, mlog.ErrConfigurationLock) {
|
||||||
// revert to default logger if the config is invalid
|
// 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).
|
// Use the app logger as the global logger (eventually remove all instances of global logging).
|
||||||
mlog.InitGlobalLogger(s.Log)
|
mlog.InitGlobalLogger(s.Log)
|
||||||
|
|
||||||
notificationLogSettings := config.GetLogSettingsFromNotificationsLogSettings(&s.Config().NotificationLogSettings)
|
notificationLogSettings := config.GetLogSettingsFromNotificationsLogSettings(&s.platform.Config().NotificationLogSettings)
|
||||||
if err := s.configureLogger("notification logging", s.NotificationsLog, notificationLogSettings, s.configStore.Store, config.GetNotificationsLogFileLocation); err != nil {
|
if err := s.platform.ConfigureLogger("notification logging", s.NotificationsLog, notificationLogSettings, config.GetNotificationsLogFileLocation); err != nil {
|
||||||
if !errors.Is(err, mlog.ErrConfigurationLock) {
|
if !errors.Is(err, mlog.ErrConfigurationLock) {
|
||||||
mlog.Error("Error configuring notification logger", mlog.Err(err))
|
mlog.Error("Error configuring notification logger", mlog.Err(err))
|
||||||
return err
|
return err
|
||||||
@@ -834,33 +830,6 @@ func (s *Server) initLogging() error {
|
|||||||
return nil
|
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.
|
// removeUnlicensedLogTargets removes any unlicensed log target types.
|
||||||
func (s *Server) removeUnlicensedLogTargets(license *model.License) {
|
func (s *Server) removeUnlicensedLogTargets(license *model.License) {
|
||||||
if license != nil && *license.Features.AdvancedLogging {
|
if license != nil && *license.Features.AdvancedLogging {
|
||||||
@@ -895,7 +864,7 @@ func (s *Server) startInterClusterServices(license *model.License) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Config check
|
// Config check
|
||||||
if !*s.Config().ExperimentalSettings.EnableRemoteClusterService {
|
if !*s.platform.Config().ExperimentalSettings.EnableRemoteClusterService {
|
||||||
mlog.Debug("Remote Cluster Service disabled via config")
|
mlog.Debug("Remote Cluster Service disabled via config")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -924,7 +893,7 @@ func (s *Server) startInterClusterServices(license *model.License) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Config check
|
// Config check
|
||||||
if !*s.Config().ExperimentalSettings.EnableSharedChannels {
|
if !*s.platform.Config().ExperimentalSettings.EnableSharedChannels {
|
||||||
mlog.Debug("Shared Channels Service disabled via config")
|
mlog.Debug("Shared Channels Service disabled via config")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -1029,14 +998,16 @@ func (s *Server) Shutdown() {
|
|||||||
|
|
||||||
s.WaitForGoroutines()
|
s.WaitForGoroutines()
|
||||||
|
|
||||||
s.RemoveConfigListener(s.configListenerId)
|
s.platform.RemoveConfigListener(s.configListenerId)
|
||||||
s.stopSearchEngine()
|
s.stopSearchEngine()
|
||||||
|
|
||||||
s.Audit.Shutdown()
|
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 {
|
if s.Cluster != nil {
|
||||||
s.Cluster.StopInterNodeCommunication()
|
s.Cluster.StopInterNodeCommunication()
|
||||||
@@ -1239,23 +1210,23 @@ func (s *Server) Start() error {
|
|||||||
|
|
||||||
s.checkPushNotificationServerURL()
|
s.checkPushNotificationServerURL()
|
||||||
|
|
||||||
s.ReloadConfig()
|
s.platform.ReloadConfig()
|
||||||
|
|
||||||
mlog.Info("Starting Server...")
|
mlog.Info("Starting Server...")
|
||||||
|
|
||||||
var handler http.Handler = s.RootRouter
|
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{
|
sentryHandler := sentryhttp.New(sentryhttp.Options{
|
||||||
Repanic: true,
|
Repanic: true,
|
||||||
})
|
})
|
||||||
handler = sentryHandler.Handle(handler)
|
handler = sentryHandler.Handle(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
if allowedOrigins := *s.Config().ServiceSettings.AllowCorsFrom; allowedOrigins != "" {
|
if allowedOrigins := *s.platform.Config().ServiceSettings.AllowCorsFrom; allowedOrigins != "" {
|
||||||
exposedCorsHeaders := *s.Config().ServiceSettings.CorsExposedHeaders
|
exposedCorsHeaders := *s.platform.Config().ServiceSettings.CorsExposedHeaders
|
||||||
allowCredentials := *s.Config().ServiceSettings.CorsAllowCredentials
|
allowCredentials := *s.platform.Config().ServiceSettings.CorsAllowCredentials
|
||||||
debug := *s.Config().ServiceSettings.CorsDebug
|
debug := *s.platform.Config().ServiceSettings.CorsDebug
|
||||||
corsWrapper := cors.New(cors.Options{
|
corsWrapper := cors.New(cors.Options{
|
||||||
AllowedOrigins: strings.Fields(allowedOrigins),
|
AllowedOrigins: strings.Fields(allowedOrigins),
|
||||||
AllowedMethods: corsAllowedMethods,
|
AllowedMethods: corsAllowedMethods,
|
||||||
@@ -1274,10 +1245,10 @@ func (s *Server) Start() error {
|
|||||||
handler = corsWrapper.Handler(handler)
|
handler = corsWrapper.Handler(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
if *s.Config().RateLimitSettings.Enable {
|
if *s.platform.Config().RateLimitSettings.Enable {
|
||||||
mlog.Info("RateLimiter is enabled")
|
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 {
|
if err2 != nil {
|
||||||
return err2
|
return err2
|
||||||
}
|
}
|
||||||
@@ -1292,15 +1263,15 @@ func (s *Server) Start() error {
|
|||||||
|
|
||||||
s.Server = &http.Server{
|
s.Server = &http.Server{
|
||||||
Handler: handler,
|
Handler: handler,
|
||||||
ReadTimeout: time.Duration(*s.Config().ServiceSettings.ReadTimeout) * time.Second,
|
ReadTimeout: time.Duration(*s.platform.Config().ServiceSettings.ReadTimeout) * time.Second,
|
||||||
WriteTimeout: time.Duration(*s.Config().ServiceSettings.WriteTimeout) * time.Second,
|
WriteTimeout: time.Duration(*s.platform.Config().ServiceSettings.WriteTimeout) * time.Second,
|
||||||
IdleTimeout: time.Duration(*s.Config().ServiceSettings.IdleTimeout) * time.Second,
|
IdleTimeout: time.Duration(*s.platform.Config().ServiceSettings.IdleTimeout) * time.Second,
|
||||||
ErrorLog: errStdLog,
|
ErrorLog: errStdLog,
|
||||||
}
|
}
|
||||||
|
|
||||||
addr := *s.Config().ServiceSettings.ListenAddress
|
addr := *s.platform.Config().ServiceSettings.ListenAddress
|
||||||
if addr == "" {
|
if addr == "" {
|
||||||
if *s.Config().ServiceSettings.ConnectionSecurity == model.ConnSecurityTLS {
|
if *s.platform.Config().ServiceSettings.ConnectionSecurity == model.ConnSecurityTLS {
|
||||||
addr = ":https"
|
addr = ":https"
|
||||||
} else {
|
} else {
|
||||||
addr = ":http"
|
addr = ":http"
|
||||||
@@ -1317,11 +1288,11 @@ func (s *Server) Start() error {
|
|||||||
mlog.Info(logListeningPort, mlog.String("address", listener.Addr().String()))
|
mlog.Info(logListeningPort, mlog.String("address", listener.Addr().String()))
|
||||||
|
|
||||||
m := &autocert.Manager{
|
m := &autocert.Manager{
|
||||||
Cache: autocert.DirCache(*s.Config().ServiceSettings.LetsEncryptCertificateCacheFile),
|
Cache: autocert.DirCache(*s.platform.Config().ServiceSettings.LetsEncryptCertificateCacheFile),
|
||||||
Prompt: autocert.AcceptTOS,
|
Prompt: autocert.AcceptTOS,
|
||||||
}
|
}
|
||||||
|
|
||||||
if *s.Config().ServiceSettings.Forward80To443 {
|
if *s.platform.Config().ServiceSettings.Forward80To443 {
|
||||||
if host, port, err := net.SplitHostPort(addr); err != nil {
|
if host, port, err := net.SplitHostPort(addr); err != nil {
|
||||||
mlog.Error("Unable to setup forwarding", mlog.Err(err))
|
mlog.Error("Unable to setup forwarding", mlog.Err(err))
|
||||||
} else if port != "443" {
|
} else if port != "443" {
|
||||||
@@ -1329,7 +1300,7 @@ func (s *Server) Start() error {
|
|||||||
} else {
|
} else {
|
||||||
httpListenAddress := net.JoinHostPort(host, "http")
|
httpListenAddress := net.JoinHostPort(host, "http")
|
||||||
|
|
||||||
if *s.Config().ServiceSettings.UseLetsEncrypt {
|
if *s.platform.Config().ServiceSettings.UseLetsEncrypt {
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
Addr: httpListenAddress,
|
Addr: httpListenAddress,
|
||||||
Handler: m.HTTPHandler(nil),
|
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"))
|
return errors.New(i18n.T("api.server.start_server.forward80to443.disabled_while_using_lets_encrypt"))
|
||||||
}
|
}
|
||||||
|
|
||||||
s.didFinishListen = make(chan struct{})
|
s.didFinishListen = make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
var err error
|
var err error
|
||||||
if *s.Config().ServiceSettings.ConnectionSecurity == model.ConnSecurityTLS {
|
if *s.platform.Config().ServiceSettings.ConnectionSecurity == model.ConnSecurityTLS {
|
||||||
|
|
||||||
tlsConfig := &tls.Config{
|
tlsConfig := &tls.Config{
|
||||||
PreferServerCipherSuites: true,
|
PreferServerCipherSuites: true,
|
||||||
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
|
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
|
||||||
}
|
}
|
||||||
|
|
||||||
switch *s.Config().ServiceSettings.TLSMinVer {
|
switch *s.platform.Config().ServiceSettings.TLSMinVer {
|
||||||
case "1.0":
|
case "1.0":
|
||||||
tlsConfig.MinVersion = tls.VersionTLS10
|
tlsConfig.MinVersion = tls.VersionTLS10
|
||||||
case "1.1":
|
case "1.1":
|
||||||
@@ -1385,11 +1356,11 @@ func (s *Server) Start() error {
|
|||||||
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
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
|
tlsConfig.CipherSuites = defaultCiphers
|
||||||
} else {
|
} else {
|
||||||
var cipherSuites []uint16
|
var cipherSuites []uint16
|
||||||
for _, cipher := range s.Config().ServiceSettings.TLSOverwriteCiphers {
|
for _, cipher := range s.platform.Config().ServiceSettings.TLSOverwriteCiphers {
|
||||||
value, ok := model.ServerTLSSupportedCiphers[cipher]
|
value, ok := model.ServerTLSSupportedCiphers[cipher]
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -1411,12 +1382,12 @@ func (s *Server) Start() error {
|
|||||||
certFile := ""
|
certFile := ""
|
||||||
keyFile := ""
|
keyFile := ""
|
||||||
|
|
||||||
if *s.Config().ServiceSettings.UseLetsEncrypt {
|
if *s.platform.Config().ServiceSettings.UseLetsEncrypt {
|
||||||
tlsConfig.GetCertificate = m.GetCertificate
|
tlsConfig.GetCertificate = m.GetCertificate
|
||||||
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2")
|
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2")
|
||||||
} else {
|
} else {
|
||||||
certFile = *s.Config().ServiceSettings.TLSCertFile
|
certFile = *s.platform.Config().ServiceSettings.TLSCertFile
|
||||||
keyFile = *s.Config().ServiceSettings.TLSKeyFile
|
keyFile = *s.platform.Config().ServiceSettings.TLSKeyFile
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Server.TLSConfig = tlsConfig
|
s.Server.TLSConfig = tlsConfig
|
||||||
@@ -1433,7 +1404,7 @@ func (s *Server) Start() error {
|
|||||||
close(s.didFinishListen)
|
close(s.didFinishListen)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if *s.Config().ServiceSettings.EnableLocalMode {
|
if *s.platform.Config().ServiceSettings.EnableLocalMode {
|
||||||
if err := s.startLocalModeServer(); err != nil {
|
if err := s.startLocalModeServer(); err != nil {
|
||||||
mlog.Critical(err.Error())
|
mlog.Critical(err.Error())
|
||||||
}
|
}
|
||||||
@@ -1451,7 +1422,7 @@ func (s *Server) startLocalModeServer() error {
|
|||||||
Handler: s.LocalRouter,
|
Handler: s.LocalRouter,
|
||||||
}
|
}
|
||||||
|
|
||||||
socket := *s.configStore.Get().ServiceSettings.LocalModeSocketLocation
|
socket := *s.platform.Config().ServiceSettings.LocalModeSocketLocation
|
||||||
if err := os.RemoveAll(socket); err != nil {
|
if err := os.RemoveAll(socket); err != nil {
|
||||||
return errors.Wrapf(err, i18n.T("api.server.start_server.starting.critical"), err)
|
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() {
|
func (s *Server) checkPushNotificationServerURL() {
|
||||||
notificationServer := *s.Config().EmailSettings.PushNotificationServer
|
notificationServer := *s.platform.Config().EmailSettings.PushNotificationServer
|
||||||
if strings.HasPrefix(notificationServer, "http://") {
|
if strings.HasPrefix(notificationServer, "http://") {
|
||||||
mlog.Warn("Your push notification server is configured with HTTP. For improved security, update to HTTPS in your configuration.")
|
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) {
|
func doReportUsageToAWSMeteringService(s *Server) {
|
||||||
awsMeter := awsmeter.New(s.Store, s.Config())
|
awsMeter := awsmeter.New(s.Store, s.platform.Config())
|
||||||
if awsMeter == nil {
|
if awsMeter == nil {
|
||||||
mlog.Error("Cannot obtain instance of AWS Metering Service.")
|
mlog.Error("Cannot obtain instance of AWS Metering Service.")
|
||||||
return
|
return
|
||||||
@@ -1604,12 +1575,12 @@ func doSessionCleanup(s *Server) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func doJobsCleanup(s *Server) {
|
func doJobsCleanup(s *Server) {
|
||||||
if *s.Config().JobSettings.CleanupJobsThresholdDays < 0 {
|
if *s.platform.Config().JobSettings.CleanupJobsThresholdDays < 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
mlog.Debug("Cleaning up jobs store.")
|
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))
|
expiry := model.GetMillisForTime(time.Now().Add(-dur))
|
||||||
err := s.Store.Job().Cleanup(expiry, jobsCleanupBatchSize)
|
err := s.Store.Job().Cleanup(expiry, jobsCleanupBatchSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1618,12 +1589,12 @@ func doJobsCleanup(s *Server) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func doConfigCleanup(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
|
return
|
||||||
}
|
}
|
||||||
mlog.Info("Cleaning up configuration store.")
|
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))
|
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 == "" {
|
if name == "" {
|
||||||
name = user.Username
|
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))
|
mlog.Error("Error sending license up for renewal email to", mlog.String("user_email", user.Email), mlog.Err(err))
|
||||||
countNotOks++
|
countNotOks++
|
||||||
}
|
}
|
||||||
@@ -1735,7 +1706,7 @@ func (s *Server) doLicenseExpirationCheck() {
|
|||||||
|
|
||||||
mlog.Debug("Sending license expired email.", mlog.String("user_email", user.Email))
|
mlog.Debug("Sending license expired email.", mlog.String("user_email", user.Email))
|
||||||
s.Go(func() {
|
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))
|
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 {
|
if s.SearchEngine == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1824,7 +1795,7 @@ func (s *Server) StartSearchEngine() (string, string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) stopSearchEngine() {
|
func (s *Server) stopSearchEngine() {
|
||||||
s.RemoveConfigListener(s.searchConfigListenerId)
|
s.platform.RemoveConfigListener(s.searchConfigListenerId)
|
||||||
s.RemoveLicenseListener(s.searchLicenseListenerId)
|
s.RemoveLicenseListener(s.searchLicenseListenerId)
|
||||||
if s.SearchEngine != nil && s.SearchEngine.ElasticsearchEngine != nil && s.SearchEngine.ElasticsearchEngine.IsActive() {
|
if s.SearchEngine != nil && s.SearchEngine.ElasticsearchEngine != nil && s.SearchEngine.ElasticsearchEngine.IsActive() {
|
||||||
s.SearchEngine.ElasticsearchEngine.Stop()
|
s.SearchEngine.ElasticsearchEngine.Stop()
|
||||||
@@ -1858,7 +1829,7 @@ func (ch *Channels) ClientConfigHash() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) initJobs() {
|
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 {
|
if jobsDataRetentionJobInterface != nil {
|
||||||
builder := jobsDataRetentionJobInterface(s)
|
builder := jobsDataRetentionJobInterface(s)
|
||||||
@@ -2031,7 +2002,7 @@ func (s *Server) SetSharedChannelSyncService(sharedChannelService SharedChannelS
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) GetProfileImage(user *model.User) ([]byte, bool, *model.AppError) {
|
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)
|
img, appErr := s.GetDefaultProfileImage(user)
|
||||||
if appErr != nil {
|
if appErr != nil {
|
||||||
return nil, false, appErr
|
return nil, false, appErr
|
||||||
@@ -2141,3 +2112,8 @@ func (a *App) GetAppliedSchemaMigrations() ([]model.AppliedMigration, *model.App
|
|||||||
}
|
}
|
||||||
return table, nil
|
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() {
|
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")
|
mlog.Info("No activity check because developer mode is enabled")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !*s.Config().EmailSettings.EnableInactivityEmail {
|
if !*s.platform.Config().EmailSettings.EnableInactivityEmail {
|
||||||
mlog.Info("No activity check because EnableInactivityEmail is false")
|
mlog.Info("No activity check because EnableInactivityEmail is false")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !s.Config().FeatureFlags.EnableInactivityCheckJob {
|
if !s.platform.Config().FeatureFlags.EnableInactivityCheckJob {
|
||||||
mlog.Info("No activity check because EnableInactivityCheckJob feature flag is disabled")
|
mlog.Info("No activity check because EnableInactivityCheckJob feature flag is disabled")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -70,7 +70,7 @@ func (s *Server) doInactivityCheck() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) takeInactivityAction() {
|
func (s *Server) takeInactivityAction() {
|
||||||
siteURL := *s.Config().ServiceSettings.SiteURL
|
siteURL := *s.platform.Config().ServiceSettings.SiteURL
|
||||||
if siteURL == "" {
|
if siteURL == "" {
|
||||||
mlog.Warn("No SiteURL configured")
|
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/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"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/config"
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"github.com/mattermost/mattermost-server/v6/model"
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/filestore"
|
"github.com/mattermost/mattermost-server/v6/shared/filestore"
|
||||||
@@ -83,54 +84,70 @@ func TestReadReplicaDisabledBasedOnLicense(t *testing.T) {
|
|||||||
s, err := NewServer(func(server *Server) error {
|
s, err := NewServer(func(server *Server) error {
|
||||||
configStore := config.NewTestMemoryStore()
|
configStore := config.NewTestMemoryStore()
|
||||||
configStore.Set(&cfg)
|
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
|
return nil
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer s.Shutdown()
|
defer s.Shutdown()
|
||||||
require.Same(t, s.sqlStore.GetMasterX(), s.sqlStore.GetReplicaX())
|
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) {
|
t.Run("Read Replicas With License", func(t *testing.T) {
|
||||||
s, err := NewServer(func(server *Server) error {
|
s, err := NewServer(func(server *Server) error {
|
||||||
configStore := config.NewTestMemoryStore()
|
configStore := config.NewTestMemoryStore()
|
||||||
configStore.Set(&cfg)
|
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())
|
server.licenseValue.Store(model.NewTestLicense())
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer s.Shutdown()
|
defer s.Shutdown()
|
||||||
require.NotSame(t, s.sqlStore.GetMasterX(), s.sqlStore.GetReplicaX())
|
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) {
|
t.Run("Search Replicas with no License", func(t *testing.T) {
|
||||||
s, err := NewServer(func(server *Server) error {
|
s, err := NewServer(func(server *Server) error {
|
||||||
configStore := config.NewTestMemoryStore()
|
configStore := config.NewTestMemoryStore()
|
||||||
configStore.Set(&cfg)
|
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
|
return nil
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer s.Shutdown()
|
defer s.Shutdown()
|
||||||
require.Same(t, s.sqlStore.GetMasterX(), s.sqlStore.GetSearchReplicaX())
|
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) {
|
t.Run("Search Replicas With License", func(t *testing.T) {
|
||||||
s, err := NewServer(func(server *Server) error {
|
s, err := NewServer(func(server *Server) error {
|
||||||
configStore := config.NewTestMemoryStore()
|
configStore := config.NewTestMemoryStore()
|
||||||
configStore.Set(&cfg)
|
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())
|
server.licenseValue.Store(model.NewTestLicense())
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer s.Shutdown()
|
defer s.Shutdown()
|
||||||
require.NotSame(t, s.sqlStore.GetMasterX(), s.sqlStore.GetSearchReplicaX())
|
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)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Attempt to listen on the port used above.
|
// 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()
|
*cfg.ServiceSettings.ListenAddress = listener.Addr().String()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -168,8 +185,12 @@ func TestStartServerNoS3Bucket(t *testing.T) {
|
|||||||
s, err := NewServer(func(server *Server) error {
|
s, err := NewServer(func(server *Server) error {
|
||||||
configStore, _ := config.NewFileStore("config.json", true)
|
configStore, _ := config.NewFileStore("config.json", true)
|
||||||
store, _ := config.NewStoreFromBacking(configStore, nil, false)
|
store, _ := config.NewStoreFromBacking(configStore, nil, false)
|
||||||
server.configStore = &configWrapper{srv: server, Store: store}
|
var err error
|
||||||
server.UpdateConfig(func(cfg *model.Config) {
|
server.platform, err = platform.New(platform.ServiceConfig{
|
||||||
|
ConfigStore: store,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
server.platform.UpdateConfig(func(cfg *model.Config) {
|
||||||
cfg.FileSettings = model.FileSettings{
|
cfg.FileSettings = model.FileSettings{
|
||||||
DriverName: model.NewString(model.ImageDriverS3),
|
DriverName: model.NewString(model.ImageDriverS3),
|
||||||
AmazonS3AccessKeyId: model.NewString(model.MinioAccessKey),
|
AmazonS3AccessKeyId: model.NewString(model.MinioAccessKey),
|
||||||
@@ -393,7 +414,7 @@ func TestPanicLog(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
testDir, _ := fileutils.FindDir("tests")
|
testDir, _ := fileutils.FindDir("tests")
|
||||||
s.UpdateConfig(func(cfg *model.Config) {
|
s.platform.UpdateConfig(func(cfg *model.Config) {
|
||||||
*cfg.ServiceSettings.ListenAddress = ":0"
|
*cfg.ServiceSettings.ListenAddress = ":0"
|
||||||
*cfg.ServiceSettings.ConnectionSecurity = "TLS"
|
*cfg.ServiceSettings.ConnectionSecurity = "TLS"
|
||||||
*cfg.ServiceSettings.TLSKeyFile = path.Join(testDir, "tls_test_key.pem")
|
*cfg.ServiceSettings.TLSKeyFile = path.Join(testDir, "tls_test_key.pem")
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ func TestCustomStatusErrors(t *testing.T) {
|
|||||||
UserStore: &mockUserStore,
|
UserStore: &mockUserStore,
|
||||||
SessionStore: &mockSessionStore,
|
SessionStore: &mockSessionStore,
|
||||||
OAuthStore: &mockOAuthStore,
|
OAuthStore: &mockOAuthStore,
|
||||||
ConfigFn: th.App.ch.srv.Config,
|
ConfigFn: th.App.ch.srv.platform.Config,
|
||||||
LicenseFn: th.App.ch.srv.License,
|
LicenseFn: th.App.ch.srv.License,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -1044,7 +1044,7 @@ func TestLeaveTeamPanic(t *testing.T) {
|
|||||||
UserStore: &mockUserStore,
|
UserStore: &mockUserStore,
|
||||||
SessionStore: &mocks.SessionStore{},
|
SessionStore: &mocks.SessionStore{},
|
||||||
OAuthStore: &mocks.OAuthStore{},
|
OAuthStore: &mocks.OAuthStore{},
|
||||||
ConfigFn: th.App.ch.srv.Config,
|
ConfigFn: th.App.ch.srv.platform.Config,
|
||||||
LicenseFn: th.App.ch.srv.License,
|
LicenseFn: th.App.ch.srv.License,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -1088,7 +1088,7 @@ func TestLeaveTeamPanic(t *testing.T) {
|
|||||||
GroupStore: &mocks.GroupStore{},
|
GroupStore: &mocks.GroupStore{},
|
||||||
Users: th.App.ch.srv.userService,
|
Users: th.App.ch.srv.userService,
|
||||||
WebHub: th.App.ch.srv,
|
WebHub: th.App.ch.srv,
|
||||||
ConfigFn: th.App.ch.srv.Config,
|
ConfigFn: th.App.ch.srv.platform.Config,
|
||||||
LicenseFn: th.App.ch.srv.License,
|
LicenseFn: th.App.ch.srv.License,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -1712,7 +1712,7 @@ func TestUpdateThreadReadForUser(t *testing.T) {
|
|||||||
UserStore: &mockUserStore,
|
UserStore: &mockUserStore,
|
||||||
SessionStore: &storemocks.SessionStore{},
|
SessionStore: &storemocks.SessionStore{},
|
||||||
OAuthStore: &storemocks.OAuthStore{},
|
OAuthStore: &storemocks.OAuthStore{},
|
||||||
ConfigFn: th.App.ch.srv.Config,
|
ConfigFn: th.App.ch.srv.platform.Config,
|
||||||
LicenseFn: th.App.ch.srv.License,
|
LicenseFn: th.App.ch.srv.License,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
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) {
|
t.Run("successfully create a user with insights feature flag disabled", func(t *testing.T) {
|
||||||
th.Server.configStore.SetReadOnlyFF(false)
|
th.Server.platform.SetConfigReadOnlyFF(false)
|
||||||
defer th.Server.configStore.SetReadOnlyFF(true)
|
defer th.Server.platform.SetConfigReadOnlyFF(true)
|
||||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = false })
|
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = false })
|
||||||
defer th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
|
defer th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
|
||||||
testUser := th.CreateUser()
|
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) {
|
t.Run("successfully create a guest user with initial tutorial, insights and recommended steps preferences", func(t *testing.T) {
|
||||||
th.Server.configStore.SetReadOnlyFF(false)
|
th.Server.platform.SetConfigReadOnlyFF(false)
|
||||||
defer th.Server.configStore.SetReadOnlyFF(true)
|
defer th.Server.platform.SetConfigReadOnlyFF(true)
|
||||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
|
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
|
||||||
testUser := th.CreateGuest()
|
testUser := th.CreateGuest()
|
||||||
defer th.App.PermanentDeleteUser(th.Context, testUser)
|
defer th.App.PermanentDeleteUser(th.Context, testUser)
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ func TestHubSessionRevokeRace(t *testing.T) {
|
|||||||
UserStore: &mockUserStore,
|
UserStore: &mockUserStore,
|
||||||
SessionStore: &mockSessionStore,
|
SessionStore: &mockSessionStore,
|
||||||
OAuthStore: &mockOAuthStore,
|
OAuthStore: &mockOAuthStore,
|
||||||
ConfigFn: th.App.ch.srv.Config,
|
ConfigFn: th.App.ch.srv.platform.Config,
|
||||||
Metrics: th.App.Metrics(),
|
Metrics: th.App.Metrics(),
|
||||||
Cluster: th.App.Cluster(),
|
Cluster: th.App.Cluster(),
|
||||||
LicenseFn: th.App.ch.srv.License,
|
LicenseFn: th.App.ch.srv.License,
|
||||||
|
|||||||
@@ -42,9 +42,10 @@ func initDBCommandContext(configDSN string, readOnlyConfigStore bool) (*app.App,
|
|||||||
model.AppErrorInit(i18n.T)
|
model.AppErrorInit(i18n.T)
|
||||||
|
|
||||||
s, err := app.NewServer(
|
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.Config(configDSN, readOnlyConfigStore, nil),
|
||||||
app.StartSearchEngine,
|
app.StartSearchEngine,
|
||||||
app.StartMetrics,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -65,11 +65,12 @@ func runServer(configStore *config.Store, interruptChan chan os.Signal) error {
|
|||||||
debug.SetTraceback("crash")
|
debug.SetTraceback("crash")
|
||||||
|
|
||||||
options := []app.Option{
|
options := []app.Option{
|
||||||
|
// The option order is important as app.Config option reads app.StartMetrics option.
|
||||||
|
app.StartMetrics,
|
||||||
app.ConfigStore(configStore),
|
app.ConfigStore(configStore),
|
||||||
app.RunEssentialJobs,
|
app.RunEssentialJobs,
|
||||||
app.JoinCluster,
|
app.JoinCluster,
|
||||||
app.StartSearchEngine,
|
app.StartSearchEngine,
|
||||||
app.StartMetrics,
|
|
||||||
}
|
}
|
||||||
server, err := app.NewServer(options...)
|
server, err := app.NewServer(options...)
|
||||||
if err != nil {
|
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
|
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()
|
serverErr := s.Start()
|
||||||
if serverErr != nil {
|
if serverErr != nil {
|
||||||
panic(serverErr)
|
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
|
// Disable strict password requirements for test
|
||||||
s.UpdateConfig(func(cfg *model.Config) {
|
a.UpdateConfig(func(cfg *model.Config) {
|
||||||
*cfg.PasswordSettings.MinimumLength = 5
|
*cfg.PasswordSettings.MinimumLength = 5
|
||||||
*cfg.PasswordSettings.Lowercase = false
|
*cfg.PasswordSettings.Lowercase = false
|
||||||
*cfg.PasswordSettings.Uppercase = false
|
*cfg.PasswordSettings.Uppercase = false
|
||||||
@@ -119,15 +120,13 @@ func setupTestHelper(tb testing.TB, includeCacheLayer bool) *TestHelper {
|
|||||||
*cfg.PasswordSettings.Number = false
|
*cfg.PasswordSettings.Number = false
|
||||||
})
|
})
|
||||||
|
|
||||||
a := app.New(app.ServerConnector(s.Channels()))
|
|
||||||
|
|
||||||
web := New(s)
|
web := New(s)
|
||||||
URL = fmt.Sprintf("http://localhost:%v", s.ListenAddr.Port)
|
URL = fmt.Sprintf("http://localhost:%v", s.ListenAddr.Port)
|
||||||
apiClient = model.NewAPIv4Client(URL)
|
apiClient = model.NewAPIv4Client(URL)
|
||||||
|
|
||||||
s.Store.MarkSystemRanUnitTests()
|
s.Store.MarkSystemRanUnitTests()
|
||||||
|
|
||||||
s.UpdateConfig(func(cfg *model.Config) {
|
a.UpdateConfig(func(cfg *model.Config) {
|
||||||
*cfg.TeamSettings.EnableOpenServer = true
|
*cfg.TeamSettings.EnableOpenServer = true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user