github.com/dschalla/mattermost-server@v4.8.1-rc1+incompatible/cmd/platform/server.go (about) 1 // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package main 5 6 import ( 7 "net" 8 "os" 9 "os/signal" 10 "syscall" 11 "time" 12 13 l4g "github.com/alecthomas/log4go" 14 "github.com/mattermost/mattermost-server/api" 15 "github.com/mattermost/mattermost-server/api4" 16 "github.com/mattermost/mattermost-server/app" 17 "github.com/mattermost/mattermost-server/manualtesting" 18 "github.com/mattermost/mattermost-server/model" 19 "github.com/mattermost/mattermost-server/utils" 20 "github.com/mattermost/mattermost-server/web" 21 "github.com/mattermost/mattermost-server/wsapi" 22 "github.com/spf13/cobra" 23 ) 24 25 const ( 26 SESSIONS_CLEANUP_BATCH_SIZE = 1000 27 ) 28 29 var MaxNotificationsPerChannelDefault int64 = 1000000 30 31 var serverCmd = &cobra.Command{ 32 Use: "server", 33 Short: "Run the Mattermost server", 34 RunE: runServerCmd, 35 SilenceUsage: true, 36 } 37 38 func runServerCmd(cmd *cobra.Command, args []string) error { 39 config, err := cmd.Flags().GetString("config") 40 if err != nil { 41 return err 42 } 43 44 disableConfigWatch, _ := cmd.Flags().GetBool("disableconfigwatch") 45 46 interruptChan := make(chan os.Signal, 1) 47 return runServer(config, disableConfigWatch, interruptChan) 48 } 49 50 func runServer(configFileLocation string, disableConfigWatch bool, interruptChan chan os.Signal) error { 51 options := []app.Option{app.ConfigFile(configFileLocation)} 52 if disableConfigWatch { 53 options = append(options, app.DisableConfigWatch) 54 } 55 56 a, err := app.New(options...) 57 if err != nil { 58 l4g.Critical(err.Error()) 59 return err 60 } 61 defer a.Shutdown() 62 63 utils.TestConnection(a.Config()) 64 65 pwd, _ := os.Getwd() 66 l4g.Info(utils.T("mattermost.current_version"), model.CurrentVersion, model.BuildNumber, model.BuildDate, model.BuildHash, model.BuildHashEnterprise) 67 l4g.Info(utils.T("mattermost.entreprise_enabled"), model.BuildEnterpriseReady) 68 l4g.Info(utils.T("mattermost.working_dir"), pwd) 69 l4g.Info(utils.T("mattermost.config_file"), utils.FindConfigFile(configFileLocation)) 70 71 backend, appErr := a.FileBackend() 72 if appErr == nil { 73 appErr = backend.TestConnection() 74 } 75 if appErr != nil { 76 l4g.Error("Problem with file storage settings: " + appErr.Error()) 77 } 78 79 if model.BuildEnterpriseReady == "true" { 80 a.LoadLicense() 81 } 82 83 a.InitPlugins(*a.Config().PluginSettings.Directory, *a.Config().PluginSettings.ClientDirectory, nil) 84 a.AddConfigListener(func(prevCfg, cfg *model.Config) { 85 if *cfg.PluginSettings.Enable { 86 a.InitPlugins(*cfg.PluginSettings.Directory, *a.Config().PluginSettings.ClientDirectory, nil) 87 } else { 88 a.ShutDownPlugins() 89 } 90 }) 91 92 serverErr := a.StartServer() 93 if serverErr != nil { 94 l4g.Critical(serverErr.Error()) 95 return serverErr 96 } 97 98 api4.Init(a, a.Srv.Router, false) 99 api3 := api.Init(a, a.Srv.Router) 100 wsapi.Init(a, a.Srv.WebSocketRouter) 101 web.Init(api3) 102 103 license := a.License() 104 105 if license == nil && len(a.Config().SqlSettings.DataSourceReplicas) > 1 { 106 l4g.Warn(utils.T("store.sql.read_replicas_not_licensed.critical")) 107 a.UpdateConfig(func(cfg *model.Config) { 108 cfg.SqlSettings.DataSourceReplicas = cfg.SqlSettings.DataSourceReplicas[:1] 109 }) 110 } 111 112 if license == nil { 113 a.UpdateConfig(func(cfg *model.Config) { 114 cfg.TeamSettings.MaxNotificationsPerChannel = &MaxNotificationsPerChannelDefault 115 }) 116 } 117 118 a.ReloadConfig() 119 120 // Enable developer settings if this is a "dev" build 121 if model.BuildNumber == "dev" { 122 a.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableDeveloper = true }) 123 } 124 125 resetStatuses(a) 126 127 // If we allow testing then listen for manual testing URL hits 128 if a.Config().ServiceSettings.EnableTesting { 129 manualtesting.Init(api3) 130 } 131 132 a.EnsureDiagnosticId() 133 134 a.Go(func() { 135 runSecurityJob(a) 136 }) 137 a.Go(func() { 138 runDiagnosticsJob(a) 139 }) 140 a.Go(func() { 141 runSessionCleanupJob(a) 142 }) 143 a.Go(func() { 144 runTokenCleanupJob(a) 145 }) 146 a.Go(func() { 147 runCommandWebhookCleanupJob(a) 148 }) 149 150 if complianceI := a.Compliance; complianceI != nil { 151 complianceI.StartComplianceDailyJob() 152 } 153 154 if a.Cluster != nil { 155 a.RegisterAllClusterMessageHandlers() 156 a.Cluster.StartInterNodeCommunication() 157 } 158 159 if a.Metrics != nil { 160 a.Metrics.StartServer() 161 } 162 163 if a.Elasticsearch != nil { 164 a.Go(func() { 165 if err := a.Elasticsearch.Start(); err != nil { 166 l4g.Error(err.Error()) 167 } 168 }) 169 } 170 171 if *a.Config().JobSettings.RunJobs { 172 a.Jobs.StartWorkers() 173 } 174 if *a.Config().JobSettings.RunScheduler { 175 a.Jobs.StartSchedulers() 176 } 177 178 notifyReady() 179 180 // wait for kill signal before attempting to gracefully shutdown 181 // the running service 182 signal.Notify(interruptChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) 183 <-interruptChan 184 185 if a.Cluster != nil { 186 a.Cluster.StopInterNodeCommunication() 187 } 188 189 if a.Metrics != nil { 190 a.Metrics.StopServer() 191 } 192 193 a.Jobs.StopSchedulers() 194 a.Jobs.StopWorkers() 195 196 return nil 197 } 198 199 func runSecurityJob(a *app.App) { 200 doSecurity(a) 201 model.CreateRecurringTask("Security", func() { 202 doSecurity(a) 203 }, time.Hour*4) 204 } 205 206 func runDiagnosticsJob(a *app.App) { 207 doDiagnostics(a) 208 model.CreateRecurringTask("Diagnostics", func() { 209 doDiagnostics(a) 210 }, time.Hour*24) 211 } 212 213 func runTokenCleanupJob(a *app.App) { 214 doTokenCleanup(a) 215 model.CreateRecurringTask("Token Cleanup", func() { 216 doTokenCleanup(a) 217 }, time.Hour*1) 218 } 219 220 func runCommandWebhookCleanupJob(a *app.App) { 221 doCommandWebhookCleanup(a) 222 model.CreateRecurringTask("Command Hook Cleanup", func() { 223 doCommandWebhookCleanup(a) 224 }, time.Hour*1) 225 } 226 227 func runSessionCleanupJob(a *app.App) { 228 doSessionCleanup(a) 229 model.CreateRecurringTask("Session Cleanup", func() { 230 doSessionCleanup(a) 231 }, time.Hour*24) 232 } 233 234 func resetStatuses(a *app.App) { 235 if result := <-a.Srv.Store.Status().ResetAll(); result.Err != nil { 236 l4g.Error(utils.T("mattermost.reset_status.error"), result.Err.Error()) 237 } 238 } 239 240 func doSecurity(a *app.App) { 241 a.DoSecurityUpdateCheck() 242 } 243 244 func doDiagnostics(a *app.App) { 245 if *a.Config().LogSettings.EnableDiagnostics { 246 a.SendDailyDiagnostics() 247 } 248 } 249 250 func notifyReady() { 251 // If the environment vars provide a systemd notification socket, 252 // notify systemd that the server is ready. 253 systemdSocket := os.Getenv("NOTIFY_SOCKET") 254 if systemdSocket != "" { 255 l4g.Info("Sending systemd READY notification.") 256 257 err := sendSystemdReadyNotification(systemdSocket) 258 if err != nil { 259 l4g.Error(err.Error()) 260 } 261 } 262 } 263 264 func sendSystemdReadyNotification(socketPath string) error { 265 msg := "READY=1" 266 addr := &net.UnixAddr{ 267 Name: socketPath, 268 Net: "unixgram", 269 } 270 conn, err := net.DialUnix(addr.Net, nil, addr) 271 if err != nil { 272 return err 273 } 274 defer conn.Close() 275 _, err = conn.Write([]byte(msg)) 276 return err 277 } 278 279 func doTokenCleanup(a *app.App) { 280 a.Srv.Store.Token().Cleanup() 281 } 282 283 func doCommandWebhookCleanup(a *app.App) { 284 a.Srv.Store.CommandWebhook().Cleanup() 285 } 286 287 func doSessionCleanup(a *app.App) { 288 a.Srv.Store.Session().Cleanup(model.GetMillis(), SESSIONS_CLEANUP_BATCH_SIZE) 289 }