Skip to content

Commit 26b6a5e

Browse files
committed
Add donut graphs to deployments, replica sets, daemon sets and stateful
sets
1 parent 98abc4a commit 26b6a5e

File tree

6 files changed

+227
-0
lines changed

6 files changed

+227
-0
lines changed

library/Kubernetes/Donut.php

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php
2+
3+
/* Icinga Kubernetes Web | (c) 2023 Icinga GmbH | GPLv2 */
4+
5+
namespace Icinga\Module\Kubernetes;
6+
7+
use ipl\Html\BaseHtmlElement;
8+
use ipl\Html\Html;
9+
use ipl\Html\HtmlString;
10+
use ipl\Html\Table;
11+
12+
class Donut extends BaseHtmlElement
13+
{
14+
protected $tag = 'section';
15+
16+
protected $defaultAttributes = ['class' => 'donut'];
17+
18+
/**
19+
* The donut data
20+
*
21+
* @var iterable
22+
*/
23+
protected $data = [];
24+
25+
/**
26+
* @var string
27+
*/
28+
protected $heading;
29+
30+
/**
31+
* @var int
32+
*/
33+
protected $headingLevel = 2;
34+
35+
/**
36+
* @var callable
37+
*/
38+
protected $labelCallback;
39+
40+
/**
41+
* Set the data to display
42+
*
43+
* @param iterable $data
44+
*
45+
* @return $this
46+
*/
47+
public function setData(iterable $data)
48+
{
49+
$this->data = $data;
50+
51+
return $this;
52+
}
53+
54+
public function setHeading(string $heading, int $level)
55+
{
56+
$this->heading = $heading;
57+
$this->headingLevel = $level;
58+
59+
return $this;
60+
}
61+
62+
public function setLabelCallback(callable $callback)
63+
{
64+
$this->labelCallback = $callback;
65+
66+
return $this;
67+
}
68+
69+
public function assemble()
70+
{
71+
$donut = new \Icinga\Chart\Donut();
72+
$legend = new Table();
73+
74+
foreach ($this->data as $index => $value) {
75+
$donut->addSlice((int) $value, ['class' => 'segment-' . $index]);
76+
$legend->add(
77+
[
78+
Html::tag('span', ['class' => 'badge badge-' . $index]),
79+
call_user_func($this->labelCallback, $index),
80+
$value
81+
]
82+
);
83+
}
84+
85+
if ($this->heading === null) {
86+
$this->addHtml(Html::tag("h{$this->headingLevel}", $this->heading), new HtmlString($donut->render()), $legend);
87+
}
88+
}
89+
}

library/Kubernetes/Web/DaemonSetDetail.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Icinga\Module\Kubernetes\Web;
44

55
use Icinga\Module\Kubernetes\Common\Database;
6+
use Icinga\Module\Kubernetes\Donut;
67
use Icinga\Module\Kubernetes\Model\DaemonSet;
78
use Icinga\Module\Kubernetes\Model\Event;
89
use Icinga\Module\Kubernetes\Model\ReplicaSet;
@@ -49,6 +50,25 @@ protected function assemble()
4950
t('Created') => new TimeAgo($this->daemonSet->created->getTimestamp())
5051
]));
5152

53+
$data = [
54+
$this->daemonSet->number_available,
55+
$this->daemonSet->number_ready - $this->daemonSet->number_available,
56+
$this->daemonSet->desired_number_scheduled - $this->daemonSet->number_ready,
57+
$this->daemonSet->number_unavailable
58+
];
59+
$labels = [
60+
t('Available'),
61+
t('Ready but not yet available'),
62+
t('Not yet ready'),
63+
t('Not yet scheduled or failing')
64+
];
65+
$donut = (new Donut())
66+
->setData($data)
67+
->setLabelCallback(function ($index) use ($labels){
68+
return HtmlElement::create('span', null, $labels[$index]);
69+
});
70+
$this->addHtml($donut);
71+
5272
$this->addHtml(new ConditionTable($this->daemonSet, (new ReplicaSetCondition())->getColumnDefinitions()));
5373

5474
$this->addHtml(new HtmlElement(

library/Kubernetes/Web/DeploymentDetail.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Icinga\Module\Kubernetes\Web;
66

7+
use Icinga\Module\Kubernetes\Donut;
78
use Icinga\Module\Kubernetes\Model\Deployment;
89
use Icinga\Module\Kubernetes\Model\DeploymentCondition;
910
use ipl\Html\Attributes;
@@ -46,6 +47,28 @@ protected function assemble()
4647
t('Created') => new TimeAgo($this->deployment->created->getTimestamp())
4748
]));
4849

50+
$data = [
51+
$this->deployment->available_replicas,
52+
$this->deployment->desired_replicas - $this->deployment->actual_replicas,
53+
$this->deployment->actual_replicas - $this->deployment->ready_replicas,
54+
$this->deployment->unavailable_replicas,
55+
];
56+
57+
$donut = (new Donut())
58+
->setData($data)
59+
->setLabelCallback(function ($index) {
60+
$labels = [
61+
'Available',
62+
'Ready but not yet available',
63+
'Not yet ready',
64+
'Not yet scheduled or failing'
65+
];
66+
return HtmlElement::create('span',
67+
null,
68+
$labels[$index]);
69+
});
70+
$this->addHtml($donut);
71+
4972
$this->addHtml(new ConditionTable($this->deployment, (new DeploymentCondition())->getColumnDefinitions()));
5073

5174
$this->addHtml(new HtmlElement(

library/Kubernetes/Web/ReplicaSetDetail.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Icinga\Module\Kubernetes\Model\Event;
99
use Icinga\Module\Kubernetes\Model\ReplicaSet;
1010
use Icinga\Module\Kubernetes\Model\replicaSetCondition;
11+
use Icinga\Module\Kubernetes\Donut;
1112
use ipl\Html\Attributes;
1213
use ipl\Html\BaseHtmlElement;
1314
use ipl\Html\HtmlElement;
@@ -46,6 +47,28 @@ protected function assemble()
4647
t('Created') => new TimeAgo($this->replicaSet->created->getTimestamp())
4748
]));
4849

50+
$data = [
51+
$this->replicaSet->available_replicas,
52+
$this->replicaSet->ready_replicas - $this->replicaSet->available_replicas,
53+
$this->replicaSet->actual_replicas - $this->replicaSet->ready_replicas,
54+
$this->replicaSet->desired_replicas - $this->replicaSet->actual_replicas,
55+
];
56+
57+
$donut = (new Donut())
58+
->setData($data)
59+
->setLabelCallback(function ($index) {
60+
$labels = [
61+
'Available',
62+
'Ready but not yet available',
63+
'Not yet ready',
64+
'Not yet scheduled or failing'
65+
];
66+
return HtmlElement::create('span',
67+
null,
68+
$labels[$index]);
69+
});
70+
$this->addHtml($donut);
71+
4972
$this->addHtml(new ConditionTable($this->replicaSet, (new ReplicaSetCondition())->getColumnDefinitions()));
5073

5174
$this->addHtml(new HtmlElement(

library/Kubernetes/Web/StatefulSetDetail.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Icinga\Module\Kubernetes\Web;
44

5+
use Icinga\Module\Kubernetes\Donut;
56
use Icinga\Module\Kubernetes\Model\StatefulSet;
67
use Icinga\Module\Kubernetes\Model\StatefulSetCondition;
78
use ipl\Html\Attributes;
@@ -42,6 +43,28 @@ protected function assemble()
4243
t('Created') => new TimeAgo($this->statefulSet->created->getTimestamp())
4344
]));
4445

46+
$data = [
47+
$this->statefulSet->available_replicas,
48+
$this->statefulSet->ready_replicas - $this->statefulSet->available_replicas,
49+
$this->statefulSet->actual_replicas - $this->statefulSet->ready_replicas,
50+
$this->statefulSet->desired_replicas - $this->statefulSet->actual_replicas
51+
];
52+
53+
$donut = (new Donut())
54+
->setData($data)
55+
->setLabelCallback(function ($index) {
56+
$labels = [
57+
'Available',
58+
'Ready but not yet available',
59+
'Not yet ready',
60+
'Not yet scheduled or failing'
61+
];
62+
return HtmlElement::create('span',
63+
null,
64+
$labels[$index]);
65+
});
66+
$this->addHtml($donut);
67+
4568
$this->addHtml(new ConditionTable($this->statefulSet, (new StatefulSetCondition())->getColumnDefinitions()));
4669

4770
$this->addHtml(new HtmlElement(

public/css/module.less

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,3 +433,52 @@ body {
433433
#grid li:nth-child(6n+4) {
434434
margin-left: 0.5%;
435435
}
436+
437+
@donut-segment-color-0: #44bb77;
438+
@donut-segment-color-1: #77aaff;
439+
@donut-segment-color-2: #aa44ff;
440+
@donut-segment-color-3: #ff5566;
441+
442+
.donut {
443+
align-self: flex-start;
444+
padding: 1em;
445+
446+
447+
.donut-graph {
448+
.segment-0 {
449+
stroke: @donut-segment-color-0;
450+
}
451+
452+
.segment-1 {
453+
stroke: @donut-segment-color-1;
454+
}
455+
456+
.segment-2 {
457+
stroke: @donut-segment-color-2;
458+
}
459+
460+
.segment-3 {
461+
stroke: @donut-segment-color-3;
462+
}
463+
}
464+
465+
.badge {
466+
height: 1.75em;
467+
468+
&.badge-0 {
469+
background: @donut-segment-color-0;
470+
}
471+
472+
&.badge-1 {
473+
background: @donut-segment-color-1;
474+
}
475+
476+
&.badge-2 {
477+
background: @donut-segment-color-2;
478+
}
479+
480+
&.badge-3 {
481+
background: @donut-segment-color-3;
482+
}
483+
}
484+
}

0 commit comments

Comments
 (0)