diff --git a/pkg/api/api.go b/pkg/api/api.go index 1f1b493f83f..e84a0a78f7e 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -275,7 +275,7 @@ func (hs *HTTPServer) registerRoutes() { apiRoute.Group("/plugins", func(pluginRoute routing.RouteRegister) { pluginRoute.Get("/:pluginId/dashboards/", routing.Wrap(hs.GetPluginDashboards)) - pluginRoute.Post("/:pluginId/settings", bind(models.UpdatePluginSettingCmd{}), routing.Wrap(UpdatePluginSetting)) + pluginRoute.Post("/:pluginId/settings", bind(models.UpdatePluginSettingCmd{}), routing.Wrap(hs.UpdatePluginSetting)) pluginRoute.Get("/:pluginId/metrics", routing.Wrap(hs.CollectPluginMetrics)) }, reqOrgAdmin) @@ -447,6 +447,7 @@ func (hs *HTTPServer) registerRoutes() { r.Delete("/api/snapshots/:key", reqEditorRole, routing.Wrap(DeleteDashboardSnapshot)) // Frontend logs - sourceMapStore := frontendlogging.NewSourceMapStore(hs.Cfg, frontendlogging.ReadSourceMapFromFS) - r.Post("/log", middleware.RateLimit(hs.Cfg.Sentry.EndpointRPS, hs.Cfg.Sentry.EndpointBurst, time.Now), bind(frontendlogging.FrontendSentryEvent{}), routing.Wrap(NewFrontendLogMessageHandler(sourceMapStore))) + sourceMapStore := frontendlogging.NewSourceMapStore(hs.Cfg, hs.PluginManager, frontendlogging.ReadSourceMapFromFS) + r.Post("/log", middleware.RateLimit(hs.Cfg.Sentry.EndpointRPS, hs.Cfg.Sentry.EndpointBurst, time.Now), + bind(frontendlogging.FrontendSentryEvent{}), routing.Wrap(NewFrontendLogMessageHandler(sourceMapStore))) } diff --git a/pkg/api/app_routes.go b/pkg/api/app_routes.go index 130c9baf580..21478a19aa9 100644 --- a/pkg/api/app_routes.go +++ b/pkg/api/app_routes.go @@ -11,7 +11,6 @@ import ( "github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" - "github.com/grafana/grafana/pkg/plugins/manager" "github.com/grafana/grafana/pkg/util" macaron "gopkg.in/macaron.v1" ) @@ -32,7 +31,7 @@ func (hs *HTTPServer) initAppPluginRoutes(r *macaron.Macaron) { TLSHandshakeTimeout: 10 * time.Second, } - for _, plugin := range manager.Apps { + for _, plugin := range hs.PluginManager.Apps() { for _, route := range plugin.Routes { url := util.JoinURLFragments("/api/plugin-proxy/"+plugin.Id, route.Path) handlers := make([]macaron.Handler, 0) diff --git a/pkg/api/fakes.go b/pkg/api/fakes.go index a62be9409fe..705ec6e3f05 100644 --- a/pkg/api/fakes.go +++ b/pkg/api/fakes.go @@ -4,6 +4,8 @@ import "github.com/grafana/grafana/pkg/plugins" type fakePluginManager struct { plugins.Manager + + staticRoutes []*plugins.PluginStaticRoute } func (pm *fakePluginManager) GetPlugin(id string) *plugins.PluginBase { @@ -17,3 +19,7 @@ func (pm *fakePluginManager) GetDataSource(id string) *plugins.DataSourcePlugin func (pm *fakePluginManager) Renderer() *plugins.RendererPlugin { return nil } + +func (pm *fakePluginManager) StaticRoutes() []*plugins.PluginStaticRoute { + return pm.staticRoutes +} diff --git a/pkg/api/frontend_logging_test.go b/pkg/api/frontend_logging_test.go index 53194d2732f..2dfcee56052 100644 --- a/pkg/api/frontend_logging_test.go +++ b/pkg/api/frontend_logging_test.go @@ -16,7 +16,6 @@ import ( "github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" - "github.com/grafana/grafana/pkg/plugins/manager" "github.com/grafana/grafana/pkg/setting" log "github.com/inconshreveable/log15" @@ -71,7 +70,17 @@ func logSentryEventScenario(t *testing.T, desc string, event frontendlogging.Fro return nil, os.ErrNotExist } - sourceMapStore := frontendlogging.NewSourceMapStore(cfg, readSourceMap) + // fake plugin route so we will try to find a source map there + pm := fakePluginManager{ + staticRoutes: []*plugins.PluginStaticRoute{ + { + Directory: "/usr/local/telepathic-panel", + PluginId: "telepathic", + }, + }, + } + + sourceMapStore := frontendlogging.NewSourceMapStore(cfg, &pm, readSourceMap) loggingHandler := NewFrontendLogMessageHandler(sourceMapStore) @@ -90,12 +99,6 @@ func TestFrontendLoggingEndpoint(t *testing.T) { ts, err := time.Parse("2006-01-02T15:04:05.000Z", "2020-10-22T06:29:29.078Z") require.NoError(t, err) - // fake plugin route so we will try to find a source map there. I can't believe I can do this - manager.StaticRoutes = append(manager.StaticRoutes, &plugins.PluginStaticRoute{ - Directory: "/usr/local/telepathic-panel", - PluginId: "telepathic", - }) - t.Run("FrontendLoggingEndpoint", func(t *testing.T) { request := sentry.Request{ URL: "http://localhost:3000/", @@ -144,19 +147,20 @@ func TestFrontendLoggingEndpoint(t *testing.T) { }, } - logSentryEventScenario(t, "Should log received error event", errorEvent, func(sc *scenarioContext, logs []*log.Record, sourceMapReads []SourceMapReadRecord) { - assert.Equal(t, 200, sc.resp.Code) - assert.Len(t, logs, 1) - assertContextContains(t, logs[0], "logger", "frontend") - assertContextContains(t, logs[0], "url", errorEvent.Request.URL) - assertContextContains(t, logs[0], "user_agent", errorEvent.Request.Headers["User-Agent"]) - assertContextContains(t, logs[0], "event_id", errorEvent.EventID) - assertContextContains(t, logs[0], "original_timestamp", errorEvent.Timestamp) - assertContextContains(t, logs[0], "stacktrace", `UserError: Please replace user and try again + logSentryEventScenario(t, "Should log received error event", errorEvent, + func(sc *scenarioContext, logs []*log.Record, sourceMapReads []SourceMapReadRecord) { + assert.Equal(t, 200, sc.resp.Code) + assert.Len(t, logs, 1) + assertContextContains(t, logs[0], "logger", "frontend") + assertContextContains(t, logs[0], "url", errorEvent.Request.URL) + assertContextContains(t, logs[0], "user_agent", errorEvent.Request.Headers["User-Agent"]) + assertContextContains(t, logs[0], "event_id", errorEvent.EventID) + assertContextContains(t, logs[0], "original_timestamp", errorEvent.Timestamp) + assertContextContains(t, logs[0], "stacktrace", `UserError: Please replace user and try again at foofn (foo.js:123:23) at barfn (bar.js:113:231)`) - assert.NotContains(t, logs[0].Ctx, "context") - }) + assert.NotContains(t, logs[0].Ctx, "context") + }) messageEvent := frontendlogging.FrontendSentryEvent{ Event: &sentry.Event{ @@ -170,21 +174,22 @@ func TestFrontendLoggingEndpoint(t *testing.T) { Exception: nil, } - logSentryEventScenario(t, "Should log received message event", messageEvent, func(sc *scenarioContext, logs []*log.Record, sourceMapReads []SourceMapReadRecord) { - assert.Equal(t, 200, sc.resp.Code) - assert.Len(t, logs, 1) - assert.Equal(t, "hello world", logs[0].Msg) - assert.Equal(t, log.LvlInfo, logs[0].Lvl) - assertContextContains(t, logs[0], "logger", "frontend") - assertContextContains(t, logs[0], "url", messageEvent.Request.URL) - assertContextContains(t, logs[0], "user_agent", messageEvent.Request.Headers["User-Agent"]) - assertContextContains(t, logs[0], "event_id", messageEvent.EventID) - assertContextContains(t, logs[0], "original_timestamp", messageEvent.Timestamp) - assert.NotContains(t, logs[0].Ctx, "stacktrace") - assert.NotContains(t, logs[0].Ctx, "context") - assertContextContains(t, logs[0], "user_email", user.Email) - assertContextContains(t, logs[0], "user_id", user.ID) - }) + logSentryEventScenario(t, "Should log received message event", messageEvent, + func(sc *scenarioContext, logs []*log.Record, sourceMapReads []SourceMapReadRecord) { + assert.Equal(t, 200, sc.resp.Code) + assert.Len(t, logs, 1) + assert.Equal(t, "hello world", logs[0].Msg) + assert.Equal(t, log.LvlInfo, logs[0].Lvl) + assertContextContains(t, logs[0], "logger", "frontend") + assertContextContains(t, logs[0], "url", messageEvent.Request.URL) + assertContextContains(t, logs[0], "user_agent", messageEvent.Request.Headers["User-Agent"]) + assertContextContains(t, logs[0], "event_id", messageEvent.EventID) + assertContextContains(t, logs[0], "original_timestamp", messageEvent.Timestamp) + assert.NotContains(t, logs[0].Ctx, "stacktrace") + assert.NotContains(t, logs[0].Ctx, "context") + assertContextContains(t, logs[0], "user_email", user.Email) + assertContextContains(t, logs[0], "user_id", user.ID) + }) eventWithContext := frontendlogging.FrontendSentryEvent{ Event: &sentry.Event{ @@ -205,13 +210,14 @@ func TestFrontendLoggingEndpoint(t *testing.T) { Exception: nil, } - logSentryEventScenario(t, "Should log event context", eventWithContext, func(sc *scenarioContext, logs []*log.Record, sourceMapReads []SourceMapReadRecord) { - assert.Equal(t, 200, sc.resp.Code) - assert.Len(t, logs, 1) - assertContextContains(t, logs[0], "context_foo_one", "two") - assertContextContains(t, logs[0], "context_foo_three", "4") - assertContextContains(t, logs[0], "context_bar", "baz") - }) + logSentryEventScenario(t, "Should log event context", eventWithContext, + func(sc *scenarioContext, logs []*log.Record, sourceMapReads []SourceMapReadRecord) { + assert.Equal(t, 200, sc.resp.Code) + assert.Len(t, logs, 1) + assertContextContains(t, logs[0], "context_foo_one", "two") + assertContextContains(t, logs[0], "context_foo_three", "4") + assertContextContains(t, logs[0], "context_bar", "baz") + }) errorEventForSourceMapping := frontendlogging.FrontendSentryEvent{ Event: &event, @@ -271,10 +277,11 @@ func TestFrontendLoggingEndpoint(t *testing.T) { }, } - logSentryEventScenario(t, "Should load sourcemap and transform stacktrace line when possible", errorEventForSourceMapping, func(sc *scenarioContext, logs []*log.Record, sourceMapReads []SourceMapReadRecord) { - assert.Equal(t, 200, sc.resp.Code) - assert.Len(t, logs, 1) - assertContextContains(t, logs[0], "stacktrace", `UserError: Please replace user and try again + logSentryEventScenario(t, "Should load sourcemap and transform stacktrace line when possible", + errorEventForSourceMapping, func(sc *scenarioContext, logs []*log.Record, sourceMapReads []SourceMapReadRecord) { + assert.Equal(t, 200, sc.resp.Code) + assert.Len(t, logs, 1) + assertContextContains(t, logs[0], "stacktrace", `UserError: Please replace user and try again at ? (core|webpack:///./some_source.ts:2:2) at ? (telepathic|webpack:///./some_source.ts:3:2) at explode (http://localhost:3000/public/build/error.js:3:10) @@ -282,20 +289,20 @@ func TestFrontendLoggingEndpoint(t *testing.T) { at nope (http://localhost:3000/baz.js:3:10) at fake (http://localhost:3000/public/build/../../secrets.txt:3:10) at ? (core|webpack:///./some_source.ts:3:2)`) - assert.Len(t, sourceMapReads, 6) - assert.Equal(t, "/staticroot", sourceMapReads[0].dir) - assert.Equal(t, "build/moo/foo.js.map", sourceMapReads[0].path) - assert.Equal(t, "/usr/local/telepathic-panel", sourceMapReads[1].dir) - assert.Equal(t, "/foo.js.map", sourceMapReads[1].path) - assert.Equal(t, "/staticroot", sourceMapReads[2].dir) - assert.Equal(t, "build/error.js.map", sourceMapReads[2].path) - assert.Equal(t, "/staticroot", sourceMapReads[3].dir) - assert.Equal(t, "build/bar.js.map", sourceMapReads[3].path) - assert.Equal(t, "/staticroot", sourceMapReads[4].dir) - assert.Equal(t, "secrets.txt.map", sourceMapReads[4].path) - assert.Equal(t, "/staticroot", sourceMapReads[5].dir) - assert.Equal(t, "build/foo.js.map", sourceMapReads[5].path) - }) + assert.Len(t, sourceMapReads, 6) + assert.Equal(t, "/staticroot", sourceMapReads[0].dir) + assert.Equal(t, "build/moo/foo.js.map", sourceMapReads[0].path) + assert.Equal(t, "/usr/local/telepathic-panel", sourceMapReads[1].dir) + assert.Equal(t, "/foo.js.map", sourceMapReads[1].path) + assert.Equal(t, "/staticroot", sourceMapReads[2].dir) + assert.Equal(t, "build/error.js.map", sourceMapReads[2].path) + assert.Equal(t, "/staticroot", sourceMapReads[3].dir) + assert.Equal(t, "build/bar.js.map", sourceMapReads[3].path) + assert.Equal(t, "/staticroot", sourceMapReads[4].dir) + assert.Equal(t, "secrets.txt.map", sourceMapReads[4].path) + assert.Equal(t, "/staticroot", sourceMapReads[5].dir) + assert.Equal(t, "build/foo.js.map", sourceMapReads[5].path) + }) }) } diff --git a/pkg/api/frontendlogging/source_maps.go b/pkg/api/frontendlogging/source_maps.go index 88ae3615be5..5f8b3a5ac63 100644 --- a/pkg/api/frontendlogging/source_maps.go +++ b/pkg/api/frontendlogging/source_maps.go @@ -12,7 +12,7 @@ import ( sourcemap "github.com/go-sourcemap/sourcemap" "github.com/getsentry/sentry-go" - "github.com/grafana/grafana/pkg/plugins/manager" + "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/setting" ) @@ -47,12 +47,14 @@ type SourceMapStore struct { cache map[string]*sourceMap cfg *setting.Cfg readSourceMap ReadSourceMapFn + pluginManager plugins.Manager } -func NewSourceMapStore(cfg *setting.Cfg, readSourceMap ReadSourceMapFn) *SourceMapStore { +func NewSourceMapStore(cfg *setting.Cfg, pluginManager plugins.Manager, readSourceMap ReadSourceMapFn) *SourceMapStore { return &SourceMapStore{ cache: make(map[string]*sourceMap), cfg: cfg, + pluginManager: pluginManager, readSourceMap: readSourceMap, } } @@ -69,7 +71,8 @@ func (store *SourceMapStore) guessSourceMapLocation(sourceURL string) (*sourceMa } // determine if source comes from grafana core, locally or CDN, look in public build dir on fs - if strings.HasPrefix(u.Path, "/public/build/") || (store.cfg.CDNRootURL != nil && strings.HasPrefix(sourceURL, store.cfg.CDNRootURL.String()) && strings.Contains(u.Path, "/public/build/")) { + if strings.HasPrefix(u.Path, "/public/build/") || (store.cfg.CDNRootURL != nil && + strings.HasPrefix(sourceURL, store.cfg.CDNRootURL.String()) && strings.Contains(u.Path, "/public/build/")) { pathParts := strings.SplitN(u.Path, "/public/build/", 2) if len(pathParts) == 2 { return &sourceMapLocation{ @@ -80,7 +83,7 @@ func (store *SourceMapStore) guessSourceMapLocation(sourceURL string) (*sourceMa } // if source comes from a plugin, look in plugin dir } else if strings.HasPrefix(u.Path, "/public/plugins/") { - for _, route := range manager.StaticRoutes { + for _, route := range store.pluginManager.StaticRoutes() { pluginPrefix := filepath.Join("/public/plugins/", route.PluginId) if strings.HasPrefix(u.Path, pluginPrefix) { return &sourceMapLocation{ diff --git a/pkg/api/http_server.go b/pkg/api/http_server.go index 916deaf29a1..ce14680964d 100644 --- a/pkg/api/http_server.go +++ b/pkg/api/http_server.go @@ -32,7 +32,6 @@ import ( "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins/backendplugin" _ "github.com/grafana/grafana/pkg/plugins/backendplugin/manager" - "github.com/grafana/grafana/pkg/plugins/manager" "github.com/grafana/grafana/pkg/plugins/plugindashboards" "github.com/grafana/grafana/pkg/registry" "github.com/grafana/grafana/pkg/services/contexthandler" @@ -321,7 +320,7 @@ func (hs *HTTPServer) addMiddlewaresAndStaticRoutes() { m.Use(middleware.Recovery(hs.Cfg)) - for _, route := range manager.StaticRoutes { + for _, route := range hs.PluginManager.StaticRoutes() { pluginRoute := path.Join("/public/plugins/", route.PluginId) hs.log.Debug("Plugins: Adding route", "route", pluginRoute, "dir", route.Directory) hs.mapStatic(m, route.Directory, "", pluginRoute) diff --git a/pkg/api/plugins.go b/pkg/api/plugins.go index 63f10387143..7b48eddfc7e 100644 --- a/pkg/api/plugins.go +++ b/pkg/api/plugins.go @@ -16,7 +16,6 @@ import ( "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins/adapters" "github.com/grafana/grafana/pkg/plugins/backendplugin" - "github.com/grafana/grafana/pkg/plugins/manager" "github.com/grafana/grafana/pkg/util/errutil" ) @@ -168,7 +167,7 @@ func (hs *HTTPServer) GetPluginSettingByID(c *models.ReqContext) response.Respon SignatureOrg: def.SignatureOrg, } - if app, ok := manager.Apps[def.Id]; ok { + if app := hs.PluginManager.GetApp(def.Id); app != nil { dto.Enabled = app.AutoEnabled dto.Pinned = app.AutoEnabled } @@ -187,16 +186,15 @@ func (hs *HTTPServer) GetPluginSettingByID(c *models.ReqContext) response.Respon return response.JSON(200, dto) } -func UpdatePluginSetting(c *models.ReqContext, cmd models.UpdatePluginSettingCmd) response.Response { +func (hs *HTTPServer) UpdatePluginSetting(c *models.ReqContext, cmd models.UpdatePluginSettingCmd) response.Response { pluginID := c.Params(":pluginId") - cmd.OrgId = c.OrgId - cmd.PluginId = pluginID - - if _, ok := manager.Apps[cmd.PluginId]; !ok { - return response.Error(404, "Plugin not installed.", nil) + if app := hs.PluginManager.GetApp(pluginID); app == nil { + return response.Error(404, "Plugin not installed", nil) } + cmd.OrgId = c.OrgId + cmd.PluginId = pluginID if err := bus.Dispatch(&cmd); err != nil { return response.Error(500, "Failed to update plugin setting", err) } diff --git a/pkg/infra/usagestats/usage_stats.go b/pkg/infra/usagestats/usage_stats.go index a22da57eb56..a0ed8951110 100644 --- a/pkg/infra/usagestats/usage_stats.go +++ b/pkg/infra/usagestats/usage_stats.go @@ -12,7 +12,6 @@ import ( "github.com/grafana/grafana/pkg/infra/metrics" "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/plugins/manager" ) var usageStatsURL = "https://stats.grafana.org/grafana-usage-report" @@ -56,8 +55,8 @@ func (uss *UsageStatsService) GetUsageReport(ctx context.Context) (UsageReport, metrics["stats.users.count"] = statsQuery.Result.Users metrics["stats.orgs.count"] = statsQuery.Result.Orgs metrics["stats.playlist.count"] = statsQuery.Result.Playlists - metrics["stats.plugins.apps.count"] = len(manager.Apps) - metrics["stats.plugins.panels.count"] = len(manager.Panels) + metrics["stats.plugins.apps.count"] = uss.PluginManager.AppCount() + metrics["stats.plugins.panels.count"] = uss.PluginManager.PanelCount() metrics["stats.plugins.datasources.count"] = uss.PluginManager.DataSourceCount() metrics["stats.alerts.count"] = statsQuery.Result.Alerts metrics["stats.active_users.count"] = statsQuery.Result.ActiveUsers diff --git a/pkg/infra/usagestats/usage_stats_test.go b/pkg/infra/usagestats/usage_stats_test.go index 2df3829e012..5b4798ca4a0 100644 --- a/pkg/infra/usagestats/usage_stats_test.go +++ b/pkg/infra/usagestats/usage_stats_test.go @@ -294,8 +294,8 @@ func TestMetrics(t *testing.T) { assert.Equal(t, getSystemStatsQuery.Result.Users, metrics.Get("stats.users.count").MustInt64()) assert.Equal(t, getSystemStatsQuery.Result.Orgs, metrics.Get("stats.orgs.count").MustInt64()) assert.Equal(t, getSystemStatsQuery.Result.Playlists, metrics.Get("stats.playlist.count").MustInt64()) - assert.Equal(t, len(manager.Apps), metrics.Get("stats.plugins.apps.count").MustInt()) - assert.Equal(t, len(manager.Panels), metrics.Get("stats.plugins.panels.count").MustInt()) + assert.Equal(t, uss.PluginManager.AppCount(), metrics.Get("stats.plugins.apps.count").MustInt()) + assert.Equal(t, uss.PluginManager.PanelCount(), metrics.Get("stats.plugins.panels.count").MustInt()) assert.Equal(t, uss.PluginManager.DataSourceCount(), metrics.Get("stats.plugins.datasources.count").MustInt()) assert.Equal(t, getSystemStatsQuery.Result.Alerts, metrics.Get("stats.alerts.count").MustInt64()) assert.Equal(t, getSystemStatsQuery.Result.ActiveUsers, metrics.Get("stats.active_users.count").MustInt64()) @@ -546,6 +546,7 @@ type fakePluginManager struct { manager.PluginManager dataSources map[string]*plugins.DataSourcePlugin + panels map[string]*plugins.PanelPlugin } func (pm fakePluginManager) DataSourceCount() int { @@ -556,6 +557,10 @@ func (pm fakePluginManager) GetDataSource(id string) *plugins.DataSourcePlugin { return pm.dataSources[id] } +func (pm fakePluginManager) PanelCount() int { + return len(pm.panels) +} + func setupSomeDataSourcePlugins(t *testing.T, uss *UsageStatsService) { t.Helper() diff --git a/pkg/plugins/ifaces.go b/pkg/plugins/ifaces.go index 72e03246f88..c268724dd78 100644 --- a/pkg/plugins/ifaces.go +++ b/pkg/plugins/ifaces.go @@ -17,10 +17,19 @@ type Manager interface { GetDataPlugin(id string) DataPlugin // GetPlugin gets a plugin with a certain ID. GetPlugin(id string) *PluginBase + // GetApp gets an app plugin with a certain ID. + GetApp(id string) *AppPlugin // DataSourceCount gets the number of data sources. DataSourceCount() int // DataSources gets all data sources. DataSources() []*DataSourcePlugin + // Apps gets all app plugins. + Apps() []*AppPlugin + // PanelCount gets the number of panels. + PanelCount() int + // AppCount gets the number of apps. + AppCount() int + // GetEnabledPlugins gets enabled plugins. // GetEnabledPlugins gets enabled plugins. GetEnabledPlugins(orgID int64) (*EnabledPlugins, error) // GrafanaLatestVersion gets the latest Grafana version. @@ -29,6 +38,8 @@ type Manager interface { GrafanaHasUpdate() bool // Plugins gets all plugins. Plugins() []*PluginBase + // StaticRoutes gets all static routes. + StaticRoutes() []*PluginStaticRoute // GetPluginSettings gets settings for a certain plugin. GetPluginSettings(orgID int64) (map[string]*models.PluginSettingInfoDTO, error) // GetPluginDashboards gets dashboards for a certain org/plugin. @@ -41,6 +52,10 @@ type Manager interface { requestHandler DataRequestHandler) (PluginDashboardInfoDTO, error) // ScanningErrors returns plugin scanning errors encountered. ScanningErrors() []PluginError + // LoadPluginDashboard loads a plugin dashboard. + LoadPluginDashboard(pluginID, path string) (*models.Dashboard, error) + // IsAppInstalled returns whether an app is installed. + IsAppInstalled(id string) bool } type ImportDashboardInput struct { diff --git a/pkg/plugins/manager/dashboard_import_test.go b/pkg/plugins/manager/dashboard_import_test.go index 0dafdec0df1..a9983fe0a48 100644 --- a/pkg/plugins/manager/dashboard_import_test.go +++ b/pkg/plugins/manager/dashboard_import_test.go @@ -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) diff --git a/pkg/plugins/manager/dashboards.go b/pkg/plugins/manager/dashboards.go index 3829abd7943..4c35284f82b 100644 --- a/pkg/plugins/manager/dashboards.go +++ b/pkg/plugins/manager/dashboards.go @@ -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) diff --git a/pkg/plugins/manager/dashboards_test.go b/pkg/plugins/manager/dashboards_test.go index af04365ce55..ccd780641bb 100644 --- a/pkg/plugins/manager/dashboards_test.go +++ b/pkg/plugins/manager/dashboards_test.go @@ -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() + 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 } - 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 - }) + return models.ErrDashboardNotFound + }) - bus.AddHandler("test", func(query *models.GetDashboardsByPluginIdQuery) error { - var data = simplejson.New() - data.Set("title", "Nginx Connections") - data.Set("revision", 22) + 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) + query.Result = []*models.Dashboard{ + {Slug: "nginx-connections", Data: data}, + } + return nil + }) - Convey("should return 2 dashboards", func() { - So(len(dashboards), ShouldEqual, 2) - }) + dashboards, err := pm.GetPluginDashboards(1, "test-app") + require.NoError(t, err) - 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") + 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) - So(dashboards[1].Revision, ShouldEqual, 2) - So(dashboards[1].ImportedRevision, ShouldEqual, 0) - }) - }) + assert.Equal(t, int64(2), dashboards[1].Revision) + assert.Equal(t, int64(0), dashboards[1].ImportedRevision) } diff --git a/pkg/plugins/manager/manager.go b/pkg/plugins/manager/manager.go index 07fbd7e5456..2ca996558eb 100644 --- a/pkg/plugins/manager/manager.go +++ b/pkg/plugins/manager/manager.go @@ -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(®istry.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 +} diff --git a/pkg/plugins/manager/manager_test.go b/pkg/plugins/manager/manager_test.go index c1e10a08b37..7082eb1e30e 100644 --- a/pkg/plugins/manager/manager_test.go +++ b/pkg/plugins/manager/manager_test.go @@ -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) } diff --git a/pkg/plugins/manager/queries.go b/pkg/plugins/manager/queries.go index b4d70666273..630dc56563f 100644 --- a/pkg/plugins/manager/queries.go +++ b/pkg/plugins/manager/queries.go @@ -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 } diff --git a/pkg/plugins/plugindashboards/service.go b/pkg/plugins/plugindashboards/service.go index 2d1f8a3c65d..ef8573b14b4 100644 --- a/pkg/plugins/plugindashboards/service.go +++ b/pkg/plugins/plugindashboards/service.go @@ -7,7 +7,6 @@ import ( "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" - "github.com/grafana/grafana/pkg/plugins/manager" "github.com/grafana/grafana/pkg/registry" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/tsdb" @@ -21,9 +20,9 @@ func init() { } type Service struct { - DataService *tsdb.Service `inject:""` - PluginManager *manager.PluginManager `inject:""` - SQLStore *sqlstore.SQLStore `inject:""` + DataService *tsdb.Service `inject:""` + PluginManager plugins.Manager `inject:""` + SQLStore *sqlstore.SQLStore `inject:""` logger log.Logger } diff --git a/pkg/server/server.go b/pkg/server/server.go index 099c102bc2b..e9fdf261fa6 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -29,7 +29,7 @@ import ( "github.com/grafana/grafana/pkg/login" "github.com/grafana/grafana/pkg/login/social" "github.com/grafana/grafana/pkg/middleware" - _ "github.com/grafana/grafana/pkg/plugins" + _ "github.com/grafana/grafana/pkg/plugins/manager" "github.com/grafana/grafana/pkg/registry" _ "github.com/grafana/grafana/pkg/services/alerting" _ "github.com/grafana/grafana/pkg/services/auth" diff --git a/pkg/services/provisioning/plugins/config_reader.go b/pkg/services/provisioning/plugins/config_reader.go index 69f07bfcbd4..7931f3d5111 100644 --- a/pkg/services/provisioning/plugins/config_reader.go +++ b/pkg/services/provisioning/plugins/config_reader.go @@ -8,7 +8,7 @@ import ( "strings" "github.com/grafana/grafana/pkg/infra/log" - "github.com/grafana/grafana/pkg/plugins/manager" + "github.com/grafana/grafana/pkg/plugins" "gopkg.in/yaml.v2" ) @@ -17,11 +17,12 @@ type configReader interface { } type configReaderImpl struct { - log log.Logger + log log.Logger + pluginManager plugins.Manager } -func newConfigReader(logger log.Logger) configReader { - return &configReaderImpl{log: logger} +func newConfigReader(logger log.Logger, pluginManager plugins.Manager) configReader { + return &configReaderImpl{log: logger, pluginManager: pluginManager} } func (cr *configReaderImpl) readConfig(path string) ([]*pluginsAsConfig, error) { @@ -112,8 +113,8 @@ func (cr *configReaderImpl) validatePluginsConfig(apps []*pluginsAsConfig) error } for _, app := range apps[i].Apps { - if !manager.IsAppInstalled(app.PluginID) { - return fmt.Errorf("app plugin not installed: %s", app.PluginID) + if !cr.pluginManager.IsAppInstalled(app.PluginID) { + return fmt.Errorf("app plugin not installed: %q", app.PluginID) } } } diff --git a/pkg/services/provisioning/plugins/config_reader_test.go b/pkg/services/provisioning/plugins/config_reader_test.go index fef840d5734..eaf6f4b0d78 100644 --- a/pkg/services/provisioning/plugins/config_reader_test.go +++ b/pkg/services/provisioning/plugins/config_reader_test.go @@ -6,7 +6,6 @@ import ( "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/plugins" - "github.com/grafana/grafana/pkg/plugins/manager" "github.com/stretchr/testify/require" ) @@ -20,36 +19,38 @@ const ( func TestConfigReader(t *testing.T) { t.Run("Broken yaml should return error", func(t *testing.T) { - reader := newConfigReader(log.New("test logger")) + reader := newConfigReader(log.New("test logger"), nil) _, err := reader.readConfig(brokenYaml) require.Error(t, err) }) t.Run("Skip invalid directory", func(t *testing.T) { - cfgProvider := newConfigReader(log.New("test logger")) + cfgProvider := newConfigReader(log.New("test logger"), nil) cfg, err := cfgProvider.readConfig(emptyFolder) require.NoError(t, err) require.Len(t, cfg, 0) }) t.Run("Unknown app plugin should return error", func(t *testing.T) { - cfgProvider := newConfigReader(log.New("test logger")) + cfgProvider := newConfigReader(log.New("test logger"), fakePluginManager{}) _, err := cfgProvider.readConfig(unknownApp) require.Error(t, err) - require.Equal(t, "app plugin not installed: nonexisting", err.Error()) + require.Equal(t, "app plugin not installed: \"nonexisting\"", err.Error()) }) t.Run("Read incorrect properties", func(t *testing.T) { - cfgProvider := newConfigReader(log.New("test logger")) + cfgProvider := newConfigReader(log.New("test logger"), nil) _, err := cfgProvider.readConfig(incorrectSettings) require.Error(t, err) require.Equal(t, "app item 1 in configuration doesn't contain required field type", err.Error()) }) t.Run("Can read correct properties", func(t *testing.T) { - manager.Apps = map[string]*plugins.AppPlugin{ - "test-plugin": {}, - "test-plugin-2": {}, + pm := fakePluginManager{ + apps: map[string]*plugins.AppPlugin{ + "test-plugin": {}, + "test-plugin-2": {}, + }, } err := os.Setenv("ENABLE_PLUGIN_VAR", "test-plugin") @@ -58,7 +59,7 @@ func TestConfigReader(t *testing.T) { _ = os.Unsetenv("ENABLE_PLUGIN_VAR") }) - cfgProvider := newConfigReader(log.New("test logger")) + cfgProvider := newConfigReader(log.New("test logger"), pm) cfg, err := cfgProvider.readConfig(correctProperties) require.NoError(t, err) require.Len(t, cfg, 1) @@ -85,3 +86,14 @@ func TestConfigReader(t *testing.T) { } }) } + +type fakePluginManager struct { + plugins.Manager + + apps map[string]*plugins.AppPlugin +} + +func (pm fakePluginManager) IsAppInstalled(id string) bool { + _, exists := pm.apps[id] + return exists +} diff --git a/pkg/services/provisioning/plugins/plugin_provisioner.go b/pkg/services/provisioning/plugins/plugin_provisioner.go index 2c2327da688..1f26c6499d2 100644 --- a/pkg/services/provisioning/plugins/plugin_provisioner.go +++ b/pkg/services/provisioning/plugins/plugin_provisioner.go @@ -6,12 +6,17 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/plugins" ) // Provision scans a directory for provisioning config files // and provisions the app in those files. -func Provision(configDirectory string) error { - ap := newAppProvisioner(log.New("provisioning.plugins")) +func Provision(configDirectory string, pluginManager plugins.Manager) error { + logger := log.New("provisioning.plugins") + ap := PluginProvisioner{ + log: logger, + cfgProvider: newConfigReader(logger, pluginManager), + } return ap.applyChanges(configDirectory) } @@ -22,13 +27,6 @@ type PluginProvisioner struct { cfgProvider configReader } -func newAppProvisioner(log log.Logger) PluginProvisioner { - return PluginProvisioner{ - log: log, - cfgProvider: newConfigReader(log), - } -} - func (ap *PluginProvisioner) apply(cfg *pluginsAsConfig) error { for _, app := range cfg.Apps { if app.OrgID == 0 && app.OrgName != "" { diff --git a/pkg/services/provisioning/provisioning.go b/pkg/services/provisioning/provisioning.go index 61a96507149..a3b373b1a0d 100644 --- a/pkg/services/provisioning/provisioning.go +++ b/pkg/services/provisioning/provisioning.go @@ -43,7 +43,7 @@ func newProvisioningServiceImpl( newDashboardProvisioner dashboards.DashboardProvisionerFactory, provisionNotifiers func(string) error, provisionDatasources func(string) error, - provisionPlugins func(string) error, + provisionPlugins func(string, plugifaces.Manager) error, ) *provisioningServiceImpl { return &provisioningServiceImpl{ log: log.New("provisioning"), @@ -58,13 +58,14 @@ type provisioningServiceImpl struct { Cfg *setting.Cfg `inject:""` RequestHandler plugifaces.DataRequestHandler `inject:""` SQLStore *sqlstore.SQLStore `inject:""` + PluginManager plugifaces.Manager `inject:""` log log.Logger pollingCtxCancel context.CancelFunc newDashboardProvisioner dashboards.DashboardProvisionerFactory dashboardProvisioner dashboards.DashboardProvisioner provisionNotifiers func(string) error provisionDatasources func(string) error - provisionPlugins func(string) error + provisionPlugins func(string, plugifaces.Manager) error mutex sync.Mutex } @@ -124,7 +125,7 @@ func (ps *provisioningServiceImpl) ProvisionDatasources() error { func (ps *provisioningServiceImpl) ProvisionPlugins() error { appPath := filepath.Join(ps.Cfg.ProvisioningPath, "plugins") - err := ps.provisionPlugins(appPath) + err := ps.provisionPlugins(appPath, ps.PluginManager) return errutil.Wrap("app provisioning error", err) }