MM-16261 - Store Plugin in File Store (#11511)

* Implemneted saving the plugin bundle on the file store upon plugin upload

* Fixed compilation error

* Fixed compilation issue

* Added deletion from file store upon plugin uninstall

* Added condition to delete from store only when exists. Added case of saving the bundle to the store when uploading from url. Added checks in plugin tests

* Fixed compilation error

* Moved storage of plugin bundle within app/installPlugin

* Moved storing to filestore before enabling the plugin

* Fixed error handling

* Code styling improvements

* Minor styling fix
Этот коммит содержится в:
Maria A Nunez
2019-07-10 16:05:33 -04:00
коммит произвёл Jesse Hallam
родитель b464f31b38
Коммит 859c571558
4 изменённых файлов: 55 добавлений и 4 удалений

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

@@ -6,6 +6,8 @@
package api4
import (
"bytes"
"io/ioutil"
"net/http"
"net/url"
@@ -124,13 +126,19 @@ func installPluginFromUrl(c *Context, w http.ResponseWriter, r *http.Request) {
if r.URL.Query().Get("force") == "true" {
force = true
}
manifest, unpackErr := c.App.InstallPlugin(resp.Body, force)
fileBytes, readErr := ioutil.ReadAll(resp.Body)
if readErr != nil {
c.Err = model.NewAppError("installPluginFromUrl", "api.plugin.install.reading_stream_failed.app_error", nil, err.Error(), http.StatusBadRequest)
return
}
manifest, unpackErr := c.App.InstallPlugin(bytes.NewReader(fileBytes), force)
if unpackErr != nil {
c.Err = unpackErr
return
}
w.WriteHeader(http.StatusCreated)
w.Write([]byte(manifest.ToJson()))
}

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

@@ -57,6 +57,11 @@ func TestPlugin(t *testing.T) {
CheckNoError(t, resp)
assert.Equal(t, "testplugin", manifest.Id)
// Stored in File Store: Install Plugin from URL case
pluginStored, err := th.App.FileExists("./plugins/" + manifest.Id + ".tar.gz")
assert.Nil(t, err)
assert.True(t, pluginStored)
th.App.RemovePlugin(manifest.Id)
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.PluginSettings.Enable = false })
@@ -89,6 +94,11 @@ func TestPlugin(t *testing.T) {
assert.Equal(t, "testplugin", manifest.Id)
// Stored in File Store: Upload Plugin case
pluginStored, err = th.App.FileExists("./plugins/" + manifest.Id + ".tar.gz")
assert.Nil(t, err)
assert.True(t, pluginStored)
// Upload error cases
_, resp = th.SystemAdminClient.UploadPlugin(bytes.NewReader([]byte("badfile")))
CheckBadRequestStatus(t, resp)

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

@@ -17,11 +17,11 @@ import (
)
// InstallPlugin unpacks and installs a plugin but does not enable or activate it.
func (a *App) InstallPlugin(pluginFile io.Reader, replace bool) (*model.Manifest, *model.AppError) {
func (a *App) InstallPlugin(pluginFile io.ReadSeeker, replace bool) (*model.Manifest, *model.AppError) {
return a.installPlugin(pluginFile, replace)
}
func (a *App) installPlugin(pluginFile io.Reader, replace bool) (*model.Manifest, *model.AppError) {
func (a *App) installPlugin(pluginFile io.ReadSeeker, replace bool) (*model.Manifest, *model.AppError) {
pluginsEnvironment := a.GetPluginsEnvironment()
if pluginsEnvironment == nil {
return nil, model.NewAppError("installPlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
@@ -83,6 +83,14 @@ func (a *App) installPlugin(pluginFile io.Reader, replace bool) (*model.Manifest
return nil, model.NewAppError("installPlugin", "app.plugin.mvdir.app_error", nil, err.Error(), http.StatusInternalServerError)
}
// Store bundle in the file store to allow access from other servers.
pluginFile.Seek(0, 0)
storePluginFileName := filepath.Join("./plugins", manifest.Id) + ".tar.gz"
if _, err := a.WriteFile(pluginFile, storePluginFileName); err != nil {
return nil, model.NewAppError("uploadPlugin", "app.plugin.store_bundle.app_error", nil, err.Error(), http.StatusInternalServerError)
}
if stashed != nil && stashed.Enable {
a.EnablePlugin(manifest.Id)
}
@@ -138,5 +146,18 @@ func (a *App) removePlugin(id string) *model.AppError {
return model.NewAppError("removePlugin", "app.plugin.remove.app_error", nil, err.Error(), http.StatusInternalServerError)
}
// Remove bundle from the file store.
storePluginFileName := filepath.Join("./plugins", manifest.Id) + ".tar.gz"
bundleExist, fileErr := a.FileExists(storePluginFileName)
if fileErr != nil {
return model.NewAppError("removePlugin", "app.plugin.remove_bundle.app_error", nil, err.Error(), http.StatusInternalServerError)
}
if bundleExist {
if err := a.RemoveFile(storePluginFileName); err != nil {
return model.NewAppError("removePlugin", "app.plugin.remove_bundle.app_error", nil, err.Error(), http.StatusInternalServerError)
}
}
return nil
}

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

@@ -1512,6 +1512,10 @@
"id": "api.plugin.install.invalid_url.app_error",
"translation": "An invalid url was given to download the plugin."
},
{
"id": "api.plugin.install.reading_stream_failed.app_error",
"translation": "An error ocurred reading the plugin file stream."
},
{
"id": "api.plugin.upload.array.app_error",
"translation": "File array is empty in multipart/form request"
@@ -3430,6 +3434,14 @@
"id": "app.plugin.remove.app_error",
"translation": "Unable to delete plugin"
},
{
"id": "app.plugin.remove_bundle.app_error",
"translation": "Unable to remove plugin bundle from file store."
},
{
"id": "app.plugin.store_bundle.app_error",
"translation": "Unable to store the plugin to the configured file store."
},
{
"id": "app.plugin.upload_disabled.app_error",
"translation": "Plugins and/or plugin uploads have been disabled."