MM-17087 - Disable plugin on removal (#11779)
* MM-17087 - Disable plugin on removal * Updated documentation * Got reid of notifyPluginEvents * Updated documentation * Added plugin installation/activatoin flow as a toplevel go doc in plugin_install.go * Generating webapp bundle on plugin installation * Fixed shadowing issue * Updated doc to include unguarded race condition * Renamed GenerateWebappBundle * Added a debug log when peers are not ready to notify * Updated docs * Removed extra line
Этот коммит содержится в:
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/mattermost/mattermost-server/plugin"
|
||||
"github.com/mattermost/mattermost-server/services/filesstore"
|
||||
"github.com/mattermost/mattermost-server/utils/fileutils"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// GetPluginsEnvironment returns the plugin environment for use if plugins are enabled and
|
||||
@@ -99,10 +100,11 @@ func (a *App) SyncPluginsActiveState() {
|
||||
continue
|
||||
}
|
||||
|
||||
if activated && updatedManifest.HasClient() {
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PLUGIN_ENABLED, "", "", "", nil)
|
||||
message.Add("manifest", updatedManifest.ClientManifest())
|
||||
a.Publish(message)
|
||||
if activated {
|
||||
// Notify all cluster clients if ready
|
||||
if err := a.notifyPluginEnabled(updatedManifest); err != nil {
|
||||
a.Log.Error("Failed to notify cluster on plugin enable", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -287,6 +289,7 @@ func (a *App) GetActivePluginManifests() ([]*model.Manifest, *model.AppError) {
|
||||
|
||||
// EnablePlugin will set the config for an installed plugin to enabled, triggering asynchronous
|
||||
// activation if inactive anywhere in the cluster.
|
||||
// Notifies cluster peers through config change.
|
||||
func (a *App) EnablePlugin(id string) *model.AppError {
|
||||
pluginsEnvironment := a.GetPluginsEnvironment()
|
||||
if pluginsEnvironment == nil {
|
||||
@@ -316,7 +319,7 @@ func (a *App) EnablePlugin(id string) *model.AppError {
|
||||
cfg.PluginSettings.PluginStates[id] = &model.PluginState{Enable: true}
|
||||
})
|
||||
|
||||
// This call will cause SyncPluginsActiveState to be called and the plugin to be activated
|
||||
// This call will implicitly invoke SyncPluginsActiveState which will activate enabled plugins.
|
||||
if err := a.SaveConfig(a.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)
|
||||
@@ -328,6 +331,7 @@ func (a *App) 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 {
|
||||
pluginsEnvironment := a.GetPluginsEnvironment()
|
||||
if pluginsEnvironment == nil {
|
||||
@@ -358,6 +362,7 @@ func (a *App) DisablePlugin(id string) *model.AppError {
|
||||
})
|
||||
a.UnregisterPluginCommands(id)
|
||||
|
||||
// This call will implicitly invoke SyncPluginsActiveState which will deactivate disabled plugins.
|
||||
if err := a.SaveConfig(a.Config(), true); err != nil {
|
||||
return model.NewAppError("DisablePlugin", "app.plugin.config.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
@@ -394,3 +399,54 @@ func (a *App) GetPlugins() (*model.PluginsResponse, *model.AppError) {
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// notifyPluginEnabled notifies connected websocket clients across all peers if the version of the given
|
||||
// plugin is same across them.
|
||||
//
|
||||
// When a peer finds itself in agreement with all other peers as to the version of the given plugin,
|
||||
// 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 (a *App) notifyPluginEnabled(manifest *model.Manifest) error {
|
||||
pluginsEnvironment := a.GetPluginsEnvironment()
|
||||
if pluginsEnvironment == nil {
|
||||
return errors.New("pluginsEnvironment is nil")
|
||||
}
|
||||
if !manifest.HasClient() || !pluginsEnvironment.IsActive(manifest.Id) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var statuses model.PluginStatuses
|
||||
|
||||
if a.Cluster != nil {
|
||||
var err *model.AppError
|
||||
statuses, err = a.Cluster.GetPluginStatuses()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
localStatus, err := a.GetPluginStatus(manifest.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
statuses = append(statuses, localStatus)
|
||||
|
||||
// This will not guard against the race condition of enabling a plugin immediately after installation.
|
||||
// As GetPluginStatuses() will not return the new plugin (since other peers are racing to install),
|
||||
// this peer will end up checking status against itself and will notify all webclients (including peer webclients),
|
||||
// which may result in a 404.
|
||||
for _, status := range statuses {
|
||||
if status.PluginId == manifest.Id && status.Version != manifest.Version {
|
||||
mlog.Debug("Not ready to notify webclients", mlog.String("cluster_id", status.ClusterId), mlog.String("plugin_id", manifest.Id))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Notify all cluster peer clients.
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PLUGIN_ENABLED, "", "", "", nil)
|
||||
message.Add("manifest", manifest.ClientManifest())
|
||||
a.Publish(message)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user