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:
@@ -552,7 +552,7 @@ describe('PrometheusDatasource', () => {
|
|||||||
config.featureToggles.promQLScope = undefined;
|
config.featureToggles.promQLScope = undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call replace function 2 times', () => {
|
it('should call replace function 3 times', () => {
|
||||||
const query: PromQuery = {
|
const query: PromQuery = {
|
||||||
expr: 'test{job="testjob"}',
|
expr: 'test{job="testjob"}',
|
||||||
format: 'time_series',
|
format: 'time_series',
|
||||||
@@ -563,7 +563,7 @@ describe('PrometheusDatasource', () => {
|
|||||||
replaceMock.mockReturnValue(interval);
|
replaceMock.mockReturnValue(interval);
|
||||||
|
|
||||||
const queries = ds.interpolateVariablesInQueries([query], { Interval: { text: interval, value: 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);
|
expect(queries[0].interval).toBe(interval);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -753,6 +753,55 @@ describe('PrometheusDatasource', () => {
|
|||||||
const result = ds.applyTemplateVariables(query, {}, filters);
|
const result = ds.applyTemplateVariables(query, {}, filters);
|
||||||
expect(result).toMatchObject({ expr: 'test{job="99", k1="v1", k2!="v2"} > 99' });
|
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', () => {
|
describe('metricFindQuery', () => {
|
||||||
|
|||||||
@@ -659,14 +659,19 @@ export class PrometheusDatasource
|
|||||||
if (queries && queries.length) {
|
if (queries && queries.length) {
|
||||||
expandedQueries = queries.map((query) => {
|
expandedQueries = queries.map((query) => {
|
||||||
const interpolatedQuery = this.templateSrv.replace(query.expr, scopedVars, this.interpolateQueryExpr);
|
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 = {
|
const expandedQuery = {
|
||||||
...query,
|
...query,
|
||||||
...(config.featureToggles.promQLScope ? { adhocFilters: this.generateScopeFilters(filters) } : {}),
|
...(config.featureToggles.promQLScope ? { adhocFilters: this.generateScopeFilters(filters) } : {}),
|
||||||
datasource: this.getRef(),
|
datasource: this.getRef(),
|
||||||
expr: config.featureToggles.promQLScope
|
expr: replacedInterpolatedQuery,
|
||||||
? interpolatedQuery
|
|
||||||
: this.enhanceExprWithAdHocFilters(filters, interpolatedQuery),
|
|
||||||
interval: this.templateSrv.replace(query.interval, scopedVars),
|
interval: this.templateSrv.replace(query.interval, scopedVars),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -824,7 +829,11 @@ export class PrometheusDatasource
|
|||||||
return [];
|
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) {
|
enhanceExprWithAdHocFilters(filters: AdHocVariableFilter[] | undefined, expr: string) {
|
||||||
@@ -865,12 +874,21 @@ export class PrometheusDatasource
|
|||||||
};
|
};
|
||||||
|
|
||||||
// interpolate expression
|
// 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);
|
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 {
|
return {
|
||||||
...target,
|
...target,
|
||||||
...(config.featureToggles.promQLScope ? { adhocFilters: this.generateScopeFilters(filters) } : {}),
|
...(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),
|
interval: this.templateSrv.replace(target.interval, variables),
|
||||||
legendFormat: this.templateSrv.replace(target.legendFormat, variables),
|
legendFormat: this.templateSrv.replace(target.legendFormat, variables),
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user