Chore: Remove unused functions form prometheus data source (#86461)

* remove createQuery

* remove redundant unit test

* update unit tests

* remove redundant mock

* exclude mocks from build

* remove it

* prettier
This commit is contained in:
ismail simsek 2024-04-19 22:00:26 +02:00 committed by GitHub
parent 74ee675732
commit 57bea68453
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 183 additions and 343 deletions

View File

@ -0,0 +1,135 @@
import { CoreApp, DataQueryRequest, dateTime, rangeUtil, TimeRange } from '@grafana/data';
import { PromQuery } from '../types';
export function createDataRequest(
targets: PromQuery[],
overrides?: Partial<DataQueryRequest>
): DataQueryRequest<PromQuery> {
const defaults: DataQueryRequest<PromQuery> = {
intervalMs: 15000,
requestId: 'createDataRequest',
startTime: 0,
timezone: 'browser',
app: CoreApp.Dashboard,
targets: targets.map((t, i) => ({
instant: false,
start: dateTime().subtract(5, 'minutes'),
end: dateTime(),
...t,
})),
range: {
from: dateTime(),
to: dateTime(),
raw: {
from: '',
to: '',
},
},
interval: '15s',
scopedVars: {},
};
return Object.assign(defaults, overrides || {}) as DataQueryRequest<PromQuery>;
}
export function createDefaultPromResponse() {
return {
data: {
data: {
result: [
{
metric: {
__name__: 'test_metric',
},
values: [[1568369640, 1]],
},
],
resultType: 'matrix',
},
},
};
}
export function createAnnotationResponse() {
const response = {
data: {
results: {
X: {
frames: [
{
schema: {
name: 'bar',
refId: 'X',
fields: [
{
name: 'Time',
type: 'time',
typeInfo: {
frame: 'time.Time',
},
},
{
name: 'Value',
type: 'number',
typeInfo: {
frame: 'float64',
},
labels: {
__name__: 'ALERTS',
alertname: 'InstanceDown',
alertstate: 'firing',
instance: 'testinstance',
job: 'testjob',
},
},
],
},
data: {
values: [[123], [456]],
},
},
],
},
},
},
};
return { ...response };
}
export function createEmptyAnnotationResponse() {
const response = {
data: {
results: {
X: {
frames: [
{
schema: {
name: 'bar',
refId: 'X',
fields: [],
},
data: {
values: [],
},
},
],
},
},
},
};
return { ...response };
}
export function getMockTimeRange(range = '6h'): TimeRange {
return rangeUtil.convertRawToRange({
from: `now-${range}`,
to: 'now',
});
}
export function fetchMockCalledWith(fetchMock: ReturnType<typeof jest.fn>): PromQuery[] {
return fetchMock.mock.calls[0][0].data.queries ?? [];
}

View File

@ -25,7 +25,6 @@ jest.mock('./monaco-query-field/MonacoQueryFieldLazy', () => {
function setup(app: CoreApp): { onRunQuery: jest.Mock } {
const dataSource = {
createQuery: jest.fn((q) => q),
getInitHints: () => [],
getPrometheusTime: jest.fn((date, roundup) => 123),
getQueryHints: jest.fn(() => []),

View File

@ -12,13 +12,20 @@ import {
DataSourceInstanceSettings,
dateTime,
LoadingState,
rangeUtil,
ScopeSpecFilter,
TimeRange,
VariableHide,
} from '@grafana/data';
import { config, TemplateSrv } from '@grafana/runtime';
import { config, getBackendSrv, setBackendSrv, TemplateSrv } from '@grafana/runtime';
import {
createAnnotationResponse,
createDataRequest,
createDefaultPromResponse,
createEmptyAnnotationResponse,
fetchMockCalledWith,
getMockTimeRange,
} from './__mocks__/datasource';
import {
alignRange,
extractRuleMappingFromGroups,
@ -32,12 +39,11 @@ import { PromApplication, PrometheusCacheLevel, PromOptions, PromQuery, PromQuer
const fetchMock = jest.fn().mockReturnValue(of(createDefaultPromResponse()));
jest.mock('./metric_find_query');
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
getBackendSrv: () => ({
fetch: fetchMock,
}),
}));
const origBackendSrv = getBackendSrv();
setBackendSrv({
...origBackendSrv,
fetch: fetchMock,
});
const replaceMock = jest.fn().mockImplementation((a: string, ...rest: unknown[]) => a);
@ -232,13 +238,13 @@ describe('PrometheusDatasource', () => {
const DEFAULT_QUERY_EXPRESSION = 'metric{job="foo"} - metric';
const target: PromQuery = { expr: DEFAULT_QUERY_EXPRESSION, refId: 'A' };
it('should not modify expression with no filters', () => {
const result = ds.createQuery(
target,
{ interval: '15s', range: getMockTimeRange() } as DataQueryRequest<PromQuery>,
0,
0
);
it('should not modify expression with no filters', async () => {
ds.query({
interval: '15s',
range: getMockTimeRange(),
targets: [target],
} as DataQueryRequest<PromQuery>);
const [result] = fetchMockCalledWith(fetchMock);
expect(result).toMatchObject({ expr: DEFAULT_QUERY_EXPRESSION });
});
@ -255,15 +261,15 @@ describe('PrometheusDatasource', () => {
value: 'v2',
},
];
const result = ds.createQuery(
target,
{ interval: '15s', range: getMockTimeRange(), filters } as DataQueryRequest<PromQuery>,
0,
0
);
ds.query({
interval: '15s',
range: getMockTimeRange(),
filters,
targets: [target],
} as DataQueryRequest<PromQuery>);
const [result] = fetchMockCalledWith(fetchMock);
expect(result).toMatchObject({ expr: 'metric{job="foo", k1="v1", k2!="v2"} - metric{k1="v1", k2!="v2"}' });
});
it('should add escaping if needed to regex filter expressions', () => {
const filters = [
{
@ -277,13 +283,13 @@ describe('PrometheusDatasource', () => {
value: `v'.*`,
},
];
const result = ds.createQuery(
target,
{ interval: '15s', range: getMockTimeRange(), filters } as DataQueryRequest<PromQuery>,
0,
0
);
ds.query({
interval: '15s',
range: getMockTimeRange(),
filters,
targets: [target],
} as DataQueryRequest<PromQuery>);
const [result] = fetchMockCalledWith(fetchMock);
expect(result).toMatchObject({
expr: `metric{job="foo", k1=~"v.*", k2=~"v\\\\'.*"} - metric{k1=~"v.*", k2=~"v\\\\'.*"}`,
});
@ -1016,91 +1022,6 @@ describe('PrometheusDatasource2', () => {
});
});
describe('The __rate_interval variable', () => {
const target = { expr: 'rate(process_cpu_seconds_total[$__rate_interval])', refId: 'A' };
beforeEach(() => {
replaceMock.mockClear();
});
it('should be 4 times the scrape interval if interval + scrape interval is lower', () => {
ds.createQuery(target, { interval: '15s', range: getMockTimeRange() } as DataQueryRequest<PromQuery>, 0, 300);
expect(replaceMock.mock.calls[1][1]['__rate_interval'].value).toBe('60s');
});
it('should be interval + scrape interval if 4 times the scrape interval is lower', () => {
ds.createQuery(
target,
{
interval: '5m',
range: getMockTimeRange(),
} as DataQueryRequest<PromQuery>,
0,
10080
);
expect(replaceMock.mock.calls[1][1]['__rate_interval'].value).toBe('315s');
});
it('should fall back to a scrape interval of 15s if min step is set to 0, resulting in 4*15s = 60s', () => {
ds.createQuery(
{ ...target, interval: '' },
{ interval: '15s', range: getMockTimeRange() } as DataQueryRequest<PromQuery>,
0,
300
);
expect(replaceMock.mock.calls[1][1]['__rate_interval'].value).toBe('60s');
});
it('should be 4 times the scrape interval if min step set to 1m and interval is 15s', () => {
// For a 5m graph, $__interval is 15s
ds.createQuery(
{ ...target, interval: '1m' },
{ interval: '15s', range: getMockTimeRange() } as DataQueryRequest<PromQuery>,
0,
300
);
expect(replaceMock.mock.calls[2][1]['__rate_interval'].value).toBe('240s');
});
it('should be interval + scrape interval if min step set to 1m and interval is 5m', () => {
// For a 7d graph, $__interval is 5m
ds.createQuery(
{ ...target, interval: '1m' },
{ interval: '5m', range: getMockTimeRange() } as DataQueryRequest<PromQuery>,
0,
10080
);
expect(replaceMock.mock.calls[2][1]['__rate_interval'].value).toBe('360s');
});
it('should be interval + scrape interval if resolution is set to 1/2 and interval is 10m', () => {
// For a 7d graph, $__interval is 10m
ds.createQuery(
{ ...target, intervalFactor: 2 },
{ interval: '10m', range: getMockTimeRange() } as DataQueryRequest<PromQuery>,
0,
10080
);
expect(replaceMock.mock.calls[1][1]['__rate_interval'].value).toBe('1215s');
});
it('should be 4 times the scrape interval if resolution is set to 1/2 and interval is 15s', () => {
// For a 5m graph, $__interval is 15s
ds.createQuery(
{ ...target, intervalFactor: 2 },
{ interval: '15s', range: getMockTimeRange() } as DataQueryRequest<PromQuery>,
0,
300
);
expect(replaceMock.mock.calls[1][1]['__rate_interval'].value).toBe('60s');
});
it('should interpolate min step if set', () => {
replaceMock.mockImplementation((_: string) => '15s');
ds.createQuery(
{ ...target, interval: '$int' },
{ interval: '15s', range: getMockTimeRange() } as DataQueryRequest<PromQuery>,
0,
300
);
expect(replaceMock.mock.calls).toHaveLength(3);
replaceMock.mockImplementation((str) => str);
});
});
it('should give back 1 exemplar target when multiple queries with exemplar enabled and same metric', () => {
const targetA: PromQuery = {
refId: 'A',
@ -1274,128 +1195,3 @@ describe('modifyQuery', () => {
});
});
});
function createDataRequest(targets: PromQuery[], overrides?: Partial<DataQueryRequest>): DataQueryRequest<PromQuery> {
const defaults: DataQueryRequest<PromQuery> = {
intervalMs: 15000,
requestId: 'createDataRequest',
startTime: 0,
timezone: 'browser',
app: CoreApp.Dashboard,
targets: targets.map((t, i) => ({
instant: false,
start: dateTime().subtract(5, 'minutes'),
end: dateTime(),
...t,
})),
range: {
from: dateTime(),
to: dateTime(),
raw: {
from: '',
to: '',
},
},
interval: '15s',
scopedVars: {},
};
return Object.assign(defaults, overrides || {}) as DataQueryRequest<PromQuery>;
}
function createDefaultPromResponse() {
return {
data: {
data: {
result: [
{
metric: {
__name__: 'test_metric',
},
values: [[1568369640, 1]],
},
],
resultType: 'matrix',
},
},
};
}
function createAnnotationResponse() {
const response = {
data: {
results: {
X: {
frames: [
{
schema: {
name: 'bar',
refId: 'X',
fields: [
{
name: 'Time',
type: 'time',
typeInfo: {
frame: 'time.Time',
},
},
{
name: 'Value',
type: 'number',
typeInfo: {
frame: 'float64',
},
labels: {
__name__: 'ALERTS',
alertname: 'InstanceDown',
alertstate: 'firing',
instance: 'testinstance',
job: 'testjob',
},
},
],
},
data: {
values: [[123], [456]],
},
},
],
},
},
},
};
return { ...response };
}
function createEmptyAnnotationResponse() {
const response = {
data: {
results: {
X: {
frames: [
{
schema: {
name: 'bar',
refId: 'X',
fields: [],
},
data: {
values: [],
},
},
],
},
},
},
};
return { ...response };
}
function getMockTimeRange(range = '6h'): TimeRange {
return rangeUtil.convertRawToRange({
from: `now-${range}`,
to: 'now',
});
}

View File

@ -432,108 +432,6 @@ export class PrometheusDatasource
);
}
createQuery(target: PromQuery, options: DataQueryRequest<PromQuery>, start: number, end: number) {
const query: PromQueryRequest = {
hinting: target.hinting,
instant: target.instant,
exemplar: target.exemplar,
step: 0,
expr: '',
refId: target.refId,
start: 0,
end: 0,
};
const range = Math.ceil(end - start);
// options.interval is the dynamically calculated interval
let interval: number = rangeUtil.intervalToSeconds(options.interval);
// Minimum interval ("Min step"), if specified for the query, or same as interval otherwise.
const minInterval = rangeUtil.intervalToSeconds(
this.templateSrv.replace(target.interval || options.interval, options.scopedVars)
);
// Scrape interval as specified for the query ("Min step") or otherwise taken from the datasource.
// Min step field can have template variables in it, make sure to replace it.
const scrapeInterval = target.interval
? rangeUtil.intervalToSeconds(this.templateSrv.replace(target.interval, options.scopedVars))
: rangeUtil.intervalToSeconds(this.interval);
const intervalFactor = target.intervalFactor || 1;
// Adjust the interval to take into account any specified minimum and interval factor plus Prometheus limits
const adjustedInterval = this.adjustInterval(interval, minInterval, range, intervalFactor);
let scopedVars = {
...options.scopedVars,
...this.getRangeScopedVars(options.range),
...this.getRateIntervalScopedVariable(adjustedInterval, scrapeInterval),
};
// If the interval was adjusted, make a shallow copy of scopedVars with updated interval vars
if (interval !== adjustedInterval) {
interval = adjustedInterval;
scopedVars = Object.assign({}, options.scopedVars, {
__interval: { text: interval + 's', value: interval + 's' },
__interval_ms: { text: interval * 1000, value: interval * 1000 },
...this.getRateIntervalScopedVariable(interval, scrapeInterval),
...this.getRangeScopedVars(options.range),
});
}
query.step = interval;
let expr = target.expr;
if (config.featureToggles.promQLScope) {
// Apply scope filters
query.adhocFilters = this.generateScopeFilters(options.filters);
} else {
// Apply adhoc filters
expr = this.enhanceExprWithAdHocFilters(options.filters, expr);
}
// Only replace vars in expression after having (possibly) updated interval vars
query.expr = this.templateSrv.replace(expr, scopedVars, this.interpolateQueryExpr);
// Align query interval with step to allow query caching and to ensure
// that about-same-time query results look the same.
const adjusted = alignRange(start, end, query.step, options.range.to.utcOffset() * 60);
query.start = adjusted.start;
query.end = adjusted.end;
this._addTracingHeaders(query, options);
return query;
}
/**
* This converts the adhocVariableFilter array and converts it to scopeFilter array
* @param filters
*/
generateScopeFilters(filters?: AdHocVariableFilter[]): ScopeSpecFilter[] {
if (!filters) {
return [];
}
return filters.map((f) => ({ ...f, operator: scopeFilterOperatorMap[f.operator] }));
}
getRateIntervalScopedVariable(interval: number, scrapeInterval: number) {
// Fall back to the default scrape interval of 15s if scrapeInterval is 0 for some reason.
if (scrapeInterval === 0) {
scrapeInterval = 15;
}
const rateInterval = Math.max(interval + scrapeInterval, 4 * scrapeInterval);
return { __rate_interval: { text: rateInterval + 's', value: rateInterval + 's' } };
}
adjustInterval(interval: number, minInterval: number, range: number, intervalFactor: number) {
// Prometheus will drop queries that might return more than 11000 data points.
// Calculate a safe interval as an additional minimum to take into account.
// Fractional safeIntervals are allowed, however serve little purpose if the interval is greater than 1
// If this is the case take the ceil of the value.
let safeInterval = range / 11000;
if (safeInterval > 1) {
safeInterval = Math.ceil(safeInterval);
}
return Math.max(interval * intervalFactor, minInterval, safeInterval);
}
metricFindQuery(query: string, options?: LegacyMetricFindQueryOptions) {
if (!query) {
return Promise.resolve([]);
@ -884,6 +782,18 @@ export class PrometheusDatasource
return getOriginalMetricName(labelData);
}
/**
* This converts the adhocVariableFilter array and converts it to scopeFilter array
* @param filters
*/
generateScopeFilters(filters?: AdHocVariableFilter[]): ScopeSpecFilter[] {
if (!filters) {
return [];
}
return filters.map((f) => ({ ...f, operator: scopeFilterOperatorMap[f.operator] }));
}
enhanceExprWithAdHocFilters(filters: AdHocVariableFilter[] | undefined, expr: string) {
if (!filters || filters.length === 0) {
return expr;

View File

@ -1,4 +1,4 @@
{
"exclude": ["dist", "node_modules", "test", "**/*.test.ts*"],
"exclude": ["dist", "node_modules", "__mocks__", "test", "**/*.test.ts*"],
"extends": "./tsconfig.json"
}