Просмотр исходного кода

prometheus heatmap: fix unhandled error when some points are missing

Alexander Zobnin 7 лет назад
Родитель
Сommit
36f08994cc

+ 14 - 4
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 = [],
     let dps = [],
       metricLabel = null;
       metricLabel = null;
 
 
-    metricLabel = this.createMetricLabel(md.metric, options);
+    metricLabel = this.createMetricLabel(metricData.metric, options);
 
 
     const stepMs = parseInt(options.step) * 1000;
     const stepMs = parseInt(options.step) * 1000;
     let baseTimestamp = start * 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]);
       let dp_value = parseFloat(value[1]);
       if (_.isNaN(dp_value)) {
       if (_.isNaN(dp_value)) {
         dp_value = null;
         dp_value = null;
@@ -164,8 +169,13 @@ export class ResultTransformer {
     for (let i = seriesList.length - 1; i > 0; i--) {
     for (let i = seriesList.length - 1; i > 0; i--) {
       let topSeries = seriesList[i].datapoints;
       let topSeries = seriesList[i].datapoints;
       let bottomSeries = seriesList[i - 1].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++) {
       for (let j = 0; j < topSeries.length; j++) {
-        topSeries[j][0] -= bottomSeries[j][0];
+        const bottomPoint = bottomSeries[j] || [0];
+        topSeries[j][0] -= bottomPoint[0];
       }
       }
     }
     }
 
 

+ 30 - 0
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]] },
         { 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', () => {
   describe('When resultFormat is time series', () => {

+ 4 - 0
public/app/plugins/panel/heatmap/heatmap_ctrl.ts

@@ -302,6 +302,10 @@ export class HeatmapCtrl extends MetricsPanelCtrl {
   }
   }
 
 
   seriesHandler(seriesData) {
   seriesHandler(seriesData) {
+    if (seriesData.datapoints === undefined) {
+      throw new Error('Heatmap error: data should be a time series');
+    }
+
     let series = new TimeSeries({
     let series = new TimeSeries({
       datapoints: seriesData.datapoints,
       datapoints: seriesData.datapoints,
       alias: seriesData.target,
       alias: seriesData.target,