server.go 5.94 KB
Newer Older
1
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
2 3 4 5 6 7 8 9 10 11 12
// See License.txt for license information.

package main

import (
	"os"
	"os/signal"
	"syscall"
	"time"

	l4g "github.com/alecthomas/log4go"
Christopher Speller's avatar
Christopher Speller committed
13 14 15 16 17 18 19 20
	"github.com/mattermost/mattermost-server/api"
	"github.com/mattermost/mattermost-server/api4"
	"github.com/mattermost/mattermost-server/app"
	"github.com/mattermost/mattermost-server/manualtesting"
	"github.com/mattermost/mattermost-server/model"
	"github.com/mattermost/mattermost-server/utils"
	"github.com/mattermost/mattermost-server/web"
	"github.com/mattermost/mattermost-server/wsapi"
21 22 23
	"github.com/spf13/cobra"
)

24 25
var MaxNotificationsPerChannelDefault int64 = 1000000

26 27 28 29 30 31 32 33 34 35 36
var serverCmd = &cobra.Command{
	Use:   "server",
	Short: "Run the Mattermost server",
	RunE:  runServerCmd,
}

func runServerCmd(cmd *cobra.Command, args []string) error {
	config, err := cmd.Flags().GetString("config")
	if err != nil {
		return err
	}
37

38 39
	utils.CfgDisableConfigWatch, _ = cmd.Flags().GetBool("disableconfigwatch")

40 41 42 43 44
	runServer(config)
	return nil
}

func runServer(configFileLocation string) {
45 46 47 48 49 50 51
	if err := utils.InitAndLoadConfig(configFileLocation); err != nil {
		l4g.Exit("Unable to load Mattermost configuration file: ", err)
		return
	}

	if err := utils.InitTranslations(utils.Cfg.LocalizationSettings); err != nil {
		l4g.Exit("Unable to load Mattermost translation files: %v", err)
52 53 54 55 56 57 58 59 60 61 62
		return
	}

	utils.TestConnection(utils.Cfg)

	pwd, _ := os.Getwd()
	l4g.Info(utils.T("mattermost.current_version"), model.CurrentVersion, model.BuildNumber, model.BuildDate, model.BuildHash, model.BuildHashEnterprise)
	l4g.Info(utils.T("mattermost.entreprise_enabled"), model.BuildEnterpriseReady)
	l4g.Info(utils.T("mattermost.working_dir"), pwd)
	l4g.Info(utils.T("mattermost.config_file"), utils.FindConfigFile(configFileLocation))

Chris's avatar
Chris committed
63
	a := app.New(app.ConfigFile(configFileLocation))
64 65
	defer a.Shutdown()

66 67 68 69 70 71 72 73
	backend, err := a.FileBackend()
	if err == nil {
		err = backend.TestConnection()
	}
	if err != nil {
		l4g.Error("Problem with file storage settings: " + err.Error())
	}

Chris's avatar
Chris committed
74 75 76
	if model.BuildEnterpriseReady == "true" {
		a.LoadLicense()
	}
77 78

	if webappDir, ok := utils.FindDir(model.CLIENT_DIR); ok {
79
		a.InitPlugins(*a.Config().PluginSettings.Directory, webappDir+"/plugins")
80 81 82 83 84 85 86
		utils.AddConfigListener(func(prevCfg, cfg *model.Config) {
			if *cfg.PluginSettings.Enable {
				a.InitPlugins(*cfg.PluginSettings.Directory, webappDir+"/plugins")
			} else {
				a.ShutDownPlugins()
			}
		})
87 88 89
	} else {
		l4g.Error("Unable to find webapp directory, could not initialize plugins")
	}
Chris's avatar
Chris committed
90

Chris's avatar
Chris committed
91
	a.StartServer()
92 93
	api4.Init(a, a.Srv.Router, false)
	api3 := api.Init(a, a.Srv.Router)
94
	wsapi.Init(a, a.Srv.WebSocketRouter)
95
	web.Init(api3)
96

Chris's avatar
Chris committed
97
	if !utils.IsLicensed() && len(a.Config().SqlSettings.DataSourceReplicas) > 1 {
98
		l4g.Warn(utils.T("store.sql.read_replicas_not_licensed.critical"))
Chris's avatar
Chris committed
99 100 101
		a.UpdateConfig(func(cfg *model.Config) {
			cfg.SqlSettings.DataSourceReplicas = cfg.SqlSettings.DataSourceReplicas[:1]
		})
102 103
	}

104
	if !utils.IsLicensed() {
Chris's avatar
Chris committed
105 106 107
		a.UpdateConfig(func(cfg *model.Config) {
			cfg.TeamSettings.MaxNotificationsPerChannel = &MaxNotificationsPerChannelDefault
		})
108 109
	}

Chris's avatar
Chris committed
110
	a.ReloadConfig()
111

112 113
	// Enable developer settings if this is a "dev" build
	if model.BuildNumber == "dev" {
Chris's avatar
Chris committed
114
		a.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableDeveloper = true })
115 116
	}

Chris's avatar
Chris committed
117
	resetStatuses(a)
