Preinstall: Automatic updates if no version is specified (#95970)
This commit is contained in:
@@ -2,11 +2,14 @@ package plugininstaller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/fakes"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/registry"
|
||||
"github.com/grafana/grafana/pkg/plugins/repo"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -24,6 +27,8 @@ func TestService_IsDisabled(t *testing.T) {
|
||||
pluginstore.New(registry.NewInMemory(), &fakes.FakeLoader{}),
|
||||
&fakes.FakePluginInstaller{},
|
||||
prometheus.NewRegistry(),
|
||||
&fakes.FakePluginRepo{},
|
||||
featuremgmt.WithFeatures(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -34,196 +39,137 @@ func TestService_IsDisabled(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestService_Run(t *testing.T) {
|
||||
t.Run("Installs a plugin", func(t *testing.T) {
|
||||
installed := false
|
||||
s, err := ProvideService(
|
||||
&setting.Cfg{
|
||||
PreinstallPlugins: []setting.InstallPlugin{{ID: "myplugin"}},
|
||||
},
|
||||
pluginstore.New(registry.NewInMemory(), &fakes.FakeLoader{}),
|
||||
&fakes.FakePluginInstaller{
|
||||
AddFunc: func(ctx context.Context, pluginID string, version string, opts plugins.CompatOpts) error {
|
||||
installed = true
|
||||
return nil
|
||||
tests := []struct {
|
||||
name string
|
||||
shouldInstall bool
|
||||
pluginsToInstall []setting.InstallPlugin
|
||||
existingPlugins []*plugins.Plugin
|
||||
pluginsToFail []string
|
||||
blocking bool
|
||||
latestPlugin *repo.PluginArchiveInfo
|
||||
}{
|
||||
{
|
||||
name: "Installs a plugin",
|
||||
shouldInstall: true,
|
||||
pluginsToInstall: []setting.InstallPlugin{{ID: "myplugin"}},
|
||||
},
|
||||
{
|
||||
name: "Install a plugin with version",
|
||||
shouldInstall: true,
|
||||
pluginsToInstall: []setting.InstallPlugin{{ID: "myplugin", Version: "1.0.0"}},
|
||||
},
|
||||
{
|
||||
name: "Skips already installed plugin",
|
||||
shouldInstall: false,
|
||||
pluginsToInstall: []setting.InstallPlugin{{ID: "myplugin"}},
|
||||
existingPlugins: []*plugins.Plugin{{JSONData: plugins.JSONData{ID: "myplugin"}}},
|
||||
},
|
||||
{
|
||||
name: "Still installs a plugin if the plugin version does not match",
|
||||
shouldInstall: true,
|
||||
pluginsToInstall: []setting.InstallPlugin{{ID: "myplugin", Version: "2.0.0"}},
|
||||
existingPlugins: []*plugins.Plugin{{JSONData: plugins.JSONData{ID: "myplugin", Info: plugins.Info{Version: "1.0.0"}}}},
|
||||
},
|
||||
{
|
||||
name: "Install multiple plugins",
|
||||
shouldInstall: true,
|
||||
pluginsToInstall: []setting.InstallPlugin{{ID: "myplugin1"}, {ID: "myplugin2"}},
|
||||
},
|
||||
{
|
||||
name: "Fails to install a plugin but install the rest",
|
||||
shouldInstall: true,
|
||||
pluginsToInstall: []setting.InstallPlugin{{ID: "myplugin1"}, {ID: "myplugin2"}},
|
||||
pluginsToFail: []string{"myplugin1"},
|
||||
},
|
||||
{
|
||||
name: "Install a blocking plugin",
|
||||
shouldInstall: true,
|
||||
pluginsToInstall: []setting.InstallPlugin{{ID: "myplugin"}},
|
||||
blocking: true,
|
||||
},
|
||||
{
|
||||
name: "Fails to install a blocking plugin",
|
||||
shouldInstall: false,
|
||||
pluginsToInstall: []setting.InstallPlugin{{ID: "myplugin"}},
|
||||
blocking: true,
|
||||
pluginsToFail: []string{"myplugin"},
|
||||
},
|
||||
{
|
||||
name: "Updates a plugin",
|
||||
shouldInstall: true,
|
||||
pluginsToInstall: []setting.InstallPlugin{{ID: "myplugin", Version: ""}},
|
||||
existingPlugins: []*plugins.Plugin{{JSONData: plugins.JSONData{ID: "myplugin", Info: plugins.Info{Version: "1.0.0"}}}},
|
||||
latestPlugin: &repo.PluginArchiveInfo{Version: "1.0.1"},
|
||||
},
|
||||
{
|
||||
name: "Should not update a plugin if the latest version is installed",
|
||||
shouldInstall: false,
|
||||
pluginsToInstall: []setting.InstallPlugin{{ID: "myplugin", Version: ""}},
|
||||
existingPlugins: []*plugins.Plugin{{JSONData: plugins.JSONData{ID: "myplugin", Info: plugins.Info{Version: "1.0.0"}}}},
|
||||
latestPlugin: &repo.PluginArchiveInfo{Version: "1.0.0"},
|
||||
},
|
||||
{
|
||||
name: "Should not update a plugin if the latest version is a major version",
|
||||
shouldInstall: false,
|
||||
pluginsToInstall: []setting.InstallPlugin{{ID: "myplugin", Version: ""}},
|
||||
existingPlugins: []*plugins.Plugin{{JSONData: plugins.JSONData{ID: "myplugin", Info: plugins.Info{Version: "1.0.0"}}}},
|
||||
latestPlugin: &repo.PluginArchiveInfo{Version: "2.0.0"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
preg := registry.NewInMemory()
|
||||
for _, plugin := range tt.existingPlugins {
|
||||
err := preg.Add(context.Background(), plugin)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
installed := 0
|
||||
s, err := ProvideService(
|
||||
&setting.Cfg{
|
||||
PreinstallPlugins: tt.pluginsToInstall,
|
||||
PreinstallPluginsAsync: !tt.blocking,
|
||||
},
|
||||
},
|
||||
prometheus.NewRegistry(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = s.Run(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.True(t, installed)
|
||||
})
|
||||
|
||||
t.Run("Install a plugin with version", func(t *testing.T) {
|
||||
installed := false
|
||||
s, err := ProvideService(
|
||||
&setting.Cfg{
|
||||
PreinstallPlugins: []setting.InstallPlugin{{ID: "myplugin", Version: "1.0.0"}},
|
||||
PreinstallPluginsAsync: true,
|
||||
},
|
||||
pluginstore.New(registry.NewInMemory(), &fakes.FakeLoader{}),
|
||||
&fakes.FakePluginInstaller{
|
||||
AddFunc: func(ctx context.Context, pluginID string, version string, opts plugins.CompatOpts) error {
|
||||
if pluginID == "myplugin" && version == "1.0.0" {
|
||||
installed = true
|
||||
}
|
||||
return nil
|
||||
pluginstore.New(preg, &fakes.FakeLoader{}),
|
||||
&fakes.FakePluginInstaller{
|
||||
AddFunc: func(ctx context.Context, pluginID string, version string, opts plugins.CompatOpts) error {
|
||||
for _, plugin := range tt.pluginsToFail {
|
||||
if plugin == pluginID {
|
||||
return errors.New("Failed to install plugin")
|
||||
}
|
||||
}
|
||||
if !tt.shouldInstall {
|
||||
t.Fatal("Should not install plugin")
|
||||
return errors.New("Should not install plugin")
|
||||
}
|
||||
for _, plugin := range tt.pluginsToInstall {
|
||||
if plugin.ID == pluginID && plugin.Version == version {
|
||||
installed++
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
prometheus.NewRegistry(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
prometheus.NewRegistry(),
|
||||
&fakes.FakePluginRepo{
|
||||
GetPluginArchiveInfoFunc: func(_ context.Context, pluginID, version string, _ repo.CompatOpts) (*repo.PluginArchiveInfo, error) {
|
||||
return tt.latestPlugin, nil
|
||||
},
|
||||
},
|
||||
featuremgmt.WithFeatures(featuremgmt.FlagPreinstallAutoUpdate),
|
||||
)
|
||||
if tt.blocking && !tt.shouldInstall {
|
||||
require.ErrorContains(t, err, "Failed to install plugin")
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
err = s.Run(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.True(t, installed)
|
||||
})
|
||||
|
||||
t.Run("Skips already installed plugin", func(t *testing.T) {
|
||||
preg := registry.NewInMemory()
|
||||
err := preg.Add(context.Background(), &plugins.Plugin{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: "myplugin",
|
||||
},
|
||||
if !tt.blocking {
|
||||
err = s.Run(context.Background())
|
||||
require.NoError(t, err)
|
||||
}
|
||||
if tt.shouldInstall {
|
||||
require.Equal(t, len(tt.pluginsToInstall)-len(tt.pluginsToFail), installed)
|
||||
}
|
||||
})
|
||||
require.NoError(t, err)
|
||||
s, err := ProvideService(
|
||||
&setting.Cfg{
|
||||
PreinstallPlugins: []setting.InstallPlugin{{ID: "myplugin"}},
|
||||
PreinstallPluginsAsync: true,
|
||||
},
|
||||
pluginstore.New(preg, &fakes.FakeLoader{}),
|
||||
&fakes.FakePluginInstaller{
|
||||
AddFunc: func(ctx context.Context, pluginID string, version string, opts plugins.CompatOpts) error {
|
||||
t.Fatal("Should not install plugin")
|
||||
return plugins.DuplicateError{}
|
||||
},
|
||||
},
|
||||
prometheus.NewRegistry(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = s.Run(context.Background())
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("Still installs a plugin if the plugin version does not match", func(t *testing.T) {
|
||||
installed := false
|
||||
preg := registry.NewInMemory()
|
||||
err := preg.Add(context.Background(), &plugins.Plugin{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: "myplugin",
|
||||
Info: plugins.Info{
|
||||
Version: "1.0.0",
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
s, err := ProvideService(
|
||||
&setting.Cfg{
|
||||
PreinstallPlugins: []setting.InstallPlugin{{ID: "myplugin", Version: "2.0.0"}},
|
||||
PreinstallPluginsAsync: true,
|
||||
},
|
||||
pluginstore.New(preg, &fakes.FakeLoader{}),
|
||||
&fakes.FakePluginInstaller{
|
||||
AddFunc: func(ctx context.Context, pluginID string, version string, opts plugins.CompatOpts) error {
|
||||
installed = true
|
||||
return nil
|
||||
},
|
||||
},
|
||||
prometheus.NewRegistry(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = s.Run(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.True(t, installed)
|
||||
})
|
||||
|
||||
t.Run("Install multiple plugins", func(t *testing.T) {
|
||||
installed := 0
|
||||
s, err := ProvideService(
|
||||
&setting.Cfg{
|
||||
PreinstallPlugins: []setting.InstallPlugin{{ID: "myplugin1"}, {ID: "myplugin2"}},
|
||||
PreinstallPluginsAsync: true,
|
||||
},
|
||||
pluginstore.New(registry.NewInMemory(), &fakes.FakeLoader{}),
|
||||
&fakes.FakePluginInstaller{
|
||||
AddFunc: func(ctx context.Context, pluginID string, version string, opts plugins.CompatOpts) error {
|
||||
installed++
|
||||
return nil
|
||||
},
|
||||
},
|
||||
prometheus.NewRegistry(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = s.Run(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, installed)
|
||||
})
|
||||
|
||||
t.Run("Fails to install a plugin but install the rest", func(t *testing.T) {
|
||||
installed := 0
|
||||
s, err := ProvideService(
|
||||
&setting.Cfg{
|
||||
PreinstallPlugins: []setting.InstallPlugin{{ID: "myplugin1"}, {ID: "myplugin2"}},
|
||||
PreinstallPluginsAsync: true,
|
||||
},
|
||||
pluginstore.New(registry.NewInMemory(), &fakes.FakeLoader{}),
|
||||
&fakes.FakePluginInstaller{
|
||||
AddFunc: func(ctx context.Context, pluginID string, version string, opts plugins.CompatOpts) error {
|
||||
if pluginID == "myplugin1" {
|
||||
return plugins.NotFoundError{}
|
||||
}
|
||||
installed++
|
||||
return nil
|
||||
},
|
||||
},
|
||||
prometheus.NewRegistry(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
err = s.Run(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, installed)
|
||||
})
|
||||
|
||||
t.Run("Install a blocking plugin", func(t *testing.T) {
|
||||
installed := false
|
||||
_, err := ProvideService(
|
||||
&setting.Cfg{
|
||||
PreinstallPlugins: []setting.InstallPlugin{{ID: "myplugin"}},
|
||||
PreinstallPluginsAsync: false,
|
||||
},
|
||||
pluginstore.New(registry.NewInMemory(), &fakes.FakeLoader{}),
|
||||
&fakes.FakePluginInstaller{
|
||||
AddFunc: func(ctx context.Context, pluginID string, version string, opts plugins.CompatOpts) error {
|
||||
installed = true
|
||||
return nil
|
||||
},
|
||||
},
|
||||
prometheus.NewRegistry(),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.True(t, installed)
|
||||
})
|
||||
|
||||
t.Run("Fails to install a blocking plugin", func(t *testing.T) {
|
||||
_, err := ProvideService(
|
||||
&setting.Cfg{
|
||||
PreinstallPlugins: []setting.InstallPlugin{{ID: "myplugin"}},
|
||||
PreinstallPluginsAsync: false,
|
||||
},
|
||||
pluginstore.New(registry.NewInMemory(), &fakes.FakeLoader{}),
|
||||
&fakes.FakePluginInstaller{
|
||||
AddFunc: func(ctx context.Context, pluginID string, version string, opts plugins.CompatOpts) error {
|
||||
return plugins.NotFoundError{}
|
||||
},
|
||||
},
|
||||
prometheus.NewRegistry(),
|
||||
)
|
||||
require.ErrorAs(t, err, &plugins.NotFoundError{})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user