mirror of https://github.com/grafana/grafana.git
Plugins: Core plugins register via backend factory provider (#43171)
* refactoring store interface and init flow * fix import * fix linter * refactor resource calling * load with class * re-order args * fix tests * fix linter * remove old creator * add custom config struct * fix some tests * cleanup * fix * tackle plugins * fix linter * refactor and fix test * add connect failure error * add fix for azure, cloud monitoring and test data * restructure * remove unused err * add fake tracer for test * fix grafana ds plugin
This commit is contained in:
parent
2fd76ecaf7
commit
7fbc7d019a
|
|
@ -0,0 +1,100 @@
|
||||||
|
package coreplugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/azuremonitor"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/cloudmonitoring"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/cloudwatch"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/elasticsearch"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/grafanads"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/graphite"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/influxdb"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/loki"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/mssql"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/mysql"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/opentsdb"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/postgres"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/prometheus"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/tempo"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/testdatasource"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CloudWatch = "cloudwatch"
|
||||||
|
CloudMonitoring = "stackdriver"
|
||||||
|
AzureMonitor = "grafana-azure-monitor-datasource"
|
||||||
|
Elasticsearch = "elasticsearch"
|
||||||
|
Graphite = "graphite"
|
||||||
|
InfluxDB = "influxdb"
|
||||||
|
Loki = "loki"
|
||||||
|
OpenTSDB = "opentsdb"
|
||||||
|
Prometheus = "prometheus"
|
||||||
|
Tempo = "tempo"
|
||||||
|
TestData = "testdata"
|
||||||
|
PostgreSQL = "postgres"
|
||||||
|
MySQL = "mysql"
|
||||||
|
MSSQL = "mssql"
|
||||||
|
Grafana = "grafana"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Registry struct {
|
||||||
|
store map[string]backendplugin.PluginFactoryFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRegistry(store map[string]backendplugin.PluginFactoryFunc) *Registry {
|
||||||
|
return &Registry{
|
||||||
|
store: store,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProvideCoreRegistry(am *azuremonitor.Service, cw *cloudwatch.CloudWatchService, cm *cloudmonitoring.Service,
|
||||||
|
es *elasticsearch.Service, grap *graphite.Service, idb *influxdb.Service, lk *loki.Service, otsdb *opentsdb.Service,
|
||||||
|
pr *prometheus.Service, t *tempo.Service, td *testdatasource.Service, pg *postgres.Service, my *mysql.Service,
|
||||||
|
ms *mssql.Service, graf *grafanads.Service) *Registry {
|
||||||
|
return NewRegistry(map[string]backendplugin.PluginFactoryFunc{
|
||||||
|
CloudWatch: asBackendPlugin(cw.Executor),
|
||||||
|
CloudMonitoring: asBackendPlugin(cm),
|
||||||
|
AzureMonitor: asBackendPlugin(am),
|
||||||
|
Elasticsearch: asBackendPlugin(es),
|
||||||
|
Graphite: asBackendPlugin(grap),
|
||||||
|
InfluxDB: asBackendPlugin(idb),
|
||||||
|
Loki: asBackendPlugin(lk),
|
||||||
|
OpenTSDB: asBackendPlugin(otsdb),
|
||||||
|
Prometheus: asBackendPlugin(pr),
|
||||||
|
Tempo: asBackendPlugin(t),
|
||||||
|
TestData: asBackendPlugin(td),
|
||||||
|
PostgreSQL: asBackendPlugin(pg),
|
||||||
|
MySQL: asBackendPlugin(my),
|
||||||
|
MSSQL: asBackendPlugin(ms),
|
||||||
|
Grafana: asBackendPlugin(graf),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *Registry) Get(pluginID string) backendplugin.PluginFactoryFunc {
|
||||||
|
return cr.store[pluginID]
|
||||||
|
}
|
||||||
|
|
||||||
|
func asBackendPlugin(svc interface{}) backendplugin.PluginFactoryFunc {
|
||||||
|
opts := backend.ServeOpts{}
|
||||||
|
if queryHandler, ok := svc.(backend.QueryDataHandler); ok {
|
||||||
|
opts.QueryDataHandler = queryHandler
|
||||||
|
}
|
||||||
|
if resourceHandler, ok := svc.(backend.CallResourceHandler); ok {
|
||||||
|
opts.CallResourceHandler = resourceHandler
|
||||||
|
}
|
||||||
|
if streamHandler, ok := svc.(backend.StreamHandler); ok {
|
||||||
|
opts.StreamHandler = streamHandler
|
||||||
|
}
|
||||||
|
if healthHandler, ok := svc.(backend.CheckHealthHandler); ok {
|
||||||
|
opts.CheckHealthHandler = healthHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.QueryDataHandler != nil || opts.CallResourceHandler != nil ||
|
||||||
|
opts.CheckHealthHandler != nil || opts.StreamHandler != nil {
|
||||||
|
return New(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -7,18 +7,23 @@ import (
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/grpcplugin"
|
"github.com/grafana/grafana/pkg/plugins/backendplugin/grpcplugin"
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2"
|
"github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Service struct{}
|
type Service struct {
|
||||||
|
coreRegistry *coreplugin.Registry
|
||||||
func ProvideService() *Service {
|
|
||||||
return &Service{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Service) BackendFactory(ctx context.Context, p *plugins.Plugin) backendplugin.PluginFactoryFunc {
|
func ProvideService(coreRegistry *coreplugin.Registry) *Service {
|
||||||
for _, provider := range []PluginBackendProvider{RendererProvider, DefaultProvider} {
|
return &Service{
|
||||||
|
coreRegistry: coreRegistry,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) BackendFactory(ctx context.Context, p *plugins.Plugin) backendplugin.PluginFactoryFunc {
|
||||||
|
for _, provider := range []PluginBackendProvider{CorePluginProvider(ctx, s.coreRegistry), RendererProvider, DefaultProvider} {
|
||||||
if factory := provider(ctx, p); factory != nil {
|
if factory := provider(ctx, p); factory != nil {
|
||||||
return factory
|
return factory
|
||||||
}
|
}
|
||||||
|
|
@ -43,6 +48,17 @@ var RendererProvider PluginBackendProvider = func(_ context.Context, p *plugins.
|
||||||
}
|
}
|
||||||
|
|
||||||
var DefaultProvider PluginBackendProvider = func(_ context.Context, p *plugins.Plugin) backendplugin.PluginFactoryFunc {
|
var DefaultProvider PluginBackendProvider = func(_ context.Context, p *plugins.Plugin) backendplugin.PluginFactoryFunc {
|
||||||
|
// TODO check for executable
|
||||||
cmd := plugins.ComposePluginStartCommand(p.Executable)
|
cmd := plugins.ComposePluginStartCommand(p.Executable)
|
||||||
return grpcplugin.NewBackendPlugin(p.ID, filepath.Join(p.PluginDir, cmd))
|
return grpcplugin.NewBackendPlugin(p.ID, filepath.Join(p.PluginDir, cmd))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var CorePluginProvider = func(ctx context.Context, registry *coreplugin.Registry) PluginBackendProvider {
|
||||||
|
return func(_ context.Context, p *plugins.Plugin) backendplugin.PluginFactoryFunc {
|
||||||
|
if !p.IsCorePlugin() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return registry.Get(p.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ import (
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/provider"
|
"github.com/grafana/grafana/pkg/plugins/backendplugin/provider"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager/loader/finder"
|
"github.com/grafana/grafana/pkg/plugins/manager/loader/finder"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager/loader/initializer"
|
"github.com/grafana/grafana/pkg/plugins/manager/loader/initializer"
|
||||||
|
|
@ -904,7 +906,7 @@ func newLoader(cfg *plugins.Cfg) *Loader {
|
||||||
return &Loader{
|
return &Loader{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
pluginFinder: finder.New(),
|
pluginFinder: finder.New(),
|
||||||
pluginInitializer: initializer.New(cfg, &provider.Service{}, &fakeLicensingService{}),
|
pluginInitializer: initializer.New(cfg, provider.ProvideService(coreplugin.NewRegistry(make(map[string]backendplugin.PluginFactoryFunc))), &fakeLicensingService{}),
|
||||||
signatureValidator: signature.NewValidator(&signature.UnsignedPluginAuthorizer{Cfg: cfg}),
|
signatureValidator: signature.NewValidator(&signature.UnsignedPluginAuthorizer{Cfg: cfg}),
|
||||||
errs: make(map[string]*plugins.SignatureError),
|
errs: make(map[string]*plugins.SignatureError),
|
||||||
log: &fakeLogger{},
|
log: &fakeLogger{},
|
||||||
|
|
|
||||||
|
|
@ -471,17 +471,10 @@ func (m *PluginManager) shutdown(ctx context.Context) {
|
||||||
|
|
||||||
// corePluginPaths provides a list of the Core plugin paths which need to be scanned on init()
|
// corePluginPaths provides a list of the Core plugin paths which need to be scanned on init()
|
||||||
func corePluginPaths(cfg *setting.Cfg) []string {
|
func corePluginPaths(cfg *setting.Cfg) []string {
|
||||||
datasourcePaths := []string{
|
datasourcePaths := filepath.Join(cfg.StaticRootPath, "app/plugins/datasource")
|
||||||
filepath.Join(cfg.StaticRootPath, "app/plugins/datasource/alertmanager"),
|
|
||||||
filepath.Join(cfg.StaticRootPath, "app/plugins/datasource/dashboard"),
|
|
||||||
filepath.Join(cfg.StaticRootPath, "app/plugins/datasource/jaeger"),
|
|
||||||
filepath.Join(cfg.StaticRootPath, "app/plugins/datasource/mixed"),
|
|
||||||
filepath.Join(cfg.StaticRootPath, "app/plugins/datasource/zipkin"),
|
|
||||||
}
|
|
||||||
|
|
||||||
panelsPath := filepath.Join(cfg.StaticRootPath, "app/plugins/panel")
|
panelsPath := filepath.Join(cfg.StaticRootPath, "app/plugins/panel")
|
||||||
|
|
||||||
return append(datasourcePaths, panelsPath)
|
return []string{datasourcePaths, panelsPath}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pluginSettingPaths provides a plugin paths defined in cfg.PluginSettings which need to be scanned on init()
|
// pluginSettingPaths provides a plugin paths defined in cfg.PluginSettings which need to be scanned on init()
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,38 @@ package manager
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net/http"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/provider"
|
"github.com/grafana/grafana/pkg/plugins/backendplugin/provider"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager/loader"
|
"github.com/grafana/grafana/pkg/plugins/manager/loader"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager/signature"
|
"github.com/grafana/grafana/pkg/plugins/manager/signature"
|
||||||
"github.com/grafana/grafana/pkg/services/licensing"
|
"github.com/grafana/grafana/pkg/services/licensing"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/azuremonitor"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/cloudmonitoring"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/cloudwatch"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/elasticsearch"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/grafanads"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/graphite"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/influxdb"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/loki"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/mssql"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/mysql"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/opentsdb"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/postgres"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/prometheus"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/tempo"
|
||||||
|
"github.com/grafana/grafana/pkg/tsdb/testdatasource"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
@ -40,13 +62,34 @@ func TestPluginManager_int_init(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracer := &fakeTracer{}
|
||||||
|
|
||||||
license := &licensing.OSSLicensingService{
|
license := &licensing.OSSLicensingService{
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hcp := httpclient.NewProvider()
|
||||||
|
am := azuremonitor.ProvideService(cfg, hcp, tracer)
|
||||||
|
cw := cloudwatch.ProvideService(cfg, hcp)
|
||||||
|
cm := cloudmonitoring.ProvideService(hcp, tracer)
|
||||||
|
es := elasticsearch.ProvideService(hcp)
|
||||||
|
grap := graphite.ProvideService(hcp, tracer)
|
||||||
|
idb := influxdb.ProvideService(hcp)
|
||||||
|
lk := loki.ProvideService(hcp, tracer)
|
||||||
|
otsdb := opentsdb.ProvideService(hcp)
|
||||||
|
pr := prometheus.ProvideService(hcp, tracer)
|
||||||
|
tmpo := tempo.ProvideService(hcp)
|
||||||
|
td := testdatasource.ProvideService(cfg)
|
||||||
|
pg := postgres.ProvideService(cfg)
|
||||||
|
my := mysql.ProvideService(cfg, hcp)
|
||||||
|
ms := mssql.ProvideService(cfg)
|
||||||
|
graf := grafanads.ProvideService(cfg)
|
||||||
|
|
||||||
|
coreRegistry := coreplugin.ProvideCoreRegistry(am, cw, cm, es, grap, idb, lk, otsdb, pr, tmpo, td, pg, my, ms, graf)
|
||||||
|
|
||||||
pmCfg := plugins.FromGrafanaCfg(cfg)
|
pmCfg := plugins.FromGrafanaCfg(cfg)
|
||||||
pm, err := ProvideService(cfg, nil, loader.New(pmCfg, license,
|
pm, err := ProvideService(cfg, nil, loader.New(pmCfg, license,
|
||||||
&signature.UnsignedPluginAuthorizer{Cfg: pmCfg}, &provider.Service{}), nil)
|
&signature.UnsignedPluginAuthorizer{Cfg: pmCfg}, provider.ProvideService(coreRegistry)), nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
verifyCorePluginCatalogue(t, pm)
|
verifyCorePluginCatalogue(t, pm)
|
||||||
|
|
@ -92,12 +135,27 @@ func verifyCorePluginCatalogue(t *testing.T, pm *PluginManager) {
|
||||||
}
|
}
|
||||||
|
|
||||||
expDataSources := map[string]struct{}{
|
expDataSources := map[string]struct{}{
|
||||||
"alertmanager": {},
|
"cloudwatch": {},
|
||||||
"dashboard": {},
|
"stackdriver": {},
|
||||||
"input": {},
|
"grafana-azure-monitor-datasource": {},
|
||||||
"jaeger": {},
|
"elasticsearch": {},
|
||||||
"mixed": {},
|
"graphite": {},
|
||||||
"zipkin": {},
|
"influxdb": {},
|
||||||
|
"loki": {},
|
||||||
|
"opentsdb": {},
|
||||||
|
"prometheus": {},
|
||||||
|
"tempo": {},
|
||||||
|
"testdata": {},
|
||||||
|
"postgres": {},
|
||||||
|
"mysql": {},
|
||||||
|
"mssql": {},
|
||||||
|
"grafana": {},
|
||||||
|
"alertmanager": {},
|
||||||
|
"dashboard": {},
|
||||||
|
"input": {},
|
||||||
|
"jaeger": {},
|
||||||
|
"mixed": {},
|
||||||
|
"zipkin": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
expApps := map[string]struct{}{
|
expApps := map[string]struct{}{
|
||||||
|
|
@ -177,3 +235,19 @@ func verifyPluginStaticRoutes(t *testing.T, pm *PluginManager) {
|
||||||
assert.Contains(t, routes, "test-app")
|
assert.Contains(t, routes, "test-app")
|
||||||
assert.Equal(t, routes["test-app"].Directory, testAppPlugin.PluginDir)
|
assert.Equal(t, routes["test-app"].Directory, testAppPlugin.PluginDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type fakeTracer struct {
|
||||||
|
tracing.Tracer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ft *fakeTracer) Run(context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ft *fakeTracer) Start(ctx context.Context, _ string, _ ...trace.SpanStartOption) (context.Context, tracing.Span) {
|
||||||
|
return ctx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ft *fakeTracer) Inject(context.Context, http.Header, tracing.Span) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,21 +23,6 @@ import (
|
||||||
secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager"
|
secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager"
|
||||||
"github.com/grafana/grafana/pkg/services/serviceaccounts"
|
"github.com/grafana/grafana/pkg/services/serviceaccounts"
|
||||||
"github.com/grafana/grafana/pkg/services/updatechecker"
|
"github.com/grafana/grafana/pkg/services/updatechecker"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/azuremonitor"
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/cloudmonitoring"
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch"
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/elasticsearch"
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/grafanads"
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/graphite"
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/influxdb"
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/loki"
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/mssql"
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/mysql"
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/opentsdb"
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/postgres"
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/prometheus"
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/tempo"
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/testdatasource"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ProvideBackgroundServiceRegistry(
|
func ProvideBackgroundServiceRegistry(
|
||||||
|
|
@ -48,11 +33,8 @@ func ProvideBackgroundServiceRegistry(
|
||||||
metrics *metrics.InternalMetricsService, usageStats *uss.UsageStats, updateChecker *updatechecker.Service,
|
metrics *metrics.InternalMetricsService, usageStats *uss.UsageStats, updateChecker *updatechecker.Service,
|
||||||
tracing tracing.Tracer, remoteCache *remotecache.RemoteCache, secretsService *secretsManager.SecretsService,
|
tracing tracing.Tracer, remoteCache *remotecache.RemoteCache, secretsService *secretsManager.SecretsService,
|
||||||
// Need to make sure these are initialized, is there a better place to put them?
|
// Need to make sure these are initialized, is there a better place to put them?
|
||||||
_ *azuremonitor.Service, _ *cloudwatch.CloudWatchService, _ *elasticsearch.Service, _ *graphite.Service,
|
_ *plugindashboards.Service, _ *dashboardsnapshots.Service, _ *pluginsettings.Service,
|
||||||
_ *influxdb.Service, _ *loki.Service, _ *opentsdb.Service, _ *prometheus.Service, _ *tempo.Service,
|
_ *alerting.AlertNotificationService, _ serviceaccounts.Service,
|
||||||
_ *testdatasource.Service, _ *plugindashboards.Service, _ *dashboardsnapshots.Service,
|
|
||||||
_ *postgres.Service, _ *mysql.Service, _ *mssql.Service, _ *grafanads.Service, _ *cloudmonitoring.Service,
|
|
||||||
_ *pluginsettings.Service, _ *alerting.AlertNotificationService, _ serviceaccounts.Service,
|
|
||||||
) *BackgroundServiceRegistry {
|
) *BackgroundServiceRegistry {
|
||||||
return NewBackgroundServiceRegistry(
|
return NewBackgroundServiceRegistry(
|
||||||
httpServer,
|
httpServer,
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"github.com/grafana/grafana/pkg/login/social"
|
"github.com/grafana/grafana/pkg/login/social"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
"github.com/grafana/grafana/pkg/plugins/manager"
|
||||||
"github.com/grafana/grafana/pkg/plugins/manager/loader"
|
"github.com/grafana/grafana/pkg/plugins/manager/loader"
|
||||||
"github.com/grafana/grafana/pkg/plugins/plugincontext"
|
"github.com/grafana/grafana/pkg/plugins/plugincontext"
|
||||||
|
|
@ -112,6 +113,7 @@ var wireBasicSet = wire.NewSet(
|
||||||
wire.Bind(new(plugins.StaticRouteResolver), new(*manager.PluginManager)),
|
wire.Bind(new(plugins.StaticRouteResolver), new(*manager.PluginManager)),
|
||||||
wire.Bind(new(plugins.PluginDashboardManager), new(*manager.PluginManager)),
|
wire.Bind(new(plugins.PluginDashboardManager), new(*manager.PluginManager)),
|
||||||
wire.Bind(new(plugins.RendererManager), new(*manager.PluginManager)),
|
wire.Bind(new(plugins.RendererManager), new(*manager.PluginManager)),
|
||||||
|
coreplugin.ProvideCoreRegistry,
|
||||||
loader.ProvideService,
|
loader.ProvideService,
|
||||||
wire.Bind(new(plugins.Loader), new(*loader.Loader)),
|
wire.Bind(new(plugins.Loader), new(*loader.Loader)),
|
||||||
wire.Bind(new(plugins.ErrorResolver), new(*loader.Loader)),
|
wire.Bind(new(plugins.ErrorResolver), new(*loader.Loader)),
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ func writeResponse(rw http.ResponseWriter, code int, msg string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) resourceHandler(subDataSource string) func(rw http.ResponseWriter, req *http.Request) {
|
func (s *Service) handleResourceReq(subDataSource string) func(rw http.ResponseWriter, req *http.Request) {
|
||||||
return func(rw http.ResponseWriter, req *http.Request) {
|
return func(rw http.ResponseWriter, req *http.Request) {
|
||||||
azlog.Debug("Received resource call", "url", req.URL.String(), "method", req.Method)
|
azlog.Debug("Received resource call", "url", req.URL.String(), "method", req.Method)
|
||||||
|
|
||||||
|
|
@ -115,11 +115,13 @@ func (s *Service) resourceHandler(subDataSource string) func(rw http.ResponseWri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// registerRoutes provides route definitions shared with the frontend.
|
// newResourceMux provides route definitions shared with the frontend.
|
||||||
// Check: /public/app/plugins/datasource/grafana-azure-monitor-datasource/utils/common.ts <routeNames>
|
// Check: /public/app/plugins/datasource/grafana-azure-monitor-datasource/utils/common.ts <routeNames>
|
||||||
func (s *Service) registerRoutes(mux *http.ServeMux) {
|
func (s *Service) newResourceMux() *http.ServeMux {
|
||||||
mux.HandleFunc("/azuremonitor/", s.resourceHandler(azureMonitor))
|
mux := http.NewServeMux()
|
||||||
mux.HandleFunc("/appinsights/", s.resourceHandler(appInsights))
|
mux.HandleFunc("/azuremonitor/", s.handleResourceReq(azureMonitor))
|
||||||
mux.HandleFunc("/loganalytics/", s.resourceHandler(azureLogAnalytics))
|
mux.HandleFunc("/appinsights/", s.handleResourceReq(appInsights))
|
||||||
mux.HandleFunc("/resourcegraph/", s.resourceHandler(azureResourceGraph))
|
mux.HandleFunc("/loganalytics/", s.handleResourceReq(azureLogAnalytics))
|
||||||
|
mux.HandleFunc("/resourcegraph/", s.handleResourceReq(azureResourceGraph))
|
||||||
|
return mux
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ func (s *fakeProxy) Do(rw http.ResponseWriter, req *http.Request, cli *http.Clie
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_resourceHandler(t *testing.T) {
|
func Test_handleResourceReq(t *testing.T) {
|
||||||
proxy := &fakeProxy{}
|
proxy := &fakeProxy{}
|
||||||
s := Service{
|
s := Service{
|
||||||
im: &fakeInstance{
|
im: &fakeInstance{
|
||||||
|
|
@ -102,7 +102,6 @@ func Test_resourceHandler(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Cfg: &setting.Cfg{},
|
|
||||||
executors: map[string]azDatasourceExecutor{
|
executors: map[string]azDatasourceExecutor{
|
||||||
azureMonitor: &AzureMonitorDatasource{
|
azureMonitor: &AzureMonitorDatasource{
|
||||||
proxy: proxy,
|
proxy: proxy,
|
||||||
|
|
@ -114,7 +113,7 @@ func Test_resourceHandler(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected error %v", err)
|
t.Fatalf("Unexpected error %v", err)
|
||||||
}
|
}
|
||||||
s.resourceHandler(azureMonitor)(rw, req)
|
s.handleResourceReq(azureMonitor)(rw, req)
|
||||||
expectedURL := "https://management.azure.com/subscriptions/44693801"
|
expectedURL := "https://management.azure.com/subscriptions/44693801"
|
||||||
if proxy.requestedURL != expectedURL {
|
if proxy.requestedURL != expectedURL {
|
||||||
t.Errorf("Unexpected result URL. Got %s, expecting %s", proxy.requestedURL, expectedURL)
|
t.Errorf("Unexpected result URL. Got %s, expecting %s", proxy.requestedURL, expectedURL)
|
||||||
|
|
|
||||||
|
|
@ -12,18 +12,16 @@ import (
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
|
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend/resource/httpadapter"
|
"github.com/grafana/grafana-plugin-sdk-go/backend/resource/httpadapter"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/azcredentials"
|
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/azcredentials"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
timeSeries = "time_series"
|
timeSeries = "time_series"
|
||||||
pluginID = "grafana-azure-monitor-datasource"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -31,7 +29,7 @@ var (
|
||||||
legendKeyFormat = regexp.MustCompile(`\{\{\s*(.+?)\s*\}\}`)
|
legendKeyFormat = regexp.MustCompile(`\{\{\s*(.+?)\s*\}\}`)
|
||||||
)
|
)
|
||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg, httpClientProvider *httpclient.Provider, pluginStore plugins.Store, tracer tracing.Tracer) *Service {
|
func ProvideService(cfg *setting.Cfg, httpClientProvider *httpclient.Provider, tracer tracing.Tracer) *Service {
|
||||||
proxy := &httpServiceProxy{}
|
proxy := &httpServiceProxy{}
|
||||||
executors := map[string]azDatasourceExecutor{
|
executors := map[string]azDatasourceExecutor{
|
||||||
azureMonitor: &AzureMonitorDatasource{proxy: proxy},
|
azureMonitor: &AzureMonitorDatasource{proxy: proxy},
|
||||||
|
|
@ -43,37 +41,36 @@ func ProvideService(cfg *setting.Cfg, httpClientProvider *httpclient.Provider, p
|
||||||
im := datasource.NewInstanceManager(NewInstanceSettings(cfg, *httpClientProvider, executors))
|
im := datasource.NewInstanceManager(NewInstanceSettings(cfg, *httpClientProvider, executors))
|
||||||
|
|
||||||
s := &Service{
|
s := &Service{
|
||||||
Cfg: cfg,
|
|
||||||
im: im,
|
im: im,
|
||||||
executors: executors,
|
executors: executors,
|
||||||
tracer: tracer,
|
tracer: tracer,
|
||||||
}
|
}
|
||||||
|
|
||||||
mux := s.newMux()
|
s.queryMux = s.newQueryMux()
|
||||||
resourceMux := http.NewServeMux()
|
s.resourceHandler = httpadapter.New(s.newResourceMux())
|
||||||
s.registerRoutes(resourceMux)
|
|
||||||
factory := coreplugin.New(backend.ServeOpts{
|
|
||||||
QueryDataHandler: mux,
|
|
||||||
CallResourceHandler: httpadapter.New(resourceMux),
|
|
||||||
})
|
|
||||||
|
|
||||||
resolver := plugins.CoreDataSourcePathResolver(cfg, pluginID)
|
|
||||||
if err := pluginStore.AddWithFactory(context.Background(), pluginID, factory, resolver); err != nil {
|
|
||||||
azlog.Error("Failed to register plugin", "error", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||||
|
return s.queryMux.QueryData(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
|
||||||
|
return s.resourceHandler.CallResource(ctx, req, sender)
|
||||||
|
}
|
||||||
|
|
||||||
type serviceProxy interface {
|
type serviceProxy interface {
|
||||||
Do(rw http.ResponseWriter, req *http.Request, cli *http.Client) http.ResponseWriter
|
Do(rw http.ResponseWriter, req *http.Request, cli *http.Client) http.ResponseWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
Cfg *setting.Cfg
|
|
||||||
im instancemgmt.InstanceManager
|
im instancemgmt.InstanceManager
|
||||||
executors map[string]azDatasourceExecutor
|
executors map[string]azDatasourceExecutor
|
||||||
tracer tracing.Tracer
|
|
||||||
|
queryMux *datasource.QueryTypeMux
|
||||||
|
resourceHandler backend.CallResourceHandler
|
||||||
|
tracer tracing.Tracer
|
||||||
}
|
}
|
||||||
|
|
||||||
type azureMonitorSettings struct {
|
type azureMonitorSettings struct {
|
||||||
|
|
@ -182,7 +179,7 @@ func (s *Service) getDataSourceFromPluginReq(req *backend.QueryDataRequest) (dat
|
||||||
return dsInfo, nil
|
return dsInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) newMux() *datasource.QueryTypeMux {
|
func (s *Service) newQueryMux() *datasource.QueryTypeMux {
|
||||||
mux := datasource.NewQueryTypeMux()
|
mux := datasource.NewQueryTypeMux()
|
||||||
for dsType := range s.executors {
|
for dsType := range s.executors {
|
||||||
// Make a copy of the string to keep the reference after the iterator
|
// Make a copy of the string to keep the reference after the iterator
|
||||||
|
|
|
||||||
|
|
@ -99,10 +99,6 @@ func (f *fakeExecutor) executeTimeSeriesQuery(ctx context.Context, originalQueri
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_newMux(t *testing.T) {
|
func Test_newMux(t *testing.T) {
|
||||||
cfg := &setting.Cfg{
|
|
||||||
Azure: setting.AzureSettings{},
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
queryType string
|
queryType string
|
||||||
|
|
@ -126,7 +122,6 @@ func Test_newMux(t *testing.T) {
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
s := &Service{
|
s := &Service{
|
||||||
Cfg: cfg,
|
|
||||||
im: &fakeInstance{
|
im: &fakeInstance{
|
||||||
routes: routes[azureMonitorPublic],
|
routes: routes[azureMonitorPublic],
|
||||||
services: map[string]datasourceService{
|
services: map[string]datasourceService{
|
||||||
|
|
@ -144,7 +139,7 @@ func Test_newMux(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
mux := s.newMux()
|
mux := s.newQueryMux()
|
||||||
res, err := mux.QueryData(context.Background(), &backend.QueryDataRequest{
|
res, err := mux.QueryData(context.Background(), &backend.QueryDataRequest{
|
||||||
PluginContext: backend.PluginContext{},
|
PluginContext: backend.PluginContext{},
|
||||||
Queries: []backend.DataQuery{
|
Queries: []backend.DataQuery{
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana-google-sdk-go/pkg/utils"
|
"github.com/grafana/grafana-google-sdk-go/pkg/utils"
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
|
"github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
|
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
|
||||||
|
|
@ -25,10 +26,6 @@ import (
|
||||||
"github.com/grafana/grafana/pkg/infra/httpclient"
|
"github.com/grafana/grafana/pkg/infra/httpclient"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
|
||||||
"github.com/grafana/grafana/pkg/services/datasources"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -61,8 +58,6 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
pluginID string = "stackdriver"
|
|
||||||
|
|
||||||
gceAuthentication string = "gce"
|
gceAuthentication string = "gce"
|
||||||
jwtAuthentication string = "jwt"
|
jwtAuthentication string = "jwt"
|
||||||
metricQueryType string = "metrics"
|
metricQueryType string = "metrics"
|
||||||
|
|
@ -72,31 +67,22 @@ const (
|
||||||
perSeriesAlignerDefault string = "ALIGN_MEAN"
|
perSeriesAlignerDefault string = "ALIGN_MEAN"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider, pluginStore plugins.Store,
|
func ProvideService(httpClientProvider httpclient.Provider, tracer tracing.Tracer) *Service {
|
||||||
dsService *datasources.Service, tracer tracing.Tracer) *Service {
|
|
||||||
s := &Service{
|
s := &Service{
|
||||||
httpClientProvider: httpClientProvider,
|
|
||||||
cfg: cfg,
|
|
||||||
im: datasource.NewInstanceManager(newInstanceSettings(httpClientProvider)),
|
|
||||||
dsService: dsService,
|
|
||||||
tracer: tracer,
|
tracer: tracer,
|
||||||
|
httpClientProvider: httpClientProvider,
|
||||||
|
im: datasource.NewInstanceManager(newInstanceSettings(httpClientProvider)),
|
||||||
}
|
}
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
s.resourceHandler = httpadapter.New(s.newResourceMux())
|
||||||
s.registerRoutes(mux)
|
|
||||||
factory := coreplugin.New(backend.ServeOpts{
|
|
||||||
QueryDataHandler: s,
|
|
||||||
CallResourceHandler: httpadapter.New(mux),
|
|
||||||
CheckHealthHandler: s,
|
|
||||||
})
|
|
||||||
|
|
||||||
resolver := plugins.CoreDataSourcePathResolver(cfg, pluginID)
|
|
||||||
if err := pluginStore.AddWithFactory(context.Background(), pluginID, factory, resolver); err != nil {
|
|
||||||
slog.Error("Failed to register plugin", "error", err)
|
|
||||||
}
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
|
||||||
|
return s.resourceHandler.CallResource(ctx, req, sender)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
|
func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
|
||||||
dsInfo, err := s.getDSInfo(req.PluginContext)
|
dsInfo, err := s.getDSInfo(req.PluginContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -138,10 +124,10 @@ func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthReque
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
httpClientProvider httpclient.Provider
|
httpClientProvider httpclient.Provider
|
||||||
cfg *setting.Cfg
|
|
||||||
im instancemgmt.InstanceManager
|
im instancemgmt.InstanceManager
|
||||||
dsService *datasources.Service
|
|
||||||
tracer tracing.Tracer
|
tracer tracing.Tracer
|
||||||
|
|
||||||
|
resourceHandler backend.CallResourceHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
type QueryModel struct {
|
type QueryModel struct {
|
||||||
|
|
|
||||||
|
|
@ -25,13 +25,14 @@ const resourceManagerPath = "/v1/projects"
|
||||||
|
|
||||||
type processResponse func(body []byte) ([]json.RawMessage, string, error)
|
type processResponse func(body []byte) ([]json.RawMessage, string, error)
|
||||||
|
|
||||||
func (s *Service) registerRoutes(mux *http.ServeMux) {
|
func (s *Service) newResourceMux() *http.ServeMux {
|
||||||
|
mux := http.NewServeMux()
|
||||||
mux.HandleFunc("/gceDefaultProject", getGCEDefaultProject)
|
mux.HandleFunc("/gceDefaultProject", getGCEDefaultProject)
|
||||||
|
mux.HandleFunc("/metricDescriptors/", s.handleResourceReq(cloudMonitor, processMetricDescriptors))
|
||||||
mux.HandleFunc("/metricDescriptors/", s.resourceHandler(cloudMonitor, processMetricDescriptors))
|
mux.HandleFunc("/services/", s.handleResourceReq(cloudMonitor, processServices))
|
||||||
mux.HandleFunc("/services/", s.resourceHandler(cloudMonitor, processServices))
|
mux.HandleFunc("/slo-services/", s.handleResourceReq(cloudMonitor, processSLOs))
|
||||||
mux.HandleFunc("/slo-services/", s.resourceHandler(cloudMonitor, processSLOs))
|
mux.HandleFunc("/projects", s.handleResourceReq(resourceManager, processProjects))
|
||||||
mux.HandleFunc("/projects", s.resourceHandler(resourceManager, processProjects))
|
return mux
|
||||||
}
|
}
|
||||||
|
|
||||||
func getGCEDefaultProject(rw http.ResponseWriter, req *http.Request) {
|
func getGCEDefaultProject(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
|
@ -43,7 +44,7 @@ func getGCEDefaultProject(rw http.ResponseWriter, req *http.Request) {
|
||||||
writeResponse(rw, http.StatusOK, project)
|
writeResponse(rw, http.StatusOK, project)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) resourceHandler(subDataSource string, responseFn processResponse) func(rw http.ResponseWriter, req *http.Request) {
|
func (s *Service) handleResourceReq(subDataSource string, responseFn processResponse) func(rw http.ResponseWriter, req *http.Request) {
|
||||||
return func(rw http.ResponseWriter, req *http.Request) {
|
return func(rw http.ResponseWriter, req *http.Request) {
|
||||||
client, code, err := s.setRequestVariables(req, subDataSource)
|
client, code, err := s.setRequestVariables(req, subDataSource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,6 @@ import (
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/infra/httpclient"
|
"github.com/grafana/grafana/pkg/infra/httpclient"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -56,29 +54,18 @@ const defaultRegion = "default"
|
||||||
const logIdentifierInternal = "__log__grafana_internal__"
|
const logIdentifierInternal = "__log__grafana_internal__"
|
||||||
const logStreamIdentifierInternal = "__logstream__grafana_internal__"
|
const logStreamIdentifierInternal = "__logstream__grafana_internal__"
|
||||||
|
|
||||||
const pluginID = "cloudwatch"
|
|
||||||
|
|
||||||
var plog = log.New("tsdb.cloudwatch")
|
var plog = log.New("tsdb.cloudwatch")
|
||||||
var aliasFormat = regexp.MustCompile(`\{\{\s*(.+?)\s*\}\}`)
|
var aliasFormat = regexp.MustCompile(`\{\{\s*(.+?)\s*\}\}`)
|
||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider, pluginStore plugins.Store) (*CloudWatchService, error) {
|
func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider) *CloudWatchService {
|
||||||
plog.Debug("initing")
|
plog.Debug("initing")
|
||||||
|
|
||||||
executor := newExecutor(datasource.NewInstanceManager(NewInstanceSettings(httpClientProvider)), cfg, awsds.NewSessionCache())
|
executor := newExecutor(datasource.NewInstanceManager(NewInstanceSettings(httpClientProvider)), cfg, awsds.NewSessionCache())
|
||||||
factory := coreplugin.New(backend.ServeOpts{
|
|
||||||
QueryDataHandler: executor,
|
|
||||||
})
|
|
||||||
|
|
||||||
resolver := plugins.CoreDataSourcePathResolver(cfg, pluginID)
|
|
||||||
if err := pluginStore.AddWithFactory(context.Background(), pluginID, factory, resolver); err != nil {
|
|
||||||
plog.Error("Failed to register plugin", "error", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &CloudWatchService{
|
return &CloudWatchService{
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
Executor: executor,
|
Executor: executor,
|
||||||
}, nil
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type CloudWatchService struct {
|
type CloudWatchService struct {
|
||||||
|
|
|
||||||
|
|
@ -12,47 +12,24 @@ import (
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
|
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
|
||||||
"github.com/grafana/grafana/pkg/infra/httpclient"
|
"github.com/grafana/grafana/pkg/infra/httpclient"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
|
||||||
es "github.com/grafana/grafana/pkg/tsdb/elasticsearch/client"
|
es "github.com/grafana/grafana/pkg/tsdb/elasticsearch/client"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
|
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var eslog = log.New("tsdb.elasticsearch")
|
var eslog = log.New("tsdb.elasticsearch")
|
||||||
|
|
||||||
const pluginID = "elasticsearch"
|
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
HTTPClientProvider httpclient.Provider
|
httpClientProvider httpclient.Provider
|
||||||
intervalCalculator intervalv2.Calculator
|
intervalCalculator intervalv2.Calculator
|
||||||
im instancemgmt.InstanceManager
|
im instancemgmt.InstanceManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider, pluginStore plugins.Store) (*Service, error) {
|
func ProvideService(httpClientProvider httpclient.Provider) *Service {
|
||||||
eslog.Debug("initializing")
|
eslog.Debug("initializing")
|
||||||
|
|
||||||
im := datasource.NewInstanceManager(newInstanceSettings())
|
|
||||||
s := newService(im, httpClientProvider)
|
|
||||||
|
|
||||||
factory := coreplugin.New(backend.ServeOpts{
|
|
||||||
QueryDataHandler: newService(im, s.HTTPClientProvider),
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := pluginStore.AddWithFactory(context.Background(), pluginID, factory,
|
|
||||||
plugins.CoreDataSourcePathResolver(cfg, pluginID)); err != nil {
|
|
||||||
eslog.Error("Failed to register plugin", "error", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newService creates a new executor func.
|
|
||||||
func newService(im instancemgmt.InstanceManager, httpClientProvider httpclient.Provider) *Service {
|
|
||||||
return &Service{
|
return &Service{
|
||||||
im: im,
|
im: datasource.NewInstanceManager(newInstanceSettings()),
|
||||||
HTTPClientProvider: httpClientProvider,
|
httpClientProvider: httpClientProvider,
|
||||||
intervalCalculator: intervalv2.NewCalculator(),
|
intervalCalculator: intervalv2.NewCalculator(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -67,7 +44,7 @@ func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest)
|
||||||
return &backend.QueryDataResponse{}, err
|
return &backend.QueryDataResponse{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := es.NewClient(ctx, s.HTTPClientProvider, dsInfo, req.Queries[0].TimeRange)
|
client, err := es.NewClient(ctx, s.httpClientProvider, dsInfo, req.Queries[0].TimeRange)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &backend.QueryDataResponse{}, err
|
return &backend.QueryDataResponse{}, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,6 @@ import (
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/testdatasource"
|
"github.com/grafana/grafana/pkg/tsdb/testdatasource"
|
||||||
)
|
)
|
||||||
|
|
@ -32,8 +30,6 @@ const DatasourceID = -1
|
||||||
// Grafana DS command.
|
// Grafana DS command.
|
||||||
const DatasourceUID = "grafana"
|
const DatasourceUID = "grafana"
|
||||||
|
|
||||||
const pluginID = "grafana"
|
|
||||||
|
|
||||||
// Make sure Service implements required interfaces.
|
// Make sure Service implements required interfaces.
|
||||||
// This is important to do since otherwise we will only get a
|
// This is important to do since otherwise we will only get a
|
||||||
// not implemented error response from plugin at runtime.
|
// not implemented error response from plugin at runtime.
|
||||||
|
|
@ -43,11 +39,11 @@ var (
|
||||||
logger = log.New("tsdb.grafana")
|
logger = log.New("tsdb.grafana")
|
||||||
)
|
)
|
||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg, pluginStore plugins.Store) *Service {
|
func ProvideService(cfg *setting.Cfg) *Service {
|
||||||
return newService(cfg, pluginStore)
|
return newService(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newService(cfg *setting.Cfg, pluginStore plugins.Store) *Service {
|
func newService(cfg *setting.Cfg) *Service {
|
||||||
s := &Service{
|
s := &Service{
|
||||||
staticRootPath: cfg.StaticRootPath,
|
staticRootPath: cfg.StaticRootPath,
|
||||||
roots: []string{
|
roots: []string{
|
||||||
|
|
@ -59,14 +55,6 @@ func newService(cfg *setting.Cfg, pluginStore plugins.Store) *Service {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
resolver := plugins.CoreDataSourcePathResolver(cfg, pluginID)
|
|
||||||
if err := pluginStore.AddWithFactory(context.Background(), pluginID, coreplugin.New(backend.ServeOpts{
|
|
||||||
CheckHealthHandler: s,
|
|
||||||
QueryDataHandler: s,
|
|
||||||
}), resolver); err != nil {
|
|
||||||
logger.Error("Failed to register plugin", "error", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,10 @@
|
||||||
package grafanads
|
package grafanads
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
|
|
@ -21,7 +18,7 @@ func asJSON(v interface{}) json.RawMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadFolderListing(t *testing.T) {
|
func TestReadFolderListing(t *testing.T) {
|
||||||
ds := newService(&setting.Cfg{StaticRootPath: "../../../public"}, &fakePluginStore{})
|
ds := newService(&setting.Cfg{StaticRootPath: "../../../public"})
|
||||||
dr := ds.doListQuery(backend.DataQuery{
|
dr := ds.doListQuery(backend.DataQuery{
|
||||||
QueryType: "x",
|
QueryType: "x",
|
||||||
JSON: asJSON(listQueryModel{
|
JSON: asJSON(listQueryModel{
|
||||||
|
|
@ -33,7 +30,7 @@ func TestReadFolderListing(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadCSVFile(t *testing.T) {
|
func TestReadCSVFile(t *testing.T) {
|
||||||
ds := newService(&setting.Cfg{StaticRootPath: "../../../public"}, &fakePluginStore{})
|
ds := newService(&setting.Cfg{StaticRootPath: "../../../public"})
|
||||||
dr := ds.doReadQuery(backend.DataQuery{
|
dr := ds.doReadQuery(backend.DataQuery{
|
||||||
QueryType: "x",
|
QueryType: "x",
|
||||||
JSON: asJSON(readQueryModel{
|
JSON: asJSON(readQueryModel{
|
||||||
|
|
@ -43,11 +40,3 @@ func TestReadCSVFile(t *testing.T) {
|
||||||
err := experimental.CheckGoldenDataResponse(path.Join("testdata", "jslib.golden.txt"), &dr, true)
|
err := experimental.CheckGoldenDataResponse(path.Join("testdata", "jslib.golden.txt"), &dr, true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
type fakePluginStore struct {
|
|
||||||
plugins.Store
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ps *fakePluginStore) AddWithFactory(_ context.Context, _ string, _ backendplugin.PluginFactoryFunc, _ plugins.PluginPathResolver) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,6 @@ import (
|
||||||
"github.com/grafana/grafana/pkg/infra/httpclient"
|
"github.com/grafana/grafana/pkg/infra/httpclient"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
)
|
)
|
||||||
|
|
@ -39,29 +37,16 @@ type Service struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
pluginID = "graphite"
|
|
||||||
TargetFullModelField = "targetFull"
|
TargetFullModelField = "targetFull"
|
||||||
TargetModelField = "target"
|
TargetModelField = "target"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider, pluginStore plugins.Store, tracer tracing.Tracer) (*Service, error) {
|
func ProvideService(httpClientProvider httpclient.Provider, tracer tracing.Tracer) *Service {
|
||||||
s := &Service{
|
return &Service{
|
||||||
logger: log.New("tsdb.graphite"),
|
logger: log.New("tsdb.graphite"),
|
||||||
im: datasource.NewInstanceManager(newInstanceSettings(httpClientProvider)),
|
im: datasource.NewInstanceManager(newInstanceSettings(httpClientProvider)),
|
||||||
tracer: tracer,
|
tracer: tracer,
|
||||||
}
|
}
|
||||||
|
|
||||||
factory := coreplugin.New(backend.ServeOpts{
|
|
||||||
QueryDataHandler: s,
|
|
||||||
})
|
|
||||||
|
|
||||||
resolver := plugins.CoreDataSourcePathResolver(cfg, pluginID)
|
|
||||||
if err := pluginStore.AddWithFactory(context.Background(), pluginID, factory, resolver); err != nil {
|
|
||||||
s.logger.Error("Failed to register plugin", "error", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type datasourceInfo struct {
|
type datasourceInfo struct {
|
||||||
|
|
|
||||||
|
|
@ -15,18 +15,14 @@ import (
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
|
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
|
||||||
"github.com/grafana/grafana/pkg/infra/httpclient"
|
"github.com/grafana/grafana/pkg/infra/httpclient"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/influxdb/flux"
|
"github.com/grafana/grafana/pkg/tsdb/influxdb/flux"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/influxdb/models"
|
"github.com/grafana/grafana/pkg/tsdb/influxdb/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
const pluginID = "influxdb"
|
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
QueryParser *InfluxdbQueryParser
|
queryParser *InfluxdbQueryParser
|
||||||
ResponseParser *ResponseParser
|
responseParser *ResponseParser
|
||||||
glog log.Logger
|
glog log.Logger
|
||||||
|
|
||||||
im instancemgmt.InstanceManager
|
im instancemgmt.InstanceManager
|
||||||
|
|
@ -34,26 +30,13 @@ type Service struct {
|
||||||
|
|
||||||
var ErrInvalidHttpMode = errors.New("'httpMode' should be either 'GET' or 'POST'")
|
var ErrInvalidHttpMode = errors.New("'httpMode' should be either 'GET' or 'POST'")
|
||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg, httpClient httpclient.Provider, pluginStore plugins.Store) (*Service, error) {
|
func ProvideService(httpClient httpclient.Provider) *Service {
|
||||||
im := datasource.NewInstanceManager(newInstanceSettings(httpClient))
|
return &Service{
|
||||||
s := &Service{
|
queryParser: &InfluxdbQueryParser{},
|
||||||
QueryParser: &InfluxdbQueryParser{},
|
responseParser: &ResponseParser{},
|
||||||
ResponseParser: &ResponseParser{},
|
|
||||||
glog: log.New("tsdb.influxdb"),
|
glog: log.New("tsdb.influxdb"),
|
||||||
im: im,
|
im: datasource.NewInstanceManager(newInstanceSettings(httpClient)),
|
||||||
}
|
}
|
||||||
|
|
||||||
factory := coreplugin.New(backend.ServeOpts{
|
|
||||||
QueryDataHandler: s,
|
|
||||||
})
|
|
||||||
|
|
||||||
resolver := plugins.CoreDataSourcePathResolver(cfg, pluginID)
|
|
||||||
if err := pluginStore.AddWithFactory(context.Background(), pluginID, factory, resolver); err != nil {
|
|
||||||
s.glog.Error("Failed to register plugin", "error", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newInstanceSettings(httpClientProvider httpclient.Provider) datasource.InstanceFactoryFunc {
|
func newInstanceSettings(httpClientProvider httpclient.Provider) datasource.InstanceFactoryFunc {
|
||||||
|
|
@ -146,7 +129,7 @@ func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest)
|
||||||
return &backend.QueryDataResponse{}, fmt.Errorf("InfluxDB returned error status: %s", res.Status)
|
return &backend.QueryDataResponse{}, fmt.Errorf("InfluxDB returned error status: %s", res.Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := s.ResponseParser.Parse(res.Body, query)
|
resp := s.responseParser.Parse(res.Body, query)
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
@ -162,7 +145,7 @@ func (s *Service) getQuery(dsInfo *models.DatasourceInfo, query *backend.QueryDa
|
||||||
|
|
||||||
q := query.Queries[0]
|
q := query.Queries[0]
|
||||||
|
|
||||||
return s.QueryParser.Parse(q)
|
return s.queryParser.Parse(q)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) createRequest(ctx context.Context, dsInfo *models.DatasourceInfo, query string) (*http.Request, error) {
|
func (s *Service) createRequest(ctx context.Context, dsInfo *models.DatasourceInfo, query string) (*http.Request, error) {
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@ func TestExecutor_createRequest(t *testing.T) {
|
||||||
}
|
}
|
||||||
query := "SELECT awesomeness FROM somewhere"
|
query := "SELECT awesomeness FROM somewhere"
|
||||||
s := &Service{
|
s := &Service{
|
||||||
QueryParser: &InfluxdbQueryParser{},
|
queryParser: &InfluxdbQueryParser{},
|
||||||
ResponseParser: &ResponseParser{},
|
responseParser: &ResponseParser{},
|
||||||
glog: log.New("test"),
|
glog: log.New("test"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,6 @@ import (
|
||||||
"github.com/grafana/grafana/pkg/infra/httpclient"
|
"github.com/grafana/grafana/pkg/infra/httpclient"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
|
||||||
"github.com/grafana/loki/pkg/logcli/client"
|
"github.com/grafana/loki/pkg/logcli/client"
|
||||||
"github.com/grafana/loki/pkg/loghttp"
|
"github.com/grafana/loki/pkg/loghttp"
|
||||||
"github.com/grafana/loki/pkg/logproto"
|
"github.com/grafana/loki/pkg/logproto"
|
||||||
|
|
@ -29,33 +26,18 @@ import (
|
||||||
"github.com/prometheus/common/model"
|
"github.com/prometheus/common/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
const pluginID = "loki"
|
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
im instancemgmt.InstanceManager
|
im instancemgmt.InstanceManager
|
||||||
plog log.Logger
|
plog log.Logger
|
||||||
tracer tracing.Tracer
|
tracer tracing.Tracer
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider, pluginStore plugins.Store, tracer tracing.Tracer) (*Service, error) {
|
func ProvideService(httpClientProvider httpclient.Provider, tracer tracing.Tracer) *Service {
|
||||||
im := datasource.NewInstanceManager(newInstanceSettings(httpClientProvider))
|
return &Service{
|
||||||
s := &Service{
|
im: datasource.NewInstanceManager(newInstanceSettings(httpClientProvider)),
|
||||||
im: im,
|
|
||||||
plog: log.New("tsdb.loki"),
|
plog: log.New("tsdb.loki"),
|
||||||
tracer: tracer,
|
tracer: tracer,
|
||||||
}
|
}
|
||||||
|
|
||||||
factory := coreplugin.New(backend.ServeOpts{
|
|
||||||
QueryDataHandler: s,
|
|
||||||
})
|
|
||||||
|
|
||||||
resolver := plugins.CoreDataSourcePathResolver(cfg, pluginID)
|
|
||||||
if err := pluginStore.AddWithFactory(context.Background(), pluginID, factory, resolver); err != nil {
|
|
||||||
s.plog.Error("Failed to register plugin", "error", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,6 @@ import (
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data/sqlutil"
|
"github.com/grafana/grafana-plugin-sdk-go/data/sqlutil"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/sqleng"
|
"github.com/grafana/grafana/pkg/tsdb/sqleng"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
|
|
@ -26,25 +24,14 @@ import (
|
||||||
|
|
||||||
var logger = log.New("tsdb.mssql")
|
var logger = log.New("tsdb.mssql")
|
||||||
|
|
||||||
const pluginID = "mssql"
|
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
im instancemgmt.InstanceManager
|
im instancemgmt.InstanceManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg, pluginStore plugins.Store) (*Service, error) {
|
func ProvideService(cfg *setting.Cfg) *Service {
|
||||||
s := &Service{
|
return &Service{
|
||||||
im: datasource.NewInstanceManager(newInstanceSettings(cfg)),
|
im: datasource.NewInstanceManager(newInstanceSettings(cfg)),
|
||||||
}
|
}
|
||||||
factory := coreplugin.New(backend.ServeOpts{
|
|
||||||
QueryDataHandler: s,
|
|
||||||
})
|
|
||||||
|
|
||||||
resolver := plugins.CoreDataSourcePathResolver(cfg, pluginID)
|
|
||||||
if err := pluginStore.AddWithFactory(context.Background(), pluginID, factory, resolver); err != nil {
|
|
||||||
logger.Error("Failed to register plugin", "error", err)
|
|
||||||
}
|
|
||||||
return s, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) getDataSourceHandler(pluginCtx backend.PluginContext) (*sqleng.DataSourceHandler, error) {
|
func (s *Service) getDataSourceHandler(pluginCtx backend.PluginContext) (*sqleng.DataSourceHandler, error) {
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,11 @@ import (
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data/sqlutil"
|
"github.com/grafana/grafana-plugin-sdk-go/data/sqlutil"
|
||||||
"github.com/grafana/grafana/pkg/infra/httpclient"
|
"github.com/grafana/grafana/pkg/infra/httpclient"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/sqleng"
|
"github.com/grafana/grafana/pkg/tsdb/sqleng"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
pluginID = "mysql"
|
|
||||||
dateFormat = "2006-01-02"
|
dateFormat = "2006-01-02"
|
||||||
dateTimeFormat1 = "2006-01-02 15:04:05"
|
dateTimeFormat1 = "2006-01-02 15:04:05"
|
||||||
dateTimeFormat2 = "2006-01-02T15:04:05Z"
|
dateTimeFormat2 = "2006-01-02T15:04:05Z"
|
||||||
|
|
@ -44,19 +41,10 @@ func characterEscape(s string, escapeChar string) string {
|
||||||
return strings.ReplaceAll(s, escapeChar, url.QueryEscape(escapeChar))
|
return strings.ReplaceAll(s, escapeChar, url.QueryEscape(escapeChar))
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg, pluginStore plugins.Store, httpClientProvider httpclient.Provider) (*Service, error) {
|
func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider) *Service {
|
||||||
s := &Service{
|
return &Service{
|
||||||
im: datasource.NewInstanceManager(newInstanceSettings(cfg, httpClientProvider)),
|
im: datasource.NewInstanceManager(newInstanceSettings(cfg, httpClientProvider)),
|
||||||
}
|
}
|
||||||
factory := coreplugin.New(backend.ServeOpts{
|
|
||||||
QueryDataHandler: s,
|
|
||||||
})
|
|
||||||
|
|
||||||
resolver := plugins.CoreDataSourcePathResolver(cfg, pluginID)
|
|
||||||
if err := pluginStore.AddWithFactory(context.Background(), pluginID, factory, resolver); err != nil {
|
|
||||||
logger.Error("Failed to register plugin", "error", err)
|
|
||||||
}
|
|
||||||
return s, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newInstanceSettings(cfg *setting.Cfg, httpClientProvider httpclient.Provider) datasource.InstanceFactoryFunc {
|
func newInstanceSettings(cfg *setting.Cfg, httpClientProvider httpclient.Provider) datasource.InstanceFactoryFunc {
|
||||||
|
|
|
||||||
|
|
@ -19,35 +19,20 @@ import (
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/infra/httpclient"
|
"github.com/grafana/grafana/pkg/infra/httpclient"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"golang.org/x/net/context/ctxhttp"
|
"golang.org/x/net/context/ctxhttp"
|
||||||
)
|
)
|
||||||
|
|
||||||
const pluginID = "opentsdb"
|
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
im instancemgmt.InstanceManager
|
im instancemgmt.InstanceManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider, pluginStore plugins.Store) (*Service, error) {
|
func ProvideService(httpClientProvider httpclient.Provider) *Service {
|
||||||
im := datasource.NewInstanceManager(newInstanceSettings(httpClientProvider))
|
return &Service{
|
||||||
s := &Service{
|
|
||||||
logger: log.New("tsdb.opentsdb"),
|
logger: log.New("tsdb.opentsdb"),
|
||||||
im: im,
|
im: datasource.NewInstanceManager(newInstanceSettings(httpClientProvider)),
|
||||||
}
|
}
|
||||||
|
|
||||||
factory := coreplugin.New(backend.ServeOpts{
|
|
||||||
QueryDataHandler: s,
|
|
||||||
})
|
|
||||||
resolver := plugins.CoreDataSourcePathResolver(cfg, pluginID)
|
|
||||||
if err := pluginStore.AddWithFactory(context.Background(), pluginID, factory, resolver); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type datasourceInfo struct {
|
type datasourceInfo struct {
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,6 @@ import (
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data/sqlutil"
|
"github.com/grafana/grafana-plugin-sdk-go/data/sqlutil"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/sqleng"
|
"github.com/grafana/grafana/pkg/tsdb/sqleng"
|
||||||
"github.com/grafana/grafana/pkg/util/errutil"
|
"github.com/grafana/grafana/pkg/util/errutil"
|
||||||
|
|
@ -23,22 +21,12 @@ import (
|
||||||
|
|
||||||
var logger = log.New("tsdb.postgres")
|
var logger = log.New("tsdb.postgres")
|
||||||
|
|
||||||
const pluginID = "postgres"
|
func ProvideService(cfg *setting.Cfg) *Service {
|
||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg, pluginStore plugins.Store) (*Service, error) {
|
|
||||||
s := &Service{
|
s := &Service{
|
||||||
tlsManager: newTLSManager(logger, cfg.DataPath),
|
tlsManager: newTLSManager(logger, cfg.DataPath),
|
||||||
}
|
}
|
||||||
s.im = datasource.NewInstanceManager(s.newInstanceSettings(cfg))
|
s.im = datasource.NewInstanceManager(s.newInstanceSettings(cfg))
|
||||||
factory := coreplugin.New(backend.ServeOpts{
|
return s
|
||||||
QueryDataHandler: s,
|
|
||||||
})
|
|
||||||
|
|
||||||
resolver := plugins.CoreDataSourcePathResolver(cfg, pluginID)
|
|
||||||
if err := pluginStore.AddWithFactory(context.Background(), pluginID, factory, resolver); err != nil {
|
|
||||||
logger.Error("Failed to register plugin", "error", err)
|
|
||||||
}
|
|
||||||
return s, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,6 @@ import (
|
||||||
"github.com/grafana/grafana/pkg/infra/httpclient"
|
"github.com/grafana/grafana/pkg/infra/httpclient"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
|
||||||
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
|
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
|
||||||
apiv1 "github.com/prometheus/client_golang/api/prometheus/v1"
|
apiv1 "github.com/prometheus/client_golang/api/prometheus/v1"
|
||||||
)
|
)
|
||||||
|
|
@ -28,34 +25,19 @@ var (
|
||||||
safeRes = 11000
|
safeRes = 11000
|
||||||
)
|
)
|
||||||
|
|
||||||
const pluginID = "prometheus"
|
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
intervalCalculator intervalv2.Calculator
|
intervalCalculator intervalv2.Calculator
|
||||||
im instancemgmt.InstanceManager
|
im instancemgmt.InstanceManager
|
||||||
tracer tracing.Tracer
|
tracer tracing.Tracer
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider, pluginStore plugins.Store, tracer tracing.Tracer) (*Service, error) {
|
func ProvideService(httpClientProvider httpclient.Provider, tracer tracing.Tracer) *Service {
|
||||||
plog.Debug("initializing")
|
plog.Debug("initializing")
|
||||||
im := datasource.NewInstanceManager(newInstanceSettings(httpClientProvider))
|
return &Service{
|
||||||
|
|
||||||
s := &Service{
|
|
||||||
intervalCalculator: intervalv2.NewCalculator(),
|
intervalCalculator: intervalv2.NewCalculator(),
|
||||||
im: im,
|
im: datasource.NewInstanceManager(newInstanceSettings(httpClientProvider)),
|
||||||
tracer: tracer,
|
tracer: tracer,
|
||||||
}
|
}
|
||||||
|
|
||||||
factory := coreplugin.New(backend.ServeOpts{
|
|
||||||
QueryDataHandler: s,
|
|
||||||
})
|
|
||||||
resolver := plugins.CoreDataSourcePathResolver(cfg, pluginID)
|
|
||||||
if err := pluginStore.AddWithFactory(context.Background(), pluginID, factory, resolver); err != nil {
|
|
||||||
plog.Error("Failed to register plugin", "error", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newInstanceSettings(httpClientProvider httpclient.Provider) datasource.InstanceFactoryFunc {
|
func newInstanceSettings(httpClientProvider httpclient.Provider) datasource.InstanceFactoryFunc {
|
||||||
|
|
|
||||||
|
|
@ -13,38 +13,19 @@ import (
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
"github.com/grafana/grafana/pkg/infra/httpclient"
|
"github.com/grafana/grafana/pkg/infra/httpclient"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
|
||||||
"go.opentelemetry.io/collector/model/otlp"
|
"go.opentelemetry.io/collector/model/otlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
const pluginID = "tempo"
|
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
im instancemgmt.InstanceManager
|
im instancemgmt.InstanceManager
|
||||||
tlog log.Logger
|
tlog log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider, pluginStore plugins.Store) (*Service, error) {
|
func ProvideService(httpClientProvider httpclient.Provider) *Service {
|
||||||
im := datasource.NewInstanceManager(newInstanceSettings(httpClientProvider))
|
return &Service{
|
||||||
|
|
||||||
s := &Service{
|
|
||||||
tlog: log.New("tsdb.tempo"),
|
tlog: log.New("tsdb.tempo"),
|
||||||
im: im,
|
im: datasource.NewInstanceManager(newInstanceSettings(httpClientProvider)),
|
||||||
}
|
}
|
||||||
|
|
||||||
factory := coreplugin.New(backend.ServeOpts{
|
|
||||||
QueryDataHandler: s,
|
|
||||||
})
|
|
||||||
|
|
||||||
resolver := plugins.CoreDataSourcePathResolver(cfg, pluginID)
|
|
||||||
if err := pluginStore.AddWithFactory(context.Background(), pluginID, factory, resolver); err != nil {
|
|
||||||
s.tlog.Error("Failed to register plugin", "error", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type datasourceInfo struct {
|
type datasourceInfo struct {
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,15 @@ import (
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend/resource/httpadapter"
|
"github.com/grafana/grafana-plugin-sdk-go/backend/resource/httpadapter"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Service) RegisterRoutes(mux *http.ServeMux) {
|
func (s *Service) registerRoutes() *http.ServeMux {
|
||||||
|
mux := http.NewServeMux()
|
||||||
mux.HandleFunc("/", s.testGetHandler)
|
mux.HandleFunc("/", s.testGetHandler)
|
||||||
mux.HandleFunc("/scenarios", s.getScenariosHandler)
|
mux.HandleFunc("/scenarios", s.getScenariosHandler)
|
||||||
mux.HandleFunc("/stream", s.testStreamHandler)
|
mux.HandleFunc("/stream", s.testStreamHandler)
|
||||||
mux.Handle("/test", createJSONHandler(s.logger))
|
mux.Handle("/test", createJSONHandler(s.logger))
|
||||||
mux.Handle("/test/json", createJSONHandler(s.logger))
|
mux.Handle("/test/json", createJSONHandler(s.logger))
|
||||||
mux.HandleFunc("/boom", s.testPanicHandler)
|
mux.HandleFunc("/boom", s.testPanicHandler)
|
||||||
|
return mux
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) testGetHandler(rw http.ResponseWriter, req *http.Request) {
|
func (s *Service) testGetHandler(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,18 @@ package testdatasource
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
|
"github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend/resource/httpadapter"
|
"github.com/grafana/grafana-plugin-sdk-go/backend/resource/httpadapter"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
|
||||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
const pluginID = "testdata"
|
func ProvideService(cfg *setting.Cfg) *Service {
|
||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg, pluginStore plugins.Store) (*Service, error) {
|
|
||||||
s := &Service{
|
s := &Service{
|
||||||
queryMux: datasource.NewQueryTypeMux(),
|
queryMux: datasource.NewQueryTypeMux(),
|
||||||
scenarios: map[string]*Scenario{},
|
scenarios: map[string]*Scenario{},
|
||||||
|
|
@ -37,29 +33,25 @@ func ProvideService(cfg *setting.Cfg, pluginStore plugins.Store) (*Service, erro
|
||||||
}
|
}
|
||||||
|
|
||||||
s.registerScenarios()
|
s.registerScenarios()
|
||||||
|
s.resourceHandler = httpadapter.New(s.registerRoutes())
|
||||||
|
|
||||||
rMux := http.NewServeMux()
|
return s
|
||||||
s.RegisterRoutes(rMux)
|
|
||||||
|
|
||||||
factory := coreplugin.New(backend.ServeOpts{
|
|
||||||
QueryDataHandler: s.queryMux,
|
|
||||||
CallResourceHandler: httpadapter.New(rMux),
|
|
||||||
StreamHandler: s,
|
|
||||||
})
|
|
||||||
resolver := plugins.CoreDataSourcePathResolver(cfg, pluginID)
|
|
||||||
err := pluginStore.AddWithFactory(context.Background(), pluginID, factory, resolver)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
cfg *setting.Cfg
|
cfg *setting.Cfg
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
scenarios map[string]*Scenario
|
scenarios map[string]*Scenario
|
||||||
frame *data.Frame
|
frame *data.Frame
|
||||||
labelFrame *data.Frame
|
labelFrame *data.Frame
|
||||||
queryMux *datasource.QueryTypeMux
|
queryMux *datasource.QueryTypeMux
|
||||||
|
resourceHandler backend.CallResourceHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||||
|
return s.queryMux.QueryData(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
|
||||||
|
return s.resourceHandler.CallResource(ctx, req, sender)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
"name": "-- Grafana --",
|
"name": "-- Grafana --",
|
||||||
"id": "grafana",
|
"id": "grafana",
|
||||||
|
|
||||||
|
"backend": true,
|
||||||
"builtIn": true,
|
"builtIn": true,
|
||||||
"annotations": true,
|
"annotations": true,
|
||||||
"metrics": true
|
"metrics": true
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue