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