Make Icinga Web 2 work without any config file

refs #5638
fixes #5523
This commit is contained in:
Eric Lippmann 2014-02-20 13:53:28 +01:00
parent 76769865da
commit 7fa6668b64
11 changed files with 181 additions and 153 deletions

View File

@ -25,7 +25,7 @@ ICINGA_MYSQL_CMD=${ICINGA_MYSQL_CMD:-"/usr/local/icinga-mysql/var/rw/icinga.cmd"
ICINGA_PGSQL_CMD=${ICINGA_PGSQL_CMD:-"/usr/local/icinga-pgsql/var/rw/icinga.cmd"} ICINGA_PGSQL_CMD=${ICINGA_PGSQL_CMD:-"/usr/local/icinga-pgsql/var/rw/icinga.cmd"}
LOCKFILE=${LOCKFILE:-/var/lock/subsys/$PROG} LOCKFILE=${LOCKFILE:-/var/lock/subsys/$PROG}
PIDFILE=${PIDFILE:-/var/lock/subsys/$PROG/$PROD.pid} PIDFILE=${PIDFILE:-/var/lock/subsys/$PROG/$PROG.pid}
RETVAL=0 RETVAL=0

View File

@ -1,17 +1,16 @@
______ ___ ______ ___
/\__ _\ __ /'___`\ /\__ _\ __ /'___`\
\/_/\ \/ ___ /\_\ ___ __ __ /\_\ /\ \ \/_/\ \/ ___ /\_\ ___ __ __ /\_\ /\ \
\ \ \ /'___\/\ \ /' _ `\ /'_ `\ /'__`\ \/_/// /__ \ \ \ /'___\/\ \ /' _ `\ /'_ `\ /'__`\ \/_/// /__
\_\ \__/\ \__/\ \ \/\ \/\ \/\ \L\ \/\ \L\.\_ // /_\ \ \_\ \__/\ \__/\ \ \/\ \/\ \/\ \L\ \/\ \L\.\_ // /_\ \
/\_____\ \____\\ \_\ \_\ \_\ \____ \ \__/.\_\ /\______/ /\_____\ \____\\ \_\ \_\ \_\ \____ \ \__/.\_\ /\______/
\/_____/\/____/ \/_/\/_/\/_/\/___L\ \/__/\/_/ \/_____/ \/_____/\/____/ \/_/\/_/\/_/\/___L\ \/__/\/_/ \/_____/
/\____/ /\____/
\_/__/ \_/__/
__ __ __ __ __ __
/\ \ __/\ \ /\ \ /\ \ __/\ \ /\ \
\ \ \/\ \ \ \ __\ \ \____ \ \ \/\ \ \ \ __\ \ \____
\ \ \ \ \ \ \ /'__`\ \ '__`\ \ \ \ \ \ \ \ /'__`\ \ '__`\
\ \ \_/ \_\ \/\ __/\ \ \L\ \ \ \ \_/ \_\ \/\ __/\ \ \L\ \
\ `\___x___/\ \____\\ \_,__/ \ `\___x___/\ \____\\ \_,__/
'\/__//__/ \/____/ \/___/ '\/__//__/ \/____/ \/___/

View File

@ -6,9 +6,6 @@ indexController = "dashboard"
dateFormat = "d/m/Y" dateFormat = "d/m/Y"
timeFormat = "g:i A" timeFormat = "g:i A"
; The directory that contains the symlinks to all enabled directories
moduleFolder = "@icingaweb_config_path@/enabledModules"
; Contains the directories that will be searched for available modules. Modules that ; Contains the directories that will be searched for available modules. Modules that
; don't exist in these directories can still be symlinked in the module folder, but ; don't exist in these directories can still be symlinked in the module folder, but
; won't show up in the list of disabled modules ; won't show up in the list of disabled modules

View File

@ -31,9 +31,12 @@ namespace Icinga\Application;
use DateTimeZone; use DateTimeZone;
use Exception; use Exception;
use Zend_Config;
use Icinga\Application\Logger;
use Icinga\Application\Modules\Manager as ModuleManager; use Icinga\Application\Modules\Manager as ModuleManager;
use Icinga\Data\ResourceFactory; use Icinga\Data\ResourceFactory;
use Icinga\Exception\ConfigurationError; use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotReadableError;
use Icinga\Util\DateTimeFactory; use Icinga\Util\DateTimeFactory;
use Icinga\Util\Translator; use Icinga\Util\Translator;
@ -78,7 +81,7 @@ abstract class ApplicationBootstrap
/** /**
* Config object * Config object
* *
* @var Config * @var Zend_Config
*/ */
protected $config; protected $config;
@ -256,6 +259,13 @@ abstract class ApplicationBootstrap
public static function start($configDir) public static function start($configDir)
{ {
$application = new static($configDir); $application = new static($configDir);
// TODO(el): This is subject to change (Feature #5683)
date_default_timezone_set('UTC');
// Log to the System Log
Logger::create(new Zend_Config(array(
'type' => 'syslog',
'application' => 'Icinga Web'
)));
$application->bootstrap(); $application->bootstrap();
return $application; return $application;
} }
@ -309,7 +319,12 @@ abstract class ApplicationBootstrap
$this->moduleManager = new ModuleManager( $this->moduleManager = new ModuleManager(
$this, $this,
$this->configDir . '/enabledModules', $this->configDir . '/enabledModules',
explode(':', $this->config->global->get('modulePath', ICINGA_APPDIR . '/../modules')) explode(
':',
$this->config->global !== null
? $this->config->global->get('modulePath', ICINGA_APPDIR . '/../modules')
: ICINGA_APPDIR . '/../modules'
)
); );
return $this; return $this;
} }
@ -323,21 +338,26 @@ abstract class ApplicationBootstrap
{ {
try { try {
$this->moduleManager->loadEnabledModules(); $this->moduleManager->loadEnabledModules();
} catch (Exception $e) { } catch (NotReadableError $e) {
Logger::exception(new Exception('Cannot load enabled modules. An exception was thrown:', 0, $e)); Logger::exception(new Exception('Cannot load enabled modules. An exception was thrown:', 0, $e));
} }
return $this; return $this;
} }
/** /**
* Load configuration * Load application configuration
* *
* @return self * @return self
*/ */
protected function loadConfig() protected function loadConfig()
{ {
Config::$configDir = $this->configDir; Config::$configDir = $this->configDir;
$this->config = Config::app(); try {
$this->config = Config::app();
} catch (NotReadableError $e) {
Logger::exception(new Exception('Cannot load application configuration. An exception was thrown:', 0, $e));
$this->config = new Zend_Config(array());
}
return $this; return $this;
} }
@ -361,19 +381,28 @@ abstract class ApplicationBootstrap
*/ */
protected function setupLogger() protected function setupLogger()
{ {
Logger::create($this->config->logging); if ($this->config->logging !== null) {
Logger::create($this->config->logging);
}
return $this; return $this;
} }
/** /**
* Setup factories that provide access to the resources * Set up the resource factory
* *
* @return self * @return self
*/ */
protected function setupResourceFactory() protected function setupResourceFactory()
{ {
$config = Config::app('resources'); try {
ResourceFactory::setConfig($config); $config = Config::app('resources');
ResourceFactory::setConfig($config);
} catch (NotReadableError $e) {
Logger::exception(
new Exception('Cannot load resource configuration. An exception was thrown:', 0, $e)
);
}
return $this; return $this;
} }
@ -385,7 +414,7 @@ abstract class ApplicationBootstrap
*/ */
protected function setupTimezone() protected function setupTimezone()
{ {
$timeZoneString = $this->config->global->get('timezone', 'UTC'); $timeZoneString = $this->config->global !== null ? $this->config->global->get('timezone', 'UTC') : 'UTC';
try { try {
$tz = new DateTimeZone($timeZoneString); $tz = new DateTimeZone($timeZoneString);
} catch (Exception $e) { } catch (Exception $e) {
@ -406,7 +435,10 @@ abstract class ApplicationBootstrap
protected function setupInternationalization() protected function setupInternationalization()
{ {
try { try {
Translator::setupLocale($this->config->global->get('language', Translator::DEFAULT_LOCALE)); Translator::setupLocale(
$this->config->global !== null ? $this->config->global->get('language', Translator::DEFAULT_LOCALE)
: Translator::DEFAULT_LOCALE
);
} catch (Exception $error) { } catch (Exception $error) {
Logger::info($error->getMessage()); Logger::info($error->getMessage());
} }

View File

@ -29,8 +29,9 @@
namespace Icinga\Application; namespace Icinga\Application;
use \Icinga\Exception\ProgrammingError; use Zend_Config_Ini;
use \Zend_Config_Ini; use Icinga\Exception\NotReadableError;
use Icinga\Exception\ProgrammingError;
/** /**
* Global registry of application and module configuration. * Global registry of application and module configuration.
@ -68,22 +69,25 @@ class Config extends Zend_Config_Ini
/** /**
* Load configuration from the config file $filename * Load configuration from the config file $filename
* *
* @param string $filename The filename to parse * @param string $filename The filename to parse
* @see Zend_Config_Ini::__construct * @throws NotReadableError When the file does not exist or cannot be read
* @throws Exception When the file can't be read
*/ */
public function __construct($filename) public function __construct($filename)
{ {
if (!@is_readable($filename)) { $canonical = realpath($filename);
throw new \Exception('Cannot read config file: ' . $filename); if ($canonical === false) {
throw new NotReadableError('Cannot read config file "' . $filename . '". Config file does not exist');
}
if (!is_readable($canonical)) {
throw new NotReadableError('Cannot read config file "' . $filename . '". Permission denied');
}; };
$this->configFile = $filename; $this->configFile = $canonical;
$section = null; $section = null;
$options = array( $options = array(
'allowModifications' => true 'allowModifications' => true
); );
parent::__construct($filename, $section, $options); parent::__construct($canonical, $section, $options);
} }
/** /**
@ -99,7 +103,7 @@ class Config extends Zend_Config_Ini
{ {
if (!isset(self::$app[$configname]) || $fromDisk) { if (!isset(self::$app[$configname]) || $fromDisk) {
$filename = self::$configDir . '/' . $configname . '.ini'; $filename = self::$configDir . '/' . $configname . '.ini';
self::$app[$configname] = new Config(realpath($filename)); self::$app[$configname] = new Config($filename);
} }
return self::$app[$configname]; return self::$app[$configname];
} }

View File

@ -40,6 +40,8 @@ use Icinga\Util\File;
/** /**
* Zend_Log wrapper * Zend_Log wrapper
*
* TODO(el): This is subject to change (Feature #5683)
*/ */
class Logger class Logger
{ {
@ -64,17 +66,6 @@ class Logger
*/ */
private static $instance; private static $instance;
/**
* Format for logging exceptions
*/
const LOG_EXCEPTION_FORMAT = <<<'EOD'
%s: %s
Stacktrace
----------
%s
EOD;
/** /**
* Create a new Logger * Create a new Logger
* *
@ -135,17 +126,17 @@ EOD;
default: default:
throw new ConfigurationError('Logger configuration defines an invalid log type "' . $type . '"'); throw new ConfigurationError('Logger configuration defines an invalid log type "' . $type . '"');
} }
if (($priority = $config->priority) === null) { if ($config->priority === null) {
$priority = Zend_Log::WARN; $priority = Zend_Log::WARN;
} else { } else {
$priority = (int) $priority; $priority = (int) $config->priority;
} }
$writer->addFilter(new Zend_Log_Filter_Priority($priority)); $writer->addFilter(new Zend_Log_Filter_Priority($priority));
$this->logger->addWriter($writer); $this->logger->addWriter($writer);
$this->writers[] = $writer; $this->writers[] = $writer;
} catch (Zend_Log_Exception $e) { } catch (Zend_Log_Exception $e) {
throw new ConfigurationError( throw new ConfigurationError(
'Cannot not add log writer of type "' . $type . '". An exception was thrown: '. $e->getMessage() 'Cannot not add log writer of type "' . $type . '". An exception was thrown: ', 0, $e
); );
} }
} }
@ -235,19 +226,19 @@ EOD;
/** /**
* Log a exception at a priority * Log a exception at a priority
* *
* @param Exception $e Exception to log * @param Exception $e Exception to log
* @param int $priority Priority of message * @param int $priority Priority of message
*/ */
public static function exception(Exception $e, $priority = Zend_Log::ERR) public static function exception(Exception $e, $priority = Zend_Log::ERR)
{ {
$message = array(); $message = array();
do { do {
$message[] = self::formatMessage( $message[] = self::formatMessage(
array(self::LOG_EXCEPTION_FORMAT, get_class($e), $e->getMessage(), $e->getTraceAsString()) array('%s in %s:%d %s', get_class($e), $e->getFile(), $e->getLine(), $e->getMessage())
); );
} while($e = $e->getPrevious()); } while($e = $e->getPrevious());
self::log( self::log(
implode(PHP_EOL, $message), implode(' ', $message),
$priority $priority
); );
} }

View File

@ -37,6 +37,7 @@ use Icinga\Data\DataArray\Query as ArrayQuery;
use Icinga\Exception\ConfigurationError; use Icinga\Exception\ConfigurationError;
use Icinga\Exception\SystemPermissionException; use Icinga\Exception\SystemPermissionException;
use Icinga\Exception\ProgrammingError; use Icinga\Exception\ProgrammingError;
use Icinga\Exception\NotReadableError;
/** /**
* Module manager that handles detecting, enabling and disabling of modules * Module manager that handles detecting, enabling and disabling of modules
@ -99,22 +100,10 @@ class Manager
* the given path * the given path
* @param array $availableDirs Installed modules location * @param array $availableDirs Installed modules location
**/ **/
public function __construct($app, $enabledDir = null, array $availableDirs = array()) public function __construct($app, $enabledDir, array $availableDirs)
{ {
$this->app = $app; $this->app = $app;
if (empty($availableDirs)) {
$availableDirs = array(realpath(ICINGA_APPDIR . '/../modules'));
} else {
foreach($availableDirs as $key => $dir) {
$dir[$key] = realpath($dir);
}
}
$this->modulePaths = $availableDirs; $this->modulePaths = $availableDirs;
if ($enabledDir === null) {
$enabledDir = $this->app->getConfigDir() . '/enabledModules';
}
$enabledDir = realpath($enabledDir);
$this->enableDir = $enabledDir; $this->enableDir = $enabledDir;
} }
@ -134,54 +123,59 @@ class Manager
* *
* Update the internal $enabledDirs property with the enabled modules. * Update the internal $enabledDirs property with the enabled modules.
* *
* @throws ConfigurationError If module dir is not a directory or not readable * @throws ConfigurationError If module dir does not exist, is not a directory or not readable
*/ */
private function detectEnabledModules() private function detectEnabledModules()
{ {
$canonical = $this->enableDir;
if ($canonical === false) {
throw new NotReadableError(
'Cannot read enabled modules. Module directory "' . $this->enableDir . '" does not exist'
);
}
if (!is_dir($this->enableDir)) { if (!is_dir($this->enableDir)) {
throw new ConfigurationError( throw new NotReadableError(
'Could not read enabled modules: Module directory is not a directory: ' . $this->enableDir 'Cannot read enabled modules. Module directory "' . $this->enableDir . '" is not a directory'
); );
} }
if (!is_readable($this->enableDir)) { if (!is_readable($this->enableDir)) {
throw new ConfigurationError( throw new NotReadableError(
'Could not read enabled modules: Module directory is not readable: ' . $this->enableDir 'Cannot read enabled modules. Module directory "' . $this->enableDir . '" is not readable'
); );
} }
if (($dh = opendir($canonical)) !== false) {
$this->enabledDirs = array();
while (($file = readdir($dh)) !== false) {
$fh = opendir($this->enableDir); if ($file[0] === '.' || $file === 'README') {
continue;
}
$this->enabledDirs = array(); $link = $this->enableDir . '/' . $file;
while (false !== ($file = readdir($fh))) { if (! is_link($link)) {
Logger::warn(
'Found invalid module in enabledModule directory "%s": "%s" is not a symlink',
$this->enableDir,
$link
);
continue;
}
if ($file[0] === '.' || $file === 'README') { $dir = realpath($link);
continue; if (!file_exists($dir) || !is_dir($dir)) {
Logger::warn(
'Found invalid module in enabledModule directory "%s": "%s" points to non existing path "%s"',
$this->enableDir,
$link,
$dir
);
continue;
}
$this->enabledDirs[$file] = $dir;
ksort($this->enabledDirs);
} }
closedir($dh);
$link = $this->enableDir . '/' . $file;
if (! is_link($link)) {
Logger::warn(
'Found invalid module in enabledModule directory "%s": "%s" is not a symlink',
$this->enableDir,
$link
);
continue;
}
$dir = realpath($link);
if (!file_exists($dir) || !is_dir($dir)) {
Logger::warn(
'Found invalid module in enabledModule directory "%s": "%s" points to non existing path "%s"',
$this->enableDir,
$link,
$dir
);
continue;
}
$this->enabledDirs[$file] = $dir;
ksort($this->enabledDirs);
} }
} }
@ -517,29 +511,37 @@ class Manager
public function detectInstalledModules() public function detectInstalledModules()
{ {
foreach ($this->modulePaths as $basedir) { foreach ($this->modulePaths as $basedir) {
if (!file_exists($basedir)) { $canonical = realpath($basedir);
Logger::warn('Module path "%s" does not exist.', $basedir); if ($canonical === false) {
Logger::warn('Module path "%s" does not exist', $basedir);
continue; continue;
} }
$fh = opendir($basedir); if (!is_dir($canonical)) {
if ($fh === false) { Logger::err('Module path "%s" is not a directory', $canonical);
return $this; continue;
} }
while ($name = readdir($fh)) { if (!is_readable($canonical)) {
if ($name[0] === '.') { Logger::err('Module path "%s" is not readable', $canonical);
continue; continue;
} }
if (is_dir($basedir . '/' . $name)) { if (($dh = opendir($canonical)) !== false) {
if (! array_key_exists($name, $this->installedBaseDirs)) { while (($file = readdir($dh)) !== false) {
$this->installedBaseDirs[$name] = $basedir . '/' . $name; if ($file[0] === '.') {
} else { continue;
Logger::warn( }
'Module "%s" already exists in installation path "%s" and is ignored.', if (is_dir($canonical . '/' . $file)) {
$basedir . '/' . $name, if (! array_key_exists($file, $this->installedBaseDirs)) {
$this->installedBaseDirs[$name] $this->installedBaseDirs[$file] = $canonical . '/' . $file;
); } else {
Logger::warn(
'Module "%s" already exists in installation path "%s" and is ignored.',
$canonical . '/' . $file,
$this->installedBaseDirs[$file]
);
}
} }
} }
closedir($dh);
} }
} }
ksort($this->installedBaseDirs); ksort($this->installedBaseDirs);

View File

@ -457,25 +457,7 @@ class Module
} }
$this->registerLocales() $this->registerLocales()
->registerRoutes() ->registerRoutes();
->registerMenuEntries();
return $this;
}
/**
* Register menu entries
*
* @return self
*/
protected function registerMenuEntries()
{
$cfg = $this->app
->getConfig()
->module($this->name, 'menu');
$view = $this->app->getViewRenderer();
if ($cfg) {
$view->view->navigation = $cfg->merge($view->view->navigation);
}
return $this; return $this;
} }

View File

@ -44,6 +44,7 @@ use \Zend_Controller_Front;
use Icinga\Application\Logger; use Icinga\Application\Logger;
use Icinga\Authentication\Manager as AuthenticationManager; use Icinga\Authentication\Manager as AuthenticationManager;
use Icinga\Exception\ConfigurationError; use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotReadableError;
use Icinga\User; use Icinga\User;
use Icinga\Web\Request; use Icinga\Web\Request;
use Icinga\Web\View; use Icinga\Web\View;
@ -208,7 +209,15 @@ class Web extends ApplicationBootstrap
*/ */
private function setupUser() private function setupUser()
{ {
$authenticationManager = AuthenticationManager::getInstance(); try {
$config = Config::app('authentication');
} catch (NotReadableError $e) {
Logger::exception(
new Exception('Cannot load authentication configuration. An exception was thrown:', 0, $e)
);
$config = null;
}
$authenticationManager = AuthenticationManager::getInstance($config);
if ($authenticationManager->isAuthenticated() === true) { if ($authenticationManager->isAuthenticated() === true) {
$this->user = $authenticationManager->getUser(); $this->user = $authenticationManager->getUser();
} }
@ -279,11 +288,10 @@ class Web extends ApplicationBootstrap
$view->view->setEncoding('UTF-8'); $view->view->setEncoding('UTF-8');
$view->view->headTitle()->prepend( $view->view->headTitle()->prepend(
$this->getConfig()->{'global'}->get('project', 'Icinga') $this->config->global !== null ? $this->config->global->get('project', 'Icinga') : 'Icinga'
); );
$view->view->headTitle()->setSeparator(' :: '); $view->view->headTitle()->setSeparator(' :: ');
$view->view->navigation = $this->getConfig()->app('menu');
$this->viewRenderer = $view; $this->viewRenderer = $view;

View File

@ -36,12 +36,12 @@ use Icinga\Web\Session;
use Icinga\Data\ResourceFactory; use Icinga\Data\ResourceFactory;
use Icinga\Application\Logger; use Icinga\Application\Logger;
use Icinga\Exception\ConfigurationError; use Icinga\Exception\ConfigurationError;
use Icinga\Exception\NotReadableError;
use Icinga\Application\Config as IcingaConfig; use Icinga\Application\Config as IcingaConfig;
use Icinga\Authentication\Backend\DbUserBackend; use Icinga\Authentication\Backend\DbUserBackend;
use Icinga\Authentication\Backend\LdapUserBackend; use Icinga\Authentication\Backend\LdapUserBackend;
use Icinga\User\Preferences; use Icinga\User\Preferences;
use Icinga\User\Preferences\PreferencesStore; use Icinga\User\Preferences\PreferencesStore;
use Icinga\Exception\NotReadableError;
/** /**
* The authentication manager allows to identify users and * The authentication manager allows to identify users and
@ -90,8 +90,10 @@ class Manager
**/ **/
private function __construct(Zend_Config $config = null) private function __construct(Zend_Config $config = null)
{ {
$this->config = $config === null ? IcingaConfig::app('authentication') : $config; if ($config !== null) {
$this->setupBackends($this->config); $this->setupBackends($config);
$this->config = $config;
}
} }
/** /**

View File

@ -31,7 +31,8 @@ namespace Icinga\Web;
use Icinga\Application\Config; use Icinga\Application\Config;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Icinga\Web\MenuItem; use Icinga\Application\Logger;
use Icinga\Exception\NotReadableError;
class Menu extends MenuItem class Menu extends MenuItem
{ {
@ -43,12 +44,22 @@ class Menu extends MenuItem
public static function fromConfig() { public static function fromConfig() {
$menu = new static('menu'); $menu = new static('menu');
$manager = Icinga::app()->getModuleManager(); $manager = Icinga::app()->getModuleManager();
$menuConfigs = array(Config::app('menu')); try {
foreach ($manager->listEnabledModules() as $moduleName) { $menuConfigs = array(Config::app('menu'));
$moduleMenuConfig = Config::module($moduleName, 'menu'); } catch (NotReadableError $e) {
if ($moduleMenuConfig) { Logger::exception($e);
$menuConfigs[] = $moduleMenuConfig; $menuConfigs = array();
}
try {
foreach ($manager->listEnabledModules() as $moduleName) {
$moduleMenuConfig = Config::module($moduleName, 'menu');
if ($moduleMenuConfig) {
$menuConfigs[] = $moduleMenuConfig;
}
} }
} catch (NotReadableError $e) {
Logger::exception($e);
} }
return $menu->loadMenuItems($menu->flattenConfigs($menuConfigs)); return $menu->loadMenuItems($menu->flattenConfigs($menuConfigs));
} }