Plugins: Refactor creation of plugin context to dedicated service (#66451)

* first pass

* fix tests

* return errs

* change signature

* tidy

* delete unnecessary fields from test

* tidy

* fix tests

* simplify

* separate error check in API

* apply nits
This commit is contained in:
Will Browne
2023-06-08 13:59:51 +02:00
committed by GitHub
parent 49ad802ca6
commit 624777258b
25 changed files with 308 additions and 261 deletions
+8 -21
View File
@@ -20,7 +20,7 @@ import (
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/datasources/permissions"
"github.com/grafana/grafana/pkg/services/pluginsintegration/adapters"
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
@@ -811,23 +811,16 @@ func (hs *HTTPServer) CheckDatasourceHealth(c *contextmodel.ReqContext) response
}
func (hs *HTTPServer) checkDatasourceHealth(c *contextmodel.ReqContext, ds *datasources.DataSource) response.Response {
plugin, exists := hs.pluginStore.Plugin(c.Req.Context(), ds.Type)
if !exists {
return response.Error(http.StatusInternalServerError, "Unable to find datasource plugin", nil)
}
dsInstanceSettings, err := adapters.ModelToInstanceSettings(ds, hs.decryptSecureJsonDataFn(c.Req.Context()))
pCtx, err := hs.pluginContextProvider.GetWithDataSource(c.Req.Context(), ds.Type, c.SignedInUser, ds)
if err != nil {
return response.Error(http.StatusInternalServerError, "Unable to get datasource model", err)
if errors.Is(err, plugincontext.ErrPluginNotFound) {
return response.Error(http.StatusNotFound, "Unable to find datasource plugin", nil)
}
return response.Error(http.StatusInternalServerError, "Unable to get plugin context", err)
}
req := &backend.CheckHealthRequest{
PluginContext: backend.PluginContext{
User: adapters.BackendUserFromSignedInUser(c.SignedInUser),
OrgID: c.OrgID,
PluginID: plugin.ID,
DataSourceInstanceSettings: dsInstanceSettings,
},
Headers: map[string]string{},
PluginContext: pCtx,
Headers: map[string]string{},
}
var dsURL string
@@ -868,12 +861,6 @@ func (hs *HTTPServer) checkDatasourceHealth(c *contextmodel.ReqContext, ds *data
return response.JSON(http.StatusOK, payload)
}
func (hs *HTTPServer) decryptSecureJsonDataFn(ctx context.Context) func(ds *datasources.DataSource) (map[string]string, error) {
return func(ds *datasources.DataSource) (map[string]string, error) {
return hs.DataSourcesService.DecryptedValues(ctx, ds)
}
}
func (hs *HTTPServer) filterDatasourcesByQueryPermission(ctx context.Context, user *user.SignedInUser, ds []*datasources.DataSource) ([]*datasources.DataSource, error) {
query := datasources.DatasourcesPermissionFilterQuery{
User: user,
+2 -2
View File
@@ -111,7 +111,7 @@ type HTTPServer struct {
namedMiddlewares []routing.RegisterNamedMiddleware
bus bus.Bus
PluginContextProvider *plugincontext.Provider
pluginContextProvider *plugincontext.Provider
RouteRegister routing.RouteRegister
RenderService rendering.Service
Cfg *setting.Cfg
@@ -291,7 +291,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
SearchService: searchService,
Live: live,
LivePushGateway: livePushGateway,
PluginContextProvider: plugCtxProvider,
pluginContextProvider: plugCtxProvider,
ContextHandler: contextHandler,
LoggerMiddleware: loggerMiddleware,
AlertNG: alertNG,
+4
View File
@@ -12,6 +12,7 @@ import (
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
"github.com/grafana/grafana/pkg/web"
)
@@ -22,6 +23,9 @@ func (hs *HTTPServer) handleQueryMetricsError(err error) *response.NormalRespons
if errors.Is(err, datasources.ErrDataSourceNotFound) {
return response.Error(http.StatusNotFound, "Data source not found", err)
}
if errors.Is(err, plugincontext.ErrPluginNotFound) {
return response.Error(http.StatusNotFound, "Plugin not found", err)
}
var secretsPlugin datasources.ErrDatasourceSecretsPluginUserFriendly
if errors.As(err, &secretsPlugin) {
+40 -3
View File
@@ -14,6 +14,8 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/infra/db/dbtest"
"github.com/grafana/grafana/pkg/infra/localcache"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin"
"github.com/grafana/grafana/pkg/plugins/config"
@@ -22,8 +24,12 @@ import (
"github.com/grafana/grafana/pkg/services/datasources"
fakeDatasources "github.com/grafana/grafana/pkg/services/datasources/fakes"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings"
pluginSettings "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings/service"
"github.com/grafana/grafana/pkg/services/query"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
secretstest "github.com/grafana/grafana/pkg/services/secrets/fakes"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util/errutil"
@@ -50,7 +56,6 @@ func TestAPIEndpoint_Metrics_QueryMetricsV2(t *testing.T) {
nil,
nil,
&fakePluginRequestValidator{},
&fakeDatasources.FakeDataSourceService{},
&fakePluginClient{
QueryDataHandlerFunc: func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
resp := backend.Responses{
@@ -61,6 +66,17 @@ func TestAPIEndpoint_Metrics_QueryMetricsV2(t *testing.T) {
return &backend.QueryDataResponse{Responses: resp}, nil
},
},
plugincontext.ProvideService(localcache.ProvideService(), &plugins.FakePluginStore{
PluginList: []plugins.PluginDTO{
{
JSONData: plugins.JSONData{
ID: "grafana",
},
},
},
}, &fakeDatasources.FakeDataSourceService{}, pluginSettings.ProvideService(dbtest.NewFakeDB(),
secretstest.NewFakeSecretsService()),
),
)
serverFeatureEnabled := SetupAPITestServer(t, func(hs *HTTPServer) {
hs.queryDataService = qds
@@ -93,12 +109,25 @@ func TestAPIEndpoint_Metrics_QueryMetricsV2(t *testing.T) {
}
func TestAPIEndpoint_Metrics_PluginDecryptionFailure(t *testing.T) {
ds := &fakeDatasources.FakeDataSourceService{SimulatePluginFailure: true}
db := &dbtest.FakeDB{ExpectedError: pluginsettings.ErrPluginSettingNotFound}
pcp := plugincontext.ProvideService(localcache.ProvideService(),
&plugins.FakePluginStore{
PluginList: []plugins.PluginDTO{
{
JSONData: plugins.JSONData{
ID: "grafana",
},
},
},
},
ds, pluginSettings.ProvideService(db, secretstest.NewFakeSecretsService()),
)
qds := query.ProvideService(
setting.NewCfg(),
nil,
nil,
&fakePluginRequestValidator{},
&fakeDatasources.FakeDataSourceService{SimulatePluginFailure: true},
&fakePluginClient{
QueryDataHandlerFunc: func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
resp := backend.Responses{
@@ -109,10 +138,12 @@ func TestAPIEndpoint_Metrics_PluginDecryptionFailure(t *testing.T) {
return &backend.QueryDataResponse{Responses: resp}, nil
},
},
pcp,
)
httpServer := SetupAPITestServer(t, func(hs *HTTPServer) {
hs.queryDataService = qds
hs.QuotaService = quotatest.New(false, nil)
hs.pluginContextProvider = pcp
})
t.Run("Status code is 500 and a secrets plugin error is returned if there is a problem getting secrets from the remote plugin", func(t *testing.T) {
@@ -255,13 +286,19 @@ func TestDataSourceQueryError(t *testing.T) {
r := registry.NewInMemory()
err := r.Add(context.Background(), p)
require.NoError(t, err)
ds := &fakeDatasources.FakeDataSourceService{}
hs.queryDataService = query.ProvideService(
setting.NewCfg(),
&fakeDatasources.FakeCacheService{},
nil,
&fakePluginRequestValidator{},
&fakeDatasources.FakeDataSourceService{},
pluginClient.ProvideService(r, &config.Cfg{}),
plugincontext.ProvideService(localcache.ProvideService(), &plugins.FakePluginStore{
PluginList: []plugins.PluginDTO{p.ToDTO()},
},
ds, pluginSettings.ProvideService(dbtest.NewFakeDB(),
secretstest.NewFakeSecretsService()),
),
)
hs.QuotaService = quotatest.New(false, nil)
})
+11 -10
View File
@@ -13,6 +13,7 @@ import (
"github.com/grafana/grafana/pkg/plugins/httpresponsesender"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
"github.com/grafana/grafana/pkg/util/proxyutil"
"github.com/grafana/grafana/pkg/web"
)
@@ -25,15 +26,15 @@ func (hs *HTTPServer) CallResource(c *contextmodel.ReqContext) {
}
func (hs *HTTPServer) callPluginResource(c *contextmodel.ReqContext, pluginID string) {
pCtx, found, err := hs.PluginContextProvider.Get(c.Req.Context(), pluginID, c.SignedInUser)
pCtx, err := hs.pluginContextProvider.Get(c.Req.Context(), pluginID, c.SignedInUser)
if err != nil {
if errors.Is(err, plugincontext.ErrPluginNotFound) {
c.JsonApiErr(404, "Plugin not found", nil)
return
}
c.JsonApiErr(500, "Failed to get plugin settings", err)
return
}
if !found {
c.JsonApiErr(404, "Plugin not found", nil)
return
}
req, err := hs.pluginResourceRequest(c)
if err != nil {
@@ -47,15 +48,15 @@ func (hs *HTTPServer) callPluginResource(c *contextmodel.ReqContext, pluginID st
}
func (hs *HTTPServer) callPluginResourceWithDataSource(c *contextmodel.ReqContext, pluginID string, ds *datasources.DataSource) {
pCtx, found, err := hs.PluginContextProvider.GetWithDataSource(c.Req.Context(), pluginID, c.SignedInUser, ds)
pCtx, err := hs.pluginContextProvider.GetWithDataSource(c.Req.Context(), pluginID, c.SignedInUser, ds)
if err != nil {
if errors.Is(err, plugincontext.ErrPluginNotFound) {
c.JsonApiErr(404, "Plugin not found", nil)
return
}
c.JsonApiErr(500, "Failed to get plugin settings", err)
return
}
if !found {
c.JsonApiErr(404, "Plugin not found", nil)
return
}
var dsURL string
if pCtx.DataSourceInstanceSettings != nil {
+5 -3
View File
@@ -42,6 +42,7 @@ import (
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
pluginSettings "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings/service"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
fakeSecrets "github.com/grafana/grafana/pkg/services/secrets/fakes"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch"
@@ -74,11 +75,12 @@ func TestCallResource(t *testing.T) {
ps, err := store.ProvideService(reg, srcs, l)
require.NoError(t, err)
pcp := plugincontext.ProvideService(localcache.ProvideService(), ps, &datasources.FakeCacheService{}, &datasources.FakeDataSourceService{}, pluginSettings.ProvideService(db.InitTestDB(t), nil))
pcp := plugincontext.ProvideService(localcache.ProvideService(), ps, &datasources.FakeDataSourceService{},
pluginSettings.ProvideService(db.InitTestDB(t), fakeSecrets.NewFakeSecretsService()))
srv := SetupAPITestServer(t, func(hs *HTTPServer) {
hs.Cfg = cfg
hs.PluginContextProvider = pcp
hs.pluginContextProvider = pcp
hs.QuotaService = quotatest.New(false, nil)
hs.pluginStore = ps
hs.pluginClient = pluginClient.ProvideService(reg, pCfg)
@@ -117,7 +119,7 @@ func TestCallResource(t *testing.T) {
srv = SetupAPITestServer(t, func(hs *HTTPServer) {
hs.Cfg = cfg
hs.PluginContextProvider = pcp
hs.pluginContextProvider = pcp
hs.QuotaService = quotatest.New(false, nil)
hs.pluginStore = ps
hs.pluginClient = pc
+6 -7
View File
@@ -27,6 +27,7 @@ import (
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginaccesscontrol"
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
@@ -259,7 +260,7 @@ func (hs *HTTPServer) UpdatePluginSetting(c *contextmodel.ReqContext) response.R
return response.Error(500, "Failed to update plugin setting", err)
}
hs.PluginContextProvider.InvalidateSettingsCache(c.Req.Context(), pluginID)
hs.pluginContextProvider.InvalidateSettingsCache(c.Req.Context(), pluginID)
return response.Success("Plugin settings updated")
}
@@ -392,15 +393,13 @@ func (hs *HTTPServer) redirectCDNPluginAsset(c *contextmodel.ReqContext, plugin
// /api/plugins/:pluginId/health
func (hs *HTTPServer) CheckHealth(c *contextmodel.ReqContext) response.Response {
pluginID := web.Params(c.Req)[":pluginId"]
pCtx, found, err := hs.PluginContextProvider.Get(c.Req.Context(), pluginID, c.SignedInUser)
pCtx, err := hs.pluginContextProvider.Get(c.Req.Context(), pluginID, c.SignedInUser)
if err != nil {
if errors.Is(err, plugincontext.ErrPluginNotFound) {
return response.Error(404, "Plugin not found", nil)
}
return response.Error(500, "Failed to get plugin settings", err)
}
if !found {
return response.Error(404, "Plugin not found", nil)
}
resp, err := hs.pluginClient.CheckHealth(c.Req.Context(), &backend.CheckHealthRequest{
PluginContext: pCtx,
Headers: map[string]string{},