2022-07-28 05:06:09 -05:00
|
|
|
package influxdb
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
2022-10-27 11:05:06 -05:00
|
|
|
|
|
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
2022-07-28 05:06:09 -05:00
|
|
|
"github.com/grafana/grafana/pkg/tsdb/influxdb/flux"
|
|
|
|
"github.com/grafana/grafana/pkg/tsdb/influxdb/models"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
refID = "healthcheck"
|
|
|
|
)
|
|
|
|
|
|
|
|
func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult,
|
|
|
|
error) {
|
2022-10-27 11:05:06 -05:00
|
|
|
logger := logger.FromContext(ctx)
|
2023-05-24 03:19:34 -05:00
|
|
|
dsInfo, err := s.getDSInfo(ctx, req.PluginContext)
|
2022-07-28 05:06:09 -05:00
|
|
|
if err != nil {
|
2022-10-27 11:05:06 -05:00
|
|
|
return getHealthCheckMessage(logger, "error getting datasource info", err)
|
2022-07-28 05:06:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if dsInfo == nil {
|
2022-10-27 11:05:06 -05:00
|
|
|
return getHealthCheckMessage(logger, "", errors.New("invalid datasource info received"))
|
2022-07-28 05:06:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
switch dsInfo.Version {
|
|
|
|
case influxVersionFlux:
|
2022-10-27 11:05:06 -05:00
|
|
|
return CheckFluxHealth(ctx, dsInfo, req)
|
2022-07-28 05:06:09 -05:00
|
|
|
case influxVersionInfluxQL:
|
|
|
|
return CheckInfluxQLHealth(ctx, dsInfo, s)
|
|
|
|
default:
|
2022-10-27 11:05:06 -05:00
|
|
|
return getHealthCheckMessage(logger, "", errors.New("unknown influx version"))
|
2022-07-28 05:06:09 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-27 11:05:06 -05:00
|
|
|
func CheckFluxHealth(ctx context.Context, dsInfo *models.DatasourceInfo,
|
2022-07-28 05:06:09 -05:00
|
|
|
req *backend.CheckHealthRequest) (*backend.CheckHealthResult,
|
|
|
|
error) {
|
2022-10-27 11:05:06 -05:00
|
|
|
logger := logger.FromContext(ctx)
|
2022-07-28 05:06:09 -05:00
|
|
|
ds, err := flux.Query(ctx, dsInfo, backend.QueryDataRequest{
|
|
|
|
PluginContext: req.PluginContext,
|
|
|
|
Queries: []backend.DataQuery{
|
|
|
|
{
|
|
|
|
RefID: refID,
|
|
|
|
JSON: []byte(`{ "query": "buckets()" }`),
|
|
|
|
Interval: 1 * time.Minute,
|
|
|
|
MaxDataPoints: 423,
|
|
|
|
TimeRange: backend.TimeRange{
|
|
|
|
From: time.Now().AddDate(0, 0, -1),
|
|
|
|
To: time.Now(),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
2022-10-27 11:05:06 -05:00
|
|
|
return getHealthCheckMessage(logger, "error performing flux query", err)
|
2022-07-28 05:06:09 -05:00
|
|
|
}
|
|
|
|
if res, ok := ds.Responses[refID]; ok {
|
|
|
|
if res.Error != nil {
|
2022-10-27 11:05:06 -05:00
|
|
|
return getHealthCheckMessage(logger, "error reading buckets", res.Error)
|
2022-07-28 05:06:09 -05:00
|
|
|
}
|
|
|
|
if len(res.Frames) > 0 && len(res.Frames[0].Fields) > 0 {
|
2022-10-27 11:05:06 -05:00
|
|
|
return getHealthCheckMessage(logger, fmt.Sprintf("%d buckets found", res.Frames[0].Fields[0].Len()), nil)
|
2022-07-28 05:06:09 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-27 11:05:06 -05:00
|
|
|
return getHealthCheckMessage(logger, "", errors.New("error getting flux query buckets"))
|
2022-07-28 05:06:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func CheckInfluxQLHealth(ctx context.Context, dsInfo *models.DatasourceInfo, s *Service) (*backend.CheckHealthResult, error) {
|
2022-10-27 11:05:06 -05:00
|
|
|
logger := logger.FromContext(ctx)
|
2022-07-28 05:06:09 -05:00
|
|
|
queryString := "SHOW measurements"
|
2022-10-27 11:05:06 -05:00
|
|
|
hcRequest, err := s.createRequest(ctx, logger, dsInfo, queryString)
|
2022-07-28 05:06:09 -05:00
|
|
|
if err != nil {
|
2022-10-27 11:05:06 -05:00
|
|
|
return getHealthCheckMessage(logger, "error creating influxDB healthcheck request", err)
|
2022-07-28 05:06:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
res, err := dsInfo.HTTPClient.Do(hcRequest)
|
|
|
|
if err != nil {
|
2022-10-27 11:05:06 -05:00
|
|
|
return getHealthCheckMessage(logger, "error performing influxQL query", err)
|
2022-07-28 05:06:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
if err := res.Body.Close(); err != nil {
|
2022-10-27 11:05:06 -05:00
|
|
|
logger.Warn("failed to close response body", "err", err)
|
2022-07-28 05:06:09 -05:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
if res.StatusCode/100 != 2 {
|
2022-10-27 11:05:06 -05:00
|
|
|
return getHealthCheckMessage(logger, "", fmt.Errorf("error reading InfluxDB. Status Code: %d", res.StatusCode))
|
2022-07-28 05:06:09 -05:00
|
|
|
}
|
|
|
|
resp := s.responseParser.Parse(res.Body, []Query{{
|
|
|
|
RefID: refID,
|
|
|
|
UseRawQuery: true,
|
|
|
|
RawQuery: queryString,
|
|
|
|
}})
|
|
|
|
if res, ok := resp.Responses[refID]; ok {
|
|
|
|
if res.Error != nil {
|
2022-10-27 11:05:06 -05:00
|
|
|
return getHealthCheckMessage(logger, "error reading influxDB", res.Error)
|
2022-07-28 05:06:09 -05:00
|
|
|
}
|
2022-10-19 08:15:32 -05:00
|
|
|
|
|
|
|
if len(res.Frames) == 0 {
|
2022-10-27 11:05:06 -05:00
|
|
|
return getHealthCheckMessage(logger, "0 measurements found", nil)
|
2022-10-19 08:15:32 -05:00
|
|
|
}
|
|
|
|
|
2022-07-28 05:06:09 -05:00
|
|
|
if len(res.Frames) > 0 && len(res.Frames[0].Fields) > 0 {
|
2022-10-27 11:05:06 -05:00
|
|
|
return getHealthCheckMessage(logger, fmt.Sprintf("%d measurements found", res.Frames[0].Fields[0].Len()), nil)
|
2022-07-28 05:06:09 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-27 11:05:06 -05:00
|
|
|
return getHealthCheckMessage(logger, "", errors.New("error connecting influxDB influxQL"))
|
2022-07-28 05:06:09 -05:00
|
|
|
}
|
|
|
|
|
2022-10-27 11:05:06 -05:00
|
|
|
func getHealthCheckMessage(logger log.Logger, message string, err error) (*backend.CheckHealthResult, error) {
|
2022-07-28 05:06:09 -05:00
|
|
|
if err == nil {
|
|
|
|
return &backend.CheckHealthResult{
|
|
|
|
Status: backend.HealthStatusOk,
|
|
|
|
Message: fmt.Sprintf("datasource is working. %s", message),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2022-10-27 11:05:06 -05:00
|
|
|
logger.Warn("error performing influxdb healthcheck", "err", err.Error())
|
2022-07-28 05:06:09 -05:00
|
|
|
errorMessage := fmt.Sprintf("%s %s", err.Error(), message)
|
|
|
|
|
|
|
|
return &backend.CheckHealthResult{
|
|
|
|
Status: backend.HealthStatusError,
|
|
|
|
Message: errorMessage,
|
|
|
|
}, nil
|
|
|
|
}
|