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:
@@ -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{
|
||||
|
||||
Reference in New Issue
Block a user