[MM-48626] Move plugins environment out of Channels (#21730)

This commit is contained in:
Ibrahim Serdar Acikgoz
2022-12-21 16:11:57 +03:00
committed by GitHub
parent 0ce7c5e3da
commit dcf499b51d
40 changed files with 537 additions and 447 deletions

View File

@@ -155,7 +155,7 @@ func installMarketplacePlugin(c *Context, w http.ResponseWriter, r *http.Request
// https://mattermost.atlassian.net/browse/MM-41981
pluginRequest.Version = ""
manifest, appErr := c.App.Channels().InstallMarketplacePlugin(pluginRequest)
manifest, appErr := c.App.PluginService().InstallMarketplacePlugin(pluginRequest)
if appErr != nil {
c.Err = appErr
return
@@ -235,7 +235,7 @@ func removePlugin(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
err := c.App.Channels().RemovePlugin(c.Params.PluginId)
err := c.App.PluginService().RemovePlugin(c.Params.PluginId)
if err != nil {
c.Err = err
return

View File

@@ -94,7 +94,7 @@ func TestPlugin(t *testing.T) {
assert.Equal(t, "testplugin", manifest.Id)
})
th.App.Channels().RemovePlugin(manifest.Id)
th.App.PluginService().RemovePlugin(manifest.Id)
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.PluginSettings.Enable = false })

View File

@@ -61,7 +61,7 @@ func connectWebSocket(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
wc := c.App.Srv().Platform().NewWebConn(cfg, c.App, c.App.Srv().Channels())
wc := c.App.Srv().Platform().NewWebConn(cfg, c.App, c.App.Srv())
if c.AppContext.Session().UserId != "" {
c.App.Srv().Platform().HubRegister(wc)
}

View File

@@ -936,6 +936,7 @@ type AppIface interface {
PermanentDeleteTeamId(c request.CTX, teamID string) *model.AppError
PermanentDeleteUser(c *request.Context, user *model.User) *model.AppError
PluginCommandsForTeam(teamID string) []*model.Command
PluginService() *PluginService
PostActionCookieSecret() []byte
PostAddToChannelMessage(c request.CTX, user *model.User, addedUser *model.User, channel *model.Channel, postRootId string) *model.AppError
PostPatchWithProxyRemovedFromImageURLs(patch *model.PostPatch) *model.PostPatch

View File

@@ -345,7 +345,7 @@ func (a *App) CreateChannel(c request.CTX, channel *model.Channel, addMember boo
a.Srv().Go(func() {
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.ChannelHasBeenCreated(pluginContext, sc)
return true
}, plugin.ChannelHasBeenCreatedID)
@@ -429,7 +429,7 @@ func (a *App) handleCreationEvent(c request.CTX, userID, otherUserID string, cha
a.Srv().Go(func() {
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.ChannelHasBeenCreated(pluginContext, channel)
return true
}, plugin.ChannelHasBeenCreatedID)
@@ -1597,7 +1597,7 @@ func (a *App) AddChannelMember(c request.CTX, userID string, channel *model.Chan
a.Srv().Go(func() {
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.UserHasJoinedChannel(pluginContext, cm, userRequestor)
return true
}, plugin.UserHasJoinedChannelID)
@@ -2173,7 +2173,7 @@ func (a *App) JoinChannel(c request.CTX, channel *model.Channel, userID string)
a.Srv().Go(func() {
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.UserHasJoinedChannel(pluginContext, cm, nil)
return true
}, plugin.UserHasJoinedChannelID)
@@ -2483,7 +2483,7 @@ func (a *App) removeUserFromChannel(c request.CTX, userIDToRemove string, remove
a.Srv().Go(func() {
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.UserHasLeftChannel(pluginContext, cm, actorUser)
return true
}, plugin.UserHasLeftChannelID)

View File

@@ -6,14 +6,11 @@ package app
import (
"fmt"
"runtime"
"strings"
"sync"
"github.com/pkg/errors"
"github.com/mattermost/mattermost-server/v6/app/imaging"
"github.com/mattermost/mattermost-server/v6/app/request"
"github.com/mattermost/mattermost-server/v6/config"
"github.com/mattermost/mattermost-server/v6/einterfaces"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/plugin"
@@ -42,12 +39,6 @@ type Channels struct {
postActionCookieSecret []byte
pluginCommandsLock sync.RWMutex
pluginCommands []*PluginCommand
pluginsLock sync.RWMutex
pluginsEnvironment *plugin.Environment
pluginConfigListenerID string
imageProxy *imageproxy.ImageProxy
// cached counts that are used during notice condition validation
@@ -79,12 +70,6 @@ type Channels struct {
postReminderMut sync.Mutex
postReminderTask *model.ScheduledTask
// collectionTypes maps from collection types to the registering plugin id
collectionTypes map[string]string
// topicTypes maps from topic types to collection types
topicTypes map[string]string
collectionAndTopicTypesMut sync.Mutex
}
func init() {
@@ -107,11 +92,9 @@ func NewChannels(services map[product.ServiceKey]any) (*Channels, error) {
return nil, errors.New("server not passed")
}
ch := &Channels{
srv: s,
imageProxy: imageproxy.MakeImageProxy(s.platform, s.httpService, s.Log()),
uploadLockMap: map[string]bool{},
collectionTypes: map[string]string{},
topicTypes: map[string]string{},
srv: s,
imageProxy: imageproxy.MakeImageProxy(s.platform, s.httpService, s.Log()),
uploadLockMap: map[string]bool{},
}
// To get another service:
@@ -208,10 +191,6 @@ func NewChannels(services map[product.ServiceKey]any) (*Channels, error) {
services[product.RouterKey] = ch.routerSvc
// Setup routes.
pluginsRoute := ch.srv.Router.PathPrefix("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}").Subrouter()
pluginsRoute.HandleFunc("", ch.ServePluginRequest)
pluginsRoute.HandleFunc("/public/{public_file:.*}", ch.ServePluginPublicRequest)
pluginsRoute.HandleFunc("/{anything:.*}", ch.ServePluginRequest)
services[product.PostKey] = &postServiceWrapper{
app: &App{ch: ch},
@@ -243,39 +222,6 @@ func NewChannels(services map[product.ServiceKey]any) (*Channels, error) {
}
func (ch *Channels) Start() error {
// Start plugins
ctx := request.EmptyContext(ch.srv.Log())
ch.initPlugins(ctx, *ch.cfgSvc.Config().PluginSettings.Directory, *ch.cfgSvc.Config().PluginSettings.ClientDirectory)
ch.AddConfigListener(func(prevCfg, cfg *model.Config) {
// We compute the difference between configs
// to ensure we don't re-init plugins unnecessarily.
diffs, err := config.Diff(prevCfg, cfg)
if err != nil {
ch.srv.Log().Warn("Error in comparing configs", mlog.Err(err))
return
}
hasDiff := false
// TODO: This could be a method on ConfigDiffs itself
for _, diff := range diffs {
if strings.HasPrefix(diff.Path, "PluginSettings.") {
hasDiff = true
break
}
}
// Do only if some plugin related settings has changed.
if hasDiff {
if *cfg.PluginSettings.Enable {
ch.initPlugins(ctx, *cfg.PluginSettings.Directory, *ch.cfgSvc.Config().PluginSettings.ClientDirectory)
} else {
ch.ShutDownPlugins()
}
}
})
// TODO: This should be moved to the platform service.
if err := ch.srv.platform.EnsureAsymmetricSigningKey(); err != nil {
return errors.Wrapf(err, "unable to ensure asymmetric signing key")
@@ -289,8 +235,6 @@ func (ch *Channels) Start() error {
}
func (ch *Channels) Stop() error {
ch.ShutDownPlugins()
ch.dndTaskMut.Lock()
if ch.dndTask != nil {
ch.dndTask.Cancel()
@@ -332,18 +276,18 @@ func (s *hooksService) RegisterHooks(productID string, hooks any) error {
return s.ch.srv.hooksManager.AddProduct(productID, hooks)
}
func (ch *Channels) RunMultiHook(hookRunnerFunc func(hooks plugin.Hooks) bool, hookId int) {
if env := ch.GetPluginsEnvironment(); env != nil {
func (s *Server) RunMultiHook(hookRunnerFunc func(hooks plugin.Hooks) bool, hookId int) {
if env := s.pluginService.GetPluginsEnvironment(); env != nil {
env.RunMultiPluginHook(hookRunnerFunc, hookId)
}
// run hook for the products
ch.srv.hooksManager.RunMultiHook(hookRunnerFunc, hookId)
s.hooksManager.RunMultiHook(hookRunnerFunc, hookId)
}
func (ch *Channels) HooksForPluginOrProduct(id string) (plugin.Hooks, error) {
func (s *Server) HooksForPluginOrProduct(id string) (plugin.Hooks, error) {
var hooks plugin.Hooks
if env := ch.GetPluginsEnvironment(); env != nil {
if env := s.pluginService.GetPluginsEnvironment(); env != nil {
// we intentionally ignore the error here, because the id can be a product id
// we are going to check if we have the hooks or not
hooks, _ = env.HooksForPlugin(id)
@@ -352,7 +296,7 @@ func (ch *Channels) HooksForPluginOrProduct(id string) (plugin.Hooks, error) {
}
}
hooks = ch.srv.hooksManager.HooksForProduct(id)
hooks = s.hooksManager.HooksForProduct(id)
if hooks != nil {
return hooks, nil
}

View File

@@ -16,7 +16,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.Channels().installPluginFromData(data)
s.pluginService.installPluginFromData(data)
}
func (s *Server) clusterRemovePluginHandler(msg *model.ClusterMessage) {
@@ -24,7 +24,7 @@ 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.Channels().removePluginFromData(data)
s.pluginService.removePluginFromData(data)
}
func (s *Server) clusterPluginEventHandler(msg *model.ClusterMessage) {
@@ -44,12 +44,7 @@ func (s *Server) clusterPluginEventHandler(msg *model.ClusterMessage) {
return
}
channels, ok := s.products["channels"].(*Channels)
if !ok {
return
}
hooks, err := channels.HooksForPluginOrProduct(pluginID)
hooks, err := s.HooksForPluginOrProduct(pluginID)
if err != nil {
mlog.Warn("Getting hooks for plugin failed", mlog.String("plugin_id", pluginID), mlog.Err(err))
return

View File

@@ -10,26 +10,26 @@ import (
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)
func (a *App) registerCollectionAndTopic(pluginID, collectionType, topicType string) error {
func (s *PluginService) registerCollectionAndTopic(pluginID, collectionType, topicType string) error {
// we have a race condition due to multiple plugins calling this method
a.ch.collectionAndTopicTypesMut.Lock()
defer a.ch.collectionAndTopicTypesMut.Unlock()
s.collectionAndTopicTypesMut.Lock()
defer s.collectionAndTopicTypesMut.Unlock()
// check if collectionType was already registered by other plugin
existingPluginID, ok := a.ch.collectionTypes[collectionType]
existingPluginID, ok := s.collectionTypes[collectionType]
if ok && existingPluginID != pluginID {
return model.NewAppError("registerCollectionAndTopic", "app.collection.add_collection.exists.app_error", nil, "", http.StatusBadRequest)
}
// check if topicType was already registered to other collection
existingCollectionType, ok := a.ch.topicTypes[topicType]
existingCollectionType, ok := s.topicTypes[topicType]
if ok && existingCollectionType != collectionType {
return model.NewAppError("registerCollectionAndTopic", "app.collection.add_topic.exists.app_error", nil, "", http.StatusBadRequest)
}
a.ch.collectionTypes[collectionType] = pluginID
a.ch.topicTypes[topicType] = collectionType
s.collectionTypes[collectionType] = pluginID
s.topicTypes[topicType] = collectionType
a.ch.srv.Log().Info("registered collection and topic type", mlog.String("plugin_id", pluginID), mlog.String("collection_type", collectionType), mlog.String("topic_type", topicType))
s.platform.Log().Info("registered collection and topic type", mlog.String("plugin_id", pluginID), mlog.String("collection_type", collectionType), mlog.String("topic_type", topicType))
return nil
}

View File

@@ -22,10 +22,10 @@ const (
)
func (a *App) DownloadFromURL(downloadURL string) ([]byte, error) {
return a.Srv().downloadFromURL(downloadURL)
return a.Srv().pluginService.downloadFromURL(downloadURL)
}
func (s *Server) downloadFromURL(downloadURL string) ([]byte, error) {
func (s *PluginService) downloadFromURL(downloadURL string) ([]byte, error) {
if !model.IsValidHTTPURL(downloadURL) {
return nil, errors.Errorf("invalid url %s", downloadURL)
}
@@ -38,7 +38,7 @@ func (s *Server) downloadFromURL(downloadURL string) ([]byte, error) {
return nil, errors.Errorf("insecure url not allowed %s", downloadURL)
}
client := s.HTTPService().MakeClient(true)
client := s.httpService.MakeClient(true)
client.Timeout = HTTPRequestTimeout
var resp *http.Response

View File

@@ -926,7 +926,7 @@ func (a *App) DoUploadFileExpectModification(c request.CTX, now time.Time, rawTe
var rejectionError *model.AppError
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
var newBytes bytes.Buffer
replacementInfo, rejectionReason := hooks.FileWillBeUploaded(pluginContext, info, bytes.NewReader(data), &newBytes)
if rejectionReason != "" {

View File

@@ -375,10 +375,10 @@ func (w *LocalResponseWriter) WriteHeader(statusCode int) {
}
func (a *App) doPluginRequest(c *request.Context, method, rawURL string, values url.Values, body []byte) (*http.Response, *model.AppError) {
return a.ch.doPluginRequest(c, method, rawURL, values, body)
return a.ch.srv.pluginService.doPluginRequest(c, method, rawURL, values, body)
}
func (ch *Channels) doPluginRequest(c *request.Context, method, rawURL string, values url.Values, body []byte) (*http.Response, *model.AppError) {
func (s *PluginService) doPluginRequest(c *request.Context, method, rawURL string, values url.Values, body []byte) (*http.Response, *model.AppError) {
rawURL = strings.TrimPrefix(rawURL, "/")
inURL, err := url.Parse(rawURL)
if err != nil {
@@ -427,7 +427,7 @@ func (ch *Channels) doPluginRequest(c *request.Context, method, rawURL string, v
params["plugin_id"] = pluginID
r = mux.SetURLVars(r, params)
ch.ServePluginRequest(w, r)
s.ServePluginRequest(w, r)
resp := &http.Response{
StatusCode: w.status,

View File

@@ -159,7 +159,7 @@ func (a *App) GetUserForLogin(id, loginId string) (*model.User, *model.AppError)
func (a *App) DoLogin(c *request.Context, w http.ResponseWriter, r *http.Request, user *model.User, deviceID string, isMobile, isOAuthUser, isSaml bool) *model.AppError {
var rejectionReason string
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
rejectionReason = hooks.UserWillLogIn(pluginContext, user)
return rejectionReason == ""
}, plugin.UserWillLogInID)
@@ -225,7 +225,7 @@ func (a *App) DoLogin(c *request.Context, w http.ResponseWriter, r *http.Request
}
a.Srv().Go(func() {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.UserHasLoggedIn(pluginContext, user)
return true
}, plugin.UserHasLoggedInID)

View File

@@ -28,11 +28,6 @@ func (a *App) markAdminOnboardingComplete(c *request.Context) *model.AppError {
}
func (a *App) CompleteOnboarding(c *request.Context, request *model.CompleteOnboardingRequest) *model.AppError {
pluginsEnvironment := a.Channels().GetPluginsEnvironment()
if pluginsEnvironment == nil {
return a.markAdminOnboardingComplete(c)
}
pluginContext := pluginContext(c)
for _, pluginID := range request.InstallPlugins {
@@ -41,7 +36,7 @@ func (a *App) CompleteOnboarding(c *request.Context, request *model.CompleteOnbo
installRequest := &model.InstallMarketplacePluginRequest{
Id: id,
}
_, appErr := a.Channels().InstallMarketplacePlugin(installRequest)
_, appErr := a.Srv().pluginService.InstallMarketplacePlugin(installRequest)
if appErr != nil {
mlog.Error("Failed to install plugin for onboarding", mlog.String("id", id), mlog.Err(appErr))
return
@@ -53,7 +48,7 @@ func (a *App) CompleteOnboarding(c *request.Context, request *model.CompleteOnbo
return
}
hooks, err := a.ch.HooksForPluginOrProduct(id)
hooks, err := a.Srv().HooksForPluginOrProduct(id)
if err != nil {
mlog.Warn("Getting hooks for plugin failed", mlog.String("plugin_id", id), mlog.Err(err))
return

View File

@@ -13125,6 +13125,23 @@ func (a *OpenTracingAppLayer) PluginCommandsForTeam(teamID string) []*model.Comm
return resultVar0
}
func (a *OpenTracingAppLayer) PluginService() *app.PluginService {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.PluginService")
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.PluginService()
return resultVar0
}
func (a *OpenTracingAppLayer) PopulateWebConnConfig(s *model.Session, cfg *platform.WebConnConfig, seqVal string) (*platform.WebConnConfig, error) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.PopulateWebConnConfig")

View File

@@ -20,16 +20,37 @@ import (
svg "github.com/h2non/go-is-svg"
"github.com/pkg/errors"
"github.com/mattermost/mattermost-server/v6/app/platform"
"github.com/mattermost/mattermost-server/v6/app/request"
"github.com/mattermost/mattermost-server/v6/config"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/plugin"
"github.com/mattermost/mattermost-server/v6/product"
"github.com/mattermost/mattermost-server/v6/services/httpservice"
"github.com/mattermost/mattermost-server/v6/services/marketplace"
"github.com/mattermost/mattermost-server/v6/shared/filestore"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
"github.com/mattermost/mattermost-server/v6/utils/fileutils"
)
type PluginService struct {
platform *platform.PlatformService
channels *Channels
fileStore filestore.FileBackend
httpService httpservice.HTTPService
pluginCommandsLock sync.RWMutex
pluginCommands []*PluginCommand
pluginsLock sync.RWMutex
pluginsEnvironment *plugin.Environment
pluginConfigListenerID string
// collectionTypes maps from collection types to the registering plugin id
collectionTypes map[string]string
// topicTypes maps from topic types to collection types
topicTypes map[string]string
collectionAndTopicTypesMut sync.Mutex
}
const prepackagedPluginsDir = "prepackaged_plugins"
type pluginSignaturePath struct {
@@ -63,20 +84,91 @@ func (rs *routerService) getHandler(productID string) (http.Handler, bool) {
return handler, ok
}
func (a *App) PluginService() *PluginService {
return a.ch.srv.pluginService
}
func (s *Server) InitializePluginService() error {
product, ok := s.products["channels"]
if !ok {
return errors.New("unable to find channels product")
}
channels, ok := product.(*Channels)
if !ok {
return errors.New("unable to cast product to channels product")
}
ps := &PluginService{
platform: s.platform,
channels: channels,
fileStore: s.platform.FileBackend(),
httpService: s.httpService,
collectionTypes: make(map[string]string),
topicTypes: make(map[string]string),
}
s.pluginService = ps
pluginsRoute := s.Router.PathPrefix("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}").Subrouter()
pluginsRoute.HandleFunc("", ps.ServePluginRequest)
pluginsRoute.HandleFunc("/public/{public_file:.*}", ps.ServePluginPublicRequest)
pluginsRoute.HandleFunc("/{anything:.*}", ps.ServePluginRequest)
ps.initPlugins(request.EmptyContext(s.platform.Log()), *s.platform.Config().PluginSettings.Directory, *s.platform.Config().PluginSettings.ClientDirectory)
// Start plugins
ctx := request.EmptyContext(s.platform.Log())
// Add the config listener to enable/disable plugins
s.platform.AddConfigListener(func(prevCfg, cfg *model.Config) {
// We compute the difference between configs
// to ensure we don't re-init plugins unnecessarily.
diffs, err := config.Diff(prevCfg, cfg)
if err != nil {
s.platform.Log().Warn("Error in comparing configs", mlog.Err(err))
return
}
hasDiff := false
// TODO: This could be a method on ConfigDiffs itself
for _, diff := range diffs {
if strings.HasPrefix(diff.Path, "PluginSettings.") {
hasDiff = true
break
}
}
// Do only if some plugin related settings has changed.
if hasDiff {
if *cfg.PluginSettings.Enable {
s.pluginService.initPlugins(ctx, *cfg.PluginSettings.Directory, *s.Config().PluginSettings.ClientDirectory)
} else {
s.pluginService.ShutDownPlugins()
}
}
})
return nil
}
func (s *Server) GetPluginsEnvironment() *plugin.Environment {
return s.pluginService.GetPluginsEnvironment()
}
// GetPluginsEnvironment returns the plugin environment for use if plugins are enabled and
// initialized.
//
// To get the plugins environment when the plugins are disabled, manually acquire the plugins
// lock instead.
func (ch *Channels) GetPluginsEnvironment() *plugin.Environment {
if !*ch.cfgSvc.Config().PluginSettings.Enable {
func (s *PluginService) GetPluginsEnvironment() *plugin.Environment {
if !*s.platform.Config().PluginSettings.Enable {
return nil
}
ch.pluginsLock.RLock()
defer ch.pluginsLock.RUnlock()
s.pluginsLock.RLock()
defer s.pluginsLock.RUnlock()
return ch.pluginsEnvironment
return s.pluginsEnvironment
}
// GetPluginsEnvironment returns the plugin environment for use if plugins are enabled and
@@ -85,33 +177,39 @@ func (ch *Channels) 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.ch.GetPluginsEnvironment()
// TODO: Telemetry service starts before products start, so we need to check if the plugin service is initialized.
// Move the telemetry service to start after products start.
if a.ch.srv.pluginService == nil {
return nil
}
return a.ch.srv.pluginService.GetPluginsEnvironment()
}
func (ch *Channels) SetPluginsEnvironment(pluginsEnvironment *plugin.Environment) {
ch.pluginsLock.Lock()
defer ch.pluginsLock.Unlock()
func (s *PluginService) SetPluginsEnvironment(pluginsEnvironment *plugin.Environment) {
s.pluginsLock.Lock()
defer s.pluginsLock.Unlock()
ch.pluginsEnvironment = pluginsEnvironment
ch.srv.Platform().SetPluginsEnvironment(ch)
s.pluginsEnvironment = pluginsEnvironment
s.platform.SetPluginsEnvironment(s.channels.srv)
}
func (ch *Channels) syncPluginsActiveState() {
func (s *PluginService) syncPluginsActiveState() {
// Acquiring lock manually, as plugins might be disabled. See GetPluginsEnvironment.
ch.pluginsLock.RLock()
pluginsEnvironment := ch.pluginsEnvironment
ch.pluginsLock.RUnlock()
s.pluginsLock.RLock()
pluginsEnvironment := s.pluginsEnvironment
s.pluginsLock.RUnlock()
if pluginsEnvironment == nil {
return
}
config := ch.cfgSvc.Config().PluginSettings
config := s.platform.Config().PluginSettings
if *config.Enable {
availablePlugins, err := pluginsEnvironment.Available()
if err != nil {
ch.srv.Log().Error("Unable to get available plugins", mlog.Err(err))
s.platform.Log().Error("Unable to get available plugins", mlog.Err(err))
return
}
@@ -125,24 +223,24 @@ func (ch *Channels) syncPluginsActiveState() {
pluginEnabled = state.Enable
}
if hasOverride, value := ch.getPluginStateOverride(pluginID); hasOverride {
if hasOverride, value := s.getPluginStateOverride(pluginID); hasOverride {
pluginEnabled = value
}
if pluginEnabled {
// Disable focalboard in product mode.
if pluginID == model.PluginIdFocalboard && ch.cfgSvc.Config().FeatureFlags.BoardsProduct {
if pluginID == model.PluginIdFocalboard && s.platform.Config().FeatureFlags.BoardsProduct {
msg := "Plugin cannot run in product mode. Disabling."
mlog.Warn(msg, mlog.String("plugin_id", model.PluginIdFocalboard))
// This is a mini-version of ch.disablePlugin.
// We don't call that directly, because that will recursively call
// this method.
ch.cfgSvc.UpdateConfig(func(cfg *model.Config) {
s.platform.UpdateConfig(func(cfg *model.Config) {
cfg.PluginSettings.PluginStates[pluginID] = &model.PluginState{Enable: false}
})
pluginsEnvironment.SetPluginError(pluginID, msg)
ch.unregisterPluginCommands(pluginID)
s.unregisterPluginCommands(pluginID)
disabledPlugins = append(disabledPlugins, plugin)
continue
}
@@ -166,7 +264,7 @@ func (ch *Channels) syncPluginsActiveState() {
if deactivated && plugin.Manifest.HasClient() {
message := model.NewWebSocketEvent(model.WebsocketEventPluginDisabled, "", "", "", nil, "")
message.Add("manifest", plugin.Manifest.ClientManifest())
ch.srv.platform.Publish(message)
s.platform.Publish(message)
}
}(plugin)
}
@@ -180,14 +278,14 @@ func (ch *Channels) syncPluginsActiveState() {
pluginID := plugin.Manifest.Id
updatedManifest, activated, err := pluginsEnvironment.Activate(pluginID)
if err != nil {
plugin.WrapLogger(ch.srv.Log()).Error("Unable to activate plugin", mlog.Err(err))
plugin.WrapLogger(s.platform.Log().(*mlog.Logger)).Error("Unable to activate plugin", mlog.Err(err))
return
}
if activated {
// Notify all cluster clients if ready
if err := ch.notifyPluginEnabled(updatedManifest); err != nil {
ch.srv.Log().Error("Failed to notify cluster on plugin enable", mlog.Err(err))
if err := s.notifyPluginEnabled(updatedManifest); err != nil {
s.platform.Log().Error("Failed to notify cluster on plugin enable", mlog.Err(err))
}
}
}(plugin)
@@ -197,7 +295,7 @@ func (ch *Channels) syncPluginsActiveState() {
pluginsEnvironment.Shutdown()
}
if err := ch.notifyPluginStatusesChanged(); err != nil {
if err := s.notifyPluginStatusesChanged(); err != nil {
mlog.Warn("failed to notify plugin status changed", mlog.Err(err))
}
}
@@ -207,27 +305,29 @@ func (a *App) NewPluginAPI(c *request.Context, manifest *model.Manifest) plugin.
}
func (a *App) InitPlugins(c *request.Context, pluginDir, webappPluginDir string) {
a.ch.initPlugins(c, pluginDir, webappPluginDir)
a.ch.srv.pluginService.initPlugins(c, pluginDir, webappPluginDir)
}
func (ch *Channels) initPlugins(c *request.Context, pluginDir, webappPluginDir string) {
func (s *PluginService) initPlugins(c *request.Context, pluginDir, webappPluginDir string) {
// Acquiring lock manually, as plugins might be disabled. See GetPluginsEnvironment.
defer func() {
ch.srv.Platform().SetPluginsEnvironment(ch)
// platform service requires plugins environment to be initialized
// so that it can use it in cluster service initialization
s.platform.SetPluginsEnvironment(s.channels.srv)
}()
ch.pluginsLock.RLock()
pluginsEnvironment := ch.pluginsEnvironment
ch.pluginsLock.RUnlock()
if pluginsEnvironment != nil || !*ch.cfgSvc.Config().PluginSettings.Enable {
ch.syncPluginsActiveState()
s.pluginsLock.RLock()
pluginsEnvironment := s.pluginsEnvironment
s.pluginsLock.RUnlock()
if pluginsEnvironment != nil || !*s.platform.Config().PluginSettings.Enable {
s.syncPluginsActiveState()
if pluginsEnvironment != nil {
pluginsEnvironment.TogglePluginHealthCheckJob(*ch.cfgSvc.Config().PluginSettings.EnableHealthCheck)
pluginsEnvironment.TogglePluginHealthCheckJob(*s.platform.Config().PluginSettings.EnableHealthCheck)
}
return
}
ch.srv.Log().Info("Starting up plugins")
s.platform.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))
@@ -240,77 +340,77 @@ func (ch *Channels) initPlugins(c *request.Context, pluginDir, webappPluginDir s
}
newAPIFunc := func(manifest *model.Manifest) plugin.API {
return New(ServerConnector(ch)).NewPluginAPI(c, manifest)
return New(ServerConnector(s.channels)).NewPluginAPI(c, manifest)
}
env, err := plugin.NewEnvironment(
newAPIFunc,
NewDriverImpl(ch.srv),
NewDriverImpl(s.platform),
pluginDir,
webappPluginDir,
*ch.cfgSvc.Config().ExperimentalSettings.PatchPluginsReactDOM,
ch.srv.Log(),
ch.srv.GetMetrics(),
*s.platform.Config().ExperimentalSettings.PatchPluginsReactDOM,
s.platform.Logger(),
s.platform.Metrics(),
)
if err != nil {
mlog.Error("Failed to start up plugins", mlog.Err(err))
return
}
ch.pluginsLock.Lock()
ch.pluginsEnvironment = env
ch.pluginsLock.Unlock()
s.pluginsLock.Lock()
s.pluginsEnvironment = env
s.pluginsLock.Unlock()
ch.pluginsEnvironment.TogglePluginHealthCheckJob(*ch.cfgSvc.Config().PluginSettings.EnableHealthCheck)
s.pluginsEnvironment.TogglePluginHealthCheckJob(*s.platform.Config().PluginSettings.EnableHealthCheck)
if err := ch.syncPlugins(); err != nil {
if err := s.syncPlugins(); err != nil {
mlog.Error("Failed to sync plugins from the file store", mlog.Err(err))
}
plugins := ch.processPrepackagedPlugins(prepackagedPluginsDir)
pluginsEnvironment = ch.GetPluginsEnvironment()
plugins := s.processPrepackagedPlugins(prepackagedPluginsDir)
pluginsEnvironment = s.GetPluginsEnvironment()
if pluginsEnvironment == nil {
mlog.Info("Plugins environment not found, server is likely shutting down")
return
}
pluginsEnvironment.SetPrepackagedPlugins(plugins)
ch.installFeatureFlagPlugins()
s.installFeatureFlagPlugins()
// Sync plugin active state when config changes. Also notify plugins.
ch.pluginsLock.Lock()
ch.RemoveConfigListener(ch.pluginConfigListenerID)
ch.pluginConfigListenerID = ch.AddConfigListener(func(old, new *model.Config) {
s.pluginsLock.Lock()
s.platform.RemoveConfigListener(s.pluginConfigListenerID)
s.pluginConfigListenerID = s.platform.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 {
ch.installFeatureFlagPlugins()
ch.syncPluginsActiveState()
s.installFeatureFlagPlugins()
s.syncPluginsActiveState()
}
ch.RunMultiHook(func(hooks plugin.Hooks) bool {
s.pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
if err := hooks.OnConfigurationChange(); err != nil {
ch.srv.Log().Error("Plugin OnConfigurationChange hook failed", mlog.Err(err))
s.platform.Log().Error("Plugin OnConfigurationChange hook failed", mlog.Err(err))
}
return true
}, plugin.OnConfigurationChangeID)
})
ch.pluginsLock.Unlock()
s.pluginsLock.Unlock()
ch.syncPluginsActiveState()
s.syncPluginsActiveState()
}
// SyncPlugins synchronizes the plugins installed locally
// with the plugin bundles available in the file store.
func (a *App) SyncPlugins() *model.AppError {
return a.ch.syncPlugins()
return a.ch.srv.pluginService.syncPlugins()
}
// SyncPlugins synchronizes the plugins installed locally
// with the plugin bundles available in the file store.
func (ch *Channels) syncPlugins() *model.AppError {
func (s *PluginService) syncPlugins() *model.AppError {
mlog.Info("Syncing plugins from the file store")
pluginsEnvironment := ch.GetPluginsEnvironment()
pluginsEnvironment := s.GetPluginsEnvironment()
if pluginsEnvironment == nil {
return model.NewAppError("SyncPlugins", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
}
@@ -326,14 +426,14 @@ func (ch *Channels) syncPlugins() *model.AppError {
go func(pluginID string) {
defer wg.Done()
// Only handle managed plugins with .filestore flag file.
_, err := os.Stat(filepath.Join(*ch.cfgSvc.Config().PluginSettings.Directory, pluginID, managedPluginFileName))
_, err := os.Stat(filepath.Join(*s.platform.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 := ch.removePluginLocally(pluginID); err != nil {
if err := s.removePluginLocally(pluginID); err != nil {
mlog.Error("Failed to remove local installation of managed plugin before sync", mlog.String("plugin_id", pluginID), mlog.Err(err))
}
}
@@ -342,7 +442,7 @@ func (ch *Channels) syncPlugins() *model.AppError {
wg.Wait()
// Install plugins from the file store.
pluginSignaturePathMap, appErr := ch.getPluginsFromFolder()
pluginSignaturePathMap, appErr := s.getPluginsFromFolder()
if appErr != nil {
return appErr
}
@@ -351,7 +451,7 @@ func (ch *Channels) syncPlugins() *model.AppError {
wg.Add(1)
go func(plugin *pluginSignaturePath) {
defer wg.Done()
reader, appErr := ch.srv.fileReader(plugin.path)
reader, appErr := s.fileStore.Reader(plugin.path)
if appErr != nil {
mlog.Error("Failed to open plugin bundle from file store.", mlog.String("bundle", plugin.path), mlog.Err(appErr))
return
@@ -359,8 +459,8 @@ func (ch *Channels) syncPlugins() *model.AppError {
defer reader.Close()
var signature filestore.ReadCloseSeeker
if *ch.cfgSvc.Config().PluginSettings.RequirePluginSignature {
signature, appErr = ch.srv.fileReader(plugin.signaturePath)
if *s.platform.Config().PluginSettings.RequirePluginSignature {
signature, appErr = s.fileStore.Reader(plugin.signaturePath)
if appErr != nil {
mlog.Error("Failed to open plugin signature from file store.", mlog.Err(appErr))
return
@@ -369,7 +469,7 @@ func (ch *Channels) syncPlugins() *model.AppError {
}
mlog.Info("Syncing plugin from file store", mlog.String("bundle", plugin.path))
if _, err := ch.installPluginLocally(reader, signature, installPluginLocallyAlways); err != nil {
if _, err := s.installPluginLocally(reader, signature, installPluginLocallyAlways); err != nil {
mlog.Error("Failed to sync plugin from file store", mlog.String("bundle", plugin.path), mlog.Err(err))
}
}(plugin)
@@ -379,11 +479,11 @@ func (ch *Channels) syncPlugins() *model.AppError {
return nil
}
func (ch *Channels) ShutDownPlugins() {
func (s *PluginService) ShutDownPlugins() {
// Acquiring lock manually, as plugins might be disabled. See GetPluginsEnvironment.
ch.pluginsLock.RLock()
pluginsEnvironment := ch.pluginsEnvironment
ch.pluginsLock.RUnlock()
s.pluginsLock.RLock()
pluginsEnvironment := s.pluginsEnvironment
s.pluginsLock.RUnlock()
if pluginsEnvironment == nil {
return
}
@@ -392,14 +492,14 @@ func (ch *Channels) ShutDownPlugins() {
pluginsEnvironment.Shutdown()
ch.RemoveConfigListener(ch.pluginConfigListenerID)
ch.pluginConfigListenerID = ""
s.platform.RemoveConfigListener(s.pluginConfigListenerID)
s.pluginConfigListenerID = ""
// Acquiring lock manually before cleaning up PluginsEnvironment.
ch.pluginsLock.Lock()
defer ch.pluginsLock.Unlock()
if ch.pluginsEnvironment == pluginsEnvironment {
ch.pluginsEnvironment = nil
s.pluginsLock.Lock()
defer s.pluginsLock.Unlock()
if s.pluginsEnvironment == pluginsEnvironment {
s.pluginsEnvironment = nil
} else {
mlog.Warn("Another PluginsEnvironment detected while shutting down plugins.")
}
@@ -425,11 +525,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.ch.enablePlugin(id)
return a.PluginService().enablePlugin(id)
}
func (ch *Channels) enablePlugin(id string) *model.AppError {
pluginsEnvironment := ch.GetPluginsEnvironment()
func (s *PluginService) enablePlugin(id string) *model.AppError {
pluginsEnvironment := s.GetPluginsEnvironment()
if pluginsEnvironment == nil {
return model.NewAppError("EnablePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
}
@@ -453,16 +553,16 @@ func (ch *Channels) enablePlugin(id string) *model.AppError {
return model.NewAppError("EnablePlugin", "app.plugin.not_installed.app_error", nil, "", http.StatusNotFound)
}
if id == model.PluginIdFocalboard && ch.cfgSvc.Config().FeatureFlags.BoardsProduct {
if id == model.PluginIdFocalboard && s.platform.Config().FeatureFlags.BoardsProduct {
return model.NewAppError("EnablePlugin", "app.plugin.product_mode.app_error", map[string]any{"Name": model.PluginIdFocalboard}, "", http.StatusBadRequest)
}
ch.cfgSvc.UpdateConfig(func(cfg *model.Config) {
s.platform.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 := ch.cfgSvc.SaveConfig(ch.cfgSvc.Config(), true); err != nil {
if _, _, err := s.platform.SaveConfig(s.platform.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)
}
@@ -475,7 +575,7 @@ func (ch *Channels) 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 {
appErr := a.ch.disablePlugin(id)
appErr := a.ch.srv.pluginService.disablePlugin(id)
if appErr != nil {
return appErr
}
@@ -483,22 +583,22 @@ func (a *App) DisablePlugin(id string) *model.AppError {
return nil
}
func (ch *Channels) disablePlugin(id string) *model.AppError {
func (s *PluginService) disablePlugin(id string) *model.AppError {
// find all collectionTypes registered by plugin
for collectionTypeToRemove, existingPluginId := range ch.collectionTypes {
for collectionTypeToRemove, existingPluginId := range s.collectionTypes {
if existingPluginId != id {
continue
}
// find all topicTypes for existing collectionType
for topicTypeToRemove, existingCollectionType := range ch.topicTypes {
for topicTypeToRemove, existingCollectionType := range s.topicTypes {
if existingCollectionType == collectionTypeToRemove {
delete(ch.topicTypes, topicTypeToRemove)
delete(s.topicTypes, topicTypeToRemove)
}
}
delete(ch.collectionTypes, collectionTypeToRemove)
delete(s.collectionTypes, collectionTypeToRemove)
}
pluginsEnvironment := ch.GetPluginsEnvironment()
pluginsEnvironment := s.GetPluginsEnvironment()
if pluginsEnvironment == nil {
return model.NewAppError("DisablePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
}
@@ -522,13 +622,13 @@ func (ch *Channels) disablePlugin(id string) *model.AppError {
return model.NewAppError("DisablePlugin", "app.plugin.not_installed.app_error", nil, "", http.StatusNotFound)
}
ch.cfgSvc.UpdateConfig(func(cfg *model.Config) {
s.platform.UpdateConfig(func(cfg *model.Config) {
cfg.PluginSettings.PluginStates[id] = &model.PluginState{Enable: false}
})
ch.unregisterPluginCommands(id)
s.unregisterPluginCommands(id)
// This call will implicitly invoke SyncPluginsActiveState which will deactivate disabled plugins.
if _, _, err := ch.cfgSvc.SaveConfig(ch.cfgSvc.Config(), true); err != nil {
if _, _, err := s.platform.SaveConfig(s.platform.Config(), true); err != nil {
return model.NewAppError("DisablePlugin", "app.plugin.config.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
@@ -615,8 +715,8 @@ func (a *App) GetMarketplacePlugins(filter *model.MarketplacePluginFilter) ([]*m
// getPrepackagedPlugin returns a pre-packaged plugin.
//
// If version is empty, the first matching plugin is returned.
func (ch *Channels) getPrepackagedPlugin(pluginID, version string) (*plugin.PrepackagedPlugin, *model.AppError) {
pluginsEnvironment := ch.GetPluginsEnvironment()
func (s *PluginService) getPrepackagedPlugin(pluginID, version string) (*plugin.PrepackagedPlugin, *model.AppError) {
pluginsEnvironment := s.GetPluginsEnvironment()
if pluginsEnvironment == nil {
return nil, model.NewAppError("getPrepackagedPlugin", "app.plugin.config.app_error", nil, "plugin environment is nil", http.StatusInternalServerError)
}
@@ -634,16 +734,16 @@ func (ch *Channels) getPrepackagedPlugin(pluginID, version string) (*plugin.Prep
// getRemoteMarketplacePlugin returns plugin from marketplace-server.
//
// If version is empty, the latest compatible version is used.
func (ch *Channels) getRemoteMarketplacePlugin(pluginID, version string) (*model.BaseMarketplacePlugin, *model.AppError) {
func (s *PluginService) getRemoteMarketplacePlugin(pluginID, version string) (*model.BaseMarketplacePlugin, *model.AppError) {
marketplaceClient, err := marketplace.NewClient(
*ch.cfgSvc.Config().PluginSettings.MarketplaceURL,
ch.srv.HTTPService(),
*s.platform.Config().PluginSettings.MarketplaceURL,
s.httpService,
)
if err != nil {
return nil, model.NewAppError("GetMarketplacePlugin", "app.plugin.marketplace_client.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
filter := ch.getBaseMarketplaceFilter()
filter := s.getBaseMarketplaceFilter()
filter.PluginId = pluginID
var plugin *model.BaseMarketplacePlugin
@@ -798,15 +898,15 @@ func (a *App) mergeLocalPlugins(remoteMarketplacePlugins map[string]*model.Marke
}
func (a *App) getBaseMarketplaceFilter() *model.MarketplacePluginFilter {
return a.ch.getBaseMarketplaceFilter()
return a.ch.srv.pluginService.getBaseMarketplaceFilter()
}
func (ch *Channels) getBaseMarketplaceFilter() *model.MarketplacePluginFilter {
func (s *PluginService) getBaseMarketplaceFilter() *model.MarketplacePluginFilter {
filter := &model.MarketplacePluginFilter{
ServerVersion: model.CurrentVersion,
}
license := ch.srv.License()
license := s.platform.License()
if license != nil && license.HasEnterpriseMarketplacePlugins() {
filter.EnterprisePlugins = true
}
@@ -853,8 +953,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 (ch *Channels) notifyPluginEnabled(manifest *model.Manifest) error {
pluginsEnvironment := ch.GetPluginsEnvironment()
func (s *PluginService) notifyPluginEnabled(manifest *model.Manifest) error {
pluginsEnvironment := s.GetPluginsEnvironment()
if pluginsEnvironment == nil {
return errors.New("pluginsEnvironment is nil")
}
@@ -864,15 +964,15 @@ func (ch *Channels) notifyPluginEnabled(manifest *model.Manifest) error {
var statuses model.PluginStatuses
if ch.srv.platform.Cluster() != nil {
if s.platform.Cluster() != nil {
var err *model.AppError
statuses, err = ch.srv.platform.Cluster().GetPluginStatuses()
statuses, err = s.platform.Cluster().GetPluginStatuses()
if err != nil {
return err
}
}
localStatus, err := ch.GetPluginStatus(manifest.Id)
localStatus, err := s.GetPluginStatus(manifest.Id)
if err != nil {
return err
}
@@ -892,26 +992,26 @@ func (ch *Channels) notifyPluginEnabled(manifest *model.Manifest) error {
// Notify all cluster peer clients.
message := model.NewWebSocketEvent(model.WebsocketEventPluginEnabled, "", "", "", nil, "")
message.Add("manifest", manifest.ClientManifest())
ch.srv.platform.Publish(message)
s.platform.Publish(message)
return nil
}
func (ch *Channels) getPluginsFromFolder() (map[string]*pluginSignaturePath, *model.AppError) {
fileStorePaths, appErr := ch.srv.listDirectory(fileStorePluginFolder, false)
func (s *PluginService) getPluginsFromFolder() (map[string]*pluginSignaturePath, *model.AppError) {
fileStorePaths, appErr := s.fileStore.ListDirectory(fileStorePluginFolder)
if appErr != nil {
return nil, model.NewAppError("getPluginsFromDir", "app.plugin.sync.list_filestore.app_error", nil, "", http.StatusInternalServerError).Wrap(appErr)
}
return ch.getPluginsFromFilePaths(fileStorePaths), nil
return s.getPluginsFromFilePaths(fileStorePaths), nil
}
func (ch *Channels) getPluginsFromFilePaths(fileStorePaths []string) map[string]*pluginSignaturePath {
func (s *PluginService) getPluginsFromFilePaths(fileStorePaths []string) map[string]*pluginSignaturePath {
pluginSignaturePathMap := make(map[string]*pluginSignaturePath)
fsPrefix := ""
if *ch.cfgSvc.Config().FileSettings.DriverName == model.ImageDriverS3 {
ptr := ch.cfgSvc.Config().FileSettings.AmazonS3PathPrefix
if *s.platform.Config().FileSettings.DriverName == model.ImageDriverS3 {
ptr := s.platform.Config().FileSettings.AmazonS3PathPrefix
if ptr != nil && *ptr != "" {
fsPrefix = *ptr + "/"
}
@@ -944,7 +1044,7 @@ func (ch *Channels) getPluginsFromFilePaths(fileStorePaths []string) map[string]
return pluginSignaturePathMap
}
func (ch *Channels) processPrepackagedPlugins(pluginsDir string) []*plugin.PrepackagedPlugin {
func (s *PluginService) processPrepackagedPlugins(pluginsDir string) []*plugin.PrepackagedPlugin {
prepackagedPluginsDir, found := fileutils.FindDir(pluginsDir)
if !found {
return nil
@@ -960,7 +1060,7 @@ func (ch *Channels) processPrepackagedPlugins(pluginsDir string) []*plugin.Prepa
return nil
}
pluginSignaturePathMap := ch.getPluginsFromFilePaths(fileStorePaths)
pluginSignaturePathMap := s.getPluginsFromFilePaths(fileStorePaths)
plugins := make([]*plugin.PrepackagedPlugin, 0, len(pluginSignaturePathMap))
prepackagedPlugins := make(chan *plugin.PrepackagedPlugin, len(pluginSignaturePathMap))
@@ -969,7 +1069,7 @@ func (ch *Channels) processPrepackagedPlugins(pluginsDir string) []*plugin.Prepa
wg.Add(1)
go func(psPath *pluginSignaturePath) {
defer wg.Done()
p, err := ch.processPrepackagedPlugin(psPath)
p, err := s.processPrepackagedPlugin(psPath)
if err != nil {
mlog.Error("Failed to install prepackaged plugin", mlog.String("path", psPath.path), mlog.Err(err))
return
@@ -990,7 +1090,7 @@ func (ch *Channels) processPrepackagedPlugins(pluginsDir string) []*plugin.Prepa
// 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 (ch *Channels) processPrepackagedPlugin(pluginPath *pluginSignaturePath) (*plugin.PrepackagedPlugin, error) {
func (s *PluginService) processPrepackagedPlugin(pluginPath *pluginSignaturePath) (*plugin.PrepackagedPlugin, error) {
mlog.Debug("Processing prepackaged plugin", mlog.String("path", pluginPath.path))
fileReader, err := os.Open(pluginPath.path)
@@ -1011,18 +1111,18 @@ func (ch *Channels) processPrepackagedPlugin(pluginPath *pluginSignaturePath) (*
}
// Skip installing the plugin at all if automatic prepackaged plugins is disabled
if !*ch.cfgSvc.Config().PluginSettings.AutomaticPrepackagedPlugins {
if !*s.platform.Config().PluginSettings.AutomaticPrepackagedPlugins {
return plugin, nil
}
// Skip installing if the plugin is has not been previously enabled.
pluginState := ch.cfgSvc.Config().PluginSettings.PluginStates[plugin.Manifest.Id]
pluginState := s.platform.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 := ch.installExtractedPlugin(plugin.Manifest, pluginDir, installPluginLocallyOnlyIfNewOrUpgrade); err != nil {
if _, err := s.installExtractedPlugin(plugin.Manifest, pluginDir, installPluginLocallyOnlyIfNewOrUpgrade); err != nil {
return nil, errors.Wrapf(err, "Failed to install extracted prepackaged plugin %s", pluginPath.path)
}
@@ -1030,24 +1130,24 @@ func (ch *Channels) processPrepackagedPlugin(pluginPath *pluginSignaturePath) (*
}
// installFeatureFlagPlugins handles the automatic installation/upgrade of plugins from feature flags
func (ch *Channels) installFeatureFlagPlugins() {
ffControledPlugins := ch.cfgSvc.Config().FeatureFlags.Plugins()
func (s *PluginService) installFeatureFlagPlugins() {
ffControledPlugins := s.platform.Config().FeatureFlags.Plugins()
// Respect the automatic prepackaged disable setting
if !*ch.cfgSvc.Config().PluginSettings.AutomaticPrepackagedPlugins {
if !*s.platform.Config().PluginSettings.AutomaticPrepackagedPlugins {
return
}
for pluginID, version := range ffControledPlugins {
// Skip installing if the plugin has been previously disabled.
pluginState := ch.cfgSvc.Config().PluginSettings.PluginStates[pluginID]
pluginState := s.platform.Config().PluginSettings.PluginStates[pluginID]
if pluginState != nil && !pluginState.Enable {
ch.srv.Log().Debug("Not auto installing/upgrade because plugin was disabled", mlog.String("plugin_id", pluginID), mlog.String("version", version))
s.platform.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 := ch.GetPluginStatus(pluginID)
pluginStatus, err := s.GetPluginStatus(pluginID)
pluginExists := err == nil
if pluginExists && pluginStatus.Version == version {
continue
@@ -1055,37 +1155,37 @@ func (ch *Channels) installFeatureFlagPlugins() {
if version != "" && version != "control" {
// If we are on-prem skip installation if this is a downgrade
license := ch.srv.License()
license := s.platform.License()
inCloud := license != nil && *license.Features.Cloud
if !inCloud && pluginExists {
parsedVersion, err := semver.Parse(version)
if err != nil {
ch.srv.Log().Debug("Bad version from feature flag", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", version))
s.platform.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 {
ch.srv.Log().Debug("Bad version from plugin manifest", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", pluginStatus.Version))
s.platform.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) {
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))
s.platform.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 := ch.InstallMarketplacePlugin(&model.InstallMarketplacePluginRequest{
_, err := s.InstallMarketplacePlugin(&model.InstallMarketplacePluginRequest{
Id: pluginID,
Version: version,
})
if err != nil {
ch.srv.Log().Debug("Unable to install plugin from FF manifest", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", version))
s.platform.Log().Debug("Unable to install plugin from FF manifest", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", version))
} else {
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))
if err := s.enablePlugin(pluginID); err != nil {
s.platform.Log().Debug("Unable to enable plugin installed from feature flag.", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", version))
} else {
ch.srv.Log().Debug("Installed and enabled plugin.", mlog.String("plugin_id", pluginID), mlog.String("version", version))
s.platform.Log().Debug("Installed and enabled plugin.", mlog.String("plugin_id", pluginID), mlog.String("version", version))
}
}
}
@@ -1140,15 +1240,15 @@ func getIcon(iconPath string) (string, error) {
return fmt.Sprintf("data:image/svg+xml;base64,%s", base64.StdEncoding.EncodeToString(icon)), nil
}
func (ch *Channels) getPluginStateOverride(pluginID string) (bool, bool) {
func (s *PluginService) getPluginStateOverride(pluginID string) (bool, bool) {
switch pluginID {
case model.PluginIdApps:
// Tie Apps proxy disabled status to the feature flag.
if !ch.cfgSvc.Config().FeatureFlags.AppsEnabled {
if !s.platform.Config().FeatureFlags.AppsEnabled {
return true, false
}
case model.PluginIdCalls:
if !ch.cfgSvc.Config().FeatureFlags.CallsEnabled {
if !s.platform.Config().FeatureFlags.CallsEnabled {
return true, false
}
}

View File

@@ -886,7 +886,7 @@ func (api *PluginAPI) DisablePlugin(id string) *model.AppError {
}
func (api *PluginAPI) RemovePlugin(id string) *model.AppError {
return api.app.Channels().RemovePlugin(id)
return api.app.Srv().pluginService.RemovePlugin(id)
}
func (api *PluginAPI) GetPluginStatus(id string) (*model.PluginStatus, *model.AppError) {
@@ -1235,7 +1235,7 @@ func (api *PluginAPI) GetCloudLimits() (*model.ProductLimits, error) {
// RegisterCollectionAndTopic informs the server that this plugin handles
// the given collection and topic types.
func (api *PluginAPI) RegisterCollectionAndTopic(collectionType, topicType string) error {
return api.app.registerCollectionAndTopic(api.id, collectionType, topicType)
return api.app.Srv().pluginService.registerCollectionAndTopic(api.id, collectionType, topicType)
}
func (api *PluginAPI) CreateUploadSession(us *model.UploadSession) (*model.UploadSession, error) {

View File

@@ -92,7 +92,7 @@ func setupMultiPluginAPITest(t *testing.T, pluginCodes []string, pluginManifests
return app.NewPluginAPI(c, manifest)
}
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(app.Srv()), pluginDir, webappPluginDir, false, app.Log(), nil)
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(app.Srv().Platform()), pluginDir, webappPluginDir, false, app.Log(), nil)
require.NoError(t, err)
require.Equal(t, len(pluginCodes), len(pluginIDs))
@@ -119,7 +119,7 @@ func setupMultiPluginAPITest(t *testing.T, pluginCodes []string, pluginManifests
})
}
app.ch.SetPluginsEnvironment(env)
app.PluginService().SetPluginsEnvironment(env)
return pluginDir
}
@@ -849,7 +849,7 @@ func TestPluginAPIGetPlugins(t *testing.T) {
defer os.RemoveAll(pluginDir)
defer os.RemoveAll(webappPluginDir)
env, err := plugin.NewEnvironment(th.NewPluginAPI, NewDriverImpl(th.Server), pluginDir, webappPluginDir, false, th.App.Log(), nil)
env, err := plugin.NewEnvironment(th.NewPluginAPI, NewDriverImpl(th.Server.Platform()), pluginDir, webappPluginDir, false, th.App.Log(), nil)
require.NoError(t, err)
pluginIDs := []string{"pluginid1", "pluginid2", "pluginid3"}
@@ -866,7 +866,7 @@ func TestPluginAPIGetPlugins(t *testing.T) {
require.True(t, activated)
pluginManifests = append(pluginManifests, manifest)
}
th.App.ch.SetPluginsEnvironment(env)
th.App.PluginService().SetPluginsEnvironment(env)
// Deactivate the last one for testing
success := env.Deactivate(pluginIDs[len(pluginIDs)-1])
@@ -937,10 +937,10 @@ func TestInstallPlugin(t *testing.T) {
return app.NewPluginAPI(c, manifest)
}
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(app.Srv()), pluginDir, webappPluginDir, false, app.Log(), nil)
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(app.Srv().Platform()), pluginDir, webappPluginDir, false, app.Log(), nil)
require.NoError(t, err)
app.ch.SetPluginsEnvironment(env)
app.PluginService().SetPluginsEnvironment(env)
backend := filepath.Join(pluginDir, pluginID, "backend.exe")
utils.CompileGo(t, pluginCode, backend)
@@ -1632,10 +1632,10 @@ func TestAPIMetrics(t *testing.T) {
defer os.RemoveAll(pluginDir)
defer os.RemoveAll(webappPluginDir)
env, err := plugin.NewEnvironment(th.NewPluginAPI, NewDriverImpl(th.Server), pluginDir, webappPluginDir, false, th.App.Log(), metricsMock)
env, err := plugin.NewEnvironment(th.NewPluginAPI, NewDriverImpl(th.Server.Platform()), pluginDir, webappPluginDir, false, th.App.Log(), metricsMock)
require.NoError(t, err)
th.App.ch.SetPluginsEnvironment(env)
th.App.PluginService().SetPluginsEnvironment(env)
pluginID := model.NewId()
backend := filepath.Join(pluginDir, pluginID, "backend.exe")
@@ -2079,10 +2079,10 @@ func TestRegisterCollectionAndTopic(t *testing.T) {
return th.App.NewPluginAPI(th.Context, manifest)
}
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(th.App.Srv()), pluginDir, webappPluginDir, false, th.App.Log(), nil)
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(th.Server.Platform()), pluginDir, webappPluginDir, false, th.App.Log(), nil)
require.NoError(t, err)
th.App.ch.SetPluginsEnvironment(env)
th.App.PluginService().SetPluginsEnvironment(env)
pluginID := "testplugin"
pluginManifest := `{"id": "testplugin", "server": {"executable": "backend.exe"}}`
@@ -2179,10 +2179,10 @@ func TestPluginUploadsAPI(t *testing.T) {
newPluginAPI := func(manifest *model.Manifest) plugin.API {
return th.App.NewPluginAPI(th.Context, manifest)
}
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(th.App.Srv()), pluginDir, webappPluginDir, false, th.App.Log(), nil)
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(th.Server.Platform()), pluginDir, webappPluginDir, false, th.App.Log(), nil)
require.NoError(t, err)
th.App.ch.SetPluginsEnvironment(env)
th.App.PluginService().SetPluginsEnvironment(env)
pluginID := "testplugin"
pluginManifest := `{"id": "testplugin", "server": {"executable": "backend.exe"}}`

View File

@@ -22,6 +22,10 @@ type PluginCommand struct {
}
func (a *App) RegisterPluginCommand(pluginID string, command *model.Command) error {
return a.Srv().pluginService.registerPluginCommand(pluginID, command)
}
func (s *PluginService) registerPluginCommand(pluginID string, command *model.Command) error {
if command.Trigger == "" {
return errors.New("invalid command")
}
@@ -55,10 +59,10 @@ func (a *App) RegisterPluginCommand(pluginID string, command *model.Command) err
AutocompleteIconData: command.AutocompleteIconData,
}
a.ch.pluginCommandsLock.Lock()
defer a.ch.pluginCommandsLock.Unlock()
s.pluginCommandsLock.Lock()
defer s.pluginCommandsLock.Unlock()
for _, pc := range a.ch.pluginCommands {
for _, pc := range s.pluginCommands {
if pc.Command.Trigger == command.Trigger && pc.Command.TeamId == command.TeamId {
if pc.PluginId == pluginID {
pc.Command = command
@@ -67,7 +71,7 @@ func (a *App) RegisterPluginCommand(pluginID string, command *model.Command) err
}
}
a.ch.pluginCommands = append(a.ch.pluginCommands, &PluginCommand{
s.pluginCommands = append(s.pluginCommands, &PluginCommand{
Command: command,
PluginId: pluginID,
})
@@ -75,39 +79,47 @@ func (a *App) RegisterPluginCommand(pluginID string, command *model.Command) err
}
func (a *App) UnregisterPluginCommand(pluginID, teamID, trigger string) {
a.Srv().pluginService.unregisterPluginCommand(pluginID, teamID, trigger)
}
func (s *PluginService) unregisterPluginCommand(pluginID, teamID, trigger string) {
trigger = strings.ToLower(trigger)
a.ch.pluginCommandsLock.Lock()
defer a.ch.pluginCommandsLock.Unlock()
s.pluginCommandsLock.Lock()
defer s.pluginCommandsLock.Unlock()
var remaining []*PluginCommand
for _, pc := range a.ch.pluginCommands {
for _, pc := range s.pluginCommands {
if pc.Command.TeamId != teamID || pc.Command.Trigger != trigger {
remaining = append(remaining, pc)
}
}
a.ch.pluginCommands = remaining
s.pluginCommands = remaining
}
func (ch *Channels) unregisterPluginCommands(pluginID string) {
ch.pluginCommandsLock.Lock()
defer ch.pluginCommandsLock.Unlock()
func (s *PluginService) unregisterPluginCommands(pluginID string) {
s.pluginCommandsLock.Lock()
defer s.pluginCommandsLock.Unlock()
var remaining []*PluginCommand
for _, pc := range ch.pluginCommands {
for _, pc := range s.pluginCommands {
if pc.PluginId != pluginID {
remaining = append(remaining, pc)
}
}
ch.pluginCommands = remaining
s.pluginCommands = remaining
}
func (a *App) PluginCommandsForTeam(teamID string) []*model.Command {
a.ch.pluginCommandsLock.RLock()
defer a.ch.pluginCommandsLock.RUnlock()
return a.Srv().pluginService.PluginCommandsForTeam(teamID)
}
func (s *PluginService) PluginCommandsForTeam(teamID string) []*model.Command {
s.pluginCommandsLock.RLock()
defer s.pluginCommandsLock.RUnlock()
var commands []*model.Command
for _, pc := range a.ch.pluginCommands {
for _, pc := range s.pluginCommands {
if pc.Command.TeamId == "" || pc.Command.TeamId == teamID {
commands = append(commands, pc.Command)
}
@@ -115,6 +127,24 @@ func (a *App) PluginCommandsForTeam(teamID string) []*model.Command {
return commands
}
func (s *PluginService) getPluginCommandFromArgs(args *model.CommandArgs) *PluginCommand {
parts := strings.Split(args.Command, " ")
trigger := parts[0][1:]
trigger = strings.ToLower(trigger)
var matched *PluginCommand
s.pluginCommandsLock.RLock()
for _, pc := range s.pluginCommands {
if (pc.Command.TeamId == "" || pc.Command.TeamId == args.TeamId) && pc.Command.Trigger == trigger {
matched = pc
break
}
}
s.pluginCommandsLock.RUnlock()
return matched
}
// tryExecutePluginCommand attempts to run a command provided by a plugin based on the given arguments. If no such
// command can be found, returns nil for all arguments.
func (a *App) tryExecutePluginCommand(c request.CTX, args *model.CommandArgs) (*model.Command, *model.CommandResponse, *model.AppError) {
@@ -122,15 +152,7 @@ func (a *App) tryExecutePluginCommand(c request.CTX, args *model.CommandArgs) (*
trigger := parts[0][1:]
trigger = strings.ToLower(trigger)
var matched *PluginCommand
a.ch.pluginCommandsLock.RLock()
for _, pc := range a.ch.pluginCommands {
if (pc.Command.TeamId == "" || pc.Command.TeamId == args.TeamId) && pc.Command.Trigger == trigger {
matched = pc
break
}
}
a.ch.pluginCommandsLock.RUnlock()
matched := a.Srv().pluginService.getPluginCommandFromArgs(args)
if matched == nil {
return nil, nil, nil
}

View File

@@ -106,7 +106,7 @@ func TestPluginCommand(t *testing.T) {
require.NotEqual(t, "plugin", commands.Trigger)
}
th.App.ch.RemovePlugin(pluginIDs[0])
th.App.PluginService().RemovePlugin(pluginIDs[0])
})
t.Run("re-entrant command registration on config change", func(t *testing.T) {
@@ -207,7 +207,7 @@ func TestPluginCommand(t *testing.T) {
killed = true
}
th.App.ch.RemovePlugin(pluginIDs[0])
th.App.PluginService().RemovePlugin(pluginIDs[0])
require.False(t, killed, "execute command appears to have deadlocked")
})
@@ -285,7 +285,7 @@ func TestPluginCommand(t *testing.T) {
require.Equal(t, model.CommandResponseTypeEphemeral, resp.ResponseType)
require.Equal(t, "text", resp.Text)
th.App.ch.RemovePlugin(pluginIDs[0])
th.App.PluginService().RemovePlugin(pluginIDs[0])
})
t.Run("plugin has crashed before execution of command", func(t *testing.T) {
tearDown, pluginIDs, activationErrors := SetAppEnvironmentWithPlugins(t, []string{`
@@ -329,7 +329,7 @@ func TestPluginCommand(t *testing.T) {
require.Nil(t, resp)
require.NotNil(t, err)
require.Equal(t, err.Id, "model.plugin_command_error.error.app_error")
th.App.ch.RemovePlugin(pluginIDs[0])
th.App.PluginService().RemovePlugin(pluginIDs[0])
})
t.Run("plugin has crashed due to the execution of the command", func(t *testing.T) {
@@ -374,7 +374,7 @@ func TestPluginCommand(t *testing.T) {
require.Nil(t, resp)
require.NotNil(t, err)
require.Equal(t, err.Id, "model.plugin_command_crash.error.app_error")
th.App.ch.RemovePlugin(pluginIDs[0])
th.App.PluginService().RemovePlugin(pluginIDs[0])
})
t.Run("plugin returning status code 0", func(t *testing.T) {

View File

@@ -10,6 +10,7 @@ import (
"sync"
"time"
"github.com/mattermost/mattermost-server/v6/app/platform"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/plugin"
)
@@ -19,7 +20,7 @@ import (
// a new entry tracked centrally in a map. Further requests operate on the
// object ID.
type DriverImpl struct {
s *Server
ps *platform.PlatformService
connMut sync.RWMutex
connMap map[string]*sql.Conn
txMut sync.Mutex
@@ -30,9 +31,9 @@ type DriverImpl struct {
rowsMap map[string]driver.Rows
}
func NewDriverImpl(s *Server) *DriverImpl {
func NewDriverImpl(s *platform.PlatformService) *DriverImpl {
return &DriverImpl{
s: s,
ps: s,
connMap: make(map[string]*sql.Conn),
txMap: make(map[string]driver.Tx),
stMap: make(map[string]driver.Stmt),
@@ -41,11 +42,11 @@ func NewDriverImpl(s *Server) *DriverImpl {
}
func (d *DriverImpl) Conn(isMaster bool) (string, error) {
dbFunc := d.s.Platform().Store.GetInternalMasterDB
dbFunc := d.ps.Store.GetInternalMasterDB
if !isMaster {
dbFunc = d.s.Platform().Store.GetInternalReplicaDB
dbFunc = d.ps.Store.GetInternalReplicaDB
}
timeout := time.Duration(*d.s.Config().SqlSettings.QueryTimeout) * time.Second
timeout := time.Duration(*d.ps.Config().SqlSettings.QueryTimeout) * time.Second
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
conn, err := dbFunc().Conn(ctx)

View File

@@ -15,7 +15,7 @@ func TestConnCreateTimeout(t *testing.T) {
*th.App.Config().SqlSettings.QueryTimeout = 0
d := NewDriverImpl(th.Server)
d := NewDriverImpl(th.Server.platform)
_, err := d.Conn(true)
require.Error(t, err)
}

View File

@@ -9,10 +9,10 @@ import (
"github.com/mattermost/mattermost-server/v6/model"
)
func (ch *Channels) notifyClusterPluginEvent(event model.ClusterEvent, data model.PluginEventData) {
func (s *PluginService) notifyClusterPluginEvent(event model.ClusterEvent, data model.PluginEventData) {
buf, _ := json.Marshal(data)
if ch.srv.platform.Cluster() != nil {
ch.srv.platform.Cluster().SendClusterMessage(&model.ClusterMessage{
if s.platform.Cluster() != nil {
s.platform.Cluster().SendClusterMessage(&model.ClusterMessage{
Event: event,
SendType: model.ClusterSendReliable,
WaitForAllToSend: true,

View File

@@ -33,10 +33,10 @@ func SetAppEnvironmentWithPlugins(t *testing.T, pluginCode []string, app *App, a
webappPluginDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
env, err := plugin.NewEnvironment(apiFunc, NewDriverImpl(app.Srv()), pluginDir, webappPluginDir, false, app.Log(), nil)
env, err := plugin.NewEnvironment(apiFunc, NewDriverImpl(app.Srv().Platform()), pluginDir, webappPluginDir, false, app.Log(), nil)
require.NoError(t, err)
app.ch.SetPluginsEnvironment(env)
app.PluginService().SetPluginsEnvironment(env)
pluginIDs := []string{}
activationErrors := []error{}
for _, code := range pluginCode {
@@ -1030,10 +1030,10 @@ func TestHookMetrics(t *testing.T) {
defer os.RemoveAll(pluginDir)
defer os.RemoveAll(webappPluginDir)
env, err := plugin.NewEnvironment(th.NewPluginAPI, NewDriverImpl(th.Server), pluginDir, webappPluginDir, false, th.App.Log(), metricsMock)
env, err := plugin.NewEnvironment(th.NewPluginAPI, NewDriverImpl(th.Server.Platform()), pluginDir, webappPluginDir, false, th.App.Log(), metricsMock)
require.NoError(t, err)
th.App.ch.SetPluginsEnvironment(env)
th.App.PluginService().SetPluginsEnvironment(env)
pluginID := model.NewId()
backend := filepath.Join(pluginDir, pluginID, "backend.exe")
@@ -1234,7 +1234,7 @@ func TestHookRunDataRetention(t *testing.T) {
require.True(t, th.App.GetPluginsEnvironment().IsActive(pluginID))
hookCalled := false
th.App.Channels().RunMultiHook(func(hooks plugin.Hooks) bool {
th.App.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
n, _ := hooks.RunDataRetention(0, 0)
// Ensure return it correct
assert.Equal(t, int64(100), n)
@@ -1278,7 +1278,7 @@ func TestHookOnSendDailyTelemetry(t *testing.T) {
require.True(t, th.App.GetPluginsEnvironment().IsActive(pluginID))
hookCalled := false
th.App.Channels().RunMultiHook(func(hooks plugin.Hooks) bool {
th.App.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.OnSendDailyTelemetry()
hookCalled = true
@@ -1322,7 +1322,7 @@ func TestHookOnCloudLimitsUpdated(t *testing.T) {
require.True(t, th.App.GetPluginsEnvironment().IsActive(pluginID))
hookCalled := false
th.App.Channels().RunMultiHook(func(hooks plugin.Hooks) bool {
th.App.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.OnCloudLimitsUpdated(nil)
hookCalled = true

View File

@@ -58,10 +58,10 @@ const managedPluginFileName = ".filestore"
// fileStorePluginFolder is the folder name in the file store of the plugin bundles installed.
const fileStorePluginFolder = "plugins"
func (ch *Channels) installPluginFromData(data model.PluginEventData) {
func (s *PluginService) installPluginFromData(data model.PluginEventData) {
mlog.Debug("Installing plugin as per cluster message", mlog.String("plugin_id", data.Id))
pluginSignaturePathMap, appErr := ch.getPluginsFromFolder()
pluginSignaturePathMap, appErr := s.getPluginsFromFolder()
if appErr != nil {
mlog.Error("Failed to get plugin signatures from filestore. Can't install plugin from data.", mlog.Err(appErr))
return
@@ -72,53 +72,53 @@ func (ch *Channels) installPluginFromData(data model.PluginEventData) {
return
}
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))
reader, err := s.fileStore.Reader(plugin.path)
if err != nil {
mlog.Error("Failed to open plugin bundle from file store.", mlog.String("bundle", plugin.path), mlog.Err(err))
return
}
defer reader.Close()
var signature filestore.ReadCloseSeeker
if *ch.cfgSvc.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))
if *s.platform.Config().PluginSettings.RequirePluginSignature {
signature, err = s.fileStore.Reader(plugin.signaturePath)
if err != nil {
mlog.Error("Failed to open plugin signature from file store.", mlog.Err(err))
return
}
defer signature.Close()
}
manifest, appErr := ch.installPluginLocally(reader, signature, installPluginLocallyAlways)
manifest, appErr := s.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 := ch.notifyPluginEnabled(manifest); err != nil {
mlog.Error("Failed notify plugin enabled", mlog.Err(err))
if err2 := s.notifyPluginEnabled(manifest); err2 != nil {
mlog.Error("Failed notify plugin enabled", mlog.Err(err2))
}
if err := ch.notifyPluginStatusesChanged(); err != nil {
mlog.Error("Failed to notify plugin status changed", mlog.Err(err))
if err2 := s.notifyPluginStatusesChanged(); err2 != nil {
mlog.Error("Failed to notify plugin status changed", mlog.Err(err2))
}
}
func (ch *Channels) removePluginFromData(data model.PluginEventData) {
func (s *PluginService) removePluginFromData(data model.PluginEventData) {
mlog.Debug("Removing plugin as per cluster message", mlog.String("plugin_id", data.Id))
if err := ch.removePluginLocally(data.Id); err != nil {
if err := s.removePluginLocally(data.Id); err != nil {
mlog.Warn("Failed to remove plugin locally", mlog.Err(err), mlog.String("id", data.Id))
}
if err := ch.notifyPluginStatusesChanged(); err != nil {
if err := s.notifyPluginStatusesChanged(); err != nil {
mlog.Warn("failed to notify plugin status changed", mlog.Err(err))
}
}
// InstallPluginWithSignature verifies and installs plugin.
func (ch *Channels) installPluginWithSignature(pluginFile, signature io.ReadSeeker) (*model.Manifest, *model.AppError) {
return ch.installPlugin(pluginFile, signature, installPluginLocallyAlways)
func (s *PluginService) installPluginWithSignature(pluginFile, signature io.ReadSeeker) (*model.Manifest, *model.AppError) {
return s.installPlugin(pluginFile, signature, installPluginLocallyAlways)
}
// InstallPlugin unpacks and installs a plugin but does not enable or activate it.
@@ -132,40 +132,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.ch.installPlugin(pluginFile, signature, installationStrategy)
return a.ch.srv.pluginService.installPlugin(pluginFile, signature, installationStrategy)
}
func (ch *Channels) installPlugin(pluginFile, signature io.ReadSeeker, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) {
manifest, appErr := ch.installPluginLocally(pluginFile, signature, installationStrategy)
func (s *PluginService) installPlugin(pluginFile, signature io.ReadSeeker, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) {
manifest, appErr := s.installPluginLocally(pluginFile, signature, installationStrategy)
if appErr != nil {
return nil, appErr
}
if signature != nil {
signature.Seek(0, 0)
if _, appErr = ch.srv.writeFile(signature, getSignatureStorePath(manifest.Id)); appErr != nil {
return nil, model.NewAppError("saveSignature", "app.plugin.store_signature.app_error", nil, "", http.StatusInternalServerError).Wrap(appErr)
if _, err := s.fileStore.WriteFile(signature, getSignatureStorePath(manifest.Id)); err != nil {
return nil, model.NewAppError("saveSignature", "app.plugin.store_signature.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}
// Store bundle in the file store to allow access from other servers.
pluginFile.Seek(0, 0)
if _, appErr := ch.srv.writeFile(pluginFile, getBundleStorePath(manifest.Id)); appErr != nil {
if _, appErr := s.fileStore.WriteFile(pluginFile, getBundleStorePath(manifest.Id)); appErr != nil {
return nil, model.NewAppError("uploadPlugin", "app.plugin.store_bundle.app_error", nil, "", http.StatusInternalServerError).Wrap(appErr)
}
ch.notifyClusterPluginEvent(
s.notifyClusterPluginEvent(
model.ClusterEventInstallPlugin,
model.PluginEventData{
Id: manifest.Id,
},
)
if err := ch.notifyPluginEnabled(manifest); err != nil {
if err := s.notifyPluginEnabled(manifest); err != nil {
mlog.Warn("Failed notify plugin enabled", mlog.Err(err))
}
if err := ch.notifyPluginStatusesChanged(); err != nil {
if err := s.notifyPluginStatusesChanged(); err != nil {
mlog.Warn("Failed to notify plugin status changed", mlog.Err(err))
}
@@ -174,10 +174,10 @@ func (ch *Channels) installPlugin(pluginFile, signature io.ReadSeeker, installat
// 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 (ch *Channels) InstallMarketplacePlugin(request *model.InstallMarketplacePluginRequest) (*model.Manifest, *model.AppError) {
func (s *PluginService) InstallMarketplacePlugin(request *model.InstallMarketplacePluginRequest) (*model.Manifest, *model.AppError) {
var pluginFile, signatureFile io.ReadSeeker
prepackagedPlugin, appErr := ch.getPrepackagedPlugin(request.Id, request.Version)
prepackagedPlugin, appErr := s.getPrepackagedPlugin(request.Id, request.Version)
if appErr != nil && appErr.Id != "app.plugin.marketplace_plugins.not_found.app_error" {
return nil, appErr
}
@@ -192,9 +192,9 @@ func (ch *Channels) InstallMarketplacePlugin(request *model.InstallMarketplacePl
signatureFile = bytes.NewReader(prepackagedPlugin.Signature)
}
if *ch.cfgSvc.Config().PluginSettings.EnableRemoteMarketplace {
if *s.platform.Config().PluginSettings.EnableRemoteMarketplace {
var plugin *model.BaseMarketplacePlugin
plugin, appErr = ch.getRemoteMarketplacePlugin(request.Id, request.Version)
plugin, appErr = s.getRemoteMarketplacePlugin(request.Id, request.Version)
if appErr != nil {
return nil, appErr
}
@@ -214,7 +214,7 @@ func (ch *Channels) InstallMarketplacePlugin(request *model.InstallMarketplacePl
}
if prepackagedVersion.LT(marketplaceVersion) { // Always true if no prepackaged plugin was found
downloadedPluginBytes, err := ch.srv.downloadFromURL(plugin.DownloadURL)
downloadedPluginBytes, err := s.downloadFromURL(plugin.DownloadURL)
if err != nil {
return nil, model.NewAppError("InstallMarketplacePlugin", "app.plugin.install_marketplace_plugin.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
@@ -234,7 +234,7 @@ func (ch *Channels) InstallMarketplacePlugin(request *model.InstallMarketplacePl
return nil, model.NewAppError("InstallMarketplacePlugin", "app.plugin.marketplace_plugins.signature_not_found.app_error", nil, "", http.StatusInternalServerError)
}
manifest, appErr := ch.installPluginWithSignature(pluginFile, signatureFile)
manifest, appErr := s.installPluginWithSignature(pluginFile, signatureFile)
if appErr != nil {
return nil, appErr
}
@@ -253,15 +253,15 @@ const (
installPluginLocallyAlways
)
func (ch *Channels) installPluginLocally(pluginFile, signature io.ReadSeeker, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) {
pluginsEnvironment := ch.GetPluginsEnvironment()
func (s *PluginService) installPluginLocally(pluginFile, signature io.ReadSeeker, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) {
pluginsEnvironment := s.GetPluginsEnvironment()
if pluginsEnvironment == nil {
return nil, model.NewAppError("installPluginLocally", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
}
// verify signature
if signature != nil {
if err := ch.verifyPlugin(pluginFile, signature); err != nil {
if err := s.verifyPlugin(pluginFile, signature); err != nil {
return nil, err
}
}
@@ -277,7 +277,7 @@ func (ch *Channels) installPluginLocally(pluginFile, signature io.ReadSeeker, in
return nil, appErr
}
manifest, appErr = ch.installExtractedPlugin(manifest, pluginDir, installationStrategy)
manifest, appErr = s.installExtractedPlugin(manifest, pluginDir, installationStrategy)
if appErr != nil {
return nil, appErr
}
@@ -312,8 +312,8 @@ func extractPlugin(pluginFile io.ReadSeeker, extractDir string) (*model.Manifest
return manifest, extractDir, nil
}
func (ch *Channels) installExtractedPlugin(manifest *model.Manifest, fromPluginDir string, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) {
pluginsEnvironment := ch.GetPluginsEnvironment()
func (s *PluginService) installExtractedPlugin(manifest *model.Manifest, fromPluginDir string, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) {
pluginsEnvironment := s.GetPluginsEnvironment()
if pluginsEnvironment == nil {
return nil, model.NewAppError("installExtractedPlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
}
@@ -360,12 +360,12 @@ func (ch *Channels) installExtractedPlugin(manifest *model.Manifest, fromPluginD
// 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 := ch.removePluginLocally(existingManifest.Id); err != nil {
if err := s.removePluginLocally(existingManifest.Id); err != nil {
return nil, model.NewAppError("installExtractedPlugin", "app.plugin.install_id_failed_remove.app_error", nil, "", http.StatusBadRequest)
}
}
pluginPath := filepath.Join(*ch.cfgSvc.Config().PluginSettings.Directory, manifest.Id)
pluginPath := filepath.Join(*s.platform.Config().PluginSettings.Directory, manifest.Id)
err = utils.CopyDir(fromPluginDir, pluginPath)
if err != nil {
return nil, model.NewAppError("installExtractedPlugin", "app.plugin.mvdir.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
@@ -387,9 +387,9 @@ func (ch *Channels) installExtractedPlugin(manifest *model.Manifest, fromPluginD
}
// Activate the plugin if enabled.
pluginState := ch.cfgSvc.Config().PluginSettings.PluginStates[manifest.Id]
pluginState := s.platform.Config().PluginSettings.PluginStates[manifest.Id]
if pluginState != nil && pluginState.Enable {
if hasOverride, enabled := ch.getPluginStateOverride(manifest.Id); hasOverride && !enabled {
if hasOverride, enabled := s.getPluginStateOverride(manifest.Id); hasOverride && !enabled {
return manifest, nil
}
@@ -405,49 +405,49 @@ func (ch *Channels) installExtractedPlugin(manifest *model.Manifest, fromPluginD
return manifest, nil
}
func (ch *Channels) RemovePlugin(id string) *model.AppError {
func (s *PluginService) RemovePlugin(id string) *model.AppError {
// Disable plugin before removal to make sure this
// plugin remains disabled on re-install.
if err := ch.disablePlugin(id); err != nil {
if err := s.disablePlugin(id); err != nil {
return err
}
if err := ch.removePluginLocally(id); err != nil {
if err := s.removePluginLocally(id); err != nil {
return err
}
// Remove bundle from the file store.
storePluginFileName := getBundleStorePath(id)
bundleExist, err := ch.srv.fileExists(storePluginFileName)
bundleExist, err := s.fileStore.FileExists(storePluginFileName)
if err != nil {
return model.NewAppError("removePlugin", "app.plugin.remove_bundle.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
if !bundleExist {
return nil
}
if err = ch.srv.removeFile(storePluginFileName); err != nil {
if err = s.fileStore.RemoveFile(storePluginFileName); err != nil {
return model.NewAppError("removePlugin", "app.plugin.remove_bundle.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
if err = ch.removeSignature(id); err != nil {
mlog.Warn("Can't remove signature", mlog.Err(err))
if err2 := s.removeSignature(id); err2 != nil {
mlog.Warn("Can't remove signature", mlog.Err(err2))
}
ch.notifyClusterPluginEvent(
s.notifyClusterPluginEvent(
model.ClusterEventRemovePlugin,
model.PluginEventData{
Id: id,
},
)
if err := ch.notifyPluginStatusesChanged(); err != nil {
if err := s.notifyPluginStatusesChanged(); err != nil {
mlog.Warn("Failed to notify plugin status changed", mlog.Err(err))
}
return nil
}
func (ch *Channels) removePluginLocally(id string) *model.AppError {
pluginsEnvironment := ch.GetPluginsEnvironment()
func (s *PluginService) removePluginLocally(id string) *model.AppError {
pluginsEnvironment := s.GetPluginsEnvironment()
if pluginsEnvironment == nil {
return model.NewAppError("removePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
}
@@ -473,7 +473,7 @@ func (ch *Channels) removePluginLocally(id string) *model.AppError {
pluginsEnvironment.Deactivate(id)
pluginsEnvironment.RemovePlugin(id)
ch.unregisterPluginCommands(id)
s.unregisterPluginCommands(id)
if err := os.RemoveAll(pluginPath); err != nil {
return model.NewAppError("removePlugin", "app.plugin.remove.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
@@ -482,9 +482,9 @@ func (ch *Channels) removePluginLocally(id string) *model.AppError {
return nil
}
func (ch *Channels) removeSignature(pluginID string) *model.AppError {
func (s *PluginService) removeSignature(pluginID string) *model.AppError {
filePath := getSignatureStorePath(pluginID)
exists, err := ch.srv.fileExists(filePath)
exists, err := s.fileStore.FileExists(filePath)
if err != nil {
return model.NewAppError("removeSignature", "app.plugin.remove_bundle.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
@@ -492,7 +492,7 @@ func (ch *Channels) removeSignature(pluginID string) *model.AppError {
mlog.Debug("no plugin signature to remove", mlog.String("plugin_id", pluginID))
return nil
}
if err = ch.srv.removeFile(filePath); err != nil {
if err = s.fileStore.RemoveFile(filePath); err != nil {
return model.NewAppError("removeSignature", "app.plugin.remove_bundle.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
return nil

View File

@@ -73,7 +73,7 @@ func TestInstallPluginLocally(t *testing.T) {
th := Setup(t)
defer th.TearDown()
actualManifest, appErr := th.App.ch.installPluginLocally(&nilReadSeeker{}, nil, installPluginLocallyOnlyIfNew)
actualManifest, appErr := th.App.PluginService().installPluginLocally(&nilReadSeeker{}, nil, installPluginLocallyOnlyIfNew)
require.NotNil(t, appErr)
assert.Equal(t, "app.plugin.extract.app_error", appErr.Id, appErr.Error())
require.Nil(t, actualManifest)
@@ -87,7 +87,7 @@ func TestInstallPluginLocally(t *testing.T) {
{"test", "test file"},
})
actualManifest, appErr := th.App.ch.installPluginLocally(reader, nil, installPluginLocallyOnlyIfNew)
actualManifest, appErr := th.App.PluginService().installPluginLocally(reader, nil, installPluginLocallyOnlyIfNew)
require.NotNil(t, appErr)
assert.Equal(t, "app.plugin.manifest.app_error", appErr.Id, appErr.Error())
require.Nil(t, actualManifest)
@@ -106,7 +106,7 @@ func TestInstallPluginLocally(t *testing.T) {
{"plugin.json", string(manifestJSON)},
})
actualManifest, appError := th.App.ch.installPluginLocally(reader, nil, installationStrategy)
actualManifest, appError := th.App.PluginService().installPluginLocally(reader, nil, installationStrategy)
if actualManifest != nil {
require.Equal(t, manifest, actualManifest)
}
@@ -134,7 +134,7 @@ func TestInstallPluginLocally(t *testing.T) {
require.NoError(t, err)
for _, bundleInfo := range bundleInfos {
err := th.App.ch.removePluginLocally(bundleInfo.Manifest.Id)
err := th.App.PluginService().removePluginLocally(bundleInfo.Manifest.Id)
require.Nilf(t, err, "failed to remove existing plugin %s", bundleInfo.Manifest.Id)
}
}

View File

@@ -20,16 +20,16 @@ import (
"github.com/mattermost/mattermost-server/v6/utils"
)
func (ch *Channels) ServePluginRequest(w http.ResponseWriter, r *http.Request) {
func (s *PluginService) ServePluginRequest(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
if handler, ok := ch.routerSvc.getHandler(params["plugin_id"]); ok {
ch.servePluginRequest(w, r, func(*plugin.Context, http.ResponseWriter, *http.Request) {
if handler, ok := s.channels.routerSvc.getHandler(params["plugin_id"]); ok {
s.servePluginRequest(w, r, func(*plugin.Context, http.ResponseWriter, *http.Request) {
handler.ServeHTTP(w, r)
})
return
}
pluginsEnvironment := ch.GetPluginsEnvironment()
pluginsEnvironment := s.GetPluginsEnvironment()
if pluginsEnvironment == nil {
err := model.NewAppError("ServePluginRequest", "app.plugin.disabled.app_error", nil, "Enable plugins to serve plugin requests", http.StatusNotImplemented)
mlog.Error(err.Error())
@@ -49,11 +49,11 @@ func (ch *Channels) ServePluginRequest(w http.ResponseWriter, r *http.Request) {
return
}
ch.servePluginRequest(w, r, hooks.ServeHTTP)
s.servePluginRequest(w, r, hooks.ServeHTTP)
}
func (a *App) ServeInterPluginRequest(w http.ResponseWriter, r *http.Request, sourcePluginId, destinationPluginId string) {
pluginsEnvironment := a.ch.GetPluginsEnvironment()
pluginsEnvironment := a.ch.srv.pluginService.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())
@@ -87,7 +87,7 @@ func (a *App) ServeInterPluginRequest(w http.ResponseWriter, r *http.Request, so
// ServePluginPublicRequest serves public plugin files
// at the URL http(s)://$SITE_URL/plugins/$PLUGIN_ID/public/{anything}
func (ch *Channels) ServePluginPublicRequest(w http.ResponseWriter, r *http.Request) {
func (s *PluginService) ServePluginPublicRequest(w http.ResponseWriter, r *http.Request) {
if strings.HasSuffix(r.URL.Path, "/") {
http.NotFound(w, r)
return
@@ -97,7 +97,7 @@ func (ch *Channels) ServePluginPublicRequest(w http.ResponseWriter, r *http.Requ
vars := mux.Vars(r)
pluginID := vars["plugin_id"]
pluginsEnv := ch.GetPluginsEnvironment()
pluginsEnv := s.GetPluginsEnvironment()
// Check if someone has nullified the pluginsEnv in the meantime
if pluginsEnv == nil {
@@ -121,11 +121,11 @@ func (ch *Channels) ServePluginPublicRequest(w http.ResponseWriter, r *http.Requ
http.ServeFile(w, r, publicFile)
}
func (ch *Channels) servePluginRequest(w http.ResponseWriter, r *http.Request, handler func(*plugin.Context, http.ResponseWriter, *http.Request)) {
func (s *PluginService) servePluginRequest(w http.ResponseWriter, r *http.Request, handler func(*plugin.Context, http.ResponseWriter, *http.Request)) {
token := ""
context := &plugin.Context{
RequestId: model.NewId(),
IPAddress: utils.GetIPAddress(r, ch.cfgSvc.Config().ServiceSettings.TrustedProxyIPHeader),
IPAddress: utils.GetIPAddress(r, s.platform.Config().ServiceSettings.TrustedProxyIPHeader),
AcceptLanguage: r.Header.Get("Accept-Language"),
UserAgent: r.UserAgent(),
}
@@ -148,8 +148,8 @@ func (ch *Channels) servePluginRequest(w http.ResponseWriter, r *http.Request, h
r.Header.Del("Mattermost-User-Id")
if token != "" {
session, err := New(ServerConnector(ch)).GetSession(token)
defer ch.srv.platform.ReturnSessionToPool(session)
session, err := New(ServerConnector(s.channels)).GetSession(token)
defer s.platform.ReturnSessionToPool(session)
csrfCheckPassed := false
@@ -190,7 +190,7 @@ func (ch *Channels) servePluginRequest(w http.ResponseWriter, r *http.Request, h
mlog.String("user_id", userID),
}
if *ch.cfgSvc.Config().ServiceSettings.ExperimentalStrictCSRFEnforcement {
if *s.platform.Config().ServiceSettings.ExperimentalStrictCSRFEnforcement {
mlog.Warn(csrfErrorMessage, fields...)
} else {
mlog.Debug(csrfErrorMessage, fields...)
@@ -219,7 +219,7 @@ func (ch *Channels) servePluginRequest(w http.ResponseWriter, r *http.Request, h
params := mux.Vars(r)
subpath, _ := utils.GetSubpathFromConfig(ch.cfgSvc.Config())
subpath, _ := utils.GetSubpathFromConfig(s.platform.Config())
newQuery := r.URL.Query()
newQuery.Del("access_token")

View File

@@ -24,7 +24,7 @@ func TestServePluginPublicRequest(t *testing.T) {
require.NoError(t, err)
rr := httptest.NewRecorder()
handler := http.HandlerFunc(th.App.ch.ServePluginPublicRequest)
handler := http.HandlerFunc(th.App.PluginService().ServePluginPublicRequest)
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusNotFound, rr.Code)

View File

@@ -63,7 +63,7 @@ func TestPluginShutdownTest(t *testing.T) {
done := make(chan bool)
go func() {
defer close(done)
th.App.ch.ShutDownPlugins()
th.App.PluginService().ShutDownPlugins()
}()
select {

View File

@@ -73,16 +73,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.ch.verifyPlugin(plugin, signature)
return a.ch.srv.pluginService.verifyPlugin(plugin, signature)
}
func (ch *Channels) verifyPlugin(plugin, signature io.ReadSeeker) *model.AppError {
func (s *PluginService) verifyPlugin(plugin, signature io.ReadSeeker) *model.AppError {
if err := verifySignature(bytes.NewReader(mattermostPluginPublicKey), plugin, signature); err == nil {
return nil
}
publicKeys := ch.cfgSvc.Config().PluginSettings.SignaturePublicKeyFiles
publicKeys := s.platform.Config().PluginSettings.SignaturePublicKeyFiles
for _, pk := range publicKeys {
pkBytes, appErr := ch.srv.getPublicKey(pk)
pkBytes, appErr := s.platform.GetConfigFile(pk)
if appErr != nil {
mlog.Warn("Unable to get public key for ", mlog.String("filename", pk))
continue

View File

@@ -10,8 +10,8 @@ import (
)
// GetPluginStatus returns the status for a plugin installed on this server.
func (ch *Channels) GetPluginStatus(id string) (*model.PluginStatus, *model.AppError) {
pluginsEnvironment := ch.GetPluginsEnvironment()
func (s *PluginService) GetPluginStatus(id string) (*model.PluginStatus, *model.AppError) {
pluginsEnvironment := s.GetPluginsEnvironment()
if pluginsEnvironment == nil {
return nil, model.NewAppError("GetPluginStatus", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
}
@@ -24,8 +24,8 @@ func (ch *Channels) GetPluginStatus(id string) (*model.PluginStatus, *model.AppE
for _, status := range pluginStatuses {
if status.PluginId == id {
// Add our cluster ID
if ch.srv.platform.Cluster() != nil {
status.ClusterId = ch.srv.platform.Cluster().GetClusterId()
if s.platform.Cluster() != nil {
status.ClusterId = s.platform.Cluster().GetClusterId()
}
return status, nil
@@ -37,12 +37,12 @@ func (ch *Channels) GetPluginStatus(id string) (*model.PluginStatus, *model.AppE
// GetPluginStatus returns the status for a plugin installed on this server.
func (a *App) GetPluginStatus(id string) (*model.PluginStatus, *model.AppError) {
return a.ch.GetPluginStatus(id)
return a.ch.srv.pluginService.GetPluginStatus(id)
}
// GetPluginStatuses returns the status for plugins installed on this server.
func (ch *Channels) GetPluginStatuses() (model.PluginStatuses, *model.AppError) {
pluginsEnvironment := ch.GetPluginsEnvironment()
func (s *PluginService) GetPluginStatuses() (model.PluginStatuses, *model.AppError) {
pluginsEnvironment := s.GetPluginsEnvironment()
if pluginsEnvironment == nil {
return nil, model.NewAppError("GetPluginStatuses", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
}
@@ -54,8 +54,8 @@ func (ch *Channels) GetPluginStatuses() (model.PluginStatuses, *model.AppError)
// Add our cluster ID
for _, status := range pluginStatuses {
if ch.srv.platform.Cluster() != nil {
status.ClusterId = ch.srv.platform.Cluster().GetClusterId()
if s.platform.Cluster() != nil {
status.ClusterId = s.platform.Cluster().GetClusterId()
} else {
status.ClusterId = ""
}
@@ -66,22 +66,22 @@ func (ch *Channels) 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.ch.GetPluginStatuses()
return a.ch.srv.pluginService.GetPluginStatuses()
}
// GetClusterPluginStatuses returns the status for plugins installed anywhere in the cluster.
func (a *App) GetClusterPluginStatuses() (model.PluginStatuses, *model.AppError) {
return a.ch.getClusterPluginStatuses()
return a.ch.srv.pluginService.getClusterPluginStatuses()
}
func (ch *Channels) getClusterPluginStatuses() (model.PluginStatuses, *model.AppError) {
pluginStatuses, err := ch.GetPluginStatuses()
func (s *PluginService) getClusterPluginStatuses() (model.PluginStatuses, *model.AppError) {
pluginStatuses, err := s.GetPluginStatuses()
if err != nil {
return nil, err
}
if ch.srv.platform.Cluster() != nil && *ch.cfgSvc.Config().ClusterSettings.Enable {
clusterPluginStatuses, err := ch.srv.platform.Cluster().GetPluginStatuses()
if s.platform.Cluster() != nil && *s.platform.Config().ClusterSettings.Enable {
clusterPluginStatuses, err := s.platform.Cluster().GetPluginStatuses()
if err != nil {
return nil, model.NewAppError("GetClusterPluginStatuses", "app.plugin.get_cluster_plugin_statuses.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
@@ -92,8 +92,8 @@ func (ch *Channels) getClusterPluginStatuses() (model.PluginStatuses, *model.App
return pluginStatuses, nil
}
func (ch *Channels) notifyPluginStatusesChanged() error {
pluginStatuses, err := ch.getClusterPluginStatuses()
func (s *PluginService) notifyPluginStatusesChanged() error {
pluginStatuses, err := s.getClusterPluginStatuses()
if err != nil {
return err
}
@@ -102,7 +102,7 @@ func (ch *Channels) notifyPluginStatusesChanged() error {
message := model.NewWebSocketEvent(model.WebsocketEventPluginStatusesChanged, "", "", "", nil, "")
message.Add("plugin_statuses", pluginStatuses)
message.GetBroadcast().ContainsSensitiveData = true
ch.srv.platform.Publish(message)
s.platform.Publish(message)
return nil
}

View File

@@ -346,7 +346,7 @@ func TestServePluginRequest(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/plugins/foo/bar", nil)
th.App.ch.ServePluginRequest(w, r)
th.App.PluginService().ServePluginRequest(w, r)
assert.Equal(t, http.StatusNotImplemented, w.Result().StatusCode)
}
@@ -390,7 +390,7 @@ func TestPrivateServePluginRequest(t *testing.T) {
request = mux.SetURLVars(request, map[string]string{"plugin_id": "id"})
th.App.ch.servePluginRequest(recorder, request, handler)
th.App.PluginService().servePluginRequest(recorder, request, handler)
})
}
@@ -413,7 +413,7 @@ func TestHandlePluginRequest(t *testing.T) {
var assertions func(*http.Request)
router := mux.NewRouter()
router.HandleFunc("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}/{anything:.*}", func(_ http.ResponseWriter, r *http.Request) {
th.App.ch.servePluginRequest(nil, r, func(_ *plugin.Context, _ http.ResponseWriter, r *http.Request) {
th.App.PluginService().servePluginRequest(nil, r, func(_ *plugin.Context, _ http.ResponseWriter, r *http.Request) {
assertions(r)
})
})
@@ -625,7 +625,7 @@ func TestPluginSync(t *testing.T) {
appErr = th.App.DeletePublicKey("pub_key")
checkNoError(t, appErr)
appErr = th.App.ch.RemovePlugin("testplugin")
appErr = th.App.PluginService().RemovePlugin("testplugin")
checkNoError(t, appErr)
})
})
@@ -642,7 +642,7 @@ func TestChannelsPluginsInit(t *testing.T) {
path, _ := fileutils.FindDir("tests")
require.NotPanics(t, func() {
th.Server.Channels().initPlugins(ctx, path, path)
th.Server.pluginService.initPlugins(ctx, path, path)
})
}
@@ -763,7 +763,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.ch.ShutDownPlugins()
th.App.PluginService().ShutDownPlugins()
tearDown()
testlib.AssertLog(t, th.LogBuffer, mlog.LvlDebug.Name, "panic: some text from panic")
@@ -831,7 +831,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, pluginBytes)
manifest, appErr := th.App.ch.installPluginLocally(bytes.NewReader(pluginBytes), nil, installPluginLocallyAlways)
manifest, appErr := th.App.PluginService().installPluginLocally(bytes.NewReader(pluginBytes), nil, installPluginLocallyAlways)
require.Nil(t, appErr)
require.Equal(t, "testplugin", manifest.Id)
@@ -848,7 +848,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
*cfg.PluginSettings.EnableRemoteMarketplace = false
})
plugins := th.App.ch.processPrepackagedPlugins(prepackagedPluginsDir)
plugins := th.App.PluginService().processPrepackagedPlugins(prepackagedPluginsDir)
require.Len(t, plugins, 1)
require.Equal(t, plugins[0].Manifest.Id, "testplugin")
require.Empty(t, plugins[0].Signature, 0)
@@ -858,7 +858,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
require.Len(t, pluginStatus, 1)
require.Equal(t, pluginStatus[0].PluginId, "testplugin")
appErr = th.App.ch.RemovePlugin("testplugin")
appErr = th.App.PluginService().RemovePlugin("testplugin")
checkNoError(t, appErr)
pluginStatus, err = env.Statuses()
@@ -875,7 +875,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
env := th.App.GetPluginsEnvironment()
plugins := th.App.ch.processPrepackagedPlugins(prepackagedPluginsDir)
plugins := th.App.PluginService().processPrepackagedPlugins(prepackagedPluginsDir)
require.Len(t, plugins, 1)
require.Equal(t, plugins[0].Manifest.Id, "testplugin")
require.Empty(t, plugins[0].Signature, 0)
@@ -908,7 +908,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
err = testlib.CopyFile(testPlugin2SignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz.sig"))
require.NoError(t, err)
plugins := th.App.ch.processPrepackagedPlugins(prepackagedPluginsDir)
plugins := th.App.PluginService().processPrepackagedPlugins(prepackagedPluginsDir)
require.Len(t, plugins, 2)
require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[0].Manifest.Id)
require.NotEmpty(t, plugins[0].Signature)
@@ -939,7 +939,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, pluginBytes)
manifest, appErr := th.App.ch.installPluginLocally(bytes.NewReader(pluginBytes), nil, installPluginLocallyAlways)
manifest, appErr := th.App.PluginService().installPluginLocally(bytes.NewReader(pluginBytes), nil, installPluginLocallyAlways)
require.Nil(t, appErr)
require.Equal(t, "testplugin", manifest.Id)
@@ -957,7 +957,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
err = testlib.CopyFile(testPlugin2SignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz.sig"))
require.NoError(t, err)
plugins := th.App.ch.processPrepackagedPlugins(prepackagedPluginsDir)
plugins := th.App.PluginService().processPrepackagedPlugins(prepackagedPluginsDir)
require.Len(t, plugins, 2)
require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[0].Manifest.Id)
require.NotEmpty(t, plugins[0].Signature)
@@ -969,7 +969,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
require.Len(t, pluginStatus, 1)
require.Equal(t, pluginStatus[0].PluginId, "testplugin")
appErr = th.App.ch.RemovePlugin("testplugin")
appErr = th.App.PluginService().RemovePlugin("testplugin")
checkNoError(t, appErr)
pluginStatus, err = env.Statuses()
@@ -994,7 +994,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
err = testlib.CopyFile(testPlugin2SignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz.sig"))
require.NoError(t, err)
plugins := th.App.ch.processPrepackagedPlugins(prepackagedPluginsDir)
plugins := th.App.PluginService().processPrepackagedPlugins(prepackagedPluginsDir)
require.Len(t, plugins, 2)
require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[0].Manifest.Id)
require.NotEmpty(t, plugins[0].Signature)
@@ -1071,14 +1071,14 @@ func TestGetPluginStateOverride(t *testing.T) {
defer th.TearDown()
t.Run("no override", func(t *testing.T) {
overrides, value := th.App.ch.getPluginStateOverride("focalboard")
overrides, value := th.App.PluginService().getPluginStateOverride("focalboard")
require.False(t, overrides)
require.False(t, value)
})
t.Run("calls override", func(t *testing.T) {
t.Run("on-prem", func(t *testing.T) {
overrides, value := th.App.ch.getPluginStateOverride("com.mattermost.calls")
overrides, value := th.App.PluginService().getPluginStateOverride("com.mattermost.calls")
require.False(t, overrides)
require.False(t, value)
})
@@ -1086,7 +1086,7 @@ func TestGetPluginStateOverride(t *testing.T) {
t.Run("Cloud, without enabled flag", func(t *testing.T) {
os.Setenv("MM_CLOUD_INSTALLATION_ID", "test")
defer os.Unsetenv("MM_CLOUD_INSTALLATION_ID")
overrides, value := th.App.ch.getPluginStateOverride("com.mattermost.calls")
overrides, value := th.App.PluginService().getPluginStateOverride("com.mattermost.calls")
require.False(t, overrides)
require.False(t, value)
})
@@ -1100,7 +1100,7 @@ func TestGetPluginStateOverride(t *testing.T) {
th2 := Setup(t)
defer th2.TearDown()
overrides, value := th2.App.ch.getPluginStateOverride("com.mattermost.calls")
overrides, value := th2.App.PluginService().getPluginStateOverride("com.mattermost.calls")
require.False(t, overrides)
require.False(t, value)
})
@@ -1114,7 +1114,7 @@ func TestGetPluginStateOverride(t *testing.T) {
th2 := Setup(t)
defer th2.TearDown()
overrides, value := th2.App.ch.getPluginStateOverride("com.mattermost.calls")
overrides, value := th2.App.PluginService().getPluginStateOverride("com.mattermost.calls")
require.True(t, overrides)
require.False(t, value)
})
@@ -1126,7 +1126,7 @@ func TestGetPluginStateOverride(t *testing.T) {
th2 := Setup(t)
defer th2.TearDown()
overrides, value := th2.App.ch.getPluginStateOverride("com.mattermost.calls")
overrides, value := th2.App.PluginService().getPluginStateOverride("com.mattermost.calls")
require.True(t, overrides)
require.False(t, value)
})
@@ -1134,7 +1134,7 @@ func TestGetPluginStateOverride(t *testing.T) {
t.Run("apps override", func(t *testing.T) {
t.Run("without enabled flag", func(t *testing.T) {
overrides, value := th.App.ch.getPluginStateOverride("com.mattermost.apps")
overrides, value := th.App.PluginService().getPluginStateOverride("com.mattermost.apps")
require.False(t, overrides)
require.False(t, value)
})
@@ -1146,7 +1146,7 @@ func TestGetPluginStateOverride(t *testing.T) {
th2 := Setup(t)
defer th2.TearDown()
overrides, value := th2.App.ch.getPluginStateOverride("com.mattermost.apps")
overrides, value := th2.App.PluginService().getPluginStateOverride("com.mattermost.apps")
require.True(t, overrides)
require.False(t, value)
})

View File

@@ -269,7 +269,7 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel
}
var rejectionError *model.AppError
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
replacementPost, rejectionReason := hooks.MessageWillBePosted(pluginContext, post.ForPlugin())
if rejectionReason != "" {
id := "Post rejected by plugin. " + rejectionReason
@@ -328,7 +328,7 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel
// and to remove the non-GOB-encodable Metadata from it.
pluginPost := rpost.ForPlugin()
a.Srv().Go(func() {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.MessageHasBeenPosted(pluginContext, pluginPost)
return true
}, plugin.MessageHasBeenPostedID)
@@ -655,7 +655,7 @@ func (a *App) UpdatePost(c *request.Context, post *model.Post, safeUpdate bool)
var rejectionReason string
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
newPost, rejectionReason = hooks.MessageWillBeUpdated(pluginContext, newPost.ForPlugin(), oldPost.ForPlugin())
return post != nil
}, plugin.MessageWillBeUpdatedID)
@@ -680,7 +680,7 @@ func (a *App) UpdatePost(c *request.Context, post *model.Post, safeUpdate bool)
pluginOldPost := oldPost.ForPlugin()
pluginNewPost := newPost.ForPlugin()
a.Srv().Go(func() {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.MessageHasBeenUpdated(pluginContext, pluginNewPost, pluginOldPost)
return true
}, plugin.MessageHasBeenUpdatedID)

View File

@@ -45,7 +45,7 @@ func (a *App) SaveReactionForPost(c *request.Context, reaction *model.Reaction)
pluginContext := pluginContext(c)
a.Srv().Go(func() {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.ReactionHasBeenAdded(pluginContext, reaction)
return true
}, plugin.ReactionHasBeenAddedID)
@@ -142,7 +142,7 @@ func (a *App) DeleteReactionForPost(c *request.Context, reaction *model.Reaction
pluginContext := pluginContext(c)
a.Srv().Go(func() {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.ReactionHasBeenRemoved(pluginContext, reaction)
return true
}, plugin.ReactionHasBeenRemovedID)

View File

@@ -119,6 +119,7 @@ type Server struct {
telemetryService *telemetry.TelemetryService
userService *users.UserService
teamService *teams.TeamService
pluginService *PluginService
serviceMux sync.RWMutex
remoteClusterService remotecluster.RemoteClusterServiceIFace
@@ -718,6 +719,10 @@ func (s *Server) Shutdown() {
}
}
// Stop the plugin service, we need to stop plugin service before stopping the
// product as products are being consumed by this service.
s.pluginService.ShutDownPlugins()
// Stop products.
// This needs to happen last because products are dependent
// on parent services.
@@ -824,11 +829,18 @@ 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.
// make sure channels starts first
if err := s.products["channels"].Start(); err != nil {
return errors.Wrap(err, "Unable to start channels")
}
// This should actually be started after products, but we have a product hooks
// dependency for now, once that get sorted out, this should be moved to the appropriate
// order.
if err := s.InitializePluginService(); err != nil {
return errors.Wrap(err, "Unable to start plugin service")
}
for name, product := range s.products {
if name == "channels" {
continue

View File

@@ -853,7 +853,7 @@ func (a *App) JoinUserToTeam(c request.CTX, team *model.Team, user *model.User,
a.Srv().Go(func() {
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.UserHasJoinedTeam(pluginContext, teamMember, actor)
return true
}, plugin.UserHasJoinedTeamID)
@@ -1225,7 +1225,7 @@ func (a *App) postProcessTeamMemberLeave(c request.CTX, teamMember *model.TeamMe
a.Srv().Go(func() {
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.UserHasLeftTeam(pluginContext, teamMember, actor)
return true
}, plugin.UserHasLeftTeamID)

View File

@@ -62,7 +62,7 @@ func (a *App) runPluginsHook(c request.CTX, info *model.FileInfo, file io.Reader
var rejErr *model.AppError
var once sync.Once
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
once.Do(func() {
hookHasRunCh <- struct{}{}
})

View File

@@ -310,7 +310,7 @@ func (a *App) createUserOrGuest(c request.CTX, user *model.User, guest bool) (*m
pluginContext := pluginContext(c)
a.Srv().Go(func() {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
hooks.UserHasBeenCreated(pluginContext, ruser)
return true
}, plugin.UserHasBeenCreatedID)

View File

@@ -16,5 +16,5 @@ func (a *App) PopulateWebConnConfig(s *model.Session, cfg *platform.WebConnConfi
// NewWebConn returns a new WebConn instance.
func (a *App) NewWebConn(cfg *platform.WebConnConfig) *platform.WebConn {
return a.Srv().Platform().NewWebConn(cfg, a, a.ch)
return a.Srv().Platform().NewWebConn(cfg, a, a.Srv())
}

View File

@@ -7,7 +7,6 @@ import (
"github.com/spf13/cobra"
"github.com/mattermost/mattermost-server/v6/app"
"github.com/mattermost/mattermost-server/v6/app/request"
"github.com/mattermost/mattermost-server/v6/config"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/shared/i18n"
@@ -21,7 +20,11 @@ func initDBCommandContextCobra(command *cobra.Command, readOnlyConfigStore bool)
panic(err)
}
a.InitPlugins(request.EmptyContext(a.Log()), *a.Config().PluginSettings.Directory, *a.Config().PluginSettings.ClientDirectory)
err = a.Srv().InitializePluginService()
if err != nil {
return nil, err
}
a.DoAppMigrations()
return a, nil

View File

@@ -282,7 +282,7 @@ func TestPublicFilesRequest(t *testing.T) {
defer os.RemoveAll(pluginDir)
defer os.RemoveAll(webappPluginDir)
env, err := plugin.NewEnvironment(th.NewPluginAPI, app.NewDriverImpl(th.Server), pluginDir, webappPluginDir, false, th.App.Log(), nil)
env, err := plugin.NewEnvironment(th.NewPluginAPI, app.NewDriverImpl(th.Server.Platform()), pluginDir, webappPluginDir, false, th.App.Log(), nil)
require.NoError(t, err)
pluginID := "com.mattermost.sample"
@@ -329,7 +329,7 @@ func TestPublicFilesRequest(t *testing.T) {
require.NotNil(t, manifest)
require.True(t, activated)
th.App.Channels().SetPluginsEnvironment(env)
th.App.PluginService().SetPluginsEnvironment(env)
req, _ := http.NewRequest("GET", "/plugins/com.mattermost.sample/public/hello.html", nil)
res := httptest.NewRecorder()