mirror of https://github.com/grafana/grafana
DataSources: Add datasource fetching + querying interface (#80749)
* first pass * separate oss + enterprise * tidy things up * add ctx * fix tests * use standalone svcs * mv plugin context provide * fix wire * fix importpull/80820/head
parent
bb0fa4f99a
commit
3f30cbf91c
@ -0,0 +1,11 @@ |
|||||||
|
package datasource |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
|
||||||
|
"github.com/grafana/grafana-plugin-sdk-go/backend" |
||||||
|
) |
||||||
|
|
||||||
|
type PluginContextProvider interface { |
||||||
|
PluginContextForDataSource(ctx context.Context, pluginID, name string) (backend.PluginContext, error) |
||||||
|
} |
@ -0,0 +1,169 @@ |
|||||||
|
package datasource |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"fmt" |
||||||
|
|
||||||
|
"github.com/grafana/grafana-plugin-sdk-go/backend" |
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||||
|
|
||||||
|
common "github.com/grafana/grafana/pkg/apis/common/v0alpha1" |
||||||
|
"github.com/grafana/grafana/pkg/apis/datasource/v0alpha1" |
||||||
|
"github.com/grafana/grafana/pkg/infra/appcontext" |
||||||
|
"github.com/grafana/grafana/pkg/plugins" |
||||||
|
"github.com/grafana/grafana/pkg/services/datasources" |
||||||
|
"github.com/grafana/grafana/pkg/services/grafana-apiserver/endpoints/request" |
||||||
|
"github.com/grafana/grafana/pkg/services/grafana-apiserver/utils" |
||||||
|
) |
||||||
|
|
||||||
|
type QuerierFactoryFunc func(ctx context.Context, ri common.ResourceInfo, pj plugins.JSONData) (Querier, error) |
||||||
|
|
||||||
|
type QuerierProvider interface { |
||||||
|
Querier(ctx context.Context, ri common.ResourceInfo, pj plugins.JSONData) (Querier, error) |
||||||
|
} |
||||||
|
|
||||||
|
type DefaultQuerierProvider struct { |
||||||
|
factory QuerierFactoryFunc |
||||||
|
} |
||||||
|
|
||||||
|
func ProvideDefaultQuerierProvider(pluginClient plugins.Client, dsService datasources.DataSourceService, |
||||||
|
dsCache datasources.CacheService) *DefaultQuerierProvider { |
||||||
|
return NewQuerierProvider(func(ctx context.Context, ri common.ResourceInfo, pj plugins.JSONData) (Querier, error) { |
||||||
|
return NewDefaultQuerier(ri, pj, pluginClient, dsService, dsCache), nil |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
func NewQuerierProvider(factory QuerierFactoryFunc) *DefaultQuerierProvider { |
||||||
|
return &DefaultQuerierProvider{ |
||||||
|
factory: factory, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (p *DefaultQuerierProvider) Querier(ctx context.Context, ri common.ResourceInfo, pj plugins.JSONData) (Querier, error) { |
||||||
|
return p.factory(ctx, ri, pj) |
||||||
|
} |
||||||
|
|
||||||
|
// Querier is the interface that wraps the Query method.
|
||||||
|
type Querier interface { |
||||||
|
// Query runs the query on behalf of the user in context.
|
||||||
|
Query(ctx context.Context, query *backend.QueryDataRequest) (*backend.QueryDataResponse, error) |
||||||
|
// Health checks the health of the plugin.
|
||||||
|
Health(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) |
||||||
|
// Resource gets a resource plugin.
|
||||||
|
Resource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error |
||||||
|
// Datasource gets all data source plugins (with elevated permissions).
|
||||||
|
Datasource(ctx context.Context, name string) (*v0alpha1.DataSourceConnection, error) |
||||||
|
// Datasources lists all data sources (with elevated permissions).
|
||||||
|
Datasources(ctx context.Context) (*v0alpha1.DataSourceConnectionList, error) |
||||||
|
} |
||||||
|
|
||||||
|
type DefaultQuerier struct { |
||||||
|
connectionResourceInfo common.ResourceInfo |
||||||
|
pluginJSON plugins.JSONData |
||||||
|
pluginClient plugins.Client |
||||||
|
dsService datasources.DataSourceService |
||||||
|
dsCache datasources.CacheService |
||||||
|
} |
||||||
|
|
||||||
|
func NewDefaultQuerier( |
||||||
|
connectionResourceInfo common.ResourceInfo, |
||||||
|
pluginJSON plugins.JSONData, |
||||||
|
pluginClient plugins.Client, |
||||||
|
dsService datasources.DataSourceService, |
||||||
|
dsCache datasources.CacheService, |
||||||
|
) *DefaultQuerier { |
||||||
|
return &DefaultQuerier{ |
||||||
|
connectionResourceInfo: connectionResourceInfo, |
||||||
|
pluginJSON: pluginJSON, |
||||||
|
pluginClient: pluginClient, |
||||||
|
dsService: dsService, |
||||||
|
dsCache: dsCache, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (q *DefaultQuerier) Query(ctx context.Context, query *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { |
||||||
|
_, err := request.NamespaceInfoFrom(ctx, true) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return q.pluginClient.QueryData(ctx, query) |
||||||
|
} |
||||||
|
|
||||||
|
func (q *DefaultQuerier) Resource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error { |
||||||
|
_, err := request.NamespaceInfoFrom(ctx, true) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
return q.pluginClient.CallResource(ctx, req, sender) |
||||||
|
} |
||||||
|
|
||||||
|
func (q *DefaultQuerier) Health(ctx context.Context, query *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) { |
||||||
|
_, err := request.NamespaceInfoFrom(ctx, true) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return q.pluginClient.CheckHealth(ctx, query) |
||||||
|
} |
||||||
|
|
||||||
|
func (q *DefaultQuerier) Datasource(ctx context.Context, name string) (*v0alpha1.DataSourceConnection, error) { |
||||||
|
info, err := request.NamespaceInfoFrom(ctx, true) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
user, err := appcontext.User(ctx) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
ds, err := q.dsCache.GetDatasourceByUID(ctx, name, user, false) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return asConnection(q.connectionResourceInfo.TypeMeta(), ds, info.Value) |
||||||
|
} |
||||||
|
|
||||||
|
func (q *DefaultQuerier) Datasources(ctx context.Context) (*v0alpha1.DataSourceConnectionList, error) { |
||||||
|
info, err := request.NamespaceInfoFrom(ctx, true) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
ds, err := q.dsService.GetDataSourcesByType(ctx, &datasources.GetDataSourcesByTypeQuery{ |
||||||
|
OrgID: info.OrgID, |
||||||
|
Type: q.pluginJSON.ID, |
||||||
|
}) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return asConnectionList(q.connectionResourceInfo.TypeMeta(), ds, info.Value) |
||||||
|
} |
||||||
|
|
||||||
|
func asConnection(typeMeta metav1.TypeMeta, ds *datasources.DataSource, ns string) (*v0alpha1.DataSourceConnection, error) { |
||||||
|
v := &v0alpha1.DataSourceConnection{ |
||||||
|
TypeMeta: typeMeta, |
||||||
|
ObjectMeta: metav1.ObjectMeta{ |
||||||
|
Name: ds.UID, |
||||||
|
Namespace: ns, |
||||||
|
CreationTimestamp: metav1.NewTime(ds.Created), |
||||||
|
ResourceVersion: fmt.Sprintf("%d", ds.Updated.UnixMilli()), |
||||||
|
}, |
||||||
|
Title: ds.Name, |
||||||
|
} |
||||||
|
v.UID = utils.CalculateClusterWideUID(v) // indicates if the value changed on the server
|
||||||
|
meta, err := utils.MetaAccessor(v) |
||||||
|
if err != nil { |
||||||
|
meta.SetUpdatedTimestamp(&ds.Updated) |
||||||
|
} |
||||||
|
return v, err |
||||||
|
} |
||||||
|
|
||||||
|
func asConnectionList(typeMeta metav1.TypeMeta, dss []*datasources.DataSource, ns string) (*v0alpha1.DataSourceConnectionList, error) { |
||||||
|
result := &v0alpha1.DataSourceConnectionList{ |
||||||
|
Items: []v0alpha1.DataSourceConnection{}, |
||||||
|
} |
||||||
|
for _, ds := range dss { |
||||||
|
v, _ := asConnection(typeMeta, ds, ns) |
||||||
|
result.Items = append(result.Items, *v) |
||||||
|
} |
||||||
|
|
||||||
|
return result, nil |
||||||
|
} |
Loading…
Reference in new issue