Revert "[MM-41576] Revamp database schema version (#19586)" (#19746)

* Revert "[MM-41576] Revamp database schema version (#19586)"

This reverts commit 645fee3fe3.

* Revert "MM-42049 - license endpoint not working (#19686)"

This reverts commit 4fe89e5847.
Этот коммит содержится в:
Jesse Hallam
2022-03-08 16:28:28 -04:00
коммит произвёл GitHub
родитель eff39010d8
Коммит a2a78577e9
47 изменённых файлов: 215 добавлений и 475 удалений

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

@@ -577,7 +577,7 @@ func TestGetOldClientConfig(t *testing.T) {
config, _, err := client.GetOldClientConfig("") config, _, err := client.GetOldClientConfig("")
require.NoError(t, err) require.NoError(t, err)
require.Empty(t, config["Version"], "config not returned correctly") require.NotEmpty(t, config["Version"], "config not returned correctly")
require.Empty(t, config["GoogleDeveloperKey"], "config should be missing developer key") require.Empty(t, config["GoogleDeveloperKey"], "config should be missing developer key")
}) })

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

@@ -70,7 +70,6 @@ func (api *API) InitSystem() {
api.BaseRoutes.System.Handle("/support_packet", api.APISessionRequired(generateSupportPacket)).Methods("GET") api.BaseRoutes.System.Handle("/support_packet", api.APISessionRequired(generateSupportPacket)).Methods("GET")
api.BaseRoutes.System.Handle("/onboarding/complete", api.APISessionRequired(getOnboarding)).Methods("GET") api.BaseRoutes.System.Handle("/onboarding/complete", api.APISessionRequired(getOnboarding)).Methods("GET")
api.BaseRoutes.System.Handle("/onboarding/complete", api.APISessionRequired(completeOnboarding)).Methods("POST") api.BaseRoutes.System.Handle("/onboarding/complete", api.APISessionRequired(completeOnboarding)).Methods("POST")
api.BaseRoutes.System.Handle("/schema/version", api.APISessionRequired(getAppliedSchemaMigrations)).Methods("GET")
} }
func generateSupportPacket(c *Context, w http.ResponseWriter, r *http.Request) { func generateSupportPacket(c *Context, w http.ResponseWriter, r *http.Request) {
@@ -936,28 +935,3 @@ func completeOnboarding(c *Context, w http.ResponseWriter, r *http.Request) {
auditRec.Success() auditRec.Success()
ReturnStatusOK(w) ReturnStatusOK(w)
} }
func getAppliedSchemaMigrations(c *Context, w http.ResponseWriter, r *http.Request) {
if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem) {
c.Err = model.NewAppError("getAppliedMigrations", "app.system.applied_migrations.not_authorized", nil, "", http.StatusForbidden)
return
}
auditRec := c.MakeAuditRecord("getAppliedSchemaMigrations", audit.Fail)
defer c.LogAuditRec(auditRec)
migrations, appErr := c.App.GetAppliedSchemaMigrations()
if appErr != nil {
c.Err = appErr
return
}
js, jsonErr := json.Marshal(migrations)
if jsonErr != nil {
c.Err = model.NewAppError("getAppliedMigrations", "api.marshal_error", nil, jsonErr.Error(), http.StatusInternalServerError)
return
}
w.Write(js)
auditRec.Success()
}

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

@@ -18,7 +18,6 @@ func (api *API) InitSystemLocal() {
api.BaseRoutes.APIRoot.Handle("/server_busy", api.APILocal(getServerBusyExpires)).Methods("GET") api.BaseRoutes.APIRoot.Handle("/server_busy", api.APILocal(getServerBusyExpires)).Methods("GET")
api.BaseRoutes.APIRoot.Handle("/server_busy", api.APILocal(clearServerBusy)).Methods("DELETE") api.BaseRoutes.APIRoot.Handle("/server_busy", api.APILocal(clearServerBusy)).Methods("DELETE")
api.BaseRoutes.APIRoot.Handle("/integrity", api.APILocal(localCheckIntegrity)).Methods("POST") api.BaseRoutes.APIRoot.Handle("/integrity", api.APILocal(localCheckIntegrity)).Methods("POST")
api.BaseRoutes.System.Handle("/schema/version", api.APILocal(getAppliedSchemaMigrations)).Methods("GET")
} }
func localCheckIntegrity(c *Context, w http.ResponseWriter, r *http.Request) { func localCheckIntegrity(c *Context, w http.ResponseWriter, r *http.Request) {

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

@@ -909,20 +909,3 @@ func TestCompleteOnboarding(t *testing.T) {
}) })
} }
func TestGetAppliedSchemaMigrations(t *testing.T) {
th := Setup(t)
defer th.TearDown()
t.Run("as a regular user", func(t *testing.T) {
_, resp, err := th.Client.GetAppliedSchemaMigrations()
require.Error(t, err)
CheckForbiddenStatus(t, resp)
})
th.TestForSystemAdminAndLocal(t, func(t *testing.T, c *model.Client4) {
_, resp, err := c.GetAppliedSchemaMigrations()
require.NoError(t, err)
CheckOKStatus(t, resp)
})
}

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

@@ -550,7 +550,6 @@ type AppIface interface {
GetAllTeamsPage(offset int, limit int, opts *model.TeamSearch) ([]*model.Team, *model.AppError) GetAllTeamsPage(offset int, limit int, opts *model.TeamSearch) ([]*model.Team, *model.AppError)
GetAllTeamsPageWithCount(offset int, limit int, opts *model.TeamSearch) (*model.TeamsWithCount, *model.AppError) GetAllTeamsPageWithCount(offset int, limit int, opts *model.TeamSearch) (*model.TeamsWithCount, *model.AppError)
GetAnalytics(name string, teamID string) (model.AnalyticsRows, *model.AppError) GetAnalytics(name string, teamID string) (model.AnalyticsRows, *model.AppError)
GetAppliedSchemaMigrations() ([]model.AppliedMigration, *model.AppError)
GetAudits(userID string, limit int) (model.Audits, *model.AppError) GetAudits(userID string, limit int) (model.Audits, *model.AppError)
GetAuditsPage(userID string, page int, perPage int) (model.Audits, *model.AppError) GetAuditsPage(userID string, page int, perPage int) (model.Audits, *model.AppError)
GetAuthorizationCode(w http.ResponseWriter, r *http.Request, service string, props map[string]string, loginHint string) (string, *model.AppError) GetAuthorizationCode(w http.ResponseWriter, r *http.Request, service string, props map[string]string, loginHint string) (string, *model.AppError)

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

@@ -56,7 +56,6 @@ func TestUnitUpdateConfig(t *testing.T) {
mockStore.On("Post").Return(&mockPostStore) mockStore.On("Post").Return(&mockPostStore)
mockStore.On("System").Return(&mockSystemStore) mockStore.On("System").Return(&mockSystemStore)
mockStore.On("License").Return(&mockLicenseStore) mockStore.On("License").Return(&mockLicenseStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
prev := *th.App.Config().ServiceSettings.SiteURL prev := *th.App.Config().ServiceSettings.SiteURL

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

@@ -2103,7 +2103,6 @@ func TestMarkChannelAsUnreadFromPostPanic(t *testing.T) {
mockStore.On("User").Return(&mockUserStore) mockStore.On("User").Return(&mockUserStore)
mockStore.On("System").Return(&mockSystemStore) mockStore.On("System").Return(&mockSystemStore)
mockStore.On("License").Return(&mockLicenseStore) mockStore.On("License").Return(&mockLicenseStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
th.App.UpdateConfig(func(cfg *model.Config) { th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ThreadAutoFollow = true *cfg.ServiceSettings.ThreadAutoFollow = true
@@ -2133,7 +2132,6 @@ func TestClearChannelMembersCache(t *testing.T) {
ChannelId: "1", ChannelId: "1",
}}, nil) }}, nil)
mockStore.On("Channel").Return(&mockChannelStore) mockStore.On("Channel").Return(&mockChannelStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
th.App.ClearChannelMembersCache("channelID") th.App.ClearChannelMembersCache("channelID")
} }
@@ -2154,7 +2152,6 @@ func TestGetMemberCountsByGroup(t *testing.T) {
} }
mockChannelStore.On("GetMemberCountsByGroup", context.Background(), "channelID", true).Return(cmc, nil) mockChannelStore.On("GetMemberCountsByGroup", context.Background(), "channelID", true).Return(cmc, nil)
mockStore.On("Channel").Return(&mockChannelStore) mockStore.On("Channel").Return(&mockChannelStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
resp, err := th.App.GetMemberCountsByGroup(context.Background(), "channelID", true) resp, err := th.App.GetMemberCountsByGroup(context.Background(), "channelID", true)
require.Nil(t, err) require.Nil(t, err)
require.ElementsMatch(t, cmc, resp) require.ElementsMatch(t, cmc, resp)

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

@@ -420,11 +420,6 @@ func (a *App) ClientConfigWithComputed() map[string]string {
if installationDate, err := a.ch.srv.getSystemInstallDate(); err == nil { if installationDate, err := a.ch.srv.getSystemInstallDate(); err == nil {
respCfg["InstallationDate"] = strconv.FormatInt(installationDate, 10) respCfg["InstallationDate"] = strconv.FormatInt(installationDate, 10)
} }
if ver, err := a.ch.srv.Store.GetDBSchemaVersion(); err != nil {
mlog.Error("Could not get the schema version", mlog.Err(err))
} else {
respCfg["Version"] = strconv.Itoa(ver)
}
return respCfg return respCfg
} }

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

@@ -79,7 +79,6 @@ func TestClientConfigWithComputed(t *testing.T) {
mockStore.On("User").Return(&mockUserStore) mockStore.On("User").Return(&mockUserStore)
mockStore.On("Post").Return(&mockPostStore) mockStore.On("Post").Return(&mockPostStore)
mockStore.On("System").Return(&mockSystemStore) mockStore.On("System").Return(&mockSystemStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
config := th.App.ClientConfigWithComputed() config := th.App.ClientConfigWithComputed()
_, ok := config["NoAccounts"] _, ok := config["NoAccounts"]

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

@@ -77,7 +77,6 @@ func TestSAMLSettings(t *testing.T) {
mockSystemStore.On("GetByName", "UpgradedFromTE").Return(&model.System{Name: "UpgradedFromTE", Value: "false"}, nil) mockSystemStore.On("GetByName", "UpgradedFromTE").Return(&model.System{Name: "UpgradedFromTE", Value: "false"}, nil)
mockSystemStore.On("GetByName", "InstallationDate").Return(&model.System{Name: "InstallationDate", Value: "10"}, nil) mockSystemStore.On("GetByName", "InstallationDate").Return(&model.System{Name: "InstallationDate", Value: "10"}, nil)
mockSystemStore.On("GetByName", "FirstServerRunTimestamp").Return(&model.System{Name: "FirstServerRunTimestamp", Value: "10"}, nil) mockSystemStore.On("GetByName", "FirstServerRunTimestamp").Return(&model.System{Name: "FirstServerRunTimestamp", Value: "10"}, nil)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
mockStore.On("User").Return(&mockUserStore) mockStore.On("User").Return(&mockUserStore)
mockStore.On("Post").Return(&mockPostStore) mockStore.On("Post").Return(&mockPostStore)

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

@@ -134,6 +134,8 @@ func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer boo
if enterprise { if enterprise {
th.App.Srv().SetLicense(model.NewTestLicense()) th.App.Srv().SetLicense(model.NewTestLicense())
th.App.Srv().Jobs.InitWorkers()
th.App.Srv().Jobs.InitSchedulers()
} else { } else {
th.App.Srv().SetLicense(nil) th.App.Srv().SetLicense(nil)
} }

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

@@ -177,34 +177,6 @@ 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 err := s.Jobs.StopWorkers(); err != nil && !errors.Is(err, jobs.ErrWorkersNotRunning) {
mlog.Warn("Stopping job server workers failed", mlog.Err(err))
}
}
if *s.Config().JobSettings.RunScheduler && s.Jobs != nil {
if err := s.Jobs.StopSchedulers(); err != nil && !errors.Is(err, jobs.ErrSchedulersNotRunning) {
mlog.Error("Stopping job server schedulers failed", mlog.Err(err))
}
}
defer func() {
// restart job server workers - this handles the edge case where a license file is uploaded, but the job server
// doesn't start until the server is restarted, which prevents the 'run job now' buttons in system console from
// functioning as expected
if *s.Config().JobSettings.RunJobs && s.Jobs != nil {
if err := s.Jobs.StartWorkers(); err != nil {
mlog.Error("Starting job server workers failed", mlog.Err(err))
}
}
if *s.Config().JobSettings.RunScheduler && s.Jobs != nil {
if err := s.Jobs.StartSchedulers(); err != nil && !errors.Is(err, jobs.ErrSchedulersRunning) {
mlog.Error("Starting job server schedulers failed", mlog.Err(err))
}
}
}()
if ok := s.SetLicense(&license); !ok { if ok := s.SetLicense(&license); !ok {
return nil, model.NewAppError("addLicense", model.ExpiredLicenseError, nil, "", http.StatusBadRequest) return nil, model.NewAppError("addLicense", model.ExpiredLicenseError, nil, "", http.StatusBadRequest)
} }
@@ -236,6 +208,25 @@ func (s *Server) SaveLicense(licenseBytes []byte) (*model.License, *model.AppErr
s.ReloadConfig() s.ReloadConfig()
s.InvalidateAllCaches() s.InvalidateAllCaches()
// restart job server workers - this handles the edge case where a license file is uploaded, but the job server
// doesn't start until the server is restarted, which prevents the 'run job now' buttons in system console from
// functioning as expected
if *s.Config().JobSettings.RunJobs && s.Jobs != nil {
if err := s.Jobs.StopWorkers(); err != nil && !errors.Is(err, jobs.ErrWorkersNotRunning) {
mlog.Warn("Stopping job server workers failed", mlog.Err(err))
}
if err := s.Jobs.InitWorkers(); err != nil {
mlog.Error("Initializing job server workers failed", mlog.Err(err))
} else if err := s.Jobs.StartWorkers(); err != nil {
mlog.Error("Starting job server workers failed", mlog.Err(err))
}
}
if *s.Config().JobSettings.RunScheduler && s.Jobs != nil {
if err := s.Jobs.StartSchedulers(); err != nil && !errors.Is(err, jobs.ErrSchedulersRunning) {
mlog.Error("Starting job server schedulers failed", mlog.Err(err))
}
}
return &license, nil return &license, nil
} }

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

@@ -566,7 +566,6 @@ func TestGetPushNotificationMessage(t *testing.T) {
mockStore.On("User").Return(&mockUserStore) mockStore.On("User").Return(&mockUserStore)
mockStore.On("Post").Return(&mockPostStore) mockStore.On("Post").Return(&mockPostStore)
mockStore.On("System").Return(&mockSystemStore) mockStore.On("System").Return(&mockSystemStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
for name, tc := range map[string]struct { for name, tc := range map[string]struct {
Message string Message string
@@ -1149,7 +1148,6 @@ func TestClearPushNotificationSync(t *testing.T) {
mockStore.On("Post").Return(&mockPostStore) mockStore.On("Post").Return(&mockPostStore)
mockStore.On("System").Return(&mockSystemStore) mockStore.On("System").Return(&mockSystemStore)
mockStore.On("Session").Return(&mockSessionStore) mockStore.On("Session").Return(&mockSessionStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
th.App.UpdateConfig(func(cfg *model.Config) { th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.EmailSettings.PushNotificationServer = pushServer.URL *cfg.EmailSettings.PushNotificationServer = pushServer.URL
@@ -1223,7 +1221,6 @@ func TestUpdateMobileAppBadgeSync(t *testing.T) {
mockStore.On("Post").Return(&mockPostStore) mockStore.On("Post").Return(&mockPostStore)
mockStore.On("System").Return(&mockSystemStore) mockStore.On("System").Return(&mockSystemStore)
mockStore.On("Session").Return(&mockSessionStore) mockStore.On("Session").Return(&mockSessionStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
th.App.UpdateConfig(func(cfg *model.Config) { th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.EmailSettings.PushNotificationServer = pushServer.URL *cfg.EmailSettings.PushNotificationServer = pushServer.URL
@@ -1290,7 +1287,6 @@ func TestSendAckToPushProxy(t *testing.T) {
mockStore.On("User").Return(&mockUserStore) mockStore.On("User").Return(&mockUserStore)
mockStore.On("Post").Return(&mockPostStore) mockStore.On("Post").Return(&mockPostStore)
mockStore.On("System").Return(&mockSystemStore) mockStore.On("System").Return(&mockSystemStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
th.App.UpdateConfig(func(cfg *model.Config) { th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.EmailSettings.PushNotificationServer = pushServer.URL *cfg.EmailSettings.PushNotificationServer = pushServer.URL
@@ -1532,7 +1528,6 @@ func BenchmarkPushNotificationThroughput(b *testing.B) {
mockStore.On("System").Return(&mockSystemStore) mockStore.On("System").Return(&mockSystemStore)
mockStore.On("Session").Return(&mockSessionStore) mockStore.On("Session").Return(&mockSessionStore)
mockStore.On("Preference").Return(&mockPreferenceStore) mockStore.On("Preference").Return(&mockPreferenceStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
// create 50 users, each having 2 sessions. // create 50 users, each having 2 sessions.
type userSession struct { type userSession struct {

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

@@ -4604,28 +4604,6 @@ func (a *OpenTracingAppLayer) GetAnalytics(name string, teamID string) (model.An
return resultVar0, resultVar1 return resultVar0, resultVar1
} }
func (a *OpenTracingAppLayer) GetAppliedSchemaMigrations() ([]model.AppliedMigration, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetAppliedSchemaMigrations")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.GetAppliedSchemaMigrations()
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) GetAudits(userID string, limit int) (model.Audits, *model.AppError) { func (a *OpenTracingAppLayer) GetAudits(userID string, limit int) (model.Audits, *model.AppError) {
origCtx := a.ctx origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetAudits") span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetAudits")

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

@@ -34,7 +34,6 @@ func TestPluginPublicKeys(t *testing.T) {
mockStore.On("User").Return(&mockUserStore) mockStore.On("User").Return(&mockUserStore)
mockStore.On("Post").Return(&mockPostStore) mockStore.On("Post").Return(&mockPostStore)
mockStore.On("System").Return(&mockSystemStore) mockStore.On("System").Return(&mockSystemStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
path, _ := fileutils.FindDir("tests") path, _ := fileutils.FindDir("tests")
publicKeyFilename := "test-public-key.plugin.gpg" publicKeyFilename := "test-public-key.plugin.gpg"

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

@@ -464,7 +464,6 @@ func TestImageProxy(t *testing.T) {
mockStore.On("User").Return(&mockUserStore) mockStore.On("User").Return(&mockUserStore)
mockStore.On("Post").Return(&mockPostStore) mockStore.On("Post").Return(&mockPostStore)
mockStore.On("System").Return(&mockSystemStore) mockStore.On("System").Return(&mockSystemStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
th.App.UpdateConfig(func(cfg *model.Config) { th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.SiteURL = "http://mymattermost.com" *cfg.ServiceSettings.SiteURL = "http://mymattermost.com"

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

@@ -32,7 +32,6 @@ func TestNoticeValidation(t *testing.T) {
mockStore.On("User").Return(&mockUserStore) mockStore.On("User").Return(&mockUserStore)
mockStore.On("Post").Return(&mockPostStore) mockStore.On("Post").Return(&mockPostStore)
mockStore.On("Preference").Return(&mockPreferenceStore) mockStore.On("Preference").Return(&mockPreferenceStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
mockSystemStore.On("SaveOrUpdate", &model.System{Name: "ActiveLicenseId", Value: ""}).Return(nil) mockSystemStore.On("SaveOrUpdate", &model.System{Name: "ActiveLicenseId", Value: ""}).Return(nil)
mockSystemStore.On("GetByName", "UpgradedFromTE").Return(&model.System{Name: "UpgradedFromTE", Value: "false"}, nil) mockSystemStore.On("GetByName", "UpgradedFromTE").Return(&model.System{Name: "UpgradedFromTE", Value: "false"}, nil)
mockSystemStore.On("GetByName", "InstallationDate").Return(&model.System{Name: "InstallationDate", Value: "10"}, nil) mockSystemStore.On("GetByName", "InstallationDate").Return(&model.System{Name: "InstallationDate", Value: "10"}, nil)

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

@@ -20,7 +20,6 @@ import (
"os/exec" "os/exec"
"path" "path"
"runtime" "runtime"
"strconv"
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
@@ -753,8 +752,8 @@ func (s *Server) Channels() *Channels {
// Return Database type (postgres or mysql) and current version of Mattermost // Return Database type (postgres or mysql) and current version of Mattermost
func (s *Server) DatabaseTypeAndMattermostVersion() (string, string) { func (s *Server) DatabaseTypeAndMattermostVersion() (string, string) {
schemaVersion, _ := s.Store.GetDBSchemaVersion() mattermostVersion, _ := s.Store.System().GetByName("Version")
return *s.Config().SqlSettings.DriverName, strconv.Itoa(schemaVersion) return *s.Config().SqlSettings.DriverName, mattermostVersion.Value
} }
// 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.
@@ -1880,6 +1879,8 @@ func (ch *Channels) ClientConfigHash() string {
func (s *Server) initJobs() { func (s *Server) initJobs() {
s.Jobs = jobs.NewJobServer(s, s.Store, s.Metrics) s.Jobs = jobs.NewJobServer(s, s.Store, s.Metrics)
s.Jobs.InitWorkers()
s.Jobs.InitSchedulers()
if jobsDataRetentionJobInterface != nil { if jobsDataRetentionJobInterface != nil {
builder := jobsDataRetentionJobInterface(s) builder := jobsDataRetentionJobInterface(s)
@@ -2303,11 +2304,3 @@ func runDNDStatusExpireJob(a *App) {
} }
}) })
} }
func (a *App) GetAppliedSchemaMigrations() ([]model.AppliedMigration, *model.AppError) {
table, err := a.Srv().Store.GetAppliedMigrations()
if err != nil {
return nil, model.NewAppError("GetDBSchemaTable", "api.file.read_file.app_error", nil, err.Error(), http.StatusInternalServerError)
}
return table, nil
}

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

@@ -235,7 +235,7 @@ func TestDatabaseTypeAndMattermostVersion(t *testing.T) {
databaseType, mattermostVersion := th.Server.DatabaseTypeAndMattermostVersion() databaseType, mattermostVersion := th.Server.DatabaseTypeAndMattermostVersion()
assert.Equal(t, "postgres", databaseType) assert.Equal(t, "postgres", databaseType)
assert.GreaterOrEqual(t, mattermostVersion, strconv.Itoa(1)) assert.Equal(t, "5.31.0", mattermostVersion)
os.Setenv("MM_SQLSETTINGS_DRIVERNAME", "mysql") os.Setenv("MM_SQLSETTINGS_DRIVERNAME", "mysql")
@@ -244,7 +244,7 @@ func TestDatabaseTypeAndMattermostVersion(t *testing.T) {
databaseType, mattermostVersion = th2.Server.DatabaseTypeAndMattermostVersion() databaseType, mattermostVersion = th2.Server.DatabaseTypeAndMattermostVersion()
assert.Equal(t, "mysql", databaseType) assert.Equal(t, "mysql", databaseType)
assert.GreaterOrEqual(t, mattermostVersion, strconv.Itoa(1)) assert.Equal(t, "5.31.0", mattermostVersion)
} }
func TestGenerateSupportPacket(t *testing.T) { func TestGenerateSupportPacket(t *testing.T) {

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

@@ -125,13 +125,9 @@ func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer boo
}) })
if enterprise { if enterprise {
th.App.Srv().Jobs.StopWorkers()
th.App.Srv().Jobs.StopSchedulers()
th.App.Srv().SetLicense(model.NewTestLicense()) th.App.Srv().SetLicense(model.NewTestLicense())
th.App.Srv().Jobs.InitWorkers()
th.App.Srv().Jobs.StartWorkers() th.App.Srv().Jobs.InitSchedulers()
th.App.Srv().Jobs.StartSchedulers()
} else { } else {
th.App.Srv().SetLicense(nil) th.App.Srv().SetLicense(nil)
} }

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

@@ -882,7 +882,6 @@ func TestLeaveTeamPanic(t *testing.T) {
mockStore.On("System").Return(&mockSystemStore) mockStore.On("System").Return(&mockSystemStore)
mockStore.On("License").Return(&mockLicenseStore) mockStore.On("License").Return(&mockLicenseStore)
mockStore.On("Team").Return(&mockTeamStore) mockStore.On("Team").Return(&mockTeamStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
team := &model.Team{Id: "myteam"} team := &model.Team{Id: "myteam"}
user := &model.User{Id: "userID"} user := &model.User{Id: "userID"}
@@ -1240,7 +1239,6 @@ func TestClearTeamMembersCache(t *testing.T) {
TeamId: "1", TeamId: "1",
}}, nil) }}, nil)
mockStore.On("Team").Return(&mockTeamStore) mockStore.On("Team").Return(&mockTeamStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
th.App.ClearTeamMembersCache("teamID") th.App.ClearTeamMembersCache("teamID")
} }

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

@@ -161,7 +161,6 @@ func TestHubSessionRevokeRace(t *testing.T) {
mockStore.On("User").Return(&mockUserStore) mockStore.On("User").Return(&mockUserStore)
mockStore.On("Post").Return(&mockPostStore) mockStore.On("Post").Return(&mockPostStore)
mockStore.On("System").Return(&mockSystemStore) mockStore.On("System").Return(&mockSystemStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
userService, err := users.New(users.ServiceConfig{ userService, err := users.New(users.ServiceConfig{
UserStore: &mockUserStore, UserStore: &mockUserStore,

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

@@ -5,7 +5,6 @@ package commands
import ( import (
"fmt" "fmt"
"strconv"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -52,21 +51,13 @@ var MigrateCmd = &cobra.Command{
RunE: migrateCmdF, RunE: migrateCmdF,
} }
var DBVersionCmd = &cobra.Command{
Use: "version",
Short: "Returns the recent applied version number",
RunE: dbVersionCmdF,
}
func init() { func init() {
ResetCmd.Flags().Bool("confirm", false, "Confirm you really want to delete everything and a DB backup has been performed.") ResetCmd.Flags().Bool("confirm", false, "Confirm you really want to delete everything and a DB backup has been performed.")
DBVersionCmd.Flags().Bool("all", false, "Returns all applied migrations")
DbCmd.AddCommand( DbCmd.AddCommand(
InitDbCmd, InitDbCmd,
ResetCmd, ResetCmd,
MigrateCmd, MigrateCmd,
DBVersionCmd,
) )
RootCmd.AddCommand( RootCmd.AddCommand(
@@ -146,35 +137,3 @@ func migrateCmdF(command *cobra.Command, args []string) error {
return nil return nil
} }
func dbVersionCmdF(command *cobra.Command, args []string) error {
cfgDSN := getConfigDSN(command, config.GetEnvironment())
cfgStore, err := config.NewStoreFromDSN(cfgDSN, true, nil, true)
if err != nil {
return errors.Wrap(err, "failed to load configuration")
}
config := cfgStore.Get()
store := sqlstore.New(config.SqlSettings, nil)
defer store.Close()
allFlag, _ := command.Flags().GetBool("all")
if allFlag {
applied, err2 := store.GetAppliedMigrations()
if err2 != nil {
return errors.Wrap(err2, "failed to get applied migrations")
}
for _, migration := range applied {
CommandPrettyPrintln(fmt.Sprintf("Varsion: %d, Name: %s", migration.Version, migration.Name))
}
return nil
}
v, err := store.GetDBSchemaVersion()
if err != nil {
return errors.Wrap(err, "failed to get schema version")
}
CommandPrettyPrintln("Current database schema version is: " + strconv.Itoa(v))
return nil
}

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

@@ -215,6 +215,7 @@ func GenerateClientConfig(c *model.Config, telemetryID string, license *model.Li
func GenerateLimitedClientConfig(c *model.Config, telemetryID string, license *model.License) map[string]string { func GenerateLimitedClientConfig(c *model.Config, telemetryID string, license *model.License) map[string]string {
props := make(map[string]string) props := make(map[string]string)
props["Version"] = model.CurrentVersion
props["BuildNumber"] = model.BuildNumber props["BuildNumber"] = model.BuildNumber
props["BuildDate"] = model.BuildDate props["BuildDate"] = model.BuildDate
props["BuildHash"] = model.BuildHash props["BuildHash"] = model.BuildHash

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

@@ -6155,10 +6155,6 @@
"id": "app.submit_interactive_dialog.json_error", "id": "app.submit_interactive_dialog.json_error",
"translation": "Encountered an error encoding JSON for the interactive dialog." "translation": "Encountered an error encoding JSON for the interactive dialog."
}, },
{
"id": "app.system.applied_migrations.not_authorized",
"translation": "You don't have the appropriate permissions."
},
{ {
"id": "app.system.complete_onboarding_request.app_error", "id": "app.system.complete_onboarding_request.app_error",
"translation": "Failed to decode the complete onboarding request." "translation": "Failed to decode the complete onboarding request."

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

@@ -28,11 +28,7 @@ func makeJobServer(t *testing.T) (*JobServer, *storetest.Store, *mocks.MetricsIn
mockMetrics.AssertExpectations(t) mockMetrics.AssertExpectations(t)
}) })
jobServer := &JobServer{ jobServer := NewJobServer(configService, mockStore, mockMetrics)
ConfigService: configService,
Store: mockStore,
metrics: mockMetrics,
}
return jobServer, mockStore, mockMetrics return jobServer, mockStore, mockMetrics
} }

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

@@ -26,6 +26,8 @@ type Watcher struct {
func (srv *JobServer) MakeWatcher(workers *Workers, pollingInterval int) *Watcher { func (srv *JobServer) MakeWatcher(workers *Workers, pollingInterval int) *Watcher {
return &Watcher{ return &Watcher{
stop: make(chan struct{}),
stopped: make(chan struct{}),
pollingInterval: pollingInterval, pollingInterval: pollingInterval,
workers: workers, workers: workers,
srv: srv, srv: srv,
@@ -34,8 +36,7 @@ func (srv *JobServer) MakeWatcher(workers *Workers, pollingInterval int) *Watche
func (watcher *Watcher) Start() { func (watcher *Watcher) Start() {
mlog.Debug("Watcher Started") mlog.Debug("Watcher Started")
watcher.stop = make(chan struct{})
watcher.stopped = make(chan struct{})
// Delay for some random number of milliseconds before starting to ensure that multiple // Delay for some random number of milliseconds before starting to ensure that multiple
// instances of the jobserver don't poll at a time too close to each other. // instances of the jobserver don't poll at a time too close to each other.
rand.Seed(time.Now().UTC().UnixNano()) rand.Seed(time.Now().UTC().UnixNano())

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

@@ -32,6 +32,30 @@ var (
ErrSchedulersUninitialized = errors.New("job schedulers are not initialized") ErrSchedulersUninitialized = errors.New("job schedulers are not initialized")
) )
func (srv *JobServer) InitSchedulers() error {
srv.mut.Lock()
defer srv.mut.Unlock()
if srv.schedulers != nil && srv.schedulers.running {
return ErrSchedulersRunning
}
mlog.Debug("Initialising schedulers.")
schedulers := &Schedulers{
stop: make(chan bool),
stopped: make(chan bool),
configChanged: make(chan *model.Config),
clusterLeaderChanged: make(chan bool, 1),
jobs: srv,
isLeader: true,
schedulers: make(map[string]model.Scheduler),
nextRunTimes: make(map[string]*time.Time),
}
srv.schedulers = schedulers
return nil
}
func (schedulers *Schedulers) AddScheduler(name string, scheduler model.Scheduler) { func (schedulers *Schedulers) AddScheduler(name string, scheduler model.Scheduler) {
schedulers.schedulers[name] = scheduler schedulers.schedulers[name] = scheduler
} }
@@ -39,8 +63,6 @@ func (schedulers *Schedulers) AddScheduler(name string, scheduler model.Schedule
// Start starts the schedulers. This call is not safe for concurrent use. // Start starts the schedulers. This call is not safe for concurrent use.
// Synchronization should be implemented by the caller. // Synchronization should be implemented by the caller.
func (schedulers *Schedulers) Start() { func (schedulers *Schedulers) Start() {
schedulers.stop = make(chan bool)
schedulers.stopped = make(chan bool)
schedulers.listenerId = schedulers.jobs.ConfigService.AddConfigListener(schedulers.handleConfigChange) schedulers.listenerId = schedulers.jobs.ConfigService.AddConfigListener(schedulers.handleConfigChange)
go func() { go func() {

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

@@ -61,7 +61,7 @@ func TestScheduler(t *testing.T) {
}, },
} }
jobServer.initSchedulers() jobServer.InitSchedulers()
jobServer.RegisterJobType(model.JobTypeDataRetention, nil, new(MockScheduler)) jobServer.RegisterJobType(model.JobTypeDataRetention, nil, new(MockScheduler))
jobServer.RegisterJobType(model.JobTypeMessageExport, nil, new(MockScheduler)) jobServer.RegisterJobType(model.JobTypeMessageExport, nil, new(MockScheduler))
@@ -77,7 +77,7 @@ func TestScheduler(t *testing.T) {
}) })
t.Run("ClusterLeaderChanged", func(t *testing.T) { t.Run("ClusterLeaderChanged", func(t *testing.T) {
jobServer.initSchedulers() jobServer.InitSchedulers()
jobServer.StartSchedulers() jobServer.StartSchedulers()
time.Sleep(time.Second) time.Sleep(time.Second)
jobServer.HandleClusterLeaderChange(false) jobServer.HandleClusterLeaderChange(false)
@@ -89,7 +89,7 @@ func TestScheduler(t *testing.T) {
}) })
t.Run("ClusterLeaderChangedBeforeStart", func(t *testing.T) { t.Run("ClusterLeaderChangedBeforeStart", func(t *testing.T) {
jobServer.initSchedulers() jobServer.InitSchedulers()
jobServer.HandleClusterLeaderChange(false) jobServer.HandleClusterLeaderChange(false)
jobServer.StartSchedulers() jobServer.StartSchedulers()
time.Sleep(time.Second) time.Sleep(time.Second)
@@ -100,7 +100,7 @@ func TestScheduler(t *testing.T) {
}) })
t.Run("DoubleClusterLeaderChangedBeforeStart", func(t *testing.T) { t.Run("DoubleClusterLeaderChangedBeforeStart", func(t *testing.T) {
jobServer.initSchedulers() jobServer.InitSchedulers()
jobServer.HandleClusterLeaderChange(false) jobServer.HandleClusterLeaderChange(false)
jobServer.HandleClusterLeaderChange(true) jobServer.HandleClusterLeaderChange(true)
jobServer.StartSchedulers() jobServer.StartSchedulers()
@@ -112,7 +112,7 @@ func TestScheduler(t *testing.T) {
}) })
t.Run("ConfigChanged", func(t *testing.T) { t.Run("ConfigChanged", func(t *testing.T) {
jobServer.initSchedulers() jobServer.InitSchedulers()
jobServer.StartSchedulers() jobServer.StartSchedulers()
time.Sleep(time.Second) time.Sleep(time.Second)
jobServer.HandleClusterLeaderChange(false) jobServer.HandleClusterLeaderChange(false)
@@ -125,7 +125,7 @@ func TestScheduler(t *testing.T) {
}) })
t.Run("ConfigChangedDeadlock", func(t *testing.T) { t.Run("ConfigChangedDeadlock", func(t *testing.T) {
jobServer.initSchedulers() jobServer.InitSchedulers()
jobServer.StartSchedulers() jobServer.StartSchedulers()
time.Sleep(time.Second) time.Sleep(time.Second)

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

@@ -5,7 +5,6 @@ package jobs
import ( import (
"sync" "sync"
"time"
"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"
@@ -25,33 +24,11 @@ type JobServer struct {
} }
func NewJobServer(configService configservice.ConfigService, store store.Store, metrics einterfaces.MetricsInterface) *JobServer { func NewJobServer(configService configservice.ConfigService, store store.Store, metrics einterfaces.MetricsInterface) *JobServer {
srv := &JobServer{ return &JobServer{
ConfigService: configService, ConfigService: configService,
Store: store, Store: store,
metrics: metrics, metrics: metrics,
} }
srv.initWorkers()
srv.initSchedulers()
return srv
}
func (srv *JobServer) initWorkers() {
workers := NewWorkers(srv.ConfigService)
workers.Watcher = srv.MakeWatcher(workers, DefaultWatcherPollingInterval)
srv.workers = workers
}
func (srv *JobServer) initSchedulers() {
schedulers := &Schedulers{
configChanged: make(chan *model.Config),
clusterLeaderChanged: make(chan bool, 1),
jobs: srv,
isLeader: true,
schedulers: make(map[string]model.Scheduler),
nextRunTimes: make(map[string]*time.Time),
}
srv.schedulers = schedulers
} }
func (srv *JobServer) Config() *model.Config { func (srv *JobServer) Config() *model.Config {

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

@@ -5,11 +5,44 @@ package jobs
import ( import (
"testing" "testing"
"time"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestInitWorkers(t *testing.T) {
t.Run("initialize", func(t *testing.T) {
jobServer, _, _ := makeJobServer(t)
err := jobServer.InitWorkers()
require.NoError(t, err)
})
t.Run("re-initialize", func(t *testing.T) {
jobServer, _, _ := makeJobServer(t)
err := jobServer.InitWorkers()
require.NoError(t, err)
err = jobServer.InitWorkers()
require.NoError(t, err)
})
t.Run("re-initialize already running", func(t *testing.T) {
jobServer, _, _ := makeJobServer(t)
err := jobServer.InitWorkers()
require.NoError(t, err)
err = jobServer.StartWorkers()
require.NoError(t, err)
err = jobServer.InitWorkers()
require.Equal(t, ErrWorkersRunning, err)
err = jobServer.StopWorkers()
require.NoError(t, err)
err = jobServer.InitWorkers()
require.NoError(t, err)
})
}
func TestStartWorkers(t *testing.T) { func TestStartWorkers(t *testing.T) {
t.Run("uninitialized", func(t *testing.T) { t.Run("uninitialized", func(t *testing.T) {
jobServer, _, _ := makeJobServer(t) jobServer, _, _ := makeJobServer(t)
@@ -19,24 +52,22 @@ func TestStartWorkers(t *testing.T) {
t.Run("already running", func(t *testing.T) { t.Run("already running", func(t *testing.T) {
jobServer, _, _ := makeJobServer(t) jobServer, _, _ := makeJobServer(t)
jobServer.initWorkers() err := jobServer.InitWorkers()
err := jobServer.StartWorkers() require.NoError(t, err)
err = jobServer.StartWorkers()
require.NoError(t, err) require.NoError(t, err)
err = jobServer.StartWorkers() err = jobServer.StartWorkers()
require.Equal(t, ErrWorkersRunning, err) require.Equal(t, ErrWorkersRunning, err)
// Parking the go routing to let the worker watcher start
time.Sleep(1 * time.Millisecond)
err = jobServer.StopWorkers() err = jobServer.StopWorkers()
require.NoError(t, err) require.NoError(t, err)
}) })
t.Run("not running", func(t *testing.T) { t.Run("not running", func(t *testing.T) {
jobServer, _, _ := makeJobServer(t) jobServer, _, _ := makeJobServer(t)
jobServer.initWorkers() err := jobServer.InitWorkers()
err := jobServer.StartWorkers() require.NoError(t, err)
err = jobServer.StartWorkers()
require.NoError(t, err) require.NoError(t, err)
// Parking the go routing to let the worker watcher start
time.Sleep(1 * time.Millisecond)
err = jobServer.StopWorkers() err = jobServer.StopWorkers()
require.NoError(t, err) require.NoError(t, err)
}) })
@@ -51,23 +82,57 @@ func TestStopWorkers(t *testing.T) {
t.Run("not running", func(t *testing.T) { t.Run("not running", func(t *testing.T) {
jobServer, _, _ := makeJobServer(t) jobServer, _, _ := makeJobServer(t)
jobServer.initWorkers() err := jobServer.InitWorkers()
err := jobServer.StopWorkers() require.NoError(t, err)
err = jobServer.StopWorkers()
require.Equal(t, ErrWorkersNotRunning, err) require.Equal(t, ErrWorkersNotRunning, err)
}) })
t.Run("running", func(t *testing.T) { t.Run("running", func(t *testing.T) {
jobServer, _, _ := makeJobServer(t) jobServer, _, _ := makeJobServer(t)
jobServer.initWorkers() err := jobServer.InitWorkers()
err := jobServer.StartWorkers() require.NoError(t, err)
err = jobServer.StartWorkers()
require.NoError(t, err) require.NoError(t, err)
// Parking the go routing to let the worker watcher start
time.Sleep(1 * time.Millisecond)
err = jobServer.StopWorkers() err = jobServer.StopWorkers()
require.NoError(t, err) require.NoError(t, err)
}) })
} }
func TestInitSchedulers(t *testing.T) {
t.Run("initialize", func(t *testing.T) {
jobServer, _, _ := makeJobServer(t)
err := jobServer.InitSchedulers()
require.NoError(t, err)
})
t.Run("re-initialize", func(t *testing.T) {
jobServer, _, _ := makeJobServer(t)
err := jobServer.InitSchedulers()
require.NoError(t, err)
err = jobServer.InitSchedulers()
require.NoError(t, err)
})
t.Run("re-initialize already running", func(t *testing.T) {
jobServer, _, _ := makeJobServer(t)
err := jobServer.InitSchedulers()
require.NoError(t, err)
err = jobServer.StartSchedulers()
require.NoError(t, err)
err = jobServer.InitSchedulers()
require.Equal(t, ErrSchedulersRunning, err)
err = jobServer.StopSchedulers()
require.NoError(t, err)
err = jobServer.InitSchedulers()
require.NoError(t, err)
})
}
func TestStartSchedulers(t *testing.T) { func TestStartSchedulers(t *testing.T) {
t.Run("uninitialized", func(t *testing.T) { t.Run("uninitialized", func(t *testing.T) {
jobServer, _, _ := makeJobServer(t) jobServer, _, _ := makeJobServer(t)
@@ -77,8 +142,9 @@ func TestStartSchedulers(t *testing.T) {
t.Run("initialized", func(t *testing.T) { t.Run("initialized", func(t *testing.T) {
jobServer, _, _ := makeJobServer(t) jobServer, _, _ := makeJobServer(t)
jobServer.initSchedulers() err := jobServer.InitSchedulers()
err := jobServer.StartSchedulers() require.NoError(t, err)
err = jobServer.StartSchedulers()
require.NoError(t, err) require.NoError(t, err)
err = jobServer.StopSchedulers() err = jobServer.StopSchedulers()
@@ -87,8 +153,9 @@ func TestStartSchedulers(t *testing.T) {
t.Run("already running", func(t *testing.T) { t.Run("already running", func(t *testing.T) {
jobServer, _, _ := makeJobServer(t) jobServer, _, _ := makeJobServer(t)
jobServer.initSchedulers() err := jobServer.InitSchedulers()
err := jobServer.StartSchedulers() require.NoError(t, err)
err = jobServer.StartSchedulers()
require.NoError(t, err) require.NoError(t, err)
err = jobServer.StartSchedulers() err = jobServer.StartSchedulers()
require.Equal(t, ErrSchedulersRunning, err) require.Equal(t, ErrSchedulersRunning, err)
@@ -107,15 +174,17 @@ func TestStopSchedulers(t *testing.T) {
t.Run("not running", func(t *testing.T) { t.Run("not running", func(t *testing.T) {
jobServer, _, _ := makeJobServer(t) jobServer, _, _ := makeJobServer(t)
jobServer.initSchedulers() err := jobServer.InitSchedulers()
err := jobServer.StopSchedulers() require.NoError(t, err)
err = jobServer.StopSchedulers()
require.Equal(t, ErrSchedulersNotRunning, err) require.Equal(t, ErrSchedulersNotRunning, err)
}) })
t.Run("running", func(t *testing.T) { t.Run("running", func(t *testing.T) {
jobServer, _, _ := makeJobServer(t) jobServer, _, _ := makeJobServer(t)
jobServer.initSchedulers() err := jobServer.InitSchedulers()
err := jobServer.StartSchedulers() require.NoError(t, err)
err = jobServer.StartSchedulers()
require.NoError(t, err) require.NoError(t, err)
err = jobServer.StopSchedulers() err = jobServer.StopSchedulers()
require.NoError(t, err) require.NoError(t, err)

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

@@ -27,6 +27,23 @@ var (
ErrWorkersUninitialized = errors.New("job workers are not initialized") ErrWorkersUninitialized = errors.New("job workers are not initialized")
) )
func (srv *JobServer) InitWorkers() error {
srv.mut.Lock()
defer srv.mut.Unlock()
if srv.workers != nil && srv.workers.running {
return ErrWorkersRunning
}
workers := NewWorkers(srv.ConfigService)
workers.Watcher = srv.MakeWatcher(workers, DefaultWatcherPollingInterval)
srv.workers = workers
return nil
}
func NewWorkers(configService configservice.ConfigService) *Workers { func NewWorkers(configService configservice.ConfigService) *Workers {
return &Workers{ return &Workers{
ConfigService: configService, ConfigService: configService,

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

@@ -7931,16 +7931,3 @@ func (c *Client4) GetUsersWithInvalidEmails(page, perPage int) ([]*User, *Respon
} }
return list, BuildResponse(r), nil return list, BuildResponse(r), nil
} }
func (c *Client4) GetAppliedSchemaMigrations() ([]AppliedMigration, *Response, error) {
r, err := c.DoAPIGet(c.systemRoute()+"/schema/version", "")
if err != nil {
return nil, BuildResponse(r), err
}
defer closeBody(r)
var list []AppliedMigration
if jsonErr := json.NewDecoder(r.Body).Decode(&list); jsonErr != nil {
return nil, nil, NewAppError("GetUsers", "api.unmarshal_error", nil, jsonErr.Error(), http.StatusInternalServerError)
}
return list, BuildResponse(r), nil
}

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

@@ -173,8 +173,3 @@ type WarnMetricStatus struct {
type SendWarnMetricAck struct { type SendWarnMetricAck struct {
ForceAck bool `json:"forceAck"` ForceAck bool `json:"forceAck"`
} }
type AppliedMigration struct {
Version int `json:"version"`
Name string `json:"name"`
}

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

@@ -11781,6 +11781,10 @@ func (s *OpenTracingLayer) DropAllTables() {
s.Store.DropAllTables() s.Store.DropAllTables()
} }
func (s *OpenTracingLayer) GetCurrentSchemaVersion() string {
return s.Store.GetCurrentSchemaVersion()
}
func (s *OpenTracingLayer) LockToMaster() { func (s *OpenTracingLayer) LockToMaster() {
s.Store.LockToMaster() s.Store.LockToMaster()
} }

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

@@ -13422,6 +13422,10 @@ func (s *RetryLayer) DropAllTables() {
s.Store.DropAllTables() s.Store.DropAllTables()
} }
func (s *RetryLayer) GetCurrentSchemaVersion() string {
return s.Store.GetCurrentSchemaVersion()
}
func (s *RetryLayer) LockToMaster() { func (s *RetryLayer) LockToMaster() {
s.Store.LockToMaster() s.Store.LockToMaster()
} }

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

@@ -353,10 +353,10 @@ func (ss *SqlStore) DriverName() string {
return *ss.settings.DriverName return *ss.settings.DriverName
} }
func (ss *SqlStore) getCurrentSchemaVersion() (string, error) { func (ss *SqlStore) GetCurrentSchemaVersion() string {
var version string var version string
err := ss.GetMasterX().Get(&version, "SELECT Value FROM Systems WHERE Name='Version'") _ = ss.GetMasterX().Get(&version, "SELECT Value FROM Systems WHERE Name='Version'")
return version, err return version
} }
// GetDbVersion returns the version of the database being used. // GetDbVersion returns the version of the database being used.
@@ -948,7 +948,6 @@ func (ss *SqlStore) DropAllTables() {
FROM pg_class FROM pg_class
WHERE relkind = 'r' -- only tables WHERE relkind = 'r' -- only tables
AND relnamespace = 'public'::regnamespace AND relnamespace = 'public'::regnamespace
AND NOT relname = 'db_migrations'
); );
END END
$func$;`) $func$;`)
@@ -956,9 +955,7 @@ func (ss *SqlStore) DropAllTables() {
tables := []string{} tables := []string{}
ss.masterX.Select(&tables, `show tables`) ss.masterX.Select(&tables, `show tables`)
for _, t := range tables { for _, t := range tables {
if t != "db_migrations" { ss.masterX.Exec(`TRUNCATE TABLE ` + t)
ss.masterX.Exec(`TRUNCATE TABLE ` + t)
}
} }
} }
} }
@@ -1198,20 +1195,3 @@ func (ss *SqlStore) toReserveCase(str string) string {
return fmt.Sprintf("`%s`", strings.Title(str)) return fmt.Sprintf("`%s`", strings.Title(str))
} }
func (ss *SqlStore) GetDBSchemaVersion() (int, error) {
var version int
if err := ss.GetMasterX().Get(&version, "SELECT Version FROM db_migrations ORDER BY Version DESC LIMIT 1"); err != nil {
return 0, errors.Wrap(err, "unable to select from db_migrations")
}
return version, nil
}
func (ss *SqlStore) GetAppliedMigrations() ([]model.AppliedMigration, error) {
migrations := []model.AppliedMigration{}
if err := ss.GetMasterX().Select(&migrations, "SELECT Version, Name FROM db_migrations ORDER BY Version DESC"); err != nil {
return nil, errors.Wrap(err, "unable to select from db_migrations")
}
return migrations, nil
}

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

@@ -6,11 +6,7 @@ package sqlstore
import ( import (
"fmt" "fmt"
"os" "os"
"path/filepath"
"regexp" "regexp"
"sort"
"strconv"
"strings"
"sync" "sync"
"testing" "testing"
"time" "time"
@@ -22,7 +18,6 @@ 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/db/migrations"
"github.com/mattermost/mattermost-server/v6/einterfaces/mocks" "github.com/mattermost/mattermost-server/v6/einterfaces/mocks"
"github.com/mattermost/mattermost-server/v6/model" "github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/store" "github.com/mattermost/mattermost-server/v6/store"
@@ -789,71 +784,3 @@ func TestMySQLReadTimeout(t *testing.T) {
_, err = store.GetMasterX().ExecNoTimeout(`SELECT SLEEP(3)`) _, err = store.GetMasterX().ExecNoTimeout(`SELECT SLEEP(3)`)
require.NoError(t, err) require.NoError(t, err)
} }
func TestGetDBSchemaVersion(t *testing.T) {
testDrivers := []string{
model.DatabaseDriverPostgres,
model.DatabaseDriverMysql,
}
for _, driver := range testDrivers {
t.Run("Should return latest version number of applied migrations for "+driver, func(t *testing.T) {
t.Parallel()
settings := makeSqlSettings(driver)
store := New(*settings, nil)
var assetNamesForDriver []string
for _, assetName := range migrations.AssetNames() {
if strings.HasPrefix(assetName, store.DriverName()) {
assetNamesForDriver = append(assetNamesForDriver, filepath.Base(assetName))
}
}
sort.Strings(assetNamesForDriver)
require.NotEmpty(t, assetNamesForDriver)
lastMigration := assetNamesForDriver[len(assetNamesForDriver)-1]
expectedVersion := strings.Split(lastMigration, "_")[0]
version, err := store.GetDBSchemaVersion()
require.NoError(t, err)
require.Equal(t, expectedVersion, fmt.Sprintf("%06d", version))
})
}
}
func TestGetAppliedMigrations(t *testing.T) {
testDrivers := []string{
model.DatabaseDriverPostgres,
model.DatabaseDriverMysql,
}
for _, driver := range testDrivers {
t.Run("Should return db applied migrations for "+driver, func(t *testing.T) {
t.Parallel()
settings := makeSqlSettings(driver)
store := New(*settings, nil)
var migrationsFromFiles []model.AppliedMigration
for _, assetName := range migrations.AssetNames() {
if strings.HasPrefix(assetName, store.DriverName()) && strings.HasSuffix(assetName, ".up.sql") {
versionString := strings.Split(filepath.Base(assetName), "_")[0]
version, err := strconv.Atoi(versionString)
require.NoError(t, err)
name := strings.TrimSuffix(strings.TrimLeft(filepath.Base(assetName), versionString+"_"), ".up.sql")
migrationsFromFiles = append(migrationsFromFiles, model.AppliedMigration{
Version: version,
Name: name,
})
}
}
require.NotEmpty(t, migrationsFromFiles)
migrations, err := store.GetAppliedMigrations()
require.NoError(t, err)
require.ElementsMatch(t, migrationsFromFiles, migrations)
})
}
}

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

@@ -114,12 +114,8 @@ func upgradeDatabase(sqlStore *SqlStore, currentModelVersionString string) error
return errors.Wrapf(err, "failed to parse oldest supported version %s", OldestSupportedVersion) return errors.Wrapf(err, "failed to parse oldest supported version %s", OldestSupportedVersion)
} }
currentSchemaVersionString, err := sqlStore.getCurrentSchemaVersion()
if err != nil {
mlog.Warn("could not receive the schema version from systems table", mlog.Err(err))
}
var currentSchemaVersion *semver.Version var currentSchemaVersion *semver.Version
currentSchemaVersionString := sqlStore.GetCurrentSchemaVersion()
if currentSchemaVersionString != "" { if currentSchemaVersionString != "" {
currentSchemaVersion, err = semver.New(currentSchemaVersionString) currentSchemaVersion, err = semver.New(currentSchemaVersionString)
if err != nil { if err != nil {
@@ -236,11 +232,7 @@ func saveSchemaVersion(sqlStore *SqlStore, version string) {
} }
func shouldPerformUpgrade(sqlStore *SqlStore, currentSchemaVersion string, expectedSchemaVersion string) bool { func shouldPerformUpgrade(sqlStore *SqlStore, currentSchemaVersion string, expectedSchemaVersion string) bool {
storedSchemaVersion, err := sqlStore.getCurrentSchemaVersion() storedSchemaVersion := sqlStore.GetCurrentSchemaVersion()
if err != nil {
mlog.Error("could not receive the schema version from systems table", mlog.Err(err))
return false
}
storedVersion, err := semver.Parse(storedSchemaVersion) storedVersion, err := semver.Parse(storedSchemaVersion)
if err != nil { if err != nil {

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

@@ -17,11 +17,7 @@ func TestStoreUpgradeDotRelease(t *testing.T) {
saveSchemaVersion(sqlStore, "5.33.1") saveSchemaVersion(sqlStore, "5.33.1")
err := upgradeDatabase(sqlStore, CurrentSchemaVersion) err := upgradeDatabase(sqlStore, CurrentSchemaVersion)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, CurrentSchemaVersion, sqlStore.GetCurrentSchemaVersion())
currentVersion, err := sqlStore.getCurrentSchemaVersion()
require.NoError(t, err)
require.Equal(t, CurrentSchemaVersion, currentVersion)
}) })
} }
@@ -38,44 +34,28 @@ func TestStoreUpgrade(t *testing.T) {
saveSchemaVersion(sqlStore, "invalid") saveSchemaVersion(sqlStore, "invalid")
err := upgradeDatabase(sqlStore, "5.8.0") err := upgradeDatabase(sqlStore, "5.8.0")
require.EqualError(t, err, "failed to parse database schema version invalid: No Major.Minor.Patch elements found") require.EqualError(t, err, "failed to parse database schema version invalid: No Major.Minor.Patch elements found")
require.Equal(t, "invalid", sqlStore.GetCurrentSchemaVersion())
currentVersion, err := sqlStore.getCurrentSchemaVersion()
require.NoError(t, err)
require.Equal(t, "invalid", currentVersion)
}) })
t.Run("upgrade from unsupported version", func(t *testing.T) { t.Run("upgrade from unsupported version", func(t *testing.T) {
saveSchemaVersion(sqlStore, "2.0.0") saveSchemaVersion(sqlStore, "2.0.0")
err := upgradeDatabase(sqlStore, "5.8.0") err := upgradeDatabase(sqlStore, "5.8.0")
require.EqualError(t, err, "Database schema version 2.0.0 is no longer supported. This Mattermost server supports automatic upgrades from schema version 3.0.0 through schema version 5.8.0. Please manually upgrade to at least version 3.0.0 before continuing.") require.EqualError(t, err, "Database schema version 2.0.0 is no longer supported. This Mattermost server supports automatic upgrades from schema version 3.0.0 through schema version 5.8.0. Please manually upgrade to at least version 3.0.0 before continuing.")
require.Equal(t, "2.0.0", sqlStore.GetCurrentSchemaVersion())
currentVersion, err := sqlStore.getCurrentSchemaVersion()
require.NoError(t, err)
require.Equal(t, "2.0.0", currentVersion)
}) })
t.Run("upgrade from earliest supported version", func(t *testing.T) { t.Run("upgrade from earliest supported version", func(t *testing.T) {
saveSchemaVersion(sqlStore, Version300) saveSchemaVersion(sqlStore, Version300)
err := upgradeDatabase(sqlStore, CurrentSchemaVersion) err := upgradeDatabase(sqlStore, CurrentSchemaVersion)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, CurrentSchemaVersion, sqlStore.GetCurrentSchemaVersion())
currentVersion, err := sqlStore.getCurrentSchemaVersion()
require.NoError(t, err)
require.Equal(t, CurrentSchemaVersion, currentVersion)
}) })
t.Run("upgrade from no existing version", func(t *testing.T) { t.Run("upgrade from no existing version", func(t *testing.T) {
saveSchemaVersion(sqlStore, "") saveSchemaVersion(sqlStore, "")
err := upgradeDatabase(sqlStore, CurrentSchemaVersion) err := upgradeDatabase(sqlStore, CurrentSchemaVersion)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, CurrentSchemaVersion, sqlStore.GetCurrentSchemaVersion())
currentVersion, err := sqlStore.getCurrentSchemaVersion()
require.NoError(t, err)
require.Equal(t, CurrentSchemaVersion, currentVersion)
}) })
t.Run("upgrade schema running earlier minor version", func(t *testing.T) { t.Run("upgrade schema running earlier minor version", func(t *testing.T) {
@@ -84,44 +64,28 @@ func TestStoreUpgrade(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
// Assert CurrentSchemaVersion, not 5.8.0, since the migrations will move // Assert CurrentSchemaVersion, not 5.8.0, since the migrations will move
// past 5.8.0 regardless of the input parameter. // past 5.8.0 regardless of the input parameter.
require.Equal(t, CurrentSchemaVersion, sqlStore.GetCurrentSchemaVersion())
currentVersion, err := sqlStore.getCurrentSchemaVersion()
require.NoError(t, err)
require.Equal(t, CurrentSchemaVersion, currentVersion)
}) })
t.Run("upgrade schema running later minor version", func(t *testing.T) { t.Run("upgrade schema running later minor version", func(t *testing.T) {
saveSchemaVersion(sqlStore, "5.99.0") saveSchemaVersion(sqlStore, "5.99.0")
err := upgradeDatabase(sqlStore, "5.8.0") err := upgradeDatabase(sqlStore, "5.8.0")
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "5.99.0", sqlStore.GetCurrentSchemaVersion())
currentVersion, err := sqlStore.getCurrentSchemaVersion()
require.NoError(t, err)
require.Equal(t, "5.99.0", currentVersion)
}) })
t.Run("upgrade schema running earlier major version", func(t *testing.T) { t.Run("upgrade schema running earlier major version", func(t *testing.T) {
saveSchemaVersion(sqlStore, "4.1.0") saveSchemaVersion(sqlStore, "4.1.0")
err := upgradeDatabase(sqlStore, CurrentSchemaVersion) err := upgradeDatabase(sqlStore, CurrentSchemaVersion)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, CurrentSchemaVersion, sqlStore.GetCurrentSchemaVersion())
currentVersion, err := sqlStore.getCurrentSchemaVersion()
require.NoError(t, err)
require.Equal(t, CurrentSchemaVersion, currentVersion)
}) })
t.Run("upgrade schema running later major version", func(t *testing.T) { t.Run("upgrade schema running later major version", func(t *testing.T) {
saveSchemaVersion(sqlStore, "6.0.0") saveSchemaVersion(sqlStore, "6.0.0")
err := upgradeDatabase(sqlStore, "5.8.0") err := upgradeDatabase(sqlStore, "5.8.0")
require.EqualError(t, err, "Database schema version 6.0.0 is not supported. This Mattermost server supports only >=5.8.0, <6.0.0. Please upgrade to at least version 6.0.0 before continuing.") require.EqualError(t, err, "Database schema version 6.0.0 is not supported. This Mattermost server supports only >=5.8.0, <6.0.0. Please upgrade to at least version 6.0.0 before continuing.")
require.Equal(t, "6.0.0", sqlStore.GetCurrentSchemaVersion())
currentVersion, err := sqlStore.getCurrentSchemaVersion()
require.NoError(t, err)
require.Equal(t, "6.0.0", currentVersion)
}) })
}) })
} }
@@ -136,11 +100,7 @@ func TestSaveSchemaVersion(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, Version300, props["Version"]) require.Equal(t, Version300, props["Version"])
require.Equal(t, Version300, sqlStore.GetCurrentSchemaVersion())
currentVersion, err := sqlStore.getCurrentSchemaVersion()
require.NoError(t, err)
require.Equal(t, Version300, currentVersion)
}) })
t.Run("set current version", func(t *testing.T) { t.Run("set current version", func(t *testing.T) {
@@ -149,11 +109,7 @@ func TestSaveSchemaVersion(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, CurrentSchemaVersion, props["Version"]) require.Equal(t, CurrentSchemaVersion, props["Version"])
require.Equal(t, CurrentSchemaVersion, sqlStore.GetCurrentSchemaVersion())
currentVersion, err := sqlStore.getCurrentSchemaVersion()
require.NoError(t, err)
require.Equal(t, CurrentSchemaVersion, currentVersion)
}) })
}) })
} }

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

@@ -63,8 +63,7 @@ type Store interface {
UnlockFromMaster() UnlockFromMaster()
DropAllTables() DropAllTables()
RecycleDBConnections(d time.Duration) RecycleDBConnections(d time.Duration)
GetDBSchemaVersion() (int, error) GetCurrentSchemaVersion() string
GetAppliedMigrations() ([]model.AppliedMigration, error)
GetDbVersion(numerical bool) (string, error) GetDbVersion(numerical bool) (string, error)
TotalMasterDbConnections() int TotalMasterDbConnections() int
TotalReadDbConnections() int TotalReadDbConnections() int

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

@@ -222,48 +222,18 @@ func (_m *Store) FileInfo() store.FileInfoStore {
return r0 return r0
} }
// GetAppliedMigrations provides a mock function with given fields: // GetCurrentSchemaVersion provides a mock function with given fields:
func (_m *Store) GetAppliedMigrations() ([]model.AppliedMigration, error) { func (_m *Store) GetCurrentSchemaVersion() string {
ret := _m.Called() ret := _m.Called()
var r0 []model.AppliedMigration var r0 string
if rf, ok := ret.Get(0).(func() []model.AppliedMigration); ok { if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf() r0 = rf()
} else { } else {
if ret.Get(0) != nil { r0 = ret.Get(0).(string)
r0 = ret.Get(0).([]model.AppliedMigration)
}
} }
var r1 error return r0
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetDBSchemaVersion provides a mock function with given fields:
func (_m *Store) GetDBSchemaVersion() (int, error) {
ret := _m.Called()
var r0 int
if rf, ok := ret.Get(0).(func() int); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(int)
}
var r1 error
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
} }
// GetDbVersion provides a mock function with given fields: numerical // GetDbVersion provides a mock function with given fields: numerical

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

@@ -104,13 +104,10 @@ func (s *Store) UnlockFromMaster() { /* do nothing */ }
func (s *Store) DropAllTables() { /* do nothing */ } func (s *Store) DropAllTables() { /* do nothing */ }
func (s *Store) GetDbVersion(bool) (string, error) { return "", nil } func (s *Store) GetDbVersion(bool) (string, error) { return "", nil }
func (s *Store) RecycleDBConnections(time.Duration) {} func (s *Store) RecycleDBConnections(time.Duration) {}
func (s *Store) GetDBSchemaVersion() (int, error) { return 1, nil } func (s *Store) TotalMasterDbConnections() int { return 1 }
func (s *Store) GetAppliedMigrations() ([]model.AppliedMigration, error) { func (s *Store) TotalReadDbConnections() int { return 1 }
return []model.AppliedMigration{}, nil func (s *Store) TotalSearchDbConnections() int { return 1 }
} func (s *Store) GetCurrentSchemaVersion() string { return "" }
func (s *Store) TotalMasterDbConnections() int { return 1 }
func (s *Store) TotalReadDbConnections() int { return 1 }
func (s *Store) TotalSearchDbConnections() int { return 1 }
func (s *Store) CheckIntegrity() <-chan model.IntegrityCheckResult { func (s *Store) CheckIntegrity() <-chan model.IntegrityCheckResult {
return make(chan model.IntegrityCheckResult) return make(chan model.IntegrityCheckResult)
} }

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

@@ -10616,6 +10616,10 @@ func (s *TimerLayer) DropAllTables() {
s.Store.DropAllTables() s.Store.DropAllTables()
} }
func (s *TimerLayer) GetCurrentSchemaVersion() string {
return s.Store.GetCurrentSchemaVersion()
}
func (s *TimerLayer) LockToMaster() { func (s *TimerLayer) LockToMaster() {
s.Store.LockToMaster() s.Store.LockToMaster()
} }

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

@@ -110,7 +110,5 @@ func GetMockStoreForSetupFunctions() *mocks.Store {
mockStore.On("Session").Return(&sessionStore) mockStore.On("Session").Return(&sessionStore)
mockStore.On("OAuth").Return(&oAuthStore) mockStore.On("OAuth").Return(&oAuthStore)
mockStore.On("Group").Return(&groupStore) mockStore.On("Group").Return(&groupStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
return &mockStore return &mockStore
} }

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

@@ -68,7 +68,6 @@ func TestMfaRequired(t *testing.T) {
mockStore.On("User").Return(&mockUserStore) mockStore.On("User").Return(&mockUserStore)
mockStore.On("Post").Return(&mockPostStore) mockStore.On("Post").Return(&mockPostStore)
mockStore.On("System").Return(&mockSystemStore) mockStore.On("System").Return(&mockSystemStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
th.App.Srv().SetLicense(model.NewTestLicense("mfa")) th.App.Srv().SetLicense(model.NewTestLicense("mfa"))

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

@@ -79,7 +79,6 @@ func TestHandlerServeHTTPSecureTransport(t *testing.T) {
mockStore.On("User").Return(&mockUserStore) mockStore.On("User").Return(&mockUserStore)
mockStore.On("Post").Return(&mockPostStore) mockStore.On("Post").Return(&mockPostStore)
mockStore.On("System").Return(&mockSystemStore) mockStore.On("System").Return(&mockSystemStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
th.App.UpdateConfig(func(config *model.Config) { th.App.UpdateConfig(func(config *model.Config) {
*config.ServiceSettings.TLSStrictTransport = true *config.ServiceSettings.TLSStrictTransport = true
@@ -322,7 +321,6 @@ func TestHandlerServeCSPHeader(t *testing.T) {
mockStore.On("User").Return(&mockUserStore) mockStore.On("User").Return(&mockUserStore)
mockStore.On("Post").Return(&mockPostStore) mockStore.On("Post").Return(&mockPostStore)
mockStore.On("System").Return(&mockSystemStore) mockStore.On("System").Return(&mockSystemStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
th.App.UpdateConfig(func(cfg *model.Config) { th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.SiteURL = *cfg.ServiceSettings.SiteURL + "/subpath" *cfg.ServiceSettings.SiteURL = *cfg.ServiceSettings.SiteURL + "/subpath"
@@ -643,7 +641,6 @@ func TestCheckCSRFToken(t *testing.T) {
mockStore.On("User").Return(&mockUserStore) mockStore.On("User").Return(&mockUserStore)
mockStore.On("Post").Return(&mockPostStore) mockStore.On("Post").Return(&mockPostStore)
mockStore.On("System").Return(&mockSystemStore) mockStore.On("System").Return(&mockSystemStore)
mockStore.On("GetDBSchemaVersion").Return(1, nil)
th.App.UpdateConfig(func(cfg *model.Config) { th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ExperimentalStrictCSRFEnforcement = true *cfg.ServiceSettings.ExperimentalStrictCSRFEnforcement = true