301 строка
7.4 KiB
Go
301 строка
7.4 KiB
Go
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
|
// See License.txt for license information.
|
|
|
|
package commands
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"net/url"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/mattermost/mattermost-server/api4"
|
|
"github.com/mattermost/mattermost-server/app"
|
|
"github.com/mattermost/mattermost-server/manualtesting"
|
|
"github.com/mattermost/mattermost-server/mlog"
|
|
"github.com/mattermost/mattermost-server/model"
|
|
"github.com/mattermost/mattermost-server/services/mailservice"
|
|
"github.com/mattermost/mattermost-server/utils"
|
|
"github.com/mattermost/mattermost-server/web"
|
|
"github.com/mattermost/mattermost-server/wsapi"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
const (
|
|
SESSIONS_CLEANUP_BATCH_SIZE = 1000
|
|
)
|
|
|
|
var MaxNotificationsPerChannelDefault int64 = 1000000
|
|
|
|
var serverCmd = &cobra.Command{
|
|
Use: "server",
|
|
Short: "Run the Mattermost server",
|
|
RunE: serverCmdF,
|
|
SilenceUsage: true,
|
|
}
|
|
|
|
func init() {
|
|
RootCmd.AddCommand(serverCmd)
|
|
RootCmd.RunE = serverCmdF
|
|
}
|
|
|
|
func serverCmdF(command *cobra.Command, args []string) error {
|
|
config, err := command.Flags().GetString("config")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
disableConfigWatch, _ := command.Flags().GetBool("disableconfigwatch")
|
|
usedPlatform, _ := command.Flags().GetBool("platform")
|
|
|
|
interruptChan := make(chan os.Signal, 1)
|
|
return runServer(config, disableConfigWatch, usedPlatform, interruptChan)
|
|
}
|
|
|
|
func runServer(configFileLocation string, disableConfigWatch bool, usedPlatform bool, interruptChan chan os.Signal) error {
|
|
options := []app.Option{app.ConfigFile(configFileLocation)}
|
|
if disableConfigWatch {
|
|
options = append(options, app.DisableConfigWatch)
|
|
}
|
|
|
|
a, err := app.New(options...)
|
|
if err != nil {
|
|
mlog.Critical(err.Error())
|
|
return err
|
|
}
|
|
defer a.Shutdown()
|
|
|
|
mailservice.TestConnection(a.Config())
|
|
|
|
pwd, _ := os.Getwd()
|
|
if usedPlatform {
|
|
mlog.Error("The platform binary has been deprecated, please switch to using the mattermost binary.")
|
|
}
|
|
|
|
if _, err := url.ParseRequestURI(*a.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: http://about.mattermost.com/default-site-url")
|
|
}
|
|
|
|
mlog.Info(fmt.Sprintf("Current version is %v (%v/%v/%v/%v)", model.CurrentVersion, model.BuildNumber, model.BuildDate, model.BuildHash, model.BuildHashEnterprise))
|
|
mlog.Info(fmt.Sprintf("Enterprise Enabled: %v", model.BuildEnterpriseReady))
|
|
mlog.Info(fmt.Sprintf("Current working directory is %v", pwd))
|
|
mlog.Info(fmt.Sprintf("Loaded config file from %v", utils.FindConfigFile(configFileLocation)))
|
|
|
|
backend, appErr := a.FileBackend()
|
|
if appErr == nil {
|
|
appErr = backend.TestConnection()
|
|
}
|
|
if appErr != nil {
|
|
mlog.Error("Problem with file storage settings: " + appErr.Error())
|
|
}
|
|
|
|
if model.BuildEnterpriseReady == "true" {
|
|
a.LoadLicense()
|
|
}
|
|
|
|
a.DoAdvancedPermissionsMigration()
|
|
a.DoEmojisPermissionsMigration()
|
|
|
|
a.InitPlugins(*a.Config().PluginSettings.Directory, *a.Config().PluginSettings.ClientDirectory)
|
|
a.AddConfigListener(func(prevCfg, cfg *model.Config) {
|
|
if *cfg.PluginSettings.Enable {
|
|
a.InitPlugins(*cfg.PluginSettings.Directory, *a.Config().PluginSettings.ClientDirectory)
|
|
} else {
|
|
a.ShutDownPlugins()
|
|
}
|
|
})
|
|
|
|
serverErr := a.StartServer()
|
|
if serverErr != nil {
|
|
mlog.Critical(serverErr.Error())
|
|
return serverErr
|
|
}
|
|
|
|
api := api4.Init(a, a.Srv.Router)
|
|
wsapi.Init(a, a.Srv.WebSocketRouter)
|
|
web.NewWeb(a, a.Srv.Router)
|
|
|
|
license := a.License()
|
|
|
|
if license == nil && len(a.Config().SqlSettings.DataSourceReplicas) > 1 {
|
|
mlog.Warn("More than 1 read replica functionality disabled by current license. Please contact your system administrator about upgrading your enterprise license.")
|
|
a.UpdateConfig(func(cfg *model.Config) {
|
|
cfg.SqlSettings.DataSourceReplicas = cfg.SqlSettings.DataSourceReplicas[:1]
|
|
})
|
|
}
|
|
|
|
if license == nil {
|
|
a.UpdateConfig(func(cfg *model.Config) {
|
|
cfg.TeamSettings.MaxNotificationsPerChannel = &MaxNotificationsPerChannelDefault
|
|
})
|
|
}
|
|
|
|
a.ReloadConfig()
|
|
|
|
// Enable developer settings if this is a "dev" build
|
|
if model.BuildNumber == "dev" {
|
|
a.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableDeveloper = true })
|
|
}
|
|
|
|
resetStatuses(a)
|
|
|
|
// If we allow testing then listen for manual testing URL hits
|
|
if a.Config().ServiceSettings.EnableTesting {
|
|
manualtesting.Init(api)
|
|
}
|
|
|
|
a.Go(func() {
|
|
runSecurityJob(a)
|
|
})
|
|
a.Go(func() {
|
|
runDiagnosticsJob(a)
|
|
})
|
|
a.Go(func() {
|
|
runSessionCleanupJob(a)
|
|
})
|
|
a.Go(func() {
|
|
runTokenCleanupJob(a)
|
|
})
|
|
a.Go(func() {
|
|
runCommandWebhookCleanupJob(a)
|
|
})
|
|
|
|
if complianceI := a.Compliance; complianceI != nil {
|
|
complianceI.StartComplianceDailyJob()
|
|
}
|
|
|
|
if a.Cluster != nil {
|
|
a.RegisterAllClusterMessageHandlers()
|
|
a.Cluster.StartInterNodeCommunication()
|
|
}
|
|
|
|
if a.Metrics != nil {
|
|
a.Metrics.StartServer()
|
|
}
|
|
|
|
if a.Elasticsearch != nil {
|
|
a.StartElasticsearch()
|
|
}
|
|
|
|
if *a.Config().JobSettings.RunJobs {
|
|
a.Jobs.StartWorkers()
|
|
defer a.Jobs.StopWorkers()
|
|
}
|
|
if *a.Config().JobSettings.RunScheduler {
|
|
a.Jobs.StartSchedulers()
|
|
defer a.Jobs.StopSchedulers()
|
|
}
|
|
|
|
notifyReady()
|
|
|
|
// wait for kill signal before attempting to gracefully shutdown
|
|
// the running service
|
|
signal.Notify(interruptChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
|
|
<-interruptChan
|
|
|
|
if a.Cluster != nil {
|
|
a.Cluster.StopInterNodeCommunication()
|
|
}
|
|
|
|
if a.Metrics != nil {
|
|
a.Metrics.StopServer()
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func runSecurityJob(a *app.App) {
|
|
doSecurity(a)
|
|
model.CreateRecurringTask("Security", func() {
|
|
doSecurity(a)
|
|
}, time.Hour*4)
|
|
}
|
|
|
|
func runDiagnosticsJob(a *app.App) {
|
|
doDiagnostics(a)
|
|
model.CreateRecurringTask("Diagnostics", func() {
|
|
doDiagnostics(a)
|
|
}, time.Hour*24)
|
|
}
|
|
|
|
func runTokenCleanupJob(a *app.App) {
|
|
doTokenCleanup(a)
|
|
model.CreateRecurringTask("Token Cleanup", func() {
|
|
doTokenCleanup(a)
|
|
}, time.Hour*1)
|
|
}
|
|
|
|
func runCommandWebhookCleanupJob(a *app.App) {
|
|
doCommandWebhookCleanup(a)
|
|
model.CreateRecurringTask("Command Hook Cleanup", func() {
|
|
doCommandWebhookCleanup(a)
|
|
}, time.Hour*1)
|
|
}
|
|
|
|
func runSessionCleanupJob(a *app.App) {
|
|
doSessionCleanup(a)
|
|
model.CreateRecurringTask("Session Cleanup", func() {
|
|
doSessionCleanup(a)
|
|
}, time.Hour*24)
|
|
}
|
|
|
|
func resetStatuses(a *app.App) {
|
|
if result := <-a.Srv.Store.Status().ResetAll(); result.Err != nil {
|
|
mlog.Error(fmt.Sprint("Error to reset the server status.", result.Err.Error()))
|
|
}
|
|
}
|
|
|
|
func doSecurity(a *app.App) {
|
|
a.DoSecurityUpdateCheck()
|
|
}
|
|
|
|
func doDiagnostics(a *app.App) {
|
|
if *a.Config().LogSettings.EnableDiagnostics {
|
|
a.SendDailyDiagnostics()
|
|
}
|
|
}
|
|
|
|
func notifyReady() {
|
|
// If the environment vars provide a systemd notification socket,
|
|
// notify systemd that the server is ready.
|
|
systemdSocket := os.Getenv("NOTIFY_SOCKET")
|
|
if systemdSocket != "" {
|
|
mlog.Info("Sending systemd READY notification.")
|
|
|
|
err := sendSystemdReadyNotification(systemdSocket)
|
|
if err != nil {
|
|
mlog.Error(err.Error())
|
|
}
|
|
}
|
|
}
|
|
|
|
func sendSystemdReadyNotification(socketPath string) error {
|
|
msg := "READY=1"
|
|
addr := &net.UnixAddr{
|
|
Name: socketPath,
|
|
Net: "unixgram",
|
|
}
|
|
conn, err := net.DialUnix(addr.Net, nil, addr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer conn.Close()
|
|
_, err = conn.Write([]byte(msg))
|
|
return err
|
|
}
|
|
|
|
func doTokenCleanup(a *app.App) {
|
|
a.Srv.Store.Token().Cleanup()
|
|
}
|
|
|
|
func doCommandWebhookCleanup(a *app.App) {
|
|
a.Srv.Store.CommandWebhook().Cleanup()
|
|
}
|
|
|
|
func doSessionCleanup(a *app.App) {
|
|
a.Srv.Store.Session().Cleanup(model.GetMillis(), SESSIONS_CLEANUP_BATCH_SIZE)
|
|
}
|