diff --git a/pkg/api/plugin_resource_test.go b/pkg/api/plugin_resource_test.go index 53593536f94..41fcd591f57 100644 --- a/pkg/api/plugin_resource_test.go +++ b/pkg/api/plugin_resource_test.go @@ -50,7 +50,7 @@ func TestCallResource(t *testing.T) { cfg.Azure = &azsettings.AzureSettings{} coreRegistry := coreplugin.ProvideCoreRegistry(tracing.InitializeTracerForTest(), nil, &cloudwatch.CloudWatchService{}, nil, nil, nil, nil, - nil, nil, nil, nil, testdatasource.ProvideService(), nil, nil, nil, nil, nil, nil, nil) + nil, nil, nil, nil, testdatasource.ProvideService(), nil, nil, nil, nil, nil, nil, nil, nil) testCtx := pluginsintegration.CreateIntegrationTestCtx(t, cfg, coreRegistry) diff --git a/pkg/plugins/backendplugin/coreplugin/registry.go b/pkg/plugins/backendplugin/coreplugin/registry.go index e346c7893af..cb0dea1bd0a 100644 --- a/pkg/plugins/backendplugin/coreplugin/registry.go +++ b/pkg/plugins/backendplugin/coreplugin/registry.go @@ -27,6 +27,7 @@ import ( "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/jaeger" "github.com/grafana/grafana/pkg/tsdb/loki" "github.com/grafana/grafana/pkg/tsdb/mssql" "github.com/grafana/grafana/pkg/tsdb/mysql" @@ -57,6 +58,7 @@ const ( Pyroscope = "grafana-pyroscope-datasource" Parca = "parca" Zipkin = "zipkin" + Jaeger = "jaeger" ) func init() { @@ -95,7 +97,7 @@ func NewRegistry(store map[string]backendplugin.PluginFactoryFunc) *Registry { func ProvideCoreRegistry(tracer tracing.Tracer, 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, pyroscope *pyroscope.Service, parca *parca.Service, zipkin *zipkin.Service) *Registry { + ms *mssql.Service, graf *grafanads.Service, pyroscope *pyroscope.Service, parca *parca.Service, zipkin *zipkin.Service, jaeger *jaeger.Service) *Registry { // Non-optimal global solution to replace plugin SDK default tracer for core plugins. sdktracing.InitDefaultTracer(tracer) @@ -118,6 +120,7 @@ func ProvideCoreRegistry(tracer tracing.Tracer, am *azuremonitor.Service, cw *cl Pyroscope: asBackendPlugin(pyroscope), Parca: asBackendPlugin(parca), Zipkin: asBackendPlugin(zipkin), + Jaeger: asBackendPlugin(jaeger), }) } @@ -245,6 +248,8 @@ func NewPlugin(pluginID string, cfg *setting.Cfg, httpClientProvider *httpclient svc = parca.ProvideService(httpClientProvider) case Zipkin: svc = zipkin.ProvideService(httpClientProvider) + case Jaeger: + svc = jaeger.ProvideService(httpClientProvider) default: return nil, ErrCorePluginNotFound } diff --git a/pkg/plugins/backendplugin/coreplugin/registry_test.go b/pkg/plugins/backendplugin/coreplugin/registry_test.go index 2bb6bed4042..41a1ca7f7ec 100644 --- a/pkg/plugins/backendplugin/coreplugin/registry_test.go +++ b/pkg/plugins/backendplugin/coreplugin/registry_test.go @@ -37,6 +37,7 @@ func TestNewPlugin(t *testing.T) { {ID: TestData, ExpectedAlias: TestDataAlias}, {ID: TestDataAlias, ExpectedID: TestData, ExpectedAlias: TestDataAlias}, {ID: Zipkin}, + {ID: Jaeger}, } for _, tc := range tcs { diff --git a/pkg/server/wire.go b/pkg/server/wire.go index a82cd042e66..9f60a03f40f 100644 --- a/pkg/server/wire.go +++ b/pkg/server/wire.go @@ -168,6 +168,7 @@ import ( "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/jaeger" "github.com/grafana/grafana/pkg/tsdb/loki" "github.com/grafana/grafana/pkg/tsdb/mssql" "github.com/grafana/grafana/pkg/tsdb/mysql" @@ -269,6 +270,7 @@ var wireBasicSet = wire.NewSet( pyroscope.ProvideService, parca.ProvideService, zipkin.ProvideService, + jaeger.ProvideService, datasourceservice.ProvideCacheService, wire.Bind(new(datasources.CacheService), new(*datasourceservice.CacheServiceImpl)), encryptionservice.ProvideEncryptionService, diff --git a/pkg/services/pluginsintegration/plugins_integration_test.go b/pkg/services/pluginsintegration/plugins_integration_test.go index 8b2e9af7439..7d5bf5f8978 100644 --- a/pkg/services/pluginsintegration/plugins_integration_test.go +++ b/pkg/services/pluginsintegration/plugins_integration_test.go @@ -34,6 +34,7 @@ import ( "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/jaeger" "github.com/grafana/grafana/pkg/tsdb/loki" "github.com/grafana/grafana/pkg/tsdb/mssql" "github.com/grafana/grafana/pkg/tsdb/mysql" @@ -93,7 +94,8 @@ func TestIntegrationPluginManager(t *testing.T) { pyroscope := pyroscope.ProvideService(hcp) parca := parca.ProvideService(hcp) zipkin := zipkin.ProvideService(hcp) - coreRegistry := coreplugin.ProvideCoreRegistry(tracing.InitializeTracerForTest(), am, cw, cm, es, grap, idb, lk, otsdb, pr, tmpo, td, pg, my, ms, graf, pyroscope, parca, zipkin) + jaeger := jaeger.ProvideService(hcp) + coreRegistry := coreplugin.ProvideCoreRegistry(tracing.InitializeTracerForTest(), am, cw, cm, es, grap, idb, lk, otsdb, pr, tmpo, td, pg, my, ms, graf, pyroscope, parca, zipkin, jaeger) testCtx := CreateIntegrationTestCtx(t, cfg, coreRegistry) diff --git a/pkg/tsdb/jaeger/client.go b/pkg/tsdb/jaeger/client.go new file mode 100644 index 00000000000..f1b14823e53 --- /dev/null +++ b/pkg/tsdb/jaeger/client.go @@ -0,0 +1,62 @@ +package jaeger + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + + "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana-plugin-sdk-go/backend/log" +) + +type JaegerClient struct { + logger log.Logger + url string + httpClient *http.Client +} + +type ServicesResponse struct { + Data []string `json:"data"` + Errors interface{} `json:"errors"` + Limit int `json:"limit"` + Offset int `json:"offset"` + Total int `json:"total"` +} + +func New(url string, hc *http.Client, logger log.Logger) (JaegerClient, error) { + client := JaegerClient{ + logger: logger, + url: url, + httpClient: hc, + } + return client, nil +} + +func (j *JaegerClient) Services() ([]string, error) { + var response ServicesResponse + services := []string{} + + u, err := url.JoinPath(j.url, "/api/services") + if err != nil { + return services, backend.DownstreamError(fmt.Errorf("failed to join url: %w", err)) + } + + res, err := j.httpClient.Get(u) + if err != nil { + return services, err + } + + defer func() { + if err = res.Body.Close(); err != nil { + j.logger.Error("Failed to close response body", "error", err) + } + }() + + if err := json.NewDecoder(res.Body).Decode(&response); err != nil { + return services, err + } + + services = response.Data + return services, err +} diff --git a/pkg/tsdb/jaeger/jaeger.go b/pkg/tsdb/jaeger/jaeger.go new file mode 100644 index 00000000000..24244c69e8e --- /dev/null +++ b/pkg/tsdb/jaeger/jaeger.go @@ -0,0 +1,87 @@ +package jaeger + +import ( + "context" + "errors" + "fmt" + + "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/instancemgmt" + + "github.com/grafana/grafana/pkg/infra/httpclient" +) + +var logger = backend.NewLoggerWith("logger", "tsdb.jaeger") + +type Service struct { + im instancemgmt.InstanceManager +} + +func ProvideService(httpClientProvider httpclient.Provider) *Service { + return &Service{ + im: datasource.NewInstanceManager(newInstanceSettings(httpClientProvider)), + } +} + +type datasourceInfo struct { + JaegerClient JaegerClient +} + +func newInstanceSettings(httpClientProvider httpclient.Provider) datasource.InstanceFactoryFunc { + return func(ctx context.Context, settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + httpClientOptions, err := settings.HTTPClientOptions(ctx) + if err != nil { + return nil, backend.DownstreamError(fmt.Errorf("error reading settings: %w", err)) + } + + httpClient, err := httpClientProvider.New(httpClientOptions) + if err != nil { + return nil, fmt.Errorf("error creating http client: %w", err) + } + + if settings.URL == "" { + return nil, backend.DownstreamError(errors.New("error reading settings: url is empty")) + } + + logger := logger.FromContext(ctx) + jaegerClient, err := New(settings.URL, httpClient, logger) + return &datasourceInfo{JaegerClient: jaegerClient}, err + } +} + +func (s *Service) getDSInfo(ctx context.Context, pluginCtx backend.PluginContext) (*datasourceInfo, error) { + i, err := s.im.Get(ctx, pluginCtx) + if err != nil { + return nil, err + } + + instance, ok := i.(*datasourceInfo) + if !ok { + return nil, errors.New("failed to cast datasource info") + } + + return instance, nil +} + +func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) { + client, err := s.getDSInfo(ctx, backend.PluginConfigFromContext(ctx)) + if err != nil { + return &backend.CheckHealthResult{ + Status: backend.HealthStatusError, + Message: err.Error(), + }, nil + } + + if _, err = client.JaegerClient.Services(); err != nil { + return &backend.CheckHealthResult{ + Status: backend.HealthStatusError, + Message: err.Error(), + }, nil + } + + return &backend.CheckHealthResult{ + Status: backend.HealthStatusOk, + Message: "Data source is working", + }, nil +} diff --git a/pkg/tsdb/jaeger/plugin.go b/pkg/tsdb/jaeger/plugin.go deleted file mode 100644 index 17974e553c4..00000000000 --- a/pkg/tsdb/jaeger/plugin.go +++ /dev/null @@ -1 +0,0 @@ -package jaeger diff --git a/public/app/plugins/datasource/jaeger/datasource.ts b/public/app/plugins/datasource/jaeger/datasource.ts index a26e755cd98..fce1db5076b 100644 --- a/public/app/plugins/datasource/jaeger/datasource.ts +++ b/public/app/plugins/datasource/jaeger/datasource.ts @@ -5,7 +5,6 @@ import { catchError, map } from 'rxjs/operators'; import { DataQueryRequest, DataQueryResponse, - DataSourceApi, DataSourceInstanceSettings, DataSourceJsonData, dateMath, @@ -17,7 +16,14 @@ import { urlUtil, } from '@grafana/data'; import { NodeGraphOptions, SpanBarOptions } from '@grafana/o11y-ds-frontend'; -import { BackendSrvRequest, getBackendSrv, getTemplateSrv, TemplateSrv } from '@grafana/runtime'; +import { + BackendSrvRequest, + config, + DataSourceWithBackend, + getBackendSrv, + getTemplateSrv, + TemplateSrv, +} from '@grafana/runtime'; import { ALL_OPERATIONS_KEY } from './components/SearchForm'; import { TraceIdTimeParamsOptions } from './configuration/TraceIdTimeParams'; @@ -32,7 +38,7 @@ export interface JaegerJsonData extends DataSourceJsonData { traceIdTimeParams?: TraceIdTimeParamsOptions; } -export class JaegerDatasource extends DataSourceApi { +export class JaegerDatasource extends DataSourceWithBackend { uploadedJson: string | ArrayBuffer | null = null; nodeGraph?: NodeGraphOptions; traceIdTimeParams?: TraceIdTimeParamsOptions; @@ -188,6 +194,10 @@ export class JaegerDatasource extends DataSourceApi } async testDatasource() { + if (config.featureToggles.jaegerBackendMigration) { + return await super.testDatasource(); + } + return lastValueFrom( this._request('/api/services').pipe( map((res) => { diff --git a/public/app/plugins/datasource/jaeger/plugin.json b/public/app/plugins/datasource/jaeger/plugin.json index 5bb7b96d60c..d9aa0aca223 100644 --- a/public/app/plugins/datasource/jaeger/plugin.json +++ b/public/app/plugins/datasource/jaeger/plugin.json @@ -4,6 +4,7 @@ "id": "jaeger", "category": "tracing", + "backend": true, "metrics": true, "alerting": false, "annotations": false,