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