PluginManager: Make remaining plugin state non-global (#32094)

* PluginDashboards: Use plugin manager interface

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* PluginManager: Make panels non-global

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* PluginManager: Make apps non-global

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* PluginManager: Make static routes non-global

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* PluginManager: Make pluginTypes non-global

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
Arve Knudsen
2021-03-18 13:53:01 +01:00
committed by GitHub
parent 1454c3723d
commit a2eda798e7
22 changed files with 293 additions and 233 deletions
+6 -8
View File
@@ -78,16 +78,14 @@ func pluginScenario(t *testing.T, desc string, fn func(*testing.T, *PluginManage
t.Helper()
t.Run("Given a plugin", func(t *testing.T) {
pm := &PluginManager{
Cfg: &setting.Cfg{
FeatureToggles: map[string]bool{},
PluginSettings: setting.PluginSettings{
"test-app": map[string]string{
"path": "testdata/test-app",
},
pm := newManager(&setting.Cfg{
FeatureToggles: map[string]bool{},
PluginSettings: setting.PluginSettings{
"test-app": map[string]string{
"path": "testdata/test-app",
},
},
}
})
err := pm.Init()
require.NoError(t, err)
+7 -7
View File
@@ -10,16 +10,16 @@ import (
"github.com/grafana/grafana/pkg/plugins"
)
func (pm *PluginManager) GetPluginDashboards(orgId int64, pluginId string) ([]*plugins.PluginDashboardInfoDTO, error) {
plugin, exists := pm.plugins[pluginId]
func (pm *PluginManager) GetPluginDashboards(orgID int64, pluginID string) ([]*plugins.PluginDashboardInfoDTO, error) {
plugin, exists := pm.plugins[pluginID]
if !exists {
return nil, plugins.PluginNotFoundError{PluginID: pluginId}
return nil, plugins.PluginNotFoundError{PluginID: pluginID}
}
result := make([]*plugins.PluginDashboardInfoDTO, 0)
// load current dashboards
query := models.GetDashboardsByPluginIdQuery{OrgId: orgId, PluginId: pluginId}
query := models.GetDashboardsByPluginIdQuery{OrgId: orgID, PluginId: pluginID}
if err := bus.Dispatch(&query); err != nil {
return nil, err
}
@@ -70,10 +70,10 @@ func (pm *PluginManager) GetPluginDashboards(orgId int64, pluginId string) ([]*p
return result, nil
}
func (pm *PluginManager) LoadPluginDashboard(pluginId, path string) (*models.Dashboard, error) {
plugin, exists := pm.plugins[pluginId]
func (pm *PluginManager) LoadPluginDashboard(pluginID, path string) (*models.Dashboard, error) {
plugin, exists := pm.plugins[pluginID]
if !exists {
return nil, plugins.PluginNotFoundError{PluginID: pluginId}
return nil, plugins.PluginNotFoundError{PluginID: pluginID}
}
dashboardFilePath := filepath.Join(plugin.PluginDir, path)
+45 -53
View File
@@ -7,61 +7,53 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
. "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPluginDashboards(t *testing.T) {
Convey("When asking for plugin dashboard info", t, func() {
pm := &PluginManager{
Cfg: &setting.Cfg{
FeatureToggles: map[string]bool{},
PluginSettings: setting.PluginSettings{
"test-app": map[string]string{
"path": "testdata/test-app",
},
},
func TestGetPluginDashboards(t *testing.T) {
pm := newManager(&setting.Cfg{
FeatureToggles: map[string]bool{},
PluginSettings: setting.PluginSettings{
"test-app": map[string]string{
"path": "testdata/test-app",
},
}
err := pm.Init()
So(err, ShouldBeNil)
bus.AddHandler("test", func(query *models.GetDashboardQuery) error {
if query.Slug == "nginx-connections" {
dash := models.NewDashboard("Nginx Connections")
dash.Data.Set("revision", "1.1")
query.Result = dash
return nil
}
return models.ErrDashboardNotFound
})
bus.AddHandler("test", func(query *models.GetDashboardsByPluginIdQuery) error {
var data = simplejson.New()
data.Set("title", "Nginx Connections")
data.Set("revision", 22)
query.Result = []*models.Dashboard{
{Slug: "nginx-connections", Data: data},
}
return nil
})
dashboards, err := pm.GetPluginDashboards(1, "test-app")
So(err, ShouldBeNil)
Convey("should return 2 dashboards", func() {
So(len(dashboards), ShouldEqual, 2)
})
Convey("should include installed version info", func() {
So(dashboards[0].Title, ShouldEqual, "Nginx Connections")
So(dashboards[0].Revision, ShouldEqual, 25)
So(dashboards[0].ImportedRevision, ShouldEqual, 22)
So(dashboards[0].ImportedUri, ShouldEqual, "db/nginx-connections")
So(dashboards[1].Revision, ShouldEqual, 2)
So(dashboards[1].ImportedRevision, ShouldEqual, 0)
})
},
})
err := pm.Init()
require.NoError(t, err)
bus.AddHandler("test", func(query *models.GetDashboardQuery) error {
if query.Slug == "nginx-connections" {
dash := models.NewDashboard("Nginx Connections")
dash.Data.Set("revision", "1.1")
query.Result = dash
return nil
}
return models.ErrDashboardNotFound
})
bus.AddHandler("test", func(query *models.GetDashboardsByPluginIdQuery) error {
var data = simplejson.New()
data.Set("title", "Nginx Connections")
data.Set("revision", 22)
query.Result = []*models.Dashboard{
{Slug: "nginx-connections", Data: data},
}
return nil
})
dashboards, err := pm.GetPluginDashboards(1, "test-app")
require.NoError(t, err)
assert.Len(t, dashboards, 2)
assert.Equal(t, "Nginx Connections", dashboards[0].Title)
assert.Equal(t, int64(25), dashboards[0].Revision)
assert.Equal(t, int64(22), dashboards[0].ImportedRevision)
assert.Equal(t, "db/nginx-connections", dashboards[0].ImportedUri)
assert.Equal(t, int64(2), dashboards[1].Revision)
assert.Equal(t, int64(0), dashboards[1].ImportedRevision)
}
+61 -31
View File
@@ -28,11 +28,6 @@ import (
)
var (
Panels map[string]*plugins.PanelPlugin
StaticRoutes []*plugins.PluginStaticRoute
Apps map[string]*plugins.AppPlugin
PluginTypes map[string]interface{}
plog log.Logger
)
@@ -63,31 +58,34 @@ type PluginManager struct {
grafanaHasUpdate bool
pluginScanningErrors map[string]plugins.PluginError
renderer *plugins.RendererPlugin
dataSources map[string]*plugins.DataSourcePlugin
plugins map[string]*plugins.PluginBase
renderer *plugins.RendererPlugin
dataSources map[string]*plugins.DataSourcePlugin
plugins map[string]*plugins.PluginBase
panels map[string]*plugins.PanelPlugin
apps map[string]*plugins.AppPlugin
staticRoutes []*plugins.PluginStaticRoute
}
func init() {
registry.RegisterService(&PluginManager{
dataSources: map[string]*plugins.DataSourcePlugin{},
registry.Register(&registry.Descriptor{
Name: "PluginManager",
Instance: newManager(nil),
})
}
func newManager(cfg *setting.Cfg) *PluginManager {
return &PluginManager{
Cfg: cfg,
dataSources: map[string]*plugins.DataSourcePlugin{},
plugins: map[string]*plugins.PluginBase{},
panels: map[string]*plugins.PanelPlugin{},
apps: map[string]*plugins.AppPlugin{},
}
}
func (pm *PluginManager) Init() error {
pm.log = log.New("plugins")
plog = log.New("plugins")
StaticRoutes = []*plugins.PluginStaticRoute{}
Panels = map[string]*plugins.PanelPlugin{}
Apps = map[string]*plugins.AppPlugin{}
pm.plugins = map[string]*plugins.PluginBase{}
PluginTypes = map[string]interface{}{
"panel": plugins.PanelPlugin{},
"datasource": plugins.DataSourcePlugin{},
"app": plugins.AppPlugin{},
"renderer": plugins.RendererPlugin{},
}
pm.pluginScanningErrors = map[string]plugins.PluginError{}
pm.log.Info("Starting plugin search")
@@ -133,24 +131,24 @@ func (pm *PluginManager) Init() error {
return err
}
for _, panel := range Panels {
for _, panel := range pm.panels {
staticRoutes := panel.InitFrontendPlugin(pm.Cfg)
StaticRoutes = append(StaticRoutes, staticRoutes...)
pm.staticRoutes = append(pm.staticRoutes, staticRoutes...)
}
for _, ds := range pm.dataSources {
staticRoutes := ds.InitFrontendPlugin(pm.Cfg)
StaticRoutes = append(StaticRoutes, staticRoutes...)
pm.staticRoutes = append(pm.staticRoutes, staticRoutes...)
}
for _, app := range Apps {
staticRoutes := app.InitApp(Panels, pm.dataSources, pm.Cfg)
StaticRoutes = append(StaticRoutes, staticRoutes...)
for _, app := range pm.apps {
staticRoutes := app.InitApp(pm.panels, pm.dataSources, pm.Cfg)
pm.staticRoutes = append(pm.staticRoutes, staticRoutes...)
}
if pm.renderer != nil {
staticRoutes := pm.renderer.InitFrontendPlugin(pm.Cfg)
StaticRoutes = append(StaticRoutes, staticRoutes...)
pm.staticRoutes = append(pm.staticRoutes, staticRoutes...)
}
for _, p := range pm.plugins {
@@ -203,6 +201,14 @@ func (pm *PluginManager) DataSourceCount() int {
return len(pm.dataSources)
}
func (pm *PluginManager) PanelCount() int {
return len(pm.panels)
}
func (pm *PluginManager) AppCount() int {
return len(pm.apps)
}
func (pm *PluginManager) Plugins() []*plugins.PluginBase {
var rslt []*plugins.PluginBase
for _, p := range pm.plugins {
@@ -212,10 +218,23 @@ func (pm *PluginManager) Plugins() []*plugins.PluginBase {
return rslt
}
func (pm *PluginManager) Apps() []*plugins.AppPlugin {
var rslt []*plugins.AppPlugin
for _, p := range pm.apps {
rslt = append(rslt, p)
}
return rslt
}
func (pm *PluginManager) GetPlugin(id string) *plugins.PluginBase {
return pm.plugins[id]
}
func (pm *PluginManager) GetApp(id string) *plugins.AppPlugin {
return pm.apps[id]
}
func (pm *PluginManager) GrafanaLatestVersion() string {
return pm.grafanaLatestVersion
}
@@ -270,6 +289,13 @@ func (pm *PluginManager) scan(pluginDir string, requireSigned bool) error {
pm.log.Debug("Initial plugin loading done")
pluginTypes := map[string]interface{}{
"panel": plugins.PanelPlugin{},
"datasource": plugins.DataSourcePlugin{},
"app": plugins.AppPlugin{},
"renderer": plugins.RendererPlugin{},
}
// 2nd pass: Validate and register plugins
for dpath, plugin := range scanner.plugins {
// Try to find any root plugin
@@ -298,7 +324,7 @@ func (pm *PluginManager) scan(pluginDir string, requireSigned bool) error {
pm.log.Debug("Attempting to add plugin", "id", plugin.Id)
pluginGoType, exists := PluginTypes[plugin.Type]
pluginGoType, exists := pluginTypes[plugin.Type]
if !exists {
return fmt.Errorf("unknown plugin type %q", plugin.Type)
}
@@ -364,13 +390,13 @@ func (pm *PluginManager) loadPlugin(jsonParser *json.Decoder, pluginBase *plugin
pm.dataSources[p.Id] = p
pb = &p.PluginBase
case *plugins.PanelPlugin:
Panels[p.Id] = p
pm.panels[p.Id] = p
pb = &p.PluginBase
case *plugins.RendererPlugin:
pm.renderer = p
pb = &p.PluginBase
case *plugins.AppPlugin:
Apps[p.Id] = p
pm.apps[p.Id] = p
pb = &p.PluginBase
default:
panic(fmt.Sprintf("Unrecognized plugin type %T", plug))
@@ -651,3 +677,7 @@ func (pm *PluginManager) GetDataPlugin(id string) plugins.DataPlugin {
return nil
}
func (pm *PluginManager) StaticRoutes() []*plugins.PluginStaticRoute {
return pm.staticRoutes
}
+10 -13
View File
@@ -31,11 +31,11 @@ func TestPluginManager_Init(t *testing.T) {
assert.Empty(t, pm.scanningErrors)
assert.Greater(t, len(pm.dataSources), 1)
assert.Greater(t, len(Panels), 1)
assert.Greater(t, len(pm.panels), 1)
assert.Equal(t, "app/plugins/datasource/graphite/module", pm.dataSources["graphite"].Module)
assert.NotEmpty(t, Apps)
assert.Equal(t, "public/plugins/test-app/img/logo_large.png", Apps["test-app"].Info.Logos.Large)
assert.Equal(t, "public/plugins/test-app/img/screenshot2.png", Apps["test-app"].Info.Screenshots[1].Path)
assert.NotEmpty(t, pm.apps)
assert.Equal(t, "public/plugins/test-app/img/logo_large.png", pm.apps["test-app"].Info.Logos.Large)
assert.Equal(t, "public/plugins/test-app/img/screenshot2.png", pm.apps["test-app"].Info.Screenshots[1].Path)
})
t.Run("With external back-end plugin lacking signature", func(t *testing.T) {
@@ -253,15 +253,12 @@ func createManager(t *testing.T, cbs ...func(*PluginManager)) *PluginManager {
staticRootPath, err := filepath.Abs("../../../public/")
require.NoError(t, err)
pm := &PluginManager{
Cfg: &setting.Cfg{
Raw: ini.Empty(),
Env: setting.Prod,
StaticRootPath: staticRootPath,
},
BackendPluginManager: &fakeBackendPluginManager{},
dataSources: map[string]*plugins.DataSourcePlugin{},
}
pm := newManager(&setting.Cfg{
Raw: ini.Empty(),
Env: setting.Prod,
StaticRootPath: staticRootPath,
})
pm.BackendPluginManager = &fakeBackendPluginManager{}
for _, cb := range cbs {
cb(pm)
}
+5 -5
View File
@@ -30,7 +30,7 @@ func (pm *PluginManager) GetPluginSettings(orgID int64) (map[string]*models.Plug
}
// apps are disabled by default unless autoEnabled: true
if app, exists := Apps[pluginDef.Id]; exists {
if app, exists := pm.apps[pluginDef.Id]; exists {
opt.Enabled = app.AutoEnabled
opt.Pinned = app.AutoEnabled
}
@@ -63,7 +63,7 @@ func (pm *PluginManager) GetEnabledPlugins(orgID int64) (*plugins.EnabledPlugins
return enabledPlugins, err
}
for pluginID, app := range Apps {
for pluginID, app := range pm.apps {
if b, ok := pluginSettingMap[pluginID]; ok {
app.Pinned = b.Pinned
enabledPlugins.Apps = append(enabledPlugins.Apps, app)
@@ -77,7 +77,7 @@ func (pm *PluginManager) GetEnabledPlugins(orgID int64) (*plugins.EnabledPlugins
}
}
for _, panel := range Panels {
for _, panel := range pm.panels {
if _, exists := pluginSettingMap[panel.Id]; exists {
enabledPlugins.Panels = append(enabledPlugins.Panels, panel)
}
@@ -87,7 +87,7 @@ func (pm *PluginManager) GetEnabledPlugins(orgID int64) (*plugins.EnabledPlugins
}
// IsAppInstalled checks if an app plugin with provided plugin ID is installed.
func IsAppInstalled(pluginID string) bool {
_, exists := Apps[pluginID]
func (pm *PluginManager) IsAppInstalled(pluginID string) bool {
_, exists := pm.apps[pluginID]
return exists
}