├── tests ├── routing.yml ├── routing_anno.yml ├── bootstrap.php ├── config_test.yml ├── config_test_with_mysql.yml ├── config_test_anno.yml ├── config_test_with_mysql_optional.yml ├── config_test_lazy.yml ├── config_test_with_redis_optional.yml ├── TestKernel.php ├── config_test_travis.yml ├── config_test_with_redis.yml └── Ekreative │ └── HealthCheckBundle │ └── Controller │ └── HealthCheckControllerTest.php ├── .gitignore ├── src └── Ekreative │ └── HealthCheckBundle │ ├── EkreativeHealthCheckBundle.php │ ├── Resources │ └── config │ │ └── routes.xml │ ├── DependencyInjection │ ├── RedisFactory.php │ ├── Configuration.php │ └── EkreativeHealthCheckExtension.php │ └── Controller │ └── HealthCheckController.php ├── .php-cs-fixer.dist.php ├── phpunit.xml.dist ├── CHANGELOG.md ├── LICENSE ├── .github └── workflows │ └── tests.yaml ├── composer.json └── README.md /tests/routing.yml: -------------------------------------------------------------------------------- 1 | ekreative_health_check: 2 | resource: '@EkreativeHealthCheckBundle/Resources/config/routes.xml' 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.php_cs.cache 2 | /.phpunit.result.cache 3 | /tests/cache/* 4 | /tests/logs/* 5 | /vendor/ 6 | /var/ 7 | /.php-cs-fixer.cache 8 | -------------------------------------------------------------------------------- /tests/routing_anno.yml: -------------------------------------------------------------------------------- 1 | ekreative_health_check: 2 | resource: "@EkreativeHealthCheckBundle/Controller/" 3 | type: attribute 4 | prefix: / 5 | -------------------------------------------------------------------------------- /src/Ekreative/HealthCheckBundle/EkreativeHealthCheckBundle.php: -------------------------------------------------------------------------------- 1 | in(['src', 'tests']) 5 | ->exclude(['cache']); 6 | 7 | return (new PhpCsFixer\Config()) 8 | ->setRules(['@Symfony' => true, 'array_syntax' => ['syntax' => 'short'], 'ordered_imports' => true]) 9 | ->setFinder($finder); 10 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /tests/config_test.yml: -------------------------------------------------------------------------------- 1 | framework: 2 | test: ~ 3 | router: 4 | resource: "%kernel.project_dir%/tests/routing.yml" 5 | strict_requirements: ~ 6 | utf8: true 7 | secret: 'fake_secret' 8 | http_method_override: true 9 | php_errors: 10 | log: true 11 | 12 | monolog: 13 | handlers: 14 | main: 15 | type: console 16 | 17 | doctrine: 18 | dbal: 19 | connections: 20 | default: 21 | driver: pdo_sqlite 22 | path: '%kernel.cache_dir%/db.sqlite' 23 | -------------------------------------------------------------------------------- /tests/config_test_with_mysql.yml: -------------------------------------------------------------------------------- 1 | framework: 2 | test: ~ 3 | router: 4 | resource: "%kernel.project_dir%/tests/routing.yml" 5 | strict_requirements: ~ 6 | utf8: true 7 | secret: 'fake_secret' 8 | 9 | monolog: 10 | handlers: 11 | main: 12 | type: console 13 | 14 | doctrine: 15 | dbal: 16 | connections: 17 | default: 18 | driver: pdo_mysql 19 | host: 'example.com' 20 | options: 21 | !php/const PDO::ATTR_TIMEOUT: 1 22 | -------------------------------------------------------------------------------- /tests/config_test_anno.yml: -------------------------------------------------------------------------------- 1 | framework: 2 | test: ~ 3 | router: 4 | resource: "%kernel.project_dir%/tests/routing_anno.yml" 5 | strict_requirements: ~ 6 | utf8: true 7 | secret: 'fake_secret' 8 | 9 | monolog: 10 | handlers: 11 | main: 12 | type: console 13 | 14 | doctrine: 15 | dbal: 16 | connections: 17 | default: 18 | driver: pdo_sqlite 19 | path: '%kernel.cache_dir%/db.sqlite' 20 | 21 | ekreative_health_check: 22 | redis: 23 | - 'redis' 24 | 25 | services: 26 | redis: 27 | class: Redis 28 | public: true 29 | -------------------------------------------------------------------------------- /tests/config_test_with_mysql_optional.yml: -------------------------------------------------------------------------------- 1 | framework: 2 | test: ~ 3 | router: 4 | resource: "%kernel.project_dir%/tests/routing.yml" 5 | strict_requirements: ~ 6 | utf8: true 7 | secret: 'fake_secret' 8 | 9 | monolog: 10 | handlers: 11 | main: 12 | type: console 13 | 14 | doctrine: 15 | dbal: 16 | connections: 17 | default: 18 | driver: pdo_mysql 19 | host: 'example.com' 20 | options: 21 | !php/const PDO::ATTR_TIMEOUT: 1 22 | 23 | ekreative_health_check: 24 | optional_doctrine: 25 | - 'default' 26 | -------------------------------------------------------------------------------- /tests/config_test_lazy.yml: -------------------------------------------------------------------------------- 1 | framework: 2 | test: ~ 3 | router: 4 | resource: "%kernel.project_dir%/tests/routing.yml" 5 | strict_requirements: ~ 6 | utf8: true 7 | secret: 'fake_secret' 8 | 9 | monolog: 10 | handlers: 11 | main: 12 | type: console 13 | 14 | doctrine: 15 | dbal: 16 | connections: 17 | default: 18 | driver: pdo_sqlite 19 | path: '%kernel.cache_dir%/db.sqlite' 20 | 21 | ekreative_health_check: 22 | redis: 23 | - 'redis' 24 | 25 | services: 26 | redis: 27 | class: Redis 28 | factory: Ekreative\HealthCheckBundle\Controller\HealthCheckControllerTest::getLazyRedis 29 | 30 | -------------------------------------------------------------------------------- /tests/config_test_with_redis_optional.yml: -------------------------------------------------------------------------------- 1 | framework: 2 | test: ~ 3 | router: 4 | resource: "%kernel.project_dir%/tests/routing.yml" 5 | strict_requirements: ~ 6 | utf8: true 7 | secret: 'fake_secret' 8 | 9 | monolog: 10 | handlers: 11 | main: 12 | type: console 13 | 14 | doctrine: 15 | dbal: 16 | connections: 17 | default: 18 | driver: pdo_sqlite 19 | path: '%kernel.cache_dir%/db.sqlite' 20 | 21 | ekreative_health_check: 22 | optional_redis: 23 | - 'redis' 24 | 25 | services: 26 | redis: 27 | class: Redis 28 | factory: Ekreative\HealthCheckBundle\DependencyInjection\RedisFactory::get 29 | arguments: 30 | $host: 'example.com' 31 | -------------------------------------------------------------------------------- /tests/TestKernel.php: -------------------------------------------------------------------------------- 1 | load($this->getProjectDir().'/tests/config_'.$this->getEnvironment().'.yml'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/config_test_travis.yml: -------------------------------------------------------------------------------- 1 | framework: 2 | test: ~ 3 | router: 4 | resource: "%kernel.project_dir%/tests/routing.yml" 5 | strict_requirements: ~ 6 | utf8: true 7 | secret: 'fake_secret' 8 | 9 | monolog: 10 | handlers: 11 | main: 12 | type: console 13 | 14 | doctrine: 15 | dbal: 16 | connections: 17 | default: 18 | driver: pdo_mysql 19 | host: '127.0.0.1' 20 | user: root 21 | options: 22 | !php/const PDO::ATTR_TIMEOUT: 1 23 | 24 | ekreative_health_check: 25 | redis: 26 | - 'redis' 27 | 28 | services: 29 | redis: 30 | class: Redis 31 | factory: Ekreative\HealthCheckBundle\DependencyInjection\RedisFactory::get 32 | arguments: 33 | $host: '127.0.0.1' 34 | -------------------------------------------------------------------------------- /tests/config_test_with_redis.yml: -------------------------------------------------------------------------------- 1 | framework: 2 | test: ~ 3 | router: 4 | resource: "%kernel.project_dir%/tests/routing.yml" 5 | strict_requirements: ~ 6 | utf8: true 7 | secret: 'fake_secret' 8 | http_method_override: true 9 | php_errors: 10 | log: true 11 | 12 | monolog: 13 | handlers: 14 | main: 15 | type: console 16 | 17 | doctrine: 18 | dbal: 19 | connections: 20 | default: 21 | driver: pdo_sqlite 22 | path: '%kernel.cache_dir%/db.sqlite' 23 | 24 | ekreative_health_check: 25 | redis: 26 | - 'redis' 27 | 28 | services: 29 | redis: 30 | class: Redis 31 | public: true 32 | factory: Ekreative\HealthCheckBundle\DependencyInjection\RedisFactory::get 33 | arguments: 34 | $host: 'example.com' 35 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | tests/*/*Bundle 16 | 17 | 18 | 19 | 20 | src 21 | 22 | 23 | src/*/*Bundle/Resources 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Ekreative/HealthCheckBundle/DependencyInjection/RedisFactory.php: -------------------------------------------------------------------------------- 1 | connect($host, $port, $timeout); 21 | if ($prefix) { 22 | $redis->setOption(\Redis::OPT_PREFIX, $prefix); 23 | } 24 | } catch (\RedisException|\ErrorException $e) { 25 | } 26 | 27 | restore_error_handler(); 28 | 29 | return $redis; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.12.0 4 | 5 | - Support for Symfony 7 6 | 7 | # 1.11.0 8 | 9 | - Using annotations for routes 10 | 11 | # 1.10.0 12 | 13 | - Allow different Redis classes to be used 14 | 15 | # 1.9.0 16 | 17 | - Support for Symfony 6 18 | 19 | # 1.8.0 20 | 21 | - Dependency updates 22 | 23 | ## 1.7.0 24 | 25 | - Fix default redis port 26 | 27 | This is potentially breaking change, but given its very unlikely someone is 28 | using this wrong default port, I'm going to make it a minor fix. 29 | 30 | ## 1.6.1 31 | 32 | - Fix travis tests 33 | 34 | ## 1.6.0 35 | 36 | - Enable Symfony 5 37 | 38 | ## 1.5.0 39 | 40 | - Change to XML routes file. 41 | 42 | ## 1.4.0 43 | 44 | - Symfony 4.1 deprecations. 45 | 46 | ## 1.3.0 47 | 48 | - Remove dependency on framework-extra-bundle. 49 | 50 | ## 1.2.1 51 | 52 | - Catch all Redis errors. 53 | 54 | ## 1.2.0 55 | 56 | - Inject services into the controller instead of using `container->get`. 57 | - Add redis factory helper class. 58 | - Support for Symfony 4. 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 eKreative 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /.github/workflows/tests.yaml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | tests: 9 | runs-on: ubuntu-22.04 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | php: ["8.3"] 14 | symfony: ["5.4", "6.4", "7.0"] 15 | enable-redis: [true, false] 16 | name: PHP ${{ matrix.php }} Symfony ${{ matrix.symfony }} Redis ${{ matrix.enable-redis && 'enabled' || 'disabled' }} 17 | steps: 18 | - uses: actions/checkout@v4 19 | - uses: shivammathur/setup-php@v2 20 | with: 21 | php-version: ${{ matrix.php }} 22 | tools: phpunit-bridge, flex 23 | extensions: "pdo_sqlite ${{ matrix.enable-redis && ', redis' || '' }}" 24 | coverage: none 25 | - run: | 26 | composer config extra.symfony.require ${{ matrix.symfony }} 27 | composer update 28 | - run: vendor/bin/php-cs-fixer fix --dry-run --diff 29 | - run: vendor/bin/phpunit --exclude-group not-${{ matrix.symfony }} ${{ !matrix.enable-redis && '--exclude-group redis' || '' }} 30 | env: 31 | SYMFONY_DEPRECATIONS_HELPER: "disabled=1" 32 | -------------------------------------------------------------------------------- /src/Ekreative/HealthCheckBundle/DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | getRootNode(); 16 | } else { 17 | // BC layer for symfony/config 4.1 and older 18 | $rootNode = $treeBuilder->root('ekreative_health_check'); 19 | } 20 | 21 | $rootNode->children() 22 | ->arrayNode('redis') 23 | ->prototype('scalar')->end() 24 | ->end() 25 | ->arrayNode('optional_redis') 26 | ->prototype('scalar')->end() 27 | ->end() 28 | ->arrayNode('doctrine') 29 | ->prototype('scalar')->end() 30 | ->end() 31 | ->arrayNode('optional_doctrine') 32 | ->prototype('scalar')->end() 33 | ->end() 34 | ->booleanNode('doctrine_enabled') 35 | ->defaultTrue() 36 | ->end() 37 | ->end() 38 | ; 39 | 40 | return $treeBuilder; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ekreative/health-check-bundle", 3 | "license": "MIT", 4 | "type": "symfony-bundle", 5 | "description": "A bundle that provides a simple /healthcheck route", 6 | "keywords": ["healthcheck", "doctrine", "redis"], 7 | "autoload": { 8 | "psr-4": { 9 | "Ekreative\\HealthCheckBundle\\": "src/Ekreative/HealthCheckBundle/" 10 | } 11 | }, 12 | "autoload-dev": { 13 | "psr-4": { 14 | "Ekreative\\HealthCheckBundle\\": "tests/Ekreative/HealthCheckBundle/" 15 | }, 16 | "classmap": [ 17 | "tests/TestKernel.php" 18 | ] 19 | }, 20 | "require": { 21 | "php": ">=8.0", 22 | "symfony/framework-bundle": "^5.0|^6.0|^7.0" 23 | }, 24 | "suggest": { 25 | "ext-redis": "To use the Redis health check" 26 | }, 27 | "require-dev": { 28 | "symfony/phpunit-bridge": "^5.0|^6.0|^7.0", 29 | "doctrine/doctrine-bundle": "^2", 30 | "phpunit/phpunit": "^11", 31 | "friendsofphp/php-cs-fixer": "^3.0", 32 | "symfony/monolog-bundle": "^3.0|^4.0", 33 | "symfony/browser-kit": "^5.0|^6.0|^7.0", 34 | "symfony/yaml": "^5.0|^6.0|^7.0" 35 | }, 36 | "authors": [ 37 | { 38 | "name": "Fred Cox", 39 | "email": "mcfedr@gmail.com" 40 | } 41 | ], 42 | "config": { 43 | "allow-plugins": { 44 | "composer/package-versions-deprecated": true 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Ekreative/HealthCheckBundle/DependencyInjection/EkreativeHealthCheckExtension.php: -------------------------------------------------------------------------------- 1 | processConfiguration($configuration, $configs); 18 | 19 | $args = []; 20 | if ($config['doctrine_enabled']) { 21 | $args[] = new Reference('doctrine', ContainerInterface::NULL_ON_INVALID_REFERENCE); 22 | } else { 23 | $args[] = null; 24 | } 25 | 26 | $args[] = $config['doctrine']; 27 | $args[] = $config['optional_doctrine']; 28 | 29 | $args[] = array_map(function ($service) { 30 | return new Reference($service); 31 | }, $config['redis']); 32 | 33 | $args[] = array_map(function ($service) { 34 | return new Reference($service); 35 | }, $config['optional_redis']); 36 | 37 | $def = new Definition(HealthCheckController::class, $args); 38 | $def->addTag('controller.service_arguments'); 39 | 40 | $container->addDefinitions([HealthCheckController::class => $def]); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Health Check Bundle 2 | 3 | A bundle that provides a simple `/healthcheck` route 4 | 5 | [![Latest Stable Version](https://poser.pugx.org/ekreative/health-check-bundle/v/stable.png)](https://packagist.org/packages/ekreative/health-check-bundle) 6 | [![License](https://poser.pugx.org/ekreative/health-check-bundle/license.png)](https://packagist.org/packages/ekreative/health-check-bundle) 7 | [![Build Status](https://travis-ci.org/ekreative/health-check-bundle.svg?branch=master)](https://travis-ci.org/ekreative/health-check-bundle) 8 | 9 | ## Install 10 | 11 | ### Composer 12 | 13 | ```bash 14 | composer require ekreative/health-check-bundle 15 | ``` 16 | 17 | ### AppKernel 18 | 19 | Include the bundle in your AppKernel 20 | 21 | ```php 22 | public function registerBundles() 23 | { 24 | $bundles = [ 25 | ... 26 | new Ekreative\HealthCheckBundle\EkreativeHealthCheckBundle(), 27 | ``` 28 | 29 | ### Routing 30 | 31 | ```yaml 32 | ekreative_health_check: 33 | resource: '@EkreativeHealthCheckBundle/Resources/config/routes.xml' 34 | ``` 35 | 36 | ### Security 37 | 38 | You should make sure `/healthcheck` does not require authentication 39 | 40 | ```yaml 41 | security: 42 | firewalls: 43 | healthcheck: 44 | pattern: ^/healthcheck 45 | security: false 46 | ``` 47 | 48 | ## Configuration 49 | 50 | By default healthcheck will check that your default doctrine connection is working. 51 | 52 | ### Doctrine 53 | 54 | To check more than one doctrine connection you should add the configuration, listing 55 | the names of the connections 56 | 57 | ```yaml 58 | ekreative_health_check: 59 | doctrine: 60 | - 'default' 61 | - 'alternative' 62 | ``` 63 | 64 | You can also list doctrine connections that should be checked, but don't cause a failure 65 | 66 | ```yaml 67 | ekreative_health_check: 68 | optional_doctrine: 69 | - 'another.optional' 70 | ``` 71 | 72 | Its possible to disable the doctrine check 73 | 74 | ```yaml 75 | ekreative_health_check: 76 | doctrine_enabled: false 77 | ``` 78 | 79 | #### Timeout 80 | 81 | Its recommended to change the default PDO connection timeout so that your health 82 | check will fail faster 83 | 84 | Add this under connection setting 85 | 86 | ```yaml 87 | doctrine: 88 | dbal: 89 | connections: 90 | default: 91 | driver: pdo_mysql 92 | host: '%database_host%' 93 | options: 94 | !php/const PDO::ATTR_TIMEOUT: 5 95 | ``` 96 | 97 | ### Redis 98 | 99 | The bundle can also check that redis connections are working. You should add a list of 100 | service names to check 101 | 102 | This will require the PHP Redis extension enabled. 103 | 104 | ```yaml 105 | ekreative_health_check: 106 | redis: 107 | - 'redis' 108 | ``` 109 | 110 | You can also list redis connections that should be checked, but don't cause a failure 111 | 112 | ```yaml 113 | ekreative_health_check: 114 | optional_redis: 115 | - 'redis.optional' 116 | ``` 117 | 118 | #### Timeout 119 | 120 | Its recommended to change the default Redis connection timeout so that your health 121 | check will fail faster. Its the third argument to `connect` call for `\Redis`. 122 | 123 | ```yaml 124 | services: 125 | redis: 126 | class: Redis 127 | calls: 128 | - [ connect, ['%redis_host%', '%redis_port%', 5]] 129 | ``` 130 | 131 | #### Redis 132 | 133 | When you want redis to be optional, you might want to use the provided `RedisFactory` 134 | (or your own) that catches any exceptions on connect. Without this a Redis failure will 135 | cause the container to fail, resulting in a 500 error. 136 | 137 | ```yaml 138 | services: 139 | redis: 140 | class: Redis 141 | factory: Ekreative\HealthCheckBundle\DependencyInjection\RedisFactory::get 142 | arguments: 143 | $host: 'example.com' 144 | ``` 145 | -------------------------------------------------------------------------------- /src/Ekreative/HealthCheckBundle/Controller/HealthCheckController.php: -------------------------------------------------------------------------------- 1 | doctrine = $doctrine; 42 | $this->connections = $connections; 43 | $this->optionalConnections = $optionalConnections; 44 | $this->redis = $redis; 45 | $this->optionalRedis = $optionalRedis; 46 | } 47 | 48 | #[Route(path: '/healthcheck', name: 'health-check', methods: ['GET'])] 49 | public function healthCheckAction(): Response 50 | { 51 | $data = [ 52 | 'app' => true, 53 | ]; 54 | 55 | $required = [ 56 | 'app' => true, 57 | ]; 58 | 59 | if ($this->doctrine) { 60 | $i = 0; 61 | $key = 'database'; 62 | if ((count($this->connections) + count($this->optionalConnections)) > 1) { 63 | $key .= "{$i}"; 64 | } 65 | 66 | foreach ($this->connections as $name) { 67 | $data[$key] = $required[$key] = $this->checkDoctrineConnection($this->doctrine->getConnection($name)); 68 | ++$i; 69 | $key = "database{$i}"; 70 | } 71 | 72 | foreach ($this->optionalConnections as $name) { 73 | $data[$key] = $this->checkDoctrineConnection($this->doctrine->getConnection($name)); 74 | ++$i; 75 | $key = "database{$i}"; 76 | } 77 | 78 | if (!count($this->connections) && !count($this->optionalConnections)) { 79 | $data[$key] = $required[$key] = $this->checkDoctrineConnection($this->doctrine->getConnection()); 80 | } 81 | } 82 | 83 | set_error_handler(function ($errno, $errstr, $errfile, $errline) { 84 | // error was suppressed with the @-operator 85 | if (0 === error_reporting()) { 86 | return false; 87 | } 88 | 89 | throw new \ErrorException($errstr, 0, $errno, $errfile, $errline); 90 | }); 91 | 92 | $i = 0; 93 | $key = 'redis'; 94 | if ((count($this->redis) + count($this->optionalRedis)) > 1) { 95 | $key .= "{$i}"; 96 | } 97 | foreach ($this->redis as $redis) { 98 | $data[$key] = $required[$key] = $this->checkRedisConnection($redis); 99 | ++$i; 100 | $key = "redis{$i}"; 101 | } 102 | foreach ($this->optionalRedis as $redis) { 103 | $data[$key] = $this->checkRedisConnection($redis); 104 | ++$i; 105 | $key = "redis{$i}"; 106 | } 107 | 108 | restore_error_handler(); 109 | 110 | $ok = array_reduce($required, function ($m, $v) { 111 | return $m && $v; 112 | }, true); 113 | 114 | return new JsonResponse($data, $ok ? 200 : 503); 115 | } 116 | 117 | private function checkDoctrineConnection(Connection $connection): bool 118 | { 119 | try { 120 | return (bool) $connection->executeQuery('SELECT 1'); 121 | } catch (\Exception $e) { 122 | return false; 123 | } 124 | } 125 | 126 | private function checkRedisConnection(\Redis|\RedisArray|RedisProxy|\Predis\ClientInterface $redis): bool 127 | { 128 | try { 129 | $redis->ping(); 130 | 131 | return true; 132 | } catch (\Exception $e) { 133 | return false; 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /tests/Ekreative/HealthCheckBundle/Controller/HealthCheckControllerTest.php: -------------------------------------------------------------------------------- 1 | 'test_travis']); 20 | } else { 21 | // This env uses a sqlite connection 22 | $client = static::createClient(); 23 | } 24 | 25 | $client->request('GET', '/healthcheck'); 26 | 27 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); 28 | $this->assertEquals('application/json', $client->getResponse()->headers->get('content-type')); 29 | 30 | $data = json_decode($client->getResponse()->getContent(), true); 31 | 32 | $this->assertIsArray($data); 33 | $this->assertCount(2, $data); 34 | 35 | $this->assertIsBool($data['app']); 36 | $this->assertTrue($data['app']); 37 | 38 | $this->assertIsBool($data['database']); 39 | $this->assertTrue($data['database']); 40 | } 41 | 42 | #[Group('redis')] 43 | public function testActionWithRedis() 44 | { 45 | if (isset($_ENV['travis'])) { 46 | // This env connects to real redis and mysql servers 47 | $client = static::createClient(['environment' => 'test_travis']); 48 | } else { 49 | // This env uses a sqlite connection and fakes the redis server 50 | $client = static::createClient(['environment' => 'test_with_redis']); 51 | 52 | $redis = $this->getMockBuilder(\Redis::class) 53 | ->onlyMethods(['ping']) 54 | ->getMock(); 55 | $redis->method('ping')->willReturn(true); 56 | 57 | $client->getKernel()->getContainer()->set('redis', $redis); 58 | } 59 | 60 | $client->request('GET', '/healthcheck'); 61 | 62 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); 63 | $this->assertEquals('application/json', $client->getResponse()->headers->get('content-type')); 64 | 65 | $data = json_decode($client->getResponse()->getContent(), true); 66 | 67 | $this->assertIsArray($data); 68 | $this->assertCount(3, $data); 69 | 70 | $this->assertIsBool($data['app']); 71 | $this->assertTrue($data['app']); 72 | 73 | $this->assertIsBool($data['database']); 74 | $this->assertTrue($data['database']); 75 | 76 | $this->assertIsBool($data['redis']); 77 | $this->assertTrue($data['redis']); 78 | } 79 | 80 | public function testMySQLFailAction() 81 | { 82 | $client = static::createClient(['environment' => 'test_with_mysql']); 83 | 84 | $client->request('GET', '/healthcheck'); 85 | 86 | $this->assertEquals(503, $client->getResponse()->getStatusCode()); 87 | $this->assertEquals('application/json', $client->getResponse()->headers->get('content-type')); 88 | 89 | $data = json_decode($client->getResponse()->getContent(), true); 90 | 91 | $this->assertIsArray($data); 92 | $this->assertCount(2, $data); 93 | 94 | $this->assertIsBool($data['database']); 95 | $this->assertFalse($data['database']); 96 | } 97 | 98 | public function testOptionalMySQLFailAction() 99 | { 100 | $client = static::createClient(['environment' => 'test_with_mysql_optional']); 101 | 102 | $client->request('GET', '/healthcheck'); 103 | 104 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); 105 | $this->assertEquals('application/json', $client->getResponse()->headers->get('content-type')); 106 | 107 | $data = json_decode($client->getResponse()->getContent(), true); 108 | 109 | $this->assertIsArray($data); 110 | $this->assertCount(2, $data); 111 | 112 | $this->assertIsBool($data['database']); 113 | $this->assertFalse($data['database']); 114 | } 115 | 116 | #[Group('redis')] 117 | public function testRedisFailAction() 118 | { 119 | $client = static::createClient(['environment' => 'test_with_redis']); 120 | 121 | $client->request('GET', '/healthcheck'); 122 | 123 | $this->assertEquals(503, $client->getResponse()->getStatusCode()); 124 | $this->assertEquals('application/json', $client->getResponse()->headers->get('content-type')); 125 | 126 | $data = json_decode($client->getResponse()->getContent(), true); 127 | 128 | $this->assertIsArray($data); 129 | $this->assertCount(3, $data); 130 | 131 | $this->assertIsBool($data['redis']); 132 | $this->assertFalse($data['redis']); 133 | } 134 | 135 | #[Group('redis')] 136 | public function testOptionalRedisFailAction() 137 | { 138 | $client = static::createClient(['environment' => 'test_with_redis_optional']); 139 | 140 | $client->request('GET', '/healthcheck'); 141 | 142 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); 143 | $this->assertEquals('application/json', $client->getResponse()->headers->get('content-type')); 144 | 145 | $data = json_decode($client->getResponse()->getContent(), true); 146 | 147 | $this->assertIsArray($data); 148 | $this->assertCount(3, $data); 149 | 150 | $this->assertIsBool($data['redis']); 151 | $this->assertFalse($data['redis']); 152 | } 153 | 154 | public function testLazyAction() 155 | { 156 | $client = static::createClient(['environment' => 'test_lazy']); 157 | $client->request('GET', '/'); 158 | $this->assertEquals(404, $client->getResponse()->getStatusCode()); 159 | } 160 | 161 | public static function getLazyRedis() 162 | { 163 | throw new \Exception('Should not be called'); 164 | } 165 | 166 | #[Group('not-5.4', 'redis')] 167 | public function testAnnoRoutes() 168 | { 169 | // This env uses a sqlite connection and fakes the redis server 170 | $client = static::createClient(['environment' => 'test_anno']); 171 | 172 | $redis = $this->getMockBuilder(\Redis::class) 173 | ->onlyMethods(['ping']) 174 | ->getMock(); 175 | $redis->method('ping')->willReturn(true); 176 | 177 | $client->getKernel()->getContainer()->set('redis', $redis); 178 | 179 | $client->request('GET', '/healthcheck'); 180 | 181 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); 182 | $this->assertEquals('application/json', $client->getResponse()->headers->get('content-type')); 183 | } 184 | } 185 | --------------------------------------------------------------------------------