From 606825703f018a14c9c03b12e26f332e3d95a8c4 Mon Sep 17 00:00:00 2001 From: Patrick Hahn Date: Thu, 6 Jun 2019 10:03:14 +0200 Subject: [PATCH] Prometheus: Use overridden panel range as $_range instead of dashboard range (#17352) The range variables get filled with the range from the query options, not with the range in the timeSrv object. This means that panels that use a relative time override get the correct values from the __range variables. Fixes #17102 --- .../datasource/prometheus/datasource.ts | 11 ++-- .../prometheus/specs/datasource.test.ts | 54 +++++++++++++++++++ 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts index a5cfc0568ec..49b24fa7f6f 100644 --- a/public/app/plugins/datasource/prometheus/datasource.ts +++ b/public/app/plugins/datasource/prometheus/datasource.ts @@ -30,6 +30,7 @@ import { ExploreUrlState } from 'app/types/explore'; import { safeStringifyValue } from 'app/core/utils/explore'; import { TemplateSrv } from 'app/features/templating/template_srv'; import { TimeSrv } from 'app/features/dashboard/services/TimeSrv'; +import { TimeRange } from '@grafana/ui/src'; export class PrometheusDatasource extends DataSourceApi { type: string; @@ -320,14 +321,14 @@ export class PrometheusDatasource extends DataSourceApi 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() }; + let scopedVars = { ...options.scopedVars, ...this.getRangeScopedVars(options.range) }; // 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.getRangeScopedVars(), + ...this.getRangeScopedVars(options.range), }); } query.step = interval; @@ -461,15 +462,15 @@ export class PrometheusDatasource extends DataSourceApi const scopedVars = { __interval: { text: this.interval, value: this.interval }, __interval_ms: { text: kbn.interval_to_ms(this.interval), value: kbn.interval_to_ms(this.interval) }, - ...this.getRangeScopedVars(), + ...this.getRangeScopedVars(this.timeSrv.timeRange()), }; const interpolated = this.templateSrv.replace(query, scopedVars, this.interpolateQueryExpr); const metricFindQuery = new PrometheusMetricFindQuery(this, interpolated, this.timeSrv); return metricFindQuery.process(); } - getRangeScopedVars() { - const range = this.timeSrv.timeRange(); + getRangeScopedVars(range: TimeRange) { + range = range || this.timeSrv.timeRange(); const msRange = range.to.diff(range.from); const sRange = Math.round(msRange / 1000); const regularRange = kbn.secondsToHms(msRange / 1000); diff --git a/public/app/plugins/datasource/prometheus/specs/datasource.test.ts b/public/app/plugins/datasource/prometheus/specs/datasource.test.ts index 77da9948122..8fab195a5d9 100644 --- a/public/app/plugins/datasource/prometheus/specs/datasource.test.ts +++ b/public/app/plugins/datasource/prometheus/specs/datasource.test.ts @@ -1226,6 +1226,60 @@ describe('PrometheusDatasource', () => { }); }); }); + describe('The __range, __range_s and __range_ms variables', () => { + const response = { + status: 'success', + data: { + data: { + resultType: 'matrix', + result: [], + }, + }, + }; + + it('should use overridden ranges, not dashboard ranges', async () => { + const expectedRangeSecond = 3600; + const expectedRangeString = '1h'; + const query = { + range: { + from: time({}), + to: time({ hours: 1 }), + }, + targets: [ + { + expr: 'test[${__range_s}s]', + }, + ], + interval: '60s', + }; + const urlExpected = `proxied/api/v1/query_range?query=${encodeURIComponent( + query.targets[0].expr + )}&start=0&end=3600&step=60`; + + templateSrv.replace = jest.fn(str => str); + backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response)); + ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any); + await ctx.ds.query(query); + const res = backendSrv.datasourceRequest.mock.calls[0][0]; + expect(res.url).toBe(urlExpected); + + // @ts-ignore + expect(templateSrv.replace.mock.calls[1][1]).toEqual({ + __range_s: { + text: expectedRangeSecond, + value: expectedRangeSecond, + }, + __range: { + text: expectedRangeString, + value: expectedRangeString, + }, + __range_ms: { + text: expectedRangeSecond * 1000, + value: expectedRangeSecond * 1000, + }, + }); + }); + }); }); describe('PrometheusDatasource for POST', () => {