mirror of
https://github.com/Icinga/icingaweb2-module-director.git
synced 2025-07-31 01:34:12 +02:00
Migrations: implement DB migration helpers
This commit is contained in:
parent
81123ea78f
commit
84cbf522c9
@ -28,8 +28,7 @@ Create Icinga Director database
|
||||
MySQL:
|
||||
|
||||
mysql -e "CREATE DATABASE director;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON director.* TO director@localhost
|
||||
IDENTIFIED BY 'some-password';"
|
||||
GRANT ALL ON director.* TO director@localhost IDENTIFIED BY 'some-password';"
|
||||
|
||||
mysql director < schema/mysql.sql
|
||||
|
||||
|
66
application/clicommands/MigrationCommand.php
Normal file
66
application/clicommands/MigrationCommand.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Clicommands;
|
||||
|
||||
use Icinga\Module\Director\Cli\Command;
|
||||
use Icinga\Module\Director\Db\Migrations;
|
||||
|
||||
/**
|
||||
* Handle DB migrations
|
||||
*
|
||||
* This command retrieves information about unapplied database migration and
|
||||
* helps applying them.
|
||||
*/
|
||||
class MigrationCommand extends Command
|
||||
{
|
||||
/**
|
||||
* Check whether there are pending migrations
|
||||
*
|
||||
* This is mostly for automation, so one could create a Puppet manifest
|
||||
* as follows:
|
||||
*
|
||||
* exec { 'Icinga Director DB migration':
|
||||
* command => 'icingacli director migration run',
|
||||
* onlyif => 'icingacli director migration pending',
|
||||
* }
|
||||
*
|
||||
* Exit code 0 means that there are pending migrations, code 1 that there
|
||||
* are no such. Use --verbose
|
||||
*/
|
||||
public function pendingAction()
|
||||
{
|
||||
if ($count = $this->migrations()->countPendingMigrations()) {
|
||||
if ($this->isVerbose) {
|
||||
if ($count === 1) {
|
||||
echo "There is 1 pending migration\n";
|
||||
} else {
|
||||
printf("There are %d pending migrations\n", $count);
|
||||
}
|
||||
}
|
||||
|
||||
exit(0);
|
||||
} else {
|
||||
if ($this->isVerbose) {
|
||||
echo "There are no pending migrations\n";
|
||||
}
|
||||
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run any pending migrations
|
||||
*
|
||||
* All pending migrations will be silently applied
|
||||
*/
|
||||
public function runAction()
|
||||
{
|
||||
$this->migrations()->applyPendingMigrations();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
protected function migrations()
|
||||
{
|
||||
return new Migrations($this->db());
|
||||
}
|
||||
}
|
60
library/Director/Db/Migration.php
Normal file
60
library/Director/Db/Migration.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Db;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Module\Director\Db;
|
||||
|
||||
class Migration
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $sql;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $version;
|
||||
|
||||
public function __construct($version, $sql)
|
||||
{
|
||||
$this->version = $version;
|
||||
$this->sql = $sql;
|
||||
}
|
||||
|
||||
public function apply(Db $connection)
|
||||
{
|
||||
$db = $connection->getDbAdapter();
|
||||
$queries = preg_split('/[\n\s\t]*\;[\n\s\t]*/s', $this->sql, -1, PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
if (empty($queries)) {
|
||||
throw new IcingaException(
|
||||
'Migration %d has no queries',
|
||||
$this->version
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$db->beginTransaction();
|
||||
|
||||
foreach ($queries as $query) {
|
||||
$db->exec($query);
|
||||
}
|
||||
|
||||
$db->commit();
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
$db->rollback();
|
||||
throw new IcingaException(
|
||||
'Migration %d failed: %s',
|
||||
$this->version,
|
||||
$e->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
136
library/Director/Db/Migrations.php
Normal file
136
library/Director/Db/Migrations.php
Normal file
@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Db;
|
||||
|
||||
use DirectoryIterator;
|
||||
use Exception;
|
||||
use Icinga\Module\Director\Db;
|
||||
|
||||
class Migrations
|
||||
{
|
||||
/**
|
||||
* @var \Zend_Db_Adapter_Abstract
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* @var Db
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
protected $migrationsDir;
|
||||
|
||||
public function __construct(Db $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
$this->db = $connection->getDbAdapter();
|
||||
}
|
||||
|
||||
public function getLastMigrationNumber()
|
||||
{
|
||||
try {
|
||||
$query = $this->db->select()->from(
|
||||
array('m' => 'director_schema_migration'),
|
||||
array('schema_version' => 'MAX(schema_version)')
|
||||
);
|
||||
|
||||
return (int) $this->db->fetchOne($query);
|
||||
} catch (Exception $e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public function hasPendingMigrations()
|
||||
{
|
||||
return $this->countPendingMigrations() > 0;
|
||||
}
|
||||
|
||||
public function countPendingMigrations()
|
||||
{
|
||||
return count($this->listPendingMigrations());
|
||||
}
|
||||
|
||||
public function getPendingMigrations()
|
||||
{
|
||||
$migrations = array();
|
||||
foreach ($this->listPendingMigrations() as $version) {
|
||||
$migrations[] = new Migration(
|
||||
$version,
|
||||
$this->loadMigrationFile($version)
|
||||
);
|
||||
}
|
||||
|
||||
return $migrations;
|
||||
}
|
||||
|
||||
public function applyPendingMigrations()
|
||||
{
|
||||
foreach ($this->getPendingMigrations() as $migration) {
|
||||
$migration->apply($this->connection);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function listPendingMigrations()
|
||||
{
|
||||
return $this->listMigrationsAfter($this->getLastMigrationNumber());
|
||||
}
|
||||
|
||||
public function listAllMigrations()
|
||||
{
|
||||
$dir = $this->getMigrationsDir();
|
||||
if (! is_readable($dir)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$versions = array();
|
||||
|
||||
foreach (new DirectoryIterator($this->getMigrationsDir()) as $file) {
|
||||
if($file->isDot()) continue;
|
||||
$filename = $file->getFilename();
|
||||
if (preg_match('/^upgrade_(\d+)\.sql$/', $filename, $match)) {
|
||||
$versions[] = $match[1];
|
||||
}
|
||||
}
|
||||
|
||||
sort($versions);
|
||||
|
||||
return $versions;
|
||||
}
|
||||
|
||||
public function loadMigrationFile($version)
|
||||
{
|
||||
$filename = sprintf(
|
||||
'%s/upgrade_%d.sql',
|
||||
$this->getMigrationsDir(),
|
||||
$version
|
||||
);
|
||||
|
||||
return file_get_contents($filename);
|
||||
}
|
||||
|
||||
protected function listMigrationsAfter($version)
|
||||
{
|
||||
$filtered = array();
|
||||
foreach ($this->listAllMigrations() as $available) {
|
||||
if ($available > $version) {
|
||||
$filtered[] = $available;
|
||||
}
|
||||
}
|
||||
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
protected function getMigrationsDir()
|
||||
{
|
||||
if ($this->migrationsDir === null) {
|
||||
$this->migrationsDir = dirname(dirname(dirname(__DIR__)))
|
||||
. '/schema/'
|
||||
. $this->connection->getDbType()
|
||||
. '-migrations';
|
||||
}
|
||||
|
||||
return $this->migrationsDir;
|
||||
}
|
||||
}
|
12
schema/mysql-migrations/upgrade_63.sql
Normal file
12
schema/mysql-migrations/upgrade_63.sql
Normal file
@ -0,0 +1,12 @@
|
||||
CREATE TABLE director_schema_migration (
|
||||
schema_version SMALLINT UNSIGNED NOT NULL,
|
||||
migration_time DATETIME NOT NULL,
|
||||
PRIMARY KEY(schema_version)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
DROP TABLE director_dbversion;
|
||||
|
||||
INSERT INTO director_schema_migration
|
||||
SET migration_time = NOW(),
|
||||
schema_version = 63;
|
||||
|
@ -16,10 +16,6 @@
|
||||
|
||||
SET sql_mode = 'STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,NO_ENGINE_SUBSTITUTION,PIPES_AS_CONCAT,ANSI_QUOTES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER';
|
||||
|
||||
CREATE TABLE director_dbversion (
|
||||
schema_version INT(10) UNSIGNED NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE director_activity_log (
|
||||
id BIGINT(20) UNSIGNED AUTO_INCREMENT NOT NULL,
|
||||
object_type VARCHAR(64) NOT NULL,
|
||||
@ -149,6 +145,12 @@ CREATE TABLE director_datafield_setting (
|
||||
ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE director_schema_migration (
|
||||
schema_version SMALLINT UNSIGNED NOT NULL,
|
||||
migration_time DATETIME NOT NULL,
|
||||
PRIMARY KEY(schema_version)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE icinga_zone (
|
||||
id INT(10) UNSIGNED AUTO_INCREMENT NOT NULL,
|
||||
parent_id INT(10) UNSIGNED DEFAULT NULL,
|
||||
@ -1022,3 +1024,8 @@ CREATE TABLE import_row_modifier_setting (
|
||||
setting_value TEXT DEFAULT NULL,
|
||||
PRIMARY KEY (modifier_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
INSERT INTO director_schema_migration
|
||||
SET migration_time = NOW(),
|
||||
schema_version = 63;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user