MM-19606- Rework Prepackaged Plugins (#13449)
* MM-19609 - Add new prepackage configuration settings (#13062) * Add signatures to the prepackaged plugins (#13138) * MM-19612 - Support querying local plugin marketplace when upst… (#13250) * MM-19612 - Support querying local plugin marketplace when upstream unavailable or disabled * Update translations file * Fixed comment * Updated to check EnableRemoteMarketplace setting and LocalOnly to get marketplace plugins * Fixed unit tests * Tests cleanup code * Removed unused error message * Updated tests * MM-19614- Updated Marketplace Service error id (#13388) * [MM-19610] Consume prepackaged plugins (#13005) * consume prepackaged plugins into memory * missing i18n * remove spurious .gitignore changes * return on failure to install prepackged plugins * cleanup * s/plugins/availablePlugins * whitespace * don't return extractDir when not needed * s/plug/plugin * error on icon, cleanup * update armored version of testplugin signature * honour AutomaticPrepackagedPlugins * document getPrepackagedPlugin * MM-19613 - Include prepackaged plugins in marketplace results (#13433) * Added prepackaged plugins to marketplace results * PR Feedback * PR Feedback * Update error where definition * Removing unnecessary var declaration * Updated comments * MM-21263 - Use EnableRemoteMarketplace in marketplace install… (#13438) * MM-21263 - Use EnableRemoteMarketplace in marketplace install endpoint * Call updateConfig before calling NewServer in TestHelper * Added translations * PR feedback * Translations * Feedback * s/helpers.go/download.go * Converging env.PrepackagedPlugins * Initial PR feedback * Ordered imports properly * Updated DownloadURL to return slice of bytes * Fixed method typo * Fixed logging * Added read lock for prepackaged plugins list * PR Feedback * Added condition to only install prepackaged plugin if it was previously enabled * Linting * Updated to check plugin state in config * Closing filereader * Only add local label if remote marketplace is enabled * Updated local tag description * Fixed tests Co-authored-by: Ali Farooq <ali.farooq0@pm.me> Co-authored-by: Shota Gvinepadze <wineson@gmail.com> Co-authored-by: Jesse Hallam <jesse.hallam@gmail.com> Co-authored-by: Ben Schumacher <ben.schumacher@mattermost.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
508fbf2f53
Коммит
87eb7697f9
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
"github.com/mattermost/mattermost-server/v5/model"
|
||||
"github.com/mattermost/mattermost-server/v5/plugin"
|
||||
"github.com/mattermost/mattermost-server/v5/utils"
|
||||
"github.com/mattermost/mattermost-server/v5/utils/fileutils"
|
||||
)
|
||||
|
||||
@@ -485,108 +486,316 @@ func TestPluginSync(t *testing.T) {
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.Description, func(t *testing.T) {
|
||||
os.MkdirAll("./test-plugins", os.ModePerm)
|
||||
defer os.RemoveAll("./test-plugins")
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.PluginSettings.Enable = true
|
||||
*cfg.PluginSettings.Directory = "./test-plugins"
|
||||
*cfg.PluginSettings.ClientDirectory = "./test-client-plugins"
|
||||
*cfg.PluginSettings.RequirePluginSignature = false
|
||||
testCase.ConfigFunc(cfg)
|
||||
})
|
||||
th.App.UpdateConfig(testCase.ConfigFunc)
|
||||
|
||||
env, err := plugin.NewEnvironment(th.App.NewPluginAPI, "./test-plugins", "./test-client-plugins", th.App.Log)
|
||||
require.NoError(t, err)
|
||||
th.App.SetPluginsEnvironment(env)
|
||||
env := th.App.GetPluginsEnvironment()
|
||||
require.NotNil(t, env)
|
||||
|
||||
// New bundle in the file store case
|
||||
path, _ := fileutils.FindDir("tests")
|
||||
fileReader, err := os.Open(filepath.Join(path, "testplugin.tar.gz"))
|
||||
require.NoError(t, err)
|
||||
defer fileReader.Close()
|
||||
|
||||
_, appErr := th.App.WriteFile(fileReader, th.App.getBundleStorePath("testplugin"))
|
||||
checkNoError(t, appErr)
|
||||
t.Run("new bundle in the file store", func(t *testing.T) {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.PluginSettings.RequirePluginSignature = false
|
||||
})
|
||||
|
||||
appErr = th.App.SyncPlugins()
|
||||
checkNoError(t, appErr)
|
||||
fileReader, err := os.Open(filepath.Join(path, "testplugin.tar.gz"))
|
||||
require.NoError(t, err)
|
||||
defer fileReader.Close()
|
||||
|
||||
// Check if installed
|
||||
pluginStatus, err := env.Statuses()
|
||||
require.Nil(t, err)
|
||||
require.Len(t, pluginStatus, 1)
|
||||
require.Equal(t, pluginStatus[0].PluginId, "testplugin")
|
||||
_, appErr := th.App.WriteFile(fileReader, th.App.getBundleStorePath("testplugin"))
|
||||
checkNoError(t, appErr)
|
||||
|
||||
// Bundle removed from the file store case
|
||||
appErr = th.App.RemoveFile(th.App.getBundleStorePath("testplugin"))
|
||||
checkNoError(t, appErr)
|
||||
appErr = th.App.SyncPlugins()
|
||||
checkNoError(t, appErr)
|
||||
|
||||
appErr = th.App.SyncPlugins()
|
||||
checkNoError(t, appErr)
|
||||
|
||||
// Check if removed
|
||||
pluginStatus, err = env.Statuses()
|
||||
require.Nil(t, err)
|
||||
require.Empty(t, pluginStatus)
|
||||
|
||||
// RequirePluginSignature = true case
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.PluginSettings.RequirePluginSignature = true
|
||||
// Check if installed
|
||||
pluginStatus, err := env.Statuses()
|
||||
require.Nil(t, err)
|
||||
require.Len(t, pluginStatus, 1)
|
||||
require.Equal(t, pluginStatus[0].PluginId, "testplugin")
|
||||
})
|
||||
pluginFileReader, err := os.Open(filepath.Join(path, "testplugin.tar.gz"))
|
||||
require.NoError(t, err)
|
||||
defer pluginFileReader.Close()
|
||||
_, appErr = th.App.WriteFile(pluginFileReader, th.App.getBundleStorePath("testplugin"))
|
||||
checkNoError(t, appErr)
|
||||
// no signature
|
||||
appErr = th.App.SyncPlugins()
|
||||
checkNoError(t, appErr)
|
||||
pluginStatus, err = env.Statuses()
|
||||
require.Nil(t, err)
|
||||
require.Empty(t, pluginStatus)
|
||||
|
||||
// Wrong signature
|
||||
signatureFileReader, err := os.Open(filepath.Join(path, "testpluginv2.tar.gz.sig"))
|
||||
require.NoError(t, err)
|
||||
defer signatureFileReader.Close()
|
||||
filePath := fmt.Sprintf("%s.sig", th.App.getBundleStorePath("testplugin"))
|
||||
_, appErr = th.App.WriteFile(signatureFileReader, filePath)
|
||||
checkNoError(t, appErr)
|
||||
t.Run("bundle removed from the file store", func(t *testing.T) {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.PluginSettings.RequirePluginSignature = false
|
||||
})
|
||||
|
||||
appErr = th.App.SyncPlugins()
|
||||
checkNoError(t, appErr)
|
||||
appErr := th.App.RemoveFile(th.App.getBundleStorePath("testplugin"))
|
||||
checkNoError(t, appErr)
|
||||
|
||||
pluginStatus, err = env.Statuses()
|
||||
require.Nil(t, err)
|
||||
require.Empty(t, pluginStatus)
|
||||
appErr = th.App.SyncPlugins()
|
||||
checkNoError(t, appErr)
|
||||
|
||||
// Correct signature
|
||||
key, err := os.Open(filepath.Join(path, "development-private-key.asc"))
|
||||
require.NoError(t, err)
|
||||
appErr = th.App.AddPublicKey("pub_key", key)
|
||||
checkNoError(t, appErr)
|
||||
// Check if removed
|
||||
pluginStatus, err := env.Statuses()
|
||||
require.Nil(t, err)
|
||||
require.Empty(t, pluginStatus)
|
||||
})
|
||||
|
||||
signatureFileReader, err = os.Open(filepath.Join(path, "testplugin.tar.gz.sig"))
|
||||
require.NoError(t, err)
|
||||
defer signatureFileReader.Close()
|
||||
filePath = fmt.Sprintf("%s.sig", th.App.getBundleStorePath("testplugin"))
|
||||
_, appErr = th.App.WriteFile(signatureFileReader, filePath)
|
||||
checkNoError(t, appErr)
|
||||
t.Run("plugin signatures required, no signature", func(t *testing.T) {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.PluginSettings.RequirePluginSignature = true
|
||||
})
|
||||
|
||||
appErr = th.App.SyncPlugins()
|
||||
checkNoError(t, appErr)
|
||||
pluginFileReader, err := os.Open(filepath.Join(path, "testplugin.tar.gz"))
|
||||
require.NoError(t, err)
|
||||
defer pluginFileReader.Close()
|
||||
_, appErr := th.App.WriteFile(pluginFileReader, th.App.getBundleStorePath("testplugin"))
|
||||
checkNoError(t, appErr)
|
||||
|
||||
pluginStatus, err = env.Statuses()
|
||||
require.Nil(t, err)
|
||||
require.Len(t, pluginStatus, 1)
|
||||
require.Equal(t, pluginStatus[0].PluginId, "testplugin")
|
||||
appErr = th.App.SyncPlugins()
|
||||
checkNoError(t, appErr)
|
||||
pluginStatus, err := env.Statuses()
|
||||
require.Nil(t, err)
|
||||
require.Len(t, pluginStatus, 0)
|
||||
})
|
||||
|
||||
appErr = th.App.DeletePublicKey("pub_key")
|
||||
checkNoError(t, appErr)
|
||||
t.Run("plugin signatures required, wrong signature", func(t *testing.T) {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.PluginSettings.RequirePluginSignature = true
|
||||
})
|
||||
|
||||
appErr = th.App.RemovePlugin("testplugin")
|
||||
checkNoError(t, appErr)
|
||||
signatureFileReader, err := os.Open(filepath.Join(path, "testplugin2.tar.gz.sig"))
|
||||
require.NoError(t, err)
|
||||
defer signatureFileReader.Close()
|
||||
_, appErr := th.App.WriteFile(signatureFileReader, th.App.getSignatureStorePath("testplugin"))
|
||||
checkNoError(t, appErr)
|
||||
|
||||
appErr = th.App.SyncPlugins()
|
||||
checkNoError(t, appErr)
|
||||
|
||||
pluginStatus, err := env.Statuses()
|
||||
require.Nil(t, err)
|
||||
require.Len(t, pluginStatus, 0)
|
||||
})
|
||||
|
||||
t.Run("plugin signatures required, correct signature", func(t *testing.T) {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.PluginSettings.RequirePluginSignature = true
|
||||
})
|
||||
|
||||
key, err := os.Open(filepath.Join(path, "development-private-key.asc"))
|
||||
require.NoError(t, err)
|
||||
appErr := th.App.AddPublicKey("pub_key", key)
|
||||
checkNoError(t, appErr)
|
||||
|
||||
signatureFileReader, err := os.Open(filepath.Join(path, "testplugin.tar.gz.sig"))
|
||||
require.NoError(t, err)
|
||||
defer signatureFileReader.Close()
|
||||
_, appErr = th.App.WriteFile(signatureFileReader, th.App.getSignatureStorePath("testplugin"))
|
||||
checkNoError(t, appErr)
|
||||
|
||||
appErr = th.App.SyncPlugins()
|
||||
checkNoError(t, appErr)
|
||||
|
||||
pluginStatus, err := env.Statuses()
|
||||
require.Nil(t, err)
|
||||
require.Len(t, pluginStatus, 1)
|
||||
require.Equal(t, pluginStatus[0].PluginId, "testplugin")
|
||||
|
||||
appErr = th.App.DeletePublicKey("pub_key")
|
||||
checkNoError(t, appErr)
|
||||
|
||||
appErr = th.App.RemovePlugin("testplugin")
|
||||
checkNoError(t, appErr)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessPrepackagedPlugins(t *testing.T) {
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
testsPath, _ := fileutils.FindDir("tests")
|
||||
prepackagedPluginsPath := filepath.Join(testsPath, prepackagedPluginsDir)
|
||||
fileErr := os.Mkdir(prepackagedPluginsPath, os.ModePerm)
|
||||
require.NoError(t, fileErr)
|
||||
defer os.RemoveAll(prepackagedPluginsPath)
|
||||
|
||||
prepackagedPluginsDir, found := fileutils.FindDir(prepackagedPluginsPath)
|
||||
require.True(t, found, "failed to find prepackaged plugins directory")
|
||||
|
||||
testPluginPath := filepath.Join(testsPath, "testplugin.tar.gz")
|
||||
fileErr = utils.CopyFile(testPluginPath, filepath.Join(prepackagedPluginsDir, "testplugin.tar.gz"))
|
||||
require.NoError(t, fileErr)
|
||||
|
||||
t.Run("automatic, enabled plugin, no signature", func(t *testing.T) {
|
||||
// Install the plugin and enable
|
||||
pluginBytes, err := ioutil.ReadFile(testPluginPath)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, pluginBytes)
|
||||
|
||||
manifest, appErr := th.App.installPluginLocally(bytes.NewReader(pluginBytes), nil, installPluginLocallyAlways)
|
||||
require.Nil(t, appErr)
|
||||
require.Equal(t, "testplugin", manifest.Id)
|
||||
|
||||
env := th.App.GetPluginsEnvironment()
|
||||
|
||||
activatedManifest, activated, err := env.Activate(manifest.Id)
|
||||
require.NoError(t, err)
|
||||
require.True(t, activated)
|
||||
require.Equal(t, manifest, activatedManifest)
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.PluginSettings.Enable = true
|
||||
*cfg.PluginSettings.AutomaticPrepackagedPlugins = true
|
||||
})
|
||||
|
||||
plugins := th.App.processPrepackagedPlugins(prepackagedPluginsDir)
|
||||
require.Len(t, plugins, 1)
|
||||
require.Equal(t, plugins[0].Manifest.Id, "testplugin")
|
||||
require.Empty(t, plugins[0].Signature, 0)
|
||||
|
||||
pluginStatus, err := env.Statuses()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, pluginStatus, 1)
|
||||
require.Equal(t, pluginStatus[0].PluginId, "testplugin")
|
||||
|
||||
appErr = th.App.RemovePlugin("testplugin")
|
||||
checkNoError(t, appErr)
|
||||
|
||||
pluginStatus, err = env.Statuses()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, pluginStatus, 0)
|
||||
})
|
||||
|
||||
t.Run("automatic, not enabled plugin", func(t *testing.T) {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.PluginSettings.Enable = true
|
||||
*cfg.PluginSettings.AutomaticPrepackagedPlugins = true
|
||||
})
|
||||
|
||||
env := th.App.GetPluginsEnvironment()
|
||||
|
||||
plugins := th.App.processPrepackagedPlugins(prepackagedPluginsDir)
|
||||
require.Len(t, plugins, 1)
|
||||
require.Equal(t, plugins[0].Manifest.Id, "testplugin")
|
||||
require.Empty(t, plugins[0].Signature, 0)
|
||||
|
||||
pluginStatus, err := env.Statuses()
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, pluginStatus, 0)
|
||||
})
|
||||
|
||||
t.Run("automatic, multiple plugins with signatures, not enabled", func(t *testing.T) {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.PluginSettings.Enable = true
|
||||
*cfg.PluginSettings.AutomaticPrepackagedPlugins = true
|
||||
})
|
||||
|
||||
env := th.App.GetPluginsEnvironment()
|
||||
|
||||
// Add signature
|
||||
testPluginSignaturePath := filepath.Join(testsPath, "testplugin.tar.gz.sig")
|
||||
err := utils.CopyFile(testPluginSignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin.tar.gz.sig"))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Add second plugin
|
||||
testPlugin2Path := filepath.Join(testsPath, "testplugin2.tar.gz")
|
||||
err = utils.CopyFile(testPlugin2Path, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz"))
|
||||
require.NoError(t, err)
|
||||
|
||||
testPlugin2SignaturePath := filepath.Join(testsPath, "testplugin2.tar.gz.sig")
|
||||
err = utils.CopyFile(testPlugin2SignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz.sig"))
|
||||
require.NoError(t, err)
|
||||
|
||||
plugins := th.App.processPrepackagedPlugins(prepackagedPluginsDir)
|
||||
require.Len(t, plugins, 2)
|
||||
require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[0].Manifest.Id)
|
||||
require.NotEmpty(t, plugins[0].Signature)
|
||||
require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[1].Manifest.Id)
|
||||
require.NotEmpty(t, plugins[1].Signature)
|
||||
|
||||
pluginStatus, err := env.Statuses()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, pluginStatus, 0)
|
||||
})
|
||||
|
||||
t.Run("automatic, multiple plugins with signatures, one enabled", func(t *testing.T) {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.PluginSettings.Enable = true
|
||||
*cfg.PluginSettings.AutomaticPrepackagedPlugins = true
|
||||
})
|
||||
|
||||
env := th.App.GetPluginsEnvironment()
|
||||
|
||||
// Add signature
|
||||
testPluginSignaturePath := filepath.Join(testsPath, "testplugin.tar.gz.sig")
|
||||
err := utils.CopyFile(testPluginSignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin.tar.gz.sig"))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Install first plugin and enable
|
||||
pluginBytes, err := ioutil.ReadFile(testPluginPath)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, pluginBytes)
|
||||
|
||||
manifest, appErr := th.App.installPluginLocally(bytes.NewReader(pluginBytes), nil, installPluginLocallyAlways)
|
||||
require.Nil(t, appErr)
|
||||
require.Equal(t, "testplugin", manifest.Id)
|
||||
|
||||
activatedManifest, activated, err := env.Activate(manifest.Id)
|
||||
require.NoError(t, err)
|
||||
require.True(t, activated)
|
||||
require.Equal(t, manifest, activatedManifest)
|
||||
|
||||
// Add second plugin
|
||||
testPlugin2Path := filepath.Join(testsPath, "testplugin2.tar.gz")
|
||||
err = utils.CopyFile(testPlugin2Path, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz"))
|
||||
require.NoError(t, err)
|
||||
|
||||
testPlugin2SignaturePath := filepath.Join(testsPath, "testplugin2.tar.gz.sig")
|
||||
err = utils.CopyFile(testPlugin2SignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz.sig"))
|
||||
require.NoError(t, err)
|
||||
|
||||
plugins := th.App.processPrepackagedPlugins(prepackagedPluginsDir)
|
||||
require.Len(t, plugins, 2)
|
||||
require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[0].Manifest.Id)
|
||||
require.NotEmpty(t, plugins[0].Signature)
|
||||
require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[1].Manifest.Id)
|
||||
require.NotEmpty(t, plugins[1].Signature)
|
||||
|
||||
pluginStatus, err := env.Statuses()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, pluginStatus, 1)
|
||||
require.Equal(t, pluginStatus[0].PluginId, "testplugin")
|
||||
|
||||
appErr = th.App.RemovePlugin("testplugin")
|
||||
checkNoError(t, appErr)
|
||||
|
||||
pluginStatus, err = env.Statuses()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, pluginStatus, 0)
|
||||
})
|
||||
|
||||
t.Run("non-automatic, multiple plugins", func(t *testing.T) {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.PluginSettings.Enable = true
|
||||
*cfg.PluginSettings.AutomaticPrepackagedPlugins = false
|
||||
})
|
||||
|
||||
env := th.App.GetPluginsEnvironment()
|
||||
|
||||
testPlugin2Path := filepath.Join(testsPath, "testplugin2.tar.gz")
|
||||
err := utils.CopyFile(testPlugin2Path, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz"))
|
||||
require.NoError(t, err)
|
||||
|
||||
testPlugin2SignaturePath := filepath.Join(testsPath, "testplugin2.tar.gz.sig")
|
||||
err = utils.CopyFile(testPlugin2SignaturePath, filepath.Join(prepackagedPluginsDir, "testplugin2.tar.gz.sig"))
|
||||
require.NoError(t, err)
|
||||
|
||||
plugins := th.App.processPrepackagedPlugins(prepackagedPluginsDir)
|
||||
require.Len(t, plugins, 2)
|
||||
require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[0].Manifest.Id)
|
||||
require.NotEmpty(t, plugins[0].Signature)
|
||||
require.Contains(t, []string{"testplugin", "testplugin2"}, plugins[1].Manifest.Id)
|
||||
require.NotEmpty(t, plugins[1].Signature)
|
||||
|
||||
pluginStatus, err := env.Statuses()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, pluginStatus, 0)
|
||||
})
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user