Plugins: Add plugins auto update feature (#104112)
This commit is contained in:
@@ -0,0 +1,302 @@
|
||||
package updatemanager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/fakes"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginchecker"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/provisionedplugins"
|
||||
)
|
||||
|
||||
type mockPluginPreinstall struct {
|
||||
pluginchecker.Preinstall
|
||||
}
|
||||
|
||||
func (m *mockPluginPreinstall) IsPinned(pluginID string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func TestPluginUpdateChecker_HasUpdate(t *testing.T) {
|
||||
t.Run("update is available", func(t *testing.T) {
|
||||
updateCheckURL, _ := url.Parse("https://grafana.com/api/plugins/versioncheck")
|
||||
|
||||
svc := PluginsService{
|
||||
availableUpdates: map[string]availableUpdate{
|
||||
"test-ds": {
|
||||
localVersion: "0.9.0",
|
||||
availableVersion: "1.0.0",
|
||||
},
|
||||
},
|
||||
pluginStore: &pluginstore.FakePluginStore{
|
||||
PluginList: []pluginstore.Plugin{
|
||||
{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: "test-ds",
|
||||
Info: plugins.Info{Version: "0.9.0"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
updateCheckURL: updateCheckURL,
|
||||
updateChecker: pluginchecker.ProvideService(managedplugins.NewNoop(), provisionedplugins.NewNoop(), &mockPluginPreinstall{}),
|
||||
features: &featuremgmt.FeatureManager{},
|
||||
}
|
||||
|
||||
update, exists := svc.HasUpdate(context.Background(), "test-ds")
|
||||
require.True(t, exists)
|
||||
require.Equal(t, "1.0.0", update)
|
||||
})
|
||||
|
||||
t.Run("update is not available", func(t *testing.T) {
|
||||
updateCheckURL, _ := url.Parse("https://grafana.com/api/plugins/versioncheck")
|
||||
|
||||
svc := PluginsService{
|
||||
availableUpdates: map[string]availableUpdate{
|
||||
"test-panel": {
|
||||
localVersion: "0.9.0",
|
||||
availableVersion: "0.9.0",
|
||||
},
|
||||
"test-app": {
|
||||
localVersion: "0.9.0",
|
||||
availableVersion: "0.9.0",
|
||||
},
|
||||
},
|
||||
pluginStore: &pluginstore.FakePluginStore{
|
||||
PluginList: []pluginstore.Plugin{
|
||||
{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: "test-ds",
|
||||
Info: plugins.Info{Version: "0.9.0"},
|
||||
},
|
||||
},
|
||||
{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: "test-panel",
|
||||
Info: plugins.Info{Version: "0.9.0"},
|
||||
},
|
||||
},
|
||||
{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: "test-app",
|
||||
Info: plugins.Info{Version: "0.9.0"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
updateCheckURL: updateCheckURL,
|
||||
updateChecker: pluginchecker.ProvideService(managedplugins.NewNoop(), provisionedplugins.NewNoop(), &mockPluginPreinstall{}),
|
||||
}
|
||||
|
||||
update, exists := svc.HasUpdate(context.Background(), "test-ds")
|
||||
require.False(t, exists)
|
||||
require.Empty(t, update)
|
||||
|
||||
update, exists = svc.HasUpdate(context.Background(), "test-panel")
|
||||
require.False(t, exists)
|
||||
require.Empty(t, update)
|
||||
|
||||
update, exists = svc.HasUpdate(context.Background(), "test-app")
|
||||
require.False(t, exists)
|
||||
require.Empty(t, update)
|
||||
})
|
||||
|
||||
t.Run("update is available but plugin is not in store", func(t *testing.T) {
|
||||
updateCheckURL, _ := url.Parse("https://grafana.com/api/plugins/versioncheck")
|
||||
|
||||
svc := PluginsService{
|
||||
availableUpdates: map[string]availableUpdate{
|
||||
"test-panel": {
|
||||
localVersion: "0.9.0",
|
||||
availableVersion: "0.9.0",
|
||||
},
|
||||
},
|
||||
pluginStore: &pluginstore.FakePluginStore{
|
||||
PluginList: []pluginstore.Plugin{
|
||||
{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: "test-ds",
|
||||
Info: plugins.Info{Version: "1.0.0"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
updateCheckURL: updateCheckURL,
|
||||
}
|
||||
|
||||
update, exists := svc.HasUpdate(context.Background(), "test-panel")
|
||||
require.False(t, exists)
|
||||
require.Empty(t, update)
|
||||
|
||||
update, exists = svc.HasUpdate(context.Background(), "test-ds")
|
||||
require.False(t, exists)
|
||||
require.Empty(t, update)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPluginUpdateChecker_checkForUpdates(t *testing.T) {
|
||||
t.Run("update is available", func(t *testing.T) {
|
||||
jsonResp := `[
|
||||
{
|
||||
"slug": "test-ds",
|
||||
"version": "1.0.12"
|
||||
},
|
||||
{
|
||||
"slug": "test-panel",
|
||||
"version": "2.5.7"
|
||||
},
|
||||
{
|
||||
"slug": "test-core-panel",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
]`
|
||||
|
||||
updateCheckURL, _ := url.Parse("https://grafana.com/api/plugins/versioncheck")
|
||||
|
||||
svc := PluginsService{
|
||||
availableUpdates: map[string]availableUpdate{
|
||||
"test-app": {
|
||||
localVersion: "0.5.0",
|
||||
availableVersion: "1.0.0",
|
||||
},
|
||||
},
|
||||
pluginStore: &pluginstore.FakePluginStore{
|
||||
PluginList: []pluginstore.Plugin{
|
||||
{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: "test-ds",
|
||||
Info: plugins.Info{Version: "0.9.0"},
|
||||
Type: plugins.TypeDataSource,
|
||||
},
|
||||
Class: plugins.ClassExternal,
|
||||
},
|
||||
{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: "test-app",
|
||||
Info: plugins.Info{Version: "0.5.0"},
|
||||
Type: plugins.TypeApp,
|
||||
},
|
||||
Class: plugins.ClassExternal,
|
||||
},
|
||||
{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: "test-panel",
|
||||
Info: plugins.Info{Version: "2.5.7"},
|
||||
Type: plugins.TypePanel,
|
||||
},
|
||||
Class: plugins.ClassExternal,
|
||||
},
|
||||
{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: "test-core-panel",
|
||||
Info: plugins.Info{Version: "0.0.1"},
|
||||
Type: plugins.TypePanel,
|
||||
},
|
||||
Class: plugins.ClassCore,
|
||||
},
|
||||
},
|
||||
},
|
||||
httpClient: &fakeHTTPClient{
|
||||
fakeResp: jsonResp,
|
||||
},
|
||||
log: log.NewNopLogger(),
|
||||
tracer: tracing.InitializeTracerForTest(),
|
||||
updateCheckURL: updateCheckURL,
|
||||
updateChecker: pluginchecker.ProvideService(managedplugins.NewNoop(), provisionedplugins.NewNoop(), &mockPluginPreinstall{}),
|
||||
features: &featuremgmt.FeatureManager{},
|
||||
}
|
||||
|
||||
svc.instrumentedCheckForUpdates(context.Background())
|
||||
|
||||
require.Equal(t, 1, len(svc.availableUpdates))
|
||||
|
||||
require.Equal(t, "1.0.12", svc.availableUpdates["test-ds"].availableVersion)
|
||||
update, exists := svc.HasUpdate(context.Background(), "test-ds")
|
||||
require.True(t, exists)
|
||||
require.Equal(t, "1.0.12", update)
|
||||
|
||||
require.Empty(t, svc.availableUpdates["test-app"])
|
||||
update, exists = svc.HasUpdate(context.Background(), "test-app")
|
||||
require.False(t, exists)
|
||||
require.Empty(t, update)
|
||||
|
||||
require.Empty(t, svc.availableUpdates["test-panel"])
|
||||
update, exists = svc.HasUpdate(context.Background(), "test-panel")
|
||||
require.False(t, exists)
|
||||
require.Empty(t, update)
|
||||
|
||||
require.Empty(t, svc.availableUpdates["test-core-panel"])
|
||||
})
|
||||
}
|
||||
func TestPluginUpdateChecker_updateAll(t *testing.T) {
|
||||
t.Run("update is available", func(t *testing.T) {
|
||||
pluginsFakeStore := map[string]string{}
|
||||
availableUpdates := map[string]availableUpdate{
|
||||
"test-app-0": {
|
||||
localVersion: "0.9.0",
|
||||
availableVersion: "1.0.0",
|
||||
},
|
||||
"test-app-1": {
|
||||
localVersion: "0.9.0",
|
||||
availableVersion: "1.0.0",
|
||||
},
|
||||
"test-app-2": {
|
||||
localVersion: "0.9.0",
|
||||
availableVersion: "1.0.0",
|
||||
},
|
||||
}
|
||||
|
||||
svc := PluginsService{
|
||||
availableUpdates: availableUpdates,
|
||||
log: log.NewNopLogger(),
|
||||
tracer: tracing.InitializeTracerForTest(),
|
||||
pluginInstaller: &fakes.FakePluginInstaller{
|
||||
AddFunc: func(ctx context.Context, pluginID, version string, opts plugins.AddOpts) error {
|
||||
pluginsFakeStore[pluginID] = version
|
||||
return nil
|
||||
},
|
||||
RemoveFunc: func(ctx context.Context, pluginID, version string) error {
|
||||
delete(pluginsFakeStore, pluginID)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
svc.updateAll(context.Background())
|
||||
|
||||
require.Equal(t, 0, len(svc.availableUpdates))
|
||||
require.Equal(t, len(availableUpdates), len(pluginsFakeStore))
|
||||
|
||||
for pluginID, availableUpdate := range availableUpdates {
|
||||
require.Equal(t, availableUpdate.availableVersion, pluginsFakeStore[pluginID])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type fakeHTTPClient struct {
|
||||
fakeResp string
|
||||
|
||||
requestURL string
|
||||
}
|
||||
|
||||
func (c *fakeHTTPClient) Do(req *http.Request) (*http.Response, error) {
|
||||
c.requestURL = req.URL.String()
|
||||
|
||||
resp := &http.Response{
|
||||
Body: io.NopCloser(strings.NewReader(c.fakeResp)),
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
Reference in New Issue
Block a user