use fieldSelector on the ListConnections endpoint to get datasources by UID, instead of relying on uniqueness in the Get endpoint

This commit is contained in:
Dafydd
2025-12-05 15:49:40 +00:00
parent 85925d0765
commit 7e991886e0
2 changed files with 61 additions and 14 deletions
+44 -11
View File
@@ -15,6 +15,7 @@ import (
queryV0 "github.com/grafana/grafana/pkg/apis/query/v0alpha1"
gapiutil "github.com/grafana/grafana/pkg/services/apiserver/utils"
"github.com/grafana/grafana/pkg/services/datasources"
"k8s.io/apimachinery/pkg/fields"
)
var (
@@ -31,8 +32,8 @@ type DataSourceConnectionProvider interface {
// The name is the legacy datasource UID.
GetConnection(ctx context.Context, namespace string, name string) (*queryV0.DataSourceConnection, error)
// List lists all data sources the user in context can see
ListConnections(ctx context.Context, namespace string) (*queryV0.DataSourceConnectionList, error)
// List lists all data sources the user in context can see. Optional field selectors can filter the results.
ListConnections(ctx context.Context, namespace string, fieldSelector fields.Selector) (*queryV0.DataSourceConnectionList, error)
}
type connectionAccess struct {
@@ -74,7 +75,11 @@ func (s *connectionAccess) Get(ctx context.Context, name string, options *metav1
}
func (s *connectionAccess) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
return s.connections.ListConnections(ctx, request.NamespaceValue(ctx))
var fs fields.Selector
if options != nil && options.FieldSelector != nil {
fs = options.FieldSelector
}
return s.connections.ListConnections(ctx, request.NamespaceValue(ctx), fs)
}
type connectionsProvider struct {
@@ -103,19 +108,47 @@ func (q *connectionsProvider) GetConnection(ctx context.Context, namespace strin
return q.asConnection(ds, namespace)
}
func (q *connectionsProvider) ListConnections(ctx context.Context, namespace string) (*queryV0.DataSourceConnectionList, error) {
func (q *connectionsProvider) ListConnections(ctx context.Context, namespace string, fieldSelector fields.Selector) (*queryV0.DataSourceConnectionList, error) {
ns, err := authlib.ParseNamespace(namespace)
if err != nil {
return nil, err
}
dss, err := q.dsService.GetDataSources(ctx, &datasources.GetDataSourcesQuery{
OrgID: ns.OrgID,
DataSourceLimit: 10000,
})
if err != nil {
return nil, err
var dss []*datasources.DataSource
// if fieldSelector is not nil, find any uids in the metadata.name field and
// use them in the query
if fieldSelector != nil && !fieldSelector.Empty() {
uids := []string{}
for _, req := range fieldSelector.Requirements() {
if req.Field == "metadata.name" {
uids = append(uids, req.Value)
}
}
// We don't have a way to fetch a subset of datasources by UID in the legacy
// datasource service, so fetch them one by one.
if len(uids) > 0 {
for _, uid := range uids {
ds, err := q.dsService.GetDataSource(ctx, &datasources.GetDataSourceQuery{
UID: uid,
OrgID: ns.OrgID,
})
if err != nil {
return nil, err
}
dss = append(dss, ds)
}
}
} else {
dss, err = q.dsService.GetDataSources(ctx, &datasources.GetDataSourcesQuery{
OrgID: ns.OrgID,
DataSourceLimit: 10000,
})
if err != nil {
return nil, err
}
}
result := &queryV0.DataSourceConnectionList{
Items: []queryV0.DataSourceConnection{},
}
@@ -137,7 +170,7 @@ func (q *connectionsProvider) asConnection(ds *datasources.DataSource, ns string
v = &queryV0.DataSourceConnection{
ObjectMeta: metav1.ObjectMeta{
Name: ds.UID,
Name: queryV0.DataSourceConnectionName(gv.Group, ds.UID),
Namespace: ns,
CreationTimestamp: metav1.NewTime(ds.Created),
ResourceVersion: fmt.Sprintf("%d", ds.Updated.UnixMilli()),
@@ -96,11 +96,16 @@ func (dc *dataSourceConnectionClient) GetByUID(ctx context.Context, uid string)
return nil, err
}
// use the list endpoint with a fieldSelector so that can get multiple results
// in the case of a non-unique "uid". This should not be possible when we are
// backed by the legacy database, but wont be guaranteed when we are using
// uniStore as the names will not be guaranteed unique across apiGroups. We
// error below if more than one result is returned.
result := client.RESTClient().Get().
Prefix("apis", "query.grafana.app", "v0alpha1").
Namespace("default"). // TODO do something about namespace
Resource("connections").
Name(uid).
Param("fieldSelector", "metadata.name="+uid).
Do(ctx)
if err = result.Error(); err != nil {
@@ -114,12 +119,21 @@ func (dc *dataSourceConnectionClient) GetByUID(ctx context.Context, uid string)
return nil, errors.New("not found")
}
fullDS := datasourcev0alpha1.DataSource{}
err = result.Into(&fullDS)
dsList := datasourcev0alpha1.DataSourceList{}
err = result.Into(&dsList)
if err != nil {
return nil, err
}
if len(dsList.Items) == 0 {
return nil, errors.New("not found")
}
if len(dsList.Items) > 1 {
return nil, errors.New("multiple connections found")
}
fullDS := dsList.Items[0]
dsConnection := &queryv0alpha1.DataSourceConnection{
Title: fullDS.Spec.Title(),
Datasource: queryv0alpha1.DataSourceConnectionRef{