diff --git a/pkg/registry/apis/datasource/connections.go b/pkg/registry/apis/datasource/connections.go index 5d3d8fd1acc..c0ef499cbc7 100644 --- a/pkg/registry/apis/datasource/connections.go +++ b/pkg/registry/apis/datasource/connections.go @@ -20,9 +20,10 @@ var ( ) type connectionAccess struct { + pluginID string resourceInfo common.ResourceInfo tableConverter rest.TableConvertor - builder Querier + configs PluginConfigProvider } func (s *connectionAccess) New() runtime.Object { @@ -52,9 +53,9 @@ func (s *connectionAccess) ConvertToTable(ctx context.Context, object runtime.Ob } func (s *connectionAccess) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) { - return s.builder.Datasource(ctx, name) + return s.configs.GetDataSource(ctx, s.pluginID, name) } func (s *connectionAccess) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) { - return s.builder.Datasources(ctx) + return s.configs.ListDatasources(ctx, s.pluginID) } diff --git a/pkg/registry/apis/datasource/plugincontext.go b/pkg/registry/apis/datasource/plugincontext.go index 3908e4b5d04..bbb6a1b8551 100644 --- a/pkg/registry/apis/datasource/plugincontext.go +++ b/pkg/registry/apis/datasource/plugincontext.go @@ -2,10 +2,122 @@ package datasource import ( "context" + "fmt" "github.com/grafana/grafana-plugin-sdk-go/backend" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1" + "github.com/grafana/grafana/pkg/infra/appcontext" + "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" + "github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext" ) -type PluginContextProvider interface { - PluginContextForDataSource(ctx context.Context, pluginID, name string) (backend.PluginContext, error) +// 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 PluginConfigProvider interface { + // GetDataSource gets a specific datasource (that the user in context can see) + GetDataSource(ctx context.Context, pluginID, uid string) (*v0alpha1.DataSourceConnection, error) + + // ListDatasources lists all data sources the user in context can see + ListDatasources(ctx context.Context, pluginID string) (*v0alpha1.DataSourceConnectionList, error) + + // Return settings (decrypted!) for a specific plugin + // This will require "query" permission for the user in context + GetDataSourceInstanceSettings(ctx context.Context, pluginID, uid string) (*backend.DataSourceInstanceSettings, error) +} + +// PluginContext requires adding system settings (feature flags, etc) to the datasource config +type PluginContextWrapper interface { + PluginContextForDataSource(ctx context.Context, datasourceSettings *backend.DataSourceInstanceSettings) (backend.PluginContext, error) +} + +func ProvideDefaultPluginConfigs( + dsService datasources.DataSourceService, + dsCache datasources.CacheService, + contextProvider *plugincontext.Provider) PluginConfigProvider { + return &defaultPluginConfigProvider{ + dsService: dsService, + dsCache: dsCache, + contextProvider: contextProvider, + } +} + +type defaultPluginConfigProvider struct { + dsService datasources.DataSourceService + dsCache datasources.CacheService + contextProvider *plugincontext.Provider +} + +var ( + _ PluginConfigProvider = (*defaultPluginConfigProvider)(nil) +) + +func (q *defaultPluginConfigProvider) GetDataSource(ctx context.Context, pluginID, uid 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, uid, user, false) + if err != nil { + return nil, err + } + return asConnection(ds, info.Value) +} + +func (q *defaultPluginConfigProvider) ListDatasources(ctx context.Context, pluginID string) (*v0alpha1.DataSourceConnectionList, error) { + info, err := request.NamespaceInfoFrom(ctx, true) + if err != nil { + return nil, err + } + + dss, err := q.dsService.GetDataSourcesByType(ctx, &datasources.GetDataSourcesByTypeQuery{ + OrgID: info.OrgID, + Type: pluginID, + }) + if err != nil { + return nil, err + } + result := &v0alpha1.DataSourceConnectionList{ + Items: []v0alpha1.DataSourceConnection{}, + } + for _, ds := range dss { + v, _ := asConnection(ds, info.Value) + result.Items = append(result.Items, *v) + } + return result, nil +} + +func (q *defaultPluginConfigProvider) GetDataSourceInstanceSettings(ctx context.Context, pluginID, uid string) (*backend.DataSourceInstanceSettings, error) { + if q.contextProvider == nil { + // NOTE!!! this is only here for the standalone example + // if we cleanup imports this can throw an error + return nil, nil + } + 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 = 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 } diff --git a/pkg/registry/apis/datasource/querier.go b/pkg/registry/apis/datasource/querier.go deleted file mode 100644 index 0bf6c26cafa..00000000000 --- a/pkg/registry/apis/datasource/querier.go +++ /dev/null @@ -1,169 +0,0 @@ -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 -} diff --git a/pkg/registry/apis/datasource/register.go b/pkg/registry/apis/datasource/register.go index ebd5d196205..890445fb6cb 100644 --- a/pkg/registry/apis/datasource/register.go +++ b/pkg/registry/apis/datasource/register.go @@ -16,6 +16,8 @@ import ( openapi "k8s.io/kube-openapi/pkg/common" "k8s.io/utils/strings/slices" + "github.com/grafana/grafana-plugin-sdk-go/backend" + common "github.com/grafana/grafana/pkg/apis/common/v0alpha1" "github.com/grafana/grafana/pkg/apis/datasource/v0alpha1" "github.com/grafana/grafana/pkg/plugins" @@ -32,17 +34,19 @@ var _ grafanaapiserver.APIGroupBuilder = (*DataSourceAPIBuilder)(nil) type DataSourceAPIBuilder struct { connectionResourceInfo common.ResourceInfo - pluginJSON plugins.JSONData - querier Querier - pluginContext PluginContextProvider - accessControl accesscontrol.AccessControl + pluginJSON plugins.JSONData + client plugins.Client // will only ever be called with the same pluginid! + pluginsConfig PluginConfigProvider + contextProvider PluginContextWrapper + accessControl accesscontrol.AccessControl } func RegisterAPIService( - querierProvider QuerierProvider, features featuremgmt.FeatureToggles, apiRegistrar grafanaapiserver.APIRegistrar, - pluginContext PluginContextProvider, + pluginClient plugins.Client, // access to everything + pluginConfigs PluginConfigProvider, + contextProvider PluginContextWrapper, pluginStore pluginstore.Store, accessControl accesscontrol.AccessControl, ) (*DataSourceAPIBuilder, error) { @@ -63,7 +67,12 @@ func RegisterAPIService( continue // skip this one } - builder, err = NewDataSourceAPIBuilder(ds.JSONData, querierProvider, pluginContext, accessControl) + builder, err = NewDataSourceAPIBuilder(ds.JSONData, + pluginClient, + pluginConfigs, + contextProvider, + accessControl, + ) if err != nil { return nil, err } @@ -74,24 +83,21 @@ func RegisterAPIService( func NewDataSourceAPIBuilder( plugin plugins.JSONData, - querierProvider QuerierProvider, - pluginContext PluginContextProvider, + client plugins.Client, + pluginsConfig PluginConfigProvider, + contextProvider PluginContextWrapper, accessControl accesscontrol.AccessControl) (*DataSourceAPIBuilder, error) { ri, err := resourceFromPluginID(plugin.ID) if err != nil { return nil, err } - querier, err := querierProvider.Querier(context.Background(), ri, plugin) - if err != nil { - return nil, err - } - return &DataSourceAPIBuilder{ connectionResourceInfo: ri, pluginJSON: plugin, - querier: querier, - pluginContext: pluginContext, + client: client, + pluginsConfig: pluginsConfig, + contextProvider: contextProvider, accessControl: accessControl, }, nil } @@ -148,7 +154,8 @@ func (b *DataSourceAPIBuilder) GetAPIGroupInfo( conn := b.connectionResourceInfo storage[conn.StoragePath()] = &connectionAccess{ - builder: b.querier, + pluginID: b.pluginJSON.ID, + configs: b.pluginsConfig, resourceInfo: conn, tableConverter: utils.NewTableConverter( conn.GroupResource(), @@ -191,6 +198,14 @@ func (b *DataSourceAPIBuilder) GetAPIGroupInfo( return &apiGroupInfo, nil } +func (b *DataSourceAPIBuilder) getPluginContext(ctx context.Context, uid string) (backend.PluginContext, error) { + instance, err := b.pluginsConfig.GetDataSourceInstanceSettings(ctx, b.pluginJSON.ID, uid) + if err != nil { + return backend.PluginContext{}, err + } + return b.contextProvider.PluginContextForDataSource(ctx, instance) +} + func (b *DataSourceAPIBuilder) GetOpenAPIDefinitions() openapi.GetOpenAPIDefinitions { return v0alpha1.GetOpenAPIDefinitions } diff --git a/pkg/registry/apis/datasource/standalone.go b/pkg/registry/apis/datasource/standalone.go index 07cbe6fbb0b..30911a67332 100644 --- a/pkg/registry/apis/datasource/standalone.go +++ b/pkg/registry/apis/datasource/standalone.go @@ -5,9 +5,12 @@ import ( "fmt" "github.com/grafana/grafana-plugin-sdk-go/backend" + v1 "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/plugins" + "github.com/grafana/grafana/pkg/services/accesscontrol/actest" + "github.com/grafana/grafana/pkg/services/grafana-apiserver/endpoints/request" "github.com/grafana/grafana/pkg/setting" testdatasource "github.com/grafana/grafana/pkg/tsdb/grafana-testdata-datasource" ) @@ -21,6 +24,22 @@ func NewTestDataAPIServer(group string) (*DataSourceAPIBuilder, error) { return nil, fmt.Errorf("only %s is currently supported", pluginID) } + // Run standalone with zero dependencies + if true { + return NewDataSourceAPIBuilder( + plugins.JSONData{ + ID: pluginID, + }, + testdatasource.ProvideService(), // the client + &testdataPluginConfigProvider{ + startup: v1.Now(), + }, + &testdataPluginConfigProvider{}, // stub + &actest.FakeAccessControl{ExpectedEvaluate: true}, + ) + } + + // Otherwise manually wire up access to testdata cfg, err := setting.NewCfgFromArgs(setting.CommandLineArgs{ // TODO: Add support for args? }) @@ -37,21 +56,69 @@ func NewTestDataAPIServer(group string) (*DataSourceAPIBuilder, error) { if !exists { return nil, fmt.Errorf("plugin %s not found", pluginID) } - - var testsDataQuerierFactory QuerierFactoryFunc = func(ctx context.Context, ri common.ResourceInfo, pj plugins.JSONData) (Querier, error) { - return NewDefaultQuerier(ri, td.JSONData, testdatasource.ProvideService(), dsService, dsCache), nil - } - return NewDataSourceAPIBuilder( td.JSONData, - NewQuerierProvider(testsDataQuerierFactory), - &TestDataPluginContextProvider{}, + testdatasource.ProvideService(), // the client + &defaultPluginConfigProvider{ + dsService: dsService, + dsCache: dsCache, + }, + &testdataPluginConfigProvider{}, // stub accessControl, ) } -type TestDataPluginContextProvider struct{} - -func (p *TestDataPluginContextProvider) PluginContextForDataSource(_ context.Context, _, _ string) (backend.PluginContext, error) { - return backend.PluginContext{}, nil +// Simple stub for standalone testing +type testdataPluginConfigProvider struct { + startup v1.Time +} + +var ( + _ PluginConfigProvider = (*testdataPluginConfigProvider)(nil) +) + +// GetDataSource implements PluginConfigProvider. +func (p *testdataPluginConfigProvider) GetDataSource(ctx context.Context, pluginID string, uid string) (*v0alpha1.DataSourceConnection, error) { + all, err := p.ListDatasources(ctx, pluginID) + if err != nil { + return nil, err + } + for idx, v := range all.Items { + if v.Name == uid { + return &all.Items[idx], nil + } + } + return nil, fmt.Errorf("not found") +} + +// ListDatasources implements PluginConfigProvider. +func (p *testdataPluginConfigProvider) ListDatasources(ctx context.Context, pluginID string) (*v0alpha1.DataSourceConnectionList, error) { + info, err := request.NamespaceInfoFrom(ctx, true) + if err != nil { + return nil, err + } + + return &v0alpha1.DataSourceConnectionList{ + TypeMeta: v0alpha1.GenericConnectionResourceInfo.TypeMeta(), + Items: []v0alpha1.DataSourceConnection{ + { + ObjectMeta: v1.ObjectMeta{ + Name: "PD8C576611E62080A", + Namespace: info.Value, // the raw namespace value + CreationTimestamp: p.startup, + }, + Title: "gdev-testdata", + }, + }, + }, nil +} + +// PluginContextForDataSource implements PluginConfigProvider. +func (*testdataPluginConfigProvider) GetDataSourceInstanceSettings(ctx context.Context, pluginID, uid string) (*backend.DataSourceInstanceSettings, error) { + return &backend.DataSourceInstanceSettings{}, nil +} + +// PluginContextWrapper +func (*testdataPluginConfigProvider) PluginContextForDataSource(ctx context.Context, datasourceSettings *backend.DataSourceInstanceSettings) (backend.PluginContext, error) { + return backend.PluginContext{DataSourceInstanceSettings: datasourceSettings}, nil } diff --git a/pkg/registry/apis/datasource/sub_health.go b/pkg/registry/apis/datasource/sub_health.go index 48199be0c4e..ebd4fa0b8e8 100644 --- a/pkg/registry/apis/datasource/sub_health.go +++ b/pkg/registry/apis/datasource/sub_health.go @@ -35,13 +35,13 @@ func (r *subHealthREST) NewConnectOptions() (runtime.Object, bool, string) { func (r *subHealthREST) Connect(ctx context.Context, name string, opts runtime.Object, responder rest.Responder) (http.Handler, error) { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - pluginCtx, err := r.builder.pluginContext.PluginContextForDataSource(ctx, r.builder.pluginJSON.ID, name) + pluginCtx, err := r.builder.getPluginContext(ctx, name) if err != nil { responder.Error(err) return } - healthResponse, err := r.builder.querier.Health(ctx, &backend.CheckHealthRequest{ + healthResponse, err := r.builder.client.CheckHealth(ctx, &backend.CheckHealthRequest{ PluginContext: pluginCtx, }) if err != nil { diff --git a/pkg/registry/apis/datasource/sub_query.go b/pkg/registry/apis/datasource/sub_query.go index b46f1043f3c..57ee6a58d4f 100644 --- a/pkg/registry/apis/datasource/sub_query.go +++ b/pkg/registry/apis/datasource/sub_query.go @@ -71,7 +71,7 @@ func (r *subQueryREST) readQueries(req *http.Request) ([]backend.DataQuery, erro } func (r *subQueryREST) Connect(ctx context.Context, name string, opts runtime.Object, responder rest.Responder) (http.Handler, error) { - pluginCtx, err := r.builder.pluginContext.PluginContextForDataSource(ctx, r.builder.pluginJSON.ID, name) + pluginCtx, err := r.builder.getPluginContext(ctx, name) if err != nil { return nil, err } @@ -83,7 +83,7 @@ func (r *subQueryREST) Connect(ctx context.Context, name string, opts runtime.Ob return } - queryResponse, err := r.builder.querier.Query(ctx, &backend.QueryDataRequest{ + queryResponse, err := r.builder.client.QueryData(ctx, &backend.QueryDataRequest{ PluginContext: pluginCtx, Queries: queries, // Headers: // from context diff --git a/pkg/registry/apis/datasource/sub_resource.go b/pkg/registry/apis/datasource/sub_resource.go index 3feb4022564..67766ab3a45 100644 --- a/pkg/registry/apis/datasource/sub_resource.go +++ b/pkg/registry/apis/datasource/sub_resource.go @@ -46,7 +46,7 @@ func (r *subResourceREST) NewConnectOptions() (runtime.Object, bool, string) { } func (r *subResourceREST) Connect(ctx context.Context, name string, opts runtime.Object, responder rest.Responder) (http.Handler, error) { - pluginCtx, err := r.builder.pluginContext.PluginContextForDataSource(ctx, r.builder.pluginJSON.ID, name) + pluginCtx, err := r.builder.getPluginContext(ctx, name) if err != nil { return nil, err } @@ -65,7 +65,7 @@ func (r *subResourceREST) Connect(ctx context.Context, name string, opts runtime } path := req.URL.Path[idx+len("/resource"):] - err = r.builder.querier.Resource(ctx, &backend.CallResourceRequest{ + err = r.builder.client.CallResource(ctx, &backend.CallResourceRequest{ PluginContext: pluginCtx, Path: path, Method: req.Method, diff --git a/pkg/registry/apis/wireset.go b/pkg/registry/apis/wireset.go index 9d484cea29f..3497faba067 100644 --- a/pkg/registry/apis/wireset.go +++ b/pkg/registry/apis/wireset.go @@ -15,10 +15,10 @@ import ( var WireSet = wire.NewSet( ProvideRegistryServiceSink, // dummy background service that forces registration - wire.Bind(new(datasource.QuerierProvider), new(*datasource.DefaultQuerierProvider)), - datasource.ProvideDefaultQuerierProvider, + // read-only datasource abstractions plugincontext.ProvideService, - wire.Bind(new(datasource.PluginContextProvider), new(*plugincontext.Provider)), + wire.Bind(new(datasource.PluginContextWrapper), new(*plugincontext.Provider)), + datasource.ProvideDefaultPluginConfigs, // Each must be added here *and* in the ServiceSink above playlist.RegisterAPIService, diff --git a/pkg/services/pluginsintegration/plugincontext/plugincontext.go b/pkg/services/pluginsintegration/plugincontext/plugincontext.go index 5c8440419ca..3f950826b0c 100644 --- a/pkg/services/pluginsintegration/plugincontext/plugincontext.go +++ b/pkg/services/pluginsintegration/plugincontext/plugincontext.go @@ -132,9 +132,22 @@ func (p *Provider) GetWithDataSource(ctx context.Context, pluginID string, user return pCtx, nil } +func (p *Provider) GetDataSourceInstanceSettings(ctx context.Context, uid string) (*backend.DataSourceInstanceSettings, error) { + user, err := appcontext.User(ctx) + if err != nil { + return nil, err + } + ds, err := p.dataSourceCache.GetDatasourceByUID(ctx, uid, user, false) + if err != nil { + return nil, err + } + return adapters.ModelToInstanceSettings(ds, p.decryptSecureJsonDataFn(ctx)) +} + // PluginContextForDataSource will retrieve plugin context by the provided pluginID and datasource UID / K8s name. // This is intended to be used for datasource API server plugin requests. -func (p *Provider) PluginContextForDataSource(ctx context.Context, pluginID, name string) (backend.PluginContext, error) { +func (p *Provider) PluginContextForDataSource(ctx context.Context, datasourceSettings *backend.DataSourceInstanceSettings) (backend.PluginContext, error) { + pluginID := datasourceSettings.Type plugin, exists := p.pluginStore.Plugin(ctx, pluginID) if !exists { return backend.PluginContext{}, plugins.ErrPluginNotRegistered @@ -144,11 +157,6 @@ func (p *Provider) PluginContextForDataSource(ctx context.Context, pluginID, nam if err != nil { return backend.PluginContext{}, err } - ds, err := p.dataSourceCache.GetDatasourceByUID(ctx, name, user, false) - if err != nil { - return backend.PluginContext{}, err - } - pCtx := backend.PluginContext{ PluginID: plugin.ID, PluginVersion: plugin.Info.Version, @@ -158,10 +166,6 @@ func (p *Provider) PluginContextForDataSource(ctx context.Context, pluginID, nam pCtx.User = adapters.BackendUserFromSignedInUser(user) } - datasourceSettings, err := adapters.ModelToInstanceSettings(ds, p.decryptSecureJsonDataFn(ctx)) - if err != nil { - return pCtx, err - } pCtx.DataSourceInstanceSettings = datasourceSettings settings := p.pluginEnvVars.GetConfigMap(ctx, pluginID, plugin.ExternalService)