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