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