mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
[MM-48626] Move plugins environment out of Channels (#21730)
This commit is contained in:
committed by
GitHub
parent
0ce7c5e3da
commit
dcf499b51d
@@ -155,7 +155,7 @@ func installMarketplacePlugin(c *Context, w http.ResponseWriter, r *http.Request
|
||||
// https://mattermost.atlassian.net/browse/MM-41981
|
||||
pluginRequest.Version = ""
|
||||
|
||||
manifest, appErr := c.App.Channels().InstallMarketplacePlugin(pluginRequest)
|
||||
manifest, appErr := c.App.PluginService().InstallMarketplacePlugin(pluginRequest)
|
||||
if appErr != nil {
|
||||
c.Err = appErr
|
||||
return
|
||||
@@ -235,7 +235,7 @@ func removePlugin(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
err := c.App.Channels().RemovePlugin(c.Params.PluginId)
|
||||
err := c.App.PluginService().RemovePlugin(c.Params.PluginId)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
|
||||
@@ -94,7 +94,7 @@ func TestPlugin(t *testing.T) {
|
||||
assert.Equal(t, "testplugin", manifest.Id)
|
||||
})
|
||||
|
||||
th.App.Channels().RemovePlugin(manifest.Id)
|
||||
th.App.PluginService().RemovePlugin(manifest.Id)
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.PluginSettings.Enable = false })
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ func connectWebSocket(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
wc := c.App.Srv().Platform().NewWebConn(cfg, c.App, c.App.Srv().Channels())
|
||||
wc := c.App.Srv().Platform().NewWebConn(cfg, c.App, c.App.Srv())
|
||||
if c.AppContext.Session().UserId != "" {
|
||||
c.App.Srv().Platform().HubRegister(wc)
|
||||
}
|
||||
|
||||
@@ -936,6 +936,7 @@ type AppIface interface {
|
||||
PermanentDeleteTeamId(c request.CTX, teamID string) *model.AppError
|
||||
PermanentDeleteUser(c *request.Context, user *model.User) *model.AppError
|
||||
PluginCommandsForTeam(teamID string) []*model.Command
|
||||
PluginService() *PluginService
|
||||
PostActionCookieSecret() []byte
|
||||
PostAddToChannelMessage(c request.CTX, user *model.User, addedUser *model.User, channel *model.Channel, postRootId string) *model.AppError
|
||||
PostPatchWithProxyRemovedFromImageURLs(patch *model.PostPatch) *model.PostPatch
|
||||
|
||||
@@ -345,7 +345,7 @@ func (a *App) CreateChannel(c request.CTX, channel *model.Channel, addMember boo
|
||||
|
||||
a.Srv().Go(func() {
|
||||
pluginContext := pluginContext(c)
|
||||
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.ChannelHasBeenCreated(pluginContext, sc)
|
||||
return true
|
||||
}, plugin.ChannelHasBeenCreatedID)
|
||||
@@ -429,7 +429,7 @@ func (a *App) handleCreationEvent(c request.CTX, userID, otherUserID string, cha
|
||||
|
||||
a.Srv().Go(func() {
|
||||
pluginContext := pluginContext(c)
|
||||
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.ChannelHasBeenCreated(pluginContext, channel)
|
||||
return true
|
||||
}, plugin.ChannelHasBeenCreatedID)
|
||||
@@ -1597,7 +1597,7 @@ func (a *App) AddChannelMember(c request.CTX, userID string, channel *model.Chan
|
||||
|
||||
a.Srv().Go(func() {
|
||||
pluginContext := pluginContext(c)
|
||||
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.UserHasJoinedChannel(pluginContext, cm, userRequestor)
|
||||
return true
|
||||
}, plugin.UserHasJoinedChannelID)
|
||||
@@ -2173,7 +2173,7 @@ func (a *App) JoinChannel(c request.CTX, channel *model.Channel, userID string)
|
||||
|
||||
a.Srv().Go(func() {
|
||||
pluginContext := pluginContext(c)
|
||||
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.UserHasJoinedChannel(pluginContext, cm, nil)
|
||||
return true
|
||||
}, plugin.UserHasJoinedChannelID)
|
||||
@@ -2483,7 +2483,7 @@ func (a *App) removeUserFromChannel(c request.CTX, userIDToRemove string, remove
|
||||
|
||||
a.Srv().Go(func() {
|
||||
pluginContext := pluginContext(c)
|
||||
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.UserHasLeftChannel(pluginContext, cm, actorUser)
|
||||
return true
|
||||
}, plugin.UserHasLeftChannelID)
|
||||
|
||||
@@ -6,14 +6,11 @@ package app
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/app/imaging"
|
||||
"github.com/mattermost/mattermost-server/v6/app/request"
|
||||
"github.com/mattermost/mattermost-server/v6/config"
|
||||
"github.com/mattermost/mattermost-server/v6/einterfaces"
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/plugin"
|
||||
@@ -42,12 +39,6 @@ type Channels struct {
|
||||
|
||||
postActionCookieSecret []byte
|
||||
|
||||
pluginCommandsLock sync.RWMutex
|
||||
pluginCommands []*PluginCommand
|
||||
pluginsLock sync.RWMutex
|
||||
pluginsEnvironment *plugin.Environment
|
||||
pluginConfigListenerID string
|
||||
|
||||
imageProxy *imageproxy.ImageProxy
|
||||
|
||||
// cached counts that are used during notice condition validation
|
||||
@@ -79,12 +70,6 @@ type Channels struct {
|
||||
|
||||
postReminderMut sync.Mutex
|
||||
postReminderTask *model.ScheduledTask
|
||||
|
||||
// collectionTypes maps from collection types to the registering plugin id
|
||||
collectionTypes map[string]string
|
||||
// topicTypes maps from topic types to collection types
|
||||
topicTypes map[string]string
|
||||
collectionAndTopicTypesMut sync.Mutex
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -107,11 +92,9 @@ func NewChannels(services map[product.ServiceKey]any) (*Channels, error) {
|
||||
return nil, errors.New("server not passed")
|
||||
}
|
||||
ch := &Channels{
|
||||
srv: s,
|
||||
imageProxy: imageproxy.MakeImageProxy(s.platform, s.httpService, s.Log()),
|
||||
uploadLockMap: map[string]bool{},
|
||||
collectionTypes: map[string]string{},
|
||||
topicTypes: map[string]string{},
|
||||
srv: s,
|
||||
imageProxy: imageproxy.MakeImageProxy(s.platform, s.httpService, s.Log()),
|
||||
uploadLockMap: map[string]bool{},
|
||||
}
|
||||
|
||||
// To get another service:
|
||||
@@ -208,10 +191,6 @@ func NewChannels(services map[product.ServiceKey]any) (*Channels, error) {
|
||||
services[product.RouterKey] = ch.routerSvc
|
||||
|
||||
// Setup routes.
|
||||
pluginsRoute := ch.srv.Router.PathPrefix("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}").Subrouter()
|
||||
pluginsRoute.HandleFunc("", ch.ServePluginRequest)
|
||||
pluginsRoute.HandleFunc("/public/{public_file:.*}", ch.ServePluginPublicRequest)
|
||||
pluginsRoute.HandleFunc("/{anything:.*}", ch.ServePluginRequest)
|
||||
|
||||
services[product.PostKey] = &postServiceWrapper{
|
||||
app: &App{ch: ch},
|
||||
@@ -243,39 +222,6 @@ func NewChannels(services map[product.ServiceKey]any) (*Channels, error) {
|
||||
}
|
||||
|
||||
func (ch *Channels) Start() error {
|
||||
// Start plugins
|
||||
ctx := request.EmptyContext(ch.srv.Log())
|
||||
ch.initPlugins(ctx, *ch.cfgSvc.Config().PluginSettings.Directory, *ch.cfgSvc.Config().PluginSettings.ClientDirectory)
|
||||
|
||||
ch.AddConfigListener(func(prevCfg, cfg *model.Config) {
|
||||
// We compute the difference between configs
|
||||
// to ensure we don't re-init plugins unnecessarily.
|
||||
diffs, err := config.Diff(prevCfg, cfg)
|
||||
if err != nil {
|
||||
ch.srv.Log().Warn("Error in comparing configs", mlog.Err(err))
|
||||
return
|
||||
}
|
||||
|
||||
hasDiff := false
|
||||
// TODO: This could be a method on ConfigDiffs itself
|
||||
for _, diff := range diffs {
|
||||
if strings.HasPrefix(diff.Path, "PluginSettings.") {
|
||||
hasDiff = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Do only if some plugin related settings has changed.
|
||||
if hasDiff {
|
||||
if *cfg.PluginSettings.Enable {
|
||||
ch.initPlugins(ctx, *cfg.PluginSettings.Directory, *ch.cfgSvc.Config().PluginSettings.ClientDirectory)
|
||||
} else {
|
||||
ch.ShutDownPlugins()
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
// TODO: This should be moved to the platform service.
|
||||
if err := ch.srv.platform.EnsureAsymmetricSigningKey(); err != nil {
|
||||
return errors.Wrapf(err, "unable to ensure asymmetric signing key")
|
||||
@@ -289,8 +235,6 @@ func (ch *Channels) Start() error {
|
||||
}
|
||||
|
||||
func (ch *Channels) Stop() error {
|
||||
ch.ShutDownPlugins()
|
||||
|
||||
ch.dndTaskMut.Lock()
|
||||
if ch.dndTask != nil {
|
||||
ch.dndTask.Cancel()
|
||||
@@ -332,18 +276,18 @@ func (s *hooksService) RegisterHooks(productID string, hooks any) error {
|
||||
return s.ch.srv.hooksManager.AddProduct(productID, hooks)
|
||||
}
|
||||
|
||||
func (ch *Channels) RunMultiHook(hookRunnerFunc func(hooks plugin.Hooks) bool, hookId int) {
|
||||
if env := ch.GetPluginsEnvironment(); env != nil {
|
||||
func (s *Server) RunMultiHook(hookRunnerFunc func(hooks plugin.Hooks) bool, hookId int) {
|
||||
if env := s.pluginService.GetPluginsEnvironment(); env != nil {
|
||||
env.RunMultiPluginHook(hookRunnerFunc, hookId)
|
||||
}
|
||||
|
||||
// run hook for the products
|
||||
ch.srv.hooksManager.RunMultiHook(hookRunnerFunc, hookId)
|
||||
s.hooksManager.RunMultiHook(hookRunnerFunc, hookId)
|
||||
}
|
||||
|
||||
func (ch *Channels) HooksForPluginOrProduct(id string) (plugin.Hooks, error) {
|
||||
func (s *Server) HooksForPluginOrProduct(id string) (plugin.Hooks, error) {
|
||||
var hooks plugin.Hooks
|
||||
if env := ch.GetPluginsEnvironment(); env != nil {
|
||||
if env := s.pluginService.GetPluginsEnvironment(); env != nil {
|
||||
// we intentionally ignore the error here, because the id can be a product id
|
||||
// we are going to check if we have the hooks or not
|
||||
hooks, _ = env.HooksForPlugin(id)
|
||||
@@ -352,7 +296,7 @@ func (ch *Channels) HooksForPluginOrProduct(id string) (plugin.Hooks, error) {
|
||||
}
|
||||
}
|
||||
|
||||
hooks = ch.srv.hooksManager.HooksForProduct(id)
|
||||
hooks = s.hooksManager.HooksForProduct(id)
|
||||
if hooks != nil {
|
||||
return hooks, nil
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ func (s *Server) clusterInstallPluginHandler(msg *model.ClusterMessage) {
|
||||
if jsonErr := json.Unmarshal(msg.Data, &data); jsonErr != nil {
|
||||
mlog.Warn("Failed to decode from JSON", mlog.Err(jsonErr))
|
||||
}
|
||||
s.Channels().installPluginFromData(data)
|
||||
s.pluginService.installPluginFromData(data)
|
||||
}
|
||||
|
||||
func (s *Server) clusterRemovePluginHandler(msg *model.ClusterMessage) {
|
||||
@@ -24,7 +24,7 @@ func (s *Server) clusterRemovePluginHandler(msg *model.ClusterMessage) {
|
||||
if jsonErr := json.Unmarshal(msg.Data, &data); jsonErr != nil {
|
||||
mlog.Warn("Failed to decode from JSON", mlog.Err(jsonErr))
|
||||
}
|
||||
s.Channels().removePluginFromData(data)
|
||||
s.pluginService.removePluginFromData(data)
|
||||
}
|
||||
|
||||
func (s *Server) clusterPluginEventHandler(msg *model.ClusterMessage) {
|
||||
@@ -44,12 +44,7 @@ func (s *Server) clusterPluginEventHandler(msg *model.ClusterMessage) {
|
||||
return
|
||||
}
|
||||
|
||||
channels, ok := s.products["channels"].(*Channels)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
hooks, err := channels.HooksForPluginOrProduct(pluginID)
|
||||
hooks, err := s.HooksForPluginOrProduct(pluginID)
|
||||
if err != nil {
|
||||
mlog.Warn("Getting hooks for plugin failed", mlog.String("plugin_id", pluginID), mlog.Err(err))
|
||||
return
|
||||
|
||||
@@ -10,26 +10,26 @@ import (
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
)
|
||||
|
||||
func (a *App) registerCollectionAndTopic(pluginID, collectionType, topicType string) error {
|
||||
func (s *PluginService) registerCollectionAndTopic(pluginID, collectionType, topicType string) error {
|
||||
// we have a race condition due to multiple plugins calling this method
|
||||
a.ch.collectionAndTopicTypesMut.Lock()
|
||||
defer a.ch.collectionAndTopicTypesMut.Unlock()
|
||||
s.collectionAndTopicTypesMut.Lock()
|
||||
defer s.collectionAndTopicTypesMut.Unlock()
|
||||
|
||||
// check if collectionType was already registered by other plugin
|
||||
existingPluginID, ok := a.ch.collectionTypes[collectionType]
|
||||
existingPluginID, ok := s.collectionTypes[collectionType]
|
||||
if ok && existingPluginID != pluginID {
|
||||
return model.NewAppError("registerCollectionAndTopic", "app.collection.add_collection.exists.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// check if topicType was already registered to other collection
|
||||
existingCollectionType, ok := a.ch.topicTypes[topicType]
|
||||
existingCollectionType, ok := s.topicTypes[topicType]
|
||||
if ok && existingCollectionType != collectionType {
|
||||
return model.NewAppError("registerCollectionAndTopic", "app.collection.add_topic.exists.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
a.ch.collectionTypes[collectionType] = pluginID
|
||||
a.ch.topicTypes[topicType] = collectionType
|
||||
s.collectionTypes[collectionType] = pluginID
|
||||
s.topicTypes[topicType] = collectionType
|
||||
|
||||
a.ch.srv.Log().Info("registered collection and topic type", mlog.String("plugin_id", pluginID), mlog.String("collection_type", collectionType), mlog.String("topic_type", topicType))
|
||||
s.platform.Log().Info("registered collection and topic type", mlog.String("plugin_id", pluginID), mlog.String("collection_type", collectionType), mlog.String("topic_type", topicType))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -22,10 +22,10 @@ const (
|
||||
)
|
||||
|
||||
func (a *App) DownloadFromURL(downloadURL string) ([]byte, error) {
|
||||
return a.Srv().downloadFromURL(downloadURL)
|
||||
return a.Srv().pluginService.downloadFromURL(downloadURL)
|
||||
}
|
||||
|
||||
func (s *Server) downloadFromURL(downloadURL string) ([]byte, error) {
|
||||
func (s *PluginService) downloadFromURL(downloadURL string) ([]byte, error) {
|
||||
if !model.IsValidHTTPURL(downloadURL) {
|
||||
return nil, errors.Errorf("invalid url %s", downloadURL)
|
||||
}
|
||||
@@ -38,7 +38,7 @@ func (s *Server) downloadFromURL(downloadURL string) ([]byte, error) {
|
||||
return nil, errors.Errorf("insecure url not allowed %s", downloadURL)
|
||||
}
|
||||
|
||||
client := s.HTTPService().MakeClient(true)
|
||||
client := s.httpService.MakeClient(true)
|
||||
client.Timeout = HTTPRequestTimeout
|
||||
|
||||
var resp *http.Response
|
||||
|
||||
@@ -926,7 +926,7 @@ func (a *App) DoUploadFileExpectModification(c request.CTX, now time.Time, rawTe
|
||||
|
||||
var rejectionError *model.AppError
|
||||
pluginContext := pluginContext(c)
|
||||
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
var newBytes bytes.Buffer
|
||||
replacementInfo, rejectionReason := hooks.FileWillBeUploaded(pluginContext, info, bytes.NewReader(data), &newBytes)
|
||||
if rejectionReason != "" {
|
||||
|
||||
@@ -375,10 +375,10 @@ func (w *LocalResponseWriter) WriteHeader(statusCode int) {
|
||||
}
|
||||
|
||||
func (a *App) doPluginRequest(c *request.Context, method, rawURL string, values url.Values, body []byte) (*http.Response, *model.AppError) {
|
||||
return a.ch.doPluginRequest(c, method, rawURL, values, body)
|
||||
return a.ch.srv.pluginService.doPluginRequest(c, method, rawURL, values, body)
|
||||
}
|
||||
|
||||
func (ch *Channels) doPluginRequest(c *request.Context, method, rawURL string, values url.Values, body []byte) (*http.Response, *model.AppError) {
|
||||
func (s *PluginService) doPluginRequest(c *request.Context, method, rawURL string, values url.Values, body []byte) (*http.Response, *model.AppError) {
|
||||
rawURL = strings.TrimPrefix(rawURL, "/")
|
||||
inURL, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
@@ -427,7 +427,7 @@ func (ch *Channels) doPluginRequest(c *request.Context, method, rawURL string, v
|
||||
params["plugin_id"] = pluginID
|
||||
r = mux.SetURLVars(r, params)
|
||||
|
||||
ch.ServePluginRequest(w, r)
|
||||
s.ServePluginRequest(w, r)
|
||||
|
||||
resp := &http.Response{
|
||||
StatusCode: w.status,
|
||||
|
||||
@@ -159,7 +159,7 @@ func (a *App) GetUserForLogin(id, loginId string) (*model.User, *model.AppError)
|
||||
func (a *App) DoLogin(c *request.Context, w http.ResponseWriter, r *http.Request, user *model.User, deviceID string, isMobile, isOAuthUser, isSaml bool) *model.AppError {
|
||||
var rejectionReason string
|
||||
pluginContext := pluginContext(c)
|
||||
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
rejectionReason = hooks.UserWillLogIn(pluginContext, user)
|
||||
return rejectionReason == ""
|
||||
}, plugin.UserWillLogInID)
|
||||
@@ -225,7 +225,7 @@ func (a *App) DoLogin(c *request.Context, w http.ResponseWriter, r *http.Request
|
||||
}
|
||||
|
||||
a.Srv().Go(func() {
|
||||
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.UserHasLoggedIn(pluginContext, user)
|
||||
return true
|
||||
}, plugin.UserHasLoggedInID)
|
||||
|
||||
@@ -28,11 +28,6 @@ func (a *App) markAdminOnboardingComplete(c *request.Context) *model.AppError {
|
||||
}
|
||||
|
||||
func (a *App) CompleteOnboarding(c *request.Context, request *model.CompleteOnboardingRequest) *model.AppError {
|
||||
pluginsEnvironment := a.Channels().GetPluginsEnvironment()
|
||||
if pluginsEnvironment == nil {
|
||||
return a.markAdminOnboardingComplete(c)
|
||||
}
|
||||
|
||||
pluginContext := pluginContext(c)
|
||||
|
||||
for _, pluginID := range request.InstallPlugins {
|
||||
@@ -41,7 +36,7 @@ func (a *App) CompleteOnboarding(c *request.Context, request *model.CompleteOnbo
|
||||
installRequest := &model.InstallMarketplacePluginRequest{
|
||||
Id: id,
|
||||
}
|
||||
_, appErr := a.Channels().InstallMarketplacePlugin(installRequest)
|
||||
_, appErr := a.Srv().pluginService.InstallMarketplacePlugin(installRequest)
|
||||
if appErr != nil {
|
||||
mlog.Error("Failed to install plugin for onboarding", mlog.String("id", id), mlog.Err(appErr))
|
||||
return
|
||||
@@ -53,7 +48,7 @@ func (a *App) CompleteOnboarding(c *request.Context, request *model.CompleteOnbo
|
||||
return
|
||||
}
|
||||
|
||||
hooks, err := a.ch.HooksForPluginOrProduct(id)
|
||||
hooks, err := a.Srv().HooksForPluginOrProduct(id)
|
||||
if err != nil {
|
||||
mlog.Warn("Getting hooks for plugin failed", mlog.String("plugin_id", id), mlog.Err(err))
|
||||
return
|
||||
|
||||
@@ -13125,6 +13125,23 @@ func (a *OpenTracingAppLayer) PluginCommandsForTeam(teamID string) []*model.Comm
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) PluginService() *app.PluginService {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.PluginService")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0 := a.app.PluginService()
|
||||
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) PopulateWebConnConfig(s *model.Session, cfg *platform.WebConnConfig, seqVal string) (*platform.WebConnConfig, error) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.PopulateWebConnConfig")
|
||||
|
||||
380
app/plugin.go
380
app/plugin.go
@@ -20,16 +20,37 @@ import (
|
||||
svg "github.com/h2non/go-is-svg"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/app/platform"
|
||||
"github.com/mattermost/mattermost-server/v6/app/request"
|
||||
"github.com/mattermost/mattermost-server/v6/config"
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/plugin"
|
||||
"github.com/mattermost/mattermost-server/v6/product"
|
||||
"github.com/mattermost/mattermost-server/v6/services/httpservice"
|
||||
"github.com/mattermost/mattermost-server/v6/services/marketplace"
|
||||
"github.com/mattermost/mattermost-server/v6/shared/filestore"
|
||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||
"github.com/mattermost/mattermost-server/v6/utils/fileutils"
|
||||
)
|
||||
|
||||
type PluginService struct {
|
||||
platform *platform.PlatformService
|
||||
channels *Channels
|
||||
fileStore filestore.FileBackend
|
||||
httpService httpservice.HTTPService
|
||||
|
||||
pluginCommandsLock sync.RWMutex
|
||||
pluginCommands []*PluginCommand
|
||||
pluginsLock sync.RWMutex
|
||||
pluginsEnvironment *plugin.Environment
|
||||
pluginConfigListenerID string
|
||||
// collectionTypes maps from collection types to the registering plugin id
|
||||
collectionTypes map[string]string
|
||||
// topicTypes maps from topic types to collection types
|
||||
topicTypes map[string]string
|
||||
collectionAndTopicTypesMut sync.Mutex
|
||||
}
|
||||
|
||||
const prepackagedPluginsDir = "prepackaged_plugins"
|
||||
|
||||
type pluginSignaturePath struct {
|
||||
@@ -63,20 +84,91 @@ func (rs *routerService) getHandler(productID string) (http.Handler, bool) {
|
||||
return handler, ok
|
||||
}
|
||||
|
||||
func (a *App) PluginService() *PluginService {
|
||||
return a.ch.srv.pluginService
|
||||
}
|
||||
|
||||
func (s *Server) InitializePluginService() error {
|
||||
product, ok := s.products["channels"]
|
||||
if !ok {
|
||||
return errors.New("unable to find channels product")
|
||||
}
|
||||
channels, ok := product.(*Channels)
|
||||
if !ok {
|
||||
return errors.New("unable to cast product to channels product")
|
||||
}
|
||||
|
||||
ps := &PluginService{
|
||||
platform: s.platform,
|
||||
channels: channels,
|
||||
fileStore: s.platform.FileBackend(),
|
||||
httpService: s.httpService,
|
||||
collectionTypes: make(map[string]string),
|
||||
topicTypes: make(map[string]string),
|
||||
}
|
||||
s.pluginService = ps
|
||||
|
||||
pluginsRoute := s.Router.PathPrefix("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}").Subrouter()
|
||||
pluginsRoute.HandleFunc("", ps.ServePluginRequest)
|
||||
pluginsRoute.HandleFunc("/public/{public_file:.*}", ps.ServePluginPublicRequest)
|
||||
pluginsRoute.HandleFunc("/{anything:.*}", ps.ServePluginRequest)
|
||||
|
||||
ps.initPlugins(request.EmptyContext(s.platform.Log()), *s.platform.Config().PluginSettings.Directory, *s.platform.Config().PluginSettings.ClientDirectory)
|
||||
|
||||
// Start plugins
|
||||
ctx := request.EmptyContext(s.platform.Log())
|
||||
|
||||
// Add the config listener to enable/disable plugins
|
||||
s.platform.AddConfigListener(func(prevCfg, cfg *model.Config) {
|
||||
// We compute the difference between configs
|
||||
// to ensure we don't re-init plugins unnecessarily.
|
||||
diffs, err := config.Diff(prevCfg, cfg)
|
||||
if err != nil {
|
||||
s.platform.Log().Warn("Error in comparing configs", mlog.Err(err))
|
||||
return
|
||||
}
|
||||
|
||||
hasDiff := false
|
||||
// TODO: This could be a method on ConfigDiffs itself
|
||||
for _, diff := range diffs {
|
||||
if strings.HasPrefix(diff.Path, "PluginSettings.") {
|
||||
hasDiff = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Do only if some plugin related settings has changed.
|
||||
if hasDiff {
|
||||
if *cfg.PluginSettings.Enable {
|
||||
s.pluginService.initPlugins(ctx, *cfg.PluginSettings.Directory, *s.Config().PluginSettings.ClientDirectory)
|
||||
} else {
|
||||
s.pluginService.ShutDownPlugins()
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) GetPluginsEnvironment() *plugin.Environment {
|
||||
return s.pluginService.GetPluginsEnvironment()
|
||||
}
|
||||
|
||||
// GetPluginsEnvironment returns the plugin environment for use if plugins are enabled and
|
||||
// initialized.
|
||||
//
|
||||
// To get the plugins environment when the plugins are disabled, manually acquire the plugins
|
||||
// lock instead.
|
||||
func (ch *Channels) GetPluginsEnvironment() *plugin.Environment {
|
||||
if !*ch.cfgSvc.Config().PluginSettings.Enable {
|
||||
func (s *PluginService) GetPluginsEnvironment() *plugin.Environment {
|
||||
if !*s.platform.Config().PluginSettings.Enable {
|
||||
return nil
|
||||
}
|
||||
|
||||
ch.pluginsLock.RLock()
|
||||
defer ch.pluginsLock.RUnlock()
|
||||
s.pluginsLock.RLock()
|
||||
defer s.pluginsLock.RUnlock()
|
||||
|
||||
return ch.pluginsEnvironment
|
||||
return s.pluginsEnvironment
|
||||
}
|
||||
|
||||
// GetPluginsEnvironment returns the plugin environment for use if plugins are enabled and
|
||||
@@ -85,33 +177,39 @@ func (ch *Channels) GetPluginsEnvironment() *plugin.Environment {
|
||||
// To get the plugins environment when the plugins are disabled, manually acquire the plugins
|
||||
// lock instead.
|
||||
func (a *App) GetPluginsEnvironment() *plugin.Environment {
|
||||
return a.ch.GetPluginsEnvironment()
|
||||
// TODO: Telemetry service starts before products start, so we need to check if the plugin service is initialized.
|
||||
// Move the telemetry service to start after products start.
|
||||
if a.ch.srv.pluginService == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return a.ch.srv.pluginService.GetPluginsEnvironment()
|
||||
}
|
||||
|
||||
func (ch *Channels) SetPluginsEnvironment(pluginsEnvironment *plugin.Environment) {
|
||||
ch.pluginsLock.Lock()
|
||||
defer ch.pluginsLock.Unlock()
|
||||
func (s *PluginService) SetPluginsEnvironment(pluginsEnvironment *plugin.Environment) {
|
||||
s.pluginsLock.Lock()
|
||||
defer s.pluginsLock.Unlock()
|
||||
|
||||
ch.pluginsEnvironment = pluginsEnvironment
|
||||
ch.srv.Platform().SetPluginsEnvironment(ch)
|
||||
s.pluginsEnvironment = pluginsEnvironment
|
||||
s.platform.SetPluginsEnvironment(s.channels.srv)
|
||||
}
|
||||
|
||||
func (ch *Channels) syncPluginsActiveState() {
|
||||
func (s *PluginService) syncPluginsActiveState() {
|
||||
// Acquiring lock manually, as plugins might be disabled. See GetPluginsEnvironment.
|
||||
ch.pluginsLock.RLock()
|
||||
pluginsEnvironment := ch.pluginsEnvironment
|
||||
ch.pluginsLock.RUnlock()
|
||||
s.pluginsLock.RLock()
|
||||
pluginsEnvironment := s.pluginsEnvironment
|
||||
s.pluginsLock.RUnlock()
|
||||
|
||||
if pluginsEnvironment == nil {
|
||||
return
|
||||
}
|
||||
|
||||
config := ch.cfgSvc.Config().PluginSettings
|
||||
config := s.platform.Config().PluginSettings
|
||||
|
||||
if *config.Enable {
|
||||
availablePlugins, err := pluginsEnvironment.Available()
|
||||
if err != nil {
|
||||
ch.srv.Log().Error("Unable to get available plugins", mlog.Err(err))
|
||||
s.platform.Log().Error("Unable to get available plugins", mlog.Err(err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -125,24 +223,24 @@ func (ch *Channels) syncPluginsActiveState() {
|
||||
pluginEnabled = state.Enable
|
||||
}
|
||||
|
||||
if hasOverride, value := ch.getPluginStateOverride(pluginID); hasOverride {
|
||||
if hasOverride, value := s.getPluginStateOverride(pluginID); hasOverride {
|
||||
pluginEnabled = value
|
||||
}
|
||||
|
||||
if pluginEnabled {
|
||||
// Disable focalboard in product mode.
|
||||
if pluginID == model.PluginIdFocalboard && ch.cfgSvc.Config().FeatureFlags.BoardsProduct {
|
||||
if pluginID == model.PluginIdFocalboard && s.platform.Config().FeatureFlags.BoardsProduct {
|
||||
msg := "Plugin cannot run in product mode. Disabling."
|
||||
mlog.Warn(msg, mlog.String("plugin_id", model.PluginIdFocalboard))
|
||||
|
||||
// This is a mini-version of ch.disablePlugin.
|
||||
// We don't call that directly, because that will recursively call
|
||||
// this method.
|
||||
ch.cfgSvc.UpdateConfig(func(cfg *model.Config) {
|
||||
s.platform.UpdateConfig(func(cfg *model.Config) {
|
||||
cfg.PluginSettings.PluginStates[pluginID] = &model.PluginState{Enable: false}
|
||||
})
|
||||
pluginsEnvironment.SetPluginError(pluginID, msg)
|
||||
ch.unregisterPluginCommands(pluginID)
|
||||
s.unregisterPluginCommands(pluginID)
|
||||
disabledPlugins = append(disabledPlugins, plugin)
|
||||
continue
|
||||
}
|
||||
@@ -166,7 +264,7 @@ func (ch *Channels) syncPluginsActiveState() {
|
||||
if deactivated && plugin.Manifest.HasClient() {
|
||||
message := model.NewWebSocketEvent(model.WebsocketEventPluginDisabled, "", "", "", nil, "")
|
||||
message.Add("manifest", plugin.Manifest.ClientManifest())
|
||||
ch.srv.platform.Publish(message)
|
||||
s.platform.Publish(message)
|
||||
}
|
||||
}(plugin)
|
||||
}
|
||||
@@ -180,14 +278,14 @@ func (ch *Channels) syncPluginsActiveState() {
|
||||
pluginID := plugin.Manifest.Id
|
||||
updatedManifest, activated, err := pluginsEnvironment.Activate(pluginID)
|
||||
if err != nil {
|
||||
plugin.WrapLogger(ch.srv.Log()).Error("Unable to activate plugin", mlog.Err(err))
|
||||
plugin.WrapLogger(s.platform.Log().(*mlog.Logger)).Error("Unable to activate plugin", mlog.Err(err))
|
||||
return
|
||||
}
|
||||
|
||||
if activated {
|
||||
// Notify all cluster clients if ready
|
||||
if err := ch.notifyPluginEnabled(updatedManifest); err != nil {
|
||||
ch.srv.Log().Error("Failed to notify cluster on plugin enable", mlog.Err(err))
|
||||
if err := s.notifyPluginEnabled(updatedManifest); err != nil {
|
||||
s.platform.Log().Error("Failed to notify cluster on plugin enable", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
}(plugin)
|
||||
@@ -197,7 +295,7 @@ func (ch *Channels) syncPluginsActiveState() {
|
||||
pluginsEnvironment.Shutdown()
|
||||
}
|
||||
|
||||
if err := ch.notifyPluginStatusesChanged(); err != nil {
|
||||
if err := s.notifyPluginStatusesChanged(); err != nil {
|
||||
mlog.Warn("failed to notify plugin status changed", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
@@ -207,27 +305,29 @@ func (a *App) NewPluginAPI(c *request.Context, manifest *model.Manifest) plugin.
|
||||
}
|
||||
|
||||
func (a *App) InitPlugins(c *request.Context, pluginDir, webappPluginDir string) {
|
||||
a.ch.initPlugins(c, pluginDir, webappPluginDir)
|
||||
a.ch.srv.pluginService.initPlugins(c, pluginDir, webappPluginDir)
|
||||
}
|
||||
|
||||
func (ch *Channels) initPlugins(c *request.Context, pluginDir, webappPluginDir string) {
|
||||
func (s *PluginService) initPlugins(c *request.Context, pluginDir, webappPluginDir string) {
|
||||
// Acquiring lock manually, as plugins might be disabled. See GetPluginsEnvironment.
|
||||
defer func() {
|
||||
ch.srv.Platform().SetPluginsEnvironment(ch)
|
||||
// platform service requires plugins environment to be initialized
|
||||
// so that it can use it in cluster service initialization
|
||||
s.platform.SetPluginsEnvironment(s.channels.srv)
|
||||
}()
|
||||
|
||||
ch.pluginsLock.RLock()
|
||||
pluginsEnvironment := ch.pluginsEnvironment
|
||||
ch.pluginsLock.RUnlock()
|
||||
if pluginsEnvironment != nil || !*ch.cfgSvc.Config().PluginSettings.Enable {
|
||||
ch.syncPluginsActiveState()
|
||||
s.pluginsLock.RLock()
|
||||
pluginsEnvironment := s.pluginsEnvironment
|
||||
s.pluginsLock.RUnlock()
|
||||
if pluginsEnvironment != nil || !*s.platform.Config().PluginSettings.Enable {
|
||||
s.syncPluginsActiveState()
|
||||
if pluginsEnvironment != nil {
|
||||
pluginsEnvironment.TogglePluginHealthCheckJob(*ch.cfgSvc.Config().PluginSettings.EnableHealthCheck)
|
||||
pluginsEnvironment.TogglePluginHealthCheckJob(*s.platform.Config().PluginSettings.EnableHealthCheck)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ch.srv.Log().Info("Starting up plugins")
|
||||
s.platform.Log().Info("Starting up plugins")
|
||||
|
||||
if err := os.Mkdir(pluginDir, 0744); err != nil && !os.IsExist(err) {
|
||||
mlog.Error("Failed to start up plugins", mlog.Err(err))
|
||||
@@ -240,77 +340,77 @@ func (ch *Channels) initPlugins(c *request.Context, pluginDir, webappPluginDir s
|
||||
}
|
||||
|
||||
newAPIFunc := func(manifest *model.Manifest) plugin.API {
|
||||
return New(ServerConnector(ch)).NewPluginAPI(c, manifest)
|
||||
return New(ServerConnector(s.channels)).NewPluginAPI(c, manifest)
|
||||
}
|
||||
|
||||
env, err := plugin.NewEnvironment(
|
||||
newAPIFunc,
|
||||
NewDriverImpl(ch.srv),
|
||||
NewDriverImpl(s.platform),
|
||||
pluginDir,
|
||||
webappPluginDir,
|
||||
*ch.cfgSvc.Config().ExperimentalSettings.PatchPluginsReactDOM,
|
||||
ch.srv.Log(),
|
||||
ch.srv.GetMetrics(),
|
||||
*s.platform.Config().ExperimentalSettings.PatchPluginsReactDOM,
|
||||
s.platform.Logger(),
|
||||
s.platform.Metrics(),
|
||||
)
|
||||
if err != nil {
|
||||
mlog.Error("Failed to start up plugins", mlog.Err(err))
|
||||
return
|
||||
}
|
||||
ch.pluginsLock.Lock()
|
||||
ch.pluginsEnvironment = env
|
||||
ch.pluginsLock.Unlock()
|
||||
s.pluginsLock.Lock()
|
||||
s.pluginsEnvironment = env
|
||||
s.pluginsLock.Unlock()
|
||||
|
||||
ch.pluginsEnvironment.TogglePluginHealthCheckJob(*ch.cfgSvc.Config().PluginSettings.EnableHealthCheck)
|
||||
s.pluginsEnvironment.TogglePluginHealthCheckJob(*s.platform.Config().PluginSettings.EnableHealthCheck)
|
||||
|
||||
if err := ch.syncPlugins(); err != nil {
|
||||
if err := s.syncPlugins(); err != nil {
|
||||
mlog.Error("Failed to sync plugins from the file store", mlog.Err(err))
|
||||
}
|
||||
|
||||
plugins := ch.processPrepackagedPlugins(prepackagedPluginsDir)
|
||||
pluginsEnvironment = ch.GetPluginsEnvironment()
|
||||
plugins := s.processPrepackagedPlugins(prepackagedPluginsDir)
|
||||
pluginsEnvironment = s.GetPluginsEnvironment()
|
||||
if pluginsEnvironment == nil {
|
||||
mlog.Info("Plugins environment not found, server is likely shutting down")
|
||||
return
|
||||
}
|
||||
pluginsEnvironment.SetPrepackagedPlugins(plugins)
|
||||
|
||||
ch.installFeatureFlagPlugins()
|
||||
s.installFeatureFlagPlugins()
|
||||
|
||||
// Sync plugin active state when config changes. Also notify plugins.
|
||||
ch.pluginsLock.Lock()
|
||||
ch.RemoveConfigListener(ch.pluginConfigListenerID)
|
||||
ch.pluginConfigListenerID = ch.AddConfigListener(func(old, new *model.Config) {
|
||||
s.pluginsLock.Lock()
|
||||
s.platform.RemoveConfigListener(s.pluginConfigListenerID)
|
||||
s.pluginConfigListenerID = s.platform.AddConfigListener(func(old, new *model.Config) {
|
||||
// If plugin status remains unchanged, only then run this.
|
||||
// Because (*App).InitPlugins is already run as a config change hook.
|
||||
if *old.PluginSettings.Enable == *new.PluginSettings.Enable {
|
||||
ch.installFeatureFlagPlugins()
|
||||
ch.syncPluginsActiveState()
|
||||
s.installFeatureFlagPlugins()
|
||||
s.syncPluginsActiveState()
|
||||
}
|
||||
|
||||
ch.RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
s.pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
if err := hooks.OnConfigurationChange(); err != nil {
|
||||
ch.srv.Log().Error("Plugin OnConfigurationChange hook failed", mlog.Err(err))
|
||||
s.platform.Log().Error("Plugin OnConfigurationChange hook failed", mlog.Err(err))
|
||||
}
|
||||
return true
|
||||
}, plugin.OnConfigurationChangeID)
|
||||
})
|
||||
ch.pluginsLock.Unlock()
|
||||
s.pluginsLock.Unlock()
|
||||
|
||||
ch.syncPluginsActiveState()
|
||||
s.syncPluginsActiveState()
|
||||
}
|
||||
|
||||
// SyncPlugins synchronizes the plugins installed locally
|
||||
// with the plugin bundles available in the file store.
|
||||
func (a *App) SyncPlugins() *model.AppError {
|
||||
return a.ch.syncPlugins()
|
||||
return a.ch.srv.pluginService.syncPlugins()
|
||||
}
|
||||
|
||||
// SyncPlugins synchronizes the plugins installed locally
|
||||
// with the plugin bundles available in the file store.
|
||||
func (ch *Channels) syncPlugins() *model.AppError {
|
||||
func (s *PluginService) syncPlugins() *model.AppError {
|
||||
mlog.Info("Syncing plugins from the file store")
|
||||
|
||||
pluginsEnvironment := ch.GetPluginsEnvironment()
|
||||
pluginsEnvironment := s.GetPluginsEnvironment()
|
||||
if pluginsEnvironment == nil {
|
||||
return model.NewAppError("SyncPlugins", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
@@ -326,14 +426,14 @@ func (ch *Channels) syncPlugins() *model.AppError {
|
||||
go func(pluginID string) {
|
||||
defer wg.Done()
|
||||
// Only handle managed plugins with .filestore flag file.
|
||||
_, err := os.Stat(filepath.Join(*ch.cfgSvc.Config().PluginSettings.Directory, pluginID, managedPluginFileName))
|
||||
_, err := os.Stat(filepath.Join(*s.platform.Config().PluginSettings.Directory, pluginID, managedPluginFileName))
|
||||
if os.IsNotExist(err) {
|
||||
mlog.Warn("Skipping sync for unmanaged plugin", mlog.String("plugin_id", pluginID))
|
||||
} else if err != nil {
|
||||
mlog.Error("Skipping sync for plugin after failure to check if managed", mlog.String("plugin_id", pluginID), mlog.Err(err))
|
||||
} else {
|
||||
mlog.Debug("Removing local installation of managed plugin before sync", mlog.String("plugin_id", pluginID))
|
||||
if err := ch.removePluginLocally(pluginID); err != nil {
|
||||
if err := s.removePluginLocally(pluginID); err != nil {
|
||||
mlog.Error("Failed to remove local installation of managed plugin before sync", mlog.String("plugin_id", pluginID), mlog.Err(err))
|
||||
}
|
||||
}
|
||||
@@ -342,7 +442,7 @@ func (ch *Channels) syncPlugins() *model.AppError {
|
||||
wg.Wait()
|
||||
|
||||
// Install plugins from the file store.
|
||||
pluginSignaturePathMap, appErr := ch.getPluginsFromFolder()
|
||||
pluginSignaturePathMap, appErr := s.getPluginsFromFolder()
|
||||
if appErr != nil {
|
||||
return appErr
|
||||
}
|
||||
@@ -351,7 +451,7 @@ func (ch *Channels) syncPlugins() *model.AppError {
|
||||
wg.Add(1)
|
||||
go func(plugin *pluginSignaturePath) {
|
||||
defer wg.Done()
|
||||
reader, appErr := ch.srv.fileReader(plugin.path)
|
||||
reader, appErr := s.fileStore.Reader(plugin.path)
|
||||
if appErr != nil {
|
||||
mlog.Error("Failed to open plugin bundle from file store.", mlog.String("bundle", plugin.path), mlog.Err(appErr))
|
||||
return
|
||||
@@ -359,8 +459,8 @@ func (ch *Channels) syncPlugins() *model.AppError {
|
||||
defer reader.Close()
|
||||
|
||||
var signature filestore.ReadCloseSeeker
|
||||
if *ch.cfgSvc.Config().PluginSettings.RequirePluginSignature {
|
||||
signature, appErr = ch.srv.fileReader(plugin.signaturePath)
|
||||
if *s.platform.Config().PluginSettings.RequirePluginSignature {
|
||||
signature, appErr = s.fileStore.Reader(plugin.signaturePath)
|
||||
if appErr != nil {
|
||||
mlog.Error("Failed to open plugin signature from file store.", mlog.Err(appErr))
|
||||
return
|
||||
@@ -369,7 +469,7 @@ func (ch *Channels) syncPlugins() *model.AppError {
|
||||
}
|
||||
|
||||
mlog.Info("Syncing plugin from file store", mlog.String("bundle", plugin.path))
|
||||
if _, err := ch.installPluginLocally(reader, signature, installPluginLocallyAlways); err != nil {
|
||||
if _, err := s.installPluginLocally(reader, signature, installPluginLocallyAlways); err != nil {
|
||||
mlog.Error("Failed to sync plugin from file store", mlog.String("bundle", plugin.path), mlog.Err(err))
|
||||
}
|
||||
}(plugin)
|
||||
@@ -379,11 +479,11 @@ func (ch *Channels) syncPlugins() *model.AppError {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ch *Channels) ShutDownPlugins() {
|
||||
func (s *PluginService) ShutDownPlugins() {
|
||||
// Acquiring lock manually, as plugins might be disabled. See GetPluginsEnvironment.
|
||||
ch.pluginsLock.RLock()
|
||||
pluginsEnvironment := ch.pluginsEnvironment
|
||||
ch.pluginsLock.RUnlock()
|
||||
s.pluginsLock.RLock()
|
||||
pluginsEnvironment := s.pluginsEnvironment
|
||||
s.pluginsLock.RUnlock()
|
||||
if pluginsEnvironment == nil {
|
||||
return
|
||||
}
|
||||
@@ -392,14 +492,14 @@ func (ch *Channels) ShutDownPlugins() {
|
||||
|
||||
pluginsEnvironment.Shutdown()
|
||||
|
||||
ch.RemoveConfigListener(ch.pluginConfigListenerID)
|
||||
ch.pluginConfigListenerID = ""
|
||||
s.platform.RemoveConfigListener(s.pluginConfigListenerID)
|
||||
s.pluginConfigListenerID = ""
|
||||
|
||||
// Acquiring lock manually before cleaning up PluginsEnvironment.
|
||||
ch.pluginsLock.Lock()
|
||||
defer ch.pluginsLock.Unlock()
|
||||
if ch.pluginsEnvironment == pluginsEnvironment {
|
||||
ch.pluginsEnvironment = nil
|
||||
s.pluginsLock.Lock()
|
||||
defer s.pluginsLock.Unlock()
|
||||
if s.pluginsEnvironment == pluginsEnvironment {
|
||||
s.pluginsEnvironment = nil
|
||||
} else {
|
||||
mlog.Warn("Another PluginsEnvironment detected while shutting down plugins.")
|
||||
}
|
||||
@@ -425,11 +525,11 @@ func (a *App) GetActivePluginManifests() ([]*model.Manifest, *model.AppError) {
|
||||
// activation if inactive anywhere in the cluster.
|
||||
// Notifies cluster peers through config change.
|
||||
func (a *App) EnablePlugin(id string) *model.AppError {
|
||||
return a.ch.enablePlugin(id)
|
||||
return a.PluginService().enablePlugin(id)
|
||||
}
|
||||
|
||||
func (ch *Channels) enablePlugin(id string) *model.AppError {
|
||||
pluginsEnvironment := ch.GetPluginsEnvironment()
|
||||
func (s *PluginService) enablePlugin(id string) *model.AppError {
|
||||
pluginsEnvironment := s.GetPluginsEnvironment()
|
||||
if pluginsEnvironment == nil {
|
||||
return model.NewAppError("EnablePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
@@ -453,16 +553,16 @@ func (ch *Channels) enablePlugin(id string) *model.AppError {
|
||||
return model.NewAppError("EnablePlugin", "app.plugin.not_installed.app_error", nil, "", http.StatusNotFound)
|
||||
}
|
||||
|
||||
if id == model.PluginIdFocalboard && ch.cfgSvc.Config().FeatureFlags.BoardsProduct {
|
||||
if id == model.PluginIdFocalboard && s.platform.Config().FeatureFlags.BoardsProduct {
|
||||
return model.NewAppError("EnablePlugin", "app.plugin.product_mode.app_error", map[string]any{"Name": model.PluginIdFocalboard}, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
ch.cfgSvc.UpdateConfig(func(cfg *model.Config) {
|
||||
s.platform.UpdateConfig(func(cfg *model.Config) {
|
||||
cfg.PluginSettings.PluginStates[id] = &model.PluginState{Enable: true}
|
||||
})
|
||||
|
||||
// This call will implicitly invoke SyncPluginsActiveState which will activate enabled plugins.
|
||||
if _, _, err := ch.cfgSvc.SaveConfig(ch.cfgSvc.Config(), true); err != nil {
|
||||
if _, _, err := s.platform.SaveConfig(s.platform.Config(), true); err != nil {
|
||||
if err.Id == "ent.cluster.save_config.error" {
|
||||
return model.NewAppError("EnablePlugin", "app.plugin.cluster.save_config.app_error", nil, "", http.StatusInternalServerError)
|
||||
}
|
||||
@@ -475,7 +575,7 @@ func (ch *Channels) enablePlugin(id string) *model.AppError {
|
||||
// DisablePlugin will set the config for an installed plugin to disabled, triggering deactivation if active.
|
||||
// Notifies cluster peers through config change.
|
||||
func (a *App) DisablePlugin(id string) *model.AppError {
|
||||
appErr := a.ch.disablePlugin(id)
|
||||
appErr := a.ch.srv.pluginService.disablePlugin(id)
|
||||
if appErr != nil {
|
||||
return appErr
|
||||
}
|
||||
@@ -483,22 +583,22 @@ func (a *App) DisablePlugin(id string) *model.AppError {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ch *Channels) disablePlugin(id string) *model.AppError {
|
||||
func (s *PluginService) disablePlugin(id string) *model.AppError {
|
||||
// find all collectionTypes registered by plugin
|
||||
for collectionTypeToRemove, existingPluginId := range ch.collectionTypes {
|
||||
for collectionTypeToRemove, existingPluginId := range s.collectionTypes {
|
||||
if existingPluginId != id {
|
||||
continue
|
||||
}
|
||||
// find all topicTypes for existing collectionType
|
||||
for topicTypeToRemove, existingCollectionType := range ch.topicTypes {
|
||||
for topicTypeToRemove, existingCollectionType := range s.topicTypes {
|
||||
if existingCollectionType == collectionTypeToRemove {
|
||||
delete(ch.topicTypes, topicTypeToRemove)
|
||||
delete(s.topicTypes, topicTypeToRemove)
|
||||
}
|
||||
}
|
||||
delete(ch.collectionTypes, collectionTypeToRemove)
|
||||
delete(s.collectionTypes, collectionTypeToRemove)
|
||||
}
|
||||
|
||||
pluginsEnvironment := ch.GetPluginsEnvironment()
|
||||
pluginsEnvironment := s.GetPluginsEnvironment()
|
||||
if pluginsEnvironment == nil {
|
||||
return model.NewAppError("DisablePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
@@ -522,13 +622,13 @@ func (ch *Channels) disablePlugin(id string) *model.AppError {
|
||||
return model.NewAppError("DisablePlugin", "app.plugin.not_installed.app_error", nil, "", http.StatusNotFound)
|
||||
}
|
||||
|
||||
ch.cfgSvc.UpdateConfig(func(cfg *model.Config) {
|
||||
s.platform.UpdateConfig(func(cfg *model.Config) {
|
||||
cfg.PluginSettings.PluginStates[id] = &model.PluginState{Enable: false}
|
||||
})
|
||||
ch.unregisterPluginCommands(id)
|
||||
s.unregisterPluginCommands(id)
|
||||
|
||||
// This call will implicitly invoke SyncPluginsActiveState which will deactivate disabled plugins.
|
||||
if _, _, err := ch.cfgSvc.SaveConfig(ch.cfgSvc.Config(), true); err != nil {
|
||||
if _, _, err := s.platform.SaveConfig(s.platform.Config(), true); err != nil {
|
||||
return model.NewAppError("DisablePlugin", "app.plugin.config.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
@@ -615,8 +715,8 @@ func (a *App) GetMarketplacePlugins(filter *model.MarketplacePluginFilter) ([]*m
|
||||
// getPrepackagedPlugin returns a pre-packaged plugin.
|
||||
//
|
||||
// If version is empty, the first matching plugin is returned.
|
||||
func (ch *Channels) getPrepackagedPlugin(pluginID, version string) (*plugin.PrepackagedPlugin, *model.AppError) {
|
||||
pluginsEnvironment := ch.GetPluginsEnvironment()
|
||||
func (s *PluginService) getPrepackagedPlugin(pluginID, version string) (*plugin.PrepackagedPlugin, *model.AppError) {
|
||||
pluginsEnvironment := s.GetPluginsEnvironment()
|
||||
if pluginsEnvironment == nil {
|
||||
return nil, model.NewAppError("getPrepackagedPlugin", "app.plugin.config.app_error", nil, "plugin environment is nil", http.StatusInternalServerError)
|
||||
}
|
||||
@@ -634,16 +734,16 @@ func (ch *Channels) getPrepackagedPlugin(pluginID, version string) (*plugin.Prep
|
||||
// getRemoteMarketplacePlugin returns plugin from marketplace-server.
|
||||
//
|
||||
// If version is empty, the latest compatible version is used.
|
||||
func (ch *Channels) getRemoteMarketplacePlugin(pluginID, version string) (*model.BaseMarketplacePlugin, *model.AppError) {
|
||||
func (s *PluginService) getRemoteMarketplacePlugin(pluginID, version string) (*model.BaseMarketplacePlugin, *model.AppError) {
|
||||
marketplaceClient, err := marketplace.NewClient(
|
||||
*ch.cfgSvc.Config().PluginSettings.MarketplaceURL,
|
||||
ch.srv.HTTPService(),
|
||||
*s.platform.Config().PluginSettings.MarketplaceURL,
|
||||
s.httpService,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetMarketplacePlugin", "app.plugin.marketplace_client.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
|
||||
filter := ch.getBaseMarketplaceFilter()
|
||||
filter := s.getBaseMarketplaceFilter()
|
||||
filter.PluginId = pluginID
|
||||
|
||||
var plugin *model.BaseMarketplacePlugin
|
||||
@@ -798,15 +898,15 @@ func (a *App) mergeLocalPlugins(remoteMarketplacePlugins map[string]*model.Marke
|
||||
}
|
||||
|
||||
func (a *App) getBaseMarketplaceFilter() *model.MarketplacePluginFilter {
|
||||
return a.ch.getBaseMarketplaceFilter()
|
||||
return a.ch.srv.pluginService.getBaseMarketplaceFilter()
|
||||
}
|
||||
|
||||
func (ch *Channels) getBaseMarketplaceFilter() *model.MarketplacePluginFilter {
|
||||
func (s *PluginService) getBaseMarketplaceFilter() *model.MarketplacePluginFilter {
|
||||
filter := &model.MarketplacePluginFilter{
|
||||
ServerVersion: model.CurrentVersion,
|
||||
}
|
||||
|
||||
license := ch.srv.License()
|
||||
license := s.platform.License()
|
||||
if license != nil && license.HasEnterpriseMarketplacePlugins() {
|
||||
filter.EnterprisePlugins = true
|
||||
}
|
||||
@@ -853,8 +953,8 @@ func pluginMatchesFilter(manifest *model.Manifest, filter string) bool {
|
||||
// it will notify all connected websocket clients (across all peers) to trigger the (re-)installation.
|
||||
// There is a small chance that this never occurs, because the last server to finish installing dies before it can announce.
|
||||
// There is also a chance that multiple servers notify, but the webapp handles this idempotently.
|
||||
func (ch *Channels) notifyPluginEnabled(manifest *model.Manifest) error {
|
||||
pluginsEnvironment := ch.GetPluginsEnvironment()
|
||||
func (s *PluginService) notifyPluginEnabled(manifest *model.Manifest) error {
|
||||
pluginsEnvironment := s.GetPluginsEnvironment()
|
||||
if pluginsEnvironment == nil {
|
||||
return errors.New("pluginsEnvironment is nil")
|
||||
}
|
||||
@@ -864,15 +964,15 @@ func (ch *Channels) notifyPluginEnabled(manifest *model.Manifest) error {
|
||||
|
||||
var statuses model.PluginStatuses
|
||||
|
||||
if ch.srv.platform.Cluster() != nil {
|
||||
if s.platform.Cluster() != nil {
|
||||
var err *model.AppError
|
||||
statuses, err = ch.srv.platform.Cluster().GetPluginStatuses()
|
||||
statuses, err = s.platform.Cluster().GetPluginStatuses()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
localStatus, err := ch.GetPluginStatus(manifest.Id)
|
||||
localStatus, err := s.GetPluginStatus(manifest.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -892,26 +992,26 @@ func (ch *Channels) notifyPluginEnabled(manifest *model.Manifest) error {
|
||||
// Notify all cluster peer clients.
|
||||
message := model.NewWebSocketEvent(model.WebsocketEventPluginEnabled, "", "", "", nil, "")
|
||||
message.Add("manifest", manifest.ClientManifest())
|
||||
ch.srv.platform.Publish(message)
|
||||
s.platform.Publish(message)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ch *Channels) getPluginsFromFolder() (map[string]*pluginSignaturePath, *model.AppError) {
|
||||
fileStorePaths, appErr := ch.srv.listDirectory(fileStorePluginFolder, false)
|
||||
func (s *PluginService) getPluginsFromFolder() (map[string]*pluginSignaturePath, *model.AppError) {
|
||||
fileStorePaths, appErr := s.fileStore.ListDirectory(fileStorePluginFolder)
|
||||
if appErr != nil {
|
||||
return nil, model.NewAppError("getPluginsFromDir", "app.plugin.sync.list_filestore.app_error", nil, "", http.StatusInternalServerError).Wrap(appErr)
|
||||
}
|
||||
|
||||
return ch.getPluginsFromFilePaths(fileStorePaths), nil
|
||||
return s.getPluginsFromFilePaths(fileStorePaths), nil
|
||||
}
|
||||
|
||||
func (ch *Channels) getPluginsFromFilePaths(fileStorePaths []string) map[string]*pluginSignaturePath {
|
||||
func (s *PluginService) getPluginsFromFilePaths(fileStorePaths []string) map[string]*pluginSignaturePath {
|
||||
pluginSignaturePathMap := make(map[string]*pluginSignaturePath)
|
||||
|
||||
fsPrefix := ""
|
||||
if *ch.cfgSvc.Config().FileSettings.DriverName == model.ImageDriverS3 {
|
||||
ptr := ch.cfgSvc.Config().FileSettings.AmazonS3PathPrefix
|
||||
if *s.platform.Config().FileSettings.DriverName == model.ImageDriverS3 {
|
||||
ptr := s.platform.Config().FileSettings.AmazonS3PathPrefix
|
||||
if ptr != nil && *ptr != "" {
|
||||
fsPrefix = *ptr + "/"
|
||||
}
|
||||
@@ -944,7 +1044,7 @@ func (ch *Channels) getPluginsFromFilePaths(fileStorePaths []string) map[string]
|
||||
return pluginSignaturePathMap
|
||||
}
|
||||
|
||||
func (ch *Channels) processPrepackagedPlugins(pluginsDir string) []*plugin.PrepackagedPlugin {
|
||||
func (s *PluginService) processPrepackagedPlugins(pluginsDir string) []*plugin.PrepackagedPlugin {
|
||||
prepackagedPluginsDir, found := fileutils.FindDir(pluginsDir)
|
||||
if !found {
|
||||
return nil
|
||||
@@ -960,7 +1060,7 @@ func (ch *Channels) processPrepackagedPlugins(pluginsDir string) []*plugin.Prepa
|
||||
return nil
|
||||
}
|
||||
|
||||
pluginSignaturePathMap := ch.getPluginsFromFilePaths(fileStorePaths)
|
||||
pluginSignaturePathMap := s.getPluginsFromFilePaths(fileStorePaths)
|
||||
plugins := make([]*plugin.PrepackagedPlugin, 0, len(pluginSignaturePathMap))
|
||||
prepackagedPlugins := make(chan *plugin.PrepackagedPlugin, len(pluginSignaturePathMap))
|
||||
|
||||
@@ -969,7 +1069,7 @@ func (ch *Channels) processPrepackagedPlugins(pluginsDir string) []*plugin.Prepa
|
||||
wg.Add(1)
|
||||
go func(psPath *pluginSignaturePath) {
|
||||
defer wg.Done()
|
||||
p, err := ch.processPrepackagedPlugin(psPath)
|
||||
p, err := s.processPrepackagedPlugin(psPath)
|
||||
if err != nil {
|
||||
mlog.Error("Failed to install prepackaged plugin", mlog.String("path", psPath.path), mlog.Err(err))
|
||||
return
|
||||
@@ -990,7 +1090,7 @@ func (ch *Channels) processPrepackagedPlugins(pluginsDir string) []*plugin.Prepa
|
||||
|
||||
// processPrepackagedPlugin will return the prepackaged plugin metadata and will also
|
||||
// install the prepackaged plugin if it had been previously enabled and AutomaticPrepackagedPlugins is true.
|
||||
func (ch *Channels) processPrepackagedPlugin(pluginPath *pluginSignaturePath) (*plugin.PrepackagedPlugin, error) {
|
||||
func (s *PluginService) processPrepackagedPlugin(pluginPath *pluginSignaturePath) (*plugin.PrepackagedPlugin, error) {
|
||||
mlog.Debug("Processing prepackaged plugin", mlog.String("path", pluginPath.path))
|
||||
|
||||
fileReader, err := os.Open(pluginPath.path)
|
||||
@@ -1011,18 +1111,18 @@ func (ch *Channels) processPrepackagedPlugin(pluginPath *pluginSignaturePath) (*
|
||||
}
|
||||
|
||||
// Skip installing the plugin at all if automatic prepackaged plugins is disabled
|
||||
if !*ch.cfgSvc.Config().PluginSettings.AutomaticPrepackagedPlugins {
|
||||
if !*s.platform.Config().PluginSettings.AutomaticPrepackagedPlugins {
|
||||
return plugin, nil
|
||||
}
|
||||
|
||||
// Skip installing if the plugin is has not been previously enabled.
|
||||
pluginState := ch.cfgSvc.Config().PluginSettings.PluginStates[plugin.Manifest.Id]
|
||||
pluginState := s.platform.Config().PluginSettings.PluginStates[plugin.Manifest.Id]
|
||||
if pluginState == nil || !pluginState.Enable {
|
||||
return plugin, nil
|
||||
}
|
||||
|
||||
mlog.Debug("Installing prepackaged plugin", mlog.String("path", pluginPath.path))
|
||||
if _, err := ch.installExtractedPlugin(plugin.Manifest, pluginDir, installPluginLocallyOnlyIfNewOrUpgrade); err != nil {
|
||||
if _, err := s.installExtractedPlugin(plugin.Manifest, pluginDir, installPluginLocallyOnlyIfNewOrUpgrade); err != nil {
|
||||
return nil, errors.Wrapf(err, "Failed to install extracted prepackaged plugin %s", pluginPath.path)
|
||||
}
|
||||
|
||||
@@ -1030,24 +1130,24 @@ func (ch *Channels) processPrepackagedPlugin(pluginPath *pluginSignaturePath) (*
|
||||
}
|
||||
|
||||
// installFeatureFlagPlugins handles the automatic installation/upgrade of plugins from feature flags
|
||||
func (ch *Channels) installFeatureFlagPlugins() {
|
||||
ffControledPlugins := ch.cfgSvc.Config().FeatureFlags.Plugins()
|
||||
func (s *PluginService) installFeatureFlagPlugins() {
|
||||
ffControledPlugins := s.platform.Config().FeatureFlags.Plugins()
|
||||
|
||||
// Respect the automatic prepackaged disable setting
|
||||
if !*ch.cfgSvc.Config().PluginSettings.AutomaticPrepackagedPlugins {
|
||||
if !*s.platform.Config().PluginSettings.AutomaticPrepackagedPlugins {
|
||||
return
|
||||
}
|
||||
|
||||
for pluginID, version := range ffControledPlugins {
|
||||
// Skip installing if the plugin has been previously disabled.
|
||||
pluginState := ch.cfgSvc.Config().PluginSettings.PluginStates[pluginID]
|
||||
pluginState := s.platform.Config().PluginSettings.PluginStates[pluginID]
|
||||
if pluginState != nil && !pluginState.Enable {
|
||||
ch.srv.Log().Debug("Not auto installing/upgrade because plugin was disabled", mlog.String("plugin_id", pluginID), mlog.String("version", version))
|
||||
s.platform.Log().Debug("Not auto installing/upgrade because plugin was disabled", mlog.String("plugin_id", pluginID), mlog.String("version", version))
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if we already installed this version as InstallMarketplacePlugin can't handle re-installs well.
|
||||
pluginStatus, err := ch.GetPluginStatus(pluginID)
|
||||
pluginStatus, err := s.GetPluginStatus(pluginID)
|
||||
pluginExists := err == nil
|
||||
if pluginExists && pluginStatus.Version == version {
|
||||
continue
|
||||
@@ -1055,37 +1155,37 @@ func (ch *Channels) installFeatureFlagPlugins() {
|
||||
|
||||
if version != "" && version != "control" {
|
||||
// If we are on-prem skip installation if this is a downgrade
|
||||
license := ch.srv.License()
|
||||
license := s.platform.License()
|
||||
inCloud := license != nil && *license.Features.Cloud
|
||||
if !inCloud && pluginExists {
|
||||
parsedVersion, err := semver.Parse(version)
|
||||
if err != nil {
|
||||
ch.srv.Log().Debug("Bad version from feature flag", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", version))
|
||||
s.platform.Log().Debug("Bad version from feature flag", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", version))
|
||||
return
|
||||
}
|
||||
parsedExistingVersion, err := semver.Parse(pluginStatus.Version)
|
||||
if err != nil {
|
||||
ch.srv.Log().Debug("Bad version from plugin manifest", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", pluginStatus.Version))
|
||||
s.platform.Log().Debug("Bad version from plugin manifest", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", pluginStatus.Version))
|
||||
return
|
||||
}
|
||||
|
||||
if parsedVersion.LTE(parsedExistingVersion) {
|
||||
ch.srv.Log().Debug("Skip installation because given version was a downgrade and on-prem installations should not downgrade.", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", pluginStatus.Version))
|
||||
s.platform.Log().Debug("Skip installation because given version was a downgrade and on-prem installations should not downgrade.", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", pluginStatus.Version))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_, err := ch.InstallMarketplacePlugin(&model.InstallMarketplacePluginRequest{
|
||||
_, err := s.InstallMarketplacePlugin(&model.InstallMarketplacePluginRequest{
|
||||
Id: pluginID,
|
||||
Version: version,
|
||||
})
|
||||
if err != nil {
|
||||
ch.srv.Log().Debug("Unable to install plugin from FF manifest", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", version))
|
||||
s.platform.Log().Debug("Unable to install plugin from FF manifest", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", version))
|
||||
} else {
|
||||
if err := ch.enablePlugin(pluginID); err != nil {
|
||||
ch.srv.Log().Debug("Unable to enable plugin installed from feature flag.", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", version))
|
||||
if err := s.enablePlugin(pluginID); err != nil {
|
||||
s.platform.Log().Debug("Unable to enable plugin installed from feature flag.", mlog.String("plugin_id", pluginID), mlog.Err(err), mlog.String("version", version))
|
||||
} else {
|
||||
ch.srv.Log().Debug("Installed and enabled plugin.", mlog.String("plugin_id", pluginID), mlog.String("version", version))
|
||||
s.platform.Log().Debug("Installed and enabled plugin.", mlog.String("plugin_id", pluginID), mlog.String("version", version))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1140,15 +1240,15 @@ func getIcon(iconPath string) (string, error) {
|
||||
return fmt.Sprintf("data:image/svg+xml;base64,%s", base64.StdEncoding.EncodeToString(icon)), nil
|
||||
}
|
||||
|
||||
func (ch *Channels) getPluginStateOverride(pluginID string) (bool, bool) {
|
||||
func (s *PluginService) getPluginStateOverride(pluginID string) (bool, bool) {
|
||||
switch pluginID {
|
||||
case model.PluginIdApps:
|
||||
// Tie Apps proxy disabled status to the feature flag.
|
||||
if !ch.cfgSvc.Config().FeatureFlags.AppsEnabled {
|
||||
if !s.platform.Config().FeatureFlags.AppsEnabled {
|
||||
return true, false
|
||||
}
|
||||
case model.PluginIdCalls:
|
||||
if !ch.cfgSvc.Config().FeatureFlags.CallsEnabled {
|
||||
if !s.platform.Config().FeatureFlags.CallsEnabled {
|
||||
return true, false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -886,7 +886,7 @@ func (api *PluginAPI) DisablePlugin(id string) *model.AppError {
|
||||
}
|
||||
|
||||
func (api *PluginAPI) RemovePlugin(id string) *model.AppError {
|
||||
return api.app.Channels().RemovePlugin(id)
|
||||
return api.app.Srv().pluginService.RemovePlugin(id)
|
||||
}
|
||||
|
||||
func (api *PluginAPI) GetPluginStatus(id string) (*model.PluginStatus, *model.AppError) {
|
||||
@@ -1235,7 +1235,7 @@ func (api *PluginAPI) GetCloudLimits() (*model.ProductLimits, error) {
|
||||
// RegisterCollectionAndTopic informs the server that this plugin handles
|
||||
// the given collection and topic types.
|
||||
func (api *PluginAPI) RegisterCollectionAndTopic(collectionType, topicType string) error {
|
||||
return api.app.registerCollectionAndTopic(api.id, collectionType, topicType)
|
||||
return api.app.Srv().pluginService.registerCollectionAndTopic(api.id, collectionType, topicType)
|
||||
}
|
||||
|
||||
func (api *PluginAPI) CreateUploadSession(us *model.UploadSession) (*model.UploadSession, error) {
|
||||
|
||||
@@ -92,7 +92,7 @@ func setupMultiPluginAPITest(t *testing.T, pluginCodes []string, pluginManifests
|
||||
return app.NewPluginAPI(c, manifest)
|
||||
}
|
||||
|
||||
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(app.Srv()), pluginDir, webappPluginDir, false, app.Log(), nil)
|
||||
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(app.Srv().Platform()), pluginDir, webappPluginDir, false, app.Log(), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, len(pluginCodes), len(pluginIDs))
|
||||
@@ -119,7 +119,7 @@ func setupMultiPluginAPITest(t *testing.T, pluginCodes []string, pluginManifests
|
||||
})
|
||||
}
|
||||
|
||||
app.ch.SetPluginsEnvironment(env)
|
||||
app.PluginService().SetPluginsEnvironment(env)
|
||||
|
||||
return pluginDir
|
||||
}
|
||||
@@ -849,7 +849,7 @@ func TestPluginAPIGetPlugins(t *testing.T) {
|
||||
defer os.RemoveAll(pluginDir)
|
||||
defer os.RemoveAll(webappPluginDir)
|
||||
|
||||
env, err := plugin.NewEnvironment(th.NewPluginAPI, NewDriverImpl(th.Server), pluginDir, webappPluginDir, false, th.App.Log(), nil)
|
||||
env, err := plugin.NewEnvironment(th.NewPluginAPI, NewDriverImpl(th.Server.Platform()), pluginDir, webappPluginDir, false, th.App.Log(), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
pluginIDs := []string{"pluginid1", "pluginid2", "pluginid3"}
|
||||
@@ -866,7 +866,7 @@ func TestPluginAPIGetPlugins(t *testing.T) {
|
||||
require.True(t, activated)
|
||||
pluginManifests = append(pluginManifests, manifest)
|
||||
}
|
||||
th.App.ch.SetPluginsEnvironment(env)
|
||||
th.App.PluginService().SetPluginsEnvironment(env)
|
||||
|
||||
// Deactivate the last one for testing
|
||||
success := env.Deactivate(pluginIDs[len(pluginIDs)-1])
|
||||
@@ -937,10 +937,10 @@ func TestInstallPlugin(t *testing.T) {
|
||||
return app.NewPluginAPI(c, manifest)
|
||||
}
|
||||
|
||||
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(app.Srv()), pluginDir, webappPluginDir, false, app.Log(), nil)
|
||||
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(app.Srv().Platform()), pluginDir, webappPluginDir, false, app.Log(), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
app.ch.SetPluginsEnvironment(env)
|
||||
app.PluginService().SetPluginsEnvironment(env)
|
||||
|
||||
backend := filepath.Join(pluginDir, pluginID, "backend.exe")
|
||||
utils.CompileGo(t, pluginCode, backend)
|
||||
@@ -1632,10 +1632,10 @@ func TestAPIMetrics(t *testing.T) {
|
||||
defer os.RemoveAll(pluginDir)
|
||||
defer os.RemoveAll(webappPluginDir)
|
||||
|
||||
env, err := plugin.NewEnvironment(th.NewPluginAPI, NewDriverImpl(th.Server), pluginDir, webappPluginDir, false, th.App.Log(), metricsMock)
|
||||
env, err := plugin.NewEnvironment(th.NewPluginAPI, NewDriverImpl(th.Server.Platform()), pluginDir, webappPluginDir, false, th.App.Log(), metricsMock)
|
||||
require.NoError(t, err)
|
||||
|
||||
th.App.ch.SetPluginsEnvironment(env)
|
||||
th.App.PluginService().SetPluginsEnvironment(env)
|
||||
|
||||
pluginID := model.NewId()
|
||||
backend := filepath.Join(pluginDir, pluginID, "backend.exe")
|
||||
@@ -2079,10 +2079,10 @@ func TestRegisterCollectionAndTopic(t *testing.T) {
|
||||
return th.App.NewPluginAPI(th.Context, manifest)
|
||||
}
|
||||
|
||||
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(th.App.Srv()), pluginDir, webappPluginDir, false, th.App.Log(), nil)
|
||||
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(th.Server.Platform()), pluginDir, webappPluginDir, false, th.App.Log(), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
th.App.ch.SetPluginsEnvironment(env)
|
||||
th.App.PluginService().SetPluginsEnvironment(env)
|
||||
|
||||
pluginID := "testplugin"
|
||||
pluginManifest := `{"id": "testplugin", "server": {"executable": "backend.exe"}}`
|
||||
@@ -2179,10 +2179,10 @@ func TestPluginUploadsAPI(t *testing.T) {
|
||||
newPluginAPI := func(manifest *model.Manifest) plugin.API {
|
||||
return th.App.NewPluginAPI(th.Context, manifest)
|
||||
}
|
||||
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(th.App.Srv()), pluginDir, webappPluginDir, false, th.App.Log(), nil)
|
||||
env, err := plugin.NewEnvironment(newPluginAPI, NewDriverImpl(th.Server.Platform()), pluginDir, webappPluginDir, false, th.App.Log(), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
th.App.ch.SetPluginsEnvironment(env)
|
||||
th.App.PluginService().SetPluginsEnvironment(env)
|
||||
|
||||
pluginID := "testplugin"
|
||||
pluginManifest := `{"id": "testplugin", "server": {"executable": "backend.exe"}}`
|
||||
|
||||
@@ -22,6 +22,10 @@ type PluginCommand struct {
|
||||
}
|
||||
|
||||
func (a *App) RegisterPluginCommand(pluginID string, command *model.Command) error {
|
||||
return a.Srv().pluginService.registerPluginCommand(pluginID, command)
|
||||
}
|
||||
|
||||
func (s *PluginService) registerPluginCommand(pluginID string, command *model.Command) error {
|
||||
if command.Trigger == "" {
|
||||
return errors.New("invalid command")
|
||||
}
|
||||
@@ -55,10 +59,10 @@ func (a *App) RegisterPluginCommand(pluginID string, command *model.Command) err
|
||||
AutocompleteIconData: command.AutocompleteIconData,
|
||||
}
|
||||
|
||||
a.ch.pluginCommandsLock.Lock()
|
||||
defer a.ch.pluginCommandsLock.Unlock()
|
||||
s.pluginCommandsLock.Lock()
|
||||
defer s.pluginCommandsLock.Unlock()
|
||||
|
||||
for _, pc := range a.ch.pluginCommands {
|
||||
for _, pc := range s.pluginCommands {
|
||||
if pc.Command.Trigger == command.Trigger && pc.Command.TeamId == command.TeamId {
|
||||
if pc.PluginId == pluginID {
|
||||
pc.Command = command
|
||||
@@ -67,7 +71,7 @@ func (a *App) RegisterPluginCommand(pluginID string, command *model.Command) err
|
||||
}
|
||||
}
|
||||
|
||||
a.ch.pluginCommands = append(a.ch.pluginCommands, &PluginCommand{
|
||||
s.pluginCommands = append(s.pluginCommands, &PluginCommand{
|
||||
Command: command,
|
||||
PluginId: pluginID,
|
||||
})
|
||||
@@ -75,39 +79,47 @@ func (a *App) RegisterPluginCommand(pluginID string, command *model.Command) err
|
||||
}
|
||||
|
||||
func (a *App) UnregisterPluginCommand(pluginID, teamID, trigger string) {
|
||||
a.Srv().pluginService.unregisterPluginCommand(pluginID, teamID, trigger)
|
||||
}
|
||||
|
||||
func (s *PluginService) unregisterPluginCommand(pluginID, teamID, trigger string) {
|
||||
trigger = strings.ToLower(trigger)
|
||||
|
||||
a.ch.pluginCommandsLock.Lock()
|
||||
defer a.ch.pluginCommandsLock.Unlock()
|
||||
s.pluginCommandsLock.Lock()
|
||||
defer s.pluginCommandsLock.Unlock()
|
||||
|
||||
var remaining []*PluginCommand
|
||||
for _, pc := range a.ch.pluginCommands {
|
||||
for _, pc := range s.pluginCommands {
|
||||
if pc.Command.TeamId != teamID || pc.Command.Trigger != trigger {
|
||||
remaining = append(remaining, pc)
|
||||
}
|
||||
}
|
||||
a.ch.pluginCommands = remaining
|
||||
s.pluginCommands = remaining
|
||||
}
|
||||
|
||||
func (ch *Channels) unregisterPluginCommands(pluginID string) {
|
||||
ch.pluginCommandsLock.Lock()
|
||||
defer ch.pluginCommandsLock.Unlock()
|
||||
func (s *PluginService) unregisterPluginCommands(pluginID string) {
|
||||
s.pluginCommandsLock.Lock()
|
||||
defer s.pluginCommandsLock.Unlock()
|
||||
|
||||
var remaining []*PluginCommand
|
||||
for _, pc := range ch.pluginCommands {
|
||||
for _, pc := range s.pluginCommands {
|
||||
if pc.PluginId != pluginID {
|
||||
remaining = append(remaining, pc)
|
||||
}
|
||||
}
|
||||
ch.pluginCommands = remaining
|
||||
s.pluginCommands = remaining
|
||||
}
|
||||
|
||||
func (a *App) PluginCommandsForTeam(teamID string) []*model.Command {
|
||||
a.ch.pluginCommandsLock.RLock()
|
||||
defer a.ch.pluginCommandsLock.RUnlock()
|
||||
return a.Srv().pluginService.PluginCommandsForTeam(teamID)
|
||||
}
|
||||
|
||||
func (s *PluginService) PluginCommandsForTeam(teamID string) []*model.Command {
|
||||
s.pluginCommandsLock.RLock()
|
||||
defer s.pluginCommandsLock.RUnlock()
|
||||
|
||||
var commands []*model.Command
|
||||
for _, pc := range a.ch.pluginCommands {
|
||||
for _, pc := range s.pluginCommands {
|
||||
if pc.Command.TeamId == "" || pc.Command.TeamId == teamID {
|
||||
commands = append(commands, pc.Command)
|
||||
}
|
||||
@@ -115,6 +127,24 @@ func (a *App) PluginCommandsForTeam(teamID string) []*model.Command {
|
||||
return commands
|
||||
}
|
||||
|
||||
func (s *PluginService) getPluginCommandFromArgs(args *model.CommandArgs) *PluginCommand {
|
||||
parts := strings.Split(args.Command, " ")
|
||||
trigger := parts[0][1:]
|
||||
trigger = strings.ToLower(trigger)
|
||||
|
||||
var matched *PluginCommand
|
||||
s.pluginCommandsLock.RLock()
|
||||
for _, pc := range s.pluginCommands {
|
||||
if (pc.Command.TeamId == "" || pc.Command.TeamId == args.TeamId) && pc.Command.Trigger == trigger {
|
||||
matched = pc
|
||||
break
|
||||
}
|
||||
}
|
||||
s.pluginCommandsLock.RUnlock()
|
||||
|
||||
return matched
|
||||
}
|
||||
|
||||
// tryExecutePluginCommand attempts to run a command provided by a plugin based on the given arguments. If no such
|
||||
// command can be found, returns nil for all arguments.
|
||||
func (a *App) tryExecutePluginCommand(c request.CTX, args *model.CommandArgs) (*model.Command, *model.CommandResponse, *model.AppError) {
|
||||
@@ -122,15 +152,7 @@ func (a *App) tryExecutePluginCommand(c request.CTX, args *model.CommandArgs) (*
|
||||
trigger := parts[0][1:]
|
||||
trigger = strings.ToLower(trigger)
|
||||
|
||||
var matched *PluginCommand
|
||||
a.ch.pluginCommandsLock.RLock()
|
||||
for _, pc := range a.ch.pluginCommands {
|
||||
if (pc.Command.TeamId == "" || pc.Command.TeamId == args.TeamId) && pc.Command.Trigger == trigger {
|
||||
matched = pc
|
||||
break
|
||||
}
|
||||
}
|
||||
a.ch.pluginCommandsLock.RUnlock()
|
||||
matched := a.Srv().pluginService.getPluginCommandFromArgs(args)
|
||||
if matched == nil {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ func TestPluginCommand(t *testing.T) {
|
||||
require.NotEqual(t, "plugin", commands.Trigger)
|
||||
}
|
||||
|
||||
th.App.ch.RemovePlugin(pluginIDs[0])
|
||||
th.App.PluginService().RemovePlugin(pluginIDs[0])
|
||||
})
|
||||
|
||||
t.Run("re-entrant command registration on config change", func(t *testing.T) {
|
||||
@@ -207,7 +207,7 @@ func TestPluginCommand(t *testing.T) {
|
||||
killed = true
|
||||
}
|
||||
|
||||
th.App.ch.RemovePlugin(pluginIDs[0])
|
||||
th.App.PluginService().RemovePlugin(pluginIDs[0])
|
||||
require.False(t, killed, "execute command appears to have deadlocked")
|
||||
})
|
||||
|
||||
@@ -285,7 +285,7 @@ func TestPluginCommand(t *testing.T) {
|
||||
require.Equal(t, model.CommandResponseTypeEphemeral, resp.ResponseType)
|
||||
require.Equal(t, "text", resp.Text)
|
||||
|
||||
th.App.ch.RemovePlugin(pluginIDs[0])
|
||||
th.App.PluginService().RemovePlugin(pluginIDs[0])
|
||||
})
|
||||
t.Run("plugin has crashed before execution of command", func(t *testing.T) {
|
||||
tearDown, pluginIDs, activationErrors := SetAppEnvironmentWithPlugins(t, []string{`
|
||||
@@ -329,7 +329,7 @@ func TestPluginCommand(t *testing.T) {
|
||||
require.Nil(t, resp)
|
||||
require.NotNil(t, err)
|
||||
require.Equal(t, err.Id, "model.plugin_command_error.error.app_error")
|
||||
th.App.ch.RemovePlugin(pluginIDs[0])
|
||||
th.App.PluginService().RemovePlugin(pluginIDs[0])
|
||||
})
|
||||
|
||||
t.Run("plugin has crashed due to the execution of the command", func(t *testing.T) {
|
||||
@@ -374,7 +374,7 @@ func TestPluginCommand(t *testing.T) {
|
||||
require.Nil(t, resp)
|
||||
require.NotNil(t, err)
|
||||
require.Equal(t, err.Id, "model.plugin_command_crash.error.app_error")
|
||||
th.App.ch.RemovePlugin(pluginIDs[0])
|
||||
th.App.PluginService().RemovePlugin(pluginIDs[0])
|
||||
})
|
||||
|
||||
t.Run("plugin returning status code 0", func(t *testing.T) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/app/platform"
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/plugin"
|
||||
)
|
||||
@@ -19,7 +20,7 @@ import (
|
||||
// a new entry tracked centrally in a map. Further requests operate on the
|
||||
// object ID.
|
||||
type DriverImpl struct {
|
||||
s *Server
|
||||
ps *platform.PlatformService
|
||||
connMut sync.RWMutex
|
||||
connMap map[string]*sql.Conn
|
||||
txMut sync.Mutex
|
||||
@@ -30,9 +31,9 @@ type DriverImpl struct {
|
||||
rowsMap map[string]driver.Rows
|
||||
}
|
||||
|
||||
func NewDriverImpl(s *Server) *DriverImpl {
|
||||
func NewDriverImpl(s *platform.PlatformService) *DriverImpl {
|
||||
return &DriverImpl{
|
||||
s: s,
|
||||
ps: s,
|
||||
connMap: make(map[string]*sql.Conn),
|
||||
txMap: make(map[string]driver.Tx),
|
||||
stMap: make(map[string]driver.Stmt),
|
||||
@@ -41,11 +42,11 @@ func NewDriverImpl(s *Server) *DriverImpl {
|
||||
}
|
||||
|
||||
func (d *DriverImpl) Conn(isMaster bool) (string, error) {
|
||||
dbFunc := d.s.Platform().Store.GetInternalMasterDB
|
||||
dbFunc := d.ps.Store.GetInternalMasterDB
|
||||
if !isMaster {
|
||||
dbFunc = d.s.Platform().Store.GetInternalReplicaDB
|
||||
dbFunc = d.ps.Store.GetInternalReplicaDB
|
||||
}
|
||||
timeout := time.Duration(*d.s.Config().SqlSettings.QueryTimeout) * time.Second
|
||||
timeout := time.Duration(*d.ps.Config().SqlSettings.QueryTimeout) * time.Second
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
conn, err := dbFunc().Conn(ctx)
|
||||
|
||||
@@ -15,7 +15,7 @@ func TestConnCreateTimeout(t *testing.T) {
|
||||
|
||||
*th.App.Config().SqlSettings.QueryTimeout = 0
|
||||
|
||||
d := NewDriverImpl(th.Server)
|
||||
d := NewDriverImpl(th.Server.platform)
|
||||
_, err := d.Conn(true)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ import (
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
)
|
||||
|
||||
func (ch *Channels) notifyClusterPluginEvent(event model.ClusterEvent, data model.PluginEventData) {
|
||||
func (s *PluginService) notifyClusterPluginEvent(event model.ClusterEvent, data model.PluginEventData) {
|
||||
buf, _ := json.Marshal(data)
|
||||
if ch.srv.platform.Cluster() != nil {
|
||||
ch.srv.platform.Cluster().SendClusterMessage(&model.ClusterMessage{
|
||||
if s.platform.Cluster() != nil {
|
||||
s.platform.Cluster().SendClusterMessage(&model.ClusterMessage{
|
||||
Event: event,
|
||||
SendType: model.ClusterSendReliable,
|
||||
WaitForAllToSend: true,
|
||||
|
||||
@@ -33,10 +33,10 @@ func SetAppEnvironmentWithPlugins(t *testing.T, pluginCode []string, app *App, a
|
||||
webappPluginDir, err := os.MkdirTemp("", "")
|
||||
require.NoError(t, err)
|
||||
|
||||
env, err := plugin.NewEnvironment(apiFunc, NewDriverImpl(app.Srv()), pluginDir, webappPluginDir, false, app.Log(), nil)
|
||||
env, err := plugin.NewEnvironment(apiFunc, NewDriverImpl(app.Srv().Platform()), pluginDir, webappPluginDir, false, app.Log(), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
app.ch.SetPluginsEnvironment(env)
|
||||
app.PluginService().SetPluginsEnvironment(env)
|
||||
pluginIDs := []string{}
|
||||
activationErrors := []error{}
|
||||
for _, code := range pluginCode {
|
||||
@@ -1030,10 +1030,10 @@ func TestHookMetrics(t *testing.T) {
|
||||
defer os.RemoveAll(pluginDir)
|
||||
defer os.RemoveAll(webappPluginDir)
|
||||
|
||||
env, err := plugin.NewEnvironment(th.NewPluginAPI, NewDriverImpl(th.Server), pluginDir, webappPluginDir, false, th.App.Log(), metricsMock)
|
||||
env, err := plugin.NewEnvironment(th.NewPluginAPI, NewDriverImpl(th.Server.Platform()), pluginDir, webappPluginDir, false, th.App.Log(), metricsMock)
|
||||
require.NoError(t, err)
|
||||
|
||||
th.App.ch.SetPluginsEnvironment(env)
|
||||
th.App.PluginService().SetPluginsEnvironment(env)
|
||||
|
||||
pluginID := model.NewId()
|
||||
backend := filepath.Join(pluginDir, pluginID, "backend.exe")
|
||||
@@ -1234,7 +1234,7 @@ func TestHookRunDataRetention(t *testing.T) {
|
||||
require.True(t, th.App.GetPluginsEnvironment().IsActive(pluginID))
|
||||
|
||||
hookCalled := false
|
||||
th.App.Channels().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
th.App.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
n, _ := hooks.RunDataRetention(0, 0)
|
||||
// Ensure return it correct
|
||||
assert.Equal(t, int64(100), n)
|
||||
@@ -1278,7 +1278,7 @@ func TestHookOnSendDailyTelemetry(t *testing.T) {
|
||||
require.True(t, th.App.GetPluginsEnvironment().IsActive(pluginID))
|
||||
|
||||
hookCalled := false
|
||||
th.App.Channels().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
th.App.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.OnSendDailyTelemetry()
|
||||
|
||||
hookCalled = true
|
||||
@@ -1322,7 +1322,7 @@ func TestHookOnCloudLimitsUpdated(t *testing.T) {
|
||||
require.True(t, th.App.GetPluginsEnvironment().IsActive(pluginID))
|
||||
|
||||
hookCalled := false
|
||||
th.App.Channels().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
th.App.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.OnCloudLimitsUpdated(nil)
|
||||
|
||||
hookCalled = true
|
||||
|
||||
@@ -58,10 +58,10 @@ const managedPluginFileName = ".filestore"
|
||||
// fileStorePluginFolder is the folder name in the file store of the plugin bundles installed.
|
||||
const fileStorePluginFolder = "plugins"
|
||||
|
||||
func (ch *Channels) installPluginFromData(data model.PluginEventData) {
|
||||
func (s *PluginService) installPluginFromData(data model.PluginEventData) {
|
||||
mlog.Debug("Installing plugin as per cluster message", mlog.String("plugin_id", data.Id))
|
||||
|
||||
pluginSignaturePathMap, appErr := ch.getPluginsFromFolder()
|
||||
pluginSignaturePathMap, appErr := s.getPluginsFromFolder()
|
||||
if appErr != nil {
|
||||
mlog.Error("Failed to get plugin signatures from filestore. Can't install plugin from data.", mlog.Err(appErr))
|
||||
return
|
||||
@@ -72,53 +72,53 @@ func (ch *Channels) installPluginFromData(data model.PluginEventData) {
|
||||
return
|
||||
}
|
||||
|
||||
reader, appErr := ch.srv.fileReader(plugin.path)
|
||||
if appErr != nil {
|
||||
mlog.Error("Failed to open plugin bundle from file store.", mlog.String("bundle", plugin.path), mlog.Err(appErr))
|
||||
reader, err := s.fileStore.Reader(plugin.path)
|
||||
if err != nil {
|
||||
mlog.Error("Failed to open plugin bundle from file store.", mlog.String("bundle", plugin.path), mlog.Err(err))
|
||||
return
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
var signature filestore.ReadCloseSeeker
|
||||
if *ch.cfgSvc.Config().PluginSettings.RequirePluginSignature {
|
||||
signature, appErr = ch.srv.fileReader(plugin.signaturePath)
|
||||
if appErr != nil {
|
||||
mlog.Error("Failed to open plugin signature from file store.", mlog.Err(appErr))
|
||||
if *s.platform.Config().PluginSettings.RequirePluginSignature {
|
||||
signature, err = s.fileStore.Reader(plugin.signaturePath)
|
||||
if err != nil {
|
||||
mlog.Error("Failed to open plugin signature from file store.", mlog.Err(err))
|
||||
return
|
||||
}
|
||||
defer signature.Close()
|
||||
}
|
||||
|
||||
manifest, appErr := ch.installPluginLocally(reader, signature, installPluginLocallyAlways)
|
||||
manifest, appErr := s.installPluginLocally(reader, signature, installPluginLocallyAlways)
|
||||
if appErr != nil {
|
||||
mlog.Error("Failed to sync plugin from file store", mlog.String("bundle", plugin.path), mlog.Err(appErr))
|
||||
return
|
||||
}
|
||||
|
||||
if err := ch.notifyPluginEnabled(manifest); err != nil {
|
||||
mlog.Error("Failed notify plugin enabled", mlog.Err(err))
|
||||
if err2 := s.notifyPluginEnabled(manifest); err2 != nil {
|
||||
mlog.Error("Failed notify plugin enabled", mlog.Err(err2))
|
||||
}
|
||||
|
||||
if err := ch.notifyPluginStatusesChanged(); err != nil {
|
||||
mlog.Error("Failed to notify plugin status changed", mlog.Err(err))
|
||||
if err2 := s.notifyPluginStatusesChanged(); err2 != nil {
|
||||
mlog.Error("Failed to notify plugin status changed", mlog.Err(err2))
|
||||
}
|
||||
}
|
||||
|
||||
func (ch *Channels) removePluginFromData(data model.PluginEventData) {
|
||||
func (s *PluginService) removePluginFromData(data model.PluginEventData) {
|
||||
mlog.Debug("Removing plugin as per cluster message", mlog.String("plugin_id", data.Id))
|
||||
|
||||
if err := ch.removePluginLocally(data.Id); err != nil {
|
||||
if err := s.removePluginLocally(data.Id); err != nil {
|
||||
mlog.Warn("Failed to remove plugin locally", mlog.Err(err), mlog.String("id", data.Id))
|
||||
}
|
||||
|
||||
if err := ch.notifyPluginStatusesChanged(); err != nil {
|
||||
if err := s.notifyPluginStatusesChanged(); err != nil {
|
||||
mlog.Warn("failed to notify plugin status changed", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
|
||||
// InstallPluginWithSignature verifies and installs plugin.
|
||||
func (ch *Channels) installPluginWithSignature(pluginFile, signature io.ReadSeeker) (*model.Manifest, *model.AppError) {
|
||||
return ch.installPlugin(pluginFile, signature, installPluginLocallyAlways)
|
||||
func (s *PluginService) installPluginWithSignature(pluginFile, signature io.ReadSeeker) (*model.Manifest, *model.AppError) {
|
||||
return s.installPlugin(pluginFile, signature, installPluginLocallyAlways)
|
||||
}
|
||||
|
||||
// InstallPlugin unpacks and installs a plugin but does not enable or activate it.
|
||||
@@ -132,40 +132,40 @@ func (a *App) InstallPlugin(pluginFile io.ReadSeeker, replace bool) (*model.Mani
|
||||
}
|
||||
|
||||
func (a *App) installPlugin(pluginFile, signature io.ReadSeeker, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) {
|
||||
return a.ch.installPlugin(pluginFile, signature, installationStrategy)
|
||||
return a.ch.srv.pluginService.installPlugin(pluginFile, signature, installationStrategy)
|
||||
}
|
||||
|
||||
func (ch *Channels) installPlugin(pluginFile, signature io.ReadSeeker, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) {
|
||||
manifest, appErr := ch.installPluginLocally(pluginFile, signature, installationStrategy)
|
||||
func (s *PluginService) installPlugin(pluginFile, signature io.ReadSeeker, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) {
|
||||
manifest, appErr := s.installPluginLocally(pluginFile, signature, installationStrategy)
|
||||
if appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
|
||||
if signature != nil {
|
||||
signature.Seek(0, 0)
|
||||
if _, appErr = ch.srv.writeFile(signature, getSignatureStorePath(manifest.Id)); appErr != nil {
|
||||
return nil, model.NewAppError("saveSignature", "app.plugin.store_signature.app_error", nil, "", http.StatusInternalServerError).Wrap(appErr)
|
||||
if _, err := s.fileStore.WriteFile(signature, getSignatureStorePath(manifest.Id)); err != nil {
|
||||
return nil, model.NewAppError("saveSignature", "app.plugin.store_signature.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Store bundle in the file store to allow access from other servers.
|
||||
pluginFile.Seek(0, 0)
|
||||
if _, appErr := ch.srv.writeFile(pluginFile, getBundleStorePath(manifest.Id)); appErr != nil {
|
||||
if _, appErr := s.fileStore.WriteFile(pluginFile, getBundleStorePath(manifest.Id)); appErr != nil {
|
||||
return nil, model.NewAppError("uploadPlugin", "app.plugin.store_bundle.app_error", nil, "", http.StatusInternalServerError).Wrap(appErr)
|
||||
}
|
||||
|
||||
ch.notifyClusterPluginEvent(
|
||||
s.notifyClusterPluginEvent(
|
||||
model.ClusterEventInstallPlugin,
|
||||
model.PluginEventData{
|
||||
Id: manifest.Id,
|
||||
},
|
||||
)
|
||||
|
||||
if err := ch.notifyPluginEnabled(manifest); err != nil {
|
||||
if err := s.notifyPluginEnabled(manifest); err != nil {
|
||||
mlog.Warn("Failed notify plugin enabled", mlog.Err(err))
|
||||
}
|
||||
|
||||
if err := ch.notifyPluginStatusesChanged(); err != nil {
|
||||
if err := s.notifyPluginStatusesChanged(); err != nil {
|
||||
mlog.Warn("Failed to notify plugin status changed", mlog.Err(err))
|
||||
}
|
||||
|
||||
@@ -174,10 +174,10 @@ func (ch *Channels) installPlugin(pluginFile, signature io.ReadSeeker, installat
|
||||
|
||||
// InstallMarketplacePlugin installs a plugin listed in the marketplace server. It will get the plugin bundle
|
||||
// from the prepackaged folder, if available, or remotely if EnableRemoteMarketplace is true.
|
||||
func (ch *Channels) InstallMarketplacePlugin(request *model.InstallMarketplacePluginRequest) (*model.Manifest, *model.AppError) {
|
||||
func (s *PluginService) InstallMarketplacePlugin(request *model.InstallMarketplacePluginRequest) (*model.Manifest, *model.AppError) {
|
||||
var pluginFile, signatureFile io.ReadSeeker
|
||||
|
||||
prepackagedPlugin, appErr := ch.getPrepackagedPlugin(request.Id, request.Version)
|
||||
prepackagedPlugin, appErr := s.getPrepackagedPlugin(request.Id, request.Version)
|
||||
if appErr != nil && appErr.Id != "app.plugin.marketplace_plugins.not_found.app_error" {
|
||||
return nil, appErr
|
||||
}
|
||||
@@ -192,9 +192,9 @@ func (ch *Channels) InstallMarketplacePlugin(request *model.InstallMarketplacePl
|
||||
signatureFile = bytes.NewReader(prepackagedPlugin.Signature)
|
||||
}
|
||||
|
||||
if *ch.cfgSvc.Config().PluginSettings.EnableRemoteMarketplace {
|
||||
if *s.platform.Config().PluginSettings.EnableRemoteMarketplace {
|
||||
var plugin *model.BaseMarketplacePlugin
|
||||
plugin, appErr = ch.getRemoteMarketplacePlugin(request.Id, request.Version)
|
||||
plugin, appErr = s.getRemoteMarketplacePlugin(request.Id, request.Version)
|
||||
if appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
@@ -214,7 +214,7 @@ func (ch *Channels) InstallMarketplacePlugin(request *model.InstallMarketplacePl
|
||||
}
|
||||
|
||||
if prepackagedVersion.LT(marketplaceVersion) { // Always true if no prepackaged plugin was found
|
||||
downloadedPluginBytes, err := ch.srv.downloadFromURL(plugin.DownloadURL)
|
||||
downloadedPluginBytes, err := s.downloadFromURL(plugin.DownloadURL)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("InstallMarketplacePlugin", "app.plugin.install_marketplace_plugin.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
@@ -234,7 +234,7 @@ func (ch *Channels) InstallMarketplacePlugin(request *model.InstallMarketplacePl
|
||||
return nil, model.NewAppError("InstallMarketplacePlugin", "app.plugin.marketplace_plugins.signature_not_found.app_error", nil, "", http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
manifest, appErr := ch.installPluginWithSignature(pluginFile, signatureFile)
|
||||
manifest, appErr := s.installPluginWithSignature(pluginFile, signatureFile)
|
||||
if appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
@@ -253,15 +253,15 @@ const (
|
||||
installPluginLocallyAlways
|
||||
)
|
||||
|
||||
func (ch *Channels) installPluginLocally(pluginFile, signature io.ReadSeeker, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) {
|
||||
pluginsEnvironment := ch.GetPluginsEnvironment()
|
||||
func (s *PluginService) installPluginLocally(pluginFile, signature io.ReadSeeker, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) {
|
||||
pluginsEnvironment := s.GetPluginsEnvironment()
|
||||
if pluginsEnvironment == nil {
|
||||
return nil, model.NewAppError("installPluginLocally", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
// verify signature
|
||||
if signature != nil {
|
||||
if err := ch.verifyPlugin(pluginFile, signature); err != nil {
|
||||
if err := s.verifyPlugin(pluginFile, signature); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -277,7 +277,7 @@ func (ch *Channels) installPluginLocally(pluginFile, signature io.ReadSeeker, in
|
||||
return nil, appErr
|
||||
}
|
||||
|
||||
manifest, appErr = ch.installExtractedPlugin(manifest, pluginDir, installationStrategy)
|
||||
manifest, appErr = s.installExtractedPlugin(manifest, pluginDir, installationStrategy)
|
||||
if appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
@@ -312,8 +312,8 @@ func extractPlugin(pluginFile io.ReadSeeker, extractDir string) (*model.Manifest
|
||||
return manifest, extractDir, nil
|
||||
}
|
||||
|
||||
func (ch *Channels) installExtractedPlugin(manifest *model.Manifest, fromPluginDir string, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) {
|
||||
pluginsEnvironment := ch.GetPluginsEnvironment()
|
||||
func (s *PluginService) installExtractedPlugin(manifest *model.Manifest, fromPluginDir string, installationStrategy pluginInstallationStrategy) (*model.Manifest, *model.AppError) {
|
||||
pluginsEnvironment := s.GetPluginsEnvironment()
|
||||
if pluginsEnvironment == nil {
|
||||
return nil, model.NewAppError("installExtractedPlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
@@ -360,12 +360,12 @@ func (ch *Channels) installExtractedPlugin(manifest *model.Manifest, fromPluginD
|
||||
|
||||
// Otherwise remove the existing installation prior to install below.
|
||||
mlog.Debug("Removing existing installation of plugin before local install", mlog.String("plugin_id", existingManifest.Id), mlog.String("version", existingManifest.Version))
|
||||
if err := ch.removePluginLocally(existingManifest.Id); err != nil {
|
||||
if err := s.removePluginLocally(existingManifest.Id); err != nil {
|
||||
return nil, model.NewAppError("installExtractedPlugin", "app.plugin.install_id_failed_remove.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
pluginPath := filepath.Join(*ch.cfgSvc.Config().PluginSettings.Directory, manifest.Id)
|
||||
pluginPath := filepath.Join(*s.platform.Config().PluginSettings.Directory, manifest.Id)
|
||||
err = utils.CopyDir(fromPluginDir, pluginPath)
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("installExtractedPlugin", "app.plugin.mvdir.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
@@ -387,9 +387,9 @@ func (ch *Channels) installExtractedPlugin(manifest *model.Manifest, fromPluginD
|
||||
}
|
||||
|
||||
// Activate the plugin if enabled.
|
||||
pluginState := ch.cfgSvc.Config().PluginSettings.PluginStates[manifest.Id]
|
||||
pluginState := s.platform.Config().PluginSettings.PluginStates[manifest.Id]
|
||||
if pluginState != nil && pluginState.Enable {
|
||||
if hasOverride, enabled := ch.getPluginStateOverride(manifest.Id); hasOverride && !enabled {
|
||||
if hasOverride, enabled := s.getPluginStateOverride(manifest.Id); hasOverride && !enabled {
|
||||
return manifest, nil
|
||||
}
|
||||
|
||||
@@ -405,49 +405,49 @@ func (ch *Channels) installExtractedPlugin(manifest *model.Manifest, fromPluginD
|
||||
return manifest, nil
|
||||
}
|
||||
|
||||
func (ch *Channels) RemovePlugin(id string) *model.AppError {
|
||||
func (s *PluginService) RemovePlugin(id string) *model.AppError {
|
||||
// Disable plugin before removal to make sure this
|
||||
// plugin remains disabled on re-install.
|
||||
if err := ch.disablePlugin(id); err != nil {
|
||||
if err := s.disablePlugin(id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ch.removePluginLocally(id); err != nil {
|
||||
if err := s.removePluginLocally(id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove bundle from the file store.
|
||||
storePluginFileName := getBundleStorePath(id)
|
||||
bundleExist, err := ch.srv.fileExists(storePluginFileName)
|
||||
bundleExist, err := s.fileStore.FileExists(storePluginFileName)
|
||||
if err != nil {
|
||||
return model.NewAppError("removePlugin", "app.plugin.remove_bundle.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
if !bundleExist {
|
||||
return nil
|
||||
}
|
||||
if err = ch.srv.removeFile(storePluginFileName); err != nil {
|
||||
if err = s.fileStore.RemoveFile(storePluginFileName); err != nil {
|
||||
return model.NewAppError("removePlugin", "app.plugin.remove_bundle.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
if err = ch.removeSignature(id); err != nil {
|
||||
mlog.Warn("Can't remove signature", mlog.Err(err))
|
||||
if err2 := s.removeSignature(id); err2 != nil {
|
||||
mlog.Warn("Can't remove signature", mlog.Err(err2))
|
||||
}
|
||||
|
||||
ch.notifyClusterPluginEvent(
|
||||
s.notifyClusterPluginEvent(
|
||||
model.ClusterEventRemovePlugin,
|
||||
model.PluginEventData{
|
||||
Id: id,
|
||||
},
|
||||
)
|
||||
|
||||
if err := ch.notifyPluginStatusesChanged(); err != nil {
|
||||
if err := s.notifyPluginStatusesChanged(); err != nil {
|
||||
mlog.Warn("Failed to notify plugin status changed", mlog.Err(err))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ch *Channels) removePluginLocally(id string) *model.AppError {
|
||||
pluginsEnvironment := ch.GetPluginsEnvironment()
|
||||
func (s *PluginService) removePluginLocally(id string) *model.AppError {
|
||||
pluginsEnvironment := s.GetPluginsEnvironment()
|
||||
if pluginsEnvironment == nil {
|
||||
return model.NewAppError("removePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
@@ -473,7 +473,7 @@ func (ch *Channels) removePluginLocally(id string) *model.AppError {
|
||||
|
||||
pluginsEnvironment.Deactivate(id)
|
||||
pluginsEnvironment.RemovePlugin(id)
|
||||
ch.unregisterPluginCommands(id)
|
||||
s.unregisterPluginCommands(id)
|
||||
|
||||
if err := os.RemoveAll(pluginPath); err != nil {
|
||||
return model.NewAppError("removePlugin", "app.plugin.remove.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
@@ -482,9 +482,9 @@ func (ch *Channels) removePluginLocally(id string) *model.AppError {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ch *Channels) removeSignature(pluginID string) *model.AppError {
|
||||
func (s *PluginService) removeSignature(pluginID string) *model.AppError {
|
||||
filePath := getSignatureStorePath(pluginID)
|
||||
exists, err := ch.srv.fileExists(filePath)
|
||||
exists, err := s.fileStore.FileExists(filePath)
|
||||
if err != nil {
|
||||
return model.NewAppError("removeSignature", "app.plugin.remove_bundle.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
@@ -492,7 +492,7 @@ func (ch *Channels) removeSignature(pluginID string) *model.AppError {
|
||||
mlog.Debug("no plugin signature to remove", mlog.String("plugin_id", pluginID))
|
||||
return nil
|
||||
}
|
||||
if err = ch.srv.removeFile(filePath); err != nil {
|
||||
if err = s.fileStore.RemoveFile(filePath); err != nil {
|
||||
return model.NewAppError("removeSignature", "app.plugin.remove_bundle.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -73,7 +73,7 @@ func TestInstallPluginLocally(t *testing.T) {
|
||||
th := Setup(t)
|
||||
defer th.TearDown()
|
||||
|
||||
actualManifest, appErr := th.App.ch.installPluginLocally(&nilReadSeeker{}, nil, installPluginLocallyOnlyIfNew)
|
||||
actualManifest, appErr := th.App.PluginService().installPluginLocally(&nilReadSeeker{}, nil, installPluginLocallyOnlyIfNew)
|
||||
require.NotNil(t, appErr)
|
||||
assert.Equal(t, "app.plugin.extract.app_error", appErr.Id, appErr.Error())
|
||||
require.Nil(t, actualManifest)
|
||||
@@ -87,7 +87,7 @@ func TestInstallPluginLocally(t *testing.T) {
|
||||
{"test", "test file"},
|
||||
})
|
||||
|
||||
actualManifest, appErr := th.App.ch.installPluginLocally(reader, nil, installPluginLocallyOnlyIfNew)
|
||||
actualManifest, appErr := th.App.PluginService().installPluginLocally(reader, nil, installPluginLocallyOnlyIfNew)
|
||||
require.NotNil(t, appErr)
|
||||
assert.Equal(t, "app.plugin.manifest.app_error", appErr.Id, appErr.Error())
|
||||
require.Nil(t, actualManifest)
|
||||
@@ -106,7 +106,7 @@ func TestInstallPluginLocally(t *testing.T) {
|
||||
{"plugin.json", string(manifestJSON)},
|
||||
})
|
||||
|
||||
actualManifest, appError := th.App.ch.installPluginLocally(reader, nil, installationStrategy)
|
||||
actualManifest, appError := th.App.PluginService().installPluginLocally(reader, nil, installationStrategy)
|
||||
if actualManifest != nil {
|
||||
require.Equal(t, manifest, actualManifest)
|
||||
}
|
||||
@@ -134,7 +134,7 @@ func TestInstallPluginLocally(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, bundleInfo := range bundleInfos {
|
||||
err := th.App.ch.removePluginLocally(bundleInfo.Manifest.Id)
|
||||
err := th.App.PluginService().removePluginLocally(bundleInfo.Manifest.Id)
|
||||
require.Nilf(t, err, "failed to remove existing plugin %s", bundleInfo.Manifest.Id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,16 +20,16 @@ import (
|
||||
"github.com/mattermost/mattermost-server/v6/utils"
|
||||
)
|
||||
|
||||
func (ch *Channels) ServePluginRequest(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *PluginService) ServePluginRequest(w http.ResponseWriter, r *http.Request) {
|
||||
params := mux.Vars(r)
|
||||
if handler, ok := ch.routerSvc.getHandler(params["plugin_id"]); ok {
|
||||
ch.servePluginRequest(w, r, func(*plugin.Context, http.ResponseWriter, *http.Request) {
|
||||
if handler, ok := s.channels.routerSvc.getHandler(params["plugin_id"]); ok {
|
||||
s.servePluginRequest(w, r, func(*plugin.Context, http.ResponseWriter, *http.Request) {
|
||||
handler.ServeHTTP(w, r)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
pluginsEnvironment := ch.GetPluginsEnvironment()
|
||||
pluginsEnvironment := s.GetPluginsEnvironment()
|
||||
if pluginsEnvironment == nil {
|
||||
err := model.NewAppError("ServePluginRequest", "app.plugin.disabled.app_error", nil, "Enable plugins to serve plugin requests", http.StatusNotImplemented)
|
||||
mlog.Error(err.Error())
|
||||
@@ -49,11 +49,11 @@ func (ch *Channels) ServePluginRequest(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
ch.servePluginRequest(w, r, hooks.ServeHTTP)
|
||||
s.servePluginRequest(w, r, hooks.ServeHTTP)
|
||||
}
|
||||
|
||||
func (a *App) ServeInterPluginRequest(w http.ResponseWriter, r *http.Request, sourcePluginId, destinationPluginId string) {
|
||||
pluginsEnvironment := a.ch.GetPluginsEnvironment()
|
||||
pluginsEnvironment := a.ch.srv.pluginService.GetPluginsEnvironment()
|
||||
if pluginsEnvironment == nil {
|
||||
err := model.NewAppError("ServeInterPluginRequest", "app.plugin.disabled.app_error", nil, "Plugin environment not found.", http.StatusNotImplemented)
|
||||
a.Log().Error(err.Error())
|
||||
@@ -87,7 +87,7 @@ func (a *App) ServeInterPluginRequest(w http.ResponseWriter, r *http.Request, so
|
||||
|
||||
// ServePluginPublicRequest serves public plugin files
|
||||
// at the URL http(s)://$SITE_URL/plugins/$PLUGIN_ID/public/{anything}
|
||||
func (ch *Channels) ServePluginPublicRequest(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *PluginService) ServePluginPublicRequest(w http.ResponseWriter, r *http.Request) {
|
||||
if strings.HasSuffix(r.URL.Path, "/") {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
@@ -97,7 +97,7 @@ func (ch *Channels) ServePluginPublicRequest(w http.ResponseWriter, r *http.Requ
|
||||
vars := mux.Vars(r)
|
||||
pluginID := vars["plugin_id"]
|
||||
|
||||
pluginsEnv := ch.GetPluginsEnvironment()
|
||||
pluginsEnv := s.GetPluginsEnvironment()
|
||||
|
||||
// Check if someone has nullified the pluginsEnv in the meantime
|
||||
if pluginsEnv == nil {
|
||||
@@ -121,11 +121,11 @@ func (ch *Channels) ServePluginPublicRequest(w http.ResponseWriter, r *http.Requ
|
||||
http.ServeFile(w, r, publicFile)
|
||||
}
|
||||
|
||||
func (ch *Channels) servePluginRequest(w http.ResponseWriter, r *http.Request, handler func(*plugin.Context, http.ResponseWriter, *http.Request)) {
|
||||
func (s *PluginService) servePluginRequest(w http.ResponseWriter, r *http.Request, handler func(*plugin.Context, http.ResponseWriter, *http.Request)) {
|
||||
token := ""
|
||||
context := &plugin.Context{
|
||||
RequestId: model.NewId(),
|
||||
IPAddress: utils.GetIPAddress(r, ch.cfgSvc.Config().ServiceSettings.TrustedProxyIPHeader),
|
||||
IPAddress: utils.GetIPAddress(r, s.platform.Config().ServiceSettings.TrustedProxyIPHeader),
|
||||
AcceptLanguage: r.Header.Get("Accept-Language"),
|
||||
UserAgent: r.UserAgent(),
|
||||
}
|
||||
@@ -148,8 +148,8 @@ func (ch *Channels) servePluginRequest(w http.ResponseWriter, r *http.Request, h
|
||||
|
||||
r.Header.Del("Mattermost-User-Id")
|
||||
if token != "" {
|
||||
session, err := New(ServerConnector(ch)).GetSession(token)
|
||||
defer ch.srv.platform.ReturnSessionToPool(session)
|
||||
session, err := New(ServerConnector(s.channels)).GetSession(token)
|
||||
defer s.platform.ReturnSessionToPool(session)
|
||||
|
||||
csrfCheckPassed := false
|
||||
|
||||
@@ -190,7 +190,7 @@ func (ch *Channels) servePluginRequest(w http.ResponseWriter, r *http.Request, h
|
||||
mlog.String("user_id", userID),
|
||||
}
|
||||
|
||||
if *ch.cfgSvc.Config().ServiceSettings.ExperimentalStrictCSRFEnforcement {
|
||||
if *s.platform.Config().ServiceSettings.ExperimentalStrictCSRFEnforcement {
|
||||
mlog.Warn(csrfErrorMessage, fields...)
|
||||
} else {
|
||||
mlog.Debug(csrfErrorMessage, fields...)
|
||||
@@ -219,7 +219,7 @@ func (ch *Channels) servePluginRequest(w http.ResponseWriter, r *http.Request, h
|
||||
|
||||
params := mux.Vars(r)
|
||||
|
||||
subpath, _ := utils.GetSubpathFromConfig(ch.cfgSvc.Config())
|
||||
subpath, _ := utils.GetSubpathFromConfig(s.platform.Config())
|
||||
|
||||
newQuery := r.URL.Query()
|
||||
newQuery.Del("access_token")
|
||||
|
||||
@@ -24,7 +24,7 @@ func TestServePluginPublicRequest(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
handler := http.HandlerFunc(th.App.ch.ServePluginPublicRequest)
|
||||
handler := http.HandlerFunc(th.App.PluginService().ServePluginPublicRequest)
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, http.StatusNotFound, rr.Code)
|
||||
|
||||
@@ -63,7 +63,7 @@ func TestPluginShutdownTest(t *testing.T) {
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
defer close(done)
|
||||
th.App.ch.ShutDownPlugins()
|
||||
th.App.PluginService().ShutDownPlugins()
|
||||
}()
|
||||
|
||||
select {
|
||||
|
||||
@@ -73,16 +73,16 @@ func (a *App) DeletePublicKey(name string) *model.AppError {
|
||||
|
||||
// VerifyPlugin checks that the given signature corresponds to the given plugin and matches a trusted certificate.
|
||||
func (a *App) VerifyPlugin(plugin, signature io.ReadSeeker) *model.AppError {
|
||||
return a.ch.verifyPlugin(plugin, signature)
|
||||
return a.ch.srv.pluginService.verifyPlugin(plugin, signature)
|
||||
}
|
||||
|
||||
func (ch *Channels) verifyPlugin(plugin, signature io.ReadSeeker) *model.AppError {
|
||||
func (s *PluginService) verifyPlugin(plugin, signature io.ReadSeeker) *model.AppError {
|
||||
if err := verifySignature(bytes.NewReader(mattermostPluginPublicKey), plugin, signature); err == nil {
|
||||
return nil
|
||||
}
|
||||
publicKeys := ch.cfgSvc.Config().PluginSettings.SignaturePublicKeyFiles
|
||||
publicKeys := s.platform.Config().PluginSettings.SignaturePublicKeyFiles
|
||||
for _, pk := range publicKeys {
|
||||
pkBytes, appErr := ch.srv.getPublicKey(pk)
|
||||
pkBytes, appErr := s.platform.GetConfigFile(pk)
|
||||
if appErr != nil {
|
||||
mlog.Warn("Unable to get public key for ", mlog.String("filename", pk))
|
||||
continue
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
)
|
||||
|
||||
// GetPluginStatus returns the status for a plugin installed on this server.
|
||||
func (ch *Channels) GetPluginStatus(id string) (*model.PluginStatus, *model.AppError) {
|
||||
pluginsEnvironment := ch.GetPluginsEnvironment()
|
||||
func (s *PluginService) GetPluginStatus(id string) (*model.PluginStatus, *model.AppError) {
|
||||
pluginsEnvironment := s.GetPluginsEnvironment()
|
||||
if pluginsEnvironment == nil {
|
||||
return nil, model.NewAppError("GetPluginStatus", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
@@ -24,8 +24,8 @@ func (ch *Channels) GetPluginStatus(id string) (*model.PluginStatus, *model.AppE
|
||||
for _, status := range pluginStatuses {
|
||||
if status.PluginId == id {
|
||||
// Add our cluster ID
|
||||
if ch.srv.platform.Cluster() != nil {
|
||||
status.ClusterId = ch.srv.platform.Cluster().GetClusterId()
|
||||
if s.platform.Cluster() != nil {
|
||||
status.ClusterId = s.platform.Cluster().GetClusterId()
|
||||
}
|
||||
|
||||
return status, nil
|
||||
@@ -37,12 +37,12 @@ func (ch *Channels) GetPluginStatus(id string) (*model.PluginStatus, *model.AppE
|
||||
|
||||
// GetPluginStatus returns the status for a plugin installed on this server.
|
||||
func (a *App) GetPluginStatus(id string) (*model.PluginStatus, *model.AppError) {
|
||||
return a.ch.GetPluginStatus(id)
|
||||
return a.ch.srv.pluginService.GetPluginStatus(id)
|
||||
}
|
||||
|
||||
// GetPluginStatuses returns the status for plugins installed on this server.
|
||||
func (ch *Channels) GetPluginStatuses() (model.PluginStatuses, *model.AppError) {
|
||||
pluginsEnvironment := ch.GetPluginsEnvironment()
|
||||
func (s *PluginService) GetPluginStatuses() (model.PluginStatuses, *model.AppError) {
|
||||
pluginsEnvironment := s.GetPluginsEnvironment()
|
||||
if pluginsEnvironment == nil {
|
||||
return nil, model.NewAppError("GetPluginStatuses", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
@@ -54,8 +54,8 @@ func (ch *Channels) GetPluginStatuses() (model.PluginStatuses, *model.AppError)
|
||||
|
||||
// Add our cluster ID
|
||||
for _, status := range pluginStatuses {
|
||||
if ch.srv.platform.Cluster() != nil {
|
||||
status.ClusterId = ch.srv.platform.Cluster().GetClusterId()
|
||||
if s.platform.Cluster() != nil {
|
||||
status.ClusterId = s.platform.Cluster().GetClusterId()
|
||||
} else {
|
||||
status.ClusterId = ""
|
||||
}
|
||||
@@ -66,22 +66,22 @@ func (ch *Channels) GetPluginStatuses() (model.PluginStatuses, *model.AppError)
|
||||
|
||||
// GetPluginStatuses returns the status for plugins installed on this server.
|
||||
func (a *App) GetPluginStatuses() (model.PluginStatuses, *model.AppError) {
|
||||
return a.ch.GetPluginStatuses()
|
||||
return a.ch.srv.pluginService.GetPluginStatuses()
|
||||
}
|
||||
|
||||
// GetClusterPluginStatuses returns the status for plugins installed anywhere in the cluster.
|
||||
func (a *App) GetClusterPluginStatuses() (model.PluginStatuses, *model.AppError) {
|
||||
return a.ch.getClusterPluginStatuses()
|
||||
return a.ch.srv.pluginService.getClusterPluginStatuses()
|
||||
}
|
||||
|
||||
func (ch *Channels) getClusterPluginStatuses() (model.PluginStatuses, *model.AppError) {
|
||||
pluginStatuses, err := ch.GetPluginStatuses()
|
||||
func (s *PluginService) getClusterPluginStatuses() (model.PluginStatuses, *model.AppError) {
|
||||
pluginStatuses, err := s.GetPluginStatuses()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ch.srv.platform.Cluster() != nil && *ch.cfgSvc.Config().ClusterSettings.Enable {
|
||||
clusterPluginStatuses, err := ch.srv.platform.Cluster().GetPluginStatuses()
|
||||
if s.platform.Cluster() != nil && *s.platform.Config().ClusterSettings.Enable {
|
||||
clusterPluginStatuses, err := s.platform.Cluster().GetPluginStatuses()
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetClusterPluginStatuses", "app.plugin.get_cluster_plugin_statuses.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
@@ -92,8 +92,8 @@ func (ch *Channels) getClusterPluginStatuses() (model.PluginStatuses, *model.App
|
||||
return pluginStatuses, nil
|
||||
}
|
||||
|
||||
func (ch *Channels) notifyPluginStatusesChanged() error {
|
||||
pluginStatuses, err := ch.getClusterPluginStatuses()
|
||||
func (s *PluginService) notifyPluginStatusesChanged() error {
|
||||
pluginStatuses, err := s.getClusterPluginStatuses()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -102,7 +102,7 @@ func (ch *Channels) notifyPluginStatusesChanged() error {
|
||||
message := model.NewWebSocketEvent(model.WebsocketEventPluginStatusesChanged, "", "", "", nil, "")
|
||||
message.Add("plugin_statuses", pluginStatuses)
|
||||
message.GetBroadcast().ContainsSensitiveData = true
|
||||
ch.srv.platform.Publish(message)
|
||||
s.platform.Publish(message)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -346,7 +346,7 @@ func TestServePluginRequest(t *testing.T) {
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("GET", "/plugins/foo/bar", nil)
|
||||
th.App.ch.ServePluginRequest(w, r)
|
||||
th.App.PluginService().ServePluginRequest(w, r)
|
||||
assert.Equal(t, http.StatusNotImplemented, w.Result().StatusCode)
|
||||
}
|
||||
|
||||
@@ -390,7 +390,7 @@ func TestPrivateServePluginRequest(t *testing.T) {
|
||||
|
||||
request = mux.SetURLVars(request, map[string]string{"plugin_id": "id"})
|
||||
|
||||
th.App.ch.servePluginRequest(recorder, request, handler)
|
||||
th.App.PluginService().servePluginRequest(recorder, request, handler)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -413,7 +413,7 @@ func TestHandlePluginRequest(t *testing.T) {
|
||||
var assertions func(*http.Request)
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/plugins/{plugin_id:[A-Za-z0-9\\_\\-\\.]+}/{anything:.*}", func(_ http.ResponseWriter, r *http.Request) {
|
||||
th.App.ch.servePluginRequest(nil, r, func(_ *plugin.Context, _ http.ResponseWriter, r *http.Request) {
|
||||
th.App.PluginService().servePluginRequest(nil, r, func(_ *plugin.Context, _ http.ResponseWriter, r *http.Request) {
|
||||
assertions(r)
|
||||
})
|
||||
})
|
||||
@@ -625,7 +625,7 @@ func TestPluginSync(t *testing.T) {
|
||||
appErr = th.App.DeletePublicKey("pub_key")
|
||||
checkNoError(t, appErr)
|
||||
|
||||
appErr = th.App.ch.RemovePlugin("testplugin")
|
||||
appErr = th.App.PluginService().RemovePlugin("testplugin")
|
||||
checkNoError(t, appErr)
|
||||
})
|
||||
})
|
||||
@@ -642,7 +642,7 @@ func TestChannelsPluginsInit(t *testing.T) {
|
||||
path, _ := fileutils.FindDir("tests")
|
||||
|
||||
require.NotPanics(t, func() {
|
||||
th.Server.Channels().initPlugins(ctx, path, path)
|
||||
th.Server.pluginService.initPlugins(ctx, path, path)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -763,7 +763,7 @@ func TestPluginPanicLogs(t *testing.T) {
|
||||
th.TestLogger.Flush()
|
||||
|
||||
// We shutdown plugins first so that the read on the log buffer is race-free.
|
||||
th.App.ch.ShutDownPlugins()
|
||||
th.App.PluginService().ShutDownPlugins()
|
||||
tearDown()
|
||||
|
||||
testlib.AssertLog(t, th.LogBuffer, mlog.LvlDebug.Name, "panic: some text from panic")
|
||||
@@ -831,7 +831,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, pluginBytes)
|
||||
|
||||
manifest, appErr := th.App.ch.installPluginLocally(bytes.NewReader(pluginBytes), nil, installPluginLocallyAlways)
|
||||
manifest, appErr := th.App.PluginService().installPluginLocally(bytes.NewReader(pluginBytes), nil, installPluginLocallyAlways)
|
||||
require.Nil(t, appErr)
|
||||
require.Equal(t, "testplugin", manifest.Id)
|
||||
|
||||
@@ -848,7 +848,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
|
||||
*cfg.PluginSettings.EnableRemoteMarketplace = false
|
||||
})
|
||||
|
||||
plugins := th.App.ch.processPrepackagedPlugins(prepackagedPluginsDir)
|
||||
plugins := th.App.PluginService().processPrepackagedPlugins(prepackagedPluginsDir)
|
||||
require.Len(t, plugins, 1)
|
||||
require.Equal(t, plugins[0].Manifest.Id, "testplugin")
|
||||
require.Empty(t, plugins[0].Signature, 0)
|
||||
@@ -858,7 +858,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
|
||||
require.Len(t, pluginStatus, 1)
|
||||
require.Equal(t, pluginStatus[0].PluginId, "testplugin")
|
||||
|
||||
appErr = th.App.ch.RemovePlugin("testplugin")
|
||||
appErr = th.App.PluginService().RemovePlugin("testplugin")
|
||||
checkNoError(t, appErr)
|
||||
|
||||
pluginStatus, err = env.Statuses()
|
||||
@@ -875,7 +875,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
|
||||
|
||||
env := th.App.GetPluginsEnvironment()
|
||||
|
||||
plugins := th.App.ch.processPrepackagedPlugins(prepackagedPluginsDir)
|
||||
plugins := th.App.PluginService().processPrepackagedPlugins(prepackagedPluginsDir)
|
||||
require.Len(t, plugins, 1)
|
||||
require.Equal(t, plugins[0].Manifest.Id, "testplugin")
|
||||
require.Empty(t, plugins[0].Signature, 0)
|
||||
@@ -908,7 +908,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
|
||||
err = testlib.CopyFile(testPlugin2SignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz.sig"))
|
||||
require.NoError(t, err)
|
||||
|
||||
plugins := th.App.ch.processPrepackagedPlugins(prepackagedPluginsDir)
|
||||
plugins := th.App.PluginService().processPrepackagedPlugins(prepackagedPluginsDir)
|
||||
require.Len(t, plugins, 2)
|
||||
require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[0].Manifest.Id)
|
||||
require.NotEmpty(t, plugins[0].Signature)
|
||||
@@ -939,7 +939,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, pluginBytes)
|
||||
|
||||
manifest, appErr := th.App.ch.installPluginLocally(bytes.NewReader(pluginBytes), nil, installPluginLocallyAlways)
|
||||
manifest, appErr := th.App.PluginService().installPluginLocally(bytes.NewReader(pluginBytes), nil, installPluginLocallyAlways)
|
||||
require.Nil(t, appErr)
|
||||
require.Equal(t, "testplugin", manifest.Id)
|
||||
|
||||
@@ -957,7 +957,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
|
||||
err = testlib.CopyFile(testPlugin2SignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz.sig"))
|
||||
require.NoError(t, err)
|
||||
|
||||
plugins := th.App.ch.processPrepackagedPlugins(prepackagedPluginsDir)
|
||||
plugins := th.App.PluginService().processPrepackagedPlugins(prepackagedPluginsDir)
|
||||
require.Len(t, plugins, 2)
|
||||
require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[0].Manifest.Id)
|
||||
require.NotEmpty(t, plugins[0].Signature)
|
||||
@@ -969,7 +969,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
|
||||
require.Len(t, pluginStatus, 1)
|
||||
require.Equal(t, pluginStatus[0].PluginId, "testplugin")
|
||||
|
||||
appErr = th.App.ch.RemovePlugin("testplugin")
|
||||
appErr = th.App.PluginService().RemovePlugin("testplugin")
|
||||
checkNoError(t, appErr)
|
||||
|
||||
pluginStatus, err = env.Statuses()
|
||||
@@ -994,7 +994,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
|
||||
err = testlib.CopyFile(testPlugin2SignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz.sig"))
|
||||
require.NoError(t, err)
|
||||
|
||||
plugins := th.App.ch.processPrepackagedPlugins(prepackagedPluginsDir)
|
||||
plugins := th.App.PluginService().processPrepackagedPlugins(prepackagedPluginsDir)
|
||||
require.Len(t, plugins, 2)
|
||||
require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[0].Manifest.Id)
|
||||
require.NotEmpty(t, plugins[0].Signature)
|
||||
@@ -1071,14 +1071,14 @@ func TestGetPluginStateOverride(t *testing.T) {
|
||||
defer th.TearDown()
|
||||
|
||||
t.Run("no override", func(t *testing.T) {
|
||||
overrides, value := th.App.ch.getPluginStateOverride("focalboard")
|
||||
overrides, value := th.App.PluginService().getPluginStateOverride("focalboard")
|
||||
require.False(t, overrides)
|
||||
require.False(t, value)
|
||||
})
|
||||
|
||||
t.Run("calls override", func(t *testing.T) {
|
||||
t.Run("on-prem", func(t *testing.T) {
|
||||
overrides, value := th.App.ch.getPluginStateOverride("com.mattermost.calls")
|
||||
overrides, value := th.App.PluginService().getPluginStateOverride("com.mattermost.calls")
|
||||
require.False(t, overrides)
|
||||
require.False(t, value)
|
||||
})
|
||||
@@ -1086,7 +1086,7 @@ func TestGetPluginStateOverride(t *testing.T) {
|
||||
t.Run("Cloud, without enabled flag", func(t *testing.T) {
|
||||
os.Setenv("MM_CLOUD_INSTALLATION_ID", "test")
|
||||
defer os.Unsetenv("MM_CLOUD_INSTALLATION_ID")
|
||||
overrides, value := th.App.ch.getPluginStateOverride("com.mattermost.calls")
|
||||
overrides, value := th.App.PluginService().getPluginStateOverride("com.mattermost.calls")
|
||||
require.False(t, overrides)
|
||||
require.False(t, value)
|
||||
})
|
||||
@@ -1100,7 +1100,7 @@ func TestGetPluginStateOverride(t *testing.T) {
|
||||
th2 := Setup(t)
|
||||
defer th2.TearDown()
|
||||
|
||||
overrides, value := th2.App.ch.getPluginStateOverride("com.mattermost.calls")
|
||||
overrides, value := th2.App.PluginService().getPluginStateOverride("com.mattermost.calls")
|
||||
require.False(t, overrides)
|
||||
require.False(t, value)
|
||||
})
|
||||
@@ -1114,7 +1114,7 @@ func TestGetPluginStateOverride(t *testing.T) {
|
||||
th2 := Setup(t)
|
||||
defer th2.TearDown()
|
||||
|
||||
overrides, value := th2.App.ch.getPluginStateOverride("com.mattermost.calls")
|
||||
overrides, value := th2.App.PluginService().getPluginStateOverride("com.mattermost.calls")
|
||||
require.True(t, overrides)
|
||||
require.False(t, value)
|
||||
})
|
||||
@@ -1126,7 +1126,7 @@ func TestGetPluginStateOverride(t *testing.T) {
|
||||
th2 := Setup(t)
|
||||
defer th2.TearDown()
|
||||
|
||||
overrides, value := th2.App.ch.getPluginStateOverride("com.mattermost.calls")
|
||||
overrides, value := th2.App.PluginService().getPluginStateOverride("com.mattermost.calls")
|
||||
require.True(t, overrides)
|
||||
require.False(t, value)
|
||||
})
|
||||
@@ -1134,7 +1134,7 @@ func TestGetPluginStateOverride(t *testing.T) {
|
||||
|
||||
t.Run("apps override", func(t *testing.T) {
|
||||
t.Run("without enabled flag", func(t *testing.T) {
|
||||
overrides, value := th.App.ch.getPluginStateOverride("com.mattermost.apps")
|
||||
overrides, value := th.App.PluginService().getPluginStateOverride("com.mattermost.apps")
|
||||
require.False(t, overrides)
|
||||
require.False(t, value)
|
||||
})
|
||||
@@ -1146,7 +1146,7 @@ func TestGetPluginStateOverride(t *testing.T) {
|
||||
th2 := Setup(t)
|
||||
defer th2.TearDown()
|
||||
|
||||
overrides, value := th2.App.ch.getPluginStateOverride("com.mattermost.apps")
|
||||
overrides, value := th2.App.PluginService().getPluginStateOverride("com.mattermost.apps")
|
||||
require.True(t, overrides)
|
||||
require.False(t, value)
|
||||
})
|
||||
|
||||
@@ -269,7 +269,7 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel
|
||||
}
|
||||
var rejectionError *model.AppError
|
||||
pluginContext := pluginContext(c)
|
||||
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
replacementPost, rejectionReason := hooks.MessageWillBePosted(pluginContext, post.ForPlugin())
|
||||
if rejectionReason != "" {
|
||||
id := "Post rejected by plugin. " + rejectionReason
|
||||
@@ -328,7 +328,7 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel
|
||||
// and to remove the non-GOB-encodable Metadata from it.
|
||||
pluginPost := rpost.ForPlugin()
|
||||
a.Srv().Go(func() {
|
||||
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.MessageHasBeenPosted(pluginContext, pluginPost)
|
||||
return true
|
||||
}, plugin.MessageHasBeenPostedID)
|
||||
@@ -655,7 +655,7 @@ func (a *App) UpdatePost(c *request.Context, post *model.Post, safeUpdate bool)
|
||||
|
||||
var rejectionReason string
|
||||
pluginContext := pluginContext(c)
|
||||
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
newPost, rejectionReason = hooks.MessageWillBeUpdated(pluginContext, newPost.ForPlugin(), oldPost.ForPlugin())
|
||||
return post != nil
|
||||
}, plugin.MessageWillBeUpdatedID)
|
||||
@@ -680,7 +680,7 @@ func (a *App) UpdatePost(c *request.Context, post *model.Post, safeUpdate bool)
|
||||
pluginOldPost := oldPost.ForPlugin()
|
||||
pluginNewPost := newPost.ForPlugin()
|
||||
a.Srv().Go(func() {
|
||||
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.MessageHasBeenUpdated(pluginContext, pluginNewPost, pluginOldPost)
|
||||
return true
|
||||
}, plugin.MessageHasBeenUpdatedID)
|
||||
|
||||
@@ -45,7 +45,7 @@ func (a *App) SaveReactionForPost(c *request.Context, reaction *model.Reaction)
|
||||
|
||||
pluginContext := pluginContext(c)
|
||||
a.Srv().Go(func() {
|
||||
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.ReactionHasBeenAdded(pluginContext, reaction)
|
||||
return true
|
||||
}, plugin.ReactionHasBeenAddedID)
|
||||
@@ -142,7 +142,7 @@ func (a *App) DeleteReactionForPost(c *request.Context, reaction *model.Reaction
|
||||
|
||||
pluginContext := pluginContext(c)
|
||||
a.Srv().Go(func() {
|
||||
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.ReactionHasBeenRemoved(pluginContext, reaction)
|
||||
return true
|
||||
}, plugin.ReactionHasBeenRemovedID)
|
||||
|
||||
@@ -119,6 +119,7 @@ type Server struct {
|
||||
telemetryService *telemetry.TelemetryService
|
||||
userService *users.UserService
|
||||
teamService *teams.TeamService
|
||||
pluginService *PluginService
|
||||
|
||||
serviceMux sync.RWMutex
|
||||
remoteClusterService remotecluster.RemoteClusterServiceIFace
|
||||
@@ -718,6 +719,10 @@ func (s *Server) Shutdown() {
|
||||
}
|
||||
}
|
||||
|
||||
// Stop the plugin service, we need to stop plugin service before stopping the
|
||||
// product as products are being consumed by this service.
|
||||
s.pluginService.ShutDownPlugins()
|
||||
|
||||
// Stop products.
|
||||
// This needs to happen last because products are dependent
|
||||
// on parent services.
|
||||
@@ -824,11 +829,18 @@ func stripPort(hostport string) string {
|
||||
func (s *Server) Start() error {
|
||||
// Start products.
|
||||
// This needs to happen before because products are dependent on the HTTP server.
|
||||
|
||||
// make sure channels starts first
|
||||
if err := s.products["channels"].Start(); err != nil {
|
||||
return errors.Wrap(err, "Unable to start channels")
|
||||
}
|
||||
|
||||
// This should actually be started after products, but we have a product hooks
|
||||
// dependency for now, once that get sorted out, this should be moved to the appropriate
|
||||
// order.
|
||||
if err := s.InitializePluginService(); err != nil {
|
||||
return errors.Wrap(err, "Unable to start plugin service")
|
||||
}
|
||||
|
||||
for name, product := range s.products {
|
||||
if name == "channels" {
|
||||
continue
|
||||
|
||||
@@ -853,7 +853,7 @@ func (a *App) JoinUserToTeam(c request.CTX, team *model.Team, user *model.User,
|
||||
|
||||
a.Srv().Go(func() {
|
||||
pluginContext := pluginContext(c)
|
||||
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.UserHasJoinedTeam(pluginContext, teamMember, actor)
|
||||
return true
|
||||
}, plugin.UserHasJoinedTeamID)
|
||||
@@ -1225,7 +1225,7 @@ func (a *App) postProcessTeamMemberLeave(c request.CTX, teamMember *model.TeamMe
|
||||
|
||||
a.Srv().Go(func() {
|
||||
pluginContext := pluginContext(c)
|
||||
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.UserHasLeftTeam(pluginContext, teamMember, actor)
|
||||
return true
|
||||
}, plugin.UserHasLeftTeamID)
|
||||
|
||||
@@ -62,7 +62,7 @@ func (a *App) runPluginsHook(c request.CTX, info *model.FileInfo, file io.Reader
|
||||
var rejErr *model.AppError
|
||||
var once sync.Once
|
||||
pluginContext := pluginContext(c)
|
||||
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
once.Do(func() {
|
||||
hookHasRunCh <- struct{}{}
|
||||
})
|
||||
|
||||
@@ -310,7 +310,7 @@ func (a *App) createUserOrGuest(c request.CTX, user *model.User, guest bool) (*m
|
||||
|
||||
pluginContext := pluginContext(c)
|
||||
a.Srv().Go(func() {
|
||||
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv().RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.UserHasBeenCreated(pluginContext, ruser)
|
||||
return true
|
||||
}, plugin.UserHasBeenCreatedID)
|
||||
|
||||
@@ -16,5 +16,5 @@ func (a *App) PopulateWebConnConfig(s *model.Session, cfg *platform.WebConnConfi
|
||||
|
||||
// NewWebConn returns a new WebConn instance.
|
||||
func (a *App) NewWebConn(cfg *platform.WebConnConfig) *platform.WebConn {
|
||||
return a.Srv().Platform().NewWebConn(cfg, a, a.ch)
|
||||
return a.Srv().Platform().NewWebConn(cfg, a, a.Srv())
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/app"
|
||||
"github.com/mattermost/mattermost-server/v6/app/request"
|
||||
"github.com/mattermost/mattermost-server/v6/config"
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/shared/i18n"
|
||||
@@ -21,7 +20,11 @@ func initDBCommandContextCobra(command *cobra.Command, readOnlyConfigStore bool)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
a.InitPlugins(request.EmptyContext(a.Log()), *a.Config().PluginSettings.Directory, *a.Config().PluginSettings.ClientDirectory)
|
||||
err = a.Srv().InitializePluginService()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a.DoAppMigrations()
|
||||
|
||||
return a, nil
|
||||
|
||||
@@ -282,7 +282,7 @@ func TestPublicFilesRequest(t *testing.T) {
|
||||
defer os.RemoveAll(pluginDir)
|
||||
defer os.RemoveAll(webappPluginDir)
|
||||
|
||||
env, err := plugin.NewEnvironment(th.NewPluginAPI, app.NewDriverImpl(th.Server), pluginDir, webappPluginDir, false, th.App.Log(), nil)
|
||||
env, err := plugin.NewEnvironment(th.NewPluginAPI, app.NewDriverImpl(th.Server.Platform()), pluginDir, webappPluginDir, false, th.App.Log(), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
pluginID := "com.mattermost.sample"
|
||||
@@ -329,7 +329,7 @@ func TestPublicFilesRequest(t *testing.T) {
|
||||
require.NotNil(t, manifest)
|
||||
require.True(t, activated)
|
||||
|
||||
th.App.Channels().SetPluginsEnvironment(env)
|
||||
th.App.PluginService().SetPluginsEnvironment(env)
|
||||
|
||||
req, _ := http.NewRequest("GET", "/plugins/com.mattermost.sample/public/hello.html", nil)
|
||||
res := httptest.NewRecorder()
|
||||
|
||||
Reference in New Issue
Block a user