From 7ba6e4550fb3c0af4f97a020fbbafb1c901866e3 Mon Sep 17 00:00:00 2001 From: Tim Younger Date: Sat, 3 Sep 2016 15:03:17 -0700 Subject: [PATCH] add a health-check controller which simply returns an empty response with a pass/fail HTTP status code based on the diagnostic results. the default status codes can be overridden via setter if you create and register a factory for the controller. --- composer.json | 4 +- config/module.config.php | 1 + config/zftool.global.php.dist | 12 +- .../Controller/HealthCheckController.php | 77 ++++++++++ .../Controller/HealthCheckControllerTest.php | 143 ++++++++++++++++++ .../Diagnostics/DiagnosticsControllerTest.php | 2 + 6 files changed, 236 insertions(+), 3 deletions(-) create mode 100644 src/ZFTool/Controller/HealthCheckController.php create mode 100644 tests/ZFToolTest/Controller/HealthCheckControllerTest.php diff --git a/composer.json b/composer.json index 688e637..6852487 100644 --- a/composer.json +++ b/composer.json @@ -30,9 +30,9 @@ "zendframework/zend-loader": ">=2.2.2", "zendframework/zend-log": ">=2.2.2", "zendframework/zend-modulemanager": ">=2.2.2", - "zendframework/zend-mvc": ">=2.2.2", + "zendframework/zend-mvc": "^2.2.2", "zendframework/zend-serializer": ">=2.2.2", - "zendframework/zend-servicemanager": ">=2.2.2", + "zendframework/zend-servicemanager": "^2.2.2", "zendframework/zend-stdlib": ">=2.2.2", "zendframework/zend-text": ">=2.2.2", "zendframework/zend-version": ">=2.2.2 ", diff --git a/config/module.config.php b/config/module.config.php index edb7565..af668e9 100644 --- a/config/module.config.php +++ b/config/module.config.php @@ -15,6 +15,7 @@ 'ZFTool\Controller\Create' => 'ZFTool\Controller\CreateController', 'ZFTool\Controller\Install' => 'ZFTool\Controller\InstallController', 'ZFTool\Controller\Diagnostics' => 'ZFTool\Controller\DiagnosticsController', + 'ZFTool\Controller\HealthCheck' => 'ZFTool\Controller\HealthCheckController', ), ), diff --git a/config/zftool.global.php.dist b/config/zftool.global.php.dist index 114d8c0..c1a3928 100644 --- a/config/zftool.global.php.dist +++ b/config/zftool.global.php.dist @@ -13,7 +13,17 @@ return array( 'action' => 'run' ) ) - ) + ), + 'zftool-health-check' => array( + 'type' => 'Zend\Mvc\Router\Http\Literal', + 'options' => array( + 'route' => '/health-check', + 'defaults' => array( + 'controller' => 'ZFTool\Controller\HealthCheck', + 'action' => 'run', + ), + ), + ), ) ), */ diff --git a/src/ZFTool/Controller/HealthCheckController.php b/src/ZFTool/Controller/HealthCheckController.php new file mode 100644 index 0000000..a916afe --- /dev/null +++ b/src/ZFTool/Controller/HealthCheckController.php @@ -0,0 +1,77 @@ +forward(); + + $diagnosticsResponse = $forward->dispatch('ZFTool\Controller\Diagnostics', array( + 'action' => 'run', + )); + + if (!$diagnosticsResponse instanceof ViewModel) { + throw new \UnexpectedValueException( + 'Response should be an instance of `\Zend\View\Model\ViewModel`.' + ); + } + + if ($diagnosticsResponse instanceof JsonModel) { + $failureCount = $diagnosticsResponse->getVariable('failure', null); + if (!is_numeric($failureCount)) { + throw new \UnexpectedValueException('Response should have a numeric `failure` variable.'); + } + } else { + $results = $diagnosticsResponse->getVariable('results'); + if (!$results instanceof Collection) { + throw new \UnexpectedValueException( + 'Response should have a `results` variable of type `\ZendDiagnostics\Result\Collection`.' + ); + } + + $failureCount = $results->getFailureCount(); + } + + $response = new Response(); + $response->setStatusCode($failureCount < 1 ? $this->healthyStatusCode : $this->unhealthyStatusCode); + return $response; + } + + /** + * @param int $healthyStatusCode + */ + public function setHealthyStatusCode($healthyStatusCode) + { + $this->healthyStatusCode = $healthyStatusCode; + } + + /** + * @param int $unhealthyStatusCode + */ + public function setUnhealthyStatusCode($unhealthyStatusCode) + { + $this->unhealthyStatusCode = $unhealthyStatusCode; + } +} diff --git a/tests/ZFToolTest/Controller/HealthCheckControllerTest.php b/tests/ZFToolTest/Controller/HealthCheckControllerTest.php new file mode 100644 index 0000000..fb1d6f7 --- /dev/null +++ b/tests/ZFToolTest/Controller/HealthCheckControllerTest.php @@ -0,0 +1,143 @@ +forward = $forward = $this->getMockBuilder('\Zend\Mvc\Controller\Plugin\Forward') + ->disableOriginalConstructor() + ->getMock(); + + $this->plugins = $this->getMockBuilder('\Zend\Mvc\Controller\PluginManager') + ->disableOriginalConstructor() + ->getMock(); + + $this->plugins->method('get')->willReturnCallback(function ($name) use ($forward) { + return $name === 'forward' ? $forward : null; + }); + + $this->sut = new HealthCheckController(); + $this->sut->setHealthyStatusCode(200); + $this->sut->setUnhealthyStatusCode(500); + $this->sut->setPluginManager($this->plugins); + } + + public function testHandleJsonSuccess() + { + $diagnosticsResponse = new JsonModel(); + $diagnosticsResponse->setVariable('failure', 0); + + $this->forward->method('dispatch')->willReturn($diagnosticsResponse); + + $actual = $this->sut->runAction(); + + self::assertInstanceOf('\Zend\Http\Response', $actual); + self::assertEquals(200, $actual->getStatusCode()); + } + + public function testHandleJsonFailure() + { + $diagnosticsResponse = new JsonModel(); + $diagnosticsResponse->setVariable('failure', 1); + + $this->forward->method('dispatch')->willReturn($diagnosticsResponse); + + $actual = $this->sut->runAction(); + + self::assertInstanceOf('\Zend\Http\Response', $actual); + self::assertEquals(500, $actual->getStatusCode()); + } + + /** + * @expectedException \UnexpectedValueException + */ + public function testHandleJsonInvalidResponse() + { + $diagnosticsResponse = new JsonModel(); + + $this->forward->method('dispatch')->willReturn($diagnosticsResponse); + + $this->sut->runAction(); + } + + public function testHandleHtmlSuccess() + { + $results = new Collection(); + + $diagnosticsResponse = new ViewModel(); + $diagnosticsResponse->setVariable('results', $results); + + $this->forward->method('dispatch')->willReturn($diagnosticsResponse); + + $actual = $this->sut->runAction(); + + self::assertInstanceOf('\Zend\Http\Response', $actual); + self::assertEquals(200, $actual->getStatusCode()); + } + + public function testHandleHtmlFailure() + { + $check = $this->getMockBuilder('\ZendDiagnostics\Check\CheckInterface')->getMock(); + + $results = new Collection(); + $results->offsetSet($check, new Failure()); + + $diagnosticsResponse = new ViewModel(); + $diagnosticsResponse->setVariable('results', $results); + + $this->forward->method('dispatch')->willReturn($diagnosticsResponse); + + $actual = $this->sut->runAction(); + + self::assertInstanceOf('\Zend\Http\Response', $actual); + self::assertEquals(500, $actual->getStatusCode()); + } + + /** + * @expectedException \UnexpectedValueException + */ + public function testHandleHtmlInvalidResponse() + { + $results = null; + + $diagnosticsResponse = new ViewModel(); + $diagnosticsResponse->setVariable('results', $results); + + $this->forward->method('dispatch')->willReturn($diagnosticsResponse); + + $this->sut->runAction(); + } + + /** + * @expectedException \UnexpectedValueException + */ + public function testHandleInvalidResponse() + { + $diagnosticsResponse = null; + + $this->forward->method('dispatch')->willReturn($diagnosticsResponse); + + $this->sut->runAction(); + } +} diff --git a/tests/ZFToolTest/Diagnostics/DiagnosticsControllerTest.php b/tests/ZFToolTest/Diagnostics/DiagnosticsControllerTest.php index cded7ee..95f8624 100644 --- a/tests/ZFToolTest/Diagnostics/DiagnosticsControllerTest.php +++ b/tests/ZFToolTest/Diagnostics/DiagnosticsControllerTest.php @@ -60,6 +60,8 @@ class DiagnosticsControllerTest extends \PHPUnit_Framework_TestCase public function setup() { + \PHPUnit_Framework_Error_Deprecated::$enabled = false; + $this->config = new ArrayObject(array( 'diagnostics' => array() ));