app/pluginservice: move plugin out from the Channels product (#21697)

Этот коммит содержится в:
Ibrahim Serdar Acikgoz
2022-11-24 11:13:29 +03:00
коммит произвёл GitHub
родитель 5e5769c4ee
Коммит 6c55c4d356
31 изменённых файлов: 497 добавлений и 408 удалений

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

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

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

@@ -94,7 +94,7 @@ func TestPlugin(t *testing.T) {
assert.Equal(t, "testplugin", manifest.Id) 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 }) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.PluginSettings.Enable = false })

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

@@ -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().GetPluginsEnvironment) wc := c.App.Srv().Platform().NewWebConn(cfg, c.App, c.App.PluginService().GetPluginsEnvironment)
if c.AppContext.Session().UserId != "" { if c.AppContext.Session().UserId != "" {
c.App.Srv().Platform().HubRegister(wc) c.App.Srv().Platform().HubRegister(wc)
} }

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

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

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

@@ -6,17 +6,13 @@ package app
import ( import (
"fmt" "fmt"
"runtime" "runtime"
"strings"
"sync" "sync"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/mattermost/mattermost-server/v6/app/imaging" "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/einterfaces"
"github.com/mattermost/mattermost-server/v6/model" "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/product"
"github.com/mattermost/mattermost-server/v6/services/imageproxy" "github.com/mattermost/mattermost-server/v6/services/imageproxy"
"github.com/mattermost/mattermost-server/v6/shared/filestore" "github.com/mattermost/mattermost-server/v6/shared/filestore"
@@ -40,12 +36,6 @@ type Channels struct {
postActionCookieSecret []byte postActionCookieSecret []byte
pluginCommandsLock sync.RWMutex
pluginCommands []*PluginCommand
pluginsLock sync.RWMutex
pluginsEnvironment *plugin.Environment
pluginConfigListenerID string
imageProxy *imageproxy.ImageProxy imageProxy *imageproxy.ImageProxy
// cached counts that are used during notice condition validation // cached counts that are used during notice condition validation
@@ -77,12 +67,6 @@ type Channels struct {
postReminderMut sync.Mutex postReminderMut sync.Mutex
postReminderTask *model.ScheduledTask 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() { func init() {
@@ -100,11 +84,9 @@ func init() {
func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) { func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
ch := &Channels{ ch := &Channels{
srv: s, srv: s,
imageProxy: imageproxy.MakeImageProxy(s.platform, s.httpService, s.Log()), imageProxy: imageproxy.MakeImageProxy(s.platform, s.httpService, s.Log()),
uploadLockMap: map[string]bool{}, uploadLockMap: map[string]bool{},
collectionTypes: map[string]string{},
topicTypes: map[string]string{},
} }
// To get another service: // To get another service:
@@ -201,10 +183,6 @@ func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
services[RouterKey] = ch.routerSvc services[RouterKey] = ch.routerSvc
// Setup routes. // 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[PostKey] = &postServiceWrapper{ services[PostKey] = &postServiceWrapper{
app: &App{ch: ch}, app: &App{ch: ch},
@@ -236,39 +214,6 @@ func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
} }
func (ch *Channels) Start() 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. // TODO: This should be moved to the platform service.
if err := ch.srv.platform.EnsureAsymmetricSigningKey(); err != nil { if err := ch.srv.platform.EnsureAsymmetricSigningKey(); err != nil {
return errors.Wrapf(err, "unable to ensure asymmetric signing key") return errors.Wrapf(err, "unable to ensure asymmetric signing key")
@@ -282,8 +227,6 @@ func (ch *Channels) Start() error {
} }
func (ch *Channels) Stop() error { func (ch *Channels) Stop() error {
ch.ShutDownPlugins()
ch.dndTaskMut.Lock() ch.dndTaskMut.Lock()
if ch.dndTask != nil { if ch.dndTask != nil {
ch.dndTask.Cancel() ch.dndTask.Cancel()
@@ -318,9 +261,9 @@ type hooksService struct {
} }
func (s *hooksService) RegisterHooks(productID string, hooks any) error { func (s *hooksService) RegisterHooks(productID string, hooks any) error {
if s.ch.pluginsEnvironment == nil { if s.ch.srv.pluginService.pluginsEnvironment == nil {
return errors.New("could not find plugins environment") return errors.New("could not find plugins environment")
} }
return s.ch.pluginsEnvironment.AddProduct(productID, hooks) return s.ch.srv.pluginService.pluginsEnvironment.AddProduct(productID, hooks)
} }

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

@@ -16,7 +16,7 @@ func (s *Server) clusterInstallPluginHandler(msg *model.ClusterMessage) {
if jsonErr := json.Unmarshal(msg.Data, &data); jsonErr != nil { if jsonErr := json.Unmarshal(msg.Data, &data); jsonErr != nil {
mlog.Warn("Failed to decode from JSON", mlog.Err(jsonErr)) 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) { func (s *Server) clusterRemovePluginHandler(msg *model.ClusterMessage) {
@@ -24,11 +24,11 @@ func (s *Server) clusterRemovePluginHandler(msg *model.ClusterMessage) {
if jsonErr := json.Unmarshal(msg.Data, &data); jsonErr != nil { if jsonErr := json.Unmarshal(msg.Data, &data); jsonErr != nil {
mlog.Warn("Failed to decode from JSON", mlog.Err(jsonErr)) 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) { func (s *Server) clusterPluginEventHandler(msg *model.ClusterMessage) {
env := s.Channels().GetPluginsEnvironment() env := s.pluginService.GetPluginsEnvironment()
if env == nil { if env == nil {
return return
} }

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

@@ -10,26 +10,26 @@ import (
"github.com/mattermost/mattermost-server/v6/shared/mlog" "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 // we have a race condition due to multiple plugins calling this method
a.ch.collectionAndTopicTypesMut.Lock() s.collectionAndTopicTypesMut.Lock()
defer a.ch.collectionAndTopicTypesMut.Unlock() defer s.collectionAndTopicTypesMut.Unlock()
// check if collectionType was already registered by other plugin // check if collectionType was already registered by other plugin
existingPluginID, ok := a.ch.collectionTypes[collectionType] existingPluginID, ok := s.collectionTypes[collectionType]
if ok && existingPluginID != pluginID { if ok && existingPluginID != pluginID {
return model.NewAppError("registerCollectionAndTopic", "app.collection.add_collection.exists.app_error", nil, "", http.StatusBadRequest) return model.NewAppError("registerCollectionAndTopic", "app.collection.add_collection.exists.app_error", nil, "", http.StatusBadRequest)
} }
// check if topicType was already registered to other collection // check if topicType was already registered to other collection
existingCollectionType, ok := a.ch.topicTypes[topicType] existingCollectionType, ok := s.topicTypes[topicType]
if ok && existingCollectionType != collectionType { if ok && existingCollectionType != collectionType {
return model.NewAppError("registerCollectionAndTopic", "app.collection.add_topic.exists.app_error", nil, "", http.StatusBadRequest) return model.NewAppError("registerCollectionAndTopic", "app.collection.add_topic.exists.app_error", nil, "", http.StatusBadRequest)
} }
a.ch.collectionTypes[collectionType] = pluginID s.collectionTypes[collectionType] = pluginID
a.ch.topicTypes[topicType] = collectionType 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 return nil
} }

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

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

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

@@ -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) { 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, "/") rawURL = strings.TrimPrefix(rawURL, "/")
inURL, err := url.Parse(rawURL) inURL, err := url.Parse(rawURL)
if err != nil { if err != nil {
@@ -427,7 +427,7 @@ func (ch *Channels) doPluginRequest(c *request.Context, method, rawURL string, v
params["plugin_id"] = pluginID params["plugin_id"] = pluginID
r = mux.SetURLVars(r, params) r = mux.SetURLVars(r, params)
ch.ServePluginRequest(w, r) s.ServePluginRequest(w, r)
resp := &http.Response{ resp := &http.Response{
StatusCode: w.status, StatusCode: w.status,

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

@@ -28,7 +28,7 @@ func (a *App) markAdminOnboardingComplete(c *request.Context) *model.AppError {
} }
func (a *App) CompleteOnboarding(c *request.Context, request *model.CompleteOnboardingRequest) *model.AppError { func (a *App) CompleteOnboarding(c *request.Context, request *model.CompleteOnboardingRequest) *model.AppError {
pluginsEnvironment := a.Channels().GetPluginsEnvironment() pluginsEnvironment := a.Srv().pluginService.GetPluginsEnvironment()
if pluginsEnvironment == nil { if pluginsEnvironment == nil {
return a.markAdminOnboardingComplete(c) return a.markAdminOnboardingComplete(c)
} }
@@ -41,7 +41,7 @@ func (a *App) CompleteOnboarding(c *request.Context, request *model.CompleteOnbo
installRequest := &model.InstallMarketplacePluginRequest{ installRequest := &model.InstallMarketplacePluginRequest{
Id: id, Id: id,
} }
_, appErr := a.Channels().InstallMarketplacePlugin(installRequest) _, appErr := a.Srv().pluginService.InstallMarketplacePlugin(installRequest)
if appErr != nil { if appErr != nil {
mlog.Error("Failed to install plugin for onboarding", mlog.String("id", id), mlog.Err(appErr)) mlog.Error("Failed to install plugin for onboarding", mlog.String("id", id), mlog.Err(appErr))
return return

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

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

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

@@ -20,16 +20,37 @@ import (
svg "github.com/h2non/go-is-svg" svg "github.com/h2non/go-is-svg"
"github.com/pkg/errors" "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/app/request"
"github.com/mattermost/mattermost-server/v6/config"
"github.com/mattermost/mattermost-server/v6/model" "github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/plugin" "github.com/mattermost/mattermost-server/v6/plugin"
"github.com/mattermost/mattermost-server/v6/product" "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/services/marketplace"
"github.com/mattermost/mattermost-server/v6/shared/filestore" "github.com/mattermost/mattermost-server/v6/shared/filestore"
"github.com/mattermost/mattermost-server/v6/shared/mlog" "github.com/mattermost/mattermost-server/v6/shared/mlog"
"github.com/mattermost/mattermost-server/v6/utils/fileutils" "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" const prepackagedPluginsDir = "prepackaged_plugins"
type pluginSignaturePath struct { type pluginSignaturePath struct {
@@ -63,20 +84,91 @@ func (rs *routerService) getHandler(productID string) (http.Handler, bool) {
return handler, ok return handler, ok
} }
func NewPluginService(platform *platform.PlatformService, channels *Channels, httpService httpservice.HTTPService, router *mux.Router) *PluginService {
ps := &PluginService{
platform: platform,
channels: channels,
fileStore: platform.FileBackend(),
httpService: httpService,
collectionTypes: make(map[string]string),
topicTypes: make(map[string]string),
}
pluginsRoute := 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(platform.Log()), *platform.Config().PluginSettings.Directory, *platform.Config().PluginSettings.ClientDirectory)
return ps
}
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")
}
s.pluginService = NewPluginService(s.platform, channels, s.httpService, s.Router)
// 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
}
// GetPluginsEnvironment returns the plugin environment for use if plugins are enabled and // GetPluginsEnvironment returns the plugin environment for use if plugins are enabled and
// initialized. // initialized.
// //
// To get the plugins environment when the plugins are disabled, manually acquire the plugins // To get the plugins environment when the plugins are disabled, manually acquire the plugins
// lock instead. // lock instead.
func (ch *Channels) GetPluginsEnvironment() *plugin.Environment { func (s *PluginService) GetPluginsEnvironment() *plugin.Environment {
if !*ch.cfgSvc.Config().PluginSettings.Enable { if !*s.platform.Config().PluginSettings.Enable {
return nil return nil
} }
ch.pluginsLock.RLock() s.pluginsLock.RLock()
defer ch.pluginsLock.RUnlock() defer s.pluginsLock.RUnlock()
return ch.pluginsEnvironment return s.pluginsEnvironment
} }
// GetPluginsEnvironment returns the plugin environment for use if plugins are enabled and // GetPluginsEnvironment returns the plugin environment for use if plugins are enabled and
@@ -85,33 +177,33 @@ func (ch *Channels) GetPluginsEnvironment() *plugin.Environment {
// To get the plugins environment when the plugins are disabled, manually acquire the plugins // To get the plugins environment when the plugins are disabled, manually acquire the plugins
// lock instead. // lock instead.
func (a *App) GetPluginsEnvironment() *plugin.Environment { func (a *App) GetPluginsEnvironment() *plugin.Environment {
return a.ch.GetPluginsEnvironment() return a.ch.srv.pluginService.GetPluginsEnvironment()
} }
func (ch *Channels) SetPluginsEnvironment(pluginsEnvironment *plugin.Environment) { func (s *PluginService) SetPluginsEnvironment(pluginsEnvironment *plugin.Environment) {
ch.pluginsLock.Lock() s.pluginsLock.Lock()
defer ch.pluginsLock.Unlock() defer s.pluginsLock.Unlock()
ch.pluginsEnvironment = pluginsEnvironment s.pluginsEnvironment = pluginsEnvironment
ch.srv.Platform().SetPluginsEnvironment(pluginsEnvironment) s.platform.SetPluginsEnvironment(pluginsEnvironment)
} }
func (ch *Channels) syncPluginsActiveState() { func (s *PluginService) syncPluginsActiveState() {
// Acquiring lock manually, as plugins might be disabled. See GetPluginsEnvironment. // Acquiring lock manually, as plugins might be disabled. See GetPluginsEnvironment.
ch.pluginsLock.RLock() s.pluginsLock.RLock()
pluginsEnvironment := ch.pluginsEnvironment pluginsEnvironment := s.pluginsEnvironment
ch.pluginsLock.RUnlock() s.pluginsLock.RUnlock()
if pluginsEnvironment == nil { if pluginsEnvironment == nil {
return return
} }
config := ch.cfgSvc.Config().PluginSettings config := s.platform.Config().PluginSettings
if *config.Enable { if *config.Enable {
availablePlugins, err := pluginsEnvironment.Available() availablePlugins, err := pluginsEnvironment.Available()
if err != nil { 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 return
} }
@@ -125,24 +217,24 @@ func (ch *Channels) syncPluginsActiveState() {
pluginEnabled = state.Enable pluginEnabled = state.Enable
} }
if hasOverride, value := ch.getPluginStateOverride(pluginID); hasOverride { if hasOverride, value := s.getPluginStateOverride(pluginID); hasOverride {
pluginEnabled = value pluginEnabled = value
} }
if pluginEnabled { if pluginEnabled {
// Disable focalboard in product mode. // 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." msg := "Plugin cannot run in product mode. Disabling."
mlog.Warn(msg, mlog.String("plugin_id", model.PluginIdFocalboard)) mlog.Warn(msg, mlog.String("plugin_id", model.PluginIdFocalboard))
// This is a mini-version of ch.disablePlugin. // This is a mini-version of ch.disablePlugin.
// We don't call that directly, because that will recursively call // We don't call that directly, because that will recursively call
// this method. // this method.
ch.cfgSvc.UpdateConfig(func(cfg *model.Config) { s.platform.UpdateConfig(func(cfg *model.Config) {
cfg.PluginSettings.PluginStates[pluginID] = &model.PluginState{Enable: false} cfg.PluginSettings.PluginStates[pluginID] = &model.PluginState{Enable: false}
}) })
pluginsEnvironment.SetPluginError(pluginID, msg) pluginsEnvironment.SetPluginError(pluginID, msg)
ch.unregisterPluginCommands(pluginID) s.unregisterPluginCommands(pluginID)
disabledPlugins = append(disabledPlugins, plugin) disabledPlugins = append(disabledPlugins, plugin)
continue continue
} }
@@ -166,7 +258,7 @@ func (ch *Channels) syncPluginsActiveState() {
if deactivated && plugin.Manifest.HasClient() { if deactivated && plugin.Manifest.HasClient() {
message := model.NewWebSocketEvent(model.WebsocketEventPluginDisabled, "", "", "", nil, "") message := model.NewWebSocketEvent(model.WebsocketEventPluginDisabled, "", "", "", nil, "")
message.Add("manifest", plugin.Manifest.ClientManifest()) message.Add("manifest", plugin.Manifest.ClientManifest())
ch.srv.platform.Publish(message) s.platform.Publish(message)
} }
}(plugin) }(plugin)
} }
@@ -180,14 +272,14 @@ func (ch *Channels) syncPluginsActiveState() {
pluginID := plugin.Manifest.Id pluginID := plugin.Manifest.Id
updatedManifest, activated, err := pluginsEnvironment.Activate(pluginID) updatedManifest, activated, err := pluginsEnvironment.Activate(pluginID)
if err != nil { 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 return
} }
if activated { if activated {
// Notify all cluster clients if ready // Notify all cluster clients if ready
if err := ch.notifyPluginEnabled(updatedManifest); err != nil { if err := s.notifyPluginEnabled(updatedManifest); err != nil {
ch.srv.Log().Error("Failed to notify cluster on plugin enable", mlog.Err(err)) s.platform.Log().Error("Failed to notify cluster on plugin enable", mlog.Err(err))
} }
} }
}(plugin) }(plugin)
@@ -197,7 +289,7 @@ func (ch *Channels) syncPluginsActiveState() {
pluginsEnvironment.Shutdown() 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)) mlog.Warn("failed to notify plugin status changed", mlog.Err(err))
} }
} }
@@ -207,27 +299,29 @@ func (a *App) NewPluginAPI(c *request.Context, manifest *model.Manifest) plugin.
} }
func (a *App) InitPlugins(c *request.Context, pluginDir, webappPluginDir string) { 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. // Acquiring lock manually, as plugins might be disabled. See GetPluginsEnvironment.
defer func() { defer func() {
ch.srv.Platform().SetPluginsEnvironment(ch.pluginsEnvironment) // platform service requires plugins environment to be initialized
// so that it can use it in cluster service initialization
s.platform.SetPluginsEnvironment(s.pluginsEnvironment)
}() }()
ch.pluginsLock.RLock() s.pluginsLock.RLock()
pluginsEnvironment := ch.pluginsEnvironment pluginsEnvironment := s.pluginsEnvironment
ch.pluginsLock.RUnlock() s.pluginsLock.RUnlock()
if pluginsEnvironment != nil || !*ch.cfgSvc.Config().PluginSettings.Enable { if pluginsEnvironment != nil || !*s.platform.Config().PluginSettings.Enable {
ch.syncPluginsActiveState() s.syncPluginsActiveState()
if pluginsEnvironment != nil { if pluginsEnvironment != nil {
pluginsEnvironment.TogglePluginHealthCheckJob(*ch.cfgSvc.Config().PluginSettings.EnableHealthCheck) pluginsEnvironment.TogglePluginHealthCheckJob(*s.platform.Config().PluginSettings.EnableHealthCheck)
} }
return 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) { if err := os.Mkdir(pluginDir, 0744); err != nil && !os.IsExist(err) {
mlog.Error("Failed to start up plugins", mlog.Err(err)) mlog.Error("Failed to start up plugins", mlog.Err(err))
@@ -240,70 +334,70 @@ func (ch *Channels) initPlugins(c *request.Context, pluginDir, webappPluginDir s
} }
newAPIFunc := func(manifest *model.Manifest) plugin.API { 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), pluginDir, webappPluginDir, ch.srv.Log(), ch.srv.GetMetrics()) env, err := plugin.NewEnvironment(newAPIFunc, NewDriverImpl(s.platform), pluginDir, webappPluginDir, s.platform.Log().(*mlog.Logger), s.platform.Metrics())
if err != nil { if err != nil {
mlog.Error("Failed to start up plugins", mlog.Err(err)) mlog.Error("Failed to start up plugins", mlog.Err(err))
return return
} }
ch.pluginsLock.Lock() s.pluginsLock.Lock()
ch.pluginsEnvironment = env s.pluginsEnvironment = env
ch.pluginsLock.Unlock() 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)) mlog.Error("Failed to sync plugins from the file store", mlog.Err(err))
} }
plugins := ch.processPrepackagedPlugins(prepackagedPluginsDir) plugins := s.processPrepackagedPlugins(prepackagedPluginsDir)
pluginsEnvironment = ch.GetPluginsEnvironment() pluginsEnvironment = s.GetPluginsEnvironment()
if pluginsEnvironment == nil { if pluginsEnvironment == nil {
mlog.Info("Plugins environment not found, server is likely shutting down") mlog.Info("Plugins environment not found, server is likely shutting down")
return return
} }
pluginsEnvironment.SetPrepackagedPlugins(plugins) pluginsEnvironment.SetPrepackagedPlugins(plugins)
ch.installFeatureFlagPlugins() s.installFeatureFlagPlugins()
// Sync plugin active state when config changes. Also notify plugins. // Sync plugin active state when config changes. Also notify plugins.
ch.pluginsLock.Lock() s.pluginsLock.Lock()
ch.RemoveConfigListener(ch.pluginConfigListenerID) s.platform.RemoveConfigListener(s.pluginConfigListenerID)
ch.pluginConfigListenerID = ch.AddConfigListener(func(old, new *model.Config) { s.pluginConfigListenerID = s.platform.AddConfigListener(func(old, new *model.Config) {
// If plugin status remains unchanged, only then run this. // If plugin status remains unchanged, only then run this.
// Because (*App).InitPlugins is already run as a config change hook. // Because (*App).InitPlugins is already run as a config change hook.
if *old.PluginSettings.Enable == *new.PluginSettings.Enable { if *old.PluginSettings.Enable == *new.PluginSettings.Enable {
ch.installFeatureFlagPlugins() s.installFeatureFlagPlugins()
ch.syncPluginsActiveState() s.syncPluginsActiveState()
} }
if pluginsEnvironment := ch.GetPluginsEnvironment(); pluginsEnvironment != nil { if pluginsEnvironment := s.GetPluginsEnvironment(); pluginsEnvironment != nil {
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool { pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
if err := hooks.OnConfigurationChange(); err != nil { 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 return true
}, plugin.OnConfigurationChangeID) }, plugin.OnConfigurationChangeID)
} }
}) })
ch.pluginsLock.Unlock() s.pluginsLock.Unlock()
ch.syncPluginsActiveState() s.syncPluginsActiveState()
} }
// SyncPlugins synchronizes the plugins installed locally // SyncPlugins synchronizes the plugins installed locally
// with the plugin bundles available in the file store. // with the plugin bundles available in the file store.
func (a *App) SyncPlugins() *model.AppError { func (a *App) SyncPlugins() *model.AppError {
return a.ch.syncPlugins() return a.ch.srv.pluginService.syncPlugins()
} }
// SyncPlugins synchronizes the plugins installed locally // SyncPlugins synchronizes the plugins installed locally
// with the plugin bundles available in the file store. // 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") mlog.Info("Syncing plugins from the file store")
pluginsEnvironment := ch.GetPluginsEnvironment() pluginsEnvironment := s.GetPluginsEnvironment()
if pluginsEnvironment == nil { if pluginsEnvironment == nil {
return model.NewAppError("SyncPlugins", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented) return model.NewAppError("SyncPlugins", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
} }
@@ -319,14 +413,14 @@ func (ch *Channels) syncPlugins() *model.AppError {
go func(pluginID string) { go func(pluginID string) {
defer wg.Done() defer wg.Done()
// Only handle managed plugins with .filestore flag file. // 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) { if os.IsNotExist(err) {
mlog.Warn("Skipping sync for unmanaged plugin", mlog.String("plugin_id", pluginID)) mlog.Warn("Skipping sync for unmanaged plugin", mlog.String("plugin_id", pluginID))
} else if err != nil { } else if err != nil {
mlog.Error("Skipping sync for plugin after failure to check if managed", mlog.String("plugin_id", pluginID), mlog.Err(err)) mlog.Error("Skipping sync for plugin after failure to check if managed", mlog.String("plugin_id", pluginID), mlog.Err(err))
} else { } else {
mlog.Debug("Removing local installation of managed plugin before sync", mlog.String("plugin_id", pluginID)) 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)) mlog.Error("Failed to remove local installation of managed plugin before sync", mlog.String("plugin_id", pluginID), mlog.Err(err))
} }
} }
@@ -335,7 +429,7 @@ func (ch *Channels) syncPlugins() *model.AppError {
wg.Wait() wg.Wait()
// Install plugins from the file store. // Install plugins from the file store.
pluginSignaturePathMap, appErr := ch.getPluginsFromFolder() pluginSignaturePathMap, appErr := s.getPluginsFromFolder()
if appErr != nil { if appErr != nil {
return appErr return appErr
} }
@@ -344,7 +438,7 @@ func (ch *Channels) syncPlugins() *model.AppError {
wg.Add(1) wg.Add(1)
go func(plugin *pluginSignaturePath) { go func(plugin *pluginSignaturePath) {
defer wg.Done() defer wg.Done()
reader, appErr := ch.srv.fileReader(plugin.path) reader, appErr := s.fileStore.Reader(plugin.path)
if appErr != nil { if appErr != nil {
mlog.Error("Failed to open plugin bundle from file store.", mlog.String("bundle", plugin.path), mlog.Err(appErr)) mlog.Error("Failed to open plugin bundle from file store.", mlog.String("bundle", plugin.path), mlog.Err(appErr))
return return
@@ -352,8 +446,8 @@ func (ch *Channels) syncPlugins() *model.AppError {
defer reader.Close() defer reader.Close()
var signature filestore.ReadCloseSeeker var signature filestore.ReadCloseSeeker
if *ch.cfgSvc.Config().PluginSettings.RequirePluginSignature { if *s.platform.Config().PluginSettings.RequirePluginSignature {
signature, appErr = ch.srv.fileReader(plugin.signaturePath) signature, appErr = s.fileStore.Reader(plugin.signaturePath)
if appErr != nil { if appErr != nil {
mlog.Error("Failed to open plugin signature from file store.", mlog.Err(appErr)) mlog.Error("Failed to open plugin signature from file store.", mlog.Err(appErr))
return return
@@ -362,7 +456,7 @@ func (ch *Channels) syncPlugins() *model.AppError {
} }
mlog.Info("Syncing plugin from file store", mlog.String("bundle", plugin.path)) 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)) mlog.Error("Failed to sync plugin from file store", mlog.String("bundle", plugin.path), mlog.Err(err))
} }
}(plugin) }(plugin)
@@ -372,11 +466,11 @@ func (ch *Channels) syncPlugins() *model.AppError {
return nil return nil
} }
func (ch *Channels) ShutDownPlugins() { func (s *PluginService) ShutDownPlugins() {
// Acquiring lock manually, as plugins might be disabled. See GetPluginsEnvironment. // Acquiring lock manually, as plugins might be disabled. See GetPluginsEnvironment.
ch.pluginsLock.RLock() s.pluginsLock.RLock()
pluginsEnvironment := ch.pluginsEnvironment pluginsEnvironment := s.pluginsEnvironment
ch.pluginsLock.RUnlock() s.pluginsLock.RUnlock()
if pluginsEnvironment == nil { if pluginsEnvironment == nil {
return return
} }
@@ -385,14 +479,14 @@ func (ch *Channels) ShutDownPlugins() {
pluginsEnvironment.Shutdown() pluginsEnvironment.Shutdown()
ch.RemoveConfigListener(ch.pluginConfigListenerID) s.platform.RemoveConfigListener(s.pluginConfigListenerID)
ch.pluginConfigListenerID = "" s.pluginConfigListenerID = ""
// Acquiring lock manually before cleaning up PluginsEnvironment. // Acquiring lock manually before cleaning up PluginsEnvironment.
ch.pluginsLock.Lock() s.pluginsLock.Lock()
defer ch.pluginsLock.Unlock() defer s.pluginsLock.Unlock()
if ch.pluginsEnvironment == pluginsEnvironment { if s.pluginsEnvironment == pluginsEnvironment {
ch.pluginsEnvironment = nil s.pluginsEnvironment = nil
} else { } else {
mlog.Warn("Another PluginsEnvironment detected while shutting down plugins.") mlog.Warn("Another PluginsEnvironment detected while shutting down plugins.")
} }
@@ -418,11 +512,11 @@ func (a *App) GetActivePluginManifests() ([]*model.Manifest, *model.AppError) {
// activation if inactive anywhere in the cluster. // activation if inactive anywhere in the cluster.
// Notifies cluster peers through config change. // Notifies cluster peers through config change.
func (a *App) EnablePlugin(id string) *model.AppError { 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 { func (s *PluginService) enablePlugin(id string) *model.AppError {
pluginsEnvironment := ch.GetPluginsEnvironment() pluginsEnvironment := s.GetPluginsEnvironment()
if pluginsEnvironment == nil { if pluginsEnvironment == nil {
return model.NewAppError("EnablePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented) return model.NewAppError("EnablePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
} }
@@ -446,16 +540,16 @@ func (ch *Channels) enablePlugin(id string) *model.AppError {
return model.NewAppError("EnablePlugin", "app.plugin.not_installed.app_error", nil, "", http.StatusNotFound) 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) 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} cfg.PluginSettings.PluginStates[id] = &model.PluginState{Enable: true}
}) })
// This call will implicitly invoke SyncPluginsActiveState which will activate enabled plugins. // 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" { if err.Id == "ent.cluster.save_config.error" {
return model.NewAppError("EnablePlugin", "app.plugin.cluster.save_config.app_error", nil, "", http.StatusInternalServerError) return model.NewAppError("EnablePlugin", "app.plugin.cluster.save_config.app_error", nil, "", http.StatusInternalServerError)
} }
@@ -468,7 +562,7 @@ func (ch *Channels) enablePlugin(id string) *model.AppError {
// DisablePlugin will set the config for an installed plugin to disabled, triggering deactivation if active. // DisablePlugin will set the config for an installed plugin to disabled, triggering deactivation if active.
// Notifies cluster peers through config change. // Notifies cluster peers through config change.
func (a *App) DisablePlugin(id string) *model.AppError { func (a *App) DisablePlugin(id string) *model.AppError {
appErr := a.ch.disablePlugin(id) appErr := a.ch.srv.pluginService.disablePlugin(id)
if appErr != nil { if appErr != nil {
return appErr return appErr
} }
@@ -476,22 +570,22 @@ func (a *App) DisablePlugin(id string) *model.AppError {
return nil return nil
} }
func (ch *Channels) disablePlugin(id string) *model.AppError { func (s *PluginService) disablePlugin(id string) *model.AppError {
// find all collectionTypes registered by plugin // find all collectionTypes registered by plugin
for collectionTypeToRemove, existingPluginId := range ch.collectionTypes { for collectionTypeToRemove, existingPluginId := range s.collectionTypes {
if existingPluginId != id { if existingPluginId != id {
continue continue
} }
// find all topicTypes for existing collectionType // find all topicTypes for existing collectionType
for topicTypeToRemove, existingCollectionType := range ch.topicTypes { for topicTypeToRemove, existingCollectionType := range s.topicTypes {
if existingCollectionType == collectionTypeToRemove { 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 { if pluginsEnvironment == nil {
return model.NewAppError("DisablePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented) return model.NewAppError("DisablePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
} }
@@ -515,13 +609,13 @@ func (ch *Channels) disablePlugin(id string) *model.AppError {
return model.NewAppError("DisablePlugin", "app.plugin.not_installed.app_error", nil, "", http.StatusNotFound) 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} 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. // 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) return model.NewAppError("DisablePlugin", "app.plugin.config.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
} }
@@ -608,8 +702,8 @@ func (a *App) GetMarketplacePlugins(filter *model.MarketplacePluginFilter) ([]*m
// getPrepackagedPlugin returns a pre-packaged plugin. // getPrepackagedPlugin returns a pre-packaged plugin.
// //
// If version is empty, the first matching plugin is returned. // If version is empty, the first matching plugin is returned.
func (ch *Channels) getPrepackagedPlugin(pluginID, version string) (*plugin.PrepackagedPlugin, *model.AppError) { func (s *PluginService) getPrepackagedPlugin(pluginID, version string) (*plugin.PrepackagedPlugin, *model.AppError) {
pluginsEnvironment := ch.GetPluginsEnvironment() pluginsEnvironment := s.GetPluginsEnvironment()
if pluginsEnvironment == nil { if pluginsEnvironment == nil {
return nil, model.NewAppError("getPrepackagedPlugin", "app.plugin.config.app_error", nil, "plugin environment is nil", http.StatusInternalServerError) return nil, model.NewAppError("getPrepackagedPlugin", "app.plugin.config.app_error", nil, "plugin environment is nil", http.StatusInternalServerError)
} }
@@ -627,16 +721,16 @@ func (ch *Channels) getPrepackagedPlugin(pluginID, version string) (*plugin.Prep
// getRemoteMarketplacePlugin returns plugin from marketplace-server. // getRemoteMarketplacePlugin returns plugin from marketplace-server.
// //
// If version is empty, the latest compatible version is used. // 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( marketplaceClient, err := marketplace.NewClient(
*ch.cfgSvc.Config().PluginSettings.MarketplaceURL, *s.platform.Config().PluginSettings.MarketplaceURL,
ch.srv.HTTPService(), s.httpService,
) )
if err != nil { if err != nil {
return nil, model.NewAppError("GetMarketplacePlugin", "app.plugin.marketplace_client.app_error", nil, "", http.StatusInternalServerError).Wrap(err) 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 filter.PluginId = pluginID
var plugin *model.BaseMarketplacePlugin var plugin *model.BaseMarketplacePlugin
@@ -791,15 +885,15 @@ func (a *App) mergeLocalPlugins(remoteMarketplacePlugins map[string]*model.Marke
} }
func (a *App) getBaseMarketplaceFilter() *model.MarketplacePluginFilter { 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{ filter := &model.MarketplacePluginFilter{
ServerVersion: model.CurrentVersion, ServerVersion: model.CurrentVersion,
} }
license := ch.srv.License() license := s.platform.License()
if license != nil && license.HasEnterpriseMarketplacePlugins() { if license != nil && license.HasEnterpriseMarketplacePlugins() {
filter.EnterprisePlugins = true filter.EnterprisePlugins = true
} }
@@ -846,8 +940,8 @@ func pluginMatchesFilter(manifest *model.Manifest, filter string) bool {
// it will notify all connected websocket clients (across all peers) to trigger the (re-)installation. // 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 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. // There is also a chance that multiple servers notify, but the webapp handles this idempotently.
func (ch *Channels) notifyPluginEnabled(manifest *model.Manifest) error { func (s *PluginService) notifyPluginEnabled(manifest *model.Manifest) error {
pluginsEnvironment := ch.GetPluginsEnvironment() pluginsEnvironment := s.GetPluginsEnvironment()
if pluginsEnvironment == nil { if pluginsEnvironment == nil {
return errors.New("pluginsEnvironment is nil") return errors.New("pluginsEnvironment is nil")
} }
@@ -857,15 +951,15 @@ func (ch *Channels) notifyPluginEnabled(manifest *model.Manifest) error {
var statuses model.PluginStatuses var statuses model.PluginStatuses
if ch.srv.platform.Cluster() != nil { if s.platform.Cluster() != nil {
var err *model.AppError var err *model.AppError
statuses, err = ch.srv.platform.Cluster().GetPluginStatuses() statuses, err = s.platform.Cluster().GetPluginStatuses()
if err != nil { if err != nil {
return err return err
} }
} }
localStatus, err := ch.GetPluginStatus(manifest.Id) localStatus, err := s.GetPluginStatus(manifest.Id)
if err != nil { if err != nil {
return err return err
} }
@@ -885,26 +979,26 @@ func (ch *Channels) notifyPluginEnabled(manifest *model.Manifest) error {
// Notify all cluster peer clients. // Notify all cluster peer clients.
message := model.NewWebSocketEvent(model.WebsocketEventPluginEnabled, "", "", "", nil, "") message := model.NewWebSocketEvent(model.WebsocketEventPluginEnabled, "", "", "", nil, "")
message.Add("manifest", manifest.ClientManifest()) message.Add("manifest", manifest.ClientManifest())
ch.srv.platform.Publish(message) s.platform.Publish(message)
return nil return nil
} }
func (ch *Channels) getPluginsFromFolder() (map[string]*pluginSignaturePath, *model.AppError) { func (s *PluginService) getPluginsFromFolder() (map[string]*pluginSignaturePath, *model.AppError) {
fileStorePaths, appErr := ch.srv.listDirectory(fileStorePluginFolder, false) fileStorePaths, appErr := s.fileStore.ListDirectory(fileStorePluginFolder)
if appErr != nil { if appErr != nil {
return nil, model.NewAppError("getPluginsFromDir", "app.plugin.sync.list_filestore.app_error", nil, "", http.StatusInternalServerError).Wrap(appErr) 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) pluginSignaturePathMap := make(map[string]*pluginSignaturePath)
fsPrefix := "" fsPrefix := ""
if *ch.cfgSvc.Config().FileSettings.DriverName == model.ImageDriverS3 { if *s.platform.Config().FileSettings.DriverName == model.ImageDriverS3 {
ptr := ch.cfgSvc.Config().FileSettings.AmazonS3PathPrefix ptr := s.platform.Config().FileSettings.AmazonS3PathPrefix
if ptr != nil && *ptr != "" { if ptr != nil && *ptr != "" {
fsPrefix = *ptr + "/" fsPrefix = *ptr + "/"
} }
@@ -937,7 +1031,7 @@ func (ch *Channels) getPluginsFromFilePaths(fileStorePaths []string) map[string]
return pluginSignaturePathMap return pluginSignaturePathMap
} }
func (ch *Channels) processPrepackagedPlugins(pluginsDir string) []*plugin.PrepackagedPlugin { func (s *PluginService) processPrepackagedPlugins(pluginsDir string) []*plugin.PrepackagedPlugin {
prepackagedPluginsDir, found := fileutils.FindDir(pluginsDir) prepackagedPluginsDir, found := fileutils.FindDir(pluginsDir)
if !found { if !found {
return nil return nil
@@ -953,7 +1047,7 @@ func (ch *Channels) processPrepackagedPlugins(pluginsDir string) []*plugin.Prepa
return nil return nil
} }
pluginSignaturePathMap := ch.getPluginsFromFilePaths(fileStorePaths) pluginSignaturePathMap := s.getPluginsFromFilePaths(fileStorePaths)
plugins := make([]*plugin.PrepackagedPlugin, 0, len(pluginSignaturePathMap)) plugins := make([]*plugin.PrepackagedPlugin, 0, len(pluginSignaturePathMap))
prepackagedPlugins := make(chan *plugin.PrepackagedPlugin, len(pluginSignaturePathMap)) prepackagedPlugins := make(chan *plugin.PrepackagedPlugin, len(pluginSignaturePathMap))
@@ -962,7 +1056,7 @@ func (ch *Channels) processPrepackagedPlugins(pluginsDir string) []*plugin.Prepa
wg.Add(1) wg.Add(1)
go func(psPath *pluginSignaturePath) { go func(psPath *pluginSignaturePath) {
defer wg.Done() defer wg.Done()
p, err := ch.processPrepackagedPlugin(psPath) p, err := s.processPrepackagedPlugin(psPath)
if err != nil { if err != nil {
mlog.Error("Failed to install prepackaged plugin", mlog.String("path", psPath.path), mlog.Err(err)) mlog.Error("Failed to install prepackaged plugin", mlog.String("path", psPath.path), mlog.Err(err))
return return
@@ -983,7 +1077,7 @@ func (ch *Channels) processPrepackagedPlugins(pluginsDir string) []*plugin.Prepa
// processPrepackagedPlugin will return the prepackaged plugin metadata and will also // processPrepackagedPlugin will return the prepackaged plugin metadata and will also
// install the prepackaged plugin if it had been previously enabled and AutomaticPrepackagedPlugins is true. // 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)) mlog.Debug("Processing prepackaged plugin", mlog.String("path", pluginPath.path))
fileReader, err := os.Open(pluginPath.path) fileReader, err := os.Open(pluginPath.path)
@@ -1004,18 +1098,18 @@ func (ch *Channels) processPrepackagedPlugin(pluginPath *pluginSignaturePath) (*
} }
// Skip installing the plugin at all if automatic prepackaged plugins is disabled // 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 return plugin, nil
} }
// Skip installing if the plugin is has not been previously enabled. // 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 { if pluginState == nil || !pluginState.Enable {
return plugin, nil return plugin, nil
} }
mlog.Debug("Installing prepackaged plugin", mlog.String("path", pluginPath.path)) 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) return nil, errors.Wrapf(err, "Failed to install extracted prepackaged plugin %s", pluginPath.path)
} }
@@ -1023,24 +1117,24 @@ func (ch *Channels) processPrepackagedPlugin(pluginPath *pluginSignaturePath) (*
} }
// installFeatureFlagPlugins handles the automatic installation/upgrade of plugins from feature flags // installFeatureFlagPlugins handles the automatic installation/upgrade of plugins from feature flags
func (ch *Channels) installFeatureFlagPlugins() { func (s *PluginService) installFeatureFlagPlugins() {
ffControledPlugins := ch.cfgSvc.Config().FeatureFlags.Plugins() ffControledPlugins := s.platform.Config().FeatureFlags.Plugins()
// Respect the automatic prepackaged disable setting // Respect the automatic prepackaged disable setting
if !*ch.cfgSvc.Config().PluginSettings.AutomaticPrepackagedPlugins { if !*s.platform.Config().PluginSettings.AutomaticPrepackagedPlugins {
return return
} }
for pluginID, version := range ffControledPlugins { for pluginID, version := range ffControledPlugins {
// Skip installing if the plugin has been previously disabled. // 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 { 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 continue
} }
// Check if we already installed this version as InstallMarketplacePlugin can't handle re-installs well. // 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 pluginExists := err == nil
if pluginExists && pluginStatus.Version == version { if pluginExists && pluginStatus.Version == version {
continue continue
@@ -1048,37 +1142,37 @@ func (ch *Channels) installFeatureFlagPlugins() {
if version != "" && version != "control" { if version != "" && version != "control" {
// If we are on-prem skip installation if this is a downgrade // 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 inCloud := license != nil && *license.Features.Cloud
if !inCloud && pluginExists { if !inCloud && pluginExists {
parsedVersion, err := semver.Parse(version) parsedVersion, err := semver.Parse(version)
if err != nil { 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 return
} }
parsedExistingVersion, err := semver.Parse(pluginStatus.Version) parsedExistingVersion, err := semver.Parse(pluginStatus.Version)
if err != nil { 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 return
} }
if parsedVersion.LTE(parsedExistingVersion) { 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 return
} }
} }
_, err := ch.InstallMarketplacePlugin(&model.InstallMarketplacePluginRequest{ _, err := s.InstallMarketplacePlugin(&model.InstallMarketplacePluginRequest{
Id: pluginID, Id: pluginID,
Version: version, Version: version,
}) })
if err != nil { 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 { } else {
if err := ch.enablePlugin(pluginID); err != nil { if err := s.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)) 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 { } 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))
} }
} }
} }
@@ -1133,15 +1227,15 @@ func getIcon(iconPath string) (string, error) {
return fmt.Sprintf("data:image/svg+xml;base64,%s", base64.StdEncoding.EncodeToString(icon)), nil 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 { switch pluginID {
case model.PluginIdApps: case model.PluginIdApps:
// Tie Apps proxy disabled status to the feature flag. // Tie Apps proxy disabled status to the feature flag.
if !ch.cfgSvc.Config().FeatureFlags.AppsEnabled { if !s.platform.Config().FeatureFlags.AppsEnabled {
return true, false return true, false
} }
case model.PluginIdCalls: case model.PluginIdCalls:
if !ch.cfgSvc.Config().FeatureFlags.CallsEnabled { if !s.platform.Config().FeatureFlags.CallsEnabled {
return true, false return true, false
} }
} }

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

@@ -886,7 +886,7 @@ func (api *PluginAPI) DisablePlugin(id string) *model.AppError {
} }
func (api *PluginAPI) RemovePlugin(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) { 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 // RegisterCollectionAndTopic informs the server that this plugin handles
// the given collection and topic types. // the given collection and topic types.
func (api *PluginAPI) RegisterCollectionAndTopic(collectionType, topicType string) error { 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) { func (api *PluginAPI) CreateUploadSession(us *model.UploadSession) (*model.UploadSession, error) {

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

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

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

@@ -22,6 +22,10 @@ type PluginCommand struct {
} }
func (a *App) RegisterPluginCommand(pluginID string, command *model.Command) error { 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 == "" { if command.Trigger == "" {
return errors.New("invalid command") return errors.New("invalid command")
} }
@@ -55,10 +59,10 @@ func (a *App) RegisterPluginCommand(pluginID string, command *model.Command) err
AutocompleteIconData: command.AutocompleteIconData, AutocompleteIconData: command.AutocompleteIconData,
} }
a.ch.pluginCommandsLock.Lock() s.pluginCommandsLock.Lock()
defer a.ch.pluginCommandsLock.Unlock() 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.Command.Trigger == command.Trigger && pc.Command.TeamId == command.TeamId {
if pc.PluginId == pluginID { if pc.PluginId == pluginID {
pc.Command = command 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, Command: command,
PluginId: pluginID, PluginId: pluginID,
}) })
@@ -75,39 +79,47 @@ func (a *App) RegisterPluginCommand(pluginID string, command *model.Command) err
} }
func (a *App) UnregisterPluginCommand(pluginID, teamID, trigger string) { 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) trigger = strings.ToLower(trigger)
a.ch.pluginCommandsLock.Lock() s.pluginCommandsLock.Lock()
defer a.ch.pluginCommandsLock.Unlock() defer s.pluginCommandsLock.Unlock()
var remaining []*PluginCommand var remaining []*PluginCommand
for _, pc := range a.ch.pluginCommands { for _, pc := range s.pluginCommands {
if pc.Command.TeamId != teamID || pc.Command.Trigger != trigger { if pc.Command.TeamId != teamID || pc.Command.Trigger != trigger {
remaining = append(remaining, pc) remaining = append(remaining, pc)
} }
} }
a.ch.pluginCommands = remaining s.pluginCommands = remaining
} }
func (ch *Channels) unregisterPluginCommands(pluginID string) { func (s *PluginService) unregisterPluginCommands(pluginID string) {
ch.pluginCommandsLock.Lock() s.pluginCommandsLock.Lock()
defer ch.pluginCommandsLock.Unlock() defer s.pluginCommandsLock.Unlock()
var remaining []*PluginCommand var remaining []*PluginCommand
for _, pc := range ch.pluginCommands { for _, pc := range s.pluginCommands {
if pc.PluginId != pluginID { if pc.PluginId != pluginID {
remaining = append(remaining, pc) remaining = append(remaining, pc)
} }
} }
ch.pluginCommands = remaining s.pluginCommands = remaining
} }
func (a *App) PluginCommandsForTeam(teamID string) []*model.Command { func (a *App) PluginCommandsForTeam(teamID string) []*model.Command {
a.ch.pluginCommandsLock.RLock() return a.Srv().pluginService.PluginCommandsForTeam(teamID)
defer a.ch.pluginCommandsLock.RUnlock() }
func (s *PluginService) PluginCommandsForTeam(teamID string) []*model.Command {
s.pluginCommandsLock.RLock()
defer s.pluginCommandsLock.RUnlock()
var commands []*model.Command var commands []*model.Command
for _, pc := range a.ch.pluginCommands { for _, pc := range s.pluginCommands {
if pc.Command.TeamId == "" || pc.Command.TeamId == teamID { if pc.Command.TeamId == "" || pc.Command.TeamId == teamID {
commands = append(commands, pc.Command) commands = append(commands, pc.Command)
} }
@@ -115,6 +127,24 @@ func (a *App) PluginCommandsForTeam(teamID string) []*model.Command {
return commands 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 // 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. // 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) { 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 := parts[0][1:]
trigger = strings.ToLower(trigger) trigger = strings.ToLower(trigger)
var matched *PluginCommand matched := a.Srv().pluginService.getPluginCommandFromArgs(args)
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()
if matched == nil { if matched == nil {
return nil, nil, nil return nil, nil, nil
} }

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

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

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

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

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

@@ -9,10 +9,10 @@ import (
"github.com/mattermost/mattermost-server/v6/model" "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) buf, _ := json.Marshal(data)
if ch.srv.platform.Cluster() != nil { if s.platform.Cluster() != nil {
ch.srv.platform.Cluster().SendClusterMessage(&model.ClusterMessage{ s.platform.Cluster().SendClusterMessage(&model.ClusterMessage{
Event: event, Event: event,
SendType: model.ClusterSendReliable, SendType: model.ClusterSendReliable,
WaitForAllToSend: true, WaitForAllToSend: true,

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

@@ -33,10 +33,10 @@ func SetAppEnvironmentWithPlugins(t *testing.T, pluginCode []string, app *App, a
webappPluginDir, err := os.MkdirTemp("", "") webappPluginDir, err := os.MkdirTemp("", "")
require.NoError(t, err) require.NoError(t, err)
env, err := plugin.NewEnvironment(apiFunc, NewDriverImpl(app.Srv()), pluginDir, webappPluginDir, app.Log(), nil) env, err := plugin.NewEnvironment(apiFunc, NewDriverImpl(app.Srv().Platform()), pluginDir, webappPluginDir, app.Log(), nil)
require.NoError(t, err) require.NoError(t, err)
app.ch.SetPluginsEnvironment(env) app.PluginService().SetPluginsEnvironment(env)
pluginIDs := []string{} pluginIDs := []string{}
activationErrors := []error{} activationErrors := []error{}
for _, code := range pluginCode { for _, code := range pluginCode {
@@ -1030,10 +1030,10 @@ func TestHookMetrics(t *testing.T) {
defer os.RemoveAll(pluginDir) defer os.RemoveAll(pluginDir)
defer os.RemoveAll(webappPluginDir) defer os.RemoveAll(webappPluginDir)
env, err := plugin.NewEnvironment(th.NewPluginAPI, NewDriverImpl(th.Server), pluginDir, webappPluginDir, th.App.Log(), metricsMock) env, err := plugin.NewEnvironment(th.NewPluginAPI, NewDriverImpl(th.Server.Platform()), pluginDir, webappPluginDir, th.App.Log(), metricsMock)
require.NoError(t, err) require.NoError(t, err)
th.App.ch.SetPluginsEnvironment(env) th.App.PluginService().SetPluginsEnvironment(env)
pluginID := model.NewId() pluginID := model.NewId()
backend := filepath.Join(pluginDir, pluginID, "backend.exe") backend := filepath.Join(pluginDir, pluginID, "backend.exe")

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

@@ -58,10 +58,10 @@ const managedPluginFileName = ".filestore"
// fileStorePluginFolder is the folder name in the file store of the plugin bundles installed. // fileStorePluginFolder is the folder name in the file store of the plugin bundles installed.
const fileStorePluginFolder = "plugins" 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)) 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 { if appErr != nil {
mlog.Error("Failed to get plugin signatures from filestore. Can't install plugin from data.", mlog.Err(appErr)) mlog.Error("Failed to get plugin signatures from filestore. Can't install plugin from data.", mlog.Err(appErr))
return return
@@ -72,53 +72,53 @@ func (ch *Channels) installPluginFromData(data model.PluginEventData) {
return return
} }
reader, appErr := ch.srv.fileReader(plugin.path) reader, err := s.fileStore.Reader(plugin.path)
if appErr != nil { if err != nil {
mlog.Error("Failed to open plugin bundle from file store.", mlog.String("bundle", plugin.path), mlog.Err(appErr)) mlog.Error("Failed to open plugin bundle from file store.", mlog.String("bundle", plugin.path), mlog.Err(err))
return return
} }
defer reader.Close() defer reader.Close()
var signature filestore.ReadCloseSeeker var signature filestore.ReadCloseSeeker
if *ch.cfgSvc.Config().PluginSettings.RequirePluginSignature { if *s.platform.Config().PluginSettings.RequirePluginSignature {
signature, appErr = ch.srv.fileReader(plugin.signaturePath) signature, err = s.fileStore.Reader(plugin.signaturePath)
if appErr != nil { if err != nil {
mlog.Error("Failed to open plugin signature from file store.", mlog.Err(appErr)) mlog.Error("Failed to open plugin signature from file store.", mlog.Err(err))
return return
} }
defer signature.Close() defer signature.Close()
} }
manifest, appErr := ch.installPluginLocally(reader, signature, installPluginLocallyAlways) manifest, appErr := s.installPluginLocally(reader, signature, installPluginLocallyAlways)
if appErr != nil { if appErr != nil {
mlog.Error("Failed to sync plugin from file store", mlog.String("bundle", plugin.path), mlog.Err(appErr)) mlog.Error("Failed to sync plugin from file store", mlog.String("bundle", plugin.path), mlog.Err(appErr))
return return
} }
if err := ch.notifyPluginEnabled(manifest); err != nil { if err2 := s.notifyPluginEnabled(manifest); err2 != nil {
mlog.Error("Failed notify plugin enabled", mlog.Err(err)) mlog.Error("Failed notify plugin enabled", mlog.Err(err2))
} }
if err := ch.notifyPluginStatusesChanged(); err != nil { if err2 := s.notifyPluginStatusesChanged(); err2 != nil {
mlog.Error("Failed to notify plugin status changed", mlog.Err(err)) 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)) 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)) 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)) mlog.Warn("failed to notify plugin status changed", mlog.Err(err))
} }
} }
// InstallPluginWithSignature verifies and installs plugin. // InstallPluginWithSignature verifies and installs plugin.
func (ch *Channels) installPluginWithSignature(pluginFile, signature io.ReadSeeker) (*model.Manifest, *model.AppError) { func (s *PluginService) installPluginWithSignature(pluginFile, signature io.ReadSeeker) (*model.Manifest, *model.AppError) {
return ch.installPlugin(pluginFile, signature, installPluginLocallyAlways) return s.installPlugin(pluginFile, signature, installPluginLocallyAlways)
} }
// InstallPlugin unpacks and installs a plugin but does not enable or activate it. // 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) { 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) { func (s *PluginService) installPlugin(pluginFile, signature io.ReadSeeker, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) {
manifest, appErr := ch.installPluginLocally(pluginFile, signature, installationStrategy) manifest, appErr := s.installPluginLocally(pluginFile, signature, installationStrategy)
if appErr != nil { if appErr != nil {
return nil, appErr return nil, appErr
} }
if signature != nil { if signature != nil {
signature.Seek(0, 0) signature.Seek(0, 0)
if _, appErr = ch.srv.writeFile(signature, getSignatureStorePath(manifest.Id)); appErr != nil { 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(appErr) 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. // Store bundle in the file store to allow access from other servers.
pluginFile.Seek(0, 0) 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) return nil, model.NewAppError("uploadPlugin", "app.plugin.store_bundle.app_error", nil, "", http.StatusInternalServerError).Wrap(appErr)
} }
ch.notifyClusterPluginEvent( s.notifyClusterPluginEvent(
model.ClusterEventInstallPlugin, model.ClusterEventInstallPlugin,
model.PluginEventData{ model.PluginEventData{
Id: manifest.Id, 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)) 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)) 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 // 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. // 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 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" { if appErr != nil && appErr.Id != "app.plugin.marketplace_plugins.not_found.app_error" {
return nil, appErr return nil, appErr
} }
@@ -192,9 +192,9 @@ func (ch *Channels) InstallMarketplacePlugin(request *model.InstallMarketplacePl
signatureFile = bytes.NewReader(prepackagedPlugin.Signature) signatureFile = bytes.NewReader(prepackagedPlugin.Signature)
} }
if *ch.cfgSvc.Config().PluginSettings.EnableRemoteMarketplace { if *s.platform.Config().PluginSettings.EnableRemoteMarketplace {
var plugin *model.BaseMarketplacePlugin var plugin *model.BaseMarketplacePlugin
plugin, appErr = ch.getRemoteMarketplacePlugin(request.Id, request.Version) plugin, appErr = s.getRemoteMarketplacePlugin(request.Id, request.Version)
if appErr != nil { if appErr != nil {
return nil, appErr 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 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 { if err != nil {
return nil, model.NewAppError("InstallMarketplacePlugin", "app.plugin.install_marketplace_plugin.app_error", nil, "", http.StatusInternalServerError).Wrap(err) 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) 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 { if appErr != nil {
return nil, appErr return nil, appErr
} }
@@ -253,15 +253,15 @@ const (
installPluginLocallyAlways installPluginLocallyAlways
) )
func (ch *Channels) installPluginLocally(pluginFile, signature io.ReadSeeker, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) { func (s *PluginService) installPluginLocally(pluginFile, signature io.ReadSeeker, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) {
pluginsEnvironment := ch.GetPluginsEnvironment() pluginsEnvironment := s.GetPluginsEnvironment()
if pluginsEnvironment == nil { if pluginsEnvironment == nil {
return nil, model.NewAppError("installPluginLocally", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented) return nil, model.NewAppError("installPluginLocally", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
} }
// verify signature // verify signature
if signature != nil { if signature != nil {
if err := ch.verifyPlugin(pluginFile, signature); err != nil { if err := s.verifyPlugin(pluginFile, signature); err != nil {
return nil, err return nil, err
} }
} }
@@ -277,7 +277,7 @@ func (ch *Channels) installPluginLocally(pluginFile, signature io.ReadSeeker, in
return nil, appErr return nil, appErr
} }
manifest, appErr = ch.installExtractedPlugin(manifest, pluginDir, installationStrategy) manifest, appErr = s.installExtractedPlugin(manifest, pluginDir, installationStrategy)
if appErr != nil { if appErr != nil {
return nil, appErr return nil, appErr
} }
@@ -312,8 +312,8 @@ func extractPlugin(pluginFile io.ReadSeeker, extractDir string) (*model.Manifest
return manifest, extractDir, nil return manifest, extractDir, nil
} }
func (ch *Channels) installExtractedPlugin(manifest *model.Manifest, fromPluginDir string, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) { func (s *PluginService) installExtractedPlugin(manifest *model.Manifest, fromPluginDir string, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) {
pluginsEnvironment := ch.GetPluginsEnvironment() pluginsEnvironment := s.GetPluginsEnvironment()
if pluginsEnvironment == nil { if pluginsEnvironment == nil {
return nil, model.NewAppError("installExtractedPlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented) 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. // 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)) 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) 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) err = utils.CopyDir(fromPluginDir, pluginPath)
if err != nil { if err != nil {
return nil, model.NewAppError("installExtractedPlugin", "app.plugin.mvdir.app_error", nil, "", http.StatusInternalServerError).Wrap(err) 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. // 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 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 return manifest, nil
} }
@@ -405,49 +405,49 @@ func (ch *Channels) installExtractedPlugin(manifest *model.Manifest, fromPluginD
return manifest, nil 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 // Disable plugin before removal to make sure this
// plugin remains disabled on re-install. // plugin remains disabled on re-install.
if err := ch.disablePlugin(id); err != nil { if err := s.disablePlugin(id); err != nil {
return err return err
} }
if err := ch.removePluginLocally(id); err != nil { if err := s.removePluginLocally(id); err != nil {
return err return err
} }
// Remove bundle from the file store. // Remove bundle from the file store.
storePluginFileName := getBundleStorePath(id) storePluginFileName := getBundleStorePath(id)
bundleExist, err := ch.srv.fileExists(storePluginFileName) bundleExist, err := s.fileStore.FileExists(storePluginFileName)
if err != nil { if err != nil {
return model.NewAppError("removePlugin", "app.plugin.remove_bundle.app_error", nil, "", http.StatusInternalServerError).Wrap(err) return model.NewAppError("removePlugin", "app.plugin.remove_bundle.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
} }
if !bundleExist { if !bundleExist {
return nil 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) return model.NewAppError("removePlugin", "app.plugin.remove_bundle.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
} }
if err = ch.removeSignature(id); err != nil { if err2 := s.removeSignature(id); err2 != nil {
mlog.Warn("Can't remove signature", mlog.Err(err)) mlog.Warn("Can't remove signature", mlog.Err(err2))
} }
ch.notifyClusterPluginEvent( s.notifyClusterPluginEvent(
model.ClusterEventRemovePlugin, model.ClusterEventRemovePlugin,
model.PluginEventData{ model.PluginEventData{
Id: id, 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)) mlog.Warn("Failed to notify plugin status changed", mlog.Err(err))
} }
return nil return nil
} }
func (ch *Channels) removePluginLocally(id string) *model.AppError { func (s *PluginService) removePluginLocally(id string) *model.AppError {
pluginsEnvironment := ch.GetPluginsEnvironment() pluginsEnvironment := s.GetPluginsEnvironment()
if pluginsEnvironment == nil { if pluginsEnvironment == nil {
return model.NewAppError("removePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented) 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.Deactivate(id)
pluginsEnvironment.RemovePlugin(id) pluginsEnvironment.RemovePlugin(id)
ch.unregisterPluginCommands(id) s.unregisterPluginCommands(id)
if err := os.RemoveAll(pluginPath); err != nil { if err := os.RemoveAll(pluginPath); err != nil {
return model.NewAppError("removePlugin", "app.plugin.remove.app_error", nil, "", http.StatusInternalServerError).Wrap(err) 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 return nil
} }
func (ch *Channels) removeSignature(pluginID string) *model.AppError { func (s *PluginService) removeSignature(pluginID string) *model.AppError {
filePath := getSignatureStorePath(pluginID) filePath := getSignatureStorePath(pluginID)
exists, err := ch.srv.fileExists(filePath) exists, err := s.fileStore.FileExists(filePath)
if err != nil { if err != nil {
return model.NewAppError("removeSignature", "app.plugin.remove_bundle.app_error", nil, "", http.StatusInternalServerError).Wrap(err) 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)) mlog.Debug("no plugin signature to remove", mlog.String("plugin_id", pluginID))
return nil 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 model.NewAppError("removeSignature", "app.plugin.remove_bundle.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
} }
return nil return nil

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

@@ -73,7 +73,7 @@ func TestInstallPluginLocally(t *testing.T) {
th := Setup(t) th := Setup(t)
defer th.TearDown() 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) require.NotNil(t, appErr)
assert.Equal(t, "app.plugin.extract.app_error", appErr.Id, appErr.Error()) assert.Equal(t, "app.plugin.extract.app_error", appErr.Id, appErr.Error())
require.Nil(t, actualManifest) require.Nil(t, actualManifest)
@@ -87,7 +87,7 @@ func TestInstallPluginLocally(t *testing.T) {
{"test", "test file"}, {"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) require.NotNil(t, appErr)
assert.Equal(t, "app.plugin.manifest.app_error", appErr.Id, appErr.Error()) assert.Equal(t, "app.plugin.manifest.app_error", appErr.Id, appErr.Error())
require.Nil(t, actualManifest) require.Nil(t, actualManifest)
@@ -106,7 +106,7 @@ func TestInstallPluginLocally(t *testing.T) {
{"plugin.json", string(manifestJSON)}, {"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 { if actualManifest != nil {
require.Equal(t, manifest, actualManifest) require.Equal(t, manifest, actualManifest)
} }
@@ -134,7 +134,7 @@ func TestInstallPluginLocally(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
for _, bundleInfo := range bundleInfos { 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) require.Nilf(t, err, "failed to remove existing plugin %s", bundleInfo.Manifest.Id)
} }
} }

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

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

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

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

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

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

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

@@ -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. // 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 { 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 { if err := verifySignature(bytes.NewReader(mattermostPluginPublicKey), plugin, signature); err == nil {
return nil return nil
} }
publicKeys := ch.cfgSvc.Config().PluginSettings.SignaturePublicKeyFiles publicKeys := s.platform.Config().PluginSettings.SignaturePublicKeyFiles
for _, pk := range publicKeys { for _, pk := range publicKeys {
pkBytes, appErr := ch.srv.getPublicKey(pk) pkBytes, appErr := s.platform.GetConfigFile(pk)
if appErr != nil { if appErr != nil {
mlog.Warn("Unable to get public key for ", mlog.String("filename", pk)) mlog.Warn("Unable to get public key for ", mlog.String("filename", pk))
continue continue

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

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

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

@@ -346,7 +346,7 @@ func TestServePluginRequest(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/plugins/foo/bar", nil) 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) 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"}) 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) var assertions func(*http.Request)
router := mux.NewRouter() router := mux.NewRouter()
router.HandleFunc("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}/{anything:.*}", func(_ http.ResponseWriter, r *http.Request) { 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) assertions(r)
}) })
}) })
@@ -625,7 +625,7 @@ func TestPluginSync(t *testing.T) {
appErr = th.App.DeletePublicKey("pub_key") appErr = th.App.DeletePublicKey("pub_key")
checkNoError(t, appErr) checkNoError(t, appErr)
appErr = th.App.ch.RemovePlugin("testplugin") appErr = th.App.PluginService().RemovePlugin("testplugin")
checkNoError(t, appErr) checkNoError(t, appErr)
}) })
}) })
@@ -642,7 +642,7 @@ func TestChannelsPluginsInit(t *testing.T) {
path, _ := fileutils.FindDir("tests") path, _ := fileutils.FindDir("tests")
require.NotPanics(t, func() { 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() th.TestLogger.Flush()
// We shutdown plugins first so that the read on the log buffer is race-free. // We shutdown plugins first so that the read on the log buffer is race-free.
th.App.ch.ShutDownPlugins() th.App.PluginService().ShutDownPlugins()
tearDown() tearDown()
testlib.AssertLog(t, th.LogBuffer, mlog.LvlDebug.Name, "panic: some text from panic") 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.NoError(t, err)
require.NotNil(t, pluginBytes) 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.Nil(t, appErr)
require.Equal(t, "testplugin", manifest.Id) require.Equal(t, "testplugin", manifest.Id)
@@ -848,7 +848,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
*cfg.PluginSettings.EnableRemoteMarketplace = false *cfg.PluginSettings.EnableRemoteMarketplace = false
}) })
plugins := th.App.ch.processPrepackagedPlugins(prepackagedPluginsDir) plugins := th.App.PluginService().processPrepackagedPlugins(prepackagedPluginsDir)
require.Len(t, plugins, 1) require.Len(t, plugins, 1)
require.Equal(t, plugins[0].Manifest.Id, "testplugin") require.Equal(t, plugins[0].Manifest.Id, "testplugin")
require.Empty(t, plugins[0].Signature, 0) require.Empty(t, plugins[0].Signature, 0)
@@ -858,7 +858,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
require.Len(t, pluginStatus, 1) require.Len(t, pluginStatus, 1)
require.Equal(t, pluginStatus[0].PluginId, "testplugin") require.Equal(t, pluginStatus[0].PluginId, "testplugin")
appErr = th.App.ch.RemovePlugin("testplugin") appErr = th.App.PluginService().RemovePlugin("testplugin")
checkNoError(t, appErr) checkNoError(t, appErr)
pluginStatus, err = env.Statuses() pluginStatus, err = env.Statuses()
@@ -875,7 +875,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
env := th.App.GetPluginsEnvironment() env := th.App.GetPluginsEnvironment()
plugins := th.App.ch.processPrepackagedPlugins(prepackagedPluginsDir) plugins := th.App.PluginService().processPrepackagedPlugins(prepackagedPluginsDir)
require.Len(t, plugins, 1) require.Len(t, plugins, 1)
require.Equal(t, plugins[0].Manifest.Id, "testplugin") require.Equal(t, plugins[0].Manifest.Id, "testplugin")
require.Empty(t, plugins[0].Signature, 0) 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")) err = testlib.CopyFile(testPlugin2SignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz.sig"))
require.NoError(t, err) require.NoError(t, err)
plugins := th.App.ch.processPrepackagedPlugins(prepackagedPluginsDir) plugins := th.App.PluginService().processPrepackagedPlugins(prepackagedPluginsDir)
require.Len(t, plugins, 2) require.Len(t, plugins, 2)
require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[0].Manifest.Id) require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[0].Manifest.Id)
require.NotEmpty(t, plugins[0].Signature) require.NotEmpty(t, plugins[0].Signature)
@@ -939,7 +939,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, pluginBytes) 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.Nil(t, appErr)
require.Equal(t, "testplugin", manifest.Id) 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")) err = testlib.CopyFile(testPlugin2SignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz.sig"))
require.NoError(t, err) require.NoError(t, err)
plugins := th.App.ch.processPrepackagedPlugins(prepackagedPluginsDir) plugins := th.App.PluginService().processPrepackagedPlugins(prepackagedPluginsDir)
require.Len(t, plugins, 2) require.Len(t, plugins, 2)
require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[0].Manifest.Id) require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[0].Manifest.Id)
require.NotEmpty(t, plugins[0].Signature) require.NotEmpty(t, plugins[0].Signature)
@@ -969,7 +969,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
require.Len(t, pluginStatus, 1) require.Len(t, pluginStatus, 1)
require.Equal(t, pluginStatus[0].PluginId, "testplugin") require.Equal(t, pluginStatus[0].PluginId, "testplugin")
appErr = th.App.ch.RemovePlugin("testplugin") appErr = th.App.PluginService().RemovePlugin("testplugin")
checkNoError(t, appErr) checkNoError(t, appErr)
pluginStatus, err = env.Statuses() pluginStatus, err = env.Statuses()
@@ -994,7 +994,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
err = testlib.CopyFile(testPlugin2SignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz.sig")) err = testlib.CopyFile(testPlugin2SignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz.sig"))
require.NoError(t, err) require.NoError(t, err)
plugins := th.App.ch.processPrepackagedPlugins(prepackagedPluginsDir) plugins := th.App.PluginService().processPrepackagedPlugins(prepackagedPluginsDir)
require.Len(t, plugins, 2) require.Len(t, plugins, 2)
require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[0].Manifest.Id) require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[0].Manifest.Id)
require.NotEmpty(t, plugins[0].Signature) require.NotEmpty(t, plugins[0].Signature)
@@ -1071,14 +1071,14 @@ func TestGetPluginStateOverride(t *testing.T) {
defer th.TearDown() defer th.TearDown()
t.Run("no override", func(t *testing.T) { 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, overrides)
require.False(t, value) require.False(t, value)
}) })
t.Run("calls override", func(t *testing.T) { t.Run("calls override", func(t *testing.T) {
t.Run("on-prem", 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, overrides)
require.False(t, value) require.False(t, value)
}) })
@@ -1086,7 +1086,7 @@ func TestGetPluginStateOverride(t *testing.T) {
t.Run("Cloud, without enabled flag", func(t *testing.T) { t.Run("Cloud, without enabled flag", func(t *testing.T) {
os.Setenv("MM_CLOUD_INSTALLATION_ID", "test") os.Setenv("MM_CLOUD_INSTALLATION_ID", "test")
defer os.Unsetenv("MM_CLOUD_INSTALLATION_ID") 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, overrides)
require.False(t, value) require.False(t, value)
}) })
@@ -1100,7 +1100,7 @@ func TestGetPluginStateOverride(t *testing.T) {
th2 := Setup(t) th2 := Setup(t)
defer th2.TearDown() 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, overrides)
require.False(t, value) require.False(t, value)
}) })
@@ -1114,7 +1114,7 @@ func TestGetPluginStateOverride(t *testing.T) {
th2 := Setup(t) th2 := Setup(t)
defer th2.TearDown() 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.True(t, overrides)
require.False(t, value) require.False(t, value)
}) })
@@ -1126,7 +1126,7 @@ func TestGetPluginStateOverride(t *testing.T) {
th2 := Setup(t) th2 := Setup(t)
defer th2.TearDown() 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.True(t, overrides)
require.False(t, value) require.False(t, value)
}) })
@@ -1134,7 +1134,7 @@ func TestGetPluginStateOverride(t *testing.T) {
t.Run("apps override", func(t *testing.T) { t.Run("apps override", func(t *testing.T) {
t.Run("without enabled flag", 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, overrides)
require.False(t, value) require.False(t, value)
}) })
@@ -1146,7 +1146,7 @@ func TestGetPluginStateOverride(t *testing.T) {
th2 := Setup(t) th2 := Setup(t)
defer th2.TearDown() 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.True(t, overrides)
require.False(t, value) require.False(t, value)
}) })

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

@@ -143,6 +143,7 @@ type Server struct {
telemetryService *telemetry.TelemetryService telemetryService *telemetry.TelemetryService
userService *users.UserService userService *users.UserService
teamService *teams.TeamService teamService *teams.TeamService
pluginService *PluginService
serviceMux sync.RWMutex serviceMux sync.RWMutex
remoteClusterService remotecluster.RemoteClusterServiceIFace remoteClusterService remotecluster.RemoteClusterServiceIFace
@@ -737,6 +738,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. // Stop products.
// This needs to happen last because products are dependent // This needs to happen last because products are dependent
// on parent services. // on parent services.
@@ -843,11 +848,18 @@ func stripPort(hostport string) string {
func (s *Server) Start() error { func (s *Server) Start() error {
// Start products. // Start products.
// This needs to happen before because products are dependent on the HTTP server. // This needs to happen before because products are dependent on the HTTP server.
// make sure channels starts first // make sure channels starts first
if err := s.products["channels"].Start(); err != nil { if err := s.products["channels"].Start(); err != nil {
return errors.Wrap(err, "Unable to start channels") 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 { for name, product := range s.products {
if name == "channels" { if name == "channels" {
continue continue

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

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

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

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

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

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