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