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