Merge branch 'feature/ini-writer-for-config-4352'

resolves #4352
This commit is contained in:
Jannis Moßhammer 2013-08-07 16:31:19 +02:00
commit 4149328216
25 changed files with 1008 additions and 23 deletions

View File

@ -28,7 +28,7 @@ namespace Icinga\Web\Form;
use Icinga\Web\Form; use Icinga\Web\Form;
use Icinga\Web\Session; use Icinga\Web\Session;
use Icinga\Web\Notification; use Icinga\Web\Notification;
use Icinga\Application\Config; use Icinga\Config\Config;
/** /**
* Class SettingsForm * Class SettingsForm

View File

@ -1,13 +1,13 @@
# Application and Module Configuration # Application and Module Configuration
The \Icinga\Application\Config class is a general purpose service to help you find, load and save The \Icinga\Config\Config class is a general purpose service to help you find, load and save
configuration data. It is used both by the Icinga 2 Web modules and the framework itself. With configuration data. It is used both by the Icinga 2 Web modules and the framework itself. With
INI files as source it enables you to store configuration in a familiar format. Icinga 2 Web INI files as source it enables you to store configuration in a familiar format. Icinga 2 Web
defines some configuration files for its own purposes. Please note that both modules and framework defines some configuration files for its own purposes. Please note that both modules and framework
keep their main configuration in the INI file called config.ini. Here's some example code: keep their main configuration in the INI file called config.ini. Here's some example code:
<?php <?php
use \Icinga\Application\Config as IcingaConfig; use \Icinga\Config\Config as IcingaConfig;
// Retrieve the default timezone using 'Europe/Berlin' in case it is not set // Retrieve the default timezone using 'Europe/Berlin' in case it is not set
IcingaConfig::app()->global->get('defaultTimezone', 'Europe/Berlin'); IcingaConfig::app()->global->get('defaultTimezone', 'Europe/Berlin');

View File

@ -31,6 +31,7 @@ namespace Icinga\Application;
use Icinga\Application\Modules\Manager as ModuleManager; use Icinga\Application\Modules\Manager as ModuleManager;
use Icinga\Application\Platform; use Icinga\Application\Platform;
use Icinga\Exception\ProgrammingError; use Icinga\Exception\ProgrammingError;
use Icinga\Config\Config;
use Zend_Loader_Autoloader; use Zend_Loader_Autoloader;
use Icinga\Exception\ConfigurationError; use Icinga\Exception\ConfigurationError;

View File

@ -29,7 +29,7 @@
namespace Icinga\Application\Modules; namespace Icinga\Application\Modules;
use Icinga\Application\ApplicationBootstrap; use Icinga\Application\ApplicationBootstrap;
use Icinga\Application\Config; use Icinga\Config\Config;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Icinga\Web\Hook; use Icinga\Web\Hook;
use Zend_Controller_Router_Route as Route; use Zend_Controller_Router_Route as Route;

View File

@ -32,7 +32,7 @@ use Icinga\User;
use Icinga\Authentication\UserBackend; use Icinga\Authentication\UserBackend;
use Icinga\Authentication\Credentials; use Icinga\Authentication\Credentials;
use Icinga\Protocol\Ldap; use Icinga\Protocol\Ldap;
use Icinga\Application\Config as IcingaConfig; use Icinga\Config\Config as IcingaConfig;
/** /**
* User authentication backend (@see Icinga\Authentication\UserBackend) for * User authentication backend (@see Icinga\Authentication\UserBackend) for

View File

@ -29,7 +29,7 @@
namespace Icinga\Authentication; namespace Icinga\Authentication;
use Icinga\Application\Logger; use Icinga\Application\Logger;
use Icinga\Application\Config as IcingaConfig; use Icinga\Config\Config as IcingaConfig;
use Icinga\Exception\ConfigurationError as ConfigError; use Icinga\Exception\ConfigurationError as ConfigError;
use Icinga\User; use Icinga\User;

View File

@ -2,7 +2,7 @@
namespace Icinga; namespace Icinga;
use Icinga\Application\Config as IcingaConfig; use Icinga\Config\Config as IcingaConfig;
use Icinga\Authentication\Manager as AuthManager; use Icinga\Authentication\Manager as AuthManager;
class Backend class Backend

View File

@ -26,9 +26,8 @@
*/ */
// {{{ICINGA_LICENSE_HEADER}}} // {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Application; namespace Icinga\Config;
use Icinga\Protocol\Ldap\Exception;
use Zend_Config_Ini; use Zend_Config_Ini;
/** /**
@ -71,7 +70,7 @@ class Config extends Zend_Config_Ini
public function __construct($filename) public function __construct($filename)
{ {
if (!@is_readable($filename)) { if (!@is_readable($filename)) {
throw new Exception('Cannot read config file: ' . $filename); throw new \Exception('Cannot read config file: ' . $filename);
}; };
$this->configFile = $filename; $this->configFile = $filename;
$section = null; $section = null;

View File

@ -0,0 +1,472 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Config;
use Icinga\Exception\ConfigurationError;
/**
* Edit the sections and keys of an ini in-place
*/
class IniEditor
{
/**
* The text that is edited
*
* @var array
*/
private $text;
/**
* The symbol that is used to separate keys
*
* @var string
*/
private $nestSeparator = '.';
/**
* Create a new IniEditor
*
* @param $content The content of the ini as string
*/
public function __construct($content)
{
$this->text = explode("\n",$content);
}
/**
* Set the value of the given key.
*
* @param array $key The key to set
* @param $value The value to set
* @param $section The section to insert to.
*/
public function set(array $key, $value, $section = null)
{
$line = $this->getKeyLine($key,$section);
if ($line === -1) {
$this->insert($key,$value,$section);
} else {
$content = $this->formatKeyValuePair($key,$value);
$this->updateLine($line,$content);
}
}
/**
* Reset the value of the given array element
*
* @param $key The key of the array value
* @param $section The section of the array.
*/
public function resetArrayElement(array $key, $section = null)
{
$line = $this->getArrayEl($key, $section);
if ($line !== -1) {
$this->deleteLine($line);
}
}
/**
* Set the value for an array element
*
* @param array $key The key of the property
* @param $value The value of the property
* @param $section The section to use
*/
public function setArrayElement(array $key, $value, $section = null)
{
$line = $this->getArrayEl($key,$section);
if ($line !== -1) {
if (isset($section)) {
$this->updateLine($line,$this->formatKeyValuePair($key,$value));
} else {
$section = $key[0];
unset($key[0]);
$this->deleteLine($line);
$this->setSection($section);
$this->insert($key,$value,$section);
}
} else {
$this->insert($key,$value,$section);
}
}
/**
* Get the line of an array element
*
* @param array $key The key of the property.
* @param $value The value
* @param $section The section to use
*
* @return The line of the array element.
*/
private function getArrayEl(array $key, $section = null)
{
$line = isset($section) ? $this->getSectionLine($section) +1 : 0;
$index = array_pop($key);
$formatted = $this->formatKey($key);
for (; $line < count($this->text); $line++) {
$l = $this->text[$line];
if ($this->isSectionDeclaration($l)) {
return -1;
}
if (strlen($formatted) > 0) {
if (preg_match('/^'.$formatted.'\[\]=/',$l) === 1 ||
preg_match(
'/^'.$formatted.$this->nestSeparator.$index.'=/',$l) === 1) {
return $line;
}
} else {
if (preg_match('/^'.$index.'=/',$l) === 1 ) {
return $line;
}
}
}
return -1;
}
/**
* When it exists, set the key back to null
*
* @param array $key The key to reset
* @param $section The section of the key
*/
public function reset(array $key, $section = null)
{
$line = $this->getKeyLine($key,$section);
if ($line === -1) {
return;
}
$this->deleteLine($line);
}
/**
* Create the section if it does not exist and set the properties
*
* @param $section The section name
* @param $extend The section that should be extended by this section
*/
public function setSection($section, $extend = null)
{
if (isset($extend)) {
$decl = '['.$section.' : '.$extend.']';
} else {
$decl = '['.$section.']';
}
$line = $this->getSectionLine($section);
if ($line !== -1) {
$this->deleteLine($line);
$this->insertAtLine($line,$decl);
} else {
$line = $this->getLastLine();
$this->insertAtLine($line,$decl);
}
}
/**
* Remove a section declaration
*
* @param $section The section name
*/
public function removeSection($section)
{
$line = $this->getSectionLine($section);
if ($line !== -1) {
$this->deleteLine($line);
}
}
/**
* Insert the key at the end of the corresponding section
*
* @param array $key The key to insert
* @param $value The value to insert
* @param array $key The key to insert
*/
private function insert(array $key, $value, $section = null)
{
$line = $this->getSectionEnd($section);
$content = $this->formatKeyValuePair($key,$value);
$this->insertAtLine($line,$content);
}
/**
* Get the edited text
*
* @return string The edited text
*/
public function getText()
{
$this->cleanUpWhitespaces();
return implode("\n",$this->text);
}
/**
* Remove all unneeded line breaks between sections
*/
private function cleanUpWhitespaces()
{
$i = count($this->text) - 1;
for (; $i >= 0; $i--) {
$line = $this->text[$i];
if ($this->isSectionDeclaration($line)) {
$i--;
$line = $this->text[$i];
/*
* Ignore comments that are glued to the section declaration
*/
while ($i > 0 && preg_match('/^;/',$line) === 1) {
$i--;
$line = $this->text[$i];
}
/*
* Remove whitespaces between the sections
*/
while ($i > 0 && preg_match('/^[\s]*$/',$line) === 1) {
$this->deleteLine($i);
$i--;
$line = $this->text[$i];
}
/*
* Add a single whitespace
*/
if ($i !== 0) {
$this->insertAtLine($i + 1,'');
}
}
}
}
/**
* Insert the text at line $lineNr
*
* @param $lineNr The line nr the inserted line should have
* @param $toInsert The text that will be inserted
*/
private function insertAtLine($lineNr, $toInsert)
{
$this->text = IniEditor::insertIntoArray($this->text,$lineNr,$toInsert);
}
/**
* Update the line $lineNr
*
* @param $lineNr The line number of the target line
* @param $toInsert The new line content
*/
private function updateLine($lineNr, $content)
{
$this->text[$lineNr] = $content;
}
/**
* Delete the line $lineNr
*
* @param $lineNr The lineNr starting at 0
*/
private function deleteLine($lineNr)
{
$this->text = $this->removeFromArray($this->text,$lineNr);
}
/**
* Format a key-value pair to an INI file-entry
*
* @param array $key The key
* @param $value The value
*
* @return string The formatted key-value pair
*/
private function formatKeyValuePair(array $key, $value)
{
return $this->formatKey($key).'='.$this->formatValue($value);
}
/**
* Format a key to an INI key
*/
private function formatKey(array $key)
{
return implode($this->nestSeparator,$key);
}
/**
* Get the first line after the given $section
*
* @param $section The name of the section
*
* @return int The line number of the section
*/
private function getSectionEnd($section = null)
{
$i = 0;
$started = isset($section) ? false: true;
foreach ($this->text as $line) {
if ($started && preg_match('/^\[/',$line) === 1) {
if ($i === 0) {
return $i;
}
/*
* ignore all comments 'glued' to the next section, to allow section
* comments in front of sections
*/
while (preg_match('/^;/',$this->text[$i - 1]) === 1) {
$i--;
}
return $i;
} elseif (preg_match('/^\['.$section.'.*\]/',$line) === 1) {
$started = true;
}
$i++;
}
if (!$started) {
return -1;
}
return $i;
}
/**
* Check if the given line contains a section declaration
*
* @param $lineContent The content of the line
* @param string $section The optional section name that will be assumed
*
* @return bool
*/
private function isSectionDeclaration($lineContent, $section = "")
{
return preg_match('/^\[/'.$section,$lineContent) === 1;
}
/**
* Get the line where the section begins
*
* @param $section The section
*
* @return int The line number
*/
private function getSectionLine($section)
{
$i = 0;
foreach ($this->text as $line) {
if (preg_match('/^\['.$section.'/',$line)) {
return $i;
}
$i++;
}
return -1;
}
/**
* Get the line number where the given key occurs
*
* @param array $keys The key and its parents
* @param $section The section of the key
*
* @return int The line number
*/
private function getKeyLine(array $keys, $section = null)
{
$key = implode($this->nestSeparator,$keys);
$inSection = isset($section) ? false : true;
$i = 0;
foreach ($this->text as $line) {
if ($inSection && preg_match('/^\[/',$line) === 1) {
return -1;
}
if ($inSection && preg_match('/^'.$key.'=/',$line) === 1) {
return $i;
}
if (!$inSection && preg_match('/^\[ *'.$section.' *[\]:]/',$line) === 1) {
$inSection = true;
}
$i++;
}
return -1;
}
/**
* Get the last line number occurring in the text
*
* @return The line number of the last line
*/
private function getLastLine()
{
return count($this->text);
}
/**
* Insert a new element into a specific position of an array
*
* @param $array The array to use
* @param $pos The target position
* @param $element The element to insert
*
* @return array The changed array
*/
private static function insertIntoArray($array, $pos, $element)
{
array_splice($array, $pos, 0, $element);
return $array;
}
/**
* Remove an element from an array
*
* @param $array The array to use
* @param $pos The position to remove
*/
private function removeFromArray($array, $pos)
{
unset($array[$pos]);
return array_values($array);
}
/**
* Prepare a value for INI
*
* @param $value The value of the string
*
* @return string The formatted value
*
* @throws Zend_Config_Exception
*/
private function formatValue($value)
{
if (is_integer($value) || is_float($value)) {
return $value;
} elseif (is_bool($value)) {
return ($value ? 'true' : 'false');
} elseif (strpos($value, '"') === false) {
return '"' . $value . '"';
} else {
throw new ConfigurationError('Value can not contain double quotes "');
}
}
}

View File

@ -0,0 +1,184 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Config;
use Zend_Config;
use Zend_Config_Ini;
/**
* A ini file adapter that respects the file structure and the comments of already
* existing ini files
*/
class PreservingIniWriter extends \Zend_Config_Writer_FileAbstract
{
/**
* Render the Zend_Config into a config file string
*
* @return string
*/
public function render()
{
$oldconfig = new Zend_Config_Ini($this->_filename);
$newconfig = $this->_config;
$editor = new IniEditor(file_get_contents($this->_filename));
$this->diffConfigs($oldconfig,$newconfig,$editor);
return $editor->getText();
}
/**
* Create a property diff and apply the changes to the editor
*
* @param Zend_Config $oldconfig The config representing the state before the change
* @param Zend_Config $newconfig The config representing the state after the change
* @param IniEditor $eeditor The editor that should be used to edit the old config file
* @param array $parents The parent keys that should be respected when editing the config
*/
private function diffConfigs(
Zend_Config $oldconfig,
Zend_Config $newconfig,
IniEditor $editor,
array $parents = array()
) {
$this->diffPropertyUpdates($oldconfig,$newconfig,$editor,$parents);
$this->diffPropertyDeletions($oldconfig,$newconfig,$editor,$parents);
}
/**
* Search for created and updated properties and use the editor to create or update these entries
*
* @param Zend_Config $oldconfig The config representing the state before the change
* @param Zend_Config $newconfig The config representing the state after the change
* @param IniEditor $eeditor The editor that should be used to edit the old config file
* @param array $parents The parent keys that should be respected when editing the config
*/
private function diffPropertyUpdates(
Zend_Config $oldconfig,
Zend_Config $newconfig,
IniEditor $editor,
array $parents = array()
) {
/*
* The current section. This value is null when processing
* the section-less root element
*/
$section = empty($parents) ? null : $parents[0];
/*
* Iterate over all properties in the new configuration file and search for changes
*/
foreach ($newconfig as $key => $value) {
$oldvalue = $oldconfig->get($key);
$nextParents = array_merge($parents,array($key));
$keyIdentifier = empty($parents) ?
array($key) : array_slice($nextParents,1,null,true);
if ($value instanceof Zend_Config) {
/*
* The value is a nested Zend_Config, handle it recursively
*/
if (!isset($section)) {
/*
* Update the section declaration
*/
$extends = $newconfig->getExtends();
$extend = array_key_exists($key,$extends) ?
$extends[$key] : null;
$editor->setSection($key,$extend);
}
if (!isset($oldvalue)) {
$oldvalue = new Zend_Config(array());
}
$this->diffConfigs($oldvalue,$value,$editor,$nextParents);
} else {
/*
* The value is a plain value, use the editor to set it
*/
if (is_numeric($key)) {
$editor->setArrayElement($keyIdentifier,$value,$section);
} else {
$editor->set($keyIdentifier,$value,$section);
}
}
}
}
/**
* Search for deleted properties and use the editor to delete these entries
*
* @param Zend_Config $oldconfig The config representing the state before the change
* @param Zend_Config $newconfig The config representing the state after the change
* @param IniEditor $eeditor The editor that should be used to edit the old config file
* @param array $parents The parent keys that should be respected when editing the config
*/
private function diffPropertyDeletions(
Zend_Config $oldconfig,
Zend_Config $newconfig,
IniEditor $editor,
array $parents = array()
) {
/*
* The current section. This value is null when processing
* the section-less root element
*/
$section = empty($parents) ? null : $parents[0];
/*
* Iterate over all properties in the old configuration file and search for
* deleted properties
*/
foreach ($oldconfig as $key => $value) {
$nextParents = array_merge($parents,array($key));
$newvalue = $newconfig->get($key);
$keyIdentifier = empty($parents) ?
array($key) : array_slice($nextParents,1,null,true);
if (!isset($newvalue)) {
if ($value instanceof Zend_Config) {
/*
* The deleted value is a nested Zend_Config, handle it recursively
*/
$this->diffConfigs($value,new Zend_Config(array()),$editor,$nextParents);
if (!isset($section)) {
$editor->removeSection($key);
}
} else {
/*
* The deleted value is a plain value, use the editor to delete it
*/
if (is_numeric($key)) {
$editor->resetArrayElement($keyIdentifier,$section);
} else {
$editor->reset($keyIdentifier,$section);
}
}
}
}
}
}

View File

@ -6,7 +6,7 @@
namespace Icinga\Protocol\Ldap; namespace Icinga\Protocol\Ldap;
use Icinga\Application\Platform; use Icinga\Application\Platform;
use Icinga\Application\Config; use Icinga\Config\Config;
use Icinga\Application\Logger as Log; use Icinga\Application\Logger as Log;
/** /**

View File

@ -31,7 +31,7 @@ namespace Icinga\Web;
use Icinga\Authentication\Manager as AuthManager; use Icinga\Authentication\Manager as AuthManager;
use Icinga\Application\Benchmark; use Icinga\Application\Benchmark;
use Icinga\Exception; use Icinga\Exception;
use Icinga\Application\Config; use Icinga\Config\Config;
use Icinga\Web\Notification; use Icinga\Web\Notification;
use Zend_Layout as ZfLayout; use Zend_Layout as ZfLayout;
use Zend_Controller_Action as ZfController; use Zend_Controller_Action as ZfController;

View File

@ -28,7 +28,7 @@
*/ */
namespace Icinga\Web; namespace Icinga\Web;
use Icinga\Application\Config as IcingaConfig; use Icinga\Config\Config as IcingaConfig;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
/** /**

View File

@ -3,7 +3,7 @@
namespace Icinga\Web\Widget; namespace Icinga\Web\Widget;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Icinga\Application\Config; use Icinga\Config\Config;
use Icinga\Web\Widget; use Icinga\Web\Widget;
use Icinga\Web\Widget\Dashboard\Pane; use Icinga\Web\Widget\Dashboard\Pane;
use Icinga\Web\Url; use Icinga\Web\Url;

View File

@ -31,7 +31,7 @@
use Icinga\Application\Benchmark; use Icinga\Application\Benchmark;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Icinga\Backend; use Icinga\Backend;
use Icinga\Application\Config; use Icinga\Config\Config;
use Icinga\Application\Logger; use Icinga\Application\Logger;
use Icinga\Authentication\Manager; use Icinga\Authentication\Manager;
use Icinga\Web\Form; use Icinga\Web\Form;

View File

@ -2,7 +2,7 @@
namespace Monitoring; namespace Monitoring;
use Icinga\Application\Config as IcingaConfig; use Icinga\Config\Config as IcingaConfig;
use Icinga\Authentication\Manager as AuthManager; use Icinga\Authentication\Manager as AuthManager;
use Exception; use Exception;

View File

@ -2,7 +2,7 @@
namespace Monitoring; namespace Monitoring;
use Icinga\Application\Config; use Icinga\Config\Config;
use Icinga\Web\Session; use Icinga\Web\Session;
use Exception; use Exception;

View File

@ -10,7 +10,7 @@ require 'Zend/Controller/Action.php';
require '../../library/Icinga/Exception/ProgrammingError.php'; require '../../library/Icinga/Exception/ProgrammingError.php';
require '../../library/Icinga/Application/Benchmark.php'; require '../../library/Icinga/Application/Benchmark.php';
require '../../library/Icinga/Application/Config.php'; require '../../library/Icinga/Config/Config.php';
require '../../library/Icinga/Application/Icinga.php'; require '../../library/Icinga/Application/Icinga.php';
require '../../library/Icinga/Web/ActionController.php'; require '../../library/Icinga/Web/ActionController.php';
require '../../library/Icinga/Web/Notification.php'; require '../../library/Icinga/Web/Notification.php';

View File

@ -36,7 +36,7 @@ require_once('Zend/Config/Ini.php');
require_once('Zend/Db.php'); require_once('Zend/Db.php');
require_once('../../library/Icinga/Authentication/UserBackend.php'); require_once('../../library/Icinga/Authentication/UserBackend.php');
require_once('../../library/Icinga/Protocol/Ldap/Exception.php'); require_once('../../library/Icinga/Protocol/Ldap/Exception.php');
require_once('../../library/Icinga/Application/Config.php'); require_once('../../library/Icinga/Config/Config.php');
require_once('../../library/Icinga/Authentication/Credentials.php'); require_once('../../library/Icinga/Authentication/Credentials.php');
require_once('../../library/Icinga/Authentication/Backend/DbUserBackend.php'); require_once('../../library/Icinga/Authentication/Backend/DbUserBackend.php');
require_once('../../library/Icinga/User.php'); require_once('../../library/Icinga/User.php');
@ -45,7 +45,7 @@ use Icinga\Authentication\Backend\DbUserBackend;
use Icinga\Util\Crypto; use Icinga\Util\Crypto;
use Icinga\Authentication\Credentials; use Icinga\Authentication\Credentials;
use Icinga\User; use Icinga\User;
use Icinga\Application\Config; use Icinga\Config\Config;
/** /**
* *

View File

@ -6,15 +6,15 @@
namespace Tests\Icinga\Application; namespace Tests\Icinga\Application;
require_once 'Zend/Config/Ini.php'; require_once 'Zend/Config/Ini.php';
require_once dirname(__FILE__) . '/../../../../../library/Icinga/Application/Config.php'; require_once '../../library/Icinga/Config/Config.php';
use Icinga\Application\Config as IcingaConfig; use Icinga\Config\Config as IcingaConfig;
class ConfigTest extends \PHPUnit_Framework_TestCase class ConfigTest extends \PHPUnit_Framework_TestCase
{ {
public function setUp() public function setUp()
{ {
IcingaConfig::$configDir = dirname(__FILE__) . '/Config/files'; IcingaConfig::$configDir = dirname(__FILE__) . '/files';
} }
public function testAppConfig() public function testAppConfig()

View File

@ -0,0 +1,329 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
/**
* This file is part of Icinga 2 Web.
*
* Icinga 2 Web - Head for multiple monitoring backends.
* Copyright (C) 2013 Icinga Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @copyright 2013 Icinga Development Team <info@icinga.org>
* @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2
* @author Icinga Development Team <info@icinga.org>
*/
// {{{ICINGA_LICENSE_HEADER}}}
namespace Tests\Icinga\PreservingIniWriterTest;
require_once 'Zend/Config.php';
require_once 'Zend/Config/Ini.php';
require_once 'Zend/Config/Writer/Ini.php';
require_once('../../library/Icinga/Config/IniEditor.php');
require_once('../../library/Icinga/Config/PreservingIniWriter.php');
use Icinga\Config\PreservingIniWriter;
class PreservingIniWriterTest extends \PHPUnit_Framework_TestCase {
private $tmpfiles = array();
/**
* Set up the test fixture
*/
public function setUp()
{
$ini =
';1
trailing1="wert"
arr[]="0"
arr[]="1"
arr[]="2"
arr[]="3"
;2
;3
Trailing2=
[parent]
;4
;5
;6
;7
list[]="zero"
list[]="one"
;8
;9
many.many.nests="value"
propOne="value1"
propTwo="2"
propThree=
propFour="true"
Prop5="true"
[child : parent]
PropOne="overwritten"
;10
';
$this->writeToTmp('orig',$ini);
$this->writeToTmp('sonst','');
$emptyIni = " ";
$this->writeToTmp('empty',$emptyIni);
$editedIni =
';1
;2
;3
;4
;5
trailing1="1"
[parent]
;6
;7
;8
;9
;10
propOne="value1"
[different]
prop1="1"
prop2="2"
[nested : different]
prop2="5"
';
$this->writeToTmp('edited',$editedIni);
}
/**
* Write a string to a temporary file
*
* @param $name The name of the temporary file
* @param $content The content
*/
private function writeToTmp($name,$content)
{
$this->tmpfiles[$name] =
tempnam(dirname(__FILE__) . '/temp',$name);
$file = fopen($this->tmpfiles[$name],'w');
fwrite($file,$content);
fflush($file);
fclose($file);
}
/**
* Tear down the test fixture
*/
public function tearDown()
{
foreach ($this->tmpfiles as $filename) {
unlink($filename);
}
}
/**
* Test if the IniWriter works correctly when writing the changes back to
* the same ini file
*/
public function testPropertyChangeSameConfig()
{
$this->changeConfigAndWriteToFile('orig');
$config = new \Zend_Config_Ini(
$this->tmpfiles['orig'],null,array('allowModifications' => true)
);
$this->checkConfigProperties($config);
$this->checkConfigComments($this->tmpfiles['orig']);
}
/**
* Test if the IniWriter works correctly when writing to an empty file
*/
public function testPropertyChangeEmptyConfig()
{
$this->changeConfigAndWriteToFile('empty');
$config = new \Zend_Config_Ini(
$this->tmpfiles['empty'],null,array('allowModifications' => true)
);
$this->checkConfigProperties($config);
}
/**
* Test if the IniWriter works correctly when writing to a file with changes
*/
public function testPropertyChangeEditedConfig()
{
$original = $this->changeConfigAndWriteToFile('edited');
$config = new \Zend_Config_Ini(
$this->tmpfiles['edited'],null,array('allowModifications' => true)
);
$this->checkConfigProperties($config);
$this->checkConfigComments($this->tmpfiles['edited']);
}
/**
* Change the test config and write the changes to the temporary
* file $tmpFile
*
* @param $tmpFile
*/
private function changeConfigAndWriteToFile($tmpFile)
{
$config = $this->createTestConfig();
$this->alterConfig($config);
$writer = new PreservingIniWriter(
array('config' => $config,'filename' => $this->tmpfiles[$tmpFile])
);
$writer->write();
return $config;
}
/**
* Check if all comments are present
*
* @param $file
*/
private function checkConfigComments($file)
{
$i = 0;
foreach (explode("\n",file_get_contents($file)) as $line) {
if (preg_match('/^;/',$line)) {
$i++;
$this->assertEquals(
$i,intval(substr($line,1)),
'Comment unchanged'
);
}
}
$this->assertEquals(10,$i,'All comments exist');
}
/**
* Test if all configuration properties are set correctly
*
* @param $config
*/
private function checkConfigProperties($config)
{
$this->assertEquals('val',$config->Trailing2,
'Section-less property updated.');
$this->assertNull($config->trailing1,
'Section-less property deleted.');
$this->assertEquals('value',$config->new,
'Section-less property created.');
$this->assertEquals('0',$config->arr->{0},
'Value persisted in array');
$this->assertEquals('update',$config->arr->{2},
'Value changed in array');
$this->assertEquals('arrvalue',$config->arr->{4},
'Value added to array');
$this->assertEquals('',$config->parent->propOne,
'Section property deleted.');
$this->assertEquals("2",$config->parent->propTwo,
'Section property numerical unchanged.');
$this->assertEquals('update',$config->parent->propThree,
'Section property updated.');
$this->assertEquals("true",$config->parent->propFour,
'Section property boolean unchanged.');
$this->assertEquals("1",$config->parent->new,
'Section property numerical created.');
$this->assertNull($config->parent->list->{0},
'Section array deleted');
$this->assertEquals('new',$config->parent->list->{1},
'Section array changed.');
$this->assertEquals('changed',$config->parent->many->many->nests,
'Change strongy nested value.');
$this->assertEquals('new',$config->parent->many->many->new,
'Ccreate strongy nested value.');
$this->assertEquals('overwritten',$config->child->PropOne,
'Overridden inherited property unchanged.');
$this->assertEquals('somethingNew',$config->child->propTwo,
'Inherited property changed.');
$this->assertEquals('test',$config->child->create,
'Non-inherited property created.');
$this->assertInstanceOf('Zend_Config',$config->newsection,
'New section created.');
$extends = $config->getExtends();
$this->assertEquals('child',$extends['newsection'],
'New inheritance created.');
}
/**
* Change the content of a Zend_Config
*
* @param Zend_Config $config
*/
private function alterConfig(\Zend_Config $config)
{
$config->Trailing2 = 'val';
unset($config->trailing1);
$config->new = 'value';
$config->arr->{2} = "update";
$config->arr->{4} = "arrvalue";
$config->parent->propOne = null;
$config->parent->propThree = 'update';
$config->parent->new = 1;
unset($config->parent->list->{0});
$config->parent->list->{1} = 'new';
$config->parent->many->many->nests = "changed";
$config->parent->many->many->new = "new";
$config->child->propTwo = 'somethingNew';
$config->child->create = 'test';
$config->newsection = array();
$config->newsection->p1 = "prop";
$config->newsection->P2 = "prop";
$config->setExtend('newsection','child');
}
/**
* Create the the configuration that will be used for the tests.
*/
private function createTestConfig()
{
return new \Zend_Config_Ini(
$this->tmpfiles['orig'],
null,
array('allowModifications' => true)
);
}
}