portainer/portainer

View on GitHub
app/kubernetes/views/applications/stats/statsController.js

Summary

Maintainability
C
1 day
Test Coverage
import angular from 'angular';
import moment from 'moment';
import _ from 'lodash-es';
import filesizeParser from 'filesize-parser';
import KubernetesResourceReservationHelper from 'Kubernetes/helpers/resourceReservationHelper';
import KubernetesPodConverter from 'Kubernetes/pod/converter';
import { getMetricsForPod } from '@/react/kubernetes/services/service.ts';

class KubernetesApplicationStatsController {
  /* @ngInject */
  constructor($async, $state, $interval, $document, Notifications, KubernetesPodService, KubernetesNodeService, ChartService) {
    this.$async = $async;
    this.$state = $state;
    this.$interval = $interval;
    this.$document = $document;
    this.Notifications = Notifications;
    this.KubernetesPodService = KubernetesPodService;
    this.KubernetesNodeService = KubernetesNodeService;
    this.ChartService = ChartService;

    this.onInit = this.onInit.bind(this);
    this.initCharts = this.initCharts.bind(this);
  }

  changeUpdateRepeater() {
    var cpuChart = this.cpuChart;
    var memoryChart = this.memoryChart;

    this.stopRepeater();
    this.setUpdateRepeater(cpuChart, memoryChart);
    $('#refreshRateChange').show();
    $('#refreshRateChange').fadeOut(1500);
  }

  updateCPUChart() {
    const label = moment(this.stats.read).format('HH:mm:ss');

    this.ChartService.UpdateCPUChart(label, this.stats.CPUUsage, this.cpuChart);
  }

  updateMemoryChart() {
    const label = moment(this.stats.read).format('HH:mm:ss');

    this.ChartService.UpdateMemoryChart(label, this.stats.MemoryUsage, this.stats.MemoryCache, this.memoryChart);
  }

  stopRepeater() {
    var repeater = this.repeater;
    if (angular.isDefined(repeater)) {
      this.$interval.cancel(repeater);
      repeater = undefined;
    }
  }

  setUpdateRepeater() {
    const refreshRate = this.state.refreshRate;

    this.repeater = this.$interval(async () => {
      try {
        await this.getStats();

        this.updateCPUChart();
        this.updateMemoryChart();
      } catch (error) {
        this.stopRepeater();
        this.Notifications.error('Failure', error);
      }
    }, refreshRate * 1000);
  }

  initCharts() {
    let i = 0;
    const findCharts = setInterval(() => {
      let cpuChartCtx = $('#cpuChart');
      let memoryChartCtx = $('#memoryChart');
      if (cpuChartCtx.length !== 0 && memoryChartCtx.length !== 0) {
        const cpuChart = this.ChartService.CreateCPUChart(cpuChartCtx);
        this.cpuChart = cpuChart;
        const memoryChart = this.ChartService.CreateMemoryChart(memoryChartCtx);
        this.memoryChart = memoryChart;
        this.updateCPUChart();
        this.updateMemoryChart();
        this.setUpdateRepeater();
        clearInterval(findCharts);
        return;
      }
      i++;
      if (i >= 10) {
        clearInterval(findCharts);
      }
    }, 200);
  }

  getStats() {
    return this.$async(async () => {
      try {
        const stats = await getMetricsForPod(this.$state.params.endpointId, this.state.transition.namespace, this.state.transition.podName);
        const container = _.find(stats.containers, { name: this.state.transition.containerName });
        if (container) {
          const memory = filesizeParser(container.usage.memory);
          const cpu = KubernetesResourceReservationHelper.parseCPU(container.usage.cpu);
          this.stats = {
            read: stats.timestamp,
            preread: '',
            MemoryCache: 0,
            MemoryUsage: memory,
            NumProcs: '',
            isWindows: false,
            PreviousCPUTotalUsage: 0,
            CPUUsage: (cpu / this.nodeCPU) * 100,
            CPUCores: 0,
          };
        }
      } catch (err) {
        this.Notifications.error('Failure', err, 'Unable to retrieve application stats');
      }
    });
  }

  $onDestroy() {
    this.stopRepeater();
  }

  async onInit() {
    this.state = {
      autoRefresh: false,
      refreshRate: '30',
      viewReady: false,
      transition: {
        podName: this.$transition$.params().pod,
        containerName: this.$transition$.params().container,
        namespace: this.$transition$.params().namespace,
        applicationName: this.$transition$.params().name,
      },
      getMetrics: false,
    };

    try {
      await getMetricsForPod(this.$state.params.endpointId, this.state.transition.namespace, this.state.transition.podName);
    } catch (error) {
      this.state.getMetrics = false;
      this.state.viewReady = true;
      return;
    }

    try {
      const podRaw = await this.KubernetesPodService.get(this.state.transition.namespace, this.state.transition.podName);
      const pod = KubernetesPodConverter.apiToModel(podRaw.Raw);
      if (pod) {
        const node = await this.KubernetesNodeService.get(pod.Node);
        this.nodeCPU = node.CPU;
      } else {
        throw new Error('Unable to find pod');
      }
      await this.getStats();
      this.state.getMetrics = true;

      this.$document.ready(() => {
        this.initCharts();
      });
    } catch (err) {
      this.Notifications.error('Failure', err, 'Unable to retrieve application stats');
    } finally {
      this.state.viewReady = true;
    }
  }

  $onInit() {
    return this.$async(this.onInit);
  }
}

export default KubernetesApplicationStatsController;
angular.module('portainer.kubernetes').controller('KubernetesApplicationStatsController', KubernetesApplicationStatsController);