From 3fcb20178ff1722329bf8689795e6cc8e53a9978 Mon Sep 17 00:00:00 2001 From: raviks789 Date: Tue, 21 Jan 2025 12:04:12 +0100 Subject: [PATCH 01/13] Apply correct restrictions to REST url endpoints The following url end points should apply relevant restrictions to filter out objects and show correct HTTP status code: - icingaweb2/director/service, if the host name is left out of the query (Example: icingaweb2/director/service?name=service-name) - icingaweb2/directore/notification - icingaweb2/director/serviceset - icingaweb2/director/scheduled-downtime --- .../controllers/NotificationController.php | 5 ++++ application/controllers/ServiceController.php | 22 ++++++++++------- .../controllers/ServicesetController.php | 5 ++++ .../Restriction/FilterByNameRestriction.php | 11 ++++++--- .../Extension/ObjectRestrictions.php | 24 ++++++++++++++++--- .../Web/Controller/ObjectsController.php | 10 ++++++-- library/Director/Web/Table/ObjectsTable.php | 11 +++++---- 7 files changed, 68 insertions(+), 20 deletions(-) diff --git a/application/controllers/NotificationController.php b/application/controllers/NotificationController.php index 97fa0f4d..cef254ab 100644 --- a/application/controllers/NotificationController.php +++ b/application/controllers/NotificationController.php @@ -2,6 +2,7 @@ namespace Icinga\Module\Director\Controllers; +use Icinga\Exception\NotFoundError; use Icinga\Module\Director\Web\Controller\ObjectController; use Icinga\Module\Director\Objects\IcingaHost; use Icinga\Module\Director\Objects\IcingaNotification; @@ -80,6 +81,10 @@ class NotificationController extends ObjectController } } + if (! $this->allowsObject($this->object)) { + throw new NotFoundError('No such object available'); + } + return $this->object; } } diff --git a/application/controllers/ServiceController.php b/application/controllers/ServiceController.php index c0a6182c..4782e5e1 100644 --- a/application/controllers/ServiceController.php +++ b/application/controllers/ServiceController.php @@ -49,14 +49,6 @@ class ServiceController extends ObjectController $this->host = $this->getOptionalRelatedObjectFromParams('host', 'host'); $this->set = $this->getOptionalRelatedObjectFromParams('service_set', 'set'); parent::init(); - if ($this->object) { - if ($this->host === null) { - $this->host = $this->loadOptionalRelatedObject($this->object, 'host'); - } - if ($this->set === null) { - $this->set = $this->loadOptionalRelatedObject($this->object, 'service_set'); - } - } $this->addOptionalHostTabs(); $this->addOptionalSetTabs(); } @@ -77,6 +69,20 @@ class ServiceController extends ObjectController return null; } + protected function loadOptionalObject(): void + { + parent::loadOptionalObject(); + + if ($this->object) { + if ($this->host === null) { + $this->host = $this->loadOptionalRelatedObject($this->object, 'host'); + } + if ($this->set === null) { + $this->set = $this->loadOptionalRelatedObject($this->object, 'service_set'); + } + } + } + protected function loadOptionalRelatedObject(IcingaObject $object, $relation) { $key = $object->getUnresolvedRelated($relation); diff --git a/application/controllers/ServicesetController.php b/application/controllers/ServicesetController.php index 684d2fc8..716eb2b2 100644 --- a/application/controllers/ServicesetController.php +++ b/application/controllers/ServicesetController.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Director\Controllers; use Icinga\Data\Filter\Filter; +use Icinga\Exception\NotFoundError; use Icinga\Module\Director\Forms\IcingaServiceSetForm; use Icinga\Module\Director\Objects\IcingaHost; use Icinga\Module\Director\Objects\IcingaServiceSet; @@ -136,6 +137,10 @@ class ServicesetController extends ObjectController } } + if (! $this->allowsObject($this->object)) { + throw new NotFoundError('No such object available'); + } + return $this->object; } } diff --git a/library/Director/Restriction/FilterByNameRestriction.php b/library/Director/Restriction/FilterByNameRestriction.php index aef28c4e..d1bea1c4 100644 --- a/library/Director/Restriction/FilterByNameRestriction.php +++ b/library/Director/Restriction/FilterByNameRestriction.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Director\Restriction; use gipfl\IcingaWeb2\Zf1\Db\FilterRenderer; use Icinga\Authentication\Auth; use Icinga\Data\Filter\Filter; +use Icinga\Data\Filter\FilterEqual; use Icinga\Module\Director\Db; use Icinga\Module\Director\Objects\IcingaObject; use Zend_Db_Select as ZfSelect; @@ -30,7 +31,11 @@ class FilterByNameRestriction extends ObjectRestriction protected function setNameForType($type) { - $this->name = "director/{$type}/filter-by-name"; + if ($type === 'service_set') { + $this->name = "director/{$type}/filter-by-name"; + } else { + $this->name = "director/{$type}/apply/filter-by-name"; + } } public function allows(IcingaObject $object) @@ -39,9 +44,9 @@ class FilterByNameRestriction extends ObjectRestriction return true; } - return $this->getFilter()->matches([ + return $this->getFilter()->matches( (object) ['object_name' => $object->getObjectName()] - ]); + ); } public function getFilter() diff --git a/library/Director/Web/Controller/Extension/ObjectRestrictions.php b/library/Director/Web/Controller/Extension/ObjectRestrictions.php index bedb3f18..fa56f023 100644 --- a/library/Director/Web/Controller/Extension/ObjectRestrictions.php +++ b/library/Director/Web/Controller/Extension/ObjectRestrictions.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Director\Web\Controller\Extension; use Icinga\Authentication\Auth; use Icinga\Module\Director\Db; use Icinga\Module\Director\Objects\IcingaObject; +use Icinga\Module\Director\Restriction\FilterByNameRestriction; use Icinga\Module\Director\Restriction\HostgroupRestriction; use Icinga\Module\Director\Restriction\ObjectRestriction; @@ -13,6 +14,9 @@ trait ObjectRestrictions /** @var ObjectRestriction[] */ private $objectRestrictions; + /** @var IcingaObject */ + private $dummyRestrictedObject; + /** * @return ObjectRestriction[] */ @@ -30,13 +34,27 @@ trait ObjectRestrictions */ protected function loadObjectRestrictions(Db $db, Auth $auth) { - return [ - new HostgroupRestriction($db, $auth) - ]; + $objectType = $this->dummyRestrictedObject->getShortTableName(); + if ( + ($objectType === 'service' && $this->dummyRestrictedObject->isApplyRule()) + || $objectType === 'notification' + || $objectType === 'service_set' + || $objectType === 'scheduled_downtime' + ) { + if ($objectType === 'scheduled_downtime') { + $objectType = 'scheduled-downtime'; + } + + return [new FilterByNameRestriction($db, $auth, $objectType)]; + } + + // If the object is host or host group load HostgroupRestriction + return [new HostgroupRestriction($db, $auth)]; } public function allowsObject(IcingaObject $object) { + $this->dummyRestrictedObject = $object; foreach ($this->getObjectRestrictions() as $restriction) { if (! $restriction->allows($object)) { return false; diff --git a/library/Director/Web/Controller/ObjectsController.php b/library/Director/Web/Controller/ObjectsController.php index 5b3c8879..1440ef45 100644 --- a/library/Director/Web/Controller/ObjectsController.php +++ b/library/Director/Web/Controller/ObjectsController.php @@ -17,6 +17,7 @@ use Icinga\Module\Director\Objects\IcingaService; use Icinga\Module\Director\RestApi\IcingaObjectsHandler; use Icinga\Module\Director\Web\ActionBar\ObjectsActionBar; use Icinga\Module\Director\Web\ActionBar\TemplateActionBar; +use Icinga\Module\Director\Web\Controller\Extension\ObjectRestrictions; use Icinga\Module\Director\Web\Form\FormLoader; use Icinga\Module\Director\Web\Table\ApplyRulesTable; use Icinga\Module\Director\Web\Table\ObjectSetTable; @@ -33,6 +34,7 @@ use Ramsey\Uuid\Uuid; abstract class ObjectsController extends ActionController { use BranchHelper; + use ObjectRestrictions; protected $isApified = true; @@ -75,9 +77,13 @@ abstract class ObjectsController extends ActionController $table = $this->getTable(); if ( $request->getControllerName() === 'services' - && $host = $this->params->get('host') + && $hostName = $this->params->get('host') ) { - $host = IcingaHost::load($host, $this->db()); + $host = IcingaHost::load($hostName, $this->db()); + if (! $this->allowsObject($host)) { + throw new NotFoundError(sprintf('Failed to load %s "%s"', $host->getTableName(), $hostName)); + } + $table->getQuery()->where('o.host_id = ?', $host->get('id')); } diff --git a/library/Director/Web/Table/ObjectsTable.php b/library/Director/Web/Table/ObjectsTable.php index 4ad11661..00332bdf 100644 --- a/library/Director/Web/Table/ObjectsTable.php +++ b/library/Director/Web/Table/ObjectsTable.php @@ -226,11 +226,14 @@ class ObjectsTable extends ZfQueryBasedTable { /** @var Db $db */ $db = $this->connection(); + $dummyObject = $this->getDummyObject(); + $type = $dummyObject->getShortTableName(); - return [ - new HostgroupRestriction($db, $this->auth), - new FilterByNameRestriction($db, $this->auth, $this->getDummyObject()->getShortTableName()) - ]; + if ($dummyObject->isApplyRule()) { + return [new FilterByNameRestriction($db, $this->auth, $type)]; + } else { + return [new HostgroupRestriction($db, $this->auth)]; + } } /** From 2196de252da9ddd4be934a6969ac0c00400c5452 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mek=20Sk=C3=ABndo?= Date: Thu, 19 Dec 2024 16:56:04 +0100 Subject: [PATCH 02/13] Fix MySQL8.4 nonstandard foreign keys deprecation If you try to set up icinga director with the latest minor version of MySQL 8.4+, the schema creation will fail with confusing SQL error messages. This commit aims to fix that. https://dev.mysql.com/doc/refman/8.4/en/mysql-nutshell.html#mysql-nutshell-deprecations --- schema/mysql-migrations/upgrade_188.sql | 17 +++++++++++++++++ schema/mysql.sql | 6 +++--- 2 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 schema/mysql-migrations/upgrade_188.sql diff --git a/schema/mysql-migrations/upgrade_188.sql b/schema/mysql-migrations/upgrade_188.sql new file mode 100644 index 00000000..1c016195 --- /dev/null +++ b/schema/mysql-migrations/upgrade_188.sql @@ -0,0 +1,17 @@ +ALTER TABLE director_activity_log +DROP INDEX checksum, +ADD UNIQUE INDEX checksum (checksum); + +ALTER TABLE director_generated_config +DROP FOREIGN KEY director_generated_config_activity; + +ALTER TABLE director_generated_config +ADD CONSTRAINT director_generated_config_activity + FOREIGN KEY (last_activity_checksum) + REFERENCES director_activity_log (checksum) + ON DELETE RESTRICT + ON UPDATE RESTRICT; + +INSERT INTO director_schema_migration + (schema_version, migration_time) + VALUES (188, NOW()); diff --git a/schema/mysql.sql b/schema/mysql.sql index 832ab122..dd8a0f84 100644 --- a/schema/mysql.sql +++ b/schema/mysql.sql @@ -46,7 +46,7 @@ CREATE TABLE director_activity_log ( INDEX search_idx (object_name), INDEX search_idx2 (object_type(32), object_name(64), change_time), INDEX search_author (author), - INDEX checksum (checksum) + UNIQUE INDEX checksum (checksum) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE director_activity_log_remark ( @@ -114,7 +114,7 @@ CREATE TABLE director_generated_config ( last_activity_checksum VARBINARY(20) NOT NULL, PRIMARY KEY (checksum), CONSTRAINT director_generated_config_activity - FOREIGN KEY activity_checksum (last_activity_checksum) + FOREIGN KEY (last_activity_checksum) REFERENCES director_activity_log (checksum) ON DELETE RESTRICT ON UPDATE RESTRICT @@ -2446,4 +2446,4 @@ CREATE TABLE branched_icinga_dependency ( INSERT INTO director_schema_migration (schema_version, migration_time) - VALUES (187, NOW()); + VALUES (188, NOW()); From 778b25873c5763dc95f912524cd2fb3413ef27e5 Mon Sep 17 00:00:00 2001 From: raviks789 Date: Thu, 20 Mar 2025 17:14:20 +0100 Subject: [PATCH 03/13] CustomVarRenderer: Fix custom var renderer for apply-for-rules --- .../Icingadb/CustomVarRenderer.php | 49 +++++++++++++------ .../Monitoring/CustomVarRenderer.php | 49 +++++++++++++------ 2 files changed, 70 insertions(+), 28 deletions(-) diff --git a/library/Director/ProvidedHook/Icingadb/CustomVarRenderer.php b/library/Director/ProvidedHook/Icingadb/CustomVarRenderer.php index fb307a52..fa609a72 100644 --- a/library/Director/ProvidedHook/Icingadb/CustomVarRenderer.php +++ b/library/Director/ProvidedHook/Icingadb/CustomVarRenderer.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Director\ProvidedHook\Icingadb; use Icinga\Application\Config; use Icinga\Exception\ConfigurationError; use Icinga\Exception\NotFoundError; +use Icinga\Module\Director\CustomVariable\CustomVariableArray; use Icinga\Module\Director\Daemon\Logger; use Icinga\Module\Director\Db; use Icinga\Module\Director\Db\AppliedServiceSetLoader; @@ -113,22 +114,42 @@ class CustomVarRenderer extends CustomVarRendererHook $appliedFilterQuery = IcingaHostAppliedServicesTable::load($directorHostObj)->getQuery(); foreach ($appliedFilterQuery->fetchAll() as $appliedService) { - if ($appliedService->name === $serviceName) { - $query = $db->getDbAdapter()->select()->from('icinga_service') - ->where('object_name = ?', $serviceName) - ->where("object_type = 'apply'") - ->where('assign_filter = ?', $appliedService->assign_filter); + if ($appliedService->apply_for === null) { + $isAppliedService = $appliedService->name === $serviceName; + } else { + /** @var ?CustomVariableArray $hostVar */ + $hostVar = $directorHostObj->vars()->get((substr($appliedService->apply_for, 10))); + if ($hostVar) { + $appliedServiceName = $appliedService->name; + $appliedForServiceLookup = []; + foreach ($hostVar->getValue() as $val) { + $appliedForServiceLookup[$appliedServiceName . $val] = true; + } - $directorAppliedServices = IcingaService::loadAll( - $db, - $query, - 'object_name' - ); - - $directorServiceObj = current($directorAppliedServices); - - break; + $isAppliedService = isset($appliedForServiceLookup[$serviceName]); + } else { + $isAppliedService = false; + } } + + if (! $isAppliedService) { + continue; + } + + $query = $db->getDbAdapter()->select()->from('icinga_service') + ->where('object_name = ?', $appliedService->name) + ->where("object_type = 'apply'") + ->where('assign_filter = ?', $appliedService->assign_filter); + + $directorAppliedServices = IcingaService::loadAll( + $db, + $query, + 'object_name' + ); + + $directorServiceObj = current($directorAppliedServices); + + break; } } elseif ($serviceOrigin[$i] === 'service-set') { $templateResolver = new IcingaTemplateResolver($directorHostObj); diff --git a/library/Director/ProvidedHook/Monitoring/CustomVarRenderer.php b/library/Director/ProvidedHook/Monitoring/CustomVarRenderer.php index 4eb8d2d0..8601828b 100644 --- a/library/Director/ProvidedHook/Monitoring/CustomVarRenderer.php +++ b/library/Director/ProvidedHook/Monitoring/CustomVarRenderer.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Director\ProvidedHook\Monitoring; use Icinga\Application\Config; use Icinga\Exception\ConfigurationError; use Icinga\Exception\NotFoundError; +use Icinga\Module\Director\CustomVariable\CustomVariableArray; use Icinga\Module\Director\Daemon\Logger; use Icinga\Module\Director\Db; use Icinga\Module\Director\Db\AppliedServiceSetLoader; @@ -114,22 +115,42 @@ class CustomVarRenderer extends CustomVarRendererHook $appliedFilterQuery = IcingaHostAppliedServicesTable::load($directorHostObj)->getQuery(); foreach ($appliedFilterQuery->fetchAll() as $appliedService) { - if ($appliedService->name === $serviceName) { - $query = $db->getDbAdapter()->select()->from('icinga_service') - ->where('object_name = ?', $serviceName) - ->where("object_type = 'apply'") - ->where('assign_filter = ?', $appliedService->assign_filter); + if ($appliedService->apply_for === null) { + $isAppliedService = $appliedService->name === $serviceName; + } else { + /** @var ?CustomVariableArray $hostVar */ + $hostVar = $directorHostObj->vars()->get((substr($appliedService->apply_for, 10))); + if ($hostVar) { + $appliedServiceName = $appliedService->name; + $appliedForServiceLookup = []; + foreach ($hostVar->getValue() as $val) { + $appliedForServiceLookup[$appliedServiceName . $val] = true; + } - $directorAppliedServices = IcingaService::loadAll( - $db, - $query, - 'object_name' - ); - - $directorServiceObj = current($directorAppliedServices); - - break; + $isAppliedService = isset($appliedForServiceLookup[$serviceName]); + } else { + $isAppliedService = false; + } } + + if (! $isAppliedService) { + continue; + } + + $query = $db->getDbAdapter()->select()->from('icinga_service') + ->where('object_name = ?', $appliedService->name) + ->where("object_type = 'apply'") + ->where('assign_filter = ?', $appliedService->assign_filter); + + $directorAppliedServices = IcingaService::loadAll( + $db, + $query, + 'object_name' + ); + + $directorServiceObj = current($directorAppliedServices); + + break; } } elseif ($serviceOrigin[$i] === 'service-set') { $templateResolver = new IcingaTemplateResolver($directorHostObj); From 42a47d61399d9996af73b27eb32300cec776bd52 Mon Sep 17 00:00:00 2001 From: raviks789 Date: Mon, 17 Mar 2025 10:14:46 +0100 Subject: [PATCH 04/13] CustomVarRenderer: Fix illegal offset error if the value is an array --- .../Icingadb/CustomVarRenderer.php | 19 ++++++++++++++++++- .../Monitoring/CustomVarRenderer.php | 19 ++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/library/Director/ProvidedHook/Icingadb/CustomVarRenderer.php b/library/Director/ProvidedHook/Icingadb/CustomVarRenderer.php index fa609a72..933fb33c 100644 --- a/library/Director/ProvidedHook/Icingadb/CustomVarRenderer.php +++ b/library/Director/ProvidedHook/Icingadb/CustomVarRenderer.php @@ -274,7 +274,24 @@ class CustomVarRenderer extends CustomVarRendererHook return '***'; } - if (isset($this->datalistMaps[$key][$value])) { + if (is_array($value)) { + $renderedValue = []; + foreach ($value as $v) { + if (is_string($v) && isset($this->datalistMaps[$key][$v])) { + $renderedValue[] = new HtmlElement( + 'span', + Attributes::create(['title' => $this->datalistMaps[$key][$v] . " [$v]"]), + Text::create($this->datalistMaps[$key][$v]) + ); + } else { + $renderedValue[] = $v; + } + } + + return $renderedValue; + } + + if (is_string($value) && isset($this->datalistMaps[$key][$value])) { return new HtmlElement( 'span', Attributes::create(['title' => $this->datalistMaps[$key][$value] . " [$value]"]), diff --git a/library/Director/ProvidedHook/Monitoring/CustomVarRenderer.php b/library/Director/ProvidedHook/Monitoring/CustomVarRenderer.php index 8601828b..40d2d006 100644 --- a/library/Director/ProvidedHook/Monitoring/CustomVarRenderer.php +++ b/library/Director/ProvidedHook/Monitoring/CustomVarRenderer.php @@ -275,7 +275,24 @@ class CustomVarRenderer extends CustomVarRendererHook return '***'; } - if (isset($this->datalistMaps[$key][$value])) { + if (is_array($value)) { + $renderedValue = []; + foreach ($value as $v) { + if (is_string($v) && isset($this->datalistMaps[$key][$v])) { + $renderedValue[] = new HtmlElement( + 'span', + Attributes::create(['title' => $this->datalistMaps[$key][$v] . " [$v]"]), + Text::create($this->datalistMaps[$key][$v]) + ); + } else { + $renderedValue[] = $v; + } + } + + return $renderedValue; + } + + if (is_string($value) && isset($this->datalistMaps[$key][$value])) { return new HtmlElement( 'span', Attributes::create(['title' => $this->datalistMaps[$key][$value] . " [$value]"]), From b18cf4f80d1fd040d1f6f3b67d91bc90739e92d1 Mon Sep 17 00:00:00 2001 From: raviks789 Date: Tue, 14 Jan 2025 17:46:43 +0100 Subject: [PATCH 05/13] Fix editing of custom variables for multi-selected objects --- application/forms/IcingaMultiEditForm.php | 5 +---- library/Director/Web/Form/IcingaObjectFieldLoader.php | 10 ++++++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/application/forms/IcingaMultiEditForm.php b/application/forms/IcingaMultiEditForm.php index 4849d467..7f0c0d11 100644 --- a/application/forms/IcingaMultiEditForm.php +++ b/application/forms/IcingaMultiEditForm.php @@ -51,6 +51,7 @@ class IcingaMultiEditForm extends DirectorObjectForm $loader = new IcingaObjectFieldLoader($object); $loader->prepareElements($this); $loader->addFieldsToForm($this); + $this->varNameMap = $loader->getNameMap(); if ($form = $this->relatedForm) { if ($form instanceof DirectorObjectForm) { @@ -237,10 +238,6 @@ class IcingaMultiEditForm extends DirectorObjectForm $this->removeElement($key); $label = $element->getLabel(); - if ($this->isCustomVar($key)) { - $this->varNameMap[$key] = $label; - } - $group = $this->getDisplayGroupForElement($element); $description = $element->getDescription(); diff --git a/library/Director/Web/Form/IcingaObjectFieldLoader.php b/library/Director/Web/Form/IcingaObjectFieldLoader.php index 83b1aa6d..cf58b9eb 100644 --- a/library/Director/Web/Form/IcingaObjectFieldLoader.php +++ b/library/Director/Web/Form/IcingaObjectFieldLoader.php @@ -60,6 +60,16 @@ class IcingaObjectFieldLoader return $this; } + /** + * Get element names to variable names map (Example: ['elName' => 'varName']) + * + * @return array + */ + public function getNameMap(): array + { + return $this->nameMap; + } + public function loadFieldsForMultipleObjects($objects) { $fields = array(); From 5f4ad0f8273ca853c538886fab944e9ff6d461a5 Mon Sep 17 00:00:00 2001 From: raviks789 Date: Tue, 18 Feb 2025 13:25:00 +0100 Subject: [PATCH 06/13] IcingaAddServiceForm: Check if service templates exist to add services --- application/forms/IcingaAddServiceForm.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/application/forms/IcingaAddServiceForm.php b/application/forms/IcingaAddServiceForm.php index 60ccb6f3..bd9a3771 100644 --- a/application/forms/IcingaAddServiceForm.php +++ b/application/forms/IcingaAddServiceForm.php @@ -30,7 +30,13 @@ class IcingaAddServiceForm extends DirectorObjectForm ); } - $this->addSingleImportElement(); + $this->addSingleImportElement(true); + + if (empty($this->enumServiceTemplates())) { + $this->setSubmitLabel(false); + + return; + } if (! ($imports = $this->getSentOrObjectValue('imports'))) { $this->setSubmitLabel($this->translate('Next')); From 63dac34b0f8dfb296249896b5b77da7233268bd0 Mon Sep 17 00:00:00 2001 From: raviks789 Date: Tue, 4 Feb 2025 14:07:03 +0100 Subject: [PATCH 07/13] `director_job`: Change timestamp data types to integer --- schema/mysql-migrations/upgrade_189.sql | 17 +++++++++++++++++ schema/mysql.sql | 6 +++--- schema/pgsql-migrations/upgrade_189.sql | 18 ++++++++++++++++++ schema/pgsql.sql | 6 +++--- 4 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 schema/mysql-migrations/upgrade_189.sql create mode 100644 schema/pgsql-migrations/upgrade_189.sql diff --git a/schema/mysql-migrations/upgrade_189.sql b/schema/mysql-migrations/upgrade_189.sql new file mode 100644 index 00000000..c8b91933 --- /dev/null +++ b/schema/mysql-migrations/upgrade_189.sql @@ -0,0 +1,17 @@ +ALTER TABLE director_job ADD COLUMN ts_last_attempt_tmp BIGINT(20) DEFAULT NULL; +ALTER TABLE director_job ADD COLUMN ts_last_error_tmp BIGINT(20) DEFAULT NULL; + + +UPDATE director_job +SET ts_last_attempt_tmp = UNIX_TIMESTAMP(ts_last_attempt) * 1000, + ts_last_error_tmp = UNIX_TIMESTAMP(ts_last_error) * 1000; + +ALTER TABLE director_job + DROP COLUMN ts_last_attempt, + DROP COLUMN ts_last_error, + CHANGE ts_last_attempt_tmp ts_last_attempt BIGINT(20) DEFAULT NULL, + CHANGE ts_last_error_tmp ts_last_error BIGINT(20) DEFAULT NULL; + +INSERT INTO director_schema_migration +(schema_version, migration_time) +VALUES (189, NOW()); \ No newline at end of file diff --git a/schema/mysql.sql b/schema/mysql.sql index dd8a0f84..8052ecde 100644 --- a/schema/mysql.sql +++ b/schema/mysql.sql @@ -347,8 +347,8 @@ CREATE TABLE director_job ( run_interval INT(10) UNSIGNED NOT NULL, -- seconds timeperiod_id INT(10) UNSIGNED DEFAULT NULL, last_attempt_succeeded ENUM('y', 'n') DEFAULT NULL, - ts_last_attempt TIMESTAMP NULL DEFAULT NULL, - ts_last_error TIMESTAMP NULL DEFAULT NULL, + ts_last_attempt BIGINT(20) NULL DEFAULT NULL, + ts_last_error BIGINT(20) NULL DEFAULT NULL, last_error_message TEXT DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY (job_name), @@ -2446,4 +2446,4 @@ CREATE TABLE branched_icinga_dependency ( INSERT INTO director_schema_migration (schema_version, migration_time) - VALUES (188, NOW()); + VALUES (189, NOW()); diff --git a/schema/pgsql-migrations/upgrade_189.sql b/schema/pgsql-migrations/upgrade_189.sql new file mode 100644 index 00000000..6d6fdadf --- /dev/null +++ b/schema/pgsql-migrations/upgrade_189.sql @@ -0,0 +1,18 @@ +ALTER TABLE director_job ADD COLUMN ts_last_attempt_tmp bigint DEFAULT NULL; +ALTER TABLE director_job ADD COLUMN ts_last_error_tmp bigint DEFAULT NULL; + + +UPDATE director_job +SET ts_last_attempt_tmp = UNIX_TIMESTAMP(ts_last_attempt) * 1000, + ts_last_error_tmp = UNIX_TIMESTAMP(ts_last_error) * 1000; + +ALTER TABLE director_job + DROP COLUMN ts_last_attempt, + DROP COLUMN ts_last_error; + +ALTER TABLE director_job RENAME COLUMN ts_last_attempt_tmp TO ts_last_attempt; +ALTER TABLE director_job RENAME COLUMN ts_last_error_tmp TO ts_last_error; + +INSERT INTO director_schema_migration + (schema_version, migration_time) + VALUES (189, NOW()); diff --git a/schema/pgsql.sql b/schema/pgsql.sql index 694d8c90..c9f8183d 100644 --- a/schema/pgsql.sql +++ b/schema/pgsql.sql @@ -448,8 +448,8 @@ CREATE TABLE director_job ( run_interval integer NOT NULL, -- seconds timeperiod_id integer DEFAULT NULL, last_attempt_succeeded enum_boolean DEFAULT NULL, - ts_last_attempt timestamp with time zone DEFAULT NULL, - ts_last_error timestamp with time zone DEFAULT NULL, + ts_last_attempt bigint DEFAULT NULL, + ts_last_error bigint DEFAULT NULL, last_error_message text NULL DEFAULT NULL, CONSTRAINT director_job_period FOREIGN KEY (timeperiod_id) @@ -2781,4 +2781,4 @@ CREATE INDEX branched_dependency_search_object_name ON branched_icinga_dependenc INSERT INTO director_schema_migration (schema_version, migration_time) - VALUES (187, NOW()); + VALUES (189, NOW()); From 4c283a24c8794975f0580be035a97a1555f9a2d1 Mon Sep 17 00:00:00 2001 From: raviks789 Date: Tue, 4 Feb 2025 15:43:18 +0100 Subject: [PATCH 08/13] Fix: Support data type change of columns `ts_last_attempt` and `ts_last_error` from timestamp to bigint --- library/Director/Objects/DirectorJob.php | 14 +++++++++----- library/Director/Web/Table/JobTable.php | 6 +++--- library/Director/Web/Widget/JobDetails.php | 2 +- schema/mysql-migrations/upgrade_189.sql | 2 +- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/library/Director/Objects/DirectorJob.php b/library/Director/Objects/DirectorJob.php index aadcabc0..a4a9eb61 100644 --- a/library/Director/Objects/DirectorJob.php +++ b/library/Director/Objects/DirectorJob.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Director\Objects; use Icinga\Exception\NotFoundError; +use Icinga\Module\Director\Daemon\DaemonUtil; use Icinga\Module\Director\Daemon\Logger; use Icinga\Module\Director\Data\Db\DbObjectWithSettings; use Icinga\Module\Director\Db; @@ -84,7 +85,8 @@ class DirectorJob extends DbObjectWithSettings implements ExportInterface, Insta public function run() { $job = $this->getInstance(); - $this->set('ts_last_attempt', date('Y-m-d H:i:s')); + $currentTimestamp = DaemonUtil::timestampWithMilliseconds(); + $this->set('ts_last_attempt', $currentTimestamp); try { $job->run(); @@ -92,7 +94,7 @@ class DirectorJob extends DbObjectWithSettings implements ExportInterface, Insta $success = true; } catch (Exception $e) { Logger::error($e->getMessage()); - $this->set('ts_last_error', date('Y-m-d H:i:s')); + $this->set('ts_last_error', $currentTimestamp); $this->set('last_error_message', $e->getMessage()); $this->set('last_attempt_succeeded', 'n'); $success = false; @@ -127,8 +129,8 @@ class DirectorJob extends DbObjectWithSettings implements ExportInterface, Insta } return ( - strtotime($this->get('ts_last_attempt')) + $this->get('run_interval') * 2 - ) < time(); + $this->get('ts_last_attempt') + $this->get('run_interval') * 2 * 1000 + ) < DaemonUtil::timestampWithMilliseconds(); } public function hasBeenDisabled() @@ -145,7 +147,9 @@ class DirectorJob extends DbObjectWithSettings implements ExportInterface, Insta return $this->isWithinTimeperiod(); } - if (strtotime($this->get('ts_last_attempt')) + $this->get('run_interval') < time()) { + if ( + $this->get('ts_last_attempt') + $this->get('run_interval') * 1000 < DaemonUtil::timestampWithMilliseconds() + ) { return $this->isWithinTimeperiod(); } diff --git a/library/Director/Web/Table/JobTable.php b/library/Director/Web/Table/JobTable.php index 81ba07b8..c9b3e85e 100644 --- a/library/Director/Web/Table/JobTable.php +++ b/library/Director/Web/Table/JobTable.php @@ -4,6 +4,7 @@ namespace Icinga\Module\Director\Web\Table; use gipfl\IcingaWeb2\Link; use gipfl\IcingaWeb2\Table\ZfQueryBasedTable; +use Icinga\Module\Director\Daemon\DaemonUtil; class JobTable extends ZfQueryBasedTable { @@ -37,11 +38,11 @@ class JobTable extends ZfQueryBasedTable protected function getJobClasses($row) { - if ($row->unixts_last_attempt === null) { + if ($row->ts_last_attempt === null) { return 'pending'; } - if ($row->unixts_last_attempt + $row->run_interval < time()) { + if ($row->ts_last_attempt + $row->run_interval * 1000 < DaemonUtil::timestampWithMilliseconds()) { return 'pending'; } @@ -73,7 +74,6 @@ class JobTable extends ZfQueryBasedTable 'run_interval' => 'j.run_interval', 'last_attempt_succeeded' => 'j.last_attempt_succeeded', 'ts_last_attempt' => 'j.ts_last_attempt', - 'unixts_last_attempt' => 'UNIX_TIMESTAMP(j.ts_last_attempt)', 'ts_last_error' => 'j.ts_last_error', 'last_error_message' => 'j.last_error_message', ] diff --git a/library/Director/Web/Widget/JobDetails.php b/library/Director/Web/Widget/JobDetails.php index 3a530a25..b794d378 100644 --- a/library/Director/Web/Widget/JobDetails.php +++ b/library/Director/Web/Widget/JobDetails.php @@ -45,7 +45,7 @@ class JobDetails extends HtmlDocument $tsLastAttempt = $job->get('ts_last_attempt'); if ($tsLastAttempt) { - $ts = \strtotime($tsLastAttempt); + $ts = $tsLastAttempt / 1000; $timeAgo = Html::tag('span', [ 'class' => 'time-ago', 'title' => DateFormatter::formatDateTime($ts) diff --git a/schema/mysql-migrations/upgrade_189.sql b/schema/mysql-migrations/upgrade_189.sql index c8b91933..4a52fc0d 100644 --- a/schema/mysql-migrations/upgrade_189.sql +++ b/schema/mysql-migrations/upgrade_189.sql @@ -14,4 +14,4 @@ ALTER TABLE director_job INSERT INTO director_schema_migration (schema_version, migration_time) -VALUES (189, NOW()); \ No newline at end of file +VALUES (189, NOW()); From 38f3fe33c90f6a3a80822f1ea5310894aba92ba8 Mon Sep 17 00:00:00 2001 From: raviks789 Date: Tue, 25 Mar 2025 13:49:47 +0100 Subject: [PATCH 09/13] Release 1.11.4 --- doc/02-Installation.md.d/From-Source.md | 4 ++-- doc/82-Changelog.md | 29 +++++++++++++++++++++++++ module.info | 2 +- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/doc/02-Installation.md.d/From-Source.md b/doc/02-Installation.md.d/From-Source.md index c8c5d1ea..377337b4 100644 --- a/doc/02-Installation.md.d/From-Source.md +++ b/doc/02-Installation.md.d/From-Source.md @@ -41,7 +41,7 @@ and extract it to a folder named `director` in one of your Icinga Web module pat You might want to use a script as follows for this task: ```shell -MODULE_VERSION="1.11.3" +MODULE_VERSION="1.11.4" ICINGAWEB_MODULEPATH="/usr/share/icingaweb2/modules" REPO_URL="https://github.com/icinga/icingaweb2-module-director" TARGET_DIR="${ICINGAWEB_MODULEPATH}/director" @@ -60,7 +60,7 @@ Simply clone the repository in one of your Icinga web module path directories. You might want to use a script as follows for this task: ```shell -MODULE_VERSION="1.11.3" +MODULE_VERSION="1.11.4" ICINGAWEB_MODULEPATH="/usr/share/icingaweb2/modules" REPO_URL="https://github.com/icinga/icingaweb2-module-director" TARGET_DIR="${ICINGAWEB_MODULEPATH}/director" diff --git a/doc/82-Changelog.md b/doc/82-Changelog.md index 7ca8c330..3fcf093f 100644 --- a/doc/82-Changelog.md +++ b/doc/82-Changelog.md @@ -4,6 +4,35 @@ Please make sure to always read our [Upgrading](05-Upgrading.md) documentation before switching to a new version. +v1.11.4 +------- + +### Security + +- Rest API endpoints accessible to restricted users ([GHSA-3233-ggc5-m3qg](https://github.com/Icinga/icingaweb2-module-director/security/advisories/GHSA-3233-ggc5-m3qg)) + +### UI + +- Fix editing of custom variables for multi-selected objects [#2950](https://github.com/Icinga/icingaweb2-module-director/issues/2950) +- Fix: Check for the existence of service templates to add services [#1249](https://github.com/Icinga/icingaweb2-module-director/issues/1249) + +### Import and Sync + +- Fix erratic job behavior during summer and winter time change (no issue) + +### Integration + +- Fix custom variable renderer for service apply for rules (no issue) +- Fix custom variable renderer for array type data lists [#2960](https://github.com/Icinga/icingaweb2-module-director/issues/2960) + +### Database Schema + +- Fix MySQL 8.4 nonstandard foreign keys deprecation [#2885](https://github.com/Icinga/icingaweb2-module-director/issues/2885) + +### Fixed Issues + +You can find issues related to this release on our [roadmap](https://github.com/Icinga/icingaweb2-module-director/milestone/39?closed=1) + v1.11.3 ------- diff --git a/module.info b/module.info index 1c448edf..ddd97ffd 100644 --- a/module.info +++ b/module.info @@ -1,5 +1,5 @@ Name: Icinga Director -Version: 1.11.3 +Version: 1.11.4 Depends: reactbundle (>=0.9.0), ipl (>=0.5.0), incubator (>=0.22.0) Description: Director - Config tool for Icinga 2 Icinga Director is a configuration tool that has been designed to make From 2423fad54cbc9bf8a20fe7e61dab77964546f3cb Mon Sep 17 00:00:00 2001 From: raviks789 Date: Tue, 1 Apr 2025 16:57:49 +0200 Subject: [PATCH 10/13] upgrade_188: Remove entries with duplicate checksums in director_activity_log Update schema/mysql-migrations/upgrade_188.sql --- schema/mysql-migrations/upgrade_188.sql | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/schema/mysql-migrations/upgrade_188.sql b/schema/mysql-migrations/upgrade_188.sql index 1c016195..d3b916bb 100644 --- a/schema/mysql-migrations/upgrade_188.sql +++ b/schema/mysql-migrations/upgrade_188.sql @@ -1,12 +1,17 @@ +ALTER TABLE director_generated_config + DROP FOREIGN KEY director_generated_config_activity; + +# Delete all entries with duplicate checksum except the first entry +DELETE log1 FROM director_activity_log log1 + INNER JOIN director_activity_log log2 ON log1.checksum = log2.checksum + WHERE log1.id > log2.id; + ALTER TABLE director_activity_log DROP INDEX checksum, ADD UNIQUE INDEX checksum (checksum); ALTER TABLE director_generated_config -DROP FOREIGN KEY director_generated_config_activity; - -ALTER TABLE director_generated_config -ADD CONSTRAINT director_generated_config_activity + ADD CONSTRAINT director_generated_config_activity FOREIGN KEY (last_activity_checksum) REFERENCES director_activity_log (checksum) ON DELETE RESTRICT From 11a10be9f8ef492fa4463648b8b31e13327c5648 Mon Sep 17 00:00:00 2001 From: raviks789 Date: Wed, 23 Apr 2025 15:55:31 +0200 Subject: [PATCH 11/13] DeployFormsBug7530: Do not pass null values to `strlen` Since PHP 8.0 passing of null to strlen is deprecated and hence must be avoided. --- application/forms/DeployFormsBug7530.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/forms/DeployFormsBug7530.php b/application/forms/DeployFormsBug7530.php index b31d3563..0d127f9e 100644 --- a/application/forms/DeployFormsBug7530.php +++ b/application/forms/DeployFormsBug7530.php @@ -13,7 +13,7 @@ trait DeployFormsBug7530 if (parent::hasBeenSubmitted()) { return true; } else { - return \strlen($this->getSentValue('confirm_7530')) > 0; + return strlen($this->getSentValue('confirm_7530', '')) > 0; } } From 177f300ac301c9466c016ab584ae39d37cdf1baa Mon Sep 17 00:00:00 2001 From: raviks789 Date: Tue, 8 Apr 2025 14:17:09 +0200 Subject: [PATCH 12/13] Release version 1.11.5 --- doc/02-Installation.md.d/From-Source.md | 4 ++-- doc/82-Changelog.md | 15 +++++++++++++++ module.info | 2 +- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/doc/02-Installation.md.d/From-Source.md b/doc/02-Installation.md.d/From-Source.md index 377337b4..d4e894f1 100644 --- a/doc/02-Installation.md.d/From-Source.md +++ b/doc/02-Installation.md.d/From-Source.md @@ -41,7 +41,7 @@ and extract it to a folder named `director` in one of your Icinga Web module pat You might want to use a script as follows for this task: ```shell -MODULE_VERSION="1.11.4" +MODULE_VERSION="1.11.5" ICINGAWEB_MODULEPATH="/usr/share/icingaweb2/modules" REPO_URL="https://github.com/icinga/icingaweb2-module-director" TARGET_DIR="${ICINGAWEB_MODULEPATH}/director" @@ -60,7 +60,7 @@ Simply clone the repository in one of your Icinga web module path directories. You might want to use a script as follows for this task: ```shell -MODULE_VERSION="1.11.4" +MODULE_VERSION="1.11.5" ICINGAWEB_MODULEPATH="/usr/share/icingaweb2/modules" REPO_URL="https://github.com/icinga/icingaweb2-module-director" TARGET_DIR="${ICINGAWEB_MODULEPATH}/director" diff --git a/doc/82-Changelog.md b/doc/82-Changelog.md index 3fcf093f..e8f392ef 100644 --- a/doc/82-Changelog.md +++ b/doc/82-Changelog.md @@ -4,6 +4,21 @@ Please make sure to always read our [Upgrading](05-Upgrading.md) documentation before switching to a new version. +v1.11.5 +------- + +## Database Schema + +- FIX: Failed schema migration of MySQL to version 188 due Integrity constraint violation ([#2970](https://github.com/Icinga/icingaweb2-module-director/issues/2970)) + +## Deployment + +- Fix: Deprecation notice that sometimes appears on deployment form (no issue) + +### Fixed Issues + +You can find issues related to this release on our [roadmap](https://github.com/Icinga/icingaweb2-module-director/milestone/40?closed=1) + v1.11.4 ------- diff --git a/module.info b/module.info index ddd97ffd..6a3ffe66 100644 --- a/module.info +++ b/module.info @@ -1,5 +1,5 @@ Name: Icinga Director -Version: 1.11.4 +Version: 1.11.5 Depends: reactbundle (>=0.9.0), ipl (>=0.5.0), incubator (>=0.22.0) Description: Director - Config tool for Icinga 2 Icinga Director is a configuration tool that has been designed to make From 6749b4fb08e4e3ae6371df6dd8f8d081cb871c70 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 3 Jun 2025 16:17:00 +0200 Subject: [PATCH 13/13] L10n-update: Use template from github-actions --- .github/workflows/L10n-update.yml | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/.github/workflows/L10n-update.yml b/.github/workflows/L10n-update.yml index 000b5348..46f7c15f 100644 --- a/.github/workflows/L10n-update.yml +++ b/.github/workflows/L10n-update.yml @@ -6,15 +6,6 @@ on: - master jobs: - trigger-update: - name: L10n Update Trigger - runs-on: ubuntu-latest - - steps: - - name: Repository dispatch - uses: peter-evans/repository-dispatch@v3 - with: - token: ${{ secrets.ICINGABOT_TOKEN }} - repository: Icinga/L10n - event-type: update - client-payload: '{"origin": "${{ github.repository }}", "commit": "${{ github.sha }}"}' + update: + uses: icinga/github-actions/.github/workflows/L10n-update.yml@main + secrets: inherit