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>
This commit is contained in:
Stéphane Cazeaux
2024-06-19 14:02:17 +02:00
committed by GitHub
parent b3907ca5ec
commit 6b876f1e38
2 changed files with 74 additions and 7 deletions

View File

@@ -552,7 +552,7 @@ describe('PrometheusDatasource', () => {
config.featureToggles.promQLScope = undefined;
});
it('should call replace function 2 times', () => {
it('should call replace function 3 times', () => {
const query: PromQuery = {
expr: 'test{job="testjob"}',
format: 'time_series',
@@ -563,7 +563,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);
});
@@ -753,6 +753,55 @@ 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"}' });
});
it('should replace variables in adhoc filters on backend when promQLScope is enabled', () => {
config.featureToggles.promQLScope = true;
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',
adhocFilters: [
{
key: 'job',
operator: 'regex-match',
value: '99',
},
],
});
});
});
describe('metricFindQuery', () => {

View File

@@ -659,14 +659,19 @@ export class PrometheusDatasource
if (queries && queries.length) {
expandedQueries = queries.map((query) => {
const interpolatedQuery = this.templateSrv.replace(query.expr, scopedVars, this.interpolateQueryExpr);
const replacedInterpolatedQuery = config.featureToggles.promQLScope
? interpolatedQuery
: this.templateSrv.replace(
this.enhanceExprWithAdHocFilters(filters, interpolatedQuery),
scopedVars,
this.interpolateQueryExpr
);
const expandedQuery = {
...query,
...(config.featureToggles.promQLScope ? { adhocFilters: this.generateScopeFilters(filters) } : {}),
datasource: this.getRef(),
expr: config.featureToggles.promQLScope
? interpolatedQuery
: this.enhanceExprWithAdHocFilters(filters, interpolatedQuery),
expr: replacedInterpolatedQuery,
interval: this.templateSrv.replace(query.interval, scopedVars),
};
@@ -824,7 +829,11 @@ export class PrometheusDatasource
return [];
}
return filters.map((f) => ({ ...f, operator: scopeFilterOperatorMap[f.operator] }));
return filters.map((f) => ({
...f,
value: this.templateSrv.replace(f.value, {}, this.interpolateQueryExpr),
operator: scopeFilterOperatorMap[f.operator],
}));
}
enhanceExprWithAdHocFilters(filters: AdHocVariableFilter[] | undefined, expr: string) {
@@ -865,12 +874,21 @@ 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);
// 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 exprWithAdhoc = config.featureToggles.promQLScope
? expr
: this.templateSrv.replace(this.enhanceExprWithAdHocFilters(filters, expr), variables, this.interpolateQueryExpr);
return {
...target,
...(config.featureToggles.promQLScope ? { adhocFilters: this.generateScopeFilters(filters) } : {}),
expr: config.featureToggles.promQLScope ? expr : this.enhanceExprWithAdHocFilters(filters, expr),
expr: exprWithAdhoc,
interval: this.templateSrv.replace(target.interval, variables),
legendFormat: this.templateSrv.replace(target.legendFormat, variables),
};