github.com/lologarithm/mattermost-server@v5.3.2-0.20181002060438-c82a84ed765b+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  }