Skip to content

Commit 2fbc0ff

Browse files
committed
Add donut graphs to deployments, replica sets, daemon sets and stateful
sets
1 parent a35e0d0 commit 2fbc0ff

File tree

6 files changed

+319
-79
lines changed

6 files changed

+319
-79
lines changed

library/Kubernetes/Donut.php

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

library/Kubernetes/Web/DaemonSetDetail.php

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,15 @@
22

33
namespace Icinga\Module\Kubernetes\Web;
44

5-
use Icinga\Module\Kubernetes\Common\Database;
5+
use Icinga\Module\Kubernetes\Donut;
66
use Icinga\Module\Kubernetes\Model\DaemonSet;
7-
use Icinga\Module\Kubernetes\Model\Event;
8-
use Icinga\Module\Kubernetes\Model\Label;
97
use Icinga\Module\Kubernetes\Model\ReplicaSet;
108
use Icinga\Module\Kubernetes\Model\ReplicaSetCondition;
119
use ipl\Html\Attributes;
1210
use ipl\Html\BaseHtmlElement;
1311
use ipl\Html\HtmlElement;
1412
use ipl\Html\Text;
15-
use ipl\Stdlib\Filter;
1613
use ipl\Stdlib\Str;
17-
use ipl\Web\Widget\HorizontalKeyValue;
1814
use ipl\Web\Widget\TimeAgo;
1915

2016
class DaemonSetDetail extends BaseHtmlElement
@@ -55,6 +51,26 @@ protected function assemble()
5551
new Labels($this->daemonSet->label),
5652
new ConditionTable($this->daemonSet, (new ReplicaSetCondition())->getColumnDefinitions())
5753
);
54+
$data = [
55+
$this->daemonSet->number_available,
56+
$this->daemonSet->number_ready - $this->daemonSet->number_available,
57+
$this->daemonSet->desired_number_scheduled - $this->daemonSet->number_ready,
58+
$this->daemonSet->number_unavailable
59+
];
60+
$labels = [
61+
t('Available'),
62+
t('Ready but not yet available'),
63+
t('Not yet ready'),
64+
t('Not yet scheduled or failing')
65+
];
66+
$donut = (new Donut())
67+
->setData($data)
68+
->setLabelCallback(function ($index) use ($labels) {
69+
return new HtmlElement('span', null, new Text($labels[$index]));
70+
});
71+
$this->addHtml($donut);
72+
73+
$this->addHtml(new ConditionTable($this->daemonSet, (new ReplicaSetCondition())->getColumnDefinitions()));
5874

5975
$this->addHtml(new HtmlElement(
6076
'section',

library/Kubernetes/Web/DeploymentDetail.php

Lines changed: 46 additions & 20 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 Icinga\Module\Kubernetes\Model\Label;
@@ -33,31 +34,56 @@ public function __construct(Deployment $deployment)
3334

3435
protected function assemble()
3536
{
36-
$this->addHtml(new Details([
37-
t('Name') => $this->deployment->name,
38-
t('Namespace') => $this->deployment->namespace,
39-
t('UID') => $this->deployment->uid,
40-
t('Strategy') => ucfirst(Str::camel($this->deployment->strategy)),
41-
t('Min Ready Seconds') => $this->deployment->min_ready_seconds,
42-
t('Desired Replicas') => $this->deployment->desired_replicas,
43-
t('Actual Replicas') => $this->deployment->actual_replicas,
44-
t('Updated Replicas') => $this->deployment->updated_replicas,
45-
t('Ready Replicas') => $this->deployment->ready_replicas,
46-
t('Available Replicas') => $this->deployment->available_replicas,
47-
t('Unavailable Replicas') => $this->deployment->unavailable_replicas,
48-
t('Created') => new TimeAgo($this->deployment->created->getTimestamp())
49-
]));
37+
$this->addHtml(
38+
new Details([
39+
t('Name') => $this->deployment->name,
40+
t('Namespace') => $this->deployment->namespace,
41+
t('UID') => $this->deployment->uid,
42+
t('Strategy') => ucfirst(Str::camel($this->deployment->strategy)),
43+
t('Min Ready Seconds') => $this->deployment->min_ready_seconds,
44+
t('Desired Replicas') => $this->deployment->desired_replicas,
45+
t('Actual Replicas') => $this->deployment->actual_replicas,
46+
t('Updated Replicas') => $this->deployment->updated_replicas,
47+
t('Ready Replicas') => $this->deployment->ready_replicas,
48+
t('Available Replicas') => $this->deployment->available_replicas,
49+
t('Unavailable Replicas') => $this->deployment->unavailable_replicas,
50+
t('Created') => new TimeAgo($this->deployment->created->getTimestamp())
51+
])
52+
);
5053

5154
$this->addHtml(
5255
new Labels($this->deployment->label),
5356
new ConditionTable($this->deployment, (new DeploymentCondition())->getColumnDefinitions())
5457
);
58+
$data = [
59+
$this->deployment->available_replicas,
60+
$this->deployment->desired_replicas - $this->deployment->actual_replicas,
61+
$this->deployment->actual_replicas - $this->deployment->ready_replicas,
62+
$this->deployment->unavailable_replicas,
63+
];
64+
65+
$donut = (new Donut())
66+
->setData($data)
67+
->setLabelCallback(function ($index) {
68+
$labels = [
69+
t('Available'),
70+
t('Ready but not yet available'),
71+
t('Not yet ready'),
72+
t('Not yet scheduled or failing')
73+
];
74+
return new HtmlElement('span', null, new Text($labels[$index]));
75+
});
76+
$this->addHtml($donut);
5577

56-
$this->addHtml(new HtmlElement(
57-
'section',
58-
new Attributes(['class' => 'resource-replica-sets']),
59-
new HtmlElement('h2', null, new Text(t('Replica Sets'))),
60-
new ReplicaSetList($this->deployment->replica_sets)
61-
));
78+
$this->addHtml(new ConditionTable($this->deployment, (new DeploymentCondition())->getColumnDefinitions()));
79+
80+
$this->addHtml(
81+
new HtmlElement(
82+
'section',
83+
new Attributes(['class' => 'resource-replica-sets']),
84+
new HtmlElement('h2', null, new Text(t('Replica Sets'))),
85+
new ReplicaSetList($this->deployment->replica_sets)
86+
)
87+
);
6288
}
6389
}

library/Kubernetes/Web/ReplicaSetDetail.php

Lines changed: 61 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Icinga\Module\Kubernetes\Model\Label;
1010
use Icinga\Module\Kubernetes\Model\ReplicaSet;
1111
use Icinga\Module\Kubernetes\Model\replicaSetCondition;
12+
use Icinga\Module\Kubernetes\Donut;
1213
use ipl\Html\Attributes;
1314
use ipl\Html\BaseHtmlElement;
1415
use ipl\Html\HtmlElement;
@@ -35,42 +36,72 @@ public function __construct($replicaSet)
3536

3637
protected function assemble()
3738
{
38-
$this->addHtml(new Details([
39-
t('Name') => $this->replicaSet->name,
40-
t('Namespace') => $this->replicaSet->namespace,
41-
t('UID') => $this->replicaSet->uid,
42-
t('Min Ready Seconds') => $this->replicaSet->min_ready_seconds,
43-
t('Desired Replicas') => $this->replicaSet->desired_replicas,
44-
t('Actual Replicas') => $this->replicaSet->actual_replicas,
45-
t('Fully Labeled Replicas') => $this->replicaSet->fully_labeled_replicas,
46-
t('Ready Replicas') => $this->replicaSet->ready_replicas,
47-
t('Available Replicas') => $this->replicaSet->available_replicas,
48-
t('Created') => new TimeAgo($this->replicaSet->created->getTimestamp())
49-
]));
39+
$this->addHtml(
40+
new Details([
41+
t('Name') => $this->replicaSet->name,
42+
t('Namespace') => $this->replicaSet->namespace,
43+
t('UID') => $this->replicaSet->uid,
44+
t('Min Ready Seconds') => $this->replicaSet->min_ready_seconds,
45+
t('Desired Replicas') => $this->replicaSet->desired_replicas,
46+
t('Actual Replicas') => $this->replicaSet->actual_replicas,
47+
t('Fully Labeled Replicas') => $this->replicaSet->fully_labeled_replicas,
48+
t('Ready Replicas') => $this->replicaSet->ready_replicas,
49+
t('Available Replicas') => $this->replicaSet->available_replicas,
50+
t('Created') => new TimeAgo($this->replicaSet->created->getTimestamp())
51+
])
52+
);
5053

5154
$this->addHtml(
5255
new Labels($this->replicaSet->label),
5356
new ConditionTable($this->replicaSet, (new ReplicaSetCondition())->getColumnDefinitions())
5457
);
58+
$data = [
59+
$this->replicaSet->available_replicas,
60+
$this->replicaSet->ready_replicas - $this->replicaSet->available_replicas,
61+
$this->replicaSet->actual_replicas - $this->replicaSet->ready_replicas,
62+
$this->replicaSet->desired_replicas - $this->replicaSet->actual_replicas,
63+
];
5564

56-
$this->addHtml(new HtmlElement(
57-
'section',
58-
new Attributes(['class' => 'resource-pods']),
59-
new HtmlElement('h2', null, new Text(t('Pods'))),
60-
new PodList($this->replicaSet->pods->with(['node']))
61-
));
65+
$labels = [
66+
t('Available'),
67+
t('Ready but not yet available'),
68+
t('Not yet ready'),
69+
t('Not yet scheduled or failing')
70+
];
71+
$donut = (new Donut())
72+
->setData($data)
73+
->setLabelCallback(function ($index) use ($labels) {
74+
return new HtmlElement('span', null, new Text($labels[$index]));
75+
});
76+
$this->addHtml($donut);
6277

63-
$this->addHtml(new HtmlElement(
64-
'section',
65-
new Attributes(['class' => 'resource-events']),
66-
new HtmlElement('h2', null, new Text(t('Events'))),
67-
new EventList(Event::on(Database::connection())
68-
->filter(Filter::all(
69-
Filter::equal('reference_kind', 'ReplicaSet'),
70-
Filter::equal('reference_namespace', $this->replicaSet->namespace),
71-
Filter::equal('reference_name', $this->replicaSet->name)
72-
)))
73-
));
74-
}
78+
$this->addHtml(new ConditionTable($this->replicaSet, (new ReplicaSetCondition())->getColumnDefinitions()));
7579

80+
$this->addHtml(
81+
new HtmlElement(
82+
'section',
83+
new Attributes(['class' => 'resource-pods']),
84+
new HtmlElement('h2', null, new Text(t('Pods'))),
85+
new PodList($this->replicaSet->pods->with(['node']))
86+
)
87+
);
88+
89+
$this->addHtml(
90+
new HtmlElement(
91+
'section',
92+
new Attributes(['class' => 'resource-events']),
93+
new HtmlElement('h2', null, new Text(t('Events'))),
94+
new EventList(
95+
Event::on(Database::connection())
96+
->filter(
97+
Filter::all(
98+
Filter::equal('reference_kind', 'ReplicaSet'),
99+
Filter::equal('reference_namespace', $this->replicaSet->namespace),
100+
Filter::equal('reference_name', $this->replicaSet->name)
101+
)
102+
)
103+
)
104+
)
105+
);
106+
}
76107
}

0 commit comments

Comments
 (0)