Commit 750ea9bb by David Kaltschmidt

Changed Prometheus interval-alignment to cover whole panel range

* the existing query date alignment shifts the range forward to match
a multiple epoch of the interval, but keeps the range length the same,
the result is that the start date is shifted forward as well, leaving a
gap in the graph (or a zero-line when null-as-zero was set, issue #12024)
* this pr extends the aligned range to cover the original start date as
well
parent 21ade3f6
...@@ -7,6 +7,15 @@ import PrometheusMetricFindQuery from './metric_find_query'; ...@@ -7,6 +7,15 @@ import PrometheusMetricFindQuery from './metric_find_query';
import { ResultTransformer } from './result_transformer'; import { ResultTransformer } from './result_transformer';
import { BackendSrv } from 'app/core/services/backend_srv'; import { BackendSrv } from 'app/core/services/backend_srv';
export function alignRange(start, end, step) {
const alignedEnd = Math.ceil(end / step) * step;
const alignedStart = Math.floor(start / step) * step;
return {
end: alignedEnd,
start: alignedStart,
};
}
export function prometheusRegularEscape(value) { export function prometheusRegularEscape(value) {
return value.replace(/'/g, "\\\\'"); return value.replace(/'/g, "\\\\'");
} }
...@@ -109,15 +118,6 @@ export class PrometheusDatasource { ...@@ -109,15 +118,6 @@ export class PrometheusDatasource {
return this.templateSrv.variableExists(target.expr); return this.templateSrv.variableExists(target.expr);
} }
clampRange(start, end, step) {
const clampedEnd = Math.ceil(end / step) * step;
const clampedRange = Math.floor((end - start) / step) * step;
return {
end: clampedEnd,
start: clampedEnd - clampedRange,
};
}
query(options) { query(options) {
var start = this.getPrometheusTime(options.range.from, false); var start = this.getPrometheusTime(options.range.from, false);
var end = this.getPrometheusTime(options.range.to, true); var end = this.getPrometheusTime(options.range.to, true);
...@@ -205,7 +205,7 @@ export class PrometheusDatasource { ...@@ -205,7 +205,7 @@ export class PrometheusDatasource {
query.requestId = options.panelId + target.refId; query.requestId = options.panelId + target.refId;
// Align query interval with step // Align query interval with step
const adjusted = this.clampRange(start, end, query.step); const adjusted = alignRange(start, end, query.step);
query.start = adjusted.start; query.start = adjusted.start;
query.end = adjusted.end; query.end = adjusted.end;
......
import _ from 'lodash'; import _ from 'lodash';
import moment from 'moment'; import moment from 'moment';
import q from 'q'; import q from 'q';
import { PrometheusDatasource, prometheusSpecialRegexEscape, prometheusRegularEscape } from '../datasource'; import { alignRange, PrometheusDatasource, prometheusSpecialRegexEscape, prometheusRegularEscape } from '../datasource';
describe('PrometheusDatasource', () => { describe('PrometheusDatasource', () => {
let ctx: any = {}; let ctx: any = {};
...@@ -142,6 +142,29 @@ describe('PrometheusDatasource', () => { ...@@ -142,6 +142,29 @@ describe('PrometheusDatasource', () => {
}); });
}); });
describe('alignRange', function() {
it('does not modify already aligned intervals with perfect step', function() {
const range = alignRange(0, 3, 3);
expect(range.start).toEqual(0);
expect(range.end).toEqual(3);
});
it('does modify end-aligned intervals to reflect number of steps possible', function() {
const range = alignRange(1, 6, 3);
expect(range.start).toEqual(0);
expect(range.end).toEqual(6);
});
it('does align intervals that are a multiple of steps', function() {
const range = alignRange(1, 4, 3);
expect(range.start).toEqual(0);
expect(range.end).toEqual(6);
});
it('does align intervals that are not a multiple of steps', function() {
const range = alignRange(1, 5, 3);
expect(range.start).toEqual(0);
expect(range.end).toEqual(6);
});
});
describe('Prometheus regular escaping', function() { describe('Prometheus regular escaping', function() {
it('should not escape simple string', function() { it('should not escape simple string', function() {
expect(prometheusRegularEscape('cryptodepression')).toEqual('cryptodepression'); expect(prometheusRegularEscape('cryptodepression')).toEqual('cryptodepression');
......
...@@ -44,7 +44,7 @@ describe('PrometheusDatasource', function() { ...@@ -44,7 +44,7 @@ describe('PrometheusDatasource', function() {
}; };
// Interval alignment with step // Interval alignment with step
var urlExpected = var urlExpected =
'proxied/api/v1/query_range?query=' + encodeURIComponent('test{job="testjob"}') + '&start=120&end=240&step=60'; 'proxied/api/v1/query_range?query=' + encodeURIComponent('test{job="testjob"}') + '&start=60&end=240&step=60';
var response = { var response = {
status: 'success', status: 'success',
data: { data: {
...@@ -181,7 +181,7 @@ describe('PrometheusDatasource', function() { ...@@ -181,7 +181,7 @@ describe('PrometheusDatasource', function() {
var urlExpected = var urlExpected =
'proxied/api/v1/query_range?query=' + 'proxied/api/v1/query_range?query=' +
encodeURIComponent('ALERTS{alertstate="firing"}') + encodeURIComponent('ALERTS{alertstate="firing"}') +
'&start=120&end=180&step=60'; '&start=60&end=180&step=60';
var options = { var options = {
annotation: { annotation: {
expr: 'ALERTS{alertstate="firing"}', expr: 'ALERTS{alertstate="firing"}',
...@@ -348,7 +348,7 @@ describe('PrometheusDatasource', function() { ...@@ -348,7 +348,7 @@ describe('PrometheusDatasource', function() {
interval: '5s', interval: '5s',
}; };
// times get rounded up to interval // times get rounded up to interval
var urlExpected = 'proxied/api/v1/query_range?query=test&start=100&end=450&step=50'; var urlExpected = 'proxied/api/v1/query_range?query=test&start=50&end=450&step=50';
ctx.$httpBackend.expect('GET', urlExpected).respond(response); ctx.$httpBackend.expect('GET', urlExpected).respond(response);
ctx.ds.query(query); ctx.ds.query(query);
ctx.$httpBackend.verifyNoOutstandingExpectation(); ctx.$httpBackend.verifyNoOutstandingExpectation();
...@@ -384,8 +384,8 @@ describe('PrometheusDatasource', function() { ...@@ -384,8 +384,8 @@ describe('PrometheusDatasource', function() {
], ],
interval: '10s', interval: '10s',
}; };
// times get rounded up to interval // times get aligned to interval
var urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=200&end=500&step=100'; var urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=0&end=500&step=100';
ctx.$httpBackend.expect('GET', urlExpected).respond(response); ctx.$httpBackend.expect('GET', urlExpected).respond(response);
ctx.ds.query(query); ctx.ds.query(query);
ctx.$httpBackend.verifyNoOutstandingExpectation(); ctx.$httpBackend.verifyNoOutstandingExpectation();
...@@ -511,7 +511,7 @@ describe('PrometheusDatasource', function() { ...@@ -511,7 +511,7 @@ describe('PrometheusDatasource', function() {
}, },
}; };
var urlExpected = var urlExpected =
'proxied/api/v1/query_range?query=' + encodeURIComponent('rate(test[100s])') + '&start=200&end=500&step=100'; 'proxied/api/v1/query_range?query=' + encodeURIComponent('rate(test[100s])') + '&start=0&end=500&step=100';
ctx.$httpBackend.expect('GET', urlExpected).respond(response); ctx.$httpBackend.expect('GET', urlExpected).respond(response);
ctx.ds.query(query); ctx.ds.query(query);
ctx.$httpBackend.verifyNoOutstandingExpectation(); ctx.$httpBackend.verifyNoOutstandingExpectation();
...@@ -539,7 +539,7 @@ describe('PrometheusDatasource', function() { ...@@ -539,7 +539,7 @@ describe('PrometheusDatasource', function() {
}, },
}; };
var urlExpected = var urlExpected =
'proxied/api/v1/query_range?query=' + encodeURIComponent('rate(test[50s])') + '&start=100&end=450&step=50'; 'proxied/api/v1/query_range?query=' + encodeURIComponent('rate(test[50s])') + '&start=50&end=450&step=50';
ctx.$httpBackend.expect('GET', urlExpected).respond(response); ctx.$httpBackend.expect('GET', urlExpected).respond(response);
ctx.ds.query(query); ctx.ds.query(query);
ctx.$httpBackend.verifyNoOutstandingExpectation(); ctx.$httpBackend.verifyNoOutstandingExpectation();
...@@ -613,29 +613,6 @@ describe('PrometheusDatasource', function() { ...@@ -613,29 +613,6 @@ describe('PrometheusDatasource', function() {
expect(query.scopedVars.__interval_ms.value).to.be(5 * 1000); expect(query.scopedVars.__interval_ms.value).to.be(5 * 1000);
}); });
}); });
describe('Step alignment of intervals', function() {
it('does not modify already aligned intervals with perfect step', function() {
const range = ctx.ds.clampRange(0, 3, 3);
expect(range.start).to.be(0);
expect(range.end).to.be(3);
});
it('does modify end-aligned intervals to reflect number of steps possible', function() {
const range = ctx.ds.clampRange(1, 6, 3);
expect(range.start).to.be(3);
expect(range.end).to.be(6);
});
it('does align intervals that are a multiple of steps', function() {
const range = ctx.ds.clampRange(1, 4, 3);
expect(range.start).to.be(3);
expect(range.end).to.be(6);
});
it('does align intervals that are not a multiple of steps', function() {
const range = ctx.ds.clampRange(1, 5, 3);
expect(range.start).to.be(3);
expect(range.end).to.be(6);
});
});
}); });
describe('PrometheusDatasource for POST', function() { describe('PrometheusDatasource for POST', function() {
...@@ -667,7 +644,7 @@ describe('PrometheusDatasource for POST', function() { ...@@ -667,7 +644,7 @@ describe('PrometheusDatasource for POST', function() {
var urlExpected = 'proxied/api/v1/query_range'; var urlExpected = 'proxied/api/v1/query_range';
var dataExpected = $.param({ var dataExpected = $.param({
query: 'test{job="testjob"}', query: 'test{job="testjob"}',
start: 2 * 60, start: 1 * 60,
end: 3 * 60, end: 3 * 60,
step: 60, step: 60,
}); });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment