From 25257d6ef660b3f29be4d7294582964472cab6ae Mon Sep 17 00:00:00 2001 From: Agniva De Sarker Date: Mon, 8 Nov 2021 11:57:22 +0530 Subject: [PATCH] Move plugins under Channels (#18938) * Move plugins under Channels We move all plugin related fields under *Channels. This essentially migrates several methods from being under *Server to under *Channels. We also move the plugin startup and shutdown code to be under Channels.Start and Channels.Shutdown. While here, we remove the getPluginPublicKeyFiles method which was a one-line method which uselessly returned an error. Lastly, we fix the product initialization order which was incorrect previously. Products are dependent on the main server. So startup should be products -> server. And shutdown should be server -> products. ```release-note NONE ``` * Added app layer ```release-note NONE ``` * Incorporate suggestions ```release-note NONE ``` --- app/app.go | 3 + app/app_iface.go | 3 +- app/channels.go | 30 +++- app/cluster_handlers.go | 6 +- app/opentracing/opentracing_layer.go | 39 ++--- app/plugin.go | 248 +++++++++++++-------------- app/plugin_event.go | 6 +- app/plugin_install.go | 116 ++++++------- app/plugin_requests.go | 6 +- app/plugin_shutdown_test.go | 2 +- app/plugin_signature.go | 20 +-- app/plugin_statuses.go | 36 ++-- app/plugin_test.go | 12 +- app/server.go | 51 ++---- 14 files changed, 288 insertions(+), 290 deletions(-) diff --git a/app/app.go b/app/app.go index a4aafa149a..ca943e5b49 100644 --- a/app/app.go +++ b/app/app.go @@ -133,6 +133,9 @@ func (a *App) ImageProxy() *imageproxy.ImageProxy { func (a *App) Timezones() *timezones.Timezones { return a.ch.srv.timezones } +func (a *App) License() *model.License { + return a.Srv().License() +} func (a *App) DBHealthCheckWrite() error { currentTime := strconv.FormatInt(time.Now().Unix(), 10) diff --git a/app/app_iface.go b/app/app_iface.go index 4f94baba5e..4e9a267482 100644 --- a/app/app_iface.go +++ b/app/app_iface.go @@ -177,8 +177,6 @@ type AppIface interface { // GetMarketplacePlugins returns a list of plugins from the marketplace-server, // and plugins that are installed locally. GetMarketplacePlugins(filter *model.MarketplacePluginFilter) ([]*model.MarketplacePlugin, *model.AppError) - // GetPluginPublicKeyFiles returns all public keys listed in the config. - GetPluginPublicKeyFiles() ([]string, *model.AppError) // GetPluginStatus returns the status for a plugin installed on this server. GetPluginStatus(id string) (*model.PluginStatus, *model.AppError) // GetPluginStatuses returns the status for plugins installed on this server. @@ -828,6 +826,7 @@ type AppIface interface { Ldap() einterfaces.LdapInterface LeaveChannel(c *request.Context, channelID string, userID string) *model.AppError LeaveTeam(c *request.Context, team *model.Team, user *model.User, requestorId string) *model.AppError + License() *model.License LimitedClientConfig() map[string]string ListAllCommands(teamID string, T i18n.TranslateFunc) ([]*model.Command, *model.AppError) ListDirectory(path string) ([]string, *model.AppError) diff --git a/app/channels.go b/app/channels.go index 0008e9f463..82c17e91d5 100644 --- a/app/channels.go +++ b/app/channels.go @@ -4,9 +4,12 @@ package app import ( + "sync" "sync/atomic" + "github.com/mattermost/mattermost-server/v6/app/request" "github.com/mattermost/mattermost-server/v6/model" + "github.com/mattermost/mattermost-server/v6/plugin" "github.com/mattermost/mattermost-server/v6/services/imageproxy" "github.com/pkg/errors" ) @@ -15,6 +18,10 @@ import ( type Channels struct { srv *Server + pluginsEnvironment *plugin.Environment + pluginConfigListenerID string + pluginsLock sync.RWMutex + imageProxy *imageproxy.ImageProxy asymmetricSigningKey atomic.Value @@ -44,12 +51,33 @@ func NewChannels(s *Server) (*Channels, error) { } func (ch *Channels) Start() error { + // Start plugins + ctx := request.EmptyContext() + ch.initPlugins(ctx, *ch.srv.Config().PluginSettings.Directory, *ch.srv.Config().PluginSettings.ClientDirectory) + + ch.AddConfigListener(func(prevCfg, cfg *model.Config) { + if *cfg.PluginSettings.Enable { + ch.initPlugins(ctx, *cfg.PluginSettings.Directory, *ch.srv.Config().PluginSettings.ClientDirectory) + } else { + ch.ShutDownPlugins() + } + }) + if err := ch.ensureAsymmetricSigningKey(); err != nil { return errors.Wrapf(err, "unable to ensure asymmetric signing key") } return nil } -func (*Channels) Stop() error { +func (ch *Channels) Stop() error { + ch.ShutDownPlugins() return nil } + +func (ch *Channels) AddConfigListener(listener func(*model.Config, *model.Config)) string { + return ch.srv.AddConfigListener(listener) +} + +func (ch *Channels) RemoveConfigListener(id string) { + ch.srv.RemoveConfigListener(id) +} diff --git a/app/cluster_handlers.go b/app/cluster_handlers.go index f1f82eae33..242875e9c5 100644 --- a/app/cluster_handlers.go +++ b/app/cluster_handlers.go @@ -17,7 +17,7 @@ func (s *Server) clusterInstallPluginHandler(msg *model.ClusterMessage) { if jsonErr := json.Unmarshal(msg.Data, &data); jsonErr != nil { mlog.Warn("Failed to decode from JSON", mlog.Err(jsonErr)) } - s.installPluginFromData(data) + s.Channels().installPluginFromData(data) } func (s *Server) clusterRemovePluginHandler(msg *model.ClusterMessage) { @@ -25,11 +25,11 @@ func (s *Server) clusterRemovePluginHandler(msg *model.ClusterMessage) { if jsonErr := json.Unmarshal(msg.Data, &data); jsonErr != nil { mlog.Warn("Failed to decode from JSON", mlog.Err(jsonErr)) } - s.removePluginFromData(data) + s.Channels().removePluginFromData(data) } func (s *Server) clusterPluginEventHandler(msg *model.ClusterMessage) { - env := s.GetPluginsEnvironment() + env := s.Channels().GetPluginsEnvironment() if env == nil { return } diff --git a/app/opentracing/opentracing_layer.go b/app/opentracing/opentracing_layer.go index e1da0953c8..d627da6ec5 100644 --- a/app/opentracing/opentracing_layer.go +++ b/app/opentracing/opentracing_layer.go @@ -7228,28 +7228,6 @@ func (a *OpenTracingAppLayer) GetPluginKey(pluginID string, key string) ([]byte, return resultVar0, resultVar1 } -func (a *OpenTracingAppLayer) GetPluginPublicKeyFiles() ([]string, *model.AppError) { - origCtx := a.ctx - span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetPluginPublicKeyFiles") - - a.ctx = newCtx - a.app.Srv().Store.SetContext(newCtx) - defer func() { - a.app.Srv().Store.SetContext(origCtx) - a.ctx = origCtx - }() - - defer span.Finish() - resultVar0, resultVar1 := a.app.GetPluginPublicKeyFiles() - - if resultVar1 != nil { - span.LogFields(spanlog.Error(resultVar1)) - ext.Error.Set(span, true) - } - - return resultVar0, resultVar1 -} - func (a *OpenTracingAppLayer) GetPluginStatus(id string) (*model.PluginStatus, *model.AppError) { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetPluginStatus") @@ -11034,6 +11012,23 @@ func (a *OpenTracingAppLayer) LeaveTeam(c *request.Context, team *model.Team, us return resultVar0 } +func (a *OpenTracingAppLayer) License() *model.License { + origCtx := a.ctx + span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.License") + + a.ctx = newCtx + a.app.Srv().Store.SetContext(newCtx) + defer func() { + a.app.Srv().Store.SetContext(origCtx) + a.ctx = origCtx + }() + + defer span.Finish() + resultVar0 := a.app.License() + + return resultVar0 +} + func (a *OpenTracingAppLayer) LimitedClientConfig() map[string]string { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.LimitedClientConfig") diff --git a/app/plugin.go b/app/plugin.go index 789ebe2364..6dcfabb3c0 100644 --- a/app/plugin.go +++ b/app/plugin.go @@ -42,15 +42,15 @@ type pluginSignaturePath struct { // // To get the plugins environment when the plugins are disabled, manually acquire the plugins // lock instead. -func (s *Server) GetPluginsEnvironment() *plugin.Environment { - if !*s.Config().PluginSettings.Enable { +func (ch *Channels) GetPluginsEnvironment() *plugin.Environment { + if !*ch.srv.Config().PluginSettings.Enable { return nil } - s.PluginsLock.RLock() - defer s.PluginsLock.RUnlock() + ch.pluginsLock.RLock() + defer ch.pluginsLock.RUnlock() - return s.PluginsEnvironment + return ch.pluginsEnvironment } // GetPluginsEnvironment returns the plugin environment for use if plugins are enabled and @@ -59,36 +59,36 @@ func (s *Server) GetPluginsEnvironment() *plugin.Environment { // To get the plugins environment when the plugins are disabled, manually acquire the plugins // lock instead. func (a *App) GetPluginsEnvironment() *plugin.Environment { - return a.Srv().GetPluginsEnvironment() + return a.ch.GetPluginsEnvironment() } func (a *App) SetPluginsEnvironment(pluginsEnvironment *plugin.Environment) { - a.Srv().PluginsLock.Lock() - defer a.Srv().PluginsLock.Unlock() + a.ch.pluginsLock.Lock() + defer a.ch.pluginsLock.Unlock() - a.Srv().PluginsEnvironment = pluginsEnvironment + a.ch.pluginsEnvironment = pluginsEnvironment } func (a *App) SyncPluginsActiveState() { - a.Srv().syncPluginsActiveState() + a.ch.syncPluginsActiveState() } -func (s *Server) syncPluginsActiveState() { +func (ch *Channels) syncPluginsActiveState() { // Acquiring lock manually, as plugins might be disabled. See GetPluginsEnvironment. - s.PluginsLock.RLock() - pluginsEnvironment := s.PluginsEnvironment - s.PluginsLock.RUnlock() + ch.pluginsLock.RLock() + pluginsEnvironment := ch.pluginsEnvironment + ch.pluginsLock.RUnlock() if pluginsEnvironment == nil { return } - config := s.Config().PluginSettings + config := ch.srv.Config().PluginSettings if *config.Enable { availablePlugins, err := pluginsEnvironment.Available() if err != nil { - s.Log.Error("Unable to get available plugins", mlog.Err(err)) + ch.srv.Log.Error("Unable to get available plugins", mlog.Err(err)) return } @@ -104,7 +104,7 @@ func (s *Server) syncPluginsActiveState() { // Tie Apps proxy disabled status to the feature flag. if pluginID == "com.mattermost.apps" { - if !s.Config().FeatureFlags.AppsEnabled { + if !ch.srv.Config().FeatureFlags.AppsEnabled { pluginEnabled = false } } @@ -129,7 +129,7 @@ func (s *Server) syncPluginsActiveState() { if deactivated && plugin.Manifest.HasClient() { message := model.NewWebSocketEvent(model.WebsocketEventPluginDisabled, "", "", "", nil) message.Add("manifest", plugin.Manifest.ClientManifest()) - s.Publish(message) + ch.srv.Publish(message) } }(plugin) } @@ -143,14 +143,14 @@ func (s *Server) syncPluginsActiveState() { pluginID := plugin.Manifest.Id updatedManifest, activated, err := pluginsEnvironment.Activate(pluginID) if err != nil { - plugin.WrapLogger(s.Log).Error("Unable to activate plugin", mlog.Err(err)) + plugin.WrapLogger(ch.srv.Log).Error("Unable to activate plugin", mlog.Err(err)) return } if activated { // Notify all cluster clients if ready - if err := s.notifyPluginEnabled(updatedManifest); err != nil { - s.Log.Error("Failed to notify cluster on plugin enable", mlog.Err(err)) + if err := ch.notifyPluginEnabled(updatedManifest); err != nil { + ch.srv.Log.Error("Failed to notify cluster on plugin enable", mlog.Err(err)) } } }(plugin) @@ -160,7 +160,7 @@ func (s *Server) syncPluginsActiveState() { pluginsEnvironment.Shutdown() } - if err := s.notifyPluginStatusesChanged(); err != nil { + if err := ch.notifyPluginStatusesChanged(); err != nil { mlog.Warn("failed to notify plugin status changed", mlog.Err(err)) } } @@ -170,21 +170,21 @@ func (a *App) NewPluginAPI(c *request.Context, manifest *model.Manifest) plugin. } func (a *App) InitPlugins(c *request.Context, pluginDir, webappPluginDir string) { - a.Srv().initPlugins(c, pluginDir, webappPluginDir) + a.ch.initPlugins(c, pluginDir, webappPluginDir) } -func (s *Server) initPlugins(c *request.Context, pluginDir, webappPluginDir string) { +func (ch *Channels) initPlugins(c *request.Context, pluginDir, webappPluginDir string) { // Acquiring lock manually, as plugins might be disabled. See GetPluginsEnvironment. - s.PluginsLock.RLock() - pluginsEnvironment := s.PluginsEnvironment - s.PluginsLock.RUnlock() - if pluginsEnvironment != nil || !*s.Config().PluginSettings.Enable { - s.syncPluginsActiveState() - pluginsEnvironment.TogglePluginHealthCheckJob(*s.Config().PluginSettings.EnableHealthCheck) + ch.pluginsLock.RLock() + pluginsEnvironment := ch.pluginsEnvironment + ch.pluginsLock.RUnlock() + if pluginsEnvironment != nil || !*ch.srv.Config().PluginSettings.Enable { + ch.syncPluginsActiveState() + pluginsEnvironment.TogglePluginHealthCheckJob(*ch.srv.Config().PluginSettings.EnableHealthCheck) return } - s.Log.Info("Starting up plugins") + ch.srv.Log.Info("Starting up plugins") if err := os.Mkdir(pluginDir, 0744); err != nil && !os.IsExist(err) { mlog.Error("Failed to start up plugins", mlog.Err(err)) @@ -197,70 +197,70 @@ func (s *Server) initPlugins(c *request.Context, pluginDir, webappPluginDir stri } newAPIFunc := func(manifest *model.Manifest) plugin.API { - return New(ServerConnector(s.Channels())).NewPluginAPI(c, manifest) + return New(ServerConnector(ch)).NewPluginAPI(c, manifest) } - env, err := plugin.NewEnvironment(newAPIFunc, NewDriverImpl(s), pluginDir, webappPluginDir, s.Log, s.Metrics) + env, err := plugin.NewEnvironment(newAPIFunc, NewDriverImpl(ch.srv), pluginDir, webappPluginDir, ch.srv.Log, ch.srv.Metrics) if err != nil { mlog.Error("Failed to start up plugins", mlog.Err(err)) return } - s.PluginsLock.Lock() - s.PluginsEnvironment = env - s.PluginsLock.Unlock() + ch.pluginsLock.Lock() + ch.pluginsEnvironment = env + ch.pluginsLock.Unlock() - s.PluginsEnvironment.TogglePluginHealthCheckJob(*s.Config().PluginSettings.EnableHealthCheck) + ch.pluginsEnvironment.TogglePluginHealthCheckJob(*ch.srv.Config().PluginSettings.EnableHealthCheck) - if err := s.syncPlugins(); err != nil { + if err := ch.syncPlugins(); err != nil { mlog.Error("Failed to sync plugins from the file store", mlog.Err(err)) } - plugins := s.processPrepackagedPlugins(prepackagedPluginsDir) - pluginsEnvironment = s.GetPluginsEnvironment() + plugins := ch.processPrepackagedPlugins(prepackagedPluginsDir) + pluginsEnvironment = ch.GetPluginsEnvironment() if pluginsEnvironment == nil { mlog.Info("Plugins environment not found, server is likely shutting down") return } pluginsEnvironment.SetPrepackagedPlugins(plugins) - s.installFeatureFlagPlugins() + ch.installFeatureFlagPlugins() // Sync plugin active state when config changes. Also notify plugins. - s.PluginsLock.Lock() - s.RemoveConfigListener(s.PluginConfigListenerId) - s.PluginConfigListenerId = s.AddConfigListener(func(old, new *model.Config) { + ch.pluginsLock.Lock() + ch.RemoveConfigListener(ch.pluginConfigListenerID) + ch.pluginConfigListenerID = ch.AddConfigListener(func(old, new *model.Config) { // If plugin status remains unchanged, only then run this. // Because (*App).InitPlugins is already run as a config change hook. if *old.PluginSettings.Enable == *new.PluginSettings.Enable { - s.installFeatureFlagPlugins() - s.syncPluginsActiveState() + ch.installFeatureFlagPlugins() + ch.syncPluginsActiveState() } - if pluginsEnvironment := s.GetPluginsEnvironment(); pluginsEnvironment != nil { + if pluginsEnvironment := ch.GetPluginsEnvironment(); pluginsEnvironment != nil { pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool { if err := hooks.OnConfigurationChange(); err != nil { - s.Log.Error("Plugin OnConfigurationChange hook failed", mlog.Err(err)) + ch.srv.Log.Error("Plugin OnConfigurationChange hook failed", mlog.Err(err)) } return true }, plugin.OnConfigurationChangeID) } }) - s.PluginsLock.Unlock() + ch.pluginsLock.Unlock() - s.syncPluginsActiveState() + ch.syncPluginsActiveState() } // SyncPlugins synchronizes the plugins installed locally // with the plugin bundles available in the file store. func (a *App) SyncPlugins() *model.AppError { - return a.Srv().syncPlugins() + return a.ch.syncPlugins() } // SyncPlugins synchronizes the plugins installed locally // with the plugin bundles available in the file store. -func (s *Server) syncPlugins() *model.AppError { +func (ch *Channels) syncPlugins() *model.AppError { mlog.Info("Syncing plugins from the file store") - pluginsEnvironment := s.GetPluginsEnvironment() + pluginsEnvironment := ch.GetPluginsEnvironment() if pluginsEnvironment == nil { return model.NewAppError("SyncPlugins", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented) } @@ -276,14 +276,14 @@ func (s *Server) syncPlugins() *model.AppError { go func(pluginID string) { defer wg.Done() // Only handle managed plugins with .filestore flag file. - _, err := os.Stat(filepath.Join(*s.Config().PluginSettings.Directory, pluginID, managedPluginFileName)) + _, err := os.Stat(filepath.Join(*ch.srv.Config().PluginSettings.Directory, pluginID, managedPluginFileName)) if os.IsNotExist(err) { mlog.Warn("Skipping sync for unmanaged plugin", mlog.String("plugin_id", pluginID)) } else if err != nil { mlog.Error("Skipping sync for plugin after failure to check if managed", mlog.String("plugin_id", pluginID), mlog.Err(err)) } else { mlog.Debug("Removing local installation of managed plugin before sync", mlog.String("plugin_id", pluginID)) - if err := s.removePluginLocally(pluginID); err != nil { + if err := ch.removePluginLocally(pluginID); err != nil { mlog.Error("Failed to remove local installation of managed plugin before sync", mlog.String("plugin_id", pluginID), mlog.Err(err)) } } @@ -292,7 +292,7 @@ func (s *Server) syncPlugins() *model.AppError { wg.Wait() // Install plugins from the file store. - pluginSignaturePathMap, appErr := s.getPluginsFromFolder() + pluginSignaturePathMap, appErr := ch.getPluginsFromFolder() if appErr != nil { return appErr } @@ -301,7 +301,7 @@ func (s *Server) syncPlugins() *model.AppError { wg.Add(1) go func(plugin *pluginSignaturePath) { defer wg.Done() - reader, appErr := s.fileReader(plugin.path) + reader, appErr := ch.srv.fileReader(plugin.path) if appErr != nil { mlog.Error("Failed to open plugin bundle from file store.", mlog.String("bundle", plugin.path), mlog.Err(appErr)) return @@ -309,8 +309,8 @@ func (s *Server) syncPlugins() *model.AppError { defer reader.Close() var signature filestore.ReadCloseSeeker - if *s.Config().PluginSettings.RequirePluginSignature { - signature, appErr = s.fileReader(plugin.signaturePath) + if *ch.srv.Config().PluginSettings.RequirePluginSignature { + signature, appErr = ch.srv.fileReader(plugin.signaturePath) if appErr != nil { mlog.Error("Failed to open plugin signature from file store.", mlog.Err(appErr)) return @@ -319,7 +319,7 @@ func (s *Server) syncPlugins() *model.AppError { } mlog.Info("Syncing plugin from file store", mlog.String("bundle", plugin.path)) - if _, err := s.installPluginLocally(reader, signature, installPluginLocallyAlways); err != nil { + if _, err := ch.installPluginLocally(reader, signature, installPluginLocallyAlways); err != nil { mlog.Error("Failed to sync plugin from file store", mlog.String("bundle", plugin.path), mlog.Err(err)) } }(plugin) @@ -329,11 +329,11 @@ func (s *Server) syncPlugins() *model.AppError { return nil } -func (s *Server) ShutDownPlugins() { +func (ch *Channels) ShutDownPlugins() { // Acquiring lock manually, as plugins might be disabled. See GetPluginsEnvironment. - s.PluginsLock.RLock() - pluginsEnvironment := s.PluginsEnvironment - s.PluginsLock.RUnlock() + ch.pluginsLock.RLock() + pluginsEnvironment := ch.pluginsEnvironment + ch.pluginsLock.RUnlock() if pluginsEnvironment == nil { return } @@ -342,14 +342,14 @@ func (s *Server) ShutDownPlugins() { pluginsEnvironment.Shutdown() - s.RemoveConfigListener(s.PluginConfigListenerId) - s.PluginConfigListenerId = "" + ch.RemoveConfigListener(ch.pluginConfigListenerID) + ch.pluginConfigListenerID = "" // Acquiring lock manually before cleaning up PluginsEnvironment. - s.PluginsLock.Lock() - defer s.PluginsLock.Unlock() - if s.PluginsEnvironment == pluginsEnvironment { - s.PluginsEnvironment = nil + ch.pluginsLock.Lock() + defer ch.pluginsLock.Unlock() + if ch.pluginsEnvironment == pluginsEnvironment { + ch.pluginsEnvironment = nil } else { mlog.Warn("Another PluginsEnvironment detected while shutting down plugins.") } @@ -375,11 +375,11 @@ func (a *App) GetActivePluginManifests() ([]*model.Manifest, *model.AppError) { // activation if inactive anywhere in the cluster. // Notifies cluster peers through config change. func (a *App) EnablePlugin(id string) *model.AppError { - return a.Srv().enablePlugin(id) + return a.ch.enablePlugin(id) } -func (s *Server) enablePlugin(id string) *model.AppError { - pluginsEnvironment := s.GetPluginsEnvironment() +func (ch *Channels) enablePlugin(id string) *model.AppError { + pluginsEnvironment := ch.GetPluginsEnvironment() if pluginsEnvironment == nil { return model.NewAppError("EnablePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented) } @@ -403,12 +403,12 @@ func (s *Server) enablePlugin(id string) *model.AppError { return model.NewAppError("EnablePlugin", "app.plugin.not_installed.app_error", nil, "", http.StatusNotFound) } - s.UpdateConfig(func(cfg *model.Config) { + ch.srv.UpdateConfig(func(cfg *model.Config) { cfg.PluginSettings.PluginStates[id] = &model.PluginState{Enable: true} }) // This call will implicitly invoke SyncPluginsActiveState which will activate enabled plugins. - if _, _, err := s.SaveConfig(s.Config(), true); err != nil { + if _, _, err := ch.srv.SaveConfig(ch.srv.Config(), true); err != nil { if err.Id == "ent.cluster.save_config.error" { return model.NewAppError("EnablePlugin", "app.plugin.cluster.save_config.app_error", nil, "", http.StatusInternalServerError) } @@ -421,11 +421,11 @@ func (s *Server) enablePlugin(id string) *model.AppError { // DisablePlugin will set the config for an installed plugin to disabled, triggering deactivation if active. // Notifies cluster peers through config change. func (a *App) DisablePlugin(id string) *model.AppError { - return a.Srv().disablePlugin(id) + return a.ch.disablePlugin(id) } -func (s *Server) disablePlugin(id string) *model.AppError { - pluginsEnvironment := s.GetPluginsEnvironment() +func (ch *Channels) disablePlugin(id string) *model.AppError { + pluginsEnvironment := ch.GetPluginsEnvironment() if pluginsEnvironment == nil { return model.NewAppError("DisablePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented) } @@ -449,13 +449,13 @@ func (s *Server) disablePlugin(id string) *model.AppError { return model.NewAppError("DisablePlugin", "app.plugin.not_installed.app_error", nil, "", http.StatusNotFound) } - s.UpdateConfig(func(cfg *model.Config) { + ch.srv.UpdateConfig(func(cfg *model.Config) { cfg.PluginSettings.PluginStates[id] = &model.PluginState{Enable: false} }) - s.unregisterPluginCommands(id) + ch.srv.unregisterPluginCommands(id) // This call will implicitly invoke SyncPluginsActiveState which will deactivate disabled plugins. - if _, _, err := s.SaveConfig(s.Config(), true); err != nil { + if _, _, err := ch.srv.SaveConfig(ch.srv.Config(), true); err != nil { return model.NewAppError("DisablePlugin", "app.plugin.config.app_error", nil, err.Error(), http.StatusInternalServerError) } @@ -540,8 +540,8 @@ func (a *App) GetMarketplacePlugins(filter *model.MarketplacePluginFilter) ([]*m } // getPrepackagedPlugin returns a pre-packaged plugin. -func (s *Server) getPrepackagedPlugin(pluginID, version string) (*plugin.PrepackagedPlugin, *model.AppError) { - pluginsEnvironment := s.GetPluginsEnvironment() +func (ch *Channels) getPrepackagedPlugin(pluginID, version string) (*plugin.PrepackagedPlugin, *model.AppError) { + pluginsEnvironment := ch.GetPluginsEnvironment() if pluginsEnvironment == nil { return nil, model.NewAppError("getPrepackagedPlugin", "app.plugin.config.app_error", nil, "plugin environment is nil", http.StatusInternalServerError) } @@ -557,16 +557,16 @@ func (s *Server) getPrepackagedPlugin(pluginID, version string) (*plugin.Prepack } // getRemoteMarketplacePlugin returns plugin from marketplace-server. -func (s *Server) getRemoteMarketplacePlugin(pluginID, version string) (*model.BaseMarketplacePlugin, *model.AppError) { +func (ch *Channels) getRemoteMarketplacePlugin(pluginID, version string) (*model.BaseMarketplacePlugin, *model.AppError) { marketplaceClient, err := marketplace.NewClient( - *s.Config().PluginSettings.MarketplaceURL, - s.HTTPService(), + *ch.srv.Config().PluginSettings.MarketplaceURL, + ch.srv.HTTPService(), ) if err != nil { return nil, model.NewAppError("GetMarketplacePlugin", "app.plugin.marketplace_client.app_error", nil, err.Error(), http.StatusInternalServerError) } - filter := s.getBaseMarketplaceFilter() + filter := ch.getBaseMarketplaceFilter() filter.PluginId = pluginID filter.ReturnAllVersions = true @@ -717,15 +717,15 @@ func (a *App) mergeLocalPlugins(remoteMarketplacePlugins map[string]*model.Marke } func (a *App) getBaseMarketplaceFilter() *model.MarketplacePluginFilter { - return a.Srv().getBaseMarketplaceFilter() + return a.ch.getBaseMarketplaceFilter() } -func (s *Server) getBaseMarketplaceFilter() *model.MarketplacePluginFilter { +func (ch *Channels) getBaseMarketplaceFilter() *model.MarketplacePluginFilter { filter := &model.MarketplacePluginFilter{ ServerVersion: model.CurrentVersion, } - license := s.License() + license := ch.srv.License() if license != nil && license.HasEnterpriseMarketplacePlugins() { filter.EnterprisePlugins = true } @@ -772,8 +772,8 @@ func pluginMatchesFilter(manifest *model.Manifest, filter string) bool { // it will notify all connected websocket clients (across all peers) to trigger the (re-)installation. // There is a small chance that this never occurs, because the last server to finish installing dies before it can announce. // There is also a chance that multiple servers notify, but the webapp handles this idempotently. -func (s *Server) notifyPluginEnabled(manifest *model.Manifest) error { - pluginsEnvironment := s.GetPluginsEnvironment() +func (ch *Channels) notifyPluginEnabled(manifest *model.Manifest) error { + pluginsEnvironment := ch.GetPluginsEnvironment() if pluginsEnvironment == nil { return errors.New("pluginsEnvironment is nil") } @@ -783,15 +783,15 @@ func (s *Server) notifyPluginEnabled(manifest *model.Manifest) error { var statuses model.PluginStatuses - if s.Cluster != nil { + if ch.srv.Cluster != nil { var err *model.AppError - statuses, err = s.Cluster.GetPluginStatuses() + statuses, err = ch.srv.Cluster.GetPluginStatuses() if err != nil { return err } } - localStatus, err := s.GetPluginStatus(manifest.Id) + localStatus, err := ch.GetPluginStatus(manifest.Id) if err != nil { return err } @@ -811,26 +811,26 @@ func (s *Server) notifyPluginEnabled(manifest *model.Manifest) error { // Notify all cluster peer clients. message := model.NewWebSocketEvent(model.WebsocketEventPluginEnabled, "", "", "", nil) message.Add("manifest", manifest.ClientManifest()) - s.Publish(message) + ch.srv.Publish(message) return nil } -func (s *Server) getPluginsFromFolder() (map[string]*pluginSignaturePath, *model.AppError) { - fileStorePaths, appErr := s.listDirectory(fileStorePluginFolder) +func (ch *Channels) getPluginsFromFolder() (map[string]*pluginSignaturePath, *model.AppError) { + fileStorePaths, appErr := ch.srv.listDirectory(fileStorePluginFolder) if appErr != nil { return nil, model.NewAppError("getPluginsFromDir", "app.plugin.sync.list_filestore.app_error", nil, appErr.Error(), http.StatusInternalServerError) } - return s.getPluginsFromFilePaths(fileStorePaths), nil + return ch.getPluginsFromFilePaths(fileStorePaths), nil } -func (s *Server) getPluginsFromFilePaths(fileStorePaths []string) map[string]*pluginSignaturePath { +func (ch *Channels) getPluginsFromFilePaths(fileStorePaths []string) map[string]*pluginSignaturePath { pluginSignaturePathMap := make(map[string]*pluginSignaturePath) fsPrefix := "" - if *s.Config().FileSettings.DriverName == model.ImageDriverS3 { - ptr := s.Config().FileSettings.AmazonS3PathPrefix + if *ch.srv.Config().FileSettings.DriverName == model.ImageDriverS3 { + ptr := ch.srv.Config().FileSettings.AmazonS3PathPrefix if ptr != nil && *ptr != "" { fsPrefix = *ptr + "/" } @@ -863,7 +863,7 @@ func (s *Server) getPluginsFromFilePaths(fileStorePaths []string) map[string]*pl return pluginSignaturePathMap } -func (s *Server) processPrepackagedPlugins(pluginsDir string) []*plugin.PrepackagedPlugin { +func (ch *Channels) processPrepackagedPlugins(pluginsDir string) []*plugin.PrepackagedPlugin { prepackagedPluginsDir, found := fileutils.FindDir(pluginsDir) if !found { return nil @@ -879,7 +879,7 @@ func (s *Server) processPrepackagedPlugins(pluginsDir string) []*plugin.Prepacka return nil } - pluginSignaturePathMap := s.getPluginsFromFilePaths(fileStorePaths) + pluginSignaturePathMap := ch.getPluginsFromFilePaths(fileStorePaths) plugins := make([]*plugin.PrepackagedPlugin, 0, len(pluginSignaturePathMap)) prepackagedPlugins := make(chan *plugin.PrepackagedPlugin, len(pluginSignaturePathMap)) @@ -888,7 +888,7 @@ func (s *Server) processPrepackagedPlugins(pluginsDir string) []*plugin.Prepacka wg.Add(1) go func(psPath *pluginSignaturePath) { defer wg.Done() - p, err := s.processPrepackagedPlugin(psPath) + p, err := ch.processPrepackagedPlugin(psPath) if err != nil { mlog.Error("Failed to install prepackaged plugin", mlog.String("path", psPath.path), mlog.Err(err)) return @@ -909,7 +909,7 @@ func (s *Server) processPrepackagedPlugins(pluginsDir string) []*plugin.Prepacka // processPrepackagedPlugin will return the prepackaged plugin metadata and will also // install the prepackaged plugin if it had been previously enabled and AutomaticPrepackagedPlugins is true. -func (s *Server) processPrepackagedPlugin(pluginPath *pluginSignaturePath) (*plugin.PrepackagedPlugin, error) { +func (ch *Channels) processPrepackagedPlugin(pluginPath *pluginSignaturePath) (*plugin.PrepackagedPlugin, error) { mlog.Debug("Processing prepackaged plugin", mlog.String("path", pluginPath.path)) fileReader, err := os.Open(pluginPath.path) @@ -930,18 +930,18 @@ func (s *Server) processPrepackagedPlugin(pluginPath *pluginSignaturePath) (*plu } // Skip installing the plugin at all if automatic prepackaged plugins is disabled - if !*s.Config().PluginSettings.AutomaticPrepackagedPlugins { + if !*ch.srv.Config().PluginSettings.AutomaticPrepackagedPlugins { return plugin, nil } // Skip installing if the plugin is has not been previously enabled. - pluginState := s.Config().PluginSettings.PluginStates[plugin.Manifest.Id] + pluginState := ch.srv.Config().PluginSettings.PluginStates[plugin.Manifest.Id] if pluginState == nil || !pluginState.Enable { return plugin, nil } mlog.Debug("Installing prepackaged plugin", mlog.String("path", pluginPath.path)) - if _, err := s.installExtractedPlugin(plugin.Manifest, pluginDir, installPluginLocallyOnlyIfNewOrUpgrade); err != nil { + if _, err := ch.installExtractedPlugin(plugin.Manifest, pluginDir, installPluginLocallyOnlyIfNewOrUpgrade); err != nil { return nil, errors.Wrapf(err, "Failed to install extracted prepackaged plugin %s", pluginPath.path) } @@ -949,24 +949,24 @@ func (s *Server) processPrepackagedPlugin(pluginPath *pluginSignaturePath) (*plu } // installFeatureFlagPlugins handles the automatic installation/upgrade of plugins from feature flags -func (s *Server) installFeatureFlagPlugins() { - ffControledPlugins := s.Config().FeatureFlags.Plugins() +func (ch *Channels) installFeatureFlagPlugins() { + ffControledPlugins := ch.srv.Config().FeatureFlags.Plugins() // Respect the automatic prepackaged disable setting - if !*s.Config().PluginSettings.AutomaticPrepackagedPlugins { + if !*ch.srv.Config().PluginSettings.AutomaticPrepackagedPlugins { return } for pluginID, version := range ffControledPlugins { // Skip installing if the plugin has been previously disabled. - pluginState := s.Config().PluginSettings.PluginStates[pluginID] + pluginState := ch.srv.Config().PluginSettings.PluginStates[pluginID] if pluginState != nil && !pluginState.Enable { - s.Log.Debug("Not auto installing/upgrade because plugin was disabled", mlog.String("plugin_id", pluginID), mlog.String("version", version)) + ch.srv.Log.Debug("Not auto installing/upgrade because plugin was disabled", mlog.String("plugin_id", pluginID), mlog.String("version", version)) continue } // Check if we already installed this version as InstallMarketplacePlugin can't handle re-installs well. - pluginStatus, err := s.GetPluginStatus(pluginID) + pluginStatus, err := ch.GetPluginStatus(pluginID) pluginExists := err == nil if pluginExists && pluginStatus.Version == version { continue @@ -974,37 +974,37 @@ func (s *Server) installFeatureFlagPlugins() { if version != "" && version != "control" { // If we are on-prem skip installation if this is a downgrade - license := s.License() + license := ch.srv.License() inCloud := license != nil && *license.Features.Cloud if !inCloud && pluginExists { parsedVersion, err := semver.Parse(version) if err != nil { - s.Log.Debug("Bad version from feature flag", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", version)) + ch.srv.Log.Debug("Bad version from feature flag", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", version)) return } parsedExistingVersion, err := semver.Parse(pluginStatus.Version) if err != nil { - s.Log.Debug("Bad version from plugin manifest", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", pluginStatus.Version)) + ch.srv.Log.Debug("Bad version from plugin manifest", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", pluginStatus.Version)) return } if parsedVersion.LTE(parsedExistingVersion) { - s.Log.Debug("Skip installation because given version was a downgrade and on-prem installations should not downgrade.", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", pluginStatus.Version)) + ch.srv.Log.Debug("Skip installation because given version was a downgrade and on-prem installations should not downgrade.", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", pluginStatus.Version)) return } } - _, err := s.installMarketplacePlugin(&model.InstallMarketplacePluginRequest{ + _, err := ch.installMarketplacePlugin(&model.InstallMarketplacePluginRequest{ Id: pluginID, Version: version, }) if err != nil { - s.Log.Debug("Unable to install plugin from FF manifest", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", version)) + ch.srv.Log.Debug("Unable to install plugin from FF manifest", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", version)) } else { - if err := s.enablePlugin(pluginID); err != nil { - s.Log.Debug("Unable to enable plugin installed from feature flag.", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", version)) + if err := ch.enablePlugin(pluginID); err != nil { + ch.srv.Log.Debug("Unable to enable plugin installed from feature flag.", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", version)) } else { - s.Log.Debug("Installed and enabled plugin.", mlog.String("plugin_id", pluginID), mlog.String("version", version)) + ch.srv.Log.Debug("Installed and enabled plugin.", mlog.String("plugin_id", pluginID), mlog.String("version", version)) } } } diff --git a/app/plugin_event.go b/app/plugin_event.go index 251edfa63d..b4b68be3b5 100644 --- a/app/plugin_event.go +++ b/app/plugin_event.go @@ -9,10 +9,10 @@ import ( "github.com/mattermost/mattermost-server/v6/model" ) -func (s *Server) notifyClusterPluginEvent(event model.ClusterEvent, data model.PluginEventData) { +func (ch *Channels) notifyClusterPluginEvent(event model.ClusterEvent, data model.PluginEventData) { buf, _ := json.Marshal(data) - if s.Cluster != nil { - s.Cluster.SendClusterMessage(&model.ClusterMessage{ + if ch.srv.Cluster != nil { + ch.srv.Cluster.SendClusterMessage(&model.ClusterMessage{ Event: event, SendType: model.ClusterSendReliable, WaitForAllToSend: true, diff --git a/app/plugin_install.go b/app/plugin_install.go index edbd718f89..185a6fcedd 100644 --- a/app/plugin_install.go +++ b/app/plugin_install.go @@ -62,13 +62,13 @@ const managedPluginFileName = ".filestore" const fileStorePluginFolder = "plugins" func (a *App) InstallPluginFromData(data model.PluginEventData) { - a.Srv().installPluginFromData(data) + a.ch.installPluginFromData(data) } -func (s *Server) installPluginFromData(data model.PluginEventData) { +func (ch *Channels) installPluginFromData(data model.PluginEventData) { mlog.Debug("Installing plugin as per cluster message", mlog.String("plugin_id", data.Id)) - pluginSignaturePathMap, appErr := s.getPluginsFromFolder() + pluginSignaturePathMap, appErr := ch.getPluginsFromFolder() if appErr != nil { mlog.Error("Failed to get plugin signatures from filestore. Can't install plugin from data.", mlog.Err(appErr)) return @@ -79,7 +79,7 @@ func (s *Server) installPluginFromData(data model.PluginEventData) { return } - reader, appErr := s.fileReader(plugin.path) + reader, appErr := ch.srv.fileReader(plugin.path) if appErr != nil { mlog.Error("Failed to open plugin bundle from file store.", mlog.String("bundle", plugin.path), mlog.Err(appErr)) return @@ -87,8 +87,8 @@ func (s *Server) installPluginFromData(data model.PluginEventData) { defer reader.Close() var signature filestore.ReadCloseSeeker - if *s.Config().PluginSettings.RequirePluginSignature { - signature, appErr = s.fileReader(plugin.signaturePath) + if *ch.srv.Config().PluginSettings.RequirePluginSignature { + signature, appErr = ch.srv.fileReader(plugin.signaturePath) if appErr != nil { mlog.Error("Failed to open plugin signature from file store.", mlog.Err(appErr)) return @@ -96,44 +96,44 @@ func (s *Server) installPluginFromData(data model.PluginEventData) { defer signature.Close() } - manifest, appErr := s.installPluginLocally(reader, signature, installPluginLocallyAlways) + manifest, appErr := ch.installPluginLocally(reader, signature, installPluginLocallyAlways) if appErr != nil { mlog.Error("Failed to sync plugin from file store", mlog.String("bundle", plugin.path), mlog.Err(appErr)) return } - if err := s.notifyPluginEnabled(manifest); err != nil { + if err := ch.notifyPluginEnabled(manifest); err != nil { mlog.Error("Failed notify plugin enabled", mlog.Err(err)) } - if err := s.notifyPluginStatusesChanged(); err != nil { + if err := ch.notifyPluginStatusesChanged(); err != nil { mlog.Error("Failed to notify plugin status changed", mlog.Err(err)) } } func (a *App) RemovePluginFromData(data model.PluginEventData) { - a.Srv().removePluginFromData(data) + a.ch.removePluginFromData(data) } -func (s *Server) removePluginFromData(data model.PluginEventData) { +func (ch *Channels) removePluginFromData(data model.PluginEventData) { mlog.Debug("Removing plugin as per cluster message", mlog.String("plugin_id", data.Id)) - if err := s.removePluginLocally(data.Id); err != nil { + if err := ch.removePluginLocally(data.Id); err != nil { mlog.Warn("Failed to remove plugin locally", mlog.Err(err), mlog.String("id", data.Id)) } - if err := s.notifyPluginStatusesChanged(); err != nil { + if err := ch.notifyPluginStatusesChanged(); err != nil { mlog.Warn("failed to notify plugin status changed", mlog.Err(err)) } } // InstallPluginWithSignature verifies and installs plugin. func (a *App) InstallPluginWithSignature(pluginFile, signature io.ReadSeeker) (*model.Manifest, *model.AppError) { - return a.Srv().installPluginWithSignature(pluginFile, signature) + return a.ch.installPluginWithSignature(pluginFile, signature) } -func (s *Server) installPluginWithSignature(pluginFile, signature io.ReadSeeker) (*model.Manifest, *model.AppError) { - return s.installPlugin(pluginFile, signature, installPluginLocallyAlways) +func (ch *Channels) installPluginWithSignature(pluginFile, signature io.ReadSeeker) (*model.Manifest, *model.AppError) { + return ch.installPlugin(pluginFile, signature, installPluginLocallyAlways) } // InstallPlugin unpacks and installs a plugin but does not enable or activate it. @@ -147,40 +147,40 @@ func (a *App) InstallPlugin(pluginFile io.ReadSeeker, replace bool) (*model.Mani } func (a *App) installPlugin(pluginFile, signature io.ReadSeeker, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) { - return a.Srv().installPlugin(pluginFile, signature, installationStrategy) + return a.ch.installPlugin(pluginFile, signature, installationStrategy) } -func (s *Server) installPlugin(pluginFile, signature io.ReadSeeker, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) { - manifest, appErr := s.installPluginLocally(pluginFile, signature, installationStrategy) +func (ch *Channels) installPlugin(pluginFile, signature io.ReadSeeker, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) { + manifest, appErr := ch.installPluginLocally(pluginFile, signature, installationStrategy) if appErr != nil { return nil, appErr } if signature != nil { signature.Seek(0, 0) - if _, appErr = s.writeFile(signature, getSignatureStorePath(manifest.Id)); appErr != nil { + if _, appErr = ch.srv.writeFile(signature, getSignatureStorePath(manifest.Id)); appErr != nil { return nil, model.NewAppError("saveSignature", "app.plugin.store_signature.app_error", nil, appErr.Error(), http.StatusInternalServerError) } } // Store bundle in the file store to allow access from other servers. pluginFile.Seek(0, 0) - if _, appErr := s.writeFile(pluginFile, getBundleStorePath(manifest.Id)); appErr != nil { + if _, appErr := ch.srv.writeFile(pluginFile, getBundleStorePath(manifest.Id)); appErr != nil { return nil, model.NewAppError("uploadPlugin", "app.plugin.store_bundle.app_error", nil, appErr.Error(), http.StatusInternalServerError) } - s.notifyClusterPluginEvent( + ch.notifyClusterPluginEvent( model.ClusterEventInstallPlugin, model.PluginEventData{ Id: manifest.Id, }, ) - if err := s.notifyPluginEnabled(manifest); err != nil { + if err := ch.notifyPluginEnabled(manifest); err != nil { mlog.Warn("Failed notify plugin enabled", mlog.Err(err)) } - if err := s.notifyPluginStatusesChanged(); err != nil { + if err := ch.notifyPluginStatusesChanged(); err != nil { mlog.Warn("Failed to notify plugin status changed", mlog.Err(err)) } @@ -190,13 +190,13 @@ func (s *Server) installPlugin(pluginFile, signature io.ReadSeeker, installation // InstallMarketplacePlugin installs a plugin listed in the marketplace server. It will get the plugin bundle // from the prepackaged folder, if available, or remotely if EnableRemoteMarketplace is true. func (a *App) InstallMarketplacePlugin(request *model.InstallMarketplacePluginRequest) (*model.Manifest, *model.AppError) { - return a.Srv().installMarketplacePlugin(request) + return a.ch.installMarketplacePlugin(request) } -func (s *Server) installMarketplacePlugin(request *model.InstallMarketplacePluginRequest) (*model.Manifest, *model.AppError) { +func (ch *Channels) installMarketplacePlugin(request *model.InstallMarketplacePluginRequest) (*model.Manifest, *model.AppError) { var pluginFile, signatureFile io.ReadSeeker - prepackagedPlugin, appErr := s.getPrepackagedPlugin(request.Id, request.Version) + prepackagedPlugin, appErr := ch.getPrepackagedPlugin(request.Id, request.Version) if appErr != nil && appErr.Id != "app.plugin.marketplace_plugins.not_found.app_error" { return nil, appErr } @@ -212,14 +212,14 @@ func (s *Server) installMarketplacePlugin(request *model.InstallMarketplacePlugi signatureFile = bytes.NewReader(prepackagedPlugin.Signature) } - if *s.Config().PluginSettings.EnableRemoteMarketplace && pluginFile == nil { + if *ch.srv.Config().PluginSettings.EnableRemoteMarketplace && pluginFile == nil { var plugin *model.BaseMarketplacePlugin - plugin, appErr = s.getRemoteMarketplacePlugin(request.Id, request.Version) + plugin, appErr = ch.getRemoteMarketplacePlugin(request.Id, request.Version) if appErr != nil { return nil, appErr } - downloadedPluginBytes, err := s.downloadFromURL(plugin.DownloadURL) + downloadedPluginBytes, err := ch.srv.downloadFromURL(plugin.DownloadURL) if err != nil { return nil, model.NewAppError("InstallMarketplacePlugin", "app.plugin.install_marketplace_plugin.app_error", nil, err.Error(), http.StatusInternalServerError) } @@ -238,7 +238,7 @@ func (s *Server) installMarketplacePlugin(request *model.InstallMarketplacePlugi return nil, model.NewAppError("InstallMarketplacePlugin", "app.plugin.marketplace_plugins.signature_not_found.app_error", nil, "", http.StatusInternalServerError) } - manifest, appErr := s.installPluginWithSignature(pluginFile, signatureFile) + manifest, appErr := ch.installPluginWithSignature(pluginFile, signatureFile) if appErr != nil { return nil, appErr } @@ -258,18 +258,18 @@ const ( ) func (a *App) installPluginLocally(pluginFile, signature io.ReadSeeker, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) { - return a.Srv().installPluginLocally(pluginFile, signature, installationStrategy) + return a.ch.installPluginLocally(pluginFile, signature, installationStrategy) } -func (s *Server) installPluginLocally(pluginFile, signature io.ReadSeeker, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) { - pluginsEnvironment := s.GetPluginsEnvironment() +func (ch *Channels) installPluginLocally(pluginFile, signature io.ReadSeeker, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) { + pluginsEnvironment := ch.GetPluginsEnvironment() if pluginsEnvironment == nil { return nil, model.NewAppError("installPluginLocally", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented) } // verify signature if signature != nil { - if err := s.verifyPlugin(pluginFile, signature); err != nil { + if err := ch.verifyPlugin(pluginFile, signature); err != nil { return nil, err } } @@ -285,7 +285,7 @@ func (s *Server) installPluginLocally(pluginFile, signature io.ReadSeeker, insta return nil, appErr } - manifest, appErr = s.installExtractedPlugin(manifest, pluginDir, installationStrategy) + manifest, appErr = ch.installExtractedPlugin(manifest, pluginDir, installationStrategy) if appErr != nil { return nil, appErr } @@ -320,8 +320,8 @@ func extractPlugin(pluginFile io.ReadSeeker, extractDir string) (*model.Manifest return manifest, extractDir, nil } -func (s *Server) installExtractedPlugin(manifest *model.Manifest, fromPluginDir string, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) { - pluginsEnvironment := s.GetPluginsEnvironment() +func (ch *Channels) installExtractedPlugin(manifest *model.Manifest, fromPluginDir string, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) { + pluginsEnvironment := ch.GetPluginsEnvironment() if pluginsEnvironment == nil { return nil, model.NewAppError("installExtractedPlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented) } @@ -368,12 +368,12 @@ func (s *Server) installExtractedPlugin(manifest *model.Manifest, fromPluginDir // Otherwise remove the existing installation prior to install below. mlog.Debug("Removing existing installation of plugin before local install", mlog.String("plugin_id", existingManifest.Id), mlog.String("version", existingManifest.Version)) - if err := s.removePluginLocally(existingManifest.Id); err != nil { + if err := ch.removePluginLocally(existingManifest.Id); err != nil { return nil, model.NewAppError("installExtractedPlugin", "app.plugin.install_id_failed_remove.app_error", nil, "", http.StatusBadRequest) } } - pluginPath := filepath.Join(*s.Config().PluginSettings.Directory, manifest.Id) + pluginPath := filepath.Join(*ch.srv.Config().PluginSettings.Directory, manifest.Id) err = utils.CopyDir(fromPluginDir, pluginPath) if err != nil { return nil, model.NewAppError("installExtractedPlugin", "app.plugin.mvdir.app_error", nil, err.Error(), http.StatusInternalServerError) @@ -395,9 +395,9 @@ func (s *Server) installExtractedPlugin(manifest *model.Manifest, fromPluginDir } // Activate the plugin if enabled. - pluginState := s.Config().PluginSettings.PluginStates[manifest.Id] + pluginState := ch.srv.Config().PluginSettings.PluginStates[manifest.Id] if pluginState != nil && pluginState.Enable { - if manifest.Id == "com.mattermost.apps" && !s.Config().FeatureFlags.AppsEnabled { + if manifest.Id == "com.mattermost.apps" && !ch.srv.Config().FeatureFlags.AppsEnabled { return manifest, nil } updatedManifest, _, err := pluginsEnvironment.Activate(manifest.Id) @@ -413,44 +413,44 @@ func (s *Server) installExtractedPlugin(manifest *model.Manifest, fromPluginDir } func (a *App) RemovePlugin(id string) *model.AppError { - return a.Srv().removePlugin(id) + return a.ch.removePlugin(id) } -func (s *Server) removePlugin(id string) *model.AppError { +func (ch *Channels) removePlugin(id string) *model.AppError { // Disable plugin before removal to make sure this // plugin remains disabled on re-install. - if err := s.disablePlugin(id); err != nil { + if err := ch.disablePlugin(id); err != nil { return err } - if err := s.removePluginLocally(id); err != nil { + if err := ch.removePluginLocally(id); err != nil { return err } // Remove bundle from the file store. storePluginFileName := getBundleStorePath(id) - bundleExist, err := s.fileExists(storePluginFileName) + bundleExist, err := ch.srv.fileExists(storePluginFileName) if err != nil { return model.NewAppError("removePlugin", "app.plugin.remove_bundle.app_error", nil, err.Error(), http.StatusInternalServerError) } if !bundleExist { return nil } - if err = s.removeFile(storePluginFileName); err != nil { + if err = ch.srv.removeFile(storePluginFileName); err != nil { return model.NewAppError("removePlugin", "app.plugin.remove_bundle.app_error", nil, err.Error(), http.StatusInternalServerError) } - if err = s.removeSignature(id); err != nil { + if err = ch.removeSignature(id); err != nil { mlog.Warn("Can't remove signature", mlog.Err(err)) } - s.notifyClusterPluginEvent( + ch.notifyClusterPluginEvent( model.ClusterEventRemovePlugin, model.PluginEventData{ Id: id, }, ) - if err := s.notifyPluginStatusesChanged(); err != nil { + if err := ch.notifyPluginStatusesChanged(); err != nil { mlog.Warn("Failed to notify plugin status changed", mlog.Err(err)) } @@ -458,11 +458,11 @@ func (s *Server) removePlugin(id string) *model.AppError { } func (a *App) removePluginLocally(id string) *model.AppError { - return a.Srv().removePluginLocally(id) + return a.ch.removePluginLocally(id) } -func (s *Server) removePluginLocally(id string) *model.AppError { - pluginsEnvironment := s.GetPluginsEnvironment() +func (ch *Channels) removePluginLocally(id string) *model.AppError { + pluginsEnvironment := ch.GetPluginsEnvironment() if pluginsEnvironment == nil { return model.NewAppError("removePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented) } @@ -488,7 +488,7 @@ func (s *Server) removePluginLocally(id string) *model.AppError { pluginsEnvironment.Deactivate(id) pluginsEnvironment.RemovePlugin(id) - s.unregisterPluginCommands(id) + ch.srv.unregisterPluginCommands(id) if err := os.RemoveAll(pluginPath); err != nil { return model.NewAppError("removePlugin", "app.plugin.remove.app_error", nil, err.Error(), http.StatusInternalServerError) @@ -497,9 +497,9 @@ func (s *Server) removePluginLocally(id string) *model.AppError { return nil } -func (s *Server) removeSignature(pluginID string) *model.AppError { +func (ch *Channels) removeSignature(pluginID string) *model.AppError { filePath := getSignatureStorePath(pluginID) - exists, err := s.fileExists(filePath) + exists, err := ch.srv.fileExists(filePath) if err != nil { return model.NewAppError("removeSignature", "app.plugin.remove_bundle.app_error", nil, err.Error(), http.StatusInternalServerError) } @@ -507,7 +507,7 @@ func (s *Server) removeSignature(pluginID string) *model.AppError { mlog.Debug("no plugin signature to remove", mlog.String("plugin_id", pluginID)) return nil } - if err = s.removeFile(filePath); err != nil { + if err = ch.srv.removeFile(filePath); err != nil { return model.NewAppError("removeSignature", "app.plugin.remove_bundle.app_error", nil, err.Error(), http.StatusInternalServerError) } return nil diff --git a/app/plugin_requests.go b/app/plugin_requests.go index 8b044c1f6a..c9d48a6307 100644 --- a/app/plugin_requests.go +++ b/app/plugin_requests.go @@ -21,7 +21,7 @@ import ( ) func (s *Server) ServePluginRequest(w http.ResponseWriter, r *http.Request) { - pluginsEnvironment := s.GetPluginsEnvironment() + pluginsEnvironment := s.Channels().GetPluginsEnvironment() if pluginsEnvironment == nil { err := model.NewAppError("ServePluginRequest", "app.plugin.disabled.app_error", nil, "Enable plugins to serve plugin requests", http.StatusNotImplemented) s.Log.Error(err.Error()) @@ -46,7 +46,7 @@ func (s *Server) ServePluginRequest(w http.ResponseWriter, r *http.Request) { } func (a *App) ServeInterPluginRequest(w http.ResponseWriter, r *http.Request, sourcePluginId, destinationPluginId string) { - pluginsEnvironment := a.GetPluginsEnvironment() + pluginsEnvironment := a.ch.GetPluginsEnvironment() if pluginsEnvironment == nil { err := model.NewAppError("ServeInterPluginRequest", "app.plugin.disabled.app_error", nil, "Plugin environment not found.", http.StatusNotImplemented) a.Log().Error(err.Error()) @@ -90,7 +90,7 @@ func (s *Server) ServePluginPublicRequest(w http.ResponseWriter, r *http.Request vars := mux.Vars(r) pluginID := vars["plugin_id"] - pluginsEnv := s.GetPluginsEnvironment() + pluginsEnv := s.Channels().GetPluginsEnvironment() // Check if someone has nullified the pluginsEnv in the meantime if pluginsEnv == nil { diff --git a/app/plugin_shutdown_test.go b/app/plugin_shutdown_test.go index 623d249cab..293d882f1f 100644 --- a/app/plugin_shutdown_test.go +++ b/app/plugin_shutdown_test.go @@ -63,7 +63,7 @@ func TestPluginShutdownTest(t *testing.T) { done := make(chan bool) go func() { defer close(done) - th.App.Srv().ShutDownPlugins() + th.App.ch.ShutDownPlugins() }() select { diff --git a/app/plugin_signature.go b/app/plugin_signature.go index 032a3f8fae..eec57807bc 100644 --- a/app/plugin_signature.go +++ b/app/plugin_signature.go @@ -19,15 +19,6 @@ import ( "github.com/mattermost/mattermost-server/v6/utils" ) -// GetPluginPublicKeyFiles returns all public keys listed in the config. -func (a *App) GetPluginPublicKeyFiles() ([]string, *model.AppError) { - return a.Srv().getPluginPublicKeyFiles() -} - -func (s *Server) getPluginPublicKeyFiles() ([]string, *model.AppError) { - return s.Config().PluginSettings.SignaturePublicKeyFiles, nil -} - // GetPublicKey will return the actual public key saved in the `name` file. func (a *App) GetPublicKey(name string) ([]byte, *model.AppError) { return a.Srv().getPublicKey(name) @@ -83,19 +74,16 @@ func (a *App) DeletePublicKey(name string) *model.AppError { // VerifyPlugin checks that the given signature corresponds to the given plugin and matches a trusted certificate. func (a *App) VerifyPlugin(plugin, signature io.ReadSeeker) *model.AppError { - return a.Srv().verifyPlugin(plugin, signature) + return a.ch.verifyPlugin(plugin, signature) } -func (s *Server) verifyPlugin(plugin, signature io.ReadSeeker) *model.AppError { +func (ch *Channels) verifyPlugin(plugin, signature io.ReadSeeker) *model.AppError { if err := verifySignature(bytes.NewReader(mattermostPluginPublicKey), plugin, signature); err == nil { return nil } - publicKeys, appErr := s.getPluginPublicKeyFiles() - if appErr != nil { - return appErr - } + publicKeys := ch.srv.Config().PluginSettings.SignaturePublicKeyFiles for _, pk := range publicKeys { - pkBytes, appErr := s.getPublicKey(pk) + pkBytes, appErr := ch.srv.getPublicKey(pk) if appErr != nil { mlog.Warn("Unable to get public key for ", mlog.String("filename", pk)) continue diff --git a/app/plugin_statuses.go b/app/plugin_statuses.go index 8c3898124a..db982f814a 100644 --- a/app/plugin_statuses.go +++ b/app/plugin_statuses.go @@ -10,8 +10,8 @@ import ( ) // GetPluginStatus returns the status for a plugin installed on this server. -func (s *Server) GetPluginStatus(id string) (*model.PluginStatus, *model.AppError) { - pluginsEnvironment := s.GetPluginsEnvironment() +func (ch *Channels) GetPluginStatus(id string) (*model.PluginStatus, *model.AppError) { + pluginsEnvironment := ch.GetPluginsEnvironment() if pluginsEnvironment == nil { return nil, model.NewAppError("GetPluginStatus", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented) } @@ -24,8 +24,8 @@ func (s *Server) GetPluginStatus(id string) (*model.PluginStatus, *model.AppErro for _, status := range pluginStatuses { if status.PluginId == id { // Add our cluster ID - if s.Cluster != nil { - status.ClusterId = s.Cluster.GetClusterId() + if ch.srv.Cluster != nil { + status.ClusterId = ch.srv.Cluster.GetClusterId() } return status, nil @@ -37,12 +37,12 @@ func (s *Server) GetPluginStatus(id string) (*model.PluginStatus, *model.AppErro // GetPluginStatus returns the status for a plugin installed on this server. func (a *App) GetPluginStatus(id string) (*model.PluginStatus, *model.AppError) { - return a.Srv().GetPluginStatus(id) + return a.ch.GetPluginStatus(id) } // GetPluginStatuses returns the status for plugins installed on this server. -func (s *Server) GetPluginStatuses() (model.PluginStatuses, *model.AppError) { - pluginsEnvironment := s.GetPluginsEnvironment() +func (ch *Channels) GetPluginStatuses() (model.PluginStatuses, *model.AppError) { + pluginsEnvironment := ch.GetPluginsEnvironment() if pluginsEnvironment == nil { return nil, model.NewAppError("GetPluginStatuses", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented) } @@ -54,8 +54,8 @@ func (s *Server) GetPluginStatuses() (model.PluginStatuses, *model.AppError) { // Add our cluster ID for _, status := range pluginStatuses { - if s.Cluster != nil { - status.ClusterId = s.Cluster.GetClusterId() + if ch.srv.Cluster != nil { + status.ClusterId = ch.srv.Cluster.GetClusterId() } else { status.ClusterId = "" } @@ -66,22 +66,22 @@ func (s *Server) GetPluginStatuses() (model.PluginStatuses, *model.AppError) { // GetPluginStatuses returns the status for plugins installed on this server. func (a *App) GetPluginStatuses() (model.PluginStatuses, *model.AppError) { - return a.Srv().GetPluginStatuses() + return a.ch.GetPluginStatuses() } // GetClusterPluginStatuses returns the status for plugins installed anywhere in the cluster. func (a *App) GetClusterPluginStatuses() (model.PluginStatuses, *model.AppError) { - return a.Srv().getClusterPluginStatuses() + return a.ch.getClusterPluginStatuses() } -func (s *Server) getClusterPluginStatuses() (model.PluginStatuses, *model.AppError) { - pluginStatuses, err := s.GetPluginStatuses() +func (ch *Channels) getClusterPluginStatuses() (model.PluginStatuses, *model.AppError) { + pluginStatuses, err := ch.GetPluginStatuses() if err != nil { return nil, err } - if s.Cluster != nil && *s.Config().ClusterSettings.Enable { - clusterPluginStatuses, err := s.Cluster.GetPluginStatuses() + if ch.srv.Cluster != nil && *ch.srv.Config().ClusterSettings.Enable { + clusterPluginStatuses, err := ch.srv.Cluster.GetPluginStatuses() if err != nil { return nil, model.NewAppError("GetClusterPluginStatuses", "app.plugin.get_cluster_plugin_statuses.app_error", nil, err.Error(), http.StatusInternalServerError) } @@ -92,8 +92,8 @@ func (s *Server) getClusterPluginStatuses() (model.PluginStatuses, *model.AppErr return pluginStatuses, nil } -func (s *Server) notifyPluginStatusesChanged() error { - pluginStatuses, err := s.getClusterPluginStatuses() +func (ch *Channels) notifyPluginStatusesChanged() error { + pluginStatuses, err := ch.getClusterPluginStatuses() if err != nil { return err } @@ -102,7 +102,7 @@ func (s *Server) notifyPluginStatusesChanged() error { message := model.NewWebSocketEvent(model.WebsocketEventPluginStatusesChanged, "", "", "", nil) message.Add("plugin_statuses", pluginStatuses) message.GetBroadcast().ContainsSensitiveData = true - s.Publish(message) + ch.srv.Publish(message) return nil } diff --git a/app/plugin_test.go b/app/plugin_test.go index c12494854e..946ab93cd2 100644 --- a/app/plugin_test.go +++ b/app/plugin_test.go @@ -728,7 +728,7 @@ func TestPluginPanicLogs(t *testing.T) { th.TestLogger.Flush() // We shutdown plugins first so that the read on the log buffer is race-free. - th.App.Srv().ShutDownPlugins() + th.App.ch.ShutDownPlugins() tearDown() testlib.AssertLog(t, th.LogBuffer, mlog.LvlDebug.Name, "panic: some text from panic") @@ -775,7 +775,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) { *cfg.PluginSettings.EnableRemoteMarketplace = false }) - plugins := th.App.Srv().processPrepackagedPlugins(prepackagedPluginsDir) + plugins := th.App.ch.processPrepackagedPlugins(prepackagedPluginsDir) require.Len(t, plugins, 1) require.Equal(t, plugins[0].Manifest.Id, "testplugin") require.Empty(t, plugins[0].Signature, 0) @@ -802,7 +802,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) { env := th.App.GetPluginsEnvironment() - plugins := th.App.Srv().processPrepackagedPlugins(prepackagedPluginsDir) + plugins := th.App.ch.processPrepackagedPlugins(prepackagedPluginsDir) require.Len(t, plugins, 1) require.Equal(t, plugins[0].Manifest.Id, "testplugin") require.Empty(t, plugins[0].Signature, 0) @@ -835,7 +835,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) { err = testlib.CopyFile(testPlugin2SignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz.sig")) require.NoError(t, err) - plugins := th.App.Srv().processPrepackagedPlugins(prepackagedPluginsDir) + plugins := th.App.ch.processPrepackagedPlugins(prepackagedPluginsDir) require.Len(t, plugins, 2) require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[0].Manifest.Id) require.NotEmpty(t, plugins[0].Signature) @@ -884,7 +884,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) { err = testlib.CopyFile(testPlugin2SignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz.sig")) require.NoError(t, err) - plugins := th.App.Srv().processPrepackagedPlugins(prepackagedPluginsDir) + plugins := th.App.ch.processPrepackagedPlugins(prepackagedPluginsDir) require.Len(t, plugins, 2) require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[0].Manifest.Id) require.NotEmpty(t, plugins[0].Signature) @@ -921,7 +921,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) { err = testlib.CopyFile(testPlugin2SignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz.sig")) require.NoError(t, err) - plugins := th.App.Srv().processPrepackagedPlugins(prepackagedPluginsDir) + plugins := th.App.ch.processPrepackagedPlugins(prepackagedPluginsDir) require.Len(t, plugins, 2) require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[0].Manifest.Id) require.NotEmpty(t, plugins[0].Signature) diff --git a/app/server.go b/app/server.go index bf957dd808..a48b1d3b6c 100644 --- a/app/server.go +++ b/app/server.go @@ -47,7 +47,6 @@ import ( "github.com/mattermost/mattermost-server/v6/einterfaces" "github.com/mattermost/mattermost-server/v6/jobs" "github.com/mattermost/mattermost-server/v6/model" - "github.com/mattermost/mattermost-server/v6/plugin" "github.com/mattermost/mattermost-server/v6/services/awsmeter" "github.com/mattermost/mattermost-server/v6/services/cache" "github.com/mattermost/mattermost-server/v6/services/httpservice" @@ -108,10 +107,6 @@ type Server struct { goroutineCount int32 goroutineExitSignal chan struct{} - PluginsEnvironment *plugin.Environment - PluginConfigListenerId string - PluginsLock sync.RWMutex - EmailService *email.Service hubs []*Hub @@ -469,7 +464,7 @@ func NewServer(options ...Option) (*Server, error) { }) - s.telemetryService = telemetry.New(s, s.Store, s.SearchEngine, s.Log) + s.telemetryService = telemetry.New(New(ServerConnector(s.Channels())), s.Store, s.SearchEngine, s.Log) emailService, err := email.NewService(email.ServiceConfig{ ConfigFn: s.Config, @@ -689,15 +684,6 @@ func NewServer(options ...Option) (*Server, error) { s.initPostMetadata() - s.initPlugins(c, *s.Config().PluginSettings.Directory, *s.Config().PluginSettings.ClientDirectory) - s.AddConfigListener(func(prevCfg, cfg *model.Config) { - if *cfg.PluginSettings.Enable { - s.initPlugins(c, *cfg.PluginSettings.Directory, *s.Config().PluginSettings.ClientDirectory) - } else { - s.ShutDownPlugins() - } - }) - // Dump the image cache if the proxy settings have changed. (need switch URLs to the correct proxy) s.AddConfigListener(func(oldCfg, newCfg *model.Config) { if (oldCfg.ImageProxySettings.Enable != newCfg.ImageProxySettings.Enable) || @@ -1002,17 +988,7 @@ func (s *Server) Shutdown() { defer sentry.Flush(2 * time.Second) - // Stop products. - // This needs to happen before because products are dependent - // on parent services. - for name, product := range s.products { - if err := product.Stop(); err != nil { - mlog.Warn("Unable to cleanly stop product", mlog.String("name", name), mlog.Err(err)) - } - } - s.HubStop() - s.ShutDownPlugins() s.RemoveLicenseListener(s.licenseListenerId) s.RemoveLicenseListener(s.loggerLicenseListenerId) s.RemoveClusterLeaderChangedListener(s.clusterLeaderListenerId) @@ -1096,6 +1072,15 @@ func (s *Server) Shutdown() { mlog.Info("Server stopped") + // Stop products. + // This needs to happen last because products are dependent + // on parent services. + for name, product := range s.products { + if err2 := product.Stop(); err2 != nil { + mlog.Warn("Unable to cleanly stop product", mlog.String("name", name), mlog.Err(err2)) + } + } + // shutdown main and notification loggers which will flush any remaining log records. timeoutCtx, timeoutCancel := context.WithTimeout(context.Background(), time.Second*15) defer timeoutCancel() @@ -1204,6 +1189,14 @@ func stripPort(hostport string) string { } func (s *Server) Start() error { + // Start products. + // This needs to happen before because products are dependent on the HTTP server. + for name, product := range s.products { + if err := product.Start(); err != nil { + return errors.Wrapf(err, "Unable to start %s", name) + } + } + mlog.Info("Starting Server...") var handler http.Handler = s.RootRouter @@ -1406,14 +1399,6 @@ func (s *Server) Start() error { mlog.Error("Error starting inter-cluster services", mlog.Err(err)) } - // Start products. - // This needs to happen after the server has started. - for name, product := range s.products { - if err := product.Start(); err != nil { - return errors.Wrapf(err, "Unable to start %s", name) - } - } - return nil }