github.com/levb/mattermost-server@v5.3.1+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) 102 a.AddConfigListener(func(prevCfg, cfg *model.Config) { 103 if *cfg.PluginSettings.Enable { 104 a.InitPlugins(*cfg.PluginSettings.Directory, *a.Config().PluginSettings.ClientDirectory) 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.Go(func() { 150 runSecurityJob(a) 151 }) 152 a.Go(func() { 153 runDiagnosticsJob(a) 154 }) 155 a.Go(func() { 156 runSessionCleanupJob(a) 157 }) 158 a.Go(func() { 159 runTokenCleanupJob(a) 160 }) 161 a.Go(func() { 162 runCommandWebhookCleanupJob(a) 163 }) 164 165 if complianceI := a.Compliance; complianceI != nil { 166 complianceI.StartComplianceDailyJob() 167 } 168 169 if a.Cluster != nil { 170 a.RegisterAllClusterMessageHandlers() 171 a.Cluster.StartInterNodeCommunication() 172 } 173 174 if a.Metrics != nil { 175 a.Metrics.StartServer() 176 } 177 178 if a.Elasticsearch != nil { 179 a.StartElasticsearch() 180 } 181 182 if *a.Config().JobSettings.RunJobs { 183 a.Jobs.StartWorkers() 184 defer a.Jobs.StopWorkers() 185 } 186 if *a.Config().JobSettings.RunScheduler { 187 a.Jobs.StartSchedulers() 188 defer a.Jobs.StopSchedulers() 189 } 190 191 notifyReady() 192 193 // wait for kill signal before attempting to gracefully shutdown 194 // the running service 195 signal.Notify(interruptChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) 196 <-interruptChan 197 198 if a.Cluster != nil { 199 a.Cluster.StopInterNodeCommunication() 200 } 201 202 if a.Metrics != nil { 203 a.Metrics.StopServer() 204 } 205 206 return nil 207 } 208 209 func runSecurityJob(a *app.App) { 210 doSecurity(a) 211 model.CreateRecurringTask("Security", func() { 212 doSecurity(a) 213 }, time.Hour*4) 214 } 215 216 func runDiagnosticsJob(a *app.App) { 217 doDiagnostics(a) 218 model.CreateRecurringTask("Diagnostics", func() { 219 doDiagnostics(a) 220 }, time.Hour*24) 221 } 222 223 func runTokenCleanupJob(a *app.App) { 224 doTokenCleanup(a) 225 model.CreateRecurringTask("Token Cleanup", func() { 226 doTokenCleanup(a) 227 }, time.Hour*1) 228 } 229 230 func runCommandWebhookCleanupJob(a *app.App) { 231 doCommandWebhookCleanup(a) 232 model.CreateRecurringTask("Command Hook Cleanup", func() { 233 doCommandWebhookCleanup(a) 234 }, time.Hour*1) 235 } 236 237 func runSessionCleanupJob(a *app.App) { 238 doSessionCleanup(a) 239 model.CreateRecurringTask("Session Cleanup", func() { 240 doSessionCleanup(a) 241 }, time.Hour*24) 242 } 243 244 func resetStatuses(a *app.App) { 245 if result := <-a.Srv.Store.Status().ResetAll(); result.Err != nil { 246 mlog.Error(fmt.Sprint("mattermost.reset_status.error FIXME: NOT FOUND IN TRANSLATIONS FILE", result.Err.Error())) 247 } 248 } 249 250 func doSecurity(a *app.App) { 251 a.DoSecurityUpdateCheck() 252 } 253 254 func doDiagnostics(a *app.App) { 255 if *a.Config().LogSettings.EnableDiagnostics { 256 a.SendDailyDiagnostics() 257 } 258 } 259 260 func notifyReady() { 261 // If the environment vars provide a systemd notification socket, 262 // notify systemd that the server is ready. 263 systemdSocket := os.Getenv("NOTIFY_SOCKET") 264 if systemdSocket != "" { 265 mlog.Info("Sending systemd READY notification.") 266 267 err := sendSystemdReadyNotification(systemdSocket) 268 if err != nil { 269 mlog.Error(err.Error()) 270 } 271 } 272 } 273 274 func sendSystemdReadyNotification(socketPath string) error { 275 msg := "READY=1" 276 addr := &net.UnixAddr{ 277 Name: socketPath, 278 Net: "unixgram", 279 } 280 conn, err := net.DialUnix(addr.Net, nil, addr) 281 if err != nil { 282 return err 283 } 284 defer conn.Close() 285 _, err = conn.Write([]byte(msg)) 286 return err 287 } 288 289 func doTokenCleanup(a *app.App) { 290 a.Srv.Store.Token().Cleanup() 291 } 292 293 func doCommandWebhookCleanup(a *app.App) { 294 a.Srv.Store.CommandWebhook().Cleanup() 295 } 296 297 func doSessionCleanup(a *app.App) { 298 a.Srv.Store.Session().Cleanup(model.GetMillis(), SESSIONS_CLEANUP_BATCH_SIZE) 299 }