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  }