schema: add UUIDs for datalist and datafield

fixes #2696
This commit is contained in:
Thomas Gelf 2023-03-07 18:29:30 +01:00
parent 6c611a5db4
commit b7e887b251
6 changed files with 89 additions and 54 deletions

View File

@ -2,28 +2,30 @@
namespace Icinga\Module\Director\Objects; namespace Icinga\Module\Director\Objects;
use gipfl\Json\JsonString;
use Icinga\Module\Director\Core\Json; use Icinga\Module\Director\Core\Json;
use Icinga\Module\Director\Data\Db\DbObjectWithSettings; use Icinga\Module\Director\Data\Db\DbObjectWithSettings;
use Icinga\Module\Director\Db; use Icinga\Module\Director\Db;
use Icinga\Module\Director\DirectorObject\Automation\CompareBasketObject; use Icinga\Module\Director\DirectorObject\Automation\CompareBasketObject;
use Icinga\Module\Director\Exception\DuplicateKeyException; use Icinga\Module\Director\Exception\JsonEncodeException;
use Icinga\Module\Director\Forms\IcingaServiceForm; use Icinga\Module\Director\Forms\IcingaServiceForm;
use Icinga\Module\Director\Hook\DataTypeHook; use Icinga\Module\Director\Hook\DataTypeHook;
use Icinga\Module\Director\Resolver\OverriddenVarsResolver; use Icinga\Module\Director\Resolver\OverriddenVarsResolver;
use Icinga\Module\Director\Web\Form\DirectorObjectForm; use Icinga\Module\Director\Web\Form\DirectorObjectForm;
use InvalidArgumentException; use Ramsey\Uuid\Uuid;
use stdClass;
use Zend_Form_Element as ZfElement; use Zend_Form_Element as ZfElement;
class DirectorDatafield extends DbObjectWithSettings class DirectorDatafield extends DbObjectWithSettings
{ {
protected $table = 'director_datafield'; protected $table = 'director_datafield';
protected $keyName = 'id'; protected $keyName = 'id';
protected $autoincKeyName = 'id'; protected $autoincKeyName = 'id';
protected $uuidColumn = 'uuid';
protected $defaultProperties = [ protected $defaultProperties = [
'id' => null, 'id' => null,
'uuid' => null,
'category_id' => null, 'category_id' => null,
'varname' => null, 'varname' => null,
'caption' => null, 'caption' => null,
@ -124,8 +126,10 @@ class DirectorDatafield extends DbObjectWithSettings
public function export() public function export()
{ {
$plain = (object) $this->getProperties(); $plain = (object) $this->getProperties();
$plain->originalId = $plain->id;
unset($plain->id); unset($plain->id);
if ($uuid = $this->get('uuid')) {
$plain->uuid = Uuid::fromBytes($uuid)->toString();
}
$plain->settings = (object) $this->getSettings(); $plain->settings = (object) $this->getSettings();
if (property_exists($plain->settings, 'datalist_id')) { if (property_exists($plain->settings, 'datalist_id')) {
@ -144,63 +148,44 @@ class DirectorDatafield extends DbObjectWithSettings
} }
/** /**
* @param $plain
* @param Db $db
* @param bool $replace
* @return DirectorDatafield
* @throws \Icinga\Exception\NotFoundError * @throws \Icinga\Exception\NotFoundError
* @throws JsonEncodeException
*/ */
public static function import($plain, Db $db, $replace = false) public static function import(stdClass $plain, Db $db): DirectorDatafield
{ {
$properties = (array) $plain; $dba = $db->getDbAdapter();
if (isset($properties['originalId'])) { if ($uuid = $plain->uuid ?? null) {
$id = $properties['originalId']; $uuid = Uuid::fromString($uuid);
unset($properties['originalId']); if ($candidate = DirectorDatafield::loadWithUniqueId($uuid, $db)) {
} else { self::fixOptionalDatalistReference($plain, $db);
$id = null; assert($candidate instanceof DirectorDatafield);
} $candidate->setProperties((array) $plain);
return $candidate;
if (isset($properties['settings']->datalist)) {
// Just try to load the list, import should fail if missing
$list = DirectorDatalist::load(
$properties['settings']->datalist,
$db
);
} else {
$list = null;
}
$compare = Json::decode(Json::encode($properties));
if ($id && static::exists($id, $db)) {
$existing = static::loadWithAutoIncId($id, $db);
$existingProperties = (array) $existing->export();
unset($existingProperties['originalId']);
if (CompareBasketObject::equals((object) $compare, (object) $existingProperties)) {
return $existing;
} }
} }
$query = $dba->select()->from('director_datafield')->where('varname = ?', $plain->varname);
if ($list) {
unset($properties['settings']->datalist);
$properties['settings']->datalist_id = $list->get('id');
}
$dba = $db->getDbAdapter();
$query = $dba->select()
->from('director_datafield')
->where('varname = ?', $plain->varname);
$candidates = DirectorDatafield::loadAll($db, $query); $candidates = DirectorDatafield::loadAll($db, $query);
foreach ($candidates as $candidate) { foreach ($candidates as $candidate) {
$export = $candidate->export(); $export = $candidate->export();
unset($export->originalId);
CompareBasketObject::normalize($export); CompareBasketObject::normalize($export);
if (CompareBasketObject::equals($export, $compare)) { if (CompareBasketObject::equals($export, $plain)) {
return $candidate; return $candidate;
} }
} }
self::fixOptionalDatalistReference($plain, $db);
return static::create($properties, $db); return static::create((array) $plain, $db);
}
protected static function fixOptionalDatalistReference(stdClass $plain, Db $db)
{
if (isset($plain->settings->datalist)) {
// Just try to load the list, import should fail if missing
$list = DirectorDatalist::load($plain->settings->datalist, $db);
unset($plain->settings->datalist);
$plain->settings->datalist_id = $list->get('id');
}
} }
protected function beforeStore() protected function beforeStore()

View File

@ -11,16 +11,16 @@ use Icinga\Module\Director\Exception\DuplicateKeyException;
class DirectorDatalist extends DbObject implements ExportInterface class DirectorDatalist extends DbObject implements ExportInterface
{ {
protected $table = 'director_datalist'; protected $table = 'director_datalist';
protected $keyName = 'list_name'; protected $keyName = 'list_name';
protected $autoincKeyName = 'id'; protected $autoincKeyName = 'id';
protected $uuidColumn = 'uuid';
protected $defaultProperties = array( protected $defaultProperties = [
'id' => null, 'id' => null,
'uuid' => null,
'list_name' => null, 'list_name' => null,
'owner' => null 'owner' => null
); ];
/** @var DirectorDatalistEntry[] */ /** @var DirectorDatalistEntry[] */
protected $storedEntries; protected $storedEntries;

View File

@ -0,0 +1,35 @@
ALTER TABLE director_datafield ADD COLUMN uuid VARBINARY(16) DEFAULT NULL AFTER id;
SET @tmp_uuid = LOWER(CONCAT(
LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0'),
LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0'), '-',
LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0'), '-',
'4',
LPAD(HEX(FLOOR(RAND() * 0x0fff)), 3, '0'), '-',
HEX(FLOOR(RAND() * 4 + 8)),
LPAD(HEX(FLOOR(RAND() * 0x0fff)), 3, '0'), '-',
LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0'),
LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0'),
LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0')
));
UPDATE director_datafield SET uuid = UNHEX(LPAD(LPAD(HEX(id), 8, '0'), 32, REPLACE(@tmp_uuid, '-', ''))) WHERE uuid IS NULL;
ALTER TABLE director_datafield MODIFY COLUMN uuid VARBINARY(16) NOT NULL, ADD UNIQUE INDEX uuid (uuid);
ALTER TABLE director_datalist ADD COLUMN uuid VARBINARY(16) DEFAULT NULL AFTER id;
SET @tmp_uuid = LOWER(CONCAT(
LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0'),
LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0'), '-',
LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0'), '-',
'4',
LPAD(HEX(FLOOR(RAND() * 0x0fff)), 3, '0'), '-',
HEX(FLOOR(RAND() * 4 + 8)),
LPAD(HEX(FLOOR(RAND() * 0x0fff)), 3, '0'), '-',
LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0'),
LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0'),
LPAD(HEX(FLOOR(RAND() * 0xffff)), 4, '0')
));
UPDATE director_datalist SET uuid = UNHEX(LPAD(LPAD(HEX(id), 8, '0'), 32, REPLACE(@tmp_uuid, '-', ''))) WHERE uuid IS NULL;
ALTER TABLE director_datalist MODIFY COLUMN uuid VARBINARY(16) NOT NULL, ADD UNIQUE INDEX uuid (uuid);
INSERT INTO director_schema_migration
(schema_version, migration_time)
VALUES (186, NOW());

View File

@ -177,6 +177,7 @@ CREATE TABLE director_deployment_log (
CREATE TABLE director_datalist ( CREATE TABLE director_datalist (
id INT(10) UNSIGNED AUTO_INCREMENT NOT NULL, id INT(10) UNSIGNED AUTO_INCREMENT NOT NULL,
uuid VARBINARY(16) NOT NULL,
list_name VARCHAR(255) NOT NULL, list_name VARCHAR(255) NOT NULL,
owner VARCHAR(255) NOT NULL, owner VARCHAR(255) NOT NULL,
PRIMARY KEY (id), PRIMARY KEY (id),
@ -207,6 +208,7 @@ CREATE TABLE director_datafield_category (
CREATE TABLE director_datafield ( CREATE TABLE director_datafield (
id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
uuid VARBINARY(16) NOT NULL,
category_id INT(10) UNSIGNED DEFAULT NULL, category_id INT(10) UNSIGNED DEFAULT NULL,
varname VARCHAR(64) NOT NULL COLLATE utf8_bin, varname VARCHAR(64) NOT NULL COLLATE utf8_bin,
caption VARCHAR(255) NOT NULL, caption VARCHAR(255) NOT NULL,
@ -2443,4 +2445,4 @@ CREATE TABLE branched_icinga_dependency (
INSERT INTO director_schema_migration INSERT INTO director_schema_migration
(schema_version, migration_time) (schema_version, migration_time)
VALUES (184, NOW()); VALUES (186, NOW());

View File

@ -0,0 +1,11 @@
ALTER TABLE director_datafield ADD COLUMN uuid bytea UNIQUE CHECK(LENGTH(uuid) = 16);
UPDATE director_datafield SET uuid = decode(replace(gen_random_uuid()::text, '-', ''), 'hex') WHERE uuid IS NULL;
ALTER TABLE director_datafield ALTER COLUMN uuid SET NOT NULL;
ALTER TABLE director_datalist ADD COLUMN uuid bytea UNIQUE CHECK(LENGTH(uuid) = 16);
UPDATE director_datalist SET uuid = decode(replace(gen_random_uuid()::text, '-', ''), 'hex') WHERE uuid IS NULL;
ALTER TABLE director_datalist ALTER COLUMN uuid SET NOT NULL;
INSERT INTO director_schema_migration
(schema_version, migration_time)
VALUES (186, NOW());

View File

@ -246,6 +246,7 @@ CREATE INDEX start_time_idx ON director_deployment_log (start_time);
CREATE TABLE director_datalist ( CREATE TABLE director_datalist (
id serial, id serial,
uuid bytea CHECK(LENGTH(uuid) = 16) NOT NULL,
list_name character varying(255) NOT NULL, list_name character varying(255) NOT NULL,
owner character varying(255) NOT NULL, owner character varying(255) NOT NULL,
PRIMARY KEY (id) PRIMARY KEY (id)
@ -283,6 +284,7 @@ CREATE UNIQUE INDEX datafield_category_name ON director_datafield_category (cate
CREATE TABLE director_datafield ( CREATE TABLE director_datafield (
id serial, id serial,
uuid bytea CHECK(LENGTH(uuid) = 16) NOT NULL,
category_id integer DEFAULT NULL, category_id integer DEFAULT NULL,
varname character varying(64) NOT NULL, varname character varying(64) NOT NULL,
caption character varying(255) NOT NULL, caption character varying(255) NOT NULL,
@ -2782,4 +2784,4 @@ CREATE INDEX branched_dependency_search_object_name ON branched_icinga_dependenc
INSERT INTO director_schema_migration INSERT INTO director_schema_migration
(schema_version, migration_time) (schema_version, migration_time)
VALUES (184, NOW()); VALUES (186, NOW());