Skip to content

Commit 5089bfb

Browse files
authored
Merge pull request #67 from Icinga/prometheus-config
Add config tab for Prometheus and multi-cluster support
2 parents c32b89e + 0301350 commit 5089bfb

File tree

6 files changed

+371
-81
lines changed

6 files changed

+371
-81
lines changed

application/controllers/ConfigController.php

Lines changed: 138 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,23 @@
77
use GuzzleHttp\Psr7\ServerRequest;
88
use Icinga\Application\Config;
99
use Icinga\Application\Logger;
10-
use Icinga\Exception\Http\HttpNotFoundException;
1110
use Icinga\Module\Kubernetes\Common\Database;
1211
use Icinga\Module\Kubernetes\Forms\DatabaseConfigForm;
1312
use Icinga\Module\Kubernetes\Forms\NotificationsConfigForm;
13+
use Icinga\Module\Kubernetes\Forms\PrometheusConfigForm;
1414
use Icinga\Module\Kubernetes\Model\Config as KConfig;
1515
use Icinga\Module\Kubernetes\Web\Controller;
1616
use Icinga\Module\Notifications\Common\Database as NotificationsDatabase;
1717
use Icinga\Module\Notifications\Forms\SourceForm;
1818
use Icinga\Module\Notifications\Model\Source;
1919
use Icinga\Web\Notification;
20+
use Icinga\Web\Session;
2021
use Icinga\Web\Widget\Tabs;
2122
use ipl\Sql\Connection;
2223
use ipl\Stdlib\Filter;
24+
use ipl\Web\Url;
2325
use LogicException;
26+
use Ramsey\Uuid\Uuid;
2427
use Throwable;
2528

2629
class ConfigController extends Controller
@@ -55,18 +58,6 @@ public function notificationsAction()
5558
{
5659
$this->mergeTabs($this->Module()->getConfigTabs()->activate('notifications'));
5760

58-
$kconfig = [];
59-
$q = KConfig::on(Database::connection())
60-
->filter(Filter::equal('key', [
61-
KConfig::NOTIFICATIONS_URL,
62-
KConfig::NOTIFICATIONS_USERNAME,
63-
KConfig::NOTIFICATIONS_KUBERNETES_WEB_URL
64-
]));
65-
66-
foreach ($q as $r) {
67-
$kconfig[$r['key']] = $r;
68-
}
69-
7061
$sourceForm = new class (NotificationsDatabase::get()) extends SourceForm {
7162
public function hasBeenSent(): bool
7263
{
@@ -80,53 +71,75 @@ public function hasBeenSubmitted(): bool
8071
};
8172

8273
$form = (new NotificationsConfigForm())
83-
->setKConfig($kconfig)
74+
->populate([
75+
'cluster_uuid' =>
76+
$this->getRequest()->get('cluster_uuid') ??
77+
Session::getSession()
78+
->getNamespace('kubernetes')
79+
->get('cluster_uuid')
80+
])
8481
->on(
8582
NotificationsConfigForm::ON_SUCCESS,
86-
function (NotificationsConfigForm $form) use ($kconfig, $sourceForm) {
83+
function (NotificationsConfigForm $form) use ($sourceForm) {
8784
if ($form->isLocked()) {
8885
$form->addMessage($this->translate('Notifications configuration is locked.'));
8986

9087
return;
9188
}
9289

90+
$clusterUuid = $form->getClusterUuid();
91+
$kconfig = $form->getKConfig($clusterUuid);
9392
$values = $form->getValues();
9493

94+
/** @var ?Source $source */
95+
$source = Source::on(NotificationsDatabase::get())
96+
->filter(Filter::all(
97+
Filter::equal('name', KConfig::DEFAULT_NOTIFICATIONS_NAME . " ($clusterUuid)"),
98+
Filter::equal('type', KConfig::DEFAULT_NOTIFICATIONS_TYPE)
99+
))
100+
->first();
101+
95102
if (
96-
! ($kconfig[KConfig::NOTIFICATIONS_USERNAME]->locked ?? false)
97-
&& ($kconfig[KConfig::NOTIFICATIONS_USERNAME]->value ?? '') === ''
103+
$source === null
104+
// Must be kept in sync with SourceForm:292.
105+
|| password_hash(
106+
$kconfig[KConfig::NOTIFICATIONS_PASSWORD]->value,
107+
SourceForm::HASH_ALGORITHM
108+
) !== $source->listener_password_hash
98109
) {
99110
try {
100-
$values[KConfig::NOTIFICATIONS_PASSWORD] = $this->createSource($sourceForm);
111+
$values[KConfig::NOTIFICATIONS_PASSWORD] = $this
112+
->createOrUpdateSource($sourceForm, $clusterUuid);
113+
114+
/** @var ?Source $source */
115+
$source = Source::on(NotificationsDatabase::get())
116+
->filter(Filter::all(
117+
Filter::equal('name', KConfig::DEFAULT_NOTIFICATIONS_NAME . " ($clusterUuid)"),
118+
Filter::equal('type', KConfig::DEFAULT_NOTIFICATIONS_TYPE)
119+
))
120+
->first();
121+
122+
if ($source === null) {
123+
throw new LogicException($this->translate('Source not created'));
124+
}
125+
126+
$values[KConfig::NOTIFICATIONS_USERNAME] = "source-$source->id";
101127
} catch (Throwable $e) {
102128
Logger::error($e);
103129
Logger::error($e->getTraceAsString());
104130

105-
$form->addMessage($e->getMessage());
106-
107-
return;
131+
throw $e;
108132
}
109-
110-
/** @var ?Source $source */
111-
$source = Source::on(NotificationsDatabase::get())
112-
->filter(Filter::all(
113-
Filter::equal('name', KConfig::DEFAULT_NOTIFICATIONS_NAME),
114-
Filter::equal('type', KConfig::DEFAULT_NOTIFICATIONS_TYPE)
115-
))
116-
->first();
117-
118-
if ($source === null) {
119-
throw new LogicException($this->translate('Source not found'));
120-
}
121-
122-
$values[KConfig::NOTIFICATIONS_USERNAME] = "source-$source->id";
123133
}
124134

125135
try {
126-
Database::connection()->transaction(function (Connection $db) use ($values) {
136+
Database::connection()->transaction(function (Connection $db) use ($values, $clusterUuid) {
137+
$key = $db->quoteIdentifier('key');
138+
127139
$db->delete((new KConfig())->getTableName(), [
128-
sprintf('%s IN (?)', $db->quoteIdentifier('key')) => array_keys($values),
129-
'locked = ?' => 'n'
140+
'cluster_uuid = ?' => Uuid::fromString($clusterUuid)->getBytes(),
141+
sprintf('%s IN (?)', $key) => array_keys($values),
142+
'locked = ?' => 'n'
130143
]);
131144

132145
foreach ($values as $k => $v) {
@@ -135,44 +148,86 @@ function (NotificationsConfigForm $form) use ($kconfig, $sourceForm) {
135148
}
136149

137150
$db->insert((new KConfig())->getTableName(), [
138-
$db->quoteIdentifier('key') => $k,
139-
'value' => $v,
151+
'cluster_uuid' => Uuid::fromString($clusterUuid)->getBytes(),
152+
$key => $k,
153+
'value' => $v
140154
]);
141155
}
142156
});
143157
} catch (Throwable $e) {
144158
Logger::error($e);
145159
Logger::error($e->getTraceAsString());
146160

147-
$form->addMessage($e->getMessage());
148-
149-
return;
161+
throw $e;
150162
}
151163

152164
Notification::success(
153165
$this->translate('New configuration has successfully been stored.')
154166
);
155167

156-
$this->redirectNow('__REFRESH__');
168+
$this->redirectNow(
169+
Url::fromPath('kubernetes/config/notifications', ['cluster_uuid' => $clusterUuid])
170+
);
157171
}
158172
)->handleRequest($this->getServerRequest());
159173

160-
if (
161-
preg_match(
162-
'/source-(\d+)/',
163-
$kconfig[KConfig::NOTIFICATIONS_USERNAME]->value ?? '',
164-
$matches
165-
) !== false
166-
&& ! empty($matches)
167-
) {
168-
try {
169-
$sourceForm->loadSource($matches[1]);
170-
171-
// TODO(el): Check password mismatch.
172-
} catch (HttpNotFoundException $e) {
173-
// TODO(el): Add error box.
174-
}
175-
}
174+
$this->addContent($form);
175+
}
176+
177+
public function prometheusAction()
178+
{
179+
$form = (new PrometheusConfigForm())
180+
->populate([
181+
'cluster_uuid' =>
182+
$this->getRequest()->get('cluster_uuid') ??
183+
Session::getSession()
184+
->getNamespace('kubernetes')
185+
->get('cluster_uuid')
186+
])
187+
->on(PrometheusConfigForm::ON_SUCCESS, function (PrometheusConfigForm $form) {
188+
if ($form->isLocked()) {
189+
$form->addMessage($this->translate('Prometheus configuration is locked.'));
190+
191+
return;
192+
}
193+
194+
$clusterUuid = $form->getClusterUuid();
195+
$values = $form->getValues();
196+
197+
try {
198+
Database::connection()->transaction(function (Connection $db) use ($values, $clusterUuid) {
199+
$key = $db->quoteIdentifier('key');
200+
201+
$db->delete((new KConfig())->getTableName(), [
202+
'cluster_uuid = ?' => Uuid::fromString($clusterUuid)->getBytes(),
203+
sprintf('%s IN (?)', $key) => array_keys($values),
204+
'locked = ?' => 'n'
205+
]);
206+
207+
foreach ($values as $k => $v) {
208+
if (empty($v)) {
209+
continue;
210+
}
211+
212+
$db->insert((new KConfig())->getTableName(), [
213+
'cluster_uuid' => Uuid::fromString($clusterUuid)->getBytes(),
214+
$key => $k,
215+
'value' => $v
216+
]);
217+
}
218+
});
219+
} catch (Throwable $e) {
220+
Logger::error($e);
221+
Logger::error($e->getTraceAsString());
222+
223+
throw $e;
224+
}
225+
226+
Notification::success($this->translate('New configuration has successfully been stored'));
227+
$this->redirectNow(Url::fromPath('kubernetes/config/prometheus', ['cluster_uuid' => $clusterUuid]));
228+
})->handleRequest($this->getServerRequest());
229+
230+
$this->mergeTabs($this->Module()->getConfigTabs()->activate('prometheus'));
176231

177232
$this->addContent($form);
178233
}
@@ -189,24 +244,42 @@ protected function mergeTabs(Tabs $tabs): void
189244
}
190245
}
191246

192-
protected function createSource(SourceForm $sourceForm): string
247+
protected function createOrUpdateSource(SourceForm $sourceForm, string $clusterUuid): string
193248
{
194249
$password = sha1(openssl_random_pseudo_bytes(16));
195250

196251
$formData = [
197252
'listener_password' => $password,
198253
'listener_password_dupe' => $password,
199-
'name' => KConfig::DEFAULT_NOTIFICATIONS_NAME,
254+
'name' => KConfig::DEFAULT_NOTIFICATIONS_NAME . " ($clusterUuid)",
200255
'type' => KConfig::DEFAULT_NOTIFICATIONS_TYPE,
201256
// TODO(el): Why?
202257
'icinga2_insecure_tls' => 'n'
203258
];
204259

205-
$sourceForm
206-
->populate($formData)
207-
->on(SourceForm::ON_SUCCESS, function (SourceForm $form) {
260+
/** @var ?Source $source */
261+
$source = Source::on(NotificationsDatabase::get())
262+
->filter(Filter::all(
263+
Filter::equal('name', KConfig::DEFAULT_NOTIFICATIONS_NAME . " ($clusterUuid)"),
264+
Filter::equal('type', KConfig::DEFAULT_NOTIFICATIONS_TYPE)
265+
))
266+
->first();
267+
268+
if ($source !== null) {
269+
$sourceForm->loadSource($source->id);
270+
}
271+
272+
$sourceForm->populate($formData);
273+
274+
if ($source !== null) {
275+
$sourceForm->on(SourceForm::ON_SUCCESS, function (SourceForm $form) {
276+
$form->editSource();
277+
});
278+
} else {
279+
$sourceForm->on(SourceForm::ON_SUCCESS, function (SourceForm $form) {
208280
$form->addSource();
209281
});
282+
}
210283

211284
$sourceForm->ensureAssembled();
212285
$csrf = $sourceForm->getElement('CSRFToken');

0 commit comments

Comments
 (0)