grafana/pkg/registry/apis/query/connections.go

162 lines
4.9 KiB
Go

package query
import (
"context"
"fmt"
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
authlib "github.com/grafana/authlib/types"
"github.com/grafana/grafana/pkg/apimachinery/utils"
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"
)
var (
_ rest.Scoper = (*connectionAccess)(nil)
_ rest.SingularNameProvider = (*connectionAccess)(nil)
_ rest.Getter = (*connectionAccess)(nil)
_ rest.Lister = (*connectionAccess)(nil)
_ rest.Storage = (*connectionAccess)(nil)
)
// Get all datasource connections -- this will be backed by search or duplicated resource in unified storage
type DataSourceConnectionProvider interface {
// Get gets a specific datasource (that the user in context can see)
// The name is {group}:{name}, see /pkg/apis/query/v0alpha1/connection.go#L34
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)
}
type connectionAccess struct {
tableConverter rest.TableConvertor
connections DataSourceConnectionProvider
}
func (s *connectionAccess) New() runtime.Object {
return queryV0.ConnectionResourceInfo.NewFunc()
}
func (s *connectionAccess) Destroy() {}
func (s *connectionAccess) NamespaceScoped() bool {
return true
}
func (s *connectionAccess) GetSingularName() string {
return queryV0.ConnectionResourceInfo.GetSingularName()
}
func (s *connectionAccess) ShortNames() []string {
return queryV0.ConnectionResourceInfo.GetShortNames()
}
func (s *connectionAccess) NewList() runtime.Object {
return queryV0.ConnectionResourceInfo.NewListFunc()
}
func (s *connectionAccess) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
if s.tableConverter == nil {
s.tableConverter = queryV0.ConnectionResourceInfo.TableConverter()
}
return s.tableConverter.ConvertToTable(ctx, object, tableOptions)
}
func (s *connectionAccess) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
return s.connections.GetConnection(ctx, request.NamespaceValue(ctx), name)
}
func (s *connectionAccess) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
return s.connections.ListConnections(ctx, request.NamespaceValue(ctx))
}
type connectionsProvider struct {
dsService datasources.DataSourceService
registry queryV0.DataSourceApiServerRegistry
}
var (
_ DataSourceConnectionProvider = (*connectionsProvider)(nil)
)
func (q *connectionsProvider) GetConnection(ctx context.Context, namespace string, name string) (*queryV0.DataSourceConnection, error) {
info, err := authlib.ParseNamespace(namespace)
if err != nil {
return nil, err
}
ds, err := q.dsService.GetDataSource(ctx, &datasources.GetDataSourceQuery{
UID: name,
OrgID: info.OrgID,
})
if err != nil {
return nil, err
}
// TODO... access control?
return q.asConnection(ds, namespace)
}
func (q *connectionsProvider) ListConnections(ctx context.Context, namespace string) (*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
}
result := &queryV0.DataSourceConnectionList{
Items: []queryV0.DataSourceConnection{},
}
for _, ds := range dss {
v, err := q.asConnection(ds, namespace)
if err != nil {
return nil, err
}
result.Items = append(result.Items, *v)
}
return result, nil
}
func (q *connectionsProvider) asConnection(ds *datasources.DataSource, ns string) (v *queryV0.DataSourceConnection, err error) {
gv, err := q.registry.GetDatasourceGroupVersion(ds.Type)
if err != nil {
return nil, fmt.Errorf("datasource type %q does not map to an apiserver %w", ds.Type, err)
}
v = &queryV0.DataSourceConnection{
ObjectMeta: metav1.ObjectMeta{
Name: queryV0.DataSourceConnectionName(gv.Group, ds.UID),
Namespace: ns,
CreationTimestamp: metav1.NewTime(ds.Created),
ResourceVersion: fmt.Sprintf("%d", ds.Updated.UnixMilli()),
Generation: int64(ds.Version),
},
Title: ds.Name,
Datasource: queryV0.DataSourceConnectionRef{
Group: gv.Group,
Version: gv.Version,
Name: ds.UID,
},
}
v.UID = gapiutil.CalculateClusterWideUID(v) // UID is unique across all groups
if !ds.Updated.IsZero() {
meta, err := utils.MetaAccessor(v)
if err != nil {
meta.SetUpdatedTimestamp(&ds.Updated)
}
}
return v, err
}