Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

db: Add tables and models for config storage #4778

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions etc/schema/mysql-upgrades/2.11.0.sql
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,22 @@ ALTER TABLE `icingaweb_user_preference`
MODIFY COLUMN `username` varchar(254) COLLATE utf8mb4_unicode_ci NOT NULL,
MODIFY COLUMN `section` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
MODIFY COLUMN `name` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL;

CREATE TABLE `icingaweb_config_scope`(
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`module` varchar(254) NOT NULL DEFAULT 'default',
`name` varchar(254) COLLATE utf8mb4_unicode_ci NOT NULL,
`hash` binary(20) NOT NULL COMMENT 'sha1(all option tuples)',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_module_name` (`module`, `name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `icingaweb_config_option`(
`scope_id` int(10) unsigned NOT NULL,
`name` varchar(254) NOT NULL,
`value` text DEFAULT NULL,
UNIQUE KEY `idx_scope_id_name` (`scope_id`, `name`),
CONSTRAINT `fk_scope_id_config_scope` FOREIGN KEY (`scope_id`)
REFERENCES `icingaweb_config_scope` (`id`)
ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
19 changes: 19 additions & 0 deletions etc/schema/mysql.schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,22 @@ CREATE TABLE `icingaweb_rememberme`(
mtime timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

CREATE TABLE `icingaweb_config_scope`(
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`module` varchar(254) NOT NULL DEFAULT 'default',
`name` varchar(254) COLLATE utf8mb4_unicode_ci NOT NULL,
`hash` binary(20) NOT NULL COMMENT 'sha1(all option tuples)',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_module_name` (`module`, `name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `icingaweb_config_option`(
`scope_id` int(10) unsigned NOT NULL,
`name` varchar(254) NOT NULL,
`value` text DEFAULT NULL,
UNIQUE KEY `idx_scope_id_name` (`scope_id`, `name`),
CONSTRAINT `fk_scope_id_config_scope` FOREIGN KEY (`scope_id`)
REFERENCES `icingaweb_config_scope` (`id`)
ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
46 changes: 46 additions & 0 deletions etc/schema/pgsql-upgrades/2.11.0.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
CREATE EXTENSION IF NOT EXISTS citext;
CREATE DOMAIN bytea20 AS bytea CONSTRAINT exactly_20_bytes_long CHECK ( VALUE IS NULL OR octet_length(VALUE) = 20 );

CREATE TABLE "icingaweb_config_scope" (
"id" serial,
"module" character varying(254) NOT NULL DEFAULT 'default',
"name" citext NOT NULL,
"hash" bytea20 NOT NULL
);

COMMENT ON COLUMN icingaweb_config_scope.hash IS 'sha1(all option tuples)';

ALTER TABLE ONLY "icingaweb_config_scope"
ADD CONSTRAINT pk_icingaweb_config_scope
PRIMARY KEY (
"id"
);

CREATE UNIQUE INDEX idx_module_name
ON "icingaweb_config_scope"
USING btree (
lower((module)::text),
lower((name)::text)
);

CREATE TABLE "icingaweb_config_option" (
"scope_id" int NOT NULL,
"name" character varying(254) NOT NULL,
"value" text DEFAULT NULL
);

CREATE UNIQUE INDEX idx_scope_id_name
ON "icingaweb_config_option"
USING btree (
scope_id,
lower((name)::text)
);

ALTER TABLE ONLY "icingaweb_config_option"
ADD CONSTRAINT fk_scope_id_config_scope
FOREIGN KEY (
"scope_id"
)
REFERENCES "icingaweb_config_scope" (
"id"
) ON DELETE CASCADE;
47 changes: 47 additions & 0 deletions etc/schema/pgsql.schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ CREATE OR REPLACE FUNCTION unix_timestamp(timestamp with time zone) RETURNS bigi
SELECT EXTRACT(EPOCH FROM $1)::bigint AS result
' LANGUAGE sql;

CREATE EXTENSION IF NOT EXISTS citext;
CREATE DOMAIN bytea20 AS bytea CONSTRAINT exactly_20_bytes_long CHECK ( VALUE IS NULL OR octet_length(VALUE) = 20 );

CREATE TABLE "icingaweb_group" (
"id" serial,
"name" character varying(64) NOT NULL,
Expand Down Expand Up @@ -117,3 +120,47 @@ ALTER TABLE ONLY "icingaweb_rememberme"
PRIMARY KEY (
"id"
);

CREATE TABLE "icingaweb_config_scope" (
"id" serial,
"module" character varying(254) NOT NULL DEFAULT 'default',
"name" citext NOT NULL,
"hash" bytea20 NOT NULL
);

COMMENT ON COLUMN icingaweb_config_scope.hash IS 'sha1(all option tuples)';

ALTER TABLE ONLY "icingaweb_config_scope"
ADD CONSTRAINT pk_icingaweb_config_scope
PRIMARY KEY (
"id"
);

CREATE UNIQUE INDEX idx_module_name
ON "icingaweb_config_scope"
USING btree (
lower((module)::text),
lower((name)::text)
);

CREATE TABLE "icingaweb_config_option" (
"scope_id" int NOT NULL,
"name" character varying(254) NOT NULL,
"value" text DEFAULT NULL
);

CREATE UNIQUE INDEX idx_scope_id_name
ON "icingaweb_config_option"
USING btree (
scope_id,
lower((name)::text)
);

ALTER TABLE ONLY "icingaweb_config_option"
ADD CONSTRAINT fk_scope_id_config_scope
FOREIGN KEY (
"scope_id"
)
REFERENCES "icingaweb_config_scope" (
"id"
) ON DELETE CASCADE;
56 changes: 56 additions & 0 deletions library/Icinga/Model/ConfigOption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php
/* Icinga Web 2 | (c) 2022 Icinga GmbH | GPLv2+ */

namespace Icinga\Model;

use ipl\Orm\Model;
use ipl\Orm\Relations;

class ConfigOption extends Model
{
public function getTableName()
{
return 'icingaweb_config_option';
}

public function getKeyName()
{
return [
'scope_id',
'name'
];
}

public function getColumns()
{
return [
'scope_id',
'name',
'value'
];
}

public function getMetaData()
{
return [
'name' => t('Config Option Name'),
'value' => t('Config Option Value')
];
}

public function getSearchColumns()
{
return ['name'];
}

public function getDefaultSort()
{
return 'icingaweb_config_option.name';
}

public function createRelations(Relations $relations)
{
$relations->belongsTo('scope', ConfigScope::class)
->setCandidateKey('scope_id');
}
}
100 changes: 100 additions & 0 deletions library/Icinga/Model/ConfigScope.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php
/* Icinga Web 2 | (c) 2022 Icinga GmbH | GPLv2+ */

namespace Icinga\Model;

use ipl\Orm\Behaviors;
use ipl\Orm\Contract\PropertyBehavior;
use ipl\Orm\Contract\QueryAwareBehavior;
use ipl\Orm\Model;
use ipl\Orm\Query;
use ipl\Orm\Relations;
use ipl\Sql\Adapter\Pgsql;

use function ipl\Stdlib\get_php_type;

class ConfigScope extends Model
{
public function getTableName()
{
return 'icingaweb_config_scope';
}

public function getKeyName()
{
return 'id';
}

public function getColumns()
{
return [
'module',
'name',
'hash'
];
}

public function getMetaData()
{
return [
'module' => t('Config Scope Module'),
'name' => t('Config Scope Name'),
'hash' => t('Config Scope Hash')
];
}

public function getSearchColumns()
{
return ['name'];
}

public function getDefaultSort()
{
return 'icingaweb_config_scope.name';
}

public function createBehaviors(Behaviors $behaviors)
{
$binary = new class (['hash']) extends PropertyBehavior implements QueryAwareBehavior {
public function setQuery(Query $query)
{
if (! $query->getDb()->getAdapter() instanceof Pgsql) {
$this->properties = [];
}
}

public function fromDb($value, $key, $context)
{
if ($value !== null) {
if (! is_resource($value)) {
throw new \UnexpectedValueException(
sprintf('%s should be a resource got %s instead', $key, get_php_type($value))
);
}

return stream_get_contents($value);
}

return null;
}

public function toDb($value, $key, $context)
{
if (is_resource($value)) {
throw new \UnexpectedValueException(sprintf('Unexpected resource for %s', $key));
}

return sprintf('\\x%s', bin2hex($value));
}
};

$behaviors->add($binary);
}

public function createRelations(Relations $relations)
{
$relations->hasMany('option', ConfigOption::class)
->setForeignKey('scope_id')
->setJoinType('LEFT');
}
}