diff --git a/.vscode/launch.json b/.vscode/launch.json index 0ff75cc92ac..c4cbc37dadc 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -23,6 +23,20 @@ "--secure-port=8443", "--runtime-config=testdata.datasource.grafana.app/v0alpha1=true"] }, + { + "name": "Run API Server (query-localhost)", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}/pkg/cmd/grafana/", + "env": {}, + "cwd": "${workspaceFolder}", + "args": ["apiserver", + "--secure-port=8443", + "--runtime-config=query.grafana.app/v0alpha1=true", + "--hg-url=http://localhost:3000", + "--hg-key=$HGAPIKEY"] + }, { "name": "Attach to Chrome", "port": 9222, diff --git a/pkg/registry/apis/datasource/plugincontext.go b/pkg/registry/apis/datasource/plugincontext.go index d5fa696052a..2c0a58aca12 100644 --- a/pkg/registry/apis/datasource/plugincontext.go +++ b/pkg/registry/apis/datasource/plugincontext.go @@ -31,6 +31,10 @@ type PluginDatasourceProvider interface { GetInstanceSettings(ctx context.Context, uid string) (*backend.DataSourceInstanceSettings, error) } +type ScopedPluginDatasourceProvider interface { + GetDatasourceProvider(pluginJson plugins.JSONData) PluginDatasourceProvider +} + // 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) @@ -39,18 +43,30 @@ type PluginContextWrapper interface { func ProvideDefaultPluginConfigs( dsService datasources.DataSourceService, dsCache datasources.CacheService, - contextProvider *plugincontext.Provider) PluginDatasourceProvider { - return &defaultPluginDatasourceProvider{ - plugin: plugins.JSONData{ - ID: datasources.DS_TESTDATA, - }, + contextProvider *plugincontext.Provider) ScopedPluginDatasourceProvider { + return &cachingDatasourceProvider{ dsService: dsService, dsCache: dsCache, contextProvider: contextProvider, } } -type defaultPluginDatasourceProvider struct { +type cachingDatasourceProvider struct { + dsService datasources.DataSourceService + dsCache datasources.CacheService + contextProvider *plugincontext.Provider +} + +func (q *cachingDatasourceProvider) GetDatasourceProvider(pluginJson plugins.JSONData) PluginDatasourceProvider { + return &scopedDatasourceProvider{ + plugin: pluginJson, + dsService: q.dsService, + dsCache: q.dsCache, + contextProvider: q.contextProvider, + } +} + +type scopedDatasourceProvider struct { plugin plugins.JSONData dsService datasources.DataSourceService dsCache datasources.CacheService @@ -58,10 +74,11 @@ type defaultPluginDatasourceProvider struct { } var ( - _ PluginDatasourceProvider = (*defaultPluginDatasourceProvider)(nil) + _ PluginDatasourceProvider = (*scopedDatasourceProvider)(nil) + _ ScopedPluginDatasourceProvider = (*cachingDatasourceProvider)(nil) ) -func (q *defaultPluginDatasourceProvider) Get(ctx context.Context, uid string) (*v0alpha1.DataSourceConnection, error) { +func (q *scopedDatasourceProvider) Get(ctx context.Context, uid string) (*v0alpha1.DataSourceConnection, error) { info, err := request.NamespaceInfoFrom(ctx, true) if err != nil { return nil, err @@ -77,7 +94,7 @@ func (q *defaultPluginDatasourceProvider) Get(ctx context.Context, uid string) ( return asConnection(ds, info.Value) } -func (q *defaultPluginDatasourceProvider) List(ctx context.Context) (*v0alpha1.DataSourceConnectionList, error) { +func (q *scopedDatasourceProvider) List(ctx context.Context) (*v0alpha1.DataSourceConnectionList, error) { info, err := request.NamespaceInfoFrom(ctx, true) if err != nil { return nil, err @@ -101,11 +118,9 @@ func (q *defaultPluginDatasourceProvider) List(ctx context.Context) (*v0alpha1.D return result, nil } -func (q *defaultPluginDatasourceProvider) GetInstanceSettings(ctx context.Context, uid string) (*backend.DataSourceInstanceSettings, error) { +func (q *scopedDatasourceProvider) GetInstanceSettings(ctx context.Context, 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 nil, fmt.Errorf("missing contextProvider") } return q.contextProvider.GetDataSourceInstanceSettings(ctx, uid) } diff --git a/pkg/registry/apis/datasource/register.go b/pkg/registry/apis/datasource/register.go index aa8267e8cb2..f54eabab17a 100644 --- a/pkg/registry/apis/datasource/register.go +++ b/pkg/registry/apis/datasource/register.go @@ -54,7 +54,7 @@ func RegisterAPIService( features featuremgmt.FeatureToggles, apiRegistrar builder.APIRegistrar, pluginClient plugins.Client, // access to everything - datasources PluginDatasourceProvider, + datasources ScopedPluginDatasourceProvider, contextProvider PluginContextWrapper, pluginStore pluginstore.Store, accessControl accesscontrol.AccessControl, @@ -69,7 +69,8 @@ func RegisterAPIService( all := pluginStore.Plugins(context.Background(), plugins.TypeDataSource) ids := []string{ "grafana-testdata-datasource", - // "prometheus", + "prometheus", + "graphite", } for _, ds := range all { @@ -79,7 +80,7 @@ func RegisterAPIService( builder, err = NewDataSourceAPIBuilder(ds.JSONData, pluginClient, - datasources, + datasources.GetDatasourceProvider(ds.JSONData), contextProvider, accessControl, ) diff --git a/pkg/registry/apis/query/query.go b/pkg/registry/apis/query/query.go index 3f9a1c2ff50..ff5829e00ae 100644 --- a/pkg/registry/apis/query/query.go +++ b/pkg/registry/apis/query/query.go @@ -119,7 +119,7 @@ func (b *QueryAPIBuilder) handleQuerySingleDatasource(ctx context.Context, req d return nil, err } - _, rsp, err := client.QueryData(ctx, *req.Request) + code, rsp, err := client.QueryData(ctx, *req.Request) if err == nil && rsp != nil { for _, q := range req.Request.Queries { if q.ResultAssertions != nil { @@ -135,6 +135,17 @@ func (b *QueryAPIBuilder) handleQuerySingleDatasource(ctx context.Context, req d } } } + + // Create a response object with the error when missing (happens for client errors like 404) + if rsp == nil && err != nil { + rsp = &backend.QueryDataResponse{Responses: make(backend.Responses)} + for _, q := range req.Request.Queries { + rsp.Responses[q.RefID] = backend.DataResponse{ + Status: backend.Status(code), + Error: err, + } + } + } return rsp, err } @@ -229,6 +240,9 @@ func (b *QueryAPIBuilder) handleExpressions(ctx context.Context, req parsedReque if qdr == nil { qdr = &backend.QueryDataResponse{} } + if qdr.Responses == nil { + qdr.Responses = make(backend.Responses) // avoid NPE for lookup + } now := start // <<< this should come from the original query parser vars := make(mathexp.Vars) for _, expression := range req.Expressions {