mirror of https://github.com/grafana/grafana.git
162 lines
4.9 KiB
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
|
||
|
}
|