Datasources: Experimental API group names use full plugin IDs (#112961)

This commit is contained in:
Dafydd
2026-01-12 15:01:40 +00:00
committed by GitHub
parent 880bc23c85
commit 51dcdd3499
7 changed files with 52 additions and 60 deletions
@@ -71,7 +71,6 @@ type cachingDatasourceProvider struct {
}
func (q *cachingDatasourceProvider) GetDatasourceProvider(pluginJson plugins.JSONData) PluginDatasourceProvider {
group, _ := plugins.GetDatasourceGroupNameFromPluginID(pluginJson.ID)
return &scopedDatasourceProvider{
plugin: pluginJson,
dsService: q.dsService,
@@ -81,7 +80,7 @@ func (q *cachingDatasourceProvider) GetDatasourceProvider(pluginJson plugins.JSO
mapper: q.converter.mapper,
plugin: pluginJson.ID,
alias: pluginJson.AliasIDs,
group: group,
group: pluginJson.ID,
},
}
}
+24 -19
View File
@@ -37,6 +37,11 @@ var (
_ builder.APIGroupBuilder = (*DataSourceAPIBuilder)(nil)
)
type DataSourceAPIBuilderConfig struct {
LoadQueryTypes bool
UseDualWriter bool
}
// DataSourceAPIBuilder is used just so wire has something unique to return
type DataSourceAPIBuilder struct {
datasourceResourceInfo utils.ResourceInfo
@@ -46,7 +51,7 @@ type DataSourceAPIBuilder struct {
contextProvider PluginContextWrapper
accessControl accesscontrol.AccessControl
queryTypes *queryV0.QueryTypeDefinitionList
configCrudUseNewApis bool
cfg DataSourceAPIBuilderConfig
dataSourceCRUDMetric *prometheus.HistogramVec
}
@@ -89,20 +94,24 @@ func RegisterAPIService(
return nil, fmt.Errorf("plugin client is not a PluginClient: %T", pluginClient)
}
groupName := pluginJSON.ID + ".datasource.grafana.app"
builder, err = NewDataSourceAPIBuilder(
groupName,
pluginJSON,
client,
datasources.GetDatasourceProvider(pluginJSON),
contextProvider,
accessControl,
//nolint:staticcheck // not yet migrated to OpenFeature
features.IsEnabledGlobally(featuremgmt.FlagDatasourceQueryTypes),
//nolint:staticcheck // not yet migrated to OpenFeature
features.IsEnabledGlobally(featuremgmt.FlagQueryServiceWithConnections),
DataSourceAPIBuilderConfig{
//nolint:staticcheck // not yet migrated to OpenFeature
LoadQueryTypes: features.IsEnabledGlobally(featuremgmt.FlagDatasourceQueryTypes),
UseDualWriter: false,
},
)
if err != nil {
return nil, err
}
builder.SetDataSourceCRUDMetrics(dataSourceCRUDMetric)
apiRegistrar.RegisterAPI(builder)
@@ -120,31 +129,27 @@ type PluginClient interface {
}
func NewDataSourceAPIBuilder(
groupName string,
plugin plugins.JSONData,
client PluginClient,
datasources PluginDatasourceProvider,
contextProvider PluginContextWrapper,
accessControl accesscontrol.AccessControl,
loadQueryTypes bool,
configCrudUseNewApis bool,
cfg DataSourceAPIBuilderConfig,
) (*DataSourceAPIBuilder, error) {
group, err := plugins.GetDatasourceGroupNameFromPluginID(plugin.ID)
if err != nil {
return nil, err
}
builder := &DataSourceAPIBuilder{
datasourceResourceInfo: datasourceV0.DataSourceResourceInfo.WithGroupAndShortName(group, plugin.ID),
datasourceResourceInfo: datasourceV0.DataSourceResourceInfo.WithGroupAndShortName(groupName, plugin.ID),
pluginJSON: plugin,
client: client,
datasources: datasources,
contextProvider: contextProvider,
accessControl: accessControl,
configCrudUseNewApis: configCrudUseNewApis,
cfg: cfg,
}
if loadQueryTypes {
var err error
if cfg.LoadQueryTypes {
// In the future, this will somehow come from the plugin
builder.queryTypes, err = getHardcodedQueryTypes(group)
builder.queryTypes, err = getHardcodedQueryTypes(groupName)
}
return builder, err
}
@@ -154,9 +159,9 @@ func getHardcodedQueryTypes(group string) (*queryV0.QueryTypeDefinitionList, err
var err error
var raw json.RawMessage
switch group {
case "testdata.datasource.grafana.app":
case "testdata.datasource.grafana.app", "grafana-testdata-datasource":
raw, err = kinds.QueryTypeDefinitionListJSON()
case "prometheus.datasource.grafana.app":
case "prometheus.datasource.grafana.app", "prometheus":
raw, err = models.QueryTypeDefinitionListJSON()
}
if err != nil {
@@ -233,7 +238,7 @@ func (b *DataSourceAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver
storage["connections"] = &noopREST{} // hidden from openapi
storage["connections/query"] = storage[ds.StoragePath("query")] // deprecated in openapi
if b.configCrudUseNewApis {
if b.cfg.UseDualWriter {
legacyStore := &legacyStorage{
datasources: b.datasources,
resourceInfo: &ds,
+3 -3
View File
@@ -62,7 +62,7 @@ func TestIntegrationTestDatasource(t *testing.T) {
t.Run("Admin configs", func(t *testing.T) {
client := helper.Org1.Admin.ResourceClient(t, schema.GroupVersionResource{
Group: "testdata.datasource.grafana.app",
Group: "grafana-testdata-datasource.datasource.grafana.app",
Version: "v0alpha1",
Resource: "datasources",
}).Namespace("default")
@@ -92,7 +92,7 @@ func TestIntegrationTestDatasource(t *testing.T) {
t.Run("Call subresources", func(t *testing.T) {
client := helper.Org1.Admin.ResourceClient(t, schema.GroupVersionResource{
Group: "testdata.datasource.grafana.app",
Group: "grafana-testdata-datasource.datasource.grafana.app",
Version: "v0alpha1",
Resource: "datasources",
}).Namespace("default")
@@ -128,7 +128,7 @@ func TestIntegrationTestDatasource(t *testing.T) {
raw := apis.DoRequest[any](helper, apis.RequestParams{
User: helper.Org1.Admin,
Method: "GET",
Path: "/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/default/datasources/test/resource",
Path: "/apis/grafana-testdata-datasource.datasource.grafana.app/v0alpha1/namespaces/default/datasources/test/resource",
}, nil)
// endpoint is disabled currently because it has not been
// sufficiently tested.
@@ -2,10 +2,10 @@
"openapi": "3.0.0",
"info": {
"description": "Generates test data in different forms",
"title": "testdata.datasource.grafana.app/v0alpha1"
"title": "grafana-testdata-datasource.datasource.grafana.app/v0alpha1"
},
"paths": {
"/apis/testdata.datasource.grafana.app/v0alpha1/": {
"/apis/grafana-testdata-datasource.datasource.grafana.app/v0alpha1/": {
"get": {
"tags": [
"API Discovery"
@@ -36,7 +36,7 @@
}
}
},
"/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/{namespace}/connections/{name}/query": {
"/apis/grafana-testdata-datasource.datasource.grafana.app/v0alpha1/namespaces/{namespace}/connections/{name}/query": {
"post": {
"tags": [
"Connections (deprecated)"
@@ -68,7 +68,7 @@
"deprecated": true,
"x-kubernetes-action": "connect",
"x-kubernetes-group-version-kind": {
"group": "testdata.datasource.grafana.app",
"group": "grafana-testdata-datasource.datasource.grafana.app",
"version": "v0alpha1",
"kind": "QueryDataResponse"
}
@@ -96,7 +96,7 @@
}
]
},
"/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources": {
"/apis/grafana-testdata-datasource.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources": {
"get": {
"tags": [
"DataSource"
@@ -137,7 +137,7 @@
},
"x-kubernetes-action": "list",
"x-kubernetes-group-version-kind": {
"group": "testdata.datasource.grafana.app",
"group": "grafana-testdata-datasource.datasource.grafana.app",
"version": "v0alpha1",
"kind": "DataSource"
}
@@ -254,7 +254,7 @@
}
]
},
"/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources/{name}": {
"/apis/grafana-testdata-datasource.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources/{name}": {
"get": {
"tags": [
"DataSource"
@@ -285,7 +285,7 @@
},
"x-kubernetes-action": "get",
"x-kubernetes-group-version-kind": {
"group": "testdata.datasource.grafana.app",
"group": "grafana-testdata-datasource.datasource.grafana.app",
"version": "v0alpha1",
"kind": "DataSource"
}
@@ -322,7 +322,7 @@
}
]
},
"/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources/{name}/health": {
"/apis/grafana-testdata-datasource.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources/{name}/health": {
"get": {
"tags": [
"DataSource"
@@ -343,7 +343,7 @@
},
"x-kubernetes-action": "connect",
"x-kubernetes-group-version-kind": {
"group": "testdata.datasource.grafana.app",
"group": "grafana-testdata-datasource.datasource.grafana.app",
"version": "v0alpha1",
"kind": "HealthCheckResult"
}
@@ -371,7 +371,7 @@
}
]
},
"/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources/{name}/query": {
"/apis/grafana-testdata-datasource.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources/{name}/query": {
"post": {
"tags": [
"DataSource"
@@ -401,7 +401,7 @@
},
"x-kubernetes-action": "connect",
"x-kubernetes-group-version-kind": {
"group": "testdata.datasource.grafana.app",
"group": "grafana-testdata-datasource.datasource.grafana.app",
"version": "v0alpha1",
"kind": "QueryDataResponse"
}
@@ -429,7 +429,7 @@
}
]
},
"/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources/{name}/resource": {
"/apis/grafana-testdata-datasource.datasource.grafana.app/v0alpha1/namespaces/{namespace}/datasources/{name}/resource": {
"get": {
"tags": [
"DataSource"
@@ -450,7 +450,7 @@
},
"x-kubernetes-action": "connect",
"x-kubernetes-group-version-kind": {
"group": "testdata.datasource.grafana.app",
"group": "grafana-testdata-datasource.datasource.grafana.app",
"version": "v0alpha1",
"kind": "Status"
}
@@ -478,7 +478,7 @@
}
]
},
"/apis/testdata.datasource.grafana.app/v0alpha1/namespaces/{namespace}/queryconvert/{name}": {
"/apis/grafana-testdata-datasource.datasource.grafana.app/v0alpha1/namespaces/{namespace}/queryconvert/{name}": {
"post": {
"tags": [
"QueryDataRequest"
@@ -499,7 +499,7 @@
},
"x-kubernetes-action": "connect",
"x-kubernetes-group-version-kind": {
"group": "testdata.datasource.grafana.app",
"group": "grafana-testdata-datasource.datasource.grafana.app",
"version": "v0alpha1",
"kind": "QueryDataRequest"
}
@@ -620,7 +620,7 @@
"apiVersion": {
"type": "string",
"enum": [
"testdata.datasource.grafana.app/v0alpha1"
"grafana-testdata-datasource.datasource.grafana.app/v0alpha1"
]
},
"kind": {
@@ -660,7 +660,7 @@
},
"x-kubernetes-group-version-kind": [
{
"group": "testdata.datasource.grafana.app",
"group": "grafana-testdata-datasource.datasource.grafana.app",
"kind": "DataSource",
"version": "v0alpha1"
}
@@ -703,7 +703,7 @@
},
"x-kubernetes-group-version-kind": [
{
"group": "testdata.datasource.grafana.app",
"group": "grafana-testdata-datasource.datasource.grafana.app",
"kind": "DataSourceList",
"version": "v0alpha1"
}
@@ -744,7 +744,7 @@
},
"x-kubernetes-group-version-kind": [
{
"group": "testdata.datasource.grafana.app",
"group": "grafana-testdata-datasource.datasource.grafana.app",
"kind": "HealthCheckResult",
"version": "v0alpha1"
}
@@ -833,7 +833,7 @@
},
"x-kubernetes-group-version-kind": [
{
"group": "testdata.datasource.grafana.app",
"group": "grafana-testdata-datasource.datasource.grafana.app",
"kind": "QueryDataResponse",
"version": "v0alpha1"
}
+1 -1
View File
@@ -124,7 +124,7 @@ func TestIntegrationOpenAPIs(t *testing.T) {
Group: "shorturl.grafana.app",
Version: "v1beta1",
}, {
Group: "testdata.datasource.grafana.app",
Group: "grafana-testdata-datasource.datasource.grafana.app",
Version: "v0alpha1",
}, {
Group: "logsdrilldown.grafana.app",
+2 -3
View File
@@ -28,18 +28,17 @@ describe('DatasourceAPIVersions', () => {
it('get', async () => {
const getMock = jest.fn().mockResolvedValue({
groups: [
{ name: 'testdata.datasource.grafana.app', preferredVersion: { version: 'v1' } },
{ name: 'grafana-testdata-datasource.datasource.grafana.app', preferredVersion: { version: 'v1' } },
{ name: 'prometheus.datasource.grafana.app', preferredVersion: { version: 'v2' } },
{ name: 'myorg-myplugin.datasource.grafana.app', preferredVersion: { version: 'v3' } },
],
});
getBackendSrv().get = getMock;
const apiVersions = new DatasourceAPIVersions();
expect(await apiVersions.get('testdata')).toBe('v1');
expect(await apiVersions.get('grafana-testdata-datasource')).toBe('v1');
expect(await apiVersions.get('prometheus')).toBe('v2');
expect(await apiVersions.get('graphite')).toBeUndefined();
expect(await apiVersions.get('myorg-myplugin-datasource')).toBe('v3');
expect(await apiVersions.get('myorg-myplugin')).toBe('v3');
expect(getMock).toHaveBeenCalledTimes(1);
expect(getMock).toHaveBeenCalledWith('/apis');
});
-11
View File
@@ -162,17 +162,6 @@ export class DatasourceAPIVersions {
if (group.name.includes('datasource.grafana.app')) {
const id = group.name.split('.')[0];
apiVersions[id] = group.preferredVersion.version;
// workaround for plugins that don't append '-datasource' for the group name
// e.g. org-plugin-datasource uses org-plugin.datasource.grafana.app
if (!id.endsWith('-datasource')) {
if (!id.includes('-')) {
// workaroud for Grafana plugins that don't include the org either
// e.g. testdata uses testdata.datasource.grafana.app
apiVersions[`grafana-${id}-datasource`] = group.preferredVersion.version;
} else {
apiVersions[`${id}-datasource`] = group.preferredVersion.version;
}
}
}
});
this.apiVersions = apiVersions;