Merge branch 'master' into mark-as-unread
Этот коммит содержится в:
@@ -10,6 +10,7 @@ import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/mlog"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
@@ -17,6 +18,9 @@ import (
|
||||
|
||||
const (
|
||||
MAXIMUM_PLUGIN_FILE_SIZE = 50 * 1024 * 1024
|
||||
// INSTALL_PLUGIN_FROM_URL_HTTP_REQUEST_TIMEOUT defines a high timeout for installing plugins
|
||||
// from an external URL to avoid slow connections or large plugins from failing to install.
|
||||
INSTALL_PLUGIN_FROM_URL_HTTP_REQUEST_TIMEOUT = 60 * time.Minute
|
||||
)
|
||||
|
||||
func (api *API) InitPlugin() {
|
||||
@@ -115,6 +119,8 @@ func installPluginFromUrl(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
client := c.App.HTTPService.MakeClient(true)
|
||||
client.Timeout = INSTALL_PLUGIN_FROM_URL_HTTP_REQUEST_TIMEOUT
|
||||
|
||||
resp, err := client.Get(downloadUrl)
|
||||
if err != nil {
|
||||
c.Err = model.NewAppError("installPluginFromUrl", "api.plugin.install.download_failed.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/testlib"
|
||||
@@ -59,6 +60,24 @@ func TestPlugin(t *testing.T) {
|
||||
CheckNoError(t, resp)
|
||||
assert.Equal(t, "testplugin", manifest.Id)
|
||||
|
||||
t.Run("install plugin from URL with slow response time", func(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test to install plugin from a slow response server")
|
||||
}
|
||||
|
||||
// Install from URL - slow server to simulate longer bundle download times
|
||||
slowTestServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
time.Sleep(60 * time.Second) // Wait longer than the previous default 30 seconds timeout
|
||||
res.WriteHeader(http.StatusOK)
|
||||
res.Write(tarData)
|
||||
}))
|
||||
defer func() { slowTestServer.Close() }()
|
||||
|
||||
manifest, resp = th.SystemAdminClient.InstallPluginFromUrl(slowTestServer.URL, true)
|
||||
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)
|
||||
@@ -255,6 +274,8 @@ func TestNotifyClusterPluginEvent(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testCluster.ClearMessages()
|
||||
|
||||
// Successful upload
|
||||
manifest, resp := th.SystemAdminClient.UploadPlugin(bytes.NewReader(tarData))
|
||||
CheckNoError(t, resp)
|
||||
@@ -266,6 +287,7 @@ func TestNotifyClusterPluginEvent(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
require.True(t, pluginStored)
|
||||
|
||||
messages := testCluster.GetMessages()
|
||||
expectedPluginData := model.PluginEventData{
|
||||
Id: manifest.Id,
|
||||
}
|
||||
@@ -275,30 +297,141 @@ func TestNotifyClusterPluginEvent(t *testing.T) {
|
||||
WaitForAllToSend: true,
|
||||
Data: expectedPluginData.ToJson(),
|
||||
}
|
||||
expectedMessages := findClusterMessages(model.CLUSTER_EVENT_INSTALL_PLUGIN, testCluster.GetMessages())
|
||||
require.Equal(t, []*model.ClusterMessage{expectedInstallMessage}, expectedMessages)
|
||||
actualMessages := findClusterMessages(model.CLUSTER_EVENT_INSTALL_PLUGIN, messages)
|
||||
require.Equal(t, []*model.ClusterMessage{expectedInstallMessage}, actualMessages)
|
||||
|
||||
// Upgrade
|
||||
testCluster.ClearMessages()
|
||||
manifest, resp = th.SystemAdminClient.UploadPluginForced(bytes.NewReader(tarData))
|
||||
CheckNoError(t, resp)
|
||||
require.Equal(t, "testplugin", manifest.Id)
|
||||
|
||||
// Successful remove
|
||||
testCluster.ClearMessages()
|
||||
|
||||
ok, resp := th.SystemAdminClient.RemovePlugin(manifest.Id)
|
||||
CheckNoError(t, resp)
|
||||
require.True(t, ok)
|
||||
|
||||
messages = testCluster.GetMessages()
|
||||
|
||||
expectedRemoveMessage := &model.ClusterMessage{
|
||||
Event: model.CLUSTER_EVENT_REMOVE_PLUGIN,
|
||||
SendType: model.CLUSTER_SEND_RELIABLE,
|
||||
WaitForAllToSend: true,
|
||||
Data: expectedPluginData.ToJson(),
|
||||
}
|
||||
expectedMessages = findClusterMessages(model.CLUSTER_EVENT_REMOVE_PLUGIN, testCluster.GetMessages())
|
||||
require.Equal(t, []*model.ClusterMessage{expectedRemoveMessage}, expectedMessages)
|
||||
actualMessages = findClusterMessages(model.CLUSTER_EVENT_REMOVE_PLUGIN, messages)
|
||||
require.Equal(t, []*model.ClusterMessage{expectedRemoveMessage}, actualMessages)
|
||||
|
||||
pluginStored, err = th.App.FileExists(expectedPath)
|
||||
require.Nil(t, err)
|
||||
require.False(t, pluginStored)
|
||||
}
|
||||
|
||||
func TestDisableOnRemove(t *testing.T) {
|
||||
path, _ := fileutils.FindDir("tests")
|
||||
tarData, err := ioutil.ReadFile(filepath.Join(path, "testplugin.tar.gz"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
Description string
|
||||
Upgrade bool
|
||||
}{
|
||||
{
|
||||
"Remove without upgrading",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"Remove after upgrading",
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.Description, func(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.PluginSettings.Enable = true
|
||||
*cfg.PluginSettings.EnableUploads = true
|
||||
})
|
||||
|
||||
// Upload
|
||||
manifest, resp := th.SystemAdminClient.UploadPlugin(bytes.NewReader(tarData))
|
||||
CheckNoError(t, resp)
|
||||
require.Equal(t, "testplugin", manifest.Id)
|
||||
|
||||
// Check initial status
|
||||
pluginsResp, resp := th.SystemAdminClient.GetPlugins()
|
||||
CheckNoError(t, resp)
|
||||
require.Len(t, pluginsResp.Active, 0)
|
||||
require.Equal(t, pluginsResp.Inactive, []*model.PluginInfo{&model.PluginInfo{
|
||||
Manifest: *manifest,
|
||||
}})
|
||||
|
||||
// Enable plugin
|
||||
ok, resp := th.SystemAdminClient.EnablePlugin(manifest.Id)
|
||||
CheckNoError(t, resp)
|
||||
require.True(t, ok)
|
||||
|
||||
// Confirm enabled status
|
||||
pluginsResp, resp = th.SystemAdminClient.GetPlugins()
|
||||
CheckNoError(t, resp)
|
||||
require.Len(t, pluginsResp.Inactive, 0)
|
||||
require.Equal(t, pluginsResp.Active, []*model.PluginInfo{&model.PluginInfo{
|
||||
Manifest: *manifest,
|
||||
}})
|
||||
|
||||
if tc.Upgrade {
|
||||
// Upgrade
|
||||
manifest, resp = th.SystemAdminClient.UploadPluginForced(bytes.NewReader(tarData))
|
||||
CheckNoError(t, resp)
|
||||
require.Equal(t, "testplugin", manifest.Id)
|
||||
|
||||
// Plugin should remain active
|
||||
pluginsResp, resp = th.SystemAdminClient.GetPlugins()
|
||||
CheckNoError(t, resp)
|
||||
require.Len(t, pluginsResp.Inactive, 0)
|
||||
require.Equal(t, pluginsResp.Active, []*model.PluginInfo{&model.PluginInfo{
|
||||
Manifest: *manifest,
|
||||
}})
|
||||
}
|
||||
|
||||
// Remove plugin
|
||||
ok, resp = th.SystemAdminClient.RemovePlugin(manifest.Id)
|
||||
CheckNoError(t, resp)
|
||||
require.True(t, ok)
|
||||
|
||||
// Plugin should have no status
|
||||
pluginsResp, resp = th.SystemAdminClient.GetPlugins()
|
||||
CheckNoError(t, resp)
|
||||
require.Len(t, pluginsResp.Inactive, 0)
|
||||
require.Len(t, pluginsResp.Active, 0)
|
||||
|
||||
// Upload same plugin
|
||||
manifest, resp = th.SystemAdminClient.UploadPlugin(bytes.NewReader(tarData))
|
||||
CheckNoError(t, resp)
|
||||
require.Equal(t, "testplugin", manifest.Id)
|
||||
|
||||
// Plugin should be inactive
|
||||
pluginsResp, resp = th.SystemAdminClient.GetPlugins()
|
||||
CheckNoError(t, resp)
|
||||
require.Len(t, pluginsResp.Active, 0)
|
||||
require.Equal(t, pluginsResp.Inactive, []*model.PluginInfo{&model.PluginInfo{
|
||||
Manifest: *manifest,
|
||||
}})
|
||||
|
||||
// Clean up
|
||||
ok, resp = th.SystemAdminClient.RemovePlugin(manifest.Id)
|
||||
CheckNoError(t, resp)
|
||||
require.True(t, ok)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func findClusterMessages(event string, msgs []*model.ClusterMessage) []*model.ClusterMessage {
|
||||
var result []*model.ClusterMessage
|
||||
for _, msg := range msgs {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/mlog"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
@@ -64,11 +65,32 @@ func getSystemPing(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if r.FormValue("get_server_status") != "" {
|
||||
dbStatusKey := "database_status"
|
||||
s[dbStatusKey] = model.STATUS_OK
|
||||
_, appErr := c.App.Srv.Store.System().Get()
|
||||
if appErr != nil {
|
||||
mlog.Debug(fmt.Sprintf("Unable to get database status: %s", appErr.Error()))
|
||||
|
||||
// Database Write/Read Check
|
||||
currentTime := fmt.Sprintf("%d", time.Now().Unix())
|
||||
healthCheckKey := "health_check"
|
||||
|
||||
writeErr := c.App.Srv.Store.System().SaveOrUpdate(&model.System{
|
||||
Name: healthCheckKey,
|
||||
Value: currentTime,
|
||||
})
|
||||
if writeErr != nil {
|
||||
mlog.Debug(fmt.Sprintf("Unable to write to database: %s", writeErr.Error()))
|
||||
s[dbStatusKey] = model.STATUS_UNHEALTHY
|
||||
s[model.STATUS] = model.STATUS_UNHEALTHY
|
||||
} else {
|
||||
healthCheck, readErr := c.App.Srv.Store.System().GetByName(healthCheckKey)
|
||||
if readErr != nil {
|
||||
mlog.Debug(fmt.Sprintf("Unable to read from database: %s", readErr.Error()))
|
||||
s[dbStatusKey] = model.STATUS_UNHEALTHY
|
||||
s[model.STATUS] = model.STATUS_UNHEALTHY
|
||||
} else if healthCheck.Value != currentTime {
|
||||
mlog.Debug(fmt.Sprintf("Incorrect healthcheck value, expected %s, got %s", currentTime, healthCheck.Value))
|
||||
s[dbStatusKey] = model.STATUS_UNHEALTHY
|
||||
s[model.STATUS] = model.STATUS_UNHEALTHY
|
||||
} else {
|
||||
mlog.Debug("Able to write/read files to database")
|
||||
}
|
||||
}
|
||||
|
||||
filestoreStatusKey := "filestore_status"
|
||||
|
||||
Ссылка в новой задаче
Block a user