[v11.0.x] Prometheus: Fix interpolating adhoc filters with template variables (#95986)

* Prometheus: Fix interpolating adhoc filters with template variables (#88626)

* Prometheus: replace variables on adhoc filters

Fixes #87979

Signed-off-by: Stéphane Cazeaux <stephane.cazeaux@orange.com>

* Prometheus: replace variable filters on adhoc variables also when promQLScope=true

Signed-off-by: Stéphane Cazeaux <stephane.cazeaux@orange.com>

---------

Signed-off-by: Stéphane Cazeaux <stephane.cazeaux@orange.com>
(cherry picked from commit 6b876f1e38)

* fix unit test

* revert

* revert some unnecessary changes

* remove unnecessary promqlscope ft checks

* linting

---------

Co-authored-by: Stéphane Cazeaux <stephane.cazeaux@casper-online.net>
This commit is contained in:
ismail simsek
2024-11-07 08:39:29 +01:00
committed by GitHub
co-authored by Stéphane Cazeaux
parent 1f0aa2a1af
commit 6a3b84ebd2
4 changed files with 74 additions and 13 deletions
@@ -539,7 +539,7 @@ describe('PrometheusDatasource', () => {
});
describe('interpolateVariablesInQueries', () => {
it('should call replace function 2 times', () => {
it('should call replace function 3 times', () => {
const query: PromQuery = {
expr: 'test{job="testjob"}',
format: 'time_series',
@@ -550,7 +550,7 @@ describe('PrometheusDatasource', () => {
replaceMock.mockReturnValue(interval);
const queries = ds.interpolateVariablesInQueries([query], { Interval: { text: interval, value: interval } });
expect(templateSrvStub.replace).toBeCalledTimes(2);
expect(templateSrvStub.replace).toBeCalledTimes(3);
expect(queries[0].interval).toBe(interval);
});
@@ -682,6 +682,26 @@ describe('PrometheusDatasource', () => {
const result = ds.applyTemplateVariables(query, {}, filters);
expect(result).toMatchObject({ expr: 'test{job="99", k1="v1", k2!="v2"} > 99' });
});
it('should replace variables in ad-hoc filters', () => {
const searchPattern = /\$A/g;
replaceMock.mockImplementation((a: string) => a?.replace(searchPattern, '99') ?? a);
const query = {
expr: 'test',
refId: 'A',
};
const filters = [
{
key: 'job',
operator: '=~',
value: '$A',
},
];
const result = ds.applyTemplateVariables(query, {}, filters);
expect(result).toMatchObject({ expr: 'test{job=~"99"}' });
});
});
describe('metricFindQuery', () => {
+15 -3
View File
@@ -726,7 +726,11 @@ export class PrometheusDatasource
if (queries && queries.length) {
expandedQueries = queries.map((query) => {
const interpolatedQuery = this.templateSrv.replace(query.expr, scopedVars, this.interpolateQueryExpr);
const withAdhocFilters = this.enhanceExprWithAdHocFilters(filters, interpolatedQuery);
const withAdhocFilters = this.templateSrv.replace(
this.enhanceExprWithAdHocFilters(filters, interpolatedQuery),
scopedVars,
this.interpolateQueryExpr
);
const expandedQuery = {
...query,
@@ -893,10 +897,18 @@ export class PrometheusDatasource
};
// interpolate expression
// We need a first replace to evaluate variables before applying adhoc filters
// This is required for an expression like `metric > $VAR` where $VAR is a float to which we must not add adhoc filters
const expr = this.templateSrv.replace(target.expr, variables, this.interpolateQueryExpr);
// Add ad hoc filters
const exprWithAdHocFilters = this.enhanceExprWithAdHocFilters(filters, expr);
// Apply ad-hoc filters
// When ad-hoc filters are applied, we replace again the variables in case the ad-hoc filters also reference a variable
const exprWithAdHocFilters = this.templateSrv.replace(
this.enhanceExprWithAdHocFilters(filters, expr),
variables,
this.interpolateQueryExpr
);
return {
...target,
@@ -29,7 +29,6 @@ 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: () => ({
@@ -75,9 +74,9 @@ describe('PrometheusDatasource', () => {
url: 'proxied',
id: 1,
uid: 'ABCDEF',
access: 'proxy',
user: 'test',
password: 'mupp',
access: 'proxy',
jsonData: {
customQueryParameters: '',
cacheLevel: PrometheusCacheLevel.Low,
@@ -548,7 +547,7 @@ describe('PrometheusDatasource', () => {
});
describe('interpolateVariablesInQueries', () => {
it('should call replace function 2 times', () => {
it('should call replace function 3 times', () => {
const query: PromQuery = {
expr: 'test{job="testjob"}',
format: 'time_series',
@@ -559,7 +558,7 @@ describe('PrometheusDatasource', () => {
replaceMock.mockReturnValue(interval);
const queries = ds.interpolateVariablesInQueries([query], { Interval: { text: interval, value: interval } });
expect(templateSrvStub.replace).toBeCalledTimes(2);
expect(templateSrvStub.replace).toBeCalledTimes(3);
expect(queries[0].interval).toBe(interval);
});
@@ -691,6 +690,26 @@ describe('PrometheusDatasource', () => {
const result = ds.applyTemplateVariables(query, {}, filters);
expect(result).toMatchObject({ expr: 'test{job="99", k1="v1", k2!="v2"} > 99' });
});
it('should replace variables in ad-hoc filters', () => {
const searchPattern = /\$A/g;
replaceMock.mockImplementation((a: string) => a?.replace(searchPattern, '99') ?? a);
const query = {
expr: 'test',
refId: 'A',
};
const filters = [
{
key: 'job',
operator: '=~',
value: '$A',
},
];
const result = ds.applyTemplateVariables(query, {}, filters);
expect(result).toMatchObject({ expr: 'test{job=~"99"}' });
});
});
describe('metricFindQuery', () => {
@@ -728,7 +728,11 @@ export class PrometheusDatasource
if (queries && queries.length) {
expandedQueries = queries.map((query) => {
const interpolatedQuery = this.templateSrv.replace(query.expr, scopedVars, this.interpolateQueryExpr);
const withAdhocFilters = this.enhanceExprWithAdHocFilters(filters, interpolatedQuery);
const withAdhocFilters = this.templateSrv.replace(
this.enhanceExprWithAdHocFilters(filters, interpolatedQuery),
scopedVars,
this.interpolateQueryExpr
);
const expandedQuery = {
...query,
@@ -894,11 +898,17 @@ export class PrometheusDatasource
value: '$__interval_ms',
};
// interpolate expression
// We need a first replace to evaluate variables before applying adhoc filters
// This is required for an expression like `metric > $VAR` where $VAR is a float to which we must not add adhoc filters
const expr = this.templateSrv.replace(target.expr, variables, this.interpolateQueryExpr);
// Add ad hoc filters
const exprWithAdHocFilters = this.enhanceExprWithAdHocFilters(filters, expr);
// Apply ad-hoc filters
// When ad-hoc filters are applied, we replace again the variables in case the ad-hoc filters also reference a variable
const exprWithAdHocFilters = this.templateSrv.replace(
this.enhanceExprWithAdHocFilters(filters, expr),
variables,
this.interpolateQueryExpr
);
return {
...target,