MM-53355: install transitionally prepackaged plugins to filestore (#24225)
* move plugin signature verification to caller The semantics for when plugin signature validation is required are unique to the caller, so move this logic there instead of masking it, thus simplifying some of the downstream code. * support transitionally prepacked plugins Transitionally prepackaged plugins are prepackaged plugins slated for unpackaging in some future release. Like prepackaged plugins, they automatically install or upgrade if the server is configured to enable that plugin, but unlike prepackaged plugins they don't add to the marketplace to allow for offline installs. In fact, if unlisted from the marketplace and not already enabled via `config.json`, a transitionally prepackaged plugin is essentially hidden. To ensure a smooth transition in the future release when this plugin is no longer prepackaged at all, transitionally prepackaged plugins are persisted to the filestore as if they had been installed by the enduser. On the next restart, even while the plugin is still transitionally prepackaged, the version in the filestore will take priority. It remains possible for a transitionally prepackaged plugin to upgrade (and once again persist) if we ship a newer version before dropping it altogether. Some complexity arises in a multi-server cluster, primarily because we don't want to deal with multiple servers writing the same object to the filestore. This is probably fine for S3, but has undefined semantics for regular filesystems, especially with some customers backing their files on any number of different fileshare technologies. To simplify the complexity, only the cluster leader persists transitionally prepackaged plugins. Unfortunately, this too is complicated, since on upgrade to the first version with the transitionally prepackaged plugin, there is no guarantee that server will be the leader. In fact, as all nodes restart, there is no guarantee that any newly started server will start as the leader. So the persistence has to happen in a job-like fashion. The migration system might work, except we want the ability to run this repeatedly as we add to (or update) these transitionally prepackaged plugins. We also want to minimize the overhead required from the server to juggle any of this. As a consequence, the persistence of transitionally prepackaged plugins occurs on every cluster leader change. Each server will try at most once to persist its collection of transitionally prepackaged plugins, and newly started servers will see the plugins in the filestore and skip this step altogether. The current set of transitionally prepackaged plugins include the following, but this is expected to change: * focalboard * complete list of transitionally prepackaged plugins * update plugin_install.go docs * updated test plugins * unit test transitionally prepackged plugins * try restoring original working directory * Apply suggestions from code review Co-authored-by: Michael Kochell <6913320+mickmister@users.noreply.github.com> * clarify processPrepackagedPlugins comment --------- Co-authored-by: Michael Kochell <6913320+mickmister@users.noreply.github.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
7db5b473bb
Коммит
ad142c958e
@@ -48,16 +48,17 @@ type PrepackagedPlugin struct {
|
||||
// It is meant for use by the Mattermost server to manipulate, interact with and report on the set
|
||||
// of active plugins.
|
||||
type Environment struct {
|
||||
registeredPlugins sync.Map
|
||||
pluginHealthCheckJob *PluginHealthCheckJob
|
||||
logger *mlog.Logger
|
||||
metrics metricsInterface
|
||||
newAPIImpl apiImplCreatorFunc
|
||||
dbDriver Driver
|
||||
pluginDir string
|
||||
webappPluginDir string
|
||||
prepackagedPlugins []*PrepackagedPlugin
|
||||
prepackagedPluginsLock sync.RWMutex
|
||||
registeredPlugins sync.Map
|
||||
pluginHealthCheckJob *PluginHealthCheckJob
|
||||
logger *mlog.Logger
|
||||
metrics metricsInterface
|
||||
newAPIImpl apiImplCreatorFunc
|
||||
dbDriver Driver
|
||||
pluginDir string
|
||||
webappPluginDir string
|
||||
prepackagedPlugins []*PrepackagedPlugin
|
||||
transitionallyPrepackagedPlugins []*PrepackagedPlugin
|
||||
prepackagedPluginsLock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewEnvironment(
|
||||
@@ -108,7 +109,9 @@ func (env *Environment) Available() ([]*model.BundleInfo, error) {
|
||||
return scanSearchPath(env.pluginDir)
|
||||
}
|
||||
|
||||
// Returns a list of prepackaged plugins available in the local prepackaged_plugins folder.
|
||||
// Returns a list of prepackaged plugins available in the local prepackaged_plugins folder,
|
||||
// excluding those in transition out of being prepackaged.
|
||||
//
|
||||
// The list content is immutable and should not be modified.
|
||||
func (env *Environment) PrepackagedPlugins() []*PrepackagedPlugin {
|
||||
env.prepackagedPluginsLock.RLock()
|
||||
@@ -117,6 +120,26 @@ func (env *Environment) PrepackagedPlugins() []*PrepackagedPlugin {
|
||||
return env.prepackagedPlugins
|
||||
}
|
||||
|
||||
// TransitionallyPrepackagedPlugins returns a list of plugins transitionally prepackaged in the
|
||||
// local prepackaged_plugins folder.
|
||||
//
|
||||
// The list content is immutable and should not be modified.
|
||||
func (env *Environment) TransitionallyPrepackagedPlugins() []*PrepackagedPlugin {
|
||||
env.prepackagedPluginsLock.RLock()
|
||||
defer env.prepackagedPluginsLock.RUnlock()
|
||||
|
||||
return env.transitionallyPrepackagedPlugins
|
||||
}
|
||||
|
||||
// ClearTransitionallyPrepackagedPlugins clears the list of plugins transitionally prepackaged
|
||||
// in the local prepackaged_plugins folder.
|
||||
func (env *Environment) ClearTransitionallyPrepackagedPlugins() {
|
||||
env.prepackagedPluginsLock.RLock()
|
||||
defer env.prepackagedPluginsLock.RUnlock()
|
||||
|
||||
env.transitionallyPrepackagedPlugins = nil
|
||||
}
|
||||
|
||||
// Returns a list of all currently active plugins within the environment.
|
||||
// The returned list should not be modified.
|
||||
func (env *Environment) Active() []*model.BundleInfo {
|
||||
@@ -532,9 +555,10 @@ func (env *Environment) PerformHealthCheck(id string) error {
|
||||
}
|
||||
|
||||
// SetPrepackagedPlugins saves prepackaged plugins in the environment.
|
||||
func (env *Environment) SetPrepackagedPlugins(plugins []*PrepackagedPlugin) {
|
||||
func (env *Environment) SetPrepackagedPlugins(plugins, transitionalPlugins []*PrepackagedPlugin) {
|
||||
env.prepackagedPluginsLock.Lock()
|
||||
env.prepackagedPlugins = plugins
|
||||
env.transitionallyPrepackagedPlugins = transitionalPlugins
|
||||
env.prepackagedPluginsLock.Unlock()
|
||||
}
|
||||
|
||||
|
||||
Ссылка в новой задаче
Block a user