mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
parent
74ee675732
commit
57bea68453
135
packages/grafana-prometheus/src/__mocks__/datasource.ts
Normal file
135
packages/grafana-prometheus/src/__mocks__/datasource.ts
Normal 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 ?? [];
|
||||
}
|
@ -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(() => []),
|
||||
|
@ -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',
|
||||
});
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"exclude": ["dist", "node_modules", "test", "**/*.test.ts*"],
|
||||
"exclude": ["dist", "node_modules", "__mocks__", "test", "**/*.test.ts*"],
|
||||
"extends": "./tsconfig.json"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user