Zipkin: Run health check through backend (#96031)

* Zipkin: Run health check through backend

* Mark some errors as downstream

* Update pkg/tsdb/zipkin/zipkin.go

Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>

* Use errors.New where error formatting is not needed

* Remove executable in plugin.json

* Fix joining the path

* Keep logger as global and then create from context

* Close the res body

---------

Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
This commit is contained in:
Ivana Huckova 2024-11-07 16:48:00 +01:00 committed by GitHub
parent 21ba507bcb
commit 4d0a7637b2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 178 additions and 12 deletions

1
.github/CODEOWNERS vendored
View File

@ -276,6 +276,7 @@
# OSS Big Tent backend code
/pkg/tsdb/mysql/ @grafana/oss-big-tent
/pkg/tsdb/grafana-postgresql-datasource/ @grafana/oss-big-tent
/pkg/tsdb/zipkin/ @grafana/oss-big-tent
# Partner Datasources backend code
/pkg/tsdb/mssql/ @grafana/partner-datasources

View File

@ -717,6 +717,8 @@ github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3C
github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0=
github.com/containerd/containerd v1.6.8 h1:h4dOFDwzHmqFEP754PgfgTeVXFnLiRc6kiqC7tplDJs=
github.com/containerd/containerd v1.6.8/go.mod h1:By6p5KqPK0/7/CgO/A6t/Gz+CUYUu2zf1hUaaymVXB0=
github.com/containerd/containerd v1.6.18 h1:qZbsLvmyu+Vlty0/Ex5xc0z2YtKpIsb5n45mAMI+2Ns=
github.com/containerd/containerd v1.6.18/go.mod h1:1RdCUu95+gc2v9t3IL+zIlpClSmew7/0YS8O5eQZrOw=
github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY=
github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o=
github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04=
@ -780,6 +782,8 @@ github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55k
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
github.com/docker/go-plugins-helpers v0.0.0-20240701071450-45e2431495c8 h1:IMfrF5LCzP2Vhw7j4IIH3HxPsCLuZYjDqFAM/C88ulg=
@ -793,6 +797,8 @@ github.com/drone/drone-yaml v1.2.3 h1:SWzLmzr8ARhbtw1WsVDENa8WFY2Pi9l0FVMfafVUWz
github.com/drone/drone-yaml v1.2.3/go.mod h1:QsqliFK8nG04AHFN9tTn9XJomRBQHD4wcejWW1uz/10=
github.com/drone/funcmap v0.0.0-20211123105308-29742f68a7d1 h1:E8hjIYiEyI+1S2XZSLpMkqT9V8+YMljFNBWrFpuVM3A=
github.com/drone/funcmap v0.0.0-20211123105308-29742f68a7d1/go.mod h1:Hph0/pT6ZxbujnE1Z6/08p5I0XXuOsppqF6NQlGOK0E=
github.com/drone/funcmap v0.0.0-20220929084810-72602997d16f h1:/jEs7lulqVO2u1+XI5rW4oFwIIusxuDOVKD9PAzlW2E=
github.com/drone/funcmap v0.0.0-20220929084810-72602997d16f/go.mod h1:nDRkX7PHq+p39AD5/usv3KZMerxZTYU/9rfLS5IDspU=
github.com/drone/signal v1.0.0 h1:NrnM2M/4yAuU/tXs6RP1a1ZfxnaHwYkd0kJurA1p6uI=
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415 h1:q1oJaUPdmpDm/VyXosjgPgr6wS7c5iV2p0PwJD73bUI=
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
@ -1047,6 +1053,8 @@ github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak=
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/jon-whit/go-grpc-prometheus v1.4.0 h1:/wmpGDJcLXuEjXryWhVYEGt9YBRhtLwFEN7T+Flr8sw=
github.com/jon-whit/go-grpc-prometheus v1.4.0/go.mod h1:iTPm+Iuhh3IIqR0iGZ91JJEg5ax6YQEe1I0f6vtBuao=
github.com/joncrlsn/dque v0.0.0-20211108142734-c2ef48c5192a h1:sfe532Ipn7GX0V6mHdynBk393rDmqgI0QmjLK7ct7TU=
@ -1327,6 +1335,8 @@ github.com/prometheus/exporter-toolkit v0.10.1-0.20230714054209-2f4150c63f97/go.
github.com/prometheus/statsd_exporter v0.26.0 h1:SQl3M6suC6NWQYEzOvIv+EF6dAMYEqIuZy+o4H9F5Ig=
github.com/prometheus/statsd_exporter v0.26.0/go.mod h1:GXFLADOmBTVDrHc7b04nX8ooq3azG61pnECNqT7O5DM=
github.com/protocolbuffers/txtpbfmt v0.0.0-20220428173112-74888fd59c2b/go.mod h1:KjY0wibdYKc4DYkerHSbguaf3JeIPGhNJBp2BNiFH78=
github.com/rabbitmq/amqp091-go v1.9.0 h1:qrQtyzB4H8BQgEuJwhmVQqVHB9O4+MNDJCCAcpc3Aoo=
github.com/rabbitmq/amqp091-go v1.9.0/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/relvacode/iso8601 v1.4.0 h1:GsInVSEJfkYuirYFxa80nMLbH2aydgZpIf52gYZXUJs=

View File

@ -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, testdatasource.ProvideService(), nil, nil, nil, nil, nil, nil, nil)
testCtx := pluginsintegration.CreateIntegrationTestCtx(t, cfg, coreRegistry)

View File

@ -20,7 +20,7 @@ require (
github.com/Masterminds/semver/v3 v3.2.0 // @grafana/grafana-developer-enablement-squad
github.com/aws/aws-sdk-go v1.55.5 // @grafana/aws-datasources
github.com/docker/docker v27.3.1+incompatible // @grafana/grafana-developer-enablement-squad
github.com/drone/drone-cli v1.6.1 // @grafana/grafana-developer-enablement-squad
github.com/drone/drone-cli v1.8.0 // @grafana/grafana-developer-enablement-squad
github.com/gogo/protobuf v1.3.2 // indirect; @grafana/alerting-backend
github.com/google/go-cmp v0.6.0 // @grafana/grafana-backend-group
github.com/google/go-github v17.0.0+incompatible // @grafana/grafana-developer-enablement-squad
@ -61,7 +61,7 @@ require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/drone-runners/drone-runner-docker v1.8.2 // indirect
github.com/drone-runners/drone-runner-docker v1.8.3 // indirect
github.com/drone/drone-go v1.7.1 // indirect
github.com/drone/envsubst v1.0.3 // indirect
github.com/drone/runner-go v1.12.0 // indirect

View File

@ -72,10 +72,10 @@ github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/drone-runners/drone-runner-docker v1.8.2 h1:F7+39FSyzEUqLXYMvTdTGBhCS79ODDIhw3DQeF5GYT8=
github.com/drone-runners/drone-runner-docker v1.8.2/go.mod h1:JR3pZeVZKKpkbTajiq0YtAx9WutkODdVKZGNR83kEwE=
github.com/drone/drone-cli v1.6.1 h1:Beh0opEGR5XYezOyOmiqWzTMBGHkGDrh2tIG1cY/5GY=
github.com/drone/drone-cli v1.6.1/go.mod h1://HC780Gua3Nhob/I2VPL3nTqmleEE/HIhGhTcJb2ds=
github.com/drone-runners/drone-runner-docker v1.8.3 h1:uUnC45C1JMSLW+9uy6RoKG5ugzeXWN89pygs9BMLObY=
github.com/drone-runners/drone-runner-docker v1.8.3/go.mod h1:JR3pZeVZKKpkbTajiq0YtAx9WutkODdVKZGNR83kEwE=
github.com/drone/drone-cli v1.8.0 h1:tpp+GPonS87IKMZCGbIoa+zfDwiuJDL3NIC6S7neNrU=
github.com/drone/drone-cli v1.8.0/go.mod h1:zu6/7OpQjWBw/5VG0M3K4iJc6kSoTrjnY7CRLBrGH84=
github.com/drone/drone-go v1.7.1 h1:ZX+3Rs8YHUSUQ5mkuMLmm1zr1ttiiE2YGNxF3AnyDKw=
github.com/drone/drone-go v1.7.1/go.mod h1:fxCf9jAnXDZV1yDr0ckTuWd1intvcQwfJmTRpTZ1mXg=
github.com/drone/envsubst v1.0.2/go.mod h1:bkZbnc/2vh1M12Ecn7EYScpI4YGYU0etwLJICOWi8Z0=

View File

@ -34,6 +34,7 @@ import (
"github.com/grafana/grafana/pkg/tsdb/parca"
"github.com/grafana/grafana/pkg/tsdb/prometheus"
"github.com/grafana/grafana/pkg/tsdb/tempo"
"github.com/grafana/grafana/pkg/tsdb/zipkin"
)
const (
@ -55,6 +56,7 @@ const (
Grafana = "grafana"
Pyroscope = "grafana-pyroscope-datasource"
Parca = "parca"
Zipkin = "zipkin"
)
func init() {
@ -93,7 +95,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) *Registry {
ms *mssql.Service, graf *grafanads.Service, pyroscope *pyroscope.Service, parca *parca.Service, zipkin *zipkin.Service) *Registry {
// Non-optimal global solution to replace plugin SDK default tracer for core plugins.
sdktracing.InitDefaultTracer(tracer)
@ -115,6 +117,7 @@ func ProvideCoreRegistry(tracer tracing.Tracer, am *azuremonitor.Service, cw *cl
Grafana: asBackendPlugin(graf),
Pyroscope: asBackendPlugin(pyroscope),
Parca: asBackendPlugin(parca),
Zipkin: asBackendPlugin(zipkin),
})
}
@ -239,6 +242,8 @@ func NewPlugin(pluginID string, cfg *setting.Cfg, httpClientProvider *httpclient
svc = pyroscope.ProvideService(httpClientProvider)
case Parca:
svc = parca.ProvideService(httpClientProvider)
case Zipkin:
svc = zipkin.ProvideService(httpClientProvider)
default:
return nil, ErrCorePluginNotFound
}

View File

@ -35,6 +35,7 @@ func TestNewPlugin(t *testing.T) {
{ID: Tempo},
{ID: TestData, ExpectedAlias: TestDataAlias},
{ID: TestDataAlias, ExpectedID: TestData, ExpectedAlias: TestDataAlias},
{ID: Zipkin},
}
for _, tc := range tcs {

View File

@ -173,6 +173,7 @@ import (
"github.com/grafana/grafana/pkg/tsdb/parca"
"github.com/grafana/grafana/pkg/tsdb/prometheus"
"github.com/grafana/grafana/pkg/tsdb/tempo"
"github.com/grafana/grafana/pkg/tsdb/zipkin"
)
var wireBasicSet = wire.NewSet(
@ -267,6 +268,7 @@ var wireBasicSet = wire.NewSet(
elasticsearch.ProvideService,
pyroscope.ProvideService,
parca.ProvideService,
zipkin.ProvideService,
datasourceservice.ProvideCacheService,
wire.Bind(new(datasources.CacheService), new(*datasourceservice.CacheServiceImpl)),
encryptionservice.ProvideEncryptionService,

View File

@ -40,6 +40,7 @@ import (
"github.com/grafana/grafana/pkg/tsdb/parca"
"github.com/grafana/grafana/pkg/tsdb/prometheus"
"github.com/grafana/grafana/pkg/tsdb/tempo"
"github.com/grafana/grafana/pkg/tsdb/zipkin"
)
func TestMain(m *testing.M) {
@ -94,7 +95,8 @@ func TestIntegrationPluginManager(t *testing.T) {
graf := grafanads.ProvideService(sv2, nil, nil, features)
pyroscope := pyroscope.ProvideService(hcp)
parca := parca.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 := 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)
testCtx := CreateIntegrationTestCtx(t, cfg, coreRegistry)

50
pkg/tsdb/zipkin/client.go Normal file
View File

@ -0,0 +1,50 @@
package zipkin
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 ZipkinClient struct {
logger log.Logger
url string
httpClient *http.Client
}
func New(url string, hc *http.Client, logger log.Logger) (ZipkinClient, error) {
client := ZipkinClient{
logger: logger,
url: url,
httpClient: hc,
}
return client, nil
}
// Services returns list of services
// https://zipkin.io/zipkin-api/#/default/get_services
func (z *ZipkinClient) Services() ([]string, error) {
services := []string{}
u, err := url.JoinPath(z.url, "/api/v2/services")
if err != nil {
return services, backend.DownstreamError(fmt.Errorf("failed to join url: %w", err))
}
res, err := z.httpClient.Get(u)
if err != nil {
return services, err
}
defer func() {
if err = res.Body.Close(); err != nil {
z.logger.Error("Failed to close response body", "error", err)
}
}()
if err := json.NewDecoder(res.Body).Decode(&services); err != nil {
return services, err
}
return services, err
}

83
pkg/tsdb/zipkin/zipkin.go Normal file
View File

@ -0,0 +1,83 @@
package zipkin
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.zipkin")
type Service struct {
im instancemgmt.InstanceManager
}
func ProvideService(httpClientProvider httpclient.Provider) *Service {
return &Service{
im: datasource.NewInstanceManager(newInstanceSettings(httpClientProvider)),
}
}
type datasourceInfo struct {
ZipkinClient ZipkinClient
}
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)
zipkinClient, err := New(settings.URL, httpClient, logger)
return &datasourceInfo{ZipkinClient: zipkinClient}, 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.ZipkinClient.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
}

View File

@ -4,7 +4,6 @@ import { map } from 'rxjs/operators';
import {
DataQueryRequest,
DataQueryResponse,
DataSourceApi,
DataSourceInstanceSettings,
DataSourceJsonData,
FieldType,
@ -13,7 +12,15 @@ import {
urlUtil,
} from '@grafana/data';
import { NodeGraphOptions, SpanBarOptions } from '@grafana/o11y-ds-frontend';
import { BackendSrvRequest, FetchResponse, getBackendSrv, getTemplateSrv, TemplateSrv } from '@grafana/runtime';
import {
BackendSrvRequest,
config,
DataSourceWithBackend,
FetchResponse,
getBackendSrv,
getTemplateSrv,
TemplateSrv,
} from '@grafana/runtime';
import { apiPrefix } from './constants';
import { ZipkinQuery, ZipkinSpan } from './types';
@ -24,7 +31,7 @@ export interface ZipkinJsonData extends DataSourceJsonData {
nodeGraph?: NodeGraphOptions;
}
export class ZipkinDatasource extends DataSourceApi<ZipkinQuery, ZipkinJsonData> {
export class ZipkinDatasource extends DataSourceWithBackend<ZipkinQuery, ZipkinJsonData> {
uploadedJson: string | ArrayBuffer | null = null;
nodeGraph?: NodeGraphOptions;
spanBar?: SpanBarOptions;
@ -66,6 +73,10 @@ export class ZipkinDatasource extends DataSourceApi<ZipkinQuery, ZipkinJsonData>
}
async testDatasource(): Promise<{ status: string; message: string }> {
if (config.featureToggles.zipkinBackendMigration) {
return await super.testDatasource();
}
await this.metadataRequest(`${apiPrefix}/services`);
return { status: 'success', message: 'Data source is working' };
}

View File

@ -4,6 +4,7 @@
"id": "zipkin",
"category": "tracing",
"backend": true,
"metrics": true,
"alerting": false,
"annotations": false,