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
co-authored by Will Browne
parent 21ba507bcb
commit 4d0a7637b2
13 changed files with 178 additions and 12 deletions
+50
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
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
}