From 36f08994ccd3f2275472ef39a1714a7c50bba1c5 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 5 Jul 2018 16:12:03 +0300 Subject: [PATCH] prometheus heatmap: fix unhandled error when some points are missing --- .../prometheus/result_transformer.ts | 18 ++++++++--- .../specs/result_transformer.jest.ts | 30 +++++++++++++++++++ .../app/plugins/panel/heatmap/heatmap_ctrl.ts | 4 +++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/public/app/plugins/datasource/prometheus/result_transformer.ts b/public/app/plugins/datasource/prometheus/result_transformer.ts index 7f5430bf7d6..b6d8a32af5f 100644 --- a/public/app/plugins/datasource/prometheus/result_transformer.ts +++ b/public/app/plugins/datasource/prometheus/result_transformer.ts @@ -28,15 +28,20 @@ export class ResultTransformer { } } - transformMetricData(md, options, start, end) { + transformMetricData(metricData, options, start, end) { let dps = [], metricLabel = null; - metricLabel = this.createMetricLabel(md.metric, options); + metricLabel = this.createMetricLabel(metricData.metric, options); const stepMs = parseInt(options.step) * 1000; let baseTimestamp = start * 1000; - for (let value of md.values) { + + if (metricData.values === undefined) { + throw new Error('Prometheus heatmap error: data should be a time series'); + } + + for (let value of metricData.values) { let dp_value = parseFloat(value[1]); if (_.isNaN(dp_value)) { dp_value = null; @@ -164,8 +169,13 @@ export class ResultTransformer { for (let i = seriesList.length - 1; i > 0; i--) { let topSeries = seriesList[i].datapoints; let bottomSeries = seriesList[i - 1].datapoints; + if (!topSeries || !bottomSeries) { + throw new Error('Prometheus heatmap transform error: data should be a time series'); + } + for (let j = 0; j < topSeries.length; j++) { - topSeries[j][0] -= bottomSeries[j][0]; + const bottomPoint = bottomSeries[j] || [0]; + topSeries[j][0] -= bottomPoint[0]; } } diff --git a/public/app/plugins/datasource/prometheus/specs/result_transformer.jest.ts b/public/app/plugins/datasource/prometheus/specs/result_transformer.jest.ts index b94cca79059..c0f2609f5b4 100644 --- a/public/app/plugins/datasource/prometheus/specs/result_transformer.jest.ts +++ b/public/app/plugins/datasource/prometheus/specs/result_transformer.jest.ts @@ -126,6 +126,36 @@ describe('Prometheus Result Transformer', () => { { target: '3', datapoints: [[10, 1445000010000], [0, 1445000020000], [10, 1445000030000]] }, ]); }); + + it('should handle missing datapoints', () => { + const seriesList = [ + { datapoints: [[1, 1000], [2, 2000]] }, + { datapoints: [[2, 1000], [5, 2000], [1, 3000]] }, + { datapoints: [[3, 1000], [7, 2000]] }, + ]; + const expected = [ + { datapoints: [[1, 1000], [2, 2000]] }, + { datapoints: [[1, 1000], [3, 2000], [1, 3000]] }, + { datapoints: [[1, 1000], [2, 2000]] }, + ]; + const result = ctx.resultTransformer.transformToHistogramOverTime(seriesList); + expect(result).toEqual(expected); + }); + + it('should throw error when data in wrong format', () => { + const seriesList = [{ rows: [] }, { datapoints: [] }]; + expect(() => { + ctx.resultTransformer.transformToHistogramOverTime(seriesList); + }).toThrow(); + }); + + it('should throw error when prometheus returned non-timeseries', () => { + // should be { metric: {}, values: [] } for timeseries + const metricData = { metric: {}, value: [] }; + expect(() => { + ctx.resultTransformer.transformMetricData(metricData, { step: 1 }, 1000, 2000); + }).toThrow(); + }); }); describe('When resultFormat is time series', () => { diff --git a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts index 11fbad47b99..31a5afa630e 100644 --- a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts +++ b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts @@ -302,6 +302,10 @@ export class HeatmapCtrl extends MetricsPanelCtrl { } seriesHandler(seriesData) { + if (seriesData.datapoints === undefined) { + throw new Error('Heatmap error: data should be a time series'); + } + let series = new TimeSeries({ datapoints: seriesData.datapoints, alias: seriesData.target,