CloudWatch: Fix handling region for legacy alerts (#109217)

This commit is contained in:
Isabella Siu 2025-08-06 15:40:20 -04:00 committed by GitHub
parent 295ace108d
commit 2bf9aea8ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 121 additions and 14 deletions

View File

@ -382,7 +382,7 @@ func (q *CloudWatchQuery) validateAndSetDefaults(refId string, metricsDataQuery
}
}
if q.Region == defaultRegion {
if q.Region == defaultRegion || q.Region == "" {
q.Region = defaultRegionValue
}

View File

@ -1195,7 +1195,7 @@ 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) {
t.Run("default region is used when when region is default", func(t *testing.T) {
query := []backend.DataQuery{
{
JSON: json.RawMessage(`{
@ -1216,6 +1216,33 @@ func Test_ParseMetricDataQueries_default_region(t *testing.T) {
},
}
region := "us-east-2"
res, err := ParseMetricDataQueries(query, time.Now().Add(-2*time.Hour), time.Now().Add(-time.Hour), region, logger, false)
assert.NoError(t, err)
require.Len(t, res, 1)
require.NotNil(t, res[0])
assert.Equal(t, region, res[0].Region)
})
t.Run("default region is used when when region not set", func(t *testing.T) {
query := []backend.DataQuery{
{
JSON: json.RawMessage(`{
"refId":"ref1",
"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, logger, false)
assert.NoError(t, err)

View File

@ -4,6 +4,7 @@ import { QueryEditorProps } from '@grafana/data';
import { CloudWatchDatasource } from '../../datasource';
import { isCloudWatchLogsQuery, isCloudWatchMetricsQuery } from '../../guards';
import useMigratedQuery from '../../migrations/useMigratedQuery';
import { CloudWatchJsonData, CloudWatchQuery } from '../../types';
import LogsQueryEditor from './LogsQueryEditor/LogsQueryEditor';
@ -14,6 +15,7 @@ export type Props = QueryEditorProps<CloudWatchDatasource, CloudWatchQuery, Clou
export const QueryEditor = (props: Props) => {
const { query, onChange, data } = props;
const migratedQuery = useMigratedQuery(query, props.onChange);
const [dataIsStale, setDataIsStale] = useState(false);
const [extraHeaderElementLeft, setExtraHeaderElementLeft] = useState<JSX.Element>();
const [extraHeaderElementRight, setExtraHeaderElementRight] = useState<JSX.Element>();
@ -39,20 +41,20 @@ export const QueryEditor = (props: Props) => {
dataIsStale={dataIsStale}
/>
{isCloudWatchMetricsQuery(query) && (
{isCloudWatchMetricsQuery(migratedQuery) && (
<MetricsQueryEditor
{...props}
query={query}
query={migratedQuery}
onRunQuery={() => {}}
onChange={onChangeInternal}
extraHeaderElementLeft={setExtraHeaderElementLeft}
extraHeaderElementRight={setExtraHeaderElementRight}
/>
)}
{isCloudWatchLogsQuery(query) && (
{isCloudWatchLogsQuery(migratedQuery) && (
<LogsQueryEditor
{...props}
query={query}
query={migratedQuery}
onChange={onChangeInternal}
extraHeaderElementLeft={setExtraHeaderElementLeft}
/>

View File

@ -1,6 +1,6 @@
import { CloudWatchMetricsQuery } from '../types';
import { CloudWatchMetricsQuery, MetricEditorMode, MetricQueryType } from '../types';
import { migrateAliasPatterns } from './metricQueryMigrations';
import { migrateAliasPatterns, migrateMetricQuery } from './metricQueryMigrations';
describe('metricQueryMigrations', () => {
interface TestScenario {
@ -73,4 +73,23 @@ describe('metricQueryMigrations', () => {
});
});
});
it('migrates type and mode', () => {
const baseQuery: CloudWatchMetricsQuery = {
statistic: 'Average',
refId: 'A',
id: '',
region: 'us-east-2',
namespace: 'AWS/EC2',
period: '300',
alias: '',
metricName: 'CPUUtilization',
dimensions: {},
matchExact: false,
expression: '',
};
const migratedQuery = migrateMetricQuery(baseQuery);
expect(migratedQuery.metricQueryType).toBe(MetricQueryType.Search);
expect(migratedQuery.metricEditorMode).toBe(MetricEditorMode.Builder);
});
});

View File

@ -2,10 +2,14 @@ import deepEqual from 'fast-deep-equal';
import { CloudWatchMetricsQuery } from '../types';
import { migrateCloudWatchQuery } from './dashboardMigrations';
// Call this function to migrate queries from within the plugin.
export function migrateMetricQuery(query: CloudWatchMetricsQuery): CloudWatchMetricsQuery {
const newQuery = { ...query };
migrateCloudWatchQuery(newQuery);
//add metric query migrations here
const migratedQuery = migrateAliasPatterns(query);
const migratedQuery = migrateAliasPatterns(newQuery);
return deepEqual(migratedQuery, query) ? query : migratedQuery;
}

View File

@ -11,15 +11,15 @@ const useMigratedMetricsQuery = (
query: CloudWatchMetricsQuery,
onChangeQuery: (newQuery: CloudWatchMetricsQuery) => void
) => {
const migratedQUery = useMemo(() => migrateMetricQuery(query), [query]);
const migratedQuery = useMemo(() => migrateMetricQuery(query), [query]);
useEffect(() => {
if (migratedQUery !== query) {
onChangeQuery(migratedQUery);
if (migratedQuery !== query) {
onChangeQuery(migratedQuery);
}
}, [migratedQUery, query, onChangeQuery]);
}, [migratedQuery, query, onChangeQuery]);
return migratedQUery;
return migratedQuery;
};
export default useMigratedMetricsQuery;

View File

@ -0,0 +1,22 @@
import { migrateQuery } from './useMigratedQuery';
describe('useMigratedQuery', () => {
it('adds region and queryMode', () => {
const legacyQuery = {
statistic: 'Average',
refId: 'A',
id: '',
region: '',
namespace: 'AWS/EC2',
period: '300',
alias: '',
metricName: 'CPUUtilization',
dimensions: {},
matchExact: false,
expression: '',
};
const migratedQuery = migrateQuery(legacyQuery);
expect(migratedQuery.region).toBe('default');
expect(migratedQuery.queryMode).toBe('Metrics');
});
});

View File

@ -0,0 +1,33 @@
import deepEqual from 'fast-deep-equal';
import { useEffect, useMemo } from 'react';
import { CloudWatchQuery } from '../types';
/**
* Returns queries with migrations, and calls onChange function to notify if it changes
*/
const useMigratedQuery = (query: CloudWatchQuery, onChangeQuery: (newQuery: CloudWatchQuery) => void) => {
const migratedQuery = useMemo(() => migrateQuery(query), [query]);
useEffect(() => {
if (migratedQuery !== query) {
onChangeQuery(migratedQuery);
}
}, [migratedQuery, query, onChangeQuery]);
return migratedQuery;
};
// The frontend doesn't run legacy queries if we don't set the queryMode and region
export function migrateQuery(query: CloudWatchQuery): CloudWatchQuery {
const newQuery = { ...query };
if (!newQuery.queryMode) {
newQuery.queryMode = 'Metrics';
}
if (!newQuery.region) {
newQuery.region = 'default';
}
return deepEqual(newQuery, query) ? query : newQuery;
}
export default useMigratedQuery;