Chore: Refactor backend plugin manager/tsdb query data (#34944)
Move QueryData method into backend plugin manager which HandleRequest uses to query data from plugin SDK supported data sources. This allowed us to remove a lot of code no longer needed. Ref #21510 Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
This commit is contained in:
co-authored by
Will Browne
parent
56e0efbb56
commit
b3e9087557
@@ -100,7 +100,15 @@ func newExecutor(logsService *LogsService, im instancemgmt.InstanceManager, cfg
|
||||
|
||||
func NewInstanceSettings() datasource.InstanceFactoryFunc {
|
||||
return func(settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
|
||||
var jsonData map[string]string
|
||||
jsonData := struct {
|
||||
Profile string `json:"profile"`
|
||||
Region string `json:"defaulRegion"`
|
||||
AssumeRoleARN string `json:"assumeRoleArn"`
|
||||
ExternalID string `json:"externalId"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
Namespace string `json:"customMetricsNamespaces"`
|
||||
AuthType string `json:"authType"`
|
||||
}{}
|
||||
|
||||
err := json.Unmarshal(settings.JSONData, &jsonData)
|
||||
if err != nil {
|
||||
@@ -108,18 +116,17 @@ func NewInstanceSettings() datasource.InstanceFactoryFunc {
|
||||
}
|
||||
|
||||
model := datasourceInfo{
|
||||
profile: jsonData["profile"],
|
||||
region: jsonData["defaultRegion"],
|
||||
assumeRoleARN: jsonData["assumeRoleArn"],
|
||||
externalID: jsonData["externalId"],
|
||||
endpoint: jsonData["endpoint"],
|
||||
namespace: jsonData["customMetricsNamespaces"],
|
||||
profile: jsonData.Profile,
|
||||
region: jsonData.Region,
|
||||
assumeRoleARN: jsonData.AssumeRoleARN,
|
||||
externalID: jsonData.ExternalID,
|
||||
endpoint: jsonData.Endpoint,
|
||||
namespace: jsonData.Namespace,
|
||||
datasourceID: settings.ID,
|
||||
}
|
||||
|
||||
atStr := jsonData["authType"]
|
||||
at := awsds.AuthTypeDefault
|
||||
switch atStr {
|
||||
switch jsonData.AuthType {
|
||||
case "credentials":
|
||||
at = awsds.AuthTypeSharedCreds
|
||||
case "keys":
|
||||
@@ -132,7 +139,7 @@ func NewInstanceSettings() datasource.InstanceFactoryFunc {
|
||||
at = awsds.AuthTypeDefault
|
||||
plog.Warn("Authentication type \"arn\" is deprecated, falling back to default")
|
||||
default:
|
||||
plog.Warn("Unrecognized AWS authentication type", "type", atStr)
|
||||
plog.Warn("Unrecognized AWS authentication type", "type", jsonData.AuthType)
|
||||
}
|
||||
|
||||
model.authType = at
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package tsdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/adapters"
|
||||
"github.com/grafana/grafana/pkg/services/oauthtoken"
|
||||
)
|
||||
|
||||
// nolint:staticcheck // plugins.DataQuery deprecated
|
||||
func dataPluginQueryAdapter(pluginID string, handler backend.QueryDataHandler) plugins.DataPluginFunc {
|
||||
return plugins.DataPluginFunc(func(ctx context.Context, ds *models.DataSource, query plugins.DataQuery) (plugins.DataResponse, error) {
|
||||
instanceSettings, err := modelToInstanceSettings(ds)
|
||||
if err != nil {
|
||||
return plugins.DataResponse{}, err
|
||||
}
|
||||
|
||||
if query.Headers == nil {
|
||||
query.Headers = make(map[string]string)
|
||||
}
|
||||
|
||||
if oauthtoken.IsOAuthPassThruEnabled(ds) {
|
||||
if token := oauthtoken.GetCurrentOAuthToken(ctx, query.User); token != nil {
|
||||
delete(query.Headers, "Authorization")
|
||||
query.Headers["Authorization"] = fmt.Sprintf("%s %s", token.Type(), token.AccessToken)
|
||||
}
|
||||
}
|
||||
|
||||
req := &backend.QueryDataRequest{
|
||||
PluginContext: backend.PluginContext{
|
||||
OrgID: ds.OrgId,
|
||||
PluginID: pluginID,
|
||||
User: adapters.BackendUserFromSignedInUser(query.User),
|
||||
DataSourceInstanceSettings: instanceSettings,
|
||||
},
|
||||
Queries: []backend.DataQuery{},
|
||||
Headers: query.Headers,
|
||||
}
|
||||
|
||||
for _, q := range query.Queries {
|
||||
modelJSON, err := q.Model.MarshalJSON()
|
||||
if err != nil {
|
||||
return plugins.DataResponse{}, err
|
||||
}
|
||||
req.Queries = append(req.Queries, backend.DataQuery{
|
||||
RefID: q.RefID,
|
||||
Interval: time.Duration(q.IntervalMS) * time.Millisecond,
|
||||
MaxDataPoints: q.MaxDataPoints,
|
||||
TimeRange: backend.TimeRange{
|
||||
From: query.TimeRange.GetFromAsTimeUTC(),
|
||||
To: query.TimeRange.GetToAsTimeUTC(),
|
||||
},
|
||||
QueryType: q.QueryType,
|
||||
JSON: modelJSON,
|
||||
})
|
||||
}
|
||||
|
||||
resp, err := handler.QueryData(ctx, req)
|
||||
if err != nil {
|
||||
return plugins.DataResponse{}, err
|
||||
}
|
||||
|
||||
tR := plugins.DataResponse{
|
||||
Results: make(map[string]plugins.DataQueryResult, len(resp.Responses)),
|
||||
}
|
||||
|
||||
for refID, r := range resp.Responses {
|
||||
qr := plugins.DataQueryResult{
|
||||
RefID: refID,
|
||||
}
|
||||
|
||||
for _, f := range r.Frames {
|
||||
if f.RefID == "" {
|
||||
f.RefID = refID
|
||||
}
|
||||
}
|
||||
|
||||
qr.Dataframes = plugins.NewDecodedDataFrames(r.Frames)
|
||||
|
||||
if r.Error != nil {
|
||||
qr.Error = r.Error
|
||||
}
|
||||
|
||||
tR.Results[refID] = qr
|
||||
}
|
||||
|
||||
return tR, nil
|
||||
})
|
||||
}
|
||||
|
||||
func modelToInstanceSettings(ds *models.DataSource) (*backend.DataSourceInstanceSettings, error) {
|
||||
jsonDataBytes, err := ds.JsonData.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &backend.DataSourceInstanceSettings{
|
||||
ID: ds.Id,
|
||||
Name: ds.Name,
|
||||
URL: ds.Url,
|
||||
Database: ds.Database,
|
||||
User: ds.User,
|
||||
BasicAuthEnabled: ds.BasicAuth,
|
||||
BasicAuthUser: ds.BasicAuthUser,
|
||||
JSONData: jsonDataBytes,
|
||||
DecryptedSecureJSONData: ds.DecryptedValues(),
|
||||
Updated: ds.Updated,
|
||||
UID: ds.Uid,
|
||||
}, nil
|
||||
}
|
||||
+14
-20
@@ -7,11 +7,11 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/httpclient"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||
"github.com/grafana/grafana/pkg/registry"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tsdb/azuremonitor"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudmonitoring"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch"
|
||||
"github.com/grafana/grafana/pkg/tsdb/elasticsearch"
|
||||
"github.com/grafana/grafana/pkg/tsdb/graphite"
|
||||
"github.com/grafana/grafana/pkg/tsdb/influxdb"
|
||||
@@ -42,13 +42,13 @@ func init() {
|
||||
|
||||
// Service handles data requests to data sources.
|
||||
type Service struct {
|
||||
Cfg *setting.Cfg `inject:""`
|
||||
CloudWatchService *cloudwatch.CloudWatchService `inject:""`
|
||||
PostgresService *postgres.PostgresService `inject:""`
|
||||
CloudMonitoringService *cloudmonitoring.Service `inject:""`
|
||||
AzureMonitorService *azuremonitor.Service `inject:""`
|
||||
PluginManager plugins.Manager `inject:""`
|
||||
HTTPClientProvider httpclient.Provider `inject:""`
|
||||
Cfg *setting.Cfg `inject:""`
|
||||
PostgresService *postgres.PostgresService `inject:""`
|
||||
CloudMonitoringService *cloudmonitoring.Service `inject:""`
|
||||
AzureMonitorService *azuremonitor.Service `inject:""`
|
||||
PluginManager plugins.Manager `inject:""`
|
||||
BackendPluginManager backendplugin.Manager `inject:""`
|
||||
HTTPClientProvider httpclient.Provider `inject:""`
|
||||
|
||||
//nolint: staticcheck // plugins.DataPlugin deprecated
|
||||
registry map[string]func(*models.DataSource) (plugins.DataPlugin, error)
|
||||
@@ -72,25 +72,19 @@ func (s *Service) Init() error {
|
||||
}
|
||||
|
||||
//nolint: staticcheck // plugins.DataPlugin deprecated
|
||||
func (s *Service) HandleRequest(ctx context.Context, ds *models.DataSource, query plugins.DataQuery) (
|
||||
plugins.DataResponse, error) {
|
||||
plugin := s.PluginManager.GetDataPlugin(ds.Type)
|
||||
if plugin == nil {
|
||||
factory, exists := s.registry[ds.Type]
|
||||
if !exists {
|
||||
return plugins.DataResponse{}, fmt.Errorf(
|
||||
"could not find plugin corresponding to data source type: %q", ds.Type)
|
||||
}
|
||||
|
||||
func (s *Service) HandleRequest(ctx context.Context, ds *models.DataSource, query plugins.DataQuery) (plugins.DataResponse, error) {
|
||||
if factory, exists := s.registry[ds.Type]; exists {
|
||||
var err error
|
||||
plugin, err = factory(ds)
|
||||
plugin, err := factory(ds)
|
||||
if err != nil {
|
||||
return plugins.DataResponse{}, fmt.Errorf("could not instantiate endpoint for data plugin %q: %w",
|
||||
ds.Type, err)
|
||||
}
|
||||
|
||||
return plugin.DataQuery(ctx, ds, query)
|
||||
}
|
||||
|
||||
return plugin.DataQuery(ctx, ds, query)
|
||||
return dataPluginQueryAdapter(ds.Type, s.BackendPluginManager).DataQuery(ctx, ds, query)
|
||||
}
|
||||
|
||||
// RegisterQueryHandler registers a query handler factory.
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||
@@ -19,7 +21,7 @@ func TestHandleRequest(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
svc, exe := createService()
|
||||
svc, exe, _ := createService()
|
||||
exe.Return("A", plugins.DataTimeSeriesSlice{plugins.DataTimeSeries{Name: "argh"}})
|
||||
|
||||
res, err := svc.HandleRequest(context.TODO(), &models.DataSource{Id: 1, Type: "test"}, req)
|
||||
@@ -36,7 +38,7 @@ func TestHandleRequest(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
svc, exe := createService()
|
||||
svc, exe, _ := createService()
|
||||
exe.Return("A", plugins.DataTimeSeriesSlice{plugins.DataTimeSeries{Name: "argh"}})
|
||||
exe.Return("B", plugins.DataTimeSeriesSlice{plugins.DataTimeSeries{Name: "barg"}})
|
||||
|
||||
@@ -48,16 +50,28 @@ func TestHandleRequest(t *testing.T) {
|
||||
require.Equal(t, "barg", res.Results["B"].Series[0].Name)
|
||||
})
|
||||
|
||||
t.Run("Should return error when handling request for query with unknown type", func(t *testing.T) {
|
||||
svc, _ := createService()
|
||||
t.Run("Should fallback to backend plugin manager when handling request for query with unregistered type", func(t *testing.T) {
|
||||
svc, _, manager := createService()
|
||||
backendPluginManagerCalled := false
|
||||
manager.QueryDataHandlerFunc = backend.QueryDataHandlerFunc(func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
backendPluginManagerCalled = true
|
||||
return &backend.QueryDataResponse{}, nil
|
||||
})
|
||||
|
||||
ds := &models.DataSource{Id: 12, Type: "unregisteredType", JsonData: simplejson.New()}
|
||||
req := plugins.DataQuery{
|
||||
TimeRange: &plugins.DataTimeRange{},
|
||||
Queries: []plugins.DataSubQuery{
|
||||
{RefID: "A", DataSource: &models.DataSource{Id: 1, Type: "asdasdas"}},
|
||||
{
|
||||
RefID: "A",
|
||||
DataSource: ds,
|
||||
Model: simplejson.New(),
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := svc.HandleRequest(context.TODO(), &models.DataSource{Id: 12, Type: "testjughjgjg"}, req)
|
||||
require.Error(t, err)
|
||||
_, err := svc.HandleRequest(context.Background(), ds, req)
|
||||
require.NoError(t, err)
|
||||
require.True(t, backendPluginManagerCalled)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -99,17 +113,22 @@ func (e *fakeExecutor) HandleQuery(refId string, fn resultsFn) {
|
||||
|
||||
type fakeBackendPM struct {
|
||||
backendplugin.Manager
|
||||
backend.QueryDataHandlerFunc
|
||||
}
|
||||
|
||||
func (pm fakeBackendPM) GetDataPlugin(string) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func createService() (Service, *fakeExecutor) {
|
||||
s := NewService()
|
||||
s.PluginManager = &manager.PluginManager{
|
||||
BackendPluginManager: fakeBackendPM{},
|
||||
func (m *fakeBackendPM) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
if m.QueryDataHandlerFunc != nil {
|
||||
return m.QueryDataHandlerFunc.QueryData(ctx, req)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func createService() (Service, *fakeExecutor, *fakeBackendPM) {
|
||||
s := NewService()
|
||||
fakeBackendPluginManager := &fakeBackendPM{}
|
||||
s.PluginManager = &manager.PluginManager{}
|
||||
s.BackendPluginManager = fakeBackendPluginManager
|
||||
e := &fakeExecutor{
|
||||
//nolint: staticcheck // plugins.DataPlugin deprecated
|
||||
results: make(map[string]plugins.DataQueryResult),
|
||||
@@ -120,5 +139,5 @@ func createService() (Service, *fakeExecutor) {
|
||||
return e, nil
|
||||
}
|
||||
|
||||
return s, e
|
||||
return s, e, fakeBackendPluginManager
|
||||
}
|
||||
Reference in New Issue
Block a user