github.com/dschalla/mattermost-server@v4.8.1-rc1+incompatible/cmd/platform/server.go (about)

     1  // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package main
     5  
     6  import (
     7  	"net"
     8  	"os"
     9  	"os/signal"
    10  	"syscall"
    11  	"time"
    12  
    13  	l4g "github.com/alecthomas/log4go"
    14  	"github.com/mattermost/mattermost-server/api"
    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/model"
    19  	"github.com/mattermost/mattermost-server/utils"
    20  	"github.com/mattermost/mattermost-server/web"
    21  	"github.com/mattermost/mattermost-server/wsapi"
    22  	"github.com/spf13/cobra"
    23  )
    24  
    25  const (
    26  	SESSIONS_CLEANUP_BATCH_SIZE = 1000
    27  )
    28  
    29  var MaxNotificationsPerChannelDefault int64 = 1000000
    30  
    31  var serverCmd = &cobra.Command{
    32  	Use:          "server",
    33  	Short:        "Run the Mattermost server",
    34  	RunE:         runServerCmd,
    35  	SilenceUsage: true,
    36  }
    37  
    38  func runServerCmd(cmd *cobra.Command, args []string) error {
    39  	config, err := cmd.Flags().GetString("config")
    40  	if err != nil {
    41  		return err
    42  	}
    43  
    44  	disableConfigWatch, _ := cmd.Flags().GetBool("disableconfigwatch")
    45  
    46  	interruptChan := make(chan os.Signal, 1)
    47  	return runServer(config, disableConfigWatch, interruptChan)
    48  }
    49  
    50  func runServer(configFileLocation string, disableConfigWatch bool, interruptChan chan os.Signal) error {
    51  	options := []app.Option{app.ConfigFile(configFileLocation)}
    52  	if disableConfigWatch {
    53  		options = append(options, app.DisableConfigWatch)
    54  	}
    55  
    56  	a, err := app.New(options...)
    57  	if err != nil {
    58  		l4g.Critical(err.Error())
    59  		return err
    60  	}
    61  	defer a.Shutdown()
    62  
    63  	utils.TestConnection(a.Config())
    64  
    65  	pwd, _ := os.Getwd()
    66  	l4g.Info(utils.T("mattermost.current_version"), model.CurrentVersion, model.BuildNumber, model.BuildDate, model.BuildHash, model.BuildHashEnterprise)
    67  	l4g.Info(utils.T("mattermost.entreprise_enabled"), model.BuildEnterpriseReady)
    68  	l4g.Info(utils.T("mattermost.working_dir"), pwd)
    69  	l4g.Info(utils.T("mattermost.config_file"), utils.FindConfigFile(configFileLocation))
    70  
    71  	backend, appErr := a.FileBackend()
    72  	if appErr == nil {
    73  		appErr = backend.TestConnection()
    74  	}
    75  	if appErr != nil {
    76  		l4g.Error("Problem with file storage settings: " + appErr.Error())
    77  	}
    78  
    79  	if model.BuildEnterpriseReady == "true" {
    80  		a.LoadLicense()
    81  	}
    82  
    83  	a.InitPlugins(*a.Config().PluginSettings.Directory, *a.Config().PluginSettings.ClientDirectory, nil)
    84  	a.AddConfigListener(func(prevCfg, cfg *model.Config) {
    85  		if *cfg.PluginSettings.Enable {
    86  			a.InitPlugins(*cfg.PluginSettings.Directory, *a.Config().PluginSettings.ClientDirectory, nil)
    87  		} else {
    88  			a.ShutDownPlugins()
    89  		}
    90  	})
    91  
    92  	serverErr := a.StartServer()
    93  	if serverErr != nil {
    94  		l4g.Critical(serverErr.Error())
    95  		return serverErr
    96  	}
    97  
    98  	api4.Init(a, a.Srv.Router, false)
    99  	api3 := api.Init(a, a.Srv.Router)
   100  	wsapi.Init(a, a.Srv.WebSocketRouter)
   101  	web.Init(api3)
   102  
   103  	license := a.License()
   104  
   105  	if license == nil && len(a.Config().SqlSettings.DataSourceReplicas) > 1 {
   106  		l4g.Warn(utils.T("store.sql.read_replicas_not_licensed.critical"))
   107  		a.UpdateConfig(func(cfg *model.Config) {
   108  			cfg.SqlSettings.DataSourceReplicas = cfg.SqlSettings.DataSourceReplicas[:1]
   109  		})
   110  	}
   111  
   112  	if license == nil {
   113  		a.UpdateConfig(func(cfg *model.Config) {
   114  			cfg.TeamSettings.MaxNotificationsPerChannel = &MaxNotificationsPerChannelDefault
   115  		})
   116  	}
   117  
   118  	a.ReloadConfig()
   119  
   120  	// Enable developer settings if this is a "dev" build
   121  	if model.BuildNumber == "dev" {
   122  		a.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableDeveloper = true })
   123  	}
   124  
   125  	resetStatuses(a)
   126  
   127  	// If we allow testing then listen for manual testing URL hits
   128  	if a.Config().ServiceSettings.EnableTesting {
   129  		manualtesting.Init(api3)
   130  	}
   131  
   132  	a.EnsureDiagnosticId()
   133  
   134  	a.Go(func() {
   135  		runSecurityJob(a)
   136  	})
   137  	a.Go(func() {
   138  		runDiagnosticsJob(a)
   139  	})
   140  	a.Go(func() {
   141  		runSessionCleanupJob(a)
   142  	})
   143  	a.Go(func() {
   144  		runTokenCleanupJob(a)
   145  	})
   146  	a.Go(func() {
   147  		runCommandWebhookCleanupJob(a)
   148  	})
   149  
   150  	if complianceI := a.Compliance; complianceI != nil {
   151  		complianceI.StartComplianceDailyJob()
   152  	}
   153  
   154  	if a.Cluster != nil {
   155  		a.RegisterAllClusterMessageHandlers()
   156  		a.Cluster.StartInterNodeCommunication()
   157  	}
   158  
   159  	if a.Metrics != nil {
   160  		a.Metrics.StartServer()
   161  	}
   162  
   163  	if a.Elasticsearch != nil {
   164  		a.Go(func() {
   165  			if err := a.Elasticsearch.Start(); err != nil {
   166  				l4g.Error(err.Error())
   167  			}
   168  		})
   169  	}
   170  
   171  	if *a.Config().JobSettings.RunJobs {
   172  		a.Jobs.StartWorkers()
   173  	}
   174  	if *a.Config().JobSettings.RunScheduler {
   175  		a.Jobs.StartSchedulers()
   176  	}
   177  
   178  	notifyReady()
   179  
   180  	// wait for kill signal before attempting to gracefully shutdown
   181  	// the running service
   182  	signal.Notify(interruptChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
   183  	<-interruptChan
   184  
   185  	if a.Cluster != nil {
   186  		a.Cluster.StopInterNodeCommunication()
   187  	}
   188  
   189  	if a.Metrics != nil {
   190  		a.Metrics.StopServer()
   191  	}
   192  
   193  	a.Jobs.StopSchedulers()
   194  	a.Jobs.StopWorkers()
   195  
   196  	return nil
   197  }
   198  
   199  func runSecurityJob(a *app.App) {
   200  	doSecurity(a)
   201  	model.CreateRecurringTask("Security", func() {
   202  		doSecurity(a)
   203  	}, time.Hour*4)
   204  }
   205  
   206  func runDiagnosticsJob(a *app.App) {
   207  	doDiagnostics(a)
   208  	model.CreateRecurringTask("Diagnostics", func() {
   209  		doDiagnostics(a)
   210  	}, time.Hour*24)
   211  }
   212  
   213  func runTokenCleanupJob(a *app.App) {
   214  	doTokenCleanup(a)
   215  	model.CreateRecurringTask("Token Cleanup", func() {
   216  		doTokenCleanup(a)
   217  	}, time.Hour*1)
   218  }
   219  
   220  func runCommandWebhookCleanupJob(a *app.App) {
   221  	doCommandWebhookCleanup(a)
   222  	model.CreateRecurringTask("Command Hook Cleanup", func() {
   223  		doCommandWebhookCleanup(a)
   224  	}, time.Hour*1)
   225  }
   226  
   227  func runSessionCleanupJob(a *app.App) {
   228  	doSessionCleanup(a)
   229  	model.CreateRecurringTask("Session Cleanup", func() {
   230  		doSessionCleanup(a)
   231  	}, time.Hour*24)
   232  }
   233  
   234  func resetStatuses(a *app.App) {
   235  	if result := <-a.Srv.Store.Status().ResetAll(); result.Err != nil {
   236  		l4g.Error(utils.T("mattermost.reset_status.error"), result.Err.Error())
   237  	}
   238  }
   239  
   240  func doSecurity(a *app.App) {
   241  	a.DoSecurityUpdateCheck()
   242  }
   243  
   244  func doDiagnostics(a *app.App) {
   245  	if *a.Config().LogSettings.EnableDiagnostics {
   246  		a.SendDailyDiagnostics()
   247  	}
   248  }
   249  
   250  func notifyReady() {
   251  	// If the environment vars provide a systemd notification socket,
   252  	// notify systemd that the server is ready.
   253  	systemdSocket := os.Getenv("NOTIFY_SOCKET")
   254  	if systemdSocket != "" {
   255  		l4g.Info("Sending systemd READY notification.")
   256  
   257  		err := sendSystemdReadyNotification(systemdSocket)
   258  		if err != nil {
   259  			l4g.Error(err.Error())
   260  		}
   261  	}
   262  }
   263  
   264  func sendSystemdReadyNotification(socketPath string) error {
   265  	msg := "READY=1"
   266  	addr := &net.UnixAddr{
   267  		Name: socketPath,
   268  		Net:  "unixgram",
   269  	}
   270  	conn, err := net.DialUnix(addr.Net, nil, addr)
   271  	if err != nil {
   272  		return err
   273  	}
   274  	defer conn.Close()
   275  	_, err = conn.Write([]byte(msg))
   276  	return err
   277  }
   278  
   279  func doTokenCleanup(a *app.App) {
   280  	a.Srv.Store.Token().Cleanup()
   281  }
   282  
   283  func doCommandWebhookCleanup(a *app.App) {
   284  	a.Srv.Store.CommandWebhook().Cleanup()
   285  }
   286  
   287  func doSessionCleanup(a *app.App) {
   288  	a.Srv.Store.Session().Cleanup(model.GetMillis(), SESSIONS_CLEANUP_BATCH_SIZE)
   289  }