118 119

	// If we allow testing then listen for manual testing URL hits
Chris's avatar
Chris committed
120
	if a.Config().ServiceSettings.EnableTesting {
121
		manualtesting.Init(api3)
122 123
	}

Chris's avatar
Chris committed
124
	setDiagnosticId(a)
125
	utils.RegenerateClientConfig()
Chris's avatar
Chris committed
126 127
	go runSecurityJob(a)
	go runDiagnosticsJob(a)
128

Chris's avatar
Chris committed
129 130
	go runTokenCleanupJob(a)
	go runCommandWebhookCleanupJob(a)
131

Chris's avatar
Chris committed
132
	if complianceI := a.Compliance; complianceI != nil {
133 134 135
		complianceI.StartComplianceDailyJob()
	}

Chris's avatar
Chris committed
136
	if a.Cluster != nil {
Chris's avatar
Chris committed
137
		a.RegisterAllClusterMessageHandlers()
Chris's avatar
Chris committed
138
		a.Cluster.StartInterNodeCommunication()
139 140
	}

Chris's avatar
Chris committed
141 142
	if a.Metrics != nil {
		a.Metrics.StartServer()
143 144
	}

Chris's avatar
Chris committed
145
	if a.Elasticsearch != nil {
146 147 148 149 150
		a.Go(func() {
			if err := a.Elasticsearch.Start(); err != nil {
				l4g.Error(err.Error())
			}
		})
151 152
	}

Chris's avatar
Chris committed
153
	if *a.Config().JobSettings.RunJobs {
154
		a.Jobs.StartWorkers()
155
	}
Chris's avatar
Chris committed
156
	if *a.Config().JobSettings.RunScheduler {
157
		a.Jobs.StartSchedulers()
158
	}
159

160 161
	// wait for kill signal before attempting to gracefully shutdown
	// the running service
162
	c := make(chan os.Signal, 1)
163 164 165
	signal.Notify(c, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
	<-c

Chris's avatar
Chris committed
166 167
	if a.Cluster != nil {
		a.Cluster.StopInterNodeCommunication()
168 169
	}

Chris's avatar
Chris committed
170 171
	if a.Metrics != nil {
		a.Metrics.StopServer()
172 173
	}

174 175
	a.Jobs.StopSchedulers()
	a.Jobs.StopWorkers()
176 177
}

Chris's avatar
Chris committed
178 179 180 181 182
func runSecurityJob(a *app.App) {
	doSecurity(a)
	model.CreateRecurringTask("Security", func() {
		doSecurity(a)
	}, time.Hour*4)
183 184
}

Chris's avatar
Chris committed
185 186 187 188 189
func runDiagnosticsJob(a *app.App) {
	doDiagnostics(a)
	model.CreateRecurringTask("Diagnostics", func() {
		doDiagnostics(a)
	}, time.Hour*24)
190 191
}

Chris's avatar
Chris committed
192 193 194 195 196
func runTokenCleanupJob(a *app.App) {
	doTokenCleanup(a)
	model.CreateRecurringTask("Token Cleanup", func() {
		doTokenCleanup(a)
	}, time.Hour*1)
197 198
}

Chris's avatar
Chris committed
199 200 201 202 203
func runCommandWebhookCleanupJob(a *app.App) {
	doCommandWebhookCleanup(a)
	model.CreateRecurringTask("Command Hook Cleanup", func() {
		doCommandWebhookCleanup(a)
	}, time.Hour*1)
204 205
}

Chris's avatar
Chris committed
206 207
func resetStatuses(a *app.App) {
	if result := <-a.Srv.Store.Status().ResetAll(); result.Err != nil {
208 209 210 211
		l4g.Error(utils.T("mattermost.reset_status.error"), result.Err.Error())
	}
}

Chris's avatar
Chris committed
212 213
func setDiagnosticId(a *app.App) {
	if result := <-a.Srv.Store.System().Get(); result.Err == nil {
214 215 216 217 218 219
		props := result.Data.(model.StringMap)

		id := props[model.SYSTEM_DIAGNOSTIC_ID]
		if len(id) == 0 {
			id = model.NewId()
			systemId := &model.System{Name: model.SYSTEM_DIAGNOSTIC_ID, Value: id}
Chris's avatar
Chris committed
220
			<-a.Srv.Store.System().Save(systemId)
221 222 223 224 225 226
		}

		utils.CfgDiagnosticId = id
	}
}

Chris's avatar
Chris committed
227 228
func doSecurity(a *app.App) {
	a.DoSecurityUpdateCheck()
229 230
}

Chris's avatar
Chris committed
231
func doDiagnostics(a *app.App) {
232
	if *utils.Cfg.LogSettings.EnableDiagnostics {
Chris's avatar
Chris committed
233
		a.SendDailyDiagnostics()
234 235
	}
}
236

Chris's avatar
Chris committed
237 238
func doTokenCleanup(a *app.App) {
	a.Srv.Store.Token().Cleanup()
239
}
240

Chris's avatar
Chris committed
241 242
func doCommandWebhookCleanup(a *app.App) {
	a.Srv.Store.CommandWebhook().Cleanup()
243
}