DataSource: Support config CRUD from apiservers (#106996)
This commit is contained in:
@@ -5,27 +5,33 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/apis/datasource/v0alpha1"
|
||||
datasourceV0 "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
gapiutil "github.com/grafana/grafana/pkg/services/apiserver/utils"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
// This provides access to settings saved in the database.
|
||||
// Authorization checks will happen within each function, and the user in ctx will
|
||||
// limit which namespace/tenant/org we are talking to
|
||||
type PluginDatasourceProvider interface {
|
||||
// Get gets a specific datasource (that the user in context can see)
|
||||
Get(ctx context.Context, uid string) (*v0alpha1.DataSourceConnection, error)
|
||||
// Get a single data source (any type)
|
||||
GetDataSource(ctx context.Context, uid string) (*datasourceV0.DataSource, error)
|
||||
|
||||
// List lists all data sources the user in context can see
|
||||
List(ctx context.Context) (*v0alpha1.DataSourceConnectionList, error)
|
||||
// List all datasources (any type)
|
||||
ListDataSources(ctx context.Context) (*datasourceV0.DataSourceList, error)
|
||||
|
||||
// Create a data source
|
||||
CreateDataSource(ctx context.Context, ds *datasourceV0.DataSource) (*datasourceV0.DataSource, error)
|
||||
|
||||
// Update a data source
|
||||
UpdateDataSource(ctx context.Context, ds *datasourceV0.DataSource) (*datasourceV0.DataSource, error)
|
||||
|
||||
// Delete a data source (any type)
|
||||
DeleteDataSource(ctx context.Context, uid string) error
|
||||
|
||||
// Return settings (decrypted!) for a specific plugin
|
||||
// This will require "query" permission for the user in context
|
||||
@@ -44,11 +50,16 @@ type PluginContextWrapper interface {
|
||||
func ProvideDefaultPluginConfigs(
|
||||
dsService datasources.DataSourceService,
|
||||
dsCache datasources.CacheService,
|
||||
contextProvider *plugincontext.Provider) ScopedPluginDatasourceProvider {
|
||||
contextProvider *plugincontext.Provider,
|
||||
cfg *setting.Cfg,
|
||||
) ScopedPluginDatasourceProvider {
|
||||
return &cachingDatasourceProvider{
|
||||
dsService: dsService,
|
||||
dsCache: dsCache,
|
||||
contextProvider: contextProvider,
|
||||
converter: &converter{
|
||||
mapper: request.GetNamespaceMapper(cfg),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,14 +67,22 @@ type cachingDatasourceProvider struct {
|
||||
dsService datasources.DataSourceService
|
||||
dsCache datasources.CacheService
|
||||
contextProvider *plugincontext.Provider
|
||||
converter *converter
|
||||
}
|
||||
|
||||
func (q *cachingDatasourceProvider) GetDatasourceProvider(pluginJson plugins.JSONData) PluginDatasourceProvider {
|
||||
group, _ := plugins.GetDatasourceGroupNameFromPluginID(pluginJson.ID)
|
||||
return &scopedDatasourceProvider{
|
||||
plugin: pluginJson,
|
||||
dsService: q.dsService,
|
||||
dsCache: q.dsCache,
|
||||
contextProvider: q.contextProvider,
|
||||
converter: &converter{
|
||||
mapper: q.converter.mapper,
|
||||
plugin: pluginJson.ID,
|
||||
alias: pluginJson.AliasIDs,
|
||||
group: group,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +91,7 @@ type scopedDatasourceProvider struct {
|
||||
dsService datasources.DataSourceService
|
||||
dsCache datasources.CacheService
|
||||
contextProvider *plugincontext.Provider
|
||||
converter *converter
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -79,11 +99,62 @@ var (
|
||||
_ ScopedPluginDatasourceProvider = (*cachingDatasourceProvider)(nil)
|
||||
)
|
||||
|
||||
func (q *scopedDatasourceProvider) Get(ctx context.Context, uid string) (*v0alpha1.DataSourceConnection, error) {
|
||||
info, err := request.NamespaceInfoFrom(ctx, true)
|
||||
func (q *scopedDatasourceProvider) GetInstanceSettings(ctx context.Context, uid string) (*backend.DataSourceInstanceSettings, error) {
|
||||
if q.contextProvider == nil {
|
||||
return nil, fmt.Errorf("missing contextProvider")
|
||||
}
|
||||
return q.contextProvider.GetDataSourceInstanceSettings(ctx, uid)
|
||||
}
|
||||
|
||||
// CreateDataSource implements PluginDatasourceProvider.
|
||||
func (q *scopedDatasourceProvider) CreateDataSource(ctx context.Context, ds *datasourceV0.DataSource) (*datasourceV0.DataSource, error) {
|
||||
cmd, err := q.converter.toAddCommand(ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := q.dsService.AddDataSource(ctx, cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return q.converter.asDataSource(out)
|
||||
}
|
||||
|
||||
// UpdateDataSource implements PluginDatasourceProvider.
|
||||
func (q *scopedDatasourceProvider) UpdateDataSource(ctx context.Context, ds *datasourceV0.DataSource) (*datasourceV0.DataSource, error) {
|
||||
cmd, err := q.converter.toUpdateCommand(ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := q.dsService.UpdateDataSource(ctx, cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return q.converter.asDataSource(out)
|
||||
}
|
||||
|
||||
// Delete implements PluginDatasourceProvider.
|
||||
func (q *scopedDatasourceProvider) DeleteDataSource(ctx context.Context, uid string) error {
|
||||
user, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ds, err := q.dsCache.GetDatasourceByUID(ctx, uid, user, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ds == nil {
|
||||
return fmt.Errorf("not found")
|
||||
}
|
||||
return q.dsService.DeleteDataSource(ctx, &datasources.DeleteDataSourceCommand{
|
||||
ID: ds.ID,
|
||||
UID: ds.UID,
|
||||
OrgID: ds.OrgID,
|
||||
Name: ds.Name,
|
||||
})
|
||||
}
|
||||
|
||||
// GetDataSource implements PluginDatasourceProvider.
|
||||
func (q *scopedDatasourceProvider) GetDataSource(ctx context.Context, uid string) (*datasourceV0.DataSource, error) {
|
||||
user, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -92,10 +163,11 @@ func (q *scopedDatasourceProvider) Get(ctx context.Context, uid string) (*v0alph
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return asConnection(ds, info.Value)
|
||||
return q.converter.asDataSource(ds)
|
||||
}
|
||||
|
||||
func (q *scopedDatasourceProvider) List(ctx context.Context) (*v0alpha1.DataSourceConnectionList, error) {
|
||||
// ListDataSource implements PluginDatasourceProvider.
|
||||
func (q *scopedDatasourceProvider) ListDataSources(ctx context.Context) (*datasourceV0.DataSourceList, error) {
|
||||
info, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -109,37 +181,12 @@ func (q *scopedDatasourceProvider) List(ctx context.Context) (*v0alpha1.DataSour
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := &v0alpha1.DataSourceConnectionList{
|
||||
Items: []v0alpha1.DataSourceConnection{},
|
||||
result := &datasourceV0.DataSourceList{
|
||||
Items: []datasourceV0.DataSource{},
|
||||
}
|
||||
for _, ds := range dss {
|
||||
v, _ := asConnection(ds, info.Value)
|
||||
v, _ := q.converter.asDataSource(ds)
|
||||
result.Items = append(result.Items, *v)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (q *scopedDatasourceProvider) GetInstanceSettings(ctx context.Context, uid string) (*backend.DataSourceInstanceSettings, error) {
|
||||
if q.contextProvider == nil {
|
||||
return nil, fmt.Errorf("missing contextProvider")
|
||||
}
|
||||
return q.contextProvider.GetDataSourceInstanceSettings(ctx, uid)
|
||||
}
|
||||
|
||||
func asConnection(ds *datasources.DataSource, ns string) (*v0alpha1.DataSourceConnection, error) {
|
||||
v := &v0alpha1.DataSourceConnection{
|
||||
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 = gapiutil.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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user