From 26b6a5ec8a52e72c45d50d7b6dff18f36f08b860 Mon Sep 17 00:00:00 2001 From: Jonada Hoxha Date: Mon, 3 Jul 2023 17:29:04 +0200 Subject: [PATCH] Add donut graphs to deployments, replica sets, daemon sets and stateful sets --- library/Kubernetes/Donut.php | 89 ++++++++++++++++++++ library/Kubernetes/Web/DaemonSetDetail.php | 20 +++++ library/Kubernetes/Web/DeploymentDetail.php | 23 +++++ library/Kubernetes/Web/ReplicaSetDetail.php | 23 +++++ library/Kubernetes/Web/StatefulSetDetail.php | 23 +++++ public/css/module.less | 49 +++++++++++ 6 files changed, 227 insertions(+) create mode 100644 library/Kubernetes/Donut.php diff --git a/library/Kubernetes/Donut.php b/library/Kubernetes/Donut.php new file mode 100644 index 00000000..bcc7e6e4 --- /dev/null +++ b/library/Kubernetes/Donut.php @@ -0,0 +1,89 @@ + 'donut']; + + /** + * The donut data + * + * @var iterable + */ + protected $data = []; + + /** + * @var string + */ + protected $heading; + + /** + * @var int + */ + protected $headingLevel = 2; + + /** + * @var callable + */ + protected $labelCallback; + + /** + * Set the data to display + * + * @param iterable $data + * + * @return $this + */ + public function setData(iterable $data) + { + $this->data = $data; + + return $this; + } + + public function setHeading(string $heading, int $level) + { + $this->heading = $heading; + $this->headingLevel = $level; + + return $this; + } + + public function setLabelCallback(callable $callback) + { + $this->labelCallback = $callback; + + return $this; + } + + public function assemble() + { + $donut = new \Icinga\Chart\Donut(); + $legend = new Table(); + + foreach ($this->data as $index => $value) { + $donut->addSlice((int) $value, ['class' => 'segment-' . $index]); + $legend->add( + [ + Html::tag('span', ['class' => 'badge badge-' . $index]), + call_user_func($this->labelCallback, $index), + $value + ] + ); + } + + if ($this->heading === null) { + $this->addHtml(Html::tag("h{$this->headingLevel}", $this->heading), new HtmlString($donut->render()), $legend); + } + } +} diff --git a/library/Kubernetes/Web/DaemonSetDetail.php b/library/Kubernetes/Web/DaemonSetDetail.php index 9c8de7fe..af6a8321 100644 --- a/library/Kubernetes/Web/DaemonSetDetail.php +++ b/library/Kubernetes/Web/DaemonSetDetail.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Kubernetes\Web; use Icinga\Module\Kubernetes\Common\Database; +use Icinga\Module\Kubernetes\Donut; use Icinga\Module\Kubernetes\Model\DaemonSet; use Icinga\Module\Kubernetes\Model\Event; use Icinga\Module\Kubernetes\Model\ReplicaSet; @@ -49,6 +50,25 @@ protected function assemble() t('Created') => new TimeAgo($this->daemonSet->created->getTimestamp()) ])); + $data = [ + $this->daemonSet->number_available, + $this->daemonSet->number_ready - $this->daemonSet->number_available, + $this->daemonSet->desired_number_scheduled - $this->daemonSet->number_ready, + $this->daemonSet->number_unavailable + ]; + $labels = [ + t('Available'), + t('Ready but not yet available'), + t('Not yet ready'), + t('Not yet scheduled or failing') + ]; + $donut = (new Donut()) + ->setData($data) + ->setLabelCallback(function ($index) use ($labels){ + return HtmlElement::create('span', null, $labels[$index]); + }); + $this->addHtml($donut); + $this->addHtml(new ConditionTable($this->daemonSet, (new ReplicaSetCondition())->getColumnDefinitions())); $this->addHtml(new HtmlElement( diff --git a/library/Kubernetes/Web/DeploymentDetail.php b/library/Kubernetes/Web/DeploymentDetail.php index 1cf0f190..e28dc0f6 100644 --- a/library/Kubernetes/Web/DeploymentDetail.php +++ b/library/Kubernetes/Web/DeploymentDetail.php @@ -4,6 +4,7 @@ namespace Icinga\Module\Kubernetes\Web; +use Icinga\Module\Kubernetes\Donut; use Icinga\Module\Kubernetes\Model\Deployment; use Icinga\Module\Kubernetes\Model\DeploymentCondition; use ipl\Html\Attributes; @@ -46,6 +47,28 @@ protected function assemble() t('Created') => new TimeAgo($this->deployment->created->getTimestamp()) ])); + $data = [ + $this->deployment->available_replicas, + $this->deployment->desired_replicas - $this->deployment->actual_replicas, + $this->deployment->actual_replicas - $this->deployment->ready_replicas, + $this->deployment->unavailable_replicas, + ]; + + $donut = (new Donut()) + ->setData($data) + ->setLabelCallback(function ($index) { + $labels = [ + 'Available', + 'Ready but not yet available', + 'Not yet ready', + 'Not yet scheduled or failing' + ]; + return HtmlElement::create('span', + null, + $labels[$index]); + }); + $this->addHtml($donut); + $this->addHtml(new ConditionTable($this->deployment, (new DeploymentCondition())->getColumnDefinitions())); $this->addHtml(new HtmlElement( diff --git a/library/Kubernetes/Web/ReplicaSetDetail.php b/library/Kubernetes/Web/ReplicaSetDetail.php index b90384ba..c5f371e0 100644 --- a/library/Kubernetes/Web/ReplicaSetDetail.php +++ b/library/Kubernetes/Web/ReplicaSetDetail.php @@ -8,6 +8,7 @@ use Icinga\Module\Kubernetes\Model\Event; use Icinga\Module\Kubernetes\Model\ReplicaSet; use Icinga\Module\Kubernetes\Model\replicaSetCondition; +use Icinga\Module\Kubernetes\Donut; use ipl\Html\Attributes; use ipl\Html\BaseHtmlElement; use ipl\Html\HtmlElement; @@ -46,6 +47,28 @@ protected function assemble() t('Created') => new TimeAgo($this->replicaSet->created->getTimestamp()) ])); + $data = [ + $this->replicaSet->available_replicas, + $this->replicaSet->ready_replicas - $this->replicaSet->available_replicas, + $this->replicaSet->actual_replicas - $this->replicaSet->ready_replicas, + $this->replicaSet->desired_replicas - $this->replicaSet->actual_replicas, + ]; + + $donut = (new Donut()) + ->setData($data) + ->setLabelCallback(function ($index) { + $labels = [ + 'Available', + 'Ready but not yet available', + 'Not yet ready', + 'Not yet scheduled or failing' + ]; + return HtmlElement::create('span', + null, + $labels[$index]); + }); + $this->addHtml($donut); + $this->addHtml(new ConditionTable($this->replicaSet, (new ReplicaSetCondition())->getColumnDefinitions())); $this->addHtml(new HtmlElement( diff --git a/library/Kubernetes/Web/StatefulSetDetail.php b/library/Kubernetes/Web/StatefulSetDetail.php index 78163a9f..a348a62d 100644 --- a/library/Kubernetes/Web/StatefulSetDetail.php +++ b/library/Kubernetes/Web/StatefulSetDetail.php @@ -2,6 +2,7 @@ namespace Icinga\Module\Kubernetes\Web; +use Icinga\Module\Kubernetes\Donut; use Icinga\Module\Kubernetes\Model\StatefulSet; use Icinga\Module\Kubernetes\Model\StatefulSetCondition; use ipl\Html\Attributes; @@ -42,6 +43,28 @@ protected function assemble() t('Created') => new TimeAgo($this->statefulSet->created->getTimestamp()) ])); + $data = [ + $this->statefulSet->available_replicas, + $this->statefulSet->ready_replicas - $this->statefulSet->available_replicas, + $this->statefulSet->actual_replicas - $this->statefulSet->ready_replicas, + $this->statefulSet->desired_replicas - $this->statefulSet->actual_replicas + ]; + + $donut = (new Donut()) + ->setData($data) + ->setLabelCallback(function ($index) { + $labels = [ + 'Available', + 'Ready but not yet available', + 'Not yet ready', + 'Not yet scheduled or failing' + ]; + return HtmlElement::create('span', + null, + $labels[$index]); + }); + $this->addHtml($donut); + $this->addHtml(new ConditionTable($this->statefulSet, (new StatefulSetCondition())->getColumnDefinitions())); $this->addHtml(new HtmlElement( diff --git a/public/css/module.less b/public/css/module.less index 7e064c22..e76d5d3d 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -433,3 +433,52 @@ body { #grid li:nth-child(6n+4) { margin-left: 0.5%; } + +@donut-segment-color-0: #44bb77; +@donut-segment-color-1: #77aaff; +@donut-segment-color-2: #aa44ff; +@donut-segment-color-3: #ff5566; + +.donut { + align-self: flex-start; + padding: 1em; + + + .donut-graph { + .segment-0 { + stroke: @donut-segment-color-0; + } + + .segment-1 { + stroke: @donut-segment-color-1; + } + + .segment-2 { + stroke: @donut-segment-color-2; + } + + .segment-3 { + stroke: @donut-segment-color-3; + } + } + + .badge { + height: 1.75em; + + &.badge-0 { + background: @donut-segment-color-0; + } + + &.badge-1 { + background: @donut-segment-color-1; + } + + &.badge-2 { + background: @donut-segment-color-2; + } + + &.badge-3 { + background: @donut-segment-color-3; + } + } +}