From f4054d575bc3f1f2ad596218408e0e19d559246c Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Thu, 16 Jul 2015 15:29:45 +0200 Subject: [PATCH 1/5] Add Inspection API to db connection refs #9641 --- .../forms/Config/Resource/DbResourceForm.php | 15 +++---- library/Icinga/Data/Db/DbConnection.php | 43 ++++++++++++++++++- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/application/forms/Config/Resource/DbResourceForm.php b/application/forms/Config/Resource/DbResourceForm.php index 0ee64eda3..75d935361 100644 --- a/application/forms/Config/Resource/DbResourceForm.php +++ b/application/forms/Config/Resource/DbResourceForm.php @@ -129,16 +129,13 @@ class DbResourceForm extends Form */ public static function isValidResource(Form $form) { - try { - $resource = ResourceFactory::createResource(new ConfigObject($form->getValues())); - $resource->getConnection()->getConnection(); - } catch (Exception $e) { - $form->addError( - $form->translate('Connectivity validation failed, connection to the given resource not possible.') - ); - return false; + $result = ResourceFactory::createResource(new ConfigObject($form->getValues()))->inspect(); + if ($result->hasError()) { + $form->addError(sprintf($form->translate('Connectivity validation failed: %s'), $result->getError())); } - return true; + // TODO: display diagnostics in $result->toArray() to the user + + return ! $result->hasError(); } } diff --git a/library/Icinga/Data/Db/DbConnection.php b/library/Icinga/Data/Db/DbConnection.php index 95f3e9235..ba7a3c6f0 100644 --- a/library/Icinga/Data/Db/DbConnection.php +++ b/library/Icinga/Data/Db/DbConnection.php @@ -3,6 +3,9 @@ namespace Icinga\Data\Db; +use Exception; +use Icinga\Data\Inspectable; +use Icinga\Data\Inspection; use PDO; use Iterator; use Zend_Db; @@ -23,7 +26,7 @@ use Icinga\Exception\ProgrammingError; /** * Encapsulate database connections and query creation */ -class DbConnection implements Selectable, Extensible, Updatable, Reducible +class DbConnection implements Selectable, Extensible, Updatable, Reducible, Inspectable { /** * Connection config @@ -435,4 +438,42 @@ class DbConnection implements Selectable, Extensible, Updatable, Reducible return $column . ' ' . $sign . ' ' . $this->dbAdapter->quote($value); } } + + public function inspect() + { + $insp = new Inspection('Db Connection'); + try { + $this->getDbAdapter()->getConnection(); + $config = $this->dbAdapter->getConfig(); + $insp->write(sprintf( + 'Connection to %s as %s on %s:%s successful', + $config['dbname'], + $config['username'], + $config['host'], + $config['port'] + )); + switch ($this->dbType) { + case 'mysql': + $rows = $this->dbAdapter->query( + 'SHOW VARIABLES WHERE variable_name ' . + 'IN (\'version\', \'protocol_version\', \'version_compile_os\');' + )->fetchAll(); + $sqlinsp = new Inspection('MySQL'); + foreach ($rows as $row) { + $sqlinsp->write($row->variable_name . ': ' . $row->value); + } + $insp->write($sqlinsp); + break; + case 'pgsql': + $row = $this->dbAdapter->query('SELECT version();')->fetchAll(); + $sqlinsp = new Inspection('PostgreSQL'); + $sqlinsp->write($row[0]->version); + $insp->write($sqlinsp); + break; + } + } catch (Exception $e) { + return $insp->error(sprintf('Connection failed %s', $e->getMessage())); + } + return $insp; + } } From 31618ce2cf815bd3f5ddb916881b28b4255991c1 Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Thu, 16 Jul 2015 16:15:27 +0200 Subject: [PATCH 2/5] Fix unit DB Form unit tests broken by inspection refs #9641 --- .../Config/Resource/DbResourceFormTest.php | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/test/php/application/forms/Config/Resource/DbResourceFormTest.php b/test/php/application/forms/Config/Resource/DbResourceFormTest.php index 3cf23a70a..6aa51dbff 100644 --- a/test/php/application/forms/Config/Resource/DbResourceFormTest.php +++ b/test/php/application/forms/Config/Resource/DbResourceFormTest.php @@ -26,7 +26,7 @@ class DbResourceFormTest extends BaseTestCase public function testValidDbResourceIsValid() { $this->setUpResourceFactoryMock( - Mockery::mock()->shouldReceive('getConnection')->atMost()->twice()->andReturn(Mockery::self())->getMock() + Mockery::mock()->shouldReceive('inspect')->andReturn(self::createInspector(false))->getMock() ); $this->assertTrue( @@ -42,7 +42,7 @@ class DbResourceFormTest extends BaseTestCase public function testInvalidDbResourceIsNotValid() { $this->setUpResourceFactoryMock( - Mockery::mock()->shouldReceive('getConnection')->once()->andThrow('\Exception')->getMock() + Mockery::mock()->shouldReceive('inspect')->andReturn(self::createInspector(true))->getMock() ); $this->assertFalse( @@ -58,4 +58,21 @@ class DbResourceFormTest extends BaseTestCase ->with(Mockery::type('Icinga\Data\ConfigObject')) ->andReturn($resourceMock); } + + public static function createInspector($error = false, $log = array('log')) + { + if (! $error) { + $calls = array( + 'hasError' => false, + 'toArray' => $log + ); + } else { + $calls = array( + 'hasError' => true, + 'getError' => 'Error', + 'toArray' => $log + ); + } + return Mockery::mock('Icinga\Data\Inspection', $calls); + } } From e357960d1ed74bb29227377c2804a6024f3d78bf Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Thu, 16 Jul 2015 16:16:55 +0200 Subject: [PATCH 3/5] Add Inspection API to DB backend refs #9641 --- .../Config/UserBackend/DbBackendForm.php | 17 +++++------- .../Authentication/User/DbUserBackend.php | 26 ++++++++++++++++++- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/application/forms/Config/UserBackend/DbBackendForm.php b/application/forms/Config/UserBackend/DbBackendForm.php index f096f3e35..c09ec166c 100644 --- a/application/forms/Config/UserBackend/DbBackendForm.php +++ b/application/forms/Config/UserBackend/DbBackendForm.php @@ -105,18 +105,15 @@ class DbBackendForm extends Form */ public static function isValidUserBackend(Form $form) { - try { - $dbUserBackend = new DbUserBackend(ResourceFactory::createResource($form->getResourceConfig())); - if ($dbUserBackend->select()->where('is_active', true)->count() < 1) { - $form->addError($form->translate('No active users found under the specified database backend')); - return false; - } - } catch (Exception $e) { - $form->addError(sprintf($form->translate('Using the specified backend failed: %s'), $e->getMessage())); - return false; + $backend = new DbUserBackend(ResourceFactory::createResource($form->getResourceConfig())); + $result = $backend->inspect(); + if ($result->hasError()) { + $form->addError(sprintf($form->translate('Using the specified backend failed: %s'), $result->getError())); } - return true; + // TODO: display diagnostics in $result->toArray() to the user + + return ! $result->hasError(); } /** diff --git a/library/Icinga/Authentication/User/DbUserBackend.php b/library/Icinga/Authentication/User/DbUserBackend.php index 0e5dad5fa..3c28a2fdd 100644 --- a/library/Icinga/Authentication/User/DbUserBackend.php +++ b/library/Icinga/Authentication/User/DbUserBackend.php @@ -4,13 +4,15 @@ namespace Icinga\Authentication\User; use Exception; +use Icinga\Data\Inspectable; +use Icinga\Data\Inspection; use PDO; use Icinga\Data\Filter\Filter; use Icinga\Exception\AuthenticationException; use Icinga\Repository\DbRepository; use Icinga\User; -class DbUserBackend extends DbRepository implements UserBackendInterface +class DbUserBackend extends DbRepository implements UserBackendInterface, Inspectable { /** * The algorithm to use when hashing passwords @@ -246,4 +248,26 @@ class DbUserBackend extends DbRepository implements UserBackendInterface { return crypt($password, self::HASH_ALGORITHM . ($salt !== null ? $salt : $this->generateSalt())); } + + /** + * Inspect this object to gain extended information about its health + * + * @return Inspection The inspection result + */ + public function inspect() + { + $insp = new Inspection('Db User Backend'); + $insp->write($this->ds->inspect()); + try { + $users = $this->select()->where('is_active', true)->count(); + if ($users > 1) { + $insp->write(sprintf('%s active users', $users)); + } else { + return $insp->error('0 active users', $users); + } + } catch (Exception $e) { + $insp->error(sprintf('Query failed: %s', $e->getMessage())); + } + return $insp; + } } From 6d209ee20316015490b91a84ab60435ba2ca541d Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Thu, 16 Jul 2015 16:22:13 +0200 Subject: [PATCH 4/5] Fix unit DB Form unit tests broken by inspection refs #9641 --- .../Config/UserBackend/DbBackendFormTest.php | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/test/php/application/forms/Config/UserBackend/DbBackendFormTest.php b/test/php/application/forms/Config/UserBackend/DbBackendFormTest.php index d58ff8a33..f1fe7b84d 100644 --- a/test/php/application/forms/Config/UserBackend/DbBackendFormTest.php +++ b/test/php/application/forms/Config/UserBackend/DbBackendFormTest.php @@ -28,8 +28,8 @@ class DbBackendFormTest extends BaseTestCase { $this->setUpResourceFactoryMock(); Mockery::mock('overload:Icinga\Authentication\User\DbUserBackend') - ->shouldReceive('select->where->count') - ->andReturn(2); + ->shouldReceive('inspect') + ->andReturn(self::createInspector(false)); // Passing array(null) is required to make Mockery call the constructor... $form = Mockery::mock('Icinga\Forms\Config\UserBackend\DbBackendForm[getView]', array(null)); @@ -54,8 +54,8 @@ class DbBackendFormTest extends BaseTestCase { $this->setUpResourceFactoryMock(); Mockery::mock('overload:Icinga\Authentication\User\DbUserBackend') - ->shouldReceive('count') - ->andReturn(0); + ->shouldReceive('inspect') + ->andReturn(self::createInspector(true)); // Passing array(null) is required to make Mockery call the constructor... $form = Mockery::mock('Icinga\Forms\Config\UserBackend\DbBackendForm[getView]', array(null)); @@ -80,4 +80,21 @@ class DbBackendFormTest extends BaseTestCase ->shouldReceive('getResourceConfig') ->andReturn(new ConfigObject()); } + + public static function createInspector($error = false, $log = array('log')) + { + if (! $error) { + $calls = array( + 'hasError' => false, + 'toArray' => $log + ); + } else { + $calls = array( + 'hasError' => true, + 'getError' => 'Error', + 'toArray' => $log + ); + } + return Mockery::mock('Icinga\Data\Inspection', $calls); + } } From 9ba4189617fa8bcfa2c38fbd6c20e628ab7c66d4 Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Thu, 16 Jul 2015 16:27:28 +0200 Subject: [PATCH 5/5] fix coding guideline violations --- .../forms/Config/UserBackend/DbBackendFormTest.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/php/application/forms/Config/UserBackend/DbBackendFormTest.php b/test/php/application/forms/Config/UserBackend/DbBackendFormTest.php index f1fe7b84d..cc6919c93 100644 --- a/test/php/application/forms/Config/UserBackend/DbBackendFormTest.php +++ b/test/php/application/forms/Config/UserBackend/DbBackendFormTest.php @@ -35,7 +35,9 @@ class DbBackendFormTest extends BaseTestCase $form = Mockery::mock('Icinga\Forms\Config\UserBackend\DbBackendForm[getView]', array(null)); $form->shouldReceive('getView->escape') ->with(Mockery::type('string')) - ->andReturnUsing(function ($s) { return $s; }); + ->andReturnUsing(function ($s) { + return $s; + }); $form->setTokenDisabled(); $form->setResources(array('test_db_backend')); $form->populate(array('resource' => 'test_db_backend')); @@ -61,7 +63,9 @@ class DbBackendFormTest extends BaseTestCase $form = Mockery::mock('Icinga\Forms\Config\UserBackend\DbBackendForm[getView]', array(null)); $form->shouldReceive('getView->escape') ->with(Mockery::type('string')) - ->andReturnUsing(function ($s) { return $s; }); + ->andReturnUsing(function ($s) { + return $s; + }); $form->setTokenDisabled(); $form->setResources(array('test_db_backend')); $form->populate(array('resource' => 'test_db_backend'));