Introduce TSDB service (#31520)
* Introduce TSDB service Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> Co-authored-by: Erik Sundell <erik.sundell87@gmail.com> Co-authored-by: Will Browne <will.browne@grafana.com> Co-authored-by: Torkel Ödegaard <torkel@grafana.org> Co-authored-by: Will Browne <wbrowne@users.noreply.github.com> Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>
This commit is contained in:
co-authored by
Erik Sundell
Will Browne
Torkel Ödegaard
Will Browne
Zoltán Bedi
parent
c899bf3592
commit
b79e61656a
@@ -4,24 +4,25 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/tsdb"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
)
|
||||
|
||||
func (e *CloudMonitoringExecutor) executeAnnotationQuery(ctx context.Context, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
||||
result := &tsdb.Response{
|
||||
Results: make(map[string]*tsdb.QueryResult),
|
||||
func (e *Executor) executeAnnotationQuery(ctx context.Context, tsdbQuery plugins.DataQuery) (
|
||||
plugins.DataResponse, error) {
|
||||
result := plugins.DataResponse{
|
||||
Results: make(map[string]plugins.DataQueryResult),
|
||||
}
|
||||
|
||||
firstQuery := tsdbQuery.Queries[0]
|
||||
|
||||
queries, err := e.buildQueryExecutors(tsdbQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return plugins.DataResponse{}, err
|
||||
}
|
||||
|
||||
queryRes, resp, _, err := queries[0].run(ctx, tsdbQuery, e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return plugins.DataResponse{}, err
|
||||
}
|
||||
|
||||
metricQuery := firstQuery.Model.Get("metricQuery")
|
||||
@@ -29,16 +30,16 @@ func (e *CloudMonitoringExecutor) executeAnnotationQuery(ctx context.Context, ts
|
||||
text := metricQuery.Get("text").MustString()
|
||||
tags := metricQuery.Get("tags").MustString()
|
||||
|
||||
err = queries[0].parseToAnnotations(queryRes, resp, title, text, tags)
|
||||
result.Results[firstQuery.RefId] = queryRes
|
||||
err = queries[0].parseToAnnotations(&queryRes, resp, title, text, tags)
|
||||
result.Results[firstQuery.RefID] = queryRes
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func transformAnnotationToTable(data []map[string]string, result *tsdb.QueryResult) {
|
||||
table := &tsdb.Table{
|
||||
Columns: make([]tsdb.TableColumn, 4),
|
||||
Rows: make([]tsdb.RowValues, 0),
|
||||
func transformAnnotationToTable(data []map[string]string, result *plugins.DataQueryResult) {
|
||||
table := plugins.DataTable{
|
||||
Columns: make([]plugins.DataTableColumn, 4),
|
||||
Rows: make([]plugins.DataRowValues, 0),
|
||||
}
|
||||
table.Columns[0].Text = "time"
|
||||
table.Columns[1].Text = "title"
|
||||
|
||||
@@ -4,23 +4,25 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/tsdb"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCloudMonitoringExecutor_parseToAnnotations(t *testing.T) {
|
||||
func TestExecutor_parseToAnnotations(t *testing.T) {
|
||||
d, err := loadTestFile("./test-data/2-series-response-no-agg.json")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, d.TimeSeries, 3)
|
||||
|
||||
res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "annotationQuery"}
|
||||
res := &plugins.DataQueryResult{Meta: simplejson.New(), RefID: "annotationQuery"}
|
||||
query := &cloudMonitoringTimeSeriesFilter{}
|
||||
|
||||
err = query.parseToAnnotations(res, d, "atitle {{metric.label.instance_name}} {{metric.value}}", "atext {{resource.label.zone}}", "atag")
|
||||
err = query.parseToAnnotations(res, d, "atitle {{metric.label.instance_name}} {{metric.value}}",
|
||||
"atext {{resource.label.zone}}", "atag")
|
||||
require.NoError(t, err)
|
||||
|
||||
decoded, _ := res.Dataframes.Decoded()
|
||||
decoded, err := res.Dataframes.Decoded()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, decoded, 3)
|
||||
assert.Equal(t, "title", decoded[0].Fields[1].Name)
|
||||
assert.Equal(t, "tags", decoded[0].Fields[2].Name)
|
||||
@@ -28,7 +30,7 @@ func TestCloudMonitoringExecutor_parseToAnnotations(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCloudMonitoringExecutor_parseToAnnotations_emptyTimeSeries(t *testing.T) {
|
||||
res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "annotationQuery"}
|
||||
res := &plugins.DataQueryResult{Meta: simplejson.New(), RefID: "annotationQuery"}
|
||||
query := &cloudMonitoringTimeSeriesFilter{}
|
||||
|
||||
response := cloudMonitoringResponse{
|
||||
@@ -38,12 +40,13 @@ func TestCloudMonitoringExecutor_parseToAnnotations_emptyTimeSeries(t *testing.T
|
||||
err := query.parseToAnnotations(res, response, "atitle", "atext", "atag")
|
||||
require.NoError(t, err)
|
||||
|
||||
decoded, _ := res.Dataframes.Decoded()
|
||||
decoded, err := res.Dataframes.Decoded()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, decoded, 0)
|
||||
}
|
||||
|
||||
func TestCloudMonitoringExecutor_parseToAnnotations_noPointsInSeries(t *testing.T) {
|
||||
res := &tsdb.QueryResult{Meta: simplejson.New(), RefId: "annotationQuery"}
|
||||
res := &plugins.DataQueryResult{Meta: simplejson.New(), RefID: "annotationQuery"}
|
||||
query := &cloudMonitoringTimeSeriesFilter{}
|
||||
|
||||
response := cloudMonitoringResponse{
|
||||
|
||||
@@ -16,14 +16,16 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
||||
"github.com/grafana/grafana/pkg/registry"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
"github.com/grafana/grafana/pkg/api/pluginproxy"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tsdb"
|
||||
"golang.org/x/oauth2/google"
|
||||
)
|
||||
|
||||
@@ -62,20 +64,35 @@ const (
|
||||
mqlEditorMode string = "mql"
|
||||
)
|
||||
|
||||
// CloudMonitoringExecutor executes queries for the CloudMonitoring datasource
|
||||
type CloudMonitoringExecutor struct {
|
||||
func init() {
|
||||
registry.Register(®istry.Descriptor{
|
||||
Name: "CloudMonitoringService",
|
||||
InitPriority: registry.Low,
|
||||
Instance: &Service{},
|
||||
})
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
}
|
||||
|
||||
func (s *Service) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Executor executes queries for the CloudMonitoring datasource.
|
||||
type Executor struct {
|
||||
httpClient *http.Client
|
||||
dsInfo *models.DataSource
|
||||
}
|
||||
|
||||
// NewCloudMonitoringExecutor initializes a http client
|
||||
func NewCloudMonitoringExecutor(dsInfo *models.DataSource) (tsdb.TsdbQueryEndpoint, error) {
|
||||
// NewExecutor returns an Executor.
|
||||
func (s *Service) NewExecutor(dsInfo *models.DataSource) (plugins.DataPlugin, error) {
|
||||
httpClient, err := dsInfo.GetHttpClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &CloudMonitoringExecutor{
|
||||
return &Executor{
|
||||
httpClient: httpClient,
|
||||
dsInfo: dsInfo,
|
||||
}, nil
|
||||
@@ -83,14 +100,14 @@ func NewCloudMonitoringExecutor(dsInfo *models.DataSource) (tsdb.TsdbQueryEndpoi
|
||||
|
||||
func init() {
|
||||
slog = log.New("tsdb.cloudMonitoring")
|
||||
tsdb.RegisterTsdbQueryEndpoint("stackdriver", NewCloudMonitoringExecutor)
|
||||
}
|
||||
|
||||
// Query takes in the frontend queries, parses them into the CloudMonitoring query format
|
||||
// executes the queries against the CloudMonitoring API and parses the response into
|
||||
// the time series or table format
|
||||
func (e *CloudMonitoringExecutor) Query(ctx context.Context, dsInfo *models.DataSource, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
||||
var result *tsdb.Response
|
||||
func (e *Executor) DataQuery(ctx context.Context, dsInfo *models.DataSource, tsdbQuery plugins.DataQuery) (
|
||||
plugins.DataResponse, error) {
|
||||
var result plugins.DataResponse
|
||||
var err error
|
||||
queryType := tsdbQuery.Queries[0].Model.Get("type").MustString("")
|
||||
|
||||
@@ -108,32 +125,34 @@ func (e *CloudMonitoringExecutor) Query(ctx context.Context, dsInfo *models.Data
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (e *CloudMonitoringExecutor) getGCEDefaultProject(ctx context.Context, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
||||
result := &tsdb.Response{
|
||||
Results: make(map[string]*tsdb.QueryResult),
|
||||
func (e *Executor) getGCEDefaultProject(ctx context.Context, tsdbQuery plugins.DataQuery) (plugins.DataResponse, error) {
|
||||
result := plugins.DataResponse{
|
||||
Results: make(map[string]plugins.DataQueryResult),
|
||||
}
|
||||
refId := tsdbQuery.Queries[0].RefId
|
||||
queryResult := &tsdb.QueryResult{Meta: simplejson.New(), RefId: refId}
|
||||
refID := tsdbQuery.Queries[0].RefID
|
||||
queryResult := plugins.DataQueryResult{Meta: simplejson.New(), RefID: refID}
|
||||
|
||||
gceDefaultProject, err := e.getDefaultProject(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve default project from GCE metadata server, error: %w", err)
|
||||
return plugins.DataResponse{}, fmt.Errorf(
|
||||
"failed to retrieve default project from GCE metadata server, error: %w", err)
|
||||
}
|
||||
|
||||
queryResult.Meta.Set("defaultProject", gceDefaultProject)
|
||||
result.Results[refId] = queryResult
|
||||
result.Results[refID] = queryResult
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (e *CloudMonitoringExecutor) executeTimeSeriesQuery(ctx context.Context, tsdbQuery *tsdb.TsdbQuery) (*tsdb.Response, error) {
|
||||
result := &tsdb.Response{
|
||||
Results: make(map[string]*tsdb.QueryResult),
|
||||
func (e *Executor) executeTimeSeriesQuery(ctx context.Context, tsdbQuery plugins.DataQuery) (
|
||||
plugins.DataResponse, error) {
|
||||
result := plugins.DataResponse{
|
||||
Results: make(map[string]plugins.DataQueryResult),
|
||||
}
|
||||
|
||||
queryExecutors, err := e.buildQueryExecutors(tsdbQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return plugins.DataResponse{}, err
|
||||
}
|
||||
|
||||
unit := e.resolvePanelUnitFromQueries(queryExecutors)
|
||||
@@ -141,9 +160,9 @@ func (e *CloudMonitoringExecutor) executeTimeSeriesQuery(ctx context.Context, ts
|
||||
for _, queryExecutor := range queryExecutors {
|
||||
queryRes, resp, executedQueryString, err := queryExecutor.run(ctx, tsdbQuery, e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return plugins.DataResponse{}, err
|
||||
}
|
||||
err = queryExecutor.parseResponse(queryRes, resp, executedQueryString)
|
||||
err = queryExecutor.parseResponse(&queryRes, resp, executedQueryString)
|
||||
if err != nil {
|
||||
queryRes.Error = err
|
||||
}
|
||||
@@ -158,7 +177,7 @@ func (e *CloudMonitoringExecutor) executeTimeSeriesQuery(ctx context.Context, ts
|
||||
}
|
||||
frames[i].Fields[1].Config.Unit = unit
|
||||
}
|
||||
queryRes.Dataframes = tsdb.NewDecodedDataFrames(frames)
|
||||
queryRes.Dataframes = plugins.NewDecodedDataFrames(frames)
|
||||
}
|
||||
result.Results[queryExecutor.getRefID()] = queryRes
|
||||
}
|
||||
@@ -166,7 +185,7 @@ func (e *CloudMonitoringExecutor) executeTimeSeriesQuery(ctx context.Context, ts
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (e *CloudMonitoringExecutor) resolvePanelUnitFromQueries(executors []cloudMonitoringQueryExecutor) string {
|
||||
func (e *Executor) resolvePanelUnitFromQueries(executors []cloudMonitoringQueryExecutor) string {
|
||||
if len(executors) == 0 {
|
||||
return ""
|
||||
}
|
||||
@@ -186,7 +205,7 @@ func (e *CloudMonitoringExecutor) resolvePanelUnitFromQueries(executors []cloudM
|
||||
return ""
|
||||
}
|
||||
|
||||
func (e *CloudMonitoringExecutor) buildQueryExecutors(tsdbQuery *tsdb.TsdbQuery) ([]cloudMonitoringQueryExecutor, error) {
|
||||
func (e *Executor) buildQueryExecutors(tsdbQuery plugins.DataQuery) ([]cloudMonitoringQueryExecutor, error) {
|
||||
cloudMonitoringQueryExecutors := []cloudMonitoringQueryExecutor{}
|
||||
|
||||
startTime, err := tsdbQuery.TimeRange.ParseFrom()
|
||||
@@ -201,10 +220,14 @@ func (e *CloudMonitoringExecutor) buildQueryExecutors(tsdbQuery *tsdb.TsdbQuery)
|
||||
|
||||
durationSeconds := int(endTime.Sub(startTime).Seconds())
|
||||
|
||||
for _, query := range tsdbQuery.Queries {
|
||||
migrateLegacyQueryModel(query)
|
||||
for i := range tsdbQuery.Queries {
|
||||
migrateLegacyQueryModel(&tsdbQuery.Queries[i])
|
||||
query := tsdbQuery.Queries[i]
|
||||
q := grafanaQuery{}
|
||||
model, _ := query.Model.MarshalJSON()
|
||||
model, err := query.Model.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.Unmarshal(model, &q); err != nil {
|
||||
return nil, fmt.Errorf("could not unmarshal CloudMonitoringQuery json: %w", err)
|
||||
}
|
||||
@@ -215,20 +238,19 @@ func (e *CloudMonitoringExecutor) buildQueryExecutors(tsdbQuery *tsdb.TsdbQuery)
|
||||
|
||||
var queryInterface cloudMonitoringQueryExecutor
|
||||
cmtsf := &cloudMonitoringTimeSeriesFilter{
|
||||
RefID: query.RefId,
|
||||
RefID: query.RefID,
|
||||
GroupBys: []string{},
|
||||
}
|
||||
|
||||
switch q.QueryType {
|
||||
case metricQueryType:
|
||||
if q.MetricQuery.EditorMode == mqlEditorMode {
|
||||
queryInterface = &cloudMonitoringTimeSeriesQuery{
|
||||
RefID: query.RefId,
|
||||
RefID: query.RefID,
|
||||
ProjectName: q.MetricQuery.ProjectName,
|
||||
Query: q.MetricQuery.Query,
|
||||
IntervalMS: query.IntervalMs,
|
||||
IntervalMS: query.IntervalMS,
|
||||
AliasBy: q.MetricQuery.AliasBy,
|
||||
timeRange: tsdbQuery.TimeRange,
|
||||
timeRange: *tsdbQuery.TimeRange,
|
||||
}
|
||||
} else {
|
||||
cmtsf.AliasBy = q.MetricQuery.AliasBy
|
||||
@@ -239,7 +261,7 @@ func (e *CloudMonitoringExecutor) buildQueryExecutors(tsdbQuery *tsdb.TsdbQuery)
|
||||
}
|
||||
params.Add("filter", buildFilterString(q.MetricQuery.MetricType, q.MetricQuery.Filters))
|
||||
params.Add("view", q.MetricQuery.View)
|
||||
setMetricAggParams(¶ms, &q.MetricQuery, durationSeconds, query.IntervalMs)
|
||||
setMetricAggParams(¶ms, &q.MetricQuery, durationSeconds, query.IntervalMS)
|
||||
queryInterface = cmtsf
|
||||
}
|
||||
case sloQueryType:
|
||||
@@ -249,8 +271,10 @@ func (e *CloudMonitoringExecutor) buildQueryExecutors(tsdbQuery *tsdb.TsdbQuery)
|
||||
cmtsf.Service = q.SloQuery.ServiceId
|
||||
cmtsf.Slo = q.SloQuery.SloId
|
||||
params.Add("filter", buildSLOFilterExpression(q.SloQuery))
|
||||
setSloAggParams(¶ms, &q.SloQuery, durationSeconds, query.IntervalMs)
|
||||
setSloAggParams(¶ms, &q.SloQuery, durationSeconds, query.IntervalMS)
|
||||
queryInterface = cmtsf
|
||||
default:
|
||||
panic(fmt.Sprintf("Unrecognized query type %q", q.QueryType))
|
||||
}
|
||||
|
||||
target = params.Encode()
|
||||
@@ -268,7 +292,7 @@ func (e *CloudMonitoringExecutor) buildQueryExecutors(tsdbQuery *tsdb.TsdbQuery)
|
||||
return cloudMonitoringQueryExecutors, nil
|
||||
}
|
||||
|
||||
func migrateLegacyQueryModel(query *tsdb.Query) {
|
||||
func migrateLegacyQueryModel(query *plugins.DataSubQuery) {
|
||||
mq := query.Model.Get("metricQuery").MustMap()
|
||||
if mq == nil {
|
||||
migratedModel := simplejson.NewFromAny(map[string]interface{}{
|
||||
@@ -402,7 +426,8 @@ func containsLabel(labels []string, newLabel string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func formatLegendKeys(metricType string, defaultMetricName string, labels map[string]string, additionalLabels map[string]string, query *cloudMonitoringTimeSeriesFilter) string {
|
||||
func formatLegendKeys(metricType string, defaultMetricName string, labels map[string]string,
|
||||
additionalLabels map[string]string, query *cloudMonitoringTimeSeriesFilter) string {
|
||||
if query.AliasBy == "" {
|
||||
return defaultMetricName
|
||||
}
|
||||
@@ -488,7 +513,7 @@ func calcBucketBound(bucketOptions cloudMonitoringBucketOptions, n int) string {
|
||||
return bucketBound
|
||||
}
|
||||
|
||||
func (e *CloudMonitoringExecutor) createRequest(ctx context.Context, dsInfo *models.DataSource, proxyPass string, body io.Reader) (*http.Request, error) {
|
||||
func (e *Executor) createRequest(ctx context.Context, dsInfo *models.DataSource, proxyPass string, body io.Reader) (*http.Request, error) {
|
||||
u, err := url.Parse(dsInfo.Url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -509,7 +534,7 @@ func (e *CloudMonitoringExecutor) createRequest(ctx context.Context, dsInfo *mod
|
||||
req.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
|
||||
|
||||
// find plugin
|
||||
plugin, ok := plugins.DataSources[dsInfo.Type]
|
||||
plugin, ok := manager.DataSources[dsInfo.Type]
|
||||
if !ok {
|
||||
return nil, errors.New("unable to find datasource plugin CloudMonitoring")
|
||||
}
|
||||
@@ -527,7 +552,7 @@ func (e *CloudMonitoringExecutor) createRequest(ctx context.Context, dsInfo *mod
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (e *CloudMonitoringExecutor) getDefaultProject(ctx context.Context) (string, error) {
|
||||
func (e *Executor) getDefaultProject(ctx context.Context) (string, error) {
|
||||
authenticationType := e.dsInfo.JsonData.Get("authenticationType").MustString(jwtAuthentication)
|
||||
if authenticationType == gceAuthentication {
|
||||
defaultCredentials, err := google.FindDefaultCredentials(ctx, "https://www.googleapis.com/auth/monitoring.read")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,13 +12,14 @@ import (
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/tsdb"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"golang.org/x/net/context/ctxhttp"
|
||||
)
|
||||
|
||||
func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) run(ctx context.Context, tsdbQuery *tsdb.TsdbQuery, e *CloudMonitoringExecutor) (*tsdb.QueryResult, cloudMonitoringResponse, string, error) {
|
||||
queryResult := &tsdb.QueryResult{Meta: simplejson.New(), RefId: timeSeriesFilter.RefID}
|
||||
func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) run(ctx context.Context, tsdbQuery plugins.DataQuery,
|
||||
e *Executor) (plugins.DataQueryResult, cloudMonitoringResponse, string, error) {
|
||||
queryResult := plugins.DataQueryResult{Meta: simplejson.New(), RefID: timeSeriesFilter.RefID}
|
||||
projectName := timeSeriesFilter.ProjectName
|
||||
if projectName == "" {
|
||||
defaultProject, err := e.getDefaultProject(ctx)
|
||||
@@ -78,7 +79,8 @@ func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) run(ctx context.Context
|
||||
return queryResult, data, req.URL.RawQuery, nil
|
||||
}
|
||||
|
||||
func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) parseResponse(queryRes *tsdb.QueryResult, response cloudMonitoringResponse, executedQueryString string) error {
|
||||
func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) parseResponse(queryRes *plugins.DataQueryResult,
|
||||
response cloudMonitoringResponse, executedQueryString string) error {
|
||||
labels := make(map[string]map[string]bool)
|
||||
frames := data.Frames{}
|
||||
for _, series := range response.TimeSeries {
|
||||
@@ -199,7 +201,8 @@ func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) parseResponse(queryRes
|
||||
additionalLabels := data.Labels{"bucket": bucketBound}
|
||||
timeField := data.NewField(data.TimeSeriesTimeFieldName, nil, []time.Time{})
|
||||
valueField := data.NewField(data.TimeSeriesValueFieldName, nil, []float64{})
|
||||
frameName := formatLegendKeys(series.Metric.Type, defaultMetricName, seriesLabels, additionalLabels, timeSeriesFilter)
|
||||
frameName := formatLegendKeys(series.Metric.Type, defaultMetricName, seriesLabels,
|
||||
additionalLabels, timeSeriesFilter)
|
||||
valueField.Name = frameName
|
||||
valueField.Labels = seriesLabels
|
||||
setDisplayNameAsFieldName(valueField)
|
||||
@@ -224,7 +227,7 @@ func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) parseResponse(queryRes
|
||||
frames = addConfigData(frames, dl)
|
||||
}
|
||||
|
||||
queryRes.Dataframes = tsdb.NewDecodedDataFrames(frames)
|
||||
queryRes.Dataframes = plugins.NewDecodedDataFrames(frames)
|
||||
|
||||
labelsByKey := make(map[string][]string)
|
||||
for key, values := range labels {
|
||||
@@ -238,8 +241,9 @@ func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) parseResponse(queryRes
|
||||
return nil
|
||||
}
|
||||
|
||||
func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) handleNonDistributionSeries(series timeSeries, defaultMetricName string, seriesLabels map[string]string,
|
||||
queryRes *tsdb.QueryResult, frame *data.Frame) {
|
||||
func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) handleNonDistributionSeries(series timeSeries,
|
||||
defaultMetricName string, seriesLabels map[string]string, queryRes *plugins.DataQueryResult,
|
||||
frame *data.Frame) {
|
||||
for i := 0; i < len(series.Points); i++ {
|
||||
point := series.Points[i]
|
||||
value := point.Value.DoubleValue
|
||||
@@ -268,7 +272,8 @@ func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) handleNonDistributionSe
|
||||
setDisplayNameAsFieldName(dataField)
|
||||
}
|
||||
|
||||
func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) parseToAnnotations(queryRes *tsdb.QueryResult, response cloudMonitoringResponse, title string, text string, tags string) error {
|
||||
func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) parseToAnnotations(queryRes *plugins.DataQueryResult,
|
||||
response cloudMonitoringResponse, title string, text string, tags string) error {
|
||||
frames := data.Frames{}
|
||||
for _, series := range response.TimeSeries {
|
||||
if len(series.Points) == 0 {
|
||||
@@ -282,18 +287,20 @@ func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) parseToAnnotations(quer
|
||||
value = point.Value.StringValue
|
||||
}
|
||||
annotation["time"] = append(annotation["time"], point.Interval.EndTime.UTC().Format(time.RFC3339))
|
||||
annotation["title"] = append(annotation["title"], formatAnnotationText(title, value, series.Metric.Type, series.Metric.Labels, series.Resource.Labels))
|
||||
annotation["title"] = append(annotation["title"], formatAnnotationText(title, value, series.Metric.Type,
|
||||
series.Metric.Labels, series.Resource.Labels))
|
||||
annotation["tags"] = append(annotation["tags"], tags)
|
||||
annotation["text"] = append(annotation["text"], formatAnnotationText(text, value, series.Metric.Type, series.Metric.Labels, series.Resource.Labels))
|
||||
annotation["text"] = append(annotation["text"], formatAnnotationText(text, value, series.Metric.Type,
|
||||
series.Metric.Labels, series.Resource.Labels))
|
||||
}
|
||||
frames = append(frames, data.NewFrame(queryRes.RefId,
|
||||
frames = append(frames, data.NewFrame(queryRes.RefID,
|
||||
data.NewField("time", nil, annotation["time"]),
|
||||
data.NewField("title", nil, annotation["title"]),
|
||||
data.NewField("tags", nil, annotation["tags"]),
|
||||
data.NewField("text", nil, annotation["text"]),
|
||||
))
|
||||
}
|
||||
queryRes.Dataframes = tsdb.NewDecodedDataFrames(frames)
|
||||
queryRes.Dataframes = plugins.NewDecodedDataFrames(frames)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -313,7 +320,8 @@ func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) buildDeepLink() string
|
||||
|
||||
u, err := url.Parse("https://console.cloud.google.com/monitoring/metrics-explorer")
|
||||
if err != nil {
|
||||
slog.Error("Failed to generate deep link: unable to parse metrics explorer URL", "ProjectName", timeSeriesFilter.ProjectName, "query", timeSeriesFilter.RefID)
|
||||
slog.Error("Failed to generate deep link: unable to parse metrics explorer URL", "ProjectName",
|
||||
timeSeriesFilter.ProjectName, "query", timeSeriesFilter.RefID)
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -353,7 +361,8 @@ func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) buildDeepLink() string
|
||||
|
||||
blob, err := json.Marshal(pageState)
|
||||
if err != nil {
|
||||
slog.Error("Failed to generate deep link", "pageState", pageState, "ProjectName", timeSeriesFilter.ProjectName, "query", timeSeriesFilter.RefID)
|
||||
slog.Error("Failed to generate deep link", "pageState", pageState, "ProjectName", timeSeriesFilter.ProjectName,
|
||||
"query", timeSeriesFilter.RefID)
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -362,7 +371,8 @@ func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) buildDeepLink() string
|
||||
|
||||
accountChooserURL, err := url.Parse("https://accounts.google.com/AccountChooser")
|
||||
if err != nil {
|
||||
slog.Error("Failed to generate deep link: unable to parse account chooser URL", "ProjectName", timeSeriesFilter.ProjectName, "query", timeSeriesFilter.RefID)
|
||||
slog.Error("Failed to generate deep link: unable to parse account chooser URL", "ProjectName",
|
||||
timeSeriesFilter.ProjectName, "query", timeSeriesFilter.RefID)
|
||||
return ""
|
||||
}
|
||||
accountChooserQuery := accountChooserURL.Query()
|
||||
|
||||
@@ -13,13 +13,15 @@ import (
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/tsdb"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/tsdb/interval"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"golang.org/x/net/context/ctxhttp"
|
||||
)
|
||||
|
||||
func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) run(ctx context.Context, tsdbQuery *tsdb.TsdbQuery, e *CloudMonitoringExecutor) (*tsdb.QueryResult, cloudMonitoringResponse, string, error) {
|
||||
queryResult := &tsdb.QueryResult{Meta: simplejson.New(), RefId: timeSeriesQuery.RefID}
|
||||
func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) run(ctx context.Context, tsdbQuery plugins.DataQuery,
|
||||
e *Executor) (plugins.DataQueryResult, cloudMonitoringResponse, string, error) {
|
||||
queryResult := plugins.DataQueryResult{Meta: simplejson.New(), RefID: timeSeriesQuery.RefID}
|
||||
projectName := timeSeriesQuery.ProjectName
|
||||
if projectName == "" {
|
||||
defaultProject, err := e.getDefaultProject(ctx)
|
||||
@@ -41,8 +43,8 @@ func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) run(ctx context.Context, t
|
||||
queryResult.Error = err
|
||||
return queryResult, cloudMonitoringResponse{}, "", nil
|
||||
}
|
||||
intervalCalculator := tsdb.NewIntervalCalculator(&tsdb.IntervalOptions{})
|
||||
interval := intervalCalculator.Calculate(tsdbQuery.TimeRange, time.Duration(timeSeriesQuery.IntervalMS/1000)*time.Second)
|
||||
intervalCalculator := interval.NewCalculator(interval.CalculatorOptions{})
|
||||
interval := intervalCalculator.Calculate(*tsdbQuery.TimeRange, time.Duration(timeSeriesQuery.IntervalMS/1000)*time.Second)
|
||||
timeFormat := "2006/01/02-15:04:05"
|
||||
timeSeriesQuery.Query += fmt.Sprintf(" | graph_period %s | within d'%s', d'%s'", interval.Text, from.UTC().Format(timeFormat), to.UTC().Format(timeFormat))
|
||||
|
||||
@@ -92,7 +94,8 @@ func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) run(ctx context.Context, t
|
||||
return queryResult, data, timeSeriesQuery.Query, nil
|
||||
}
|
||||
|
||||
func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) parseResponse(queryRes *tsdb.QueryResult, response cloudMonitoringResponse, executedQueryString string) error {
|
||||
func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) parseResponse(queryRes *plugins.DataQueryResult,
|
||||
response cloudMonitoringResponse, executedQueryString string) error {
|
||||
labels := make(map[string]map[string]bool)
|
||||
frames := data.Frames{}
|
||||
for _, series := range response.TimeSeriesData {
|
||||
@@ -157,7 +160,10 @@ func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) parseResponse(queryRes *ts
|
||||
frame.SetRow(len(series.PointData)-1-i, series.PointData[i].TimeInterval.EndTime, value)
|
||||
}
|
||||
|
||||
metricName := formatLegendKeys(d.Key, defaultMetricName, seriesLabels, nil, &cloudMonitoringTimeSeriesFilter{ProjectName: timeSeriesQuery.ProjectName, AliasBy: timeSeriesQuery.AliasBy})
|
||||
metricName := formatLegendKeys(d.Key, defaultMetricName, seriesLabels, nil,
|
||||
&cloudMonitoringTimeSeriesFilter{
|
||||
ProjectName: timeSeriesQuery.ProjectName, AliasBy: timeSeriesQuery.AliasBy,
|
||||
})
|
||||
dataField := frame.Fields[1]
|
||||
dataField.Name = metricName
|
||||
dataField.Labels = seriesLabels
|
||||
@@ -244,7 +250,7 @@ func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) parseResponse(queryRes *ts
|
||||
frames = addConfigData(frames, dl)
|
||||
}
|
||||
|
||||
queryRes.Dataframes = tsdb.NewDecodedDataFrames(frames)
|
||||
queryRes.Dataframes = plugins.NewDecodedDataFrames(frames)
|
||||
|
||||
labelsByKey := make(map[string][]string)
|
||||
for key, values := range labels {
|
||||
@@ -258,7 +264,8 @@ func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) parseResponse(queryRes *ts
|
||||
return nil
|
||||
}
|
||||
|
||||
func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) parseToAnnotations(queryRes *tsdb.QueryResult, data cloudMonitoringResponse, title string, text string, tags string) error {
|
||||
func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) parseToAnnotations(queryRes *plugins.DataQueryResult,
|
||||
data cloudMonitoringResponse, title string, text string, tags string) error {
|
||||
annotations := make([]map[string]string, 0)
|
||||
|
||||
for _, series := range data.TimeSeriesData {
|
||||
|
||||
@@ -5,14 +5,15 @@ import (
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/tsdb"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
)
|
||||
|
||||
type (
|
||||
cloudMonitoringQueryExecutor interface {
|
||||
run(ctx context.Context, tsdbQuery *tsdb.TsdbQuery, e *CloudMonitoringExecutor) (*tsdb.QueryResult, cloudMonitoringResponse, string, error)
|
||||
parseResponse(queryRes *tsdb.QueryResult, data cloudMonitoringResponse, executedQueryString string) error
|
||||
parseToAnnotations(queryRes *tsdb.QueryResult, data cloudMonitoringResponse, title string, text string, tags string) error
|
||||
run(ctx context.Context, tsdbQuery plugins.DataQuery, e *Executor) (
|
||||
plugins.DataQueryResult, cloudMonitoringResponse, string, error)
|
||||
parseResponse(queryRes *plugins.DataQueryResult, data cloudMonitoringResponse, executedQueryString string) error
|
||||
parseToAnnotations(queryRes *plugins.DataQueryResult, data cloudMonitoringResponse, title string, text string, tags string) error
|
||||
buildDeepLink() string
|
||||
getRefID() string
|
||||
getUnit() string
|
||||
@@ -39,7 +40,7 @@ type (
|
||||
Query string
|
||||
IntervalMS int64
|
||||
AliasBy string
|
||||
timeRange *tsdb.TimeRange
|
||||
timeRange plugins.DataTimeRange
|
||||
Unit string
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user