jnfrati 4 лет назад
Родитель
Сommit
d435cb40fc

+ 14 - 19
dashboard/src/main/home/cluster-dashboard/expanded-chart/metrics/AreaChart.tsx

@@ -5,7 +5,6 @@ import { scaleTime, scaleLinear } from "@visx/scale";
 import { AxisLeft, AxisBottom } from "@visx/axis";
 
 import {
-  withTooltip,
   Tooltip,
   TooltipWithBounds,
   defaultStyles,
@@ -14,25 +13,20 @@ import {
 
 import { GridRows, GridColumns } from "@visx/grid";
 
-import { WithTooltipProvidedProps } from "@visx/tooltip/lib/enhancers/withTooltip";
 import { localPoint } from "@visx/event";
 import { LinearGradient } from "@visx/gradient";
 import { max, extent, bisector } from "d3-array";
 import { timeFormat } from "d3-time-format";
+import { NormalizedMetricsData } from "./types";
 
 /*
 export const accentColor = '#f5cb42';
 export const accentColorDark = '#949eff';
 */
 
-export type MetricsData = {
-  date: number; // unix timestamp
-  value: number; // value
-};
-
-type TooltipData = MetricsData;
+type TooltipData = NormalizedMetricsData;
 
-var globalData: MetricsData[];
+var globalData: NormalizedMetricsData[];
 
 export const background = "#3b697800";
 export const background2 = "#20405100";
@@ -60,15 +54,16 @@ const formats: { [range: string]: (date: Date) => string } = {
 };
 
 // accessors
-const getDate = (d: MetricsData) => new Date(d.date * 1000);
-const getValue = (d: MetricsData) => d.value;
+const getDate = (d: NormalizedMetricsData) => new Date(d.date * 1000);
+const getValue = (d: NormalizedMetricsData) => d.value;
 
-const bisectDate = bisector<MetricsData, Date>((d) => new Date(d.date * 1000))
-  .left;
+const bisectDate = bisector<NormalizedMetricsData, Date>(
+  (d) => new Date(d.date * 1000)
+).left;
 
 export type AreaProps = {
-  data: MetricsData[];
-  hpaData: MetricsData[];
+  data: NormalizedMetricsData[];
+  hpaData: NormalizedMetricsData[];
   resolution: string;
   width: number;
   height: number;
@@ -93,7 +88,7 @@ const AreaChart: React.FunctionComponent<AreaProps> = ({
     tooltipData,
     tooltipTop,
     tooltipLeft,
-  } = useTooltip<MetricsData>();
+  } = useTooltip<NormalizedMetricsData>();
 
   // bounds
   const innerWidth = width - margin.left - margin.right - 40;
@@ -159,7 +154,7 @@ const AreaChart: React.FunctionComponent<AreaProps> = ({
         tooltipTop: valueScale(getValue(d)) || 0,
       });
     },
-    [showTooltip, valueScale, dateScale, width, height, data]
+    [showTooltip, valueScale, dateScale, width, height, data, hpaData]
   );
 
   if (width == 0 || height == 0 || width < 10) {
@@ -207,7 +202,7 @@ const AreaChart: React.FunctionComponent<AreaProps> = ({
           strokeOpacity={0.2}
           pointerEvents="none"
         />
-        <AreaClosed<MetricsData>
+        <AreaClosed<NormalizedMetricsData>
           data={data}
           x={(d) => dateScale(getDate(d)) ?? 0}
           y={(d) => valueScale(getValue(d)) ?? 0}
@@ -218,7 +213,7 @@ const AreaChart: React.FunctionComponent<AreaProps> = ({
           fill="url(#area-gradient)"
           curve={curveMonotoneX}
         />
-        <LinePath<MetricsData>
+        <LinePath<NormalizedMetricsData>
           stroke="#fd0101"
           strokeWidth={2}
           data={hpaData}

+ 76 - 0
dashboard/src/main/home/cluster-dashboard/expanded-chart/metrics/MetricNormalizer.ts

@@ -0,0 +1,76 @@
+import {
+  GenericMetricResponse,
+  NormalizedMetricsData,
+  MetricsMemoryDataResponse,
+  MetricsCPUDataResponse,
+  MetricsNetworkDataResponse,
+  MetricsNGINXErrorsDataResponse,
+  AvailableMetrics,
+} from "./types";
+
+export class MetricNormalizer {
+  metric_results: GenericMetricResponse["results"];
+  kind: AvailableMetrics;
+
+  constructor(data: GenericMetricResponse[], kind: AvailableMetrics) {
+    if (!Array.isArray(data) || !data[0]?.results) {
+      throw new Error("Failed parsing response" + JSON.stringify(data));
+    }
+    this.metric_results = data[0].results;
+    this.kind = kind;
+  }
+
+  getParsedData(): NormalizedMetricsData[] {
+    if (this.kind.includes("cpu")) {
+      return this.parseCPUMetrics(this.metric_results);
+    }
+    if (this.kind.includes("memory")) {
+      return this.parseMemoryMetrics(this.metric_results);
+    }
+    if (this.kind.includes("network")) {
+      return this.parseNetworkMetrics(this.metric_results);
+    }
+    if (this.kind.includes("nginx:errors")) {
+      return this.parseNGINXErrorsMetrics(this.metric_results);
+    }
+    return [];
+  }
+
+  private parseCPUMetrics(arr: MetricsCPUDataResponse["results"]) {
+    return arr.map((d) => {
+      return {
+        date: d.date,
+        value: parseFloat(d.cpu),
+      };
+    });
+  }
+
+  private parseMemoryMetrics(arr: MetricsMemoryDataResponse["results"]) {
+    return arr.map((d) => {
+      return {
+        date: d.date,
+        value: parseFloat(d.memory) / (1024 * 1024), // put units in Mi
+      };
+    });
+  }
+
+  private parseNetworkMetrics(arr: MetricsNetworkDataResponse["results"]) {
+    return arr.map((d) => {
+      return {
+        date: d.date,
+        value: parseFloat(d.bytes) / 1024, // put units in Ki
+      };
+    });
+  }
+
+  private parseNGINXErrorsMetrics(
+    arr: MetricsNGINXErrorsDataResponse["results"]
+  ) {
+    return arr.map((d) => {
+      return {
+        date: d.date,
+        value: parseFloat(d.error_pct), // put units in Ki
+      };
+    });
+  }
+}

+ 75 - 212
dashboard/src/main/home/cluster-dashboard/expanded-chart/metrics/MetricsSection.tsx

@@ -1,11 +1,4 @@
-import React, {
-  Component,
-  Props,
-  useContext,
-  useEffect,
-  useMemo,
-  useState,
-} from "react";
+import React, { useContext, useEffect, useState } from "react";
 import styled from "styled-components";
 import ParentSize from "@visx/responsive/lib/components/ParentSize";
 
@@ -17,69 +10,14 @@ import { ChartType, StorageType } from "shared/types";
 import TabSelector from "components/TabSelector";
 import Loading from "components/Loading";
 import SelectRow from "components/values-form/SelectRow";
-import AreaChart, { MetricsData } from "./AreaChart";
-import useAuth from "shared/auth/useAuth";
+import AreaChart from "./AreaChart";
+import { MetricNormalizer } from "./MetricNormalizer";
+import { AvailableMetrics, NormalizedMetricsData } from "./types";
 
 type PropsType = {
   currentChart: ChartType;
 };
 
-type StateType = {
-  controllerOptions: any[];
-  ingressOptions: any[];
-  selectedController: any;
-  selectedIngress: any;
-  pods: any[];
-  selectedPod: string;
-  selectedRange: string;
-  selectedMetric: string;
-  selectedMetricLabel: string;
-  controllerDropdownExpanded: boolean;
-  podDropdownExpanded: boolean;
-  dropdownExpanded: boolean;
-  data: MetricsData[];
-  showMetricsSettings: boolean;
-  metricsOptions: MetricsOption[];
-  isLoading: number;
-};
-
-type MetricsCPUDataResponse = {
-  pod?: string;
-  results: {
-    date: number;
-    cpu: string;
-  }[];
-}[];
-
-type MetricsMemoryDataResponse = {
-  pod?: string;
-  results: {
-    date: number;
-    memory: string;
-  }[];
-}[];
-
-type MetricsNetworkDataResponse = {
-  pod?: string;
-  results: {
-    date: number;
-    bytes: string;
-  }[];
-}[];
-
-type MetricsNGINXErrorsDataResponse = {
-  pod?: string;
-  results: {
-    date: number;
-    error_pct: string;
-  }[];
-}[];
-
-type MetricsOption = {
-  value: string;
-  label: string;
-};
-
 const resolutions: { [range: string]: string } = {
   "1H": "15s",
   "6H": "15s",
@@ -109,7 +47,7 @@ const MetricsSection: React.FunctionComponent<PropsType> = ({
     "CPU Utilization (vCPUs)"
   );
   const [dropdownExpanded, setDropdownExpanded] = useState(false);
-  const [data, setData] = useState<MetricsData[]>([]);
+  const [data, setData] = useState<NormalizedMetricsData[]>([]);
   const [showMetricsSettings, setShowMetricsSettings] = useState(false);
   const [metricsOptions, setMetricsOptions] = useState([
     { value: "cpu", label: "CPU Utilization (vCPUs)" },
@@ -161,11 +99,10 @@ const MetricsSection: React.FunctionComponent<PropsType> = ({
           setControllerOptions([]);
         })
         .finally(() => {
-          setIsLoading((prev) => {
-            return prev - 1;
-          });
+          setIsLoading((prev) => prev - 1);
         });
     }
+
     setIsLoading((prev) => prev + 1);
 
     api
@@ -254,85 +191,79 @@ const MetricsSection: React.FunctionComponent<PropsType> = ({
       });
   };
 
-  const getAutoscalingThreshold = (
+  const getAutoscalingThreshold = async (
     kind: string,
     shouldsum: boolean,
     namespace: string,
     start: number,
     end: number
   ) => {
-    return api.getMetrics(
-      "<token>",
-      {
-        cluster_id: currentCluster.id,
-        metric: kind,
-        shouldsum: shouldsum,
-        kind: selectedController?.kind,
-        name: selectedController?.metadata.name,
-        namespace: namespace,
-        startrange: start,
-        endrange: end,
-        resolution: resolutions[selectedRange],
-        pods: [],
-      },
-      {
-        id: currentProject.id,
-      }
-    );
-  };
-
-  const parseCPUMetrics = (arr: Array<{ date: number; cpu: string }>) => {
-    return arr.map((d) => {
-      return {
-        date: d.date,
-        value: parseFloat(d.cpu),
-      };
-    });
-  };
+    setIsLoading((prev) => prev + 1);
+    setHpaData([]);
+    try {
+      const res = await api.getMetrics(
+        "<token>",
+        {
+          cluster_id: currentCluster.id,
+          metric: kind,
+          shouldsum: shouldsum,
+          kind: selectedController?.kind,
+          name: selectedController?.metadata.name,
+          namespace: namespace,
+          startrange: start,
+          endrange: end,
+          resolution: resolutions[selectedRange],
+          pods: [],
+        },
+        {
+          id: currentProject.id,
+        }
+      );
 
-  const parseMemoryMetrics = (arr: Array<{ date: number; memory: string }>) => {
-    return arr.map((d) => {
-      return {
-        date: d.date,
-        value: parseFloat(d.memory) / (1024 * 1024), // put units in Mi
-      };
-    });
+      if (!Array.isArray(res.data) || !res.data[0]?.results) {
+        return;
+      }
+      return;
+    } catch (error) {
+      console.error(error);
+    } finally {
+      setIsLoading((prev) => prev - 1);
+    }
   };
 
-  const getMetrics = () => {
+  const getMetrics = async () => {
     if (pods?.length == 0) {
       return;
     }
+    try {
+      let shouldsum = selectedPod === "All";
+      let namespace = currentChart.namespace;
 
-    const kind = selectedMetric;
-    let shouldsum = selectedPod === "All";
-    let namespace = currentChart.namespace;
-
-    // calculate start and end range
-    const d = new Date();
-    const end = Math.round(d.getTime() / 1000);
-    const start = end - secondsBeforeNow[selectedRange];
+      // calculate start and end range
+      const d = new Date();
+      const end = Math.round(d.getTime() / 1000);
+      const start = end - secondsBeforeNow[selectedRange];
 
-    let podNames = [] as string[];
+      let podNames = [] as string[];
 
-    if (!shouldsum) {
-      podNames = [selectedPod];
-    }
+      if (!shouldsum) {
+        podNames = [selectedPod];
+      }
 
-    if (selectedMetric == "nginx:errors") {
-      podNames = [selectedIngress?.name];
-      namespace = selectedIngress?.namespace || "default";
-      shouldsum = false;
-    }
+      if (selectedMetric == "nginx:errors") {
+        podNames = [selectedIngress?.name];
+        namespace = selectedIngress?.namespace || "default";
+        shouldsum = false;
+      }
 
-    setIsLoading((prev) => prev + 1);
+      setIsLoading((prev) => prev + 1);
+      setData([]);
 
-    api
-      .getMetrics(
+      const res = await api.getMetrics(
         "<token>",
         {
           cluster_id: currentCluster.id,
-          metric: kind,
+          metric: selectedMetric,
           shouldsum: shouldsum,
           kind: selectedController?.kind,
           name: selectedController?.metadata.name,
@@ -345,91 +276,23 @@ const MetricsSection: React.FunctionComponent<PropsType> = ({
         {
           id: currentProject.id,
         }
-      )
-      .then((res) => {
-        if (!Array.isArray(res.data) || !res.data[0]?.results) {
-          return;
-        }
+      );
 
-        // transform the metrics to expected form
-        if (kind == "cpu") {
-          let data = res.data as MetricsCPUDataResponse;
-
-          // if summed, just look at the first data
-          let tData = parseCPUMetrics(data[0].results);
-          getAutoscalingThreshold(
-            "cpu_hpa_threshold",
-            shouldsum,
-            namespace,
-            start,
-            end
-          ).then((res) => {
-            const data = res.data as MetricsCPUDataResponse;
-            setHpaData(parseCPUMetrics(data[0].results));
-            setData(tData);
-          });
-        } else if (kind == "memory") {
-          let data = res.data as MetricsMemoryDataResponse;
-
-          let tData = parseMemoryMetrics(data[0].results);
-          getAutoscalingThreshold(
-            "memory_hpa_threshold",
-            shouldsum,
-            namespace,
-            start,
-            end
-          ).then((res) => {
-            const data = res.data as MetricsMemoryDataResponse;
-            setHpaData(parseMemoryMetrics(data[0].results));
-            setData(tData);
-          });
-          setData(tData);
-        } else if (kind == "network") {
-          let data = res.data as MetricsNetworkDataResponse;
-
-          let tData = data[0].results.map(
-            (
-              d: {
-                date: number;
-                bytes: string;
-              },
-              i: number
-            ) => {
-              return {
-                date: d.date,
-                value: parseFloat(d.bytes) / 1024, // put units in Ki
-              };
-            }
-          );
-
-          setData(tData);
-        } else if (kind == "nginx:errors") {
-          let data = res.data as MetricsNGINXErrorsDataResponse;
-
-          let tData = data[0].results.map(
-            (
-              d: {
-                date: number;
-                error_pct: string;
-              },
-              i: number
-            ) => {
-              return {
-                date: d.date,
-                value: parseFloat(d.error_pct), // put units in Ki
-              };
-            }
-          );
-
-          setData(tData);
-        }
-      })
-      .catch((err) => {
-        setCurrentError(JSON.stringify(err));
-      })
-      .finally(() => {
-        setIsLoading((prev) => prev - 1);
-      });
+      let tData = [];
+
+      const metrics = new MetricNormalizer(
+        res.data,
+        selectedMetric as AvailableMetrics
+      );
+
+      tData = metrics.getParsedData();
+      // transform the metrics to expected form
+      setData(tData);
+    } catch (error) {
+      setCurrentError(JSON.stringify(error));
+    } finally {
+      setIsLoading((prev) => prev - 1);
+    }
   };
 
   useEffect(() => {

+ 49 - 0
dashboard/src/main/home/cluster-dashboard/expanded-chart/metrics/types.ts

@@ -0,0 +1,49 @@
+export type MetricsCPUDataResponse = {
+  pod?: string;
+  results: {
+    date: number;
+    cpu: string;
+  }[];
+};
+
+export type MetricsMemoryDataResponse = {
+  pod?: string;
+  results: {
+    date: number;
+    memory: string;
+  }[];
+};
+
+export type MetricsNetworkDataResponse = {
+  pod?: string;
+  results: {
+    date: number;
+    bytes: string;
+  }[];
+};
+
+export type MetricsNGINXErrorsDataResponse = {
+  pod?: string;
+  results: {
+    date: number;
+    error_pct: string;
+  }[];
+};
+
+export type GenericMetricResponse = {
+  pod?: string;
+  results: {
+    date: number;
+    cpu: string;
+    memory: string;
+    bytes: string;
+    error_pct: string;
+  }[];
+};
+
+export type NormalizedMetricsData = {
+  date: number; // unix timestamp
+  value: number; // value
+};
+
+export type AvailableMetrics = "cpu" | "memory" | "network" | "nginx:errors";