fix(metrics): node chart race condition EE-5447 (#9252)

pull/9268/head
Dakota Walsh 1 year ago committed by GitHub
parent fc89066846
commit 88de50649f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -26,95 +26,93 @@
<kubernetes-view-loading view-ready="ctrl.state.viewReady"></kubernetes-view-loading>
<div ng-if="ctrl.state.viewReady">
<information-panel ng-if="!ctrl.state.getMetrics" title-text="Unable to retrieve container metrics">
<span class="small text-warning vertical-center">
<pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon>
Portainer was unable to retrieve any metrics associated to that container. Please contact your administrator to ensure that the Kubernetes metrics feature is properly
configured.
</span>
</information-panel>
<div class="row" ng-if="ctrl.state.getMetrics">
<div class="col-md-12">
<rd-widget>
<div class="toolBar px-5 pt-5">
<div class="toolBarTitle flex">
<div class="widget-icon space-right">
<pr-icon icon="'info'"></pr-icon>
</div>
<span class="vertical-center"> About statistics </span>
<information-panel ng-if="!ctrl.state.getMetrics" title-text="Unable to retrieve container metrics">
<span class="small text-warning vertical-center">
<pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon>
Portainer was unable to retrieve any metrics associated to that container. Please contact your administrator to ensure that the Kubernetes metrics feature is properly
configured.
</span>
</information-panel>
<div class="row" ng-if="ctrl.state.getMetrics">
<div class="col-md-12">
<rd-widget>
<div class="toolBar px-5 pt-5">
<div class="toolBarTitle flex">
<div class="widget-icon space-right">
<pr-icon icon="'info'"></pr-icon>
</div>
<span class="vertical-center"> About statistics </span>
</div>
<rd-widget-body>
<form class="form-horizontal">
<div class="form-group">
<div class="col-sm-12">
<span class="small text-warning">
This view displays real-time statistics about the container <b>{{ ctrl.state.transition.containerName | trimcontainername }}</b
>.
</span>
</div>
</div>
<div class="form-group">
<label for="refreshRate" class="col-sm-3 col-md-2 col-lg-2 margin-sm-top control-label text-left"> Refresh rate </label>
<div class="col-sm-3 col-md-2">
<select id="refreshRate" ng-model="ctrl.state.refreshRate" ng-change="ctrl.changeUpdateRepeater()" class="form-control">
<option value="30">30s</option>
<option value="60">60s</option>
</select>
</div>
<span>
<pr-icon id="refreshRateChange" icon="'check'" mode="'success'" size="'sm'"></pr-icon>
</div>
<rd-widget-body>
<form class="form-horizontal">
<div class="form-group">
<div class="col-sm-12">
<span class="small text-warning">
This view displays real-time statistics about the container <b>{{ ctrl.state.transition.containerName | trimcontainername }}</b
>.
</span>
</div>
<div class="form-group" ng-if="ctrl.state.networkStatsUnavailable">
<div class="col-sm-12">
<span class="small text-muted">
<pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon>
Network stats are unavailable for this container.
</span>
</div>
</div>
<div class="form-group">
<label for="refreshRate" class="col-sm-3 col-md-2 col-lg-2 margin-sm-top control-label text-left"> Refresh rate </label>
<div class="col-sm-3 col-md-2">
<select id="refreshRate" ng-model="ctrl.state.refreshRate" ng-change="ctrl.changeUpdateRepeater()" class="form-control">
<option value="30">30s</option>
<option value="60">60s</option>
</select>
</div>
<span>
<pr-icon id="refreshRateChange" icon="'check'" mode="'success'" size="'sm'"></pr-icon>
</span>
</div>
<div class="form-group" ng-if="ctrl.state.networkStatsUnavailable">
<div class="col-sm-12">
<span class="small text-muted">
<pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon>
Network stats are unavailable for this container.
</span>
</div>
</form>
</rd-widget-body>
</rd-widget>
</div>
</div>
</form>
</rd-widget-body>
</rd-widget>
</div>
</div>
<div class="row" ng-if="ctrl.state.getMetrics">
<div class="col-lg-6 col-md-12 col-sm-12">
<rd-widget>
<div class="toolBar px-5 pt-5">
<div class="toolBarTitle flex">
<div class="widget-icon space-right">
<pr-icon icon="'svg-memory'"></pr-icon>
</div>
<span class="vertical-center"> Memory usage </span>
<div class="row" ng-if="ctrl.state.getMetrics">
<div class="col-lg-6 col-md-12 col-sm-12">
<rd-widget>
<div class="toolBar px-5 pt-5">
<div class="toolBarTitle flex">
<div class="widget-icon space-right">
<pr-icon icon="'svg-memory'"></pr-icon>
</div>
<span class="vertical-center"> Memory usage </span>
</div>
<rd-widget-body>
<div class="chart-container" style="position: relative">
<canvas id="memoryChart" width="770" height="300"></canvas>
</div>
</rd-widget-body>
</rd-widget>
</div>
<div class="col-lg-6 col-md-12 col-sm-12" ng-if="!ctrl.state.networkStatsUnavailable">
<rd-widget>
<div class="toolBar px-5 pt-5">
<div class="toolBarTitle flex">
<div class="widget-icon space-right">
<pr-icon icon="'cpu'"></pr-icon>
</div>
<span class="vertical-center"> CPU usage </span>
</div>
</div>
<rd-widget-body>
<div class="chart-container" style="position: relative">
<canvas id="memoryChart" width="770" height="300"></canvas>
</div>
<rd-widget-body>
<div class="chart-container" style="position: relative">
<canvas id="cpuChart" width="770" height="300"></canvas>
</rd-widget-body>
</rd-widget>
</div>
<div class="col-lg-6 col-md-12 col-sm-12" ng-if="!ctrl.state.networkStatsUnavailable">
<rd-widget>
<div class="toolBar px-5 pt-5">
<div class="toolBarTitle flex">
<div class="widget-icon space-right">
<pr-icon icon="'cpu'"></pr-icon>
</div>
</rd-widget-body>
</rd-widget>
</div>
<span class="vertical-center"> CPU usage </span>
</div>
</div>
<rd-widget-body>
<div class="chart-container" style="position: relative">
<canvas id="cpuChart" width="770" height="300"></canvas>
</div>
</rd-widget-body>
</rd-widget>
</div>
</div>

@ -19,6 +19,7 @@ class KubernetesApplicationStatsController {
this.ChartService = ChartService;
this.onInit = this.onInit.bind(this);
this.initCharts = this.initCharts.bind(this);
}
changeUpdateRepeater() {
@ -68,17 +69,26 @@ class KubernetesApplicationStatsController {
}
initCharts() {
const cpuChartCtx = $('#cpuChart');
const cpuChart = this.ChartService.CreateCPUChart(cpuChartCtx);
this.cpuChart = cpuChart;
const memoryChartCtx = $('#memoryChart');
const memoryChart = this.ChartService.CreateMemoryChart(memoryChartCtx);
this.memoryChart = memoryChart;
this.updateCPUChart();
this.updateMemoryChart();
this.setUpdateRepeater();
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() {

@ -15,86 +15,84 @@
<kubernetes-view-loading view-ready="ctrl.state.viewReady"></kubernetes-view-loading>
<div ng-if="ctrl.state.viewReady">
<information-panel ng-if="!ctrl.state.getMetrics" title-text="Unable to retrieve node metrics">
<span class="small text-muted vertical-center">
<pr-icon icon="'alert-triangle'" mode="'primary'"></pr-icon>
Portainer was unable to retrieve any metrics associated to that node. Please contact your administrator to ensure that the Kubernetes metrics feature is properly configured.
</span>
</information-panel>
<div class="row" ng-if="ctrl.state.getMetrics">
<div class="col-md-12">
<rd-widget>
<div class="toolBar px-5 pt-5">
<div class="toolBarTitle flex">
<div class="widget-icon space-right">
<pr-icon icon="'info'"></pr-icon>
</div>
<span class="vertical-center"> About statistics </span>
<information-panel ng-if="!ctrl.state.getMetrics" title-text="Unable to retrieve node metrics">
<span class="small text-muted vertical-center">
<pr-icon icon="'alert-triangle'" mode="'primary'"></pr-icon>
Portainer was unable to retrieve any metrics associated to that node. Please contact your administrator to ensure that the Kubernetes metrics feature is properly configured.
</span>
</information-panel>
<div class="row" ng-if="ctrl.state.getMetrics">
<div class="col-md-12">
<rd-widget>
<div class="toolBar px-5 pt-5">
<div class="toolBarTitle flex">
<div class="widget-icon space-right">
<pr-icon icon="'info'"></pr-icon>
</div>
<span class="vertical-center"> About statistics </span>
</div>
<rd-widget-body>
<form class="form-horizontal">
<div class="form-group">
<div class="col-sm-12">
<span class="small text-muted">
This view displays real-time statistics about the node <b>{{ ctrl.state.transition.nodeName }}</b
>.
</span>
</div>
</div>
<div class="form-group">
<label for="refreshRate" class="col-sm-3 col-md-2 col-lg-2 margin-sm-top control-label text-left"> Refresh rate </label>
<div class="col-sm-3 col-md-2">
<select id="refreshRate" ng-model="ctrl.state.refreshRate" ng-change="ctrl.changeUpdateRepeater()" class="form-control">
<option value="30">30s</option>
<option value="60">60s</option>
</select>
</div>
<span>
<pr-icon id="refreshRateChange" icon="'check'" mode="'success'" style="display: none"></pr-icon>
</div>
<rd-widget-body>
<form class="form-horizontal">
<div class="form-group">
<div class="col-sm-12">
<span class="small text-muted">
This view displays real-time statistics about the node <b>{{ ctrl.state.transition.nodeName }}</b
>.
</span>
</div>
</form>
</rd-widget-body>
</rd-widget>
</div>
</div>
<div class="form-group">
<label for="refreshRate" class="col-sm-3 col-md-2 col-lg-2 margin-sm-top control-label text-left"> Refresh rate </label>
<div class="col-sm-3 col-md-2">
<select id="refreshRate" ng-model="ctrl.state.refreshRate" ng-change="ctrl.changeUpdateRepeater()" class="form-control">
<option value="30">30s</option>
<option value="60">60s</option>
</select>
</div>
<span>
<pr-icon id="refreshRateChange" icon="'check'" mode="'success'" style="display: none"></pr-icon>
</span>
</div>
</form>
</rd-widget-body>
</rd-widget>
</div>
</div>
<div class="row" ng-show="ctrl.state.getMetrics">
<div class="col-lg-6 col-md-12 col-sm-12">
<rd-widget>
<div class="toolBar px-5 pt-5">
<div class="toolBarTitle flex">
<div class="widget-icon space-right">
<pr-icon icon="'svg-memory'"></pr-icon>
</div>
<span class="vertical-center"> Memory usage </span>
<div class="row" ng-show="ctrl.state.getMetrics">
<div class="col-lg-6 col-md-12 col-sm-12">
<rd-widget>
<div class="toolBar px-5 pt-5">
<div class="toolBarTitle flex">
<div class="widget-icon space-right">
<pr-icon icon="'svg-memory'"></pr-icon>
</div>
<span class="vertical-center"> Memory usage </span>
</div>
<rd-widget-body>
<div class="chart-node" style="position: relative">
<canvas id="memoryChart" width="770" height="300"></canvas>
</div>
</rd-widget-body>
</rd-widget>
</div>
<div class="col-lg-6 col-md-12 col-sm-12">
<rd-widget>
<div class="toolBar px-5 pt-5">
<div class="toolBarTitle flex">
<div class="widget-icon space-right">
<pr-icon icon="'cpu'"></pr-icon>
</div>
<span class="vertical-center"> CPU usage </span>
</div>
</div>
<rd-widget-body>
<div class="chart-node" style="position: relative">
<canvas id="memoryChart" width="770" height="300"></canvas>
</div>
<rd-widget-body>
<div class="chart-node" style="position: relative">
<canvas id="cpuChart" width="770" height="300"></canvas>
</rd-widget-body>
</rd-widget>
</div>
<div class="col-lg-6 col-md-12 col-sm-12">
<rd-widget>
<div class="toolBar px-5 pt-5">
<div class="toolBarTitle flex">
<div class="widget-icon space-right">
<pr-icon icon="'cpu'"></pr-icon>
</div>
</rd-widget-body>
</rd-widget>
</div>
<span class="vertical-center"> CPU usage </span>
</div>
</div>
<rd-widget-body>
<div class="chart-node" style="position: relative">
<canvas id="cpuChart" width="770" height="300"></canvas>
</div>
</rd-widget-body>
</rd-widget>
</div>
</div>

@ -17,6 +17,7 @@ class KubernetesNodeStatsController {
this.ChartService = ChartService;
this.onInit = this.onInit.bind(this);
this.initCharts = this.initCharts.bind(this);
}
changeUpdateRepeater() {
@ -63,17 +64,20 @@ class KubernetesNodeStatsController {
}
initCharts() {
const cpuChartCtx = $('#cpuChart');
const cpuChart = this.ChartService.CreateCPUChart(cpuChartCtx);
this.cpuChart = cpuChart;
const memoryChartCtx = $('#memoryChart');
const memoryChart = this.ChartService.CreateMemoryChart(memoryChartCtx);
this.memoryChart = memoryChart;
this.updateCPUChart();
this.updateMemoryChart();
this.setUpdateRepeater();
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);
}
}, 200);
}
getStats() {
@ -84,7 +88,7 @@ class KubernetesNodeStatsController {
const memory = filesizeParser(stats.usage.memory);
const cpu = KubernetesResourceReservationHelper.parseCPU(stats.usage.cpu);
this.stats = {
read: stats.creationTimestamp,
read: stats.metadata.creationTimestamp,
MemoryUsage: memory,
CPUUsage: (cpu / this.nodeCPU) * 100,
};
@ -118,12 +122,6 @@ class KubernetesNodeStatsController {
this.nodeCPU = node.CPU || 1;
await this.getStats();
if (this.state.getMetrics) {
this.$document.ready(() => {
this.initCharts();
});
}
} else {
this.state.getMetrics = false;
}
@ -132,6 +130,11 @@ class KubernetesNodeStatsController {
this.Notifications.error('Failure', err, 'Unable to retrieve node stats');
} finally {
this.state.viewReady = true;
if (this.state.getMetrics) {
this.$document.ready(() => {
this.initCharts();
});
}
}
}

@ -189,7 +189,7 @@ class KubernetesConfigureController {
await getMetricsForAllNodes(this.endpoint.Id);
this.state.metrics.isServerRunning = true;
this.state.metrics.pending = false;
this.state.metrics.userClick = false;
this.state.metrics.userClick = true;
this.formValues.UseServerMetrics = true;
} catch (_) {
this.state.metrics.isServerRunning = false;

Loading…
Cancel
Save