mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
CloudWatch: remove core imports from CloudWatchMetricsQueryRunner (#80926)
* CloudWatch: remove core imports from CloudWatchMetricsQueryRunner * use getAppEvents to publish error * use default wait time * put test back in original position * fix throttling error message link
This commit is contained in:
@@ -20,7 +20,7 @@ export const ThrottlingErrorMessage = ({ region }: Props) => (
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-link"
|
||||
href="https://grafana.com/docs/grafana/latest/datasources/cloudwatch/#service-quotas"
|
||||
href="https://grafana.com/docs/grafana/latest/datasources/cloudwatch/#manage-service-quotas"
|
||||
>
|
||||
documentation
|
||||
</a>
|
||||
|
||||
@@ -3,7 +3,6 @@ import { of } from 'rxjs';
|
||||
import { CustomVariableModel, getFrameDisplayName, VariableHide } from '@grafana/data';
|
||||
import { dateTime } from '@grafana/data/src/datetime/moment_wrapper';
|
||||
import { toDataQueryResponse } from '@grafana/runtime';
|
||||
import * as redux from 'app/store/store';
|
||||
|
||||
import {
|
||||
namespaceVariable,
|
||||
@@ -19,6 +18,13 @@ import { setupMockedMetricsQueryRunner } from '../__mocks__/MetricsQueryRunner';
|
||||
import { validMetricSearchBuilderQuery, validMetricSearchCodeQuery } from '../__mocks__/queries';
|
||||
import { MetricQueryType, MetricEditorMode, CloudWatchMetricsQuery } from '../types';
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
getAppEvents: () => ({
|
||||
publish: jest.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('CloudWatchMetricsQueryRunner', () => {
|
||||
describe('performTimeSeriesQuery', () => {
|
||||
it('should return the same length of data as result', async () => {
|
||||
@@ -92,6 +98,68 @@ describe('CloudWatchMetricsQueryRunner', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should enrich the error message for throttling errors', async () => {
|
||||
const partialQuery: CloudWatchMetricsQuery = {
|
||||
metricQueryType: MetricQueryType.Search,
|
||||
metricEditorMode: MetricEditorMode.Builder,
|
||||
queryMode: 'Metrics',
|
||||
namespace: 'AWS/EC2',
|
||||
metricName: 'CPUUtilization',
|
||||
dimensions: {
|
||||
InstanceId: 'i-12345678',
|
||||
},
|
||||
statistic: 'Average',
|
||||
period: '300',
|
||||
expression: '',
|
||||
id: '',
|
||||
region: '',
|
||||
refId: '',
|
||||
};
|
||||
|
||||
const queries: CloudWatchMetricsQuery[] = [
|
||||
{ ...partialQuery, refId: 'A', region: 'us-east-1' },
|
||||
{ ...partialQuery, refId: 'B', region: 'us-east-2' },
|
||||
];
|
||||
|
||||
const dataWithThrottlingError = {
|
||||
data: {
|
||||
message: 'Throttling: exception',
|
||||
results: {
|
||||
A: {
|
||||
frames: [],
|
||||
series: [],
|
||||
tables: [],
|
||||
error: 'Throttling: exception',
|
||||
refId: 'A',
|
||||
meta: {},
|
||||
},
|
||||
B: {
|
||||
frames: [],
|
||||
series: [],
|
||||
tables: [],
|
||||
error: 'Throttling: exception',
|
||||
refId: 'B',
|
||||
meta: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const expectedUsEast1Message =
|
||||
'Please visit the AWS Service Quotas console at https://us-east-1.console.aws.amazon.com/servicequotas/home?region=us-east-1#!/services/monitoring/quotas/L-5E141212 to request a quota increase or see our documentation at https://grafana.com/docs/grafana/latest/datasources/cloudwatch/#manage-service-quotas to learn more. Throttling: exception';
|
||||
const expectedUsEast2Message =
|
||||
'Please visit the AWS Service Quotas console at https://us-east-2.console.aws.amazon.com/servicequotas/home?region=us-east-2#!/services/monitoring/quotas/L-5E141212 to request a quota increase or see our documentation at https://grafana.com/docs/grafana/latest/datasources/cloudwatch/#manage-service-quotas to learn more. Throttling: exception';
|
||||
|
||||
const { runner, request, queryMock } = setupMockedMetricsQueryRunner({
|
||||
response: toDataQueryResponse(dataWithThrottlingError),
|
||||
});
|
||||
|
||||
await expect(runner.handleMetricQueries(queries, request, queryMock)).toEmitValuesWith((received) => {
|
||||
expect(received[0].errors).toHaveLength(2);
|
||||
expect(received[0]?.errors?.[0].message).toEqual(expectedUsEast1Message);
|
||||
expect(received[0]?.errors?.[1].message).toEqual(expectedUsEast2Message);
|
||||
});
|
||||
});
|
||||
|
||||
describe('When performing CloudWatch metrics query', () => {
|
||||
const queries: CloudWatchMetricsQuery[] = [
|
||||
{
|
||||
@@ -275,13 +343,6 @@ describe('CloudWatchMetricsQueryRunner', () => {
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
redux.setStore({
|
||||
...redux.store,
|
||||
dispatch: jest.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
it('should display one alert error message per region+datasource combination', async () => {
|
||||
const { runner, request, queryMock } = setupMockedMetricsQueryRunner({
|
||||
response: toDataQueryResponse(dataWithThrottlingError),
|
||||
|
||||
@@ -3,6 +3,7 @@ import React from 'react';
|
||||
import { catchError, map, Observable, of } from 'rxjs';
|
||||
|
||||
import {
|
||||
AppEvents,
|
||||
DataFrame,
|
||||
DataQueryError,
|
||||
DataQueryRequest,
|
||||
@@ -13,11 +14,7 @@ import {
|
||||
rangeUtil,
|
||||
ScopedVars,
|
||||
} from '@grafana/data';
|
||||
import { TemplateSrv } from '@grafana/runtime';
|
||||
import { notifyApp } from 'app/core/actions';
|
||||
import { createErrorNotification } from 'app/core/copy/appNotification';
|
||||
import { store } from 'app/store/store';
|
||||
import { AppNotificationTimeout } from 'app/types';
|
||||
import { TemplateSrv, getAppEvents } from '@grafana/runtime';
|
||||
|
||||
import { ThrottlingErrorMessage } from '../components/Errors/ThrottlingErrorMessage';
|
||||
import memoizedDebounce from '../memoizedDebounce';
|
||||
@@ -27,24 +24,23 @@ import { filterMetricsQuery } from '../utils/utils';
|
||||
|
||||
import { CloudWatchRequest } from './CloudWatchRequest';
|
||||
|
||||
const getThrottlingErrorMessage = (region: string, message: string) =>
|
||||
`Please visit the AWS Service Quotas console at https://${region}.console.aws.amazon.com/servicequotas/home?region=${region}#!/services/monitoring/quotas/L-5E141212 to request a quota increase or see our documentation at https://grafana.com/docs/grafana/latest/datasources/cloudwatch/#manage-service-quotas to learn more. ${message}`;
|
||||
|
||||
const displayAlert = (datasourceName: string, region: string) =>
|
||||
store.dispatch(
|
||||
notifyApp(
|
||||
createErrorNotification(
|
||||
`CloudWatch request limit reached in ${region} for data source ${datasourceName}`,
|
||||
'',
|
||||
undefined,
|
||||
React.createElement(ThrottlingErrorMessage, { region }, null)
|
||||
)
|
||||
)
|
||||
);
|
||||
getAppEvents().publish({
|
||||
type: AppEvents.alertError.name,
|
||||
payload: [
|
||||
`CloudWatch request limit reached in ${region} for data source ${datasourceName}`,
|
||||
'',
|
||||
undefined,
|
||||
React.createElement(ThrottlingErrorMessage, { region }, null),
|
||||
],
|
||||
});
|
||||
|
||||
// This class handles execution of CloudWatch metrics query data queries
|
||||
export class CloudWatchMetricsQueryRunner extends CloudWatchRequest {
|
||||
debouncedThrottlingAlert: (datasourceName: string, region: string) => void = memoizedDebounce(
|
||||
displayAlert,
|
||||
AppNotificationTimeout.Error
|
||||
);
|
||||
debouncedThrottlingAlert: (datasourceName: string, region: string) => void = memoizedDebounce(displayAlert);
|
||||
|
||||
constructor(instanceSettings: DataSourceInstanceSettings<CloudWatchJsonData>, templateSrv: TemplateSrv) {
|
||||
super(instanceSettings, templateSrv);
|
||||
@@ -123,13 +119,13 @@ export class CloudWatchMetricsQueryRunner extends CloudWatchRequest {
|
||||
});
|
||||
|
||||
if (res.errors?.length) {
|
||||
this.alertOnErrors(res.errors, request);
|
||||
this.alertOnThrottlingErrors(res.errors, request);
|
||||
}
|
||||
|
||||
return {
|
||||
data: dataframes,
|
||||
// DataSourceWithBackend will not throw an error, instead it will return "errors" field along with the response
|
||||
errors: res.errors,
|
||||
errors: this.enrichThrottlingErrorMessages(request, res.errors),
|
||||
};
|
||||
}),
|
||||
catchError((err: unknown) => {
|
||||
@@ -142,7 +138,23 @@ export class CloudWatchMetricsQueryRunner extends CloudWatchRequest {
|
||||
);
|
||||
}
|
||||
|
||||
alertOnErrors(errors: DataQueryError[], request: DataQueryRequest<CloudWatchQuery>) {
|
||||
enrichThrottlingErrorMessages(request: DataQueryRequest<CloudWatchQuery>, errors?: DataQueryError[]) {
|
||||
if (!errors || errors.length === 0) {
|
||||
return errors;
|
||||
}
|
||||
const result: DataQueryError[] = [];
|
||||
errors.forEach((error) => {
|
||||
if (error.message && (/^Throttling:.*/.test(error.message) || /^Rate exceeded.*/.test(error.message))) {
|
||||
const region = this.getActualRegion(request.targets.find((target) => target.refId === error.refId)?.region);
|
||||
result.push({ ...error, message: getThrottlingErrorMessage(region, error.message) });
|
||||
} else {
|
||||
result.push(error);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
alertOnThrottlingErrors(errors: DataQueryError[], request: DataQueryRequest<CloudWatchQuery>) {
|
||||
const hasThrottlingError = errors.some(
|
||||
(err) => err.message && (/^Throttling:.*/.test(err.message) || /^Rate exceeded.*/.test(err.message))
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user