datasources: querier: handle the grafana-ds (#103980)

* datasources: querier: handle the grafana-ds

* updated dependencies
This commit is contained in:
Gábor Farkas
2025-04-15 14:55:39 +02:00
committed by GitHub
parent eac312753f
commit 66a04babe1
5 changed files with 158 additions and 2 deletions
+29 -2
View File
@@ -19,9 +19,11 @@ import (
"github.com/grafana/grafana/pkg/registry/apis/query/clientapi"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/pluginsintegration/adapters"
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/tsdb/grafanads"
apierrors "k8s.io/apimachinery/pkg/api/errors"
)
@@ -90,6 +92,23 @@ func (d *pluginClient) CanQueryDataSource(ctx context.Context, uid string) (bool
return d.ac.Evaluate(ctx, user, evaluate)
}
// this handles the special `--grafana--` data source
func getGrafanaDataSourceSettings(ctx context.Context) (*backend.DataSourceInstanceSettings, error) {
user, err := identity.GetRequester(ctx)
if err != nil {
return nil, err
}
ds := grafanads.DataSourceModel(user.GetOrgID())
decryptFunc := func(ds *datasources.DataSource) (map[string]string, error) {
// we do not need to handle any secrets
return nil, nil
}
return adapters.ModelToInstanceSettings(ds, decryptFunc)
}
// ExecuteQueryData implements QueryHelper.
func (d *pluginClient) QueryData(ctx context.Context, req data.QueryDataRequest) (*backend.QueryDataResponse, error) {
queries, dsRef, err := data.ToDataSourceQueries(req)
@@ -109,8 +128,16 @@ func (d *pluginClient) QueryData(ctx context.Context, req data.QueryDataRequest)
return nil, k8sForbiddenError
}
// NOTE: this depends on uid unique across datasources
settings, err := d.pCtxProvider.GetDataSourceInstanceSettings(ctx, dsRef.UID)
var settings *backend.DataSourceInstanceSettings
// we need to special-case the "--grafana--" data source
if dsRef.UID == grafanads.DatasourceUID {
settings, err = getGrafanaDataSourceSettings(ctx)
} else {
// NOTE: this depends on uid unique across datasources
settings, err = d.pCtxProvider.GetDataSourceInstanceSettings(ctx, dsRef.UID)
}
if err != nil {
// there is no better way to differentiate between plugin-not-found and other-error
if errors.Is(err, datasources.ErrDataSourceNotFound) {
+12
View File
@@ -237,6 +237,18 @@ func (p *queryParser) getValidDataSourceRef(ctx context.Context, ds *data.DataSo
}
return p.legacy.GetDataSourceFromDeprecatedFields(ctx, "", id)
}
// we need to special-case the "grafana" data source
if ds.UID == "grafana" {
return &data.DataSourceRef{
// it does not really matter what `type` we set here,
// we will always detect this case by `uid` later.
// here we go with what the data source's plugin.json says.
Type: "grafana",
UID: "grafana",
}, nil
}
if ds.Type == "" {
if ds.UID == "" {
return nil, fmt.Errorf("missing name/uid in data source reference")
+54
View File
@@ -3,6 +3,7 @@ package query
import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
"path"
@@ -200,6 +201,59 @@ func TestSqlInputs(t *testing.T) {
require.Equal(t, parsedRequestInfo.SqlInputs["B"], struct{}{})
}
func TestGrafanaDS(t *testing.T) {
ctx := context.Background()
parser := newQueryParser(expr.NewExpressionQueryReader(featuremgmt.WithFeatures()),
&noLegacyRetriever{}, tracing.InitializeTracerForTest(), log.NewNopLogger())
t.Run("grafana ds without type", func(t *testing.T) {
parsed, err := parser.parseRequest(ctx, &query.QueryDataRequest{
QueryDataRequest: data.QueryDataRequest{
Queries: []data.DataQuery{{
CommonQueryProperties: data.CommonQueryProperties{
RefID: "A",
Datasource: &data.DataSourceRef{
UID: "grafana",
},
},
}},
},
})
require.NoError(t, err)
require.Len(t, parsed.Requests, 1)
require.Equal(t, "grafana", parsed.Requests[0].PluginId)
require.Equal(t, "grafana", parsed.Requests[0].UID)
})
t.Run("grafana ds with different type", func(t *testing.T) {
parsed, err := parser.parseRequest(ctx, &query.QueryDataRequest{
QueryDataRequest: data.QueryDataRequest{
Queries: []data.DataQuery{{
CommonQueryProperties: data.CommonQueryProperties{
RefID: "A",
Datasource: &data.DataSourceRef{
UID: "grafana",
Type: "datasource",
},
},
}},
},
})
require.NoError(t, err)
require.Len(t, parsed.Requests, 1)
require.Equal(t, "grafana", parsed.Requests[0].PluginId)
require.Equal(t, "grafana", parsed.Requests[0].UID)
})
}
type noLegacyRetriever struct{}
var errNoLegacy = errors.New("legacy dds retriever reached, it should not")
func (s *noLegacyRetriever) GetDataSourceFromDeprecatedFields(ctx context.Context, name string, id int64) (*data.DataSourceRef, error) {
return nil, errNoLegacy
}
type legacyDataSourceRetriever struct{}
func (s *legacyDataSourceRetriever) GetDataSourceFromDeprecatedFields(ctx context.Context, name string, id int64) (*data.DataSourceRef, error) {