MM-40818: Refactor product initialization to happen late (#19658)

This is more of a general refactor of the initialization
process which should allow us to pass services more
easily.

The changes are minimal to keep the scope limited.
For now, the objective is to pass the file service
to the Channels product. For that, it was required
to move some of the enterprise interfaces under Channels
from Server.

We also create a filestore field in the server to
avoid creating filestore reference every time
we make a filestore operation. This will be later
passed on to the Channels product.

Also removed an unnecessary test. 
The test was working so far because we were creating
the filebackend every time for every request. But
we should go via UpdateConfig call which would fail,
were we to assign an invalid filestore name.

So we were actually testing for a different thing.
Therefore, removed the test.

```release-note
NONE
```
Этот коммит содержится в:
Agniva De Sarker
2022-03-03 12:22:10 +05:30
коммит произвёл GitHub
родитель 768fe43d3a
Коммит ab8de49f0a
17 изменённых файлов: 119 добавлений и 200 удалений

Просмотреть файл

@@ -159,6 +159,7 @@ type Server struct {
searchLicenseListenerId string
loggerLicenseListenerId string
configStore *configWrapper
filestore filestore.FileBackend
telemetryService *telemetry.TelemetryService
userService *users.UserService
@@ -184,10 +185,7 @@ type Server struct {
Cluster einterfaces.ClusterInterface
Cloud einterfaces.CloudInterface
Metrics einterfaces.MetricsInterface
Notification einterfaces.NotificationInterface
LicenseManager einterfaces.LicenseInterface
Saml einterfaces.SamlInterface
Ldap einterfaces.LdapInterface
CacheProvider cache.Provider
@@ -257,27 +255,8 @@ func NewServer(options ...Option) (*Server, error) {
mlog.Info("Server is initializing...", mlog.String("go_version", runtime.Version()))
s.httpService = httpservice.MakeHTTPService(s)
s.licenseWrapper = &licenseWrapper{
srv: s,
}
serviceMap := map[ServiceKey]interface{}{
ConfigKey: s.configStore,
LicenseKey: s.licenseWrapper,
}
// Step 3: Initialize products.
// Depends on s.httpService.
for name, initializer := range products {
prod, err2 := initializer(s, serviceMap)
if err2 != nil {
return nil, errors.Wrapf(err2, "error initializing product: %s", name)
}
s.products[name] = prod
}
// Step 4: Search Engine
// Step 3: Search Engine
// Depends on Step 1 (config).
searchEngine := searchengine.NewBroker(s.Config())
bleveEngine := bleveengine.NewBleveEngine(s.Config())
@@ -287,12 +266,11 @@ func NewServer(options ...Option) (*Server, error) {
searchEngine.RegisterBleveEngine(bleveEngine)
s.SearchEngine = searchEngine
// Step 5: Init Enterprise
// Depends on step 3 (s.Channels() must be non-nil)
// and step 4 (s.SearchEngine must be non-nil)
// Step 4: Init Enterprise
// Depends on step 3 (s.SearchEngine must be non-nil)
s.initEnterprise()
// Step 6: Cache provider.
// Step 5: Cache provider.
// At the moment we only have this implementation
// in the future the cache provider will be built based on the loaded config
s.CacheProvider = cache.NewProvider()
@@ -300,12 +278,8 @@ func NewServer(options ...Option) (*Server, error) {
return nil, errors.Wrapf(err2, "Unable to connect to cache provider")
}
// It is important to initialize the hub only after the global logger is set
// to avoid race conditions while logging from inside the hub.
s.HubStart()
// Step 7: Store.
// Depends on Step 1 (config), 5 (metrics, cluster) and 6 (cacheProvider).
// Step 6: Store.
// Depends on Step 1 (config), 4 (metrics, cluster) and 5 (cacheProvider).
if s.newStore == nil {
s.newStore = func() (store.Store, error) {
s.sqlStore = sqlstore.New(s.Config().SqlSettings, s.Metrics)
@@ -347,6 +321,42 @@ func NewServer(options ...Option) (*Server, error) {
return nil, errors.Wrap(err, "cannot create store")
}
if model.BuildEnterpriseReady == "true" {
s.LoadLicense()
}
license := s.License()
// Step 7: Initialize filestore
backend, err := filestore.NewFileBackend(s.Config().FileSettings.ToFileBackendSettings(license != nil && *license.Features.Compliance))
if err != nil {
return nil, errors.Wrap(err, "failed to initialize filebackend")
}
s.filestore = backend
s.licenseWrapper = &licenseWrapper{
srv: s,
}
serviceMap := map[ServiceKey]interface{}{
ConfigKey: s.configStore,
LicenseKey: s.licenseWrapper,
}
// Step 8: Initialize products.
// Depends on s.httpService.
for name, initializer := range products {
prod, err2 := initializer(s, serviceMap)
if err2 != nil {
return nil, errors.Wrapf(err2, "error initializing product: %s", name)
}
s.products[name] = prod
}
// It is important to initialize the hub only after the global logger is set
// to avoid race conditions while logging from inside the hub.
// Step 9: Hub depends on s.Channels() (step 8)
s.HubStart()
// -------------------------------------------------------------------------
// Everything below this is not order sensitive and safe to be moved around.
// If you are adding a new field that is non-channels specific, please add
@@ -503,10 +513,6 @@ func NewServer(options ...Option) (*Server, error) {
}
s.EmailService = emailService
if model.BuildEnterpriseReady == "true" {
s.LoadLicense()
}
s.setupFeatureFlags()
s.initJobs()
@@ -560,7 +566,6 @@ func NewServer(options ...Option) (*Server, error) {
mlog.Info("Printing current working", mlog.String("directory", pwd))
mlog.Info("Loaded config", mlog.String("source", s.configStore.String()))
license := s.License()
allowAdvancedLogging := license != nil && *license.Features.AdvancedLogging
if s.Audit == nil {
@@ -1172,18 +1177,13 @@ func (s *Server) Start() error {
mlog.Error("Mail server connection test is failed", mlog.Err(err))
}
backend, appErr := s.FileBackend()
if appErr != nil {
mlog.Error("Problem with file storage settings", mlog.Err(appErr))
} else {
err := backend.TestConnection()
err := s.FileBackend().TestConnection()
if err != nil {
if _, ok := err.(*filestore.S3FileBackendNoBucketError); ok {
err = s.FileBackend().(*filestore.S3FileBackend).MakeBucket()
}
if err != nil {
if _, ok := err.(*filestore.S3FileBackendNoBucketError); ok {
err = backend.(*filestore.S3FileBackend).MakeBucket()
}
if err != nil {
mlog.Error("Problem with file storage settings", mlog.Err(err))
}
mlog.Error("Problem with file storage settings", mlog.Err(err))
}
}
@@ -1227,9 +1227,9 @@ func (s *Server) Start() error {
if *s.Config().RateLimitSettings.Enable {
mlog.Info("RateLimiter is enabled")
rateLimiter, err := NewRateLimiter(&s.Config().RateLimitSettings, s.Config().ServiceSettings.TrustedProxyIPHeader)
if err != nil {
return err
rateLimiter, err2 := NewRateLimiter(&s.Config().RateLimitSettings, s.Config().ServiceSettings.TrustedProxyIPHeader)
if err2 != nil {
return err2
}
s.RateLimiter = rateLimiter
@@ -1850,13 +1850,8 @@ func (s *Server) stopSearchEngine() {
}
}
func (s *Server) FileBackend() (filestore.FileBackend, *model.AppError) {
license := s.License()
backend, err := filestore.NewFileBackend(s.Config().FileSettings.ToFileBackendSettings(license != nil && *license.Features.Compliance))
if err != nil {
return nil, model.NewAppError("FileBackend", "api.file.no_driver.app_error", nil, err.Error(), http.StatusInternalServerError)
}
return backend, nil
func (s *Server) FileBackend() filestore.FileBackend {
return s.filestore
}
func (s *Server) TotalWebsocketConnections() int {
@@ -1904,7 +1899,7 @@ func (s *Server) initJobs() {
}
if jobsLdapSyncInterface != nil {
builder := jobsLdapSyncInterface(s)
builder := jobsLdapSyncInterface(New(ServerConnector(s.Channels())))
s.Jobs.RegisterJobType(model.JobTypeLdapSync, builder.MakeWorker(), builder.MakeScheduler())
}
@@ -2189,7 +2184,7 @@ func (a *App) generateSupportPacketYaml() (*model.FileData, string) {
}
// Here we are getting information regarding LDAP
ldapInterface := a.ch.srv.Ldap
ldapInterface := a.ch.Ldap
var vendorName, vendorVersion string
if ldapInterface != nil {
vendorName, vendorVersion = ldapInterface.GetVendorNameAndVendorVersion()
@@ -2270,11 +2265,7 @@ func (s *Server) GetDefaultProfileImage(user *model.User) ([]byte, *model.AppErr
}
func (s *Server) ReadFile(path string) ([]byte, *model.AppError) {
backend, err := s.FileBackend()
if err != nil {
return nil, err
}
result, nErr := backend.ReadFile(path)
result, nErr := s.FileBackend().ReadFile(path)
if nErr != nil {
return nil, model.NewAppError("ReadFile", "api.file.read_file.app_error", nil, nErr.Error(), http.StatusInternalServerError)
}