[v9.2.x] Google Cloud Monitoring: Set frame interval to draw null values (#57914)

This commit is contained in:
Andres Martinez Gotor
2022-11-02 12:14:30 +01:00
committed by GitHub
parent 484b098a20
commit 1ac0c03a1d
6 changed files with 79 additions and 13 deletions
+10 -1
View File
@@ -663,7 +663,7 @@ func unmarshalResponse(res *http.Response) (cloudMonitoringResponse, error) {
return data, nil
}
func addConfigData(frames data.Frames, dl string, unit string) data.Frames {
func addConfigData(frames data.Frames, dl string, unit string, period string) data.Frames {
for i := range frames {
if frames[i].Fields[1].Config == nil {
frames[i].Fields[1].Config = &data.FieldConfig{}
@@ -681,6 +681,15 @@ func addConfigData(frames data.Frames, dl string, unit string) data.Frames {
frames[i].Fields[1].Config.Unit = val
}
}
if frames[i].Fields[0].Config == nil {
frames[i].Fields[0].Config = &data.FieldConfig{}
}
if period != "" {
err := addInterval(period, frames[i].Fields[0])
if err != nil {
slog.Error("Failed to add interval", "error", err)
}
}
}
return frames
}
@@ -211,7 +211,7 @@ func (timeSeriesFilter *cloudMonitoringTimeSeriesFilter) parseResponse(queryRes
}
if len(response.TimeSeries) > 0 {
dl := timeSeriesFilter.buildDeepLink()
frames = addConfigData(frames, dl, response.Unit)
frames = addConfigData(frames, dl, response.Unit, timeSeriesFilter.Params.Get("aggregation.alignmentPeriod"))
}
queryRes.Frames = frames
@@ -389,6 +389,21 @@ func TestTimeSeriesFilter(t *testing.T) {
assert.Equal(t, "114250375703598695", labels["resource.label.instance_id"])
})
t.Run("includes time interval", func(t *testing.T) {
data, err := loadTestFile("./test-data/5-series-response-meta-data.json")
require.NoError(t, err)
assert.Equal(t, 3, len(data.TimeSeries))
res := &backend.DataResponse{}
query := &cloudMonitoringTimeSeriesFilter{Params: url.Values{
"aggregation.alignmentPeriod": []string{"+60s"},
}}
err = query.parseResponse(res, data, "")
require.NoError(t, err)
frames := res.Frames
timeField := frames[0].Fields[0]
assert.Equal(t, float64(60*1000), timeField.Config.Interval)
})
t.Run("parseResponse successfully parses metadata for distribution valueType", func(t *testing.T) {
t.Run("exponential bounds", func(t *testing.T) {
data, err := loadTestFile("./test-data/3-series-response-distribution-exponential.json")
+10 -11
View File
@@ -20,22 +20,21 @@ import (
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
)
func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) appendGraphPeriod(req *backend.QueryDataRequest) string {
func (timeSeriesQuery *cloudMonitoringTimeSeriesQuery) appendGraphPeriod(req *backend.QueryDataRequest) string {
// GraphPeriod needs to be explicitly disabled.
// If not set, the default behavior is to set an automatic value
if timeSeriesQuery.GraphPeriod != "disabled" {
graphPeriod := timeSeriesQuery.GraphPeriod
if graphPeriod == "auto" || graphPeriod == "" {
if timeSeriesQuery.GraphPeriod == "auto" || timeSeriesQuery.GraphPeriod == "" {
intervalCalculator := intervalv2.NewCalculator(intervalv2.CalculatorOptions{})
interval := intervalCalculator.Calculate(req.Queries[0].TimeRange, time.Duration(timeSeriesQuery.IntervalMS/1000)*time.Second, req.Queries[0].MaxDataPoints)
graphPeriod = interval.Text
timeSeriesQuery.GraphPeriod = interval.Text
}
return fmt.Sprintf(" | graph_period %s", graphPeriod)
return fmt.Sprintf(" | graph_period %s", timeSeriesQuery.GraphPeriod)
}
return ""
}
func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) run(ctx context.Context, req *backend.QueryDataRequest,
func (timeSeriesQuery *cloudMonitoringTimeSeriesQuery) run(ctx context.Context, req *backend.QueryDataRequest,
s *Service, dsInfo datasourceInfo, tracer tracing.Tracer) (*backend.DataResponse, cloudMonitoringResponse, string, error) {
dr := &backend.DataResponse{}
projectName := timeSeriesQuery.ProjectName
@@ -93,7 +92,7 @@ func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) run(ctx context.Context, r
return dr, d, timeSeriesQuery.Query, nil
}
func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) parseResponse(queryRes *backend.DataResponse,
func (timeSeriesQuery *cloudMonitoringTimeSeriesQuery) parseResponse(queryRes *backend.DataResponse,
response cloudMonitoringResponse, executedQueryString string) error {
frames := data.Frames{}
@@ -256,7 +255,7 @@ func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) parseResponse(queryRes *ba
}
if len(response.TimeSeriesData) > 0 {
dl := timeSeriesQuery.buildDeepLink()
frames = addConfigData(frames, dl, response.Unit)
frames = addConfigData(frames, dl, response.Unit, timeSeriesQuery.GraphPeriod)
}
queryRes.Frames = frames
@@ -264,7 +263,7 @@ func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) parseResponse(queryRes *ba
return nil
}
func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) parseToAnnotations(queryRes *backend.DataResponse,
func (timeSeriesQuery *cloudMonitoringTimeSeriesQuery) parseToAnnotations(queryRes *backend.DataResponse,
data cloudMonitoringResponse, title, text string) error {
annotations := make([]*annotationEvent, 0)
@@ -316,7 +315,7 @@ func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) parseToAnnotations(queryRe
return nil
}
func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) buildDeepLink() string {
func (timeSeriesQuery *cloudMonitoringTimeSeriesQuery) 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", timeSeriesQuery.ProjectName, "query", timeSeriesQuery.RefID)
@@ -371,6 +370,6 @@ func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) buildDeepLink() string {
return accountChooserURL.String()
}
func (timeSeriesQuery cloudMonitoringTimeSeriesQuery) getRefID() string {
func (timeSeriesQuery *cloudMonitoringTimeSeriesQuery) getRefID() string {
return timeSeriesQuery.RefID
}
@@ -102,6 +102,28 @@ func TestTimeSeriesQuery(t *testing.T) {
assert.Equal(t, "6724404429462225363", labels["resource.label.instance_id"])
})
t.Run("includes time interval", func(t *testing.T) {
data, err := loadTestFile("./test-data/7-series-response-mql.json")
require.NoError(t, err)
fromStart := time.Date(2018, 3, 15, 13, 0, 0, 0, time.UTC).In(time.Local)
res := &backend.DataResponse{}
query := &cloudMonitoringTimeSeriesQuery{
ProjectName: "test-proj",
Query: "test-query",
timeRange: backend.TimeRange{
From: fromStart,
To: fromStart.Add(34 * time.Minute),
},
GraphPeriod: "60s",
}
err = query.parseResponse(res, data, "")
require.NoError(t, err)
frames := res.Frames
timeField := frames[0].Fields[0]
assert.Equal(t, float64(60*1000), timeField.Config.Interval)
})
t.Run("appends graph_period to the query", func(t *testing.T) {
query := &cloudMonitoringTimeSeriesQuery{}
assert.Equal(t, query.appendGraphPeriod(&backend.QueryDataRequest{Queries: []backend.DataQuery{{}}}), " | graph_period 1ms")
+21
View File
@@ -2,6 +2,9 @@ package cloudmonitoring
import (
"strings"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
)
func reverse(s string) string {
@@ -24,3 +27,21 @@ func containsLabel(labels []string, newLabel string) bool {
}
return false
}
func addInterval(period string, field *data.Field) error {
period = strings.TrimPrefix(period, "+")
p, err := intervalv2.ParseIntervalStringToTimeDuration(period)
if err != nil {
return err
}
if err == nil {
if field.Config != nil {
field.Config.Interval = float64(p.Milliseconds())
} else {
field.SetConfig(&data.FieldConfig{
Interval: float64(p.Milliseconds()),
})
}
}
return nil
}