From 7646b7851e6ebc84614c7ffcfff5f0e990fdeca1 Mon Sep 17 00:00:00 2001 From: Isabella Siu Date: Tue, 13 Dec 2022 15:24:28 -0500 Subject: [PATCH] Cloudwatch: Fix deeplink with default region (#60260) Cloudwatch: fix deeplink with default region (cherry picked from commit d6bb2a74931a9c2b393fa22040737fb6480819f3) --- .../cloudwatch/models/cloudwatch_query.go | 12 +- .../models/cloudwatch_query_test.go | 107 +++++++++++------- pkg/tsdb/cloudwatch/time_series_query.go | 7 +- 3 files changed, 84 insertions(+), 42 deletions(-) diff --git a/pkg/tsdb/cloudwatch/models/cloudwatch_query.go b/pkg/tsdb/cloudwatch/models/cloudwatch_query.go index a6cf683152e3..22de49c5fc43 100644 --- a/pkg/tsdb/cloudwatch/models/cloudwatch_query.go +++ b/pkg/tsdb/cloudwatch/models/cloudwatch_query.go @@ -41,6 +41,8 @@ const ( GMDApiModeSQLExpression ) +const defaultRegion = "default" + type CloudWatchQuery struct { RefId string Region string @@ -227,7 +229,7 @@ type metricsDataQuery struct { // ParseMetricDataQueries decodes the metric data queries json, validates, sets default values and returns an array of CloudWatchQueries. // The CloudWatchQuery has a 1 to 1 mapping to a query editor row -func ParseMetricDataQueries(dataQueries []backend.DataQuery, startTime time.Time, endTime time.Time, dynamicLabelsEnabled, +func ParseMetricDataQueries(dataQueries []backend.DataQuery, startTime time.Time, endTime time.Time, defaultRegion string, dynamicLabelsEnabled, crossAccountQueryingEnabled bool) ([]*CloudWatchQuery, error) { var metricDataQueries = make(map[string]metricsDataQuery) for _, query := range dataQueries { @@ -260,7 +262,7 @@ func ParseMetricDataQueries(dataQueries []backend.DataQuery, startTime time.Time Expression: mdq.Expression, } - if err := cwQuery.validateAndSetDefaults(refId, mdq, startTime, endTime, crossAccountQueryingEnabled); err != nil { + if err := cwQuery.validateAndSetDefaults(refId, mdq, startTime, endTime, defaultRegion, crossAccountQueryingEnabled); err != nil { return nil, &QueryError{Err: err, RefID: refId} } @@ -278,7 +280,7 @@ func (q *CloudWatchQuery) migrateLegacyQuery(query metricsDataQuery, dynamicLabe } func (q *CloudWatchQuery) validateAndSetDefaults(refId string, metricsDataQuery metricsDataQuery, startTime, endTime time.Time, - crossAccountQueryingEnabled bool) error { + defaultRegionValue string, crossAccountQueryingEnabled bool) error { if metricsDataQuery.Statistic == nil && metricsDataQuery.Statistics == nil { return fmt.Errorf("query must have either statistic or statistics field") } @@ -337,6 +339,10 @@ func (q *CloudWatchQuery) validateAndSetDefaults(refId string, metricsDataQuery } } + if q.Region == defaultRegion { + q.Region = defaultRegionValue + } + return nil } diff --git a/pkg/tsdb/cloudwatch/models/cloudwatch_query_test.go b/pkg/tsdb/cloudwatch/models/cloudwatch_query_test.go index 89459f66bea6..11c3f70a747a 100644 --- a/pkg/tsdb/cloudwatch/models/cloudwatch_query_test.go +++ b/pkg/tsdb/cloudwatch/models/cloudwatch_query_test.go @@ -316,7 +316,7 @@ func TestRequestParser(t *testing.T) { }, } - migratedQueries, err := ParseMetricDataQueries(oldQuery, time.Now(), time.Now(), false, false) + migratedQueries, err := ParseMetricDataQueries(oldQuery, time.Now(), time.Now(), "us-east-2", false, false) assert.NoError(t, err) require.Len(t, migratedQueries, 1) require.NotNil(t, migratedQueries[0]) @@ -347,7 +347,7 @@ func TestRequestParser(t *testing.T) { }, } - results, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), false, false) + results, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), "us-east-2", false, false) require.NoError(t, err) require.Len(t, results, 1) res := results[0] @@ -390,7 +390,7 @@ func TestRequestParser(t *testing.T) { }, } - results, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), false, false) + results, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), "us-east-2", false, false) assert.NoError(t, err) require.Len(t, results, 1) res := results[0] @@ -423,7 +423,7 @@ func TestRequestParser(t *testing.T) { }, } - _, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), false, false) + _, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), "us-east-2", false, false) require.Error(t, err) assert.Equal(t, `error parsing query "", failed to parse dimensions: unknown type as dimension value`, err.Error()) @@ -452,7 +452,7 @@ func Test_ParseMetricDataQueries_periods(t *testing.T) { }, } - res, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), false, false) + res, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), "us-east-2", false, false) assert.NoError(t, err) require.Len(t, res, 1) require.NotNil(t, res[0]) @@ -484,7 +484,7 @@ func Test_ParseMetricDataQueries_periods(t *testing.T) { to := time.Now() from := to.Local().Add(time.Minute * time.Duration(5)) - res, err := ParseMetricDataQueries(query, from, to, false, false) + res, err := ParseMetricDataQueries(query, from, to, "us-east-2", false, false) require.NoError(t, err) require.Len(t, res, 1) assert.Equal(t, 60, res[0].Period) @@ -494,7 +494,7 @@ func Test_ParseMetricDataQueries_periods(t *testing.T) { to := time.Now() from := to.AddDate(0, 0, -1) - res, err := ParseMetricDataQueries(query, from, to, false, false) + res, err := ParseMetricDataQueries(query, from, to, "us-east-2", false, false) require.NoError(t, err) require.Len(t, res, 1) assert.Equal(t, 60, res[0].Period) @@ -503,7 +503,7 @@ func Test_ParseMetricDataQueries_periods(t *testing.T) { t.Run("Time range is 2 days", func(t *testing.T) { to := time.Now() from := to.AddDate(0, 0, -2) - res, err := ParseMetricDataQueries(query, from, to, false, false) + res, err := ParseMetricDataQueries(query, from, to, "us-east-2", false, false) require.NoError(t, err) require.Len(t, res, 1) assert.Equal(t, 300, res[0].Period) @@ -513,7 +513,7 @@ func Test_ParseMetricDataQueries_periods(t *testing.T) { to := time.Now() from := to.AddDate(0, 0, -7) - res, err := ParseMetricDataQueries(query, from, to, false, false) + res, err := ParseMetricDataQueries(query, from, to, "us-east-2", false, false) require.NoError(t, err) require.Len(t, res, 1) assert.Equal(t, 900, res[0].Period) @@ -523,7 +523,7 @@ func Test_ParseMetricDataQueries_periods(t *testing.T) { to := time.Now() from := to.AddDate(0, 0, -30) - res, err := ParseMetricDataQueries(query, from, to, false, false) + res, err := ParseMetricDataQueries(query, from, to, "us-east-2", false, false) require.NoError(t, err) require.Len(t, res, 1) assert.Equal(t, 3600, res[0].Period) @@ -533,7 +533,7 @@ func Test_ParseMetricDataQueries_periods(t *testing.T) { to := time.Now() from := to.AddDate(0, 0, -90) - res, err := ParseMetricDataQueries(query, from, to, false, false) + res, err := ParseMetricDataQueries(query, from, to, "us-east-2", false, false) require.NoError(t, err) require.Len(t, res, 1) assert.Equal(t, 21600, res[0].Period) @@ -543,7 +543,7 @@ func Test_ParseMetricDataQueries_periods(t *testing.T) { to := time.Now() from := to.AddDate(-1, 0, 0) - res, err := ParseMetricDataQueries(query, from, to, false, false) + res, err := ParseMetricDataQueries(query, from, to, "us-east-2", false, false) require.Nil(t, err) require.Len(t, res, 1) assert.Equal(t, 21600, res[0].Period) @@ -553,7 +553,7 @@ func Test_ParseMetricDataQueries_periods(t *testing.T) { to := time.Now() from := to.AddDate(-2, 0, 0) - res, err := ParseMetricDataQueries(query, from, to, false, false) + res, err := ParseMetricDataQueries(query, from, to, "us-east-2", false, false) require.NoError(t, err) require.Len(t, res, 1) assert.Equal(t, 86400, res[0].Period) @@ -562,7 +562,7 @@ func Test_ParseMetricDataQueries_periods(t *testing.T) { t.Run("Time range is 2 days, but 16 days ago", func(t *testing.T) { to := time.Now().AddDate(0, 0, -14) from := to.AddDate(0, 0, -2) - res, err := ParseMetricDataQueries(query, from, to, false, false) + res, err := ParseMetricDataQueries(query, from, to, "us-east-2", false, false) require.NoError(t, err) require.Len(t, res, 1) assert.Equal(t, 300, res[0].Period) @@ -571,7 +571,7 @@ func Test_ParseMetricDataQueries_periods(t *testing.T) { t.Run("Time range is 2 days, but 90 days ago", func(t *testing.T) { to := time.Now().AddDate(0, 0, -88) from := to.AddDate(0, 0, -2) - res, err := ParseMetricDataQueries(query, from, to, false, false) + res, err := ParseMetricDataQueries(query, from, to, "us-east-2", false, false) require.NoError(t, err) require.Len(t, res, 1) assert.Equal(t, 3600, res[0].Period) @@ -580,7 +580,7 @@ func Test_ParseMetricDataQueries_periods(t *testing.T) { t.Run("Time range is 2 days, but 456 days ago", func(t *testing.T) { to := time.Now().AddDate(0, 0, -454) from := to.AddDate(0, 0, -2) - res, err := ParseMetricDataQueries(query, from, to, false, false) + res, err := ParseMetricDataQueries(query, from, to, "us-east-2", false, false) require.NoError(t, err) require.Len(t, res, 1) assert.Equal(t, 21600, res[0].Period) @@ -595,7 +595,7 @@ func Test_ParseMetricDataQueries_periods(t *testing.T) { }`), }, } - _, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), false, false) + _, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), "us-east-2", false, false) require.Error(t, err) assert.Equal(t, `error parsing query "", failed to parse period as duration: time: invalid duration "invalid"`, err.Error()) }) @@ -610,7 +610,7 @@ func Test_ParseMetricDataQueries_periods(t *testing.T) { }, } - res, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), false, false) + res, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), "us-east-2", false, false) assert.NoError(t, err) require.Len(t, res, 1) @@ -697,7 +697,7 @@ func Test_ParseMetricDataQueries_query_type_and_metric_editor_mode_and_GMD_query ), }, } - res, err := ParseMetricDataQueries(query, time.Now(), time.Now(), false, false) + res, err := ParseMetricDataQueries(query, time.Now(), time.Now(), "us-east-2", false, false) require.NoError(t, err) require.Len(t, res, 1) require.NotNil(t, res[0]) @@ -723,7 +723,7 @@ func Test_ParseMetricDataQueries_hide_and_ReturnData(t *testing.T) { }`), }, } - res, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), false, false) + res, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), "us-east-2", false, false) require.NoError(t, err) require.Len(t, res, 1) require.NotNil(t, res[0]) @@ -744,7 +744,7 @@ func Test_ParseMetricDataQueries_hide_and_ReturnData(t *testing.T) { }`), }, } - res, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), false, false) + res, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), "us-east-2", false, false) require.NoError(t, err) require.Len(t, res, 1) require.NotNil(t, res[0]) @@ -765,7 +765,7 @@ func Test_ParseMetricDataQueries_hide_and_ReturnData(t *testing.T) { }`), }, } - res, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), false, false) + res, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), "us-east-2", false, false) require.NoError(t, err) require.Len(t, res, 1) require.NotNil(t, res[0]) @@ -784,7 +784,7 @@ func Test_ParseMetricDataQueries_hide_and_ReturnData(t *testing.T) { }`), }, } - res, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), false, false) + res, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), "us-east-2", false, false) require.NoError(t, err) require.Len(t, res, 1) require.NotNil(t, res[0]) @@ -805,7 +805,7 @@ func Test_ParseMetricDataQueries_hide_and_ReturnData(t *testing.T) { }`), }, } - res, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), false, false) + res, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), "us-east-2", false, false) require.NoError(t, err) require.Len(t, res, 1) require.NotNil(t, res[0]) @@ -826,7 +826,7 @@ func Test_ParseMetricDataQueries_hide_and_ReturnData(t *testing.T) { }`), }, } - res, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), false, false) + res, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), "us-east-2", false, false) require.NoError(t, err) require.Len(t, res, 1) require.NotNil(t, res[0]) @@ -849,7 +849,7 @@ func Test_ParseMetricDataQueries_ID(t *testing.T) { }`), }, } - res, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), false, false) + res, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), "us-east-2", false, false) require.NoError(t, err) require.Len(t, res, 1) require.NotNil(t, res[0]) @@ -870,7 +870,7 @@ func Test_ParseMetricDataQueries_ID(t *testing.T) { }`), }, } - res, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), false, false) + res, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), "us-east-2", false, false) require.NoError(t, err) require.Len(t, res, 1) require.NotNil(t, res[0]) @@ -897,7 +897,7 @@ func Test_ParseMetricDataQueries_sets_label_when_label_is_present_in_json_query( }, } - res, err := ParseMetricDataQueries(query, time.Now(), time.Now(), true, false) + res, err := ParseMetricDataQueries(query, time.Now(), time.Now(), "us-east-2", true, false) assert.NoError(t, err) require.Len(t, res, 1) require.NotNil(t, res[0]) @@ -961,7 +961,7 @@ func Test_ParseMetricDataQueries_migrate_alias_to_label(t *testing.T) { }, } - res, err := ParseMetricDataQueries(query, time.Now(), time.Now(), true, false) + res, err := ParseMetricDataQueries(query, time.Now(), time.Now(), "us-east-2", true, false) assert.NoError(t, err) require.Len(t, res, 1) @@ -1008,7 +1008,7 @@ func Test_ParseMetricDataQueries_migrate_alias_to_label(t *testing.T) { }, } - res, err := ParseMetricDataQueries(query, time.Now(), time.Now(), true, false) + res, err := ParseMetricDataQueries(query, time.Now(), time.Now(), "us-east-2", true, false) assert.NoError(t, err) require.Len(t, res, 2) @@ -1078,7 +1078,7 @@ func Test_ParseMetricDataQueries_migrate_alias_to_label(t *testing.T) { }`, tc.labelJson)), }, } - res, err := ParseMetricDataQueries(query, time.Now(), time.Now(), tc.dynamicLabelsFeatureToggleEnabled, false) + res, err := ParseMetricDataQueries(query, time.Now(), time.Now(), "us-east-2", tc.dynamicLabelsFeatureToggleEnabled, false) assert.NoError(t, err) require.Len(t, res, 1) @@ -1105,7 +1105,7 @@ func Test_ParseMetricDataQueries_statistics_and_query_type_validation_and_MatchE { JSON: []byte("{}"), }, - }, time.Now(), time.Now(), false, false) + }, time.Now(), time.Now(), "us-east-2", false, false) assert.Error(t, err) assert.Equal(t, `error parsing query "", query must have either statistic or statistics field`, err.Error()) @@ -1118,7 +1118,7 @@ func Test_ParseMetricDataQueries_statistics_and_query_type_validation_and_MatchE { JSON: []byte(`{"type":"some other type", "statistic":"Average", "matchExact":false}`), }, - }, time.Now(), time.Now(), false, false) + }, time.Now(), time.Now(), "us-east-2", false, false) assert.NoError(t, err) assert.Empty(t, actual) @@ -1130,7 +1130,7 @@ func Test_ParseMetricDataQueries_statistics_and_query_type_validation_and_MatchE { JSON: []byte(`{"statistic":"Average"}`), }, - }, time.Now(), time.Now(), false, false) + }, time.Now(), time.Now(), "us-east-2", false, false) assert.NoError(t, err) assert.NotEmpty(t, actual) @@ -1142,7 +1142,7 @@ func Test_ParseMetricDataQueries_statistics_and_query_type_validation_and_MatchE { JSON: []byte(`{"statistic":"Average"}`), }, - }, time.Now(), time.Now(), false, false) + }, time.Now(), time.Now(), "us-east-2", false, false) assert.NoError(t, err) assert.Len(t, actual, 1) @@ -1156,7 +1156,7 @@ func Test_ParseMetricDataQueries_statistics_and_query_type_validation_and_MatchE { JSON: []byte(`{"statistic":"Average","matchExact":false}`), }, - }, time.Now(), time.Now(), false, false) + }, time.Now(), time.Now(), "us-east-2", false, false) assert.NoError(t, err) assert.Len(t, actual, 1) @@ -1172,7 +1172,7 @@ func Test_ParseMetricDataQueries_account_Id(t *testing.T) { { JSON: []byte(`{"accountId":"some account id", "statistic":"Average"}`), }, - }, time.Now(), time.Now(), false, true) + }, time.Now(), time.Now(), "us-east-2", false, true) assert.NoError(t, err) require.Len(t, actual, 1) @@ -1187,7 +1187,7 @@ func Test_ParseMetricDataQueries_account_Id(t *testing.T) { { JSON: []byte(`{"accountId":"some account id", "statistic":"Average"}`), }, - }, time.Now(), time.Now(), false, false) + }, time.Now(), time.Now(), "us-east-2", false, false) assert.NoError(t, err) require.Len(t, actual, 1) @@ -1196,4 +1196,35 @@ func Test_ParseMetricDataQueries_account_Id(t *testing.T) { }) } +func Test_ParseMetricDataQueries_default_region(t *testing.T) { + t.Run("default region is used when when region not set", func(t *testing.T) { + query := []backend.DataQuery{ + { + JSON: json.RawMessage(`{ + "refId":"ref1", + "region":"default", + "namespace":"ec2", + "metricName":"CPUUtilization", + "id": "", + "expression": "", + "dimensions":{ + "InstanceId":["test"], + "InstanceType":["test2"] + }, + "statistic":"Average", + "period":"900", + "hide":false + }`), + }, + } + + region := "us-east-2" + res, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), region, false, false) + assert.NoError(t, err) + require.Len(t, res, 1) + require.NotNil(t, res[0]) + assert.Equal(t, region, res[0].Region) + }) +} + func pointer[T any](arg T) *T { return &arg } diff --git a/pkg/tsdb/cloudwatch/time_series_query.go b/pkg/tsdb/cloudwatch/time_series_query.go index fbec70a42b16..7b17a5071d69 100644 --- a/pkg/tsdb/cloudwatch/time_series_query.go +++ b/pkg/tsdb/cloudwatch/time_series_query.go @@ -31,7 +31,12 @@ func (e *cloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, logger return nil, fmt.Errorf("invalid time range: start time must be before end time") } - requestQueries, err := models.ParseMetricDataQueries(req.Queries, startTime, endTime, + instance, err := e.getInstance(req.PluginContext) + if err != nil { + return nil, err + } + + requestQueries, err := models.ParseMetricDataQueries(req.Queries, startTime, endTime, instance.Settings.Region, e.features.IsEnabled(featuremgmt.FlagCloudWatchDynamicLabels), e.features.IsEnabled(featuremgmt.FlagCloudWatchCrossAccountQuerying)) if err != nil {