Explorar o código

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
Patrick Hahn %!s(int64=6) %!d(string=hai) anos
pai
achega
606825703f

+ 6 - 5
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 { safeStringifyValue } from 'app/core/utils/explore';
 import { TemplateSrv } from 'app/features/templating/template_srv';
 import { TemplateSrv } from 'app/features/templating/template_srv';
 import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
 import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
+import { TimeRange } from '@grafana/ui/src';
 
 
 export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> {
 export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> {
   type: string;
   type: string;
@@ -320,14 +321,14 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
     const intervalFactor = target.intervalFactor || 1;
     const intervalFactor = target.intervalFactor || 1;
     // Adjust the interval to take into account any specified minimum and interval factor plus Prometheus limits
     // Adjust the interval to take into account any specified minimum and interval factor plus Prometheus limits
     const adjustedInterval = this.adjustInterval(interval, minInterval, range, intervalFactor);
     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 the interval was adjusted, make a shallow copy of scopedVars with updated interval vars
     if (interval !== adjustedInterval) {
     if (interval !== adjustedInterval) {
       interval = adjustedInterval;
       interval = adjustedInterval;
       scopedVars = Object.assign({}, options.scopedVars, {
       scopedVars = Object.assign({}, options.scopedVars, {
         __interval: { text: interval + 's', value: interval + 's' },
         __interval: { text: interval + 's', value: interval + 's' },
         __interval_ms: { text: interval * 1000, value: interval * 1000 },
         __interval_ms: { text: interval * 1000, value: interval * 1000 },
-        ...this.getRangeScopedVars(),
+        ...this.getRangeScopedVars(options.range),
       });
       });
     }
     }
     query.step = interval;
     query.step = interval;
@@ -461,15 +462,15 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
     const scopedVars = {
     const scopedVars = {
       __interval: { text: this.interval, value: this.interval },
       __interval: { text: this.interval, value: this.interval },
       __interval_ms: { text: kbn.interval_to_ms(this.interval), value: kbn.interval_to_ms(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 interpolated = this.templateSrv.replace(query, scopedVars, this.interpolateQueryExpr);
     const metricFindQuery = new PrometheusMetricFindQuery(this, interpolated, this.timeSrv);
     const metricFindQuery = new PrometheusMetricFindQuery(this, interpolated, this.timeSrv);
     return metricFindQuery.process();
     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 msRange = range.to.diff(range.from);
     const sRange = Math.round(msRange / 1000);
     const sRange = Math.round(msRange / 1000);
     const regularRange = kbn.secondsToHms(msRange / 1000);
     const regularRange = kbn.secondsToHms(msRange / 1000);

+ 54 - 0
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', () => {
 describe('PrometheusDatasource for POST', () => {