diff --git a/.install/symfony/config/routes/open_conext_monitor.yaml b/.install/symfony/config/routes/open_conext_monitor.yaml
index ca03974..856e844 100644
--- a/.install/symfony/config/routes/open_conext_monitor.yaml
+++ b/.install/symfony/config/routes/open_conext_monitor.yaml
@@ -1,5 +1,4 @@
open_conext_monitor:
- resource: "@OpenConextMonitorBundle/Resources/config/routing.yml"
- prefix: /
-
-
+ resource: "@OpenConextMonitorBundle/src/Controller"
+ type: attribute
+ prefix: /
diff --git a/README.md b/README.md
index a266d65..01e3c63 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ A Symfony 5/6/7 bundle that adds an /internal/health and /internal/info endpoint
The endpoints return JSON responses. The `/internal/info` endpoint tries to give as much information about the currently installed
version of the application as possible. This information is based on the build path of the installation. But also
-includes the Symfony environment that is currently active and whether or not the debugger is enabled.
+includes the Symfony environment that is currently active and whether the debugger is enabled.
The `/internal/health` endpoint reports on the health of the application. This information could be used for example by a load
balancer. Example output:
@@ -46,9 +46,11 @@ When a health check failed the HTTP Response status code will be 503. And the JS
* Include the routing configuration in `config/routes.yaml` by adding:
```yaml
open_conext_monitor:
- resource: "@OpenConextMonitorBundle/Resources/config/routing.yml"
- prefix: /
+ resource: "@OpenConextMonitorBundle/src/Controller"
+ type: attribute
+ prefix: /
```
+_Note: this is currently done by the bundle itself, with an external dependency called https://github.com/endroid/installer_
* Add security exceptions in `config/packages/security.yaml` (if this is required at all)
```yaml
@@ -78,24 +80,18 @@ use OpenConext\MonitorBundle\Value\HealthReport;
class ApiHealthCheck implements HealthCheckInterface
{
- /**
- * @var MyService
- */
- private $testService;
-
- public function __construct(MyService $service)
+ public function __construct(private readonly MyService $service)
{
- $this->testService = $service;
}
public function check(HealthReportInterface $report): HealthReportInterface
{
- if (!$this->testService->everythingOk()) {
+ if (!$this->service->everythingOk()) {
// Return a HealthReport with a DOWN status when there are indications the application is not functioning as
// intended. You can provide an optional message that is displayed alongside the DOWN status.
return HealthReport::buildStatusDown('Not everything is allright.');
}
- // By default return the report that was passed along as a parameter to the check method
+ // By default, return the report that was passed along as a parameter to the check method
return $report;
}
}
@@ -104,35 +100,20 @@ class ApiHealthCheck implements HealthCheckInterface
registered health checkers. If everything was OK, just return the report that was passed to the method.
### Register the checker
-To actually include the home made checker simply tag it with 'surfnet.monitor.health_check'
-
-Example service definition in `services.yml`
-
-```yaml
-services:
- acme.monitor.my_custom_health_check:
- class: Acme\AppBundle\HealthCheck\MyCustomHealthCheck
- arguments:
- - @test_service
- tags:
- - { name: surfnet.monitor.health_check }
-```
+By implementing the `HealthCheckInterface` you can register your own health check.
+This interface is tagged automatically, so you don't have to do it yourself.
## Overriding a default HealthCheck
To run a custom query with the DoctrineConnectionHealthCheck you will need to override it in your own project.
-For example in your ACME bunde that is using the monitor bundle:
+For example in your ACME bundle that is using the monitor bundle:
-`services.yml`
+`services.yaml`
```yaml
- # Override the service, service names can be found in `/src/Resources/config/services.yml`
- openconext.monitor.database_health_check:
+ # Override the service in `/src/config/services.yaml`
+ OpenConext\MonitorBundle\HealthCheck\DoctrineConnectionHealthCheck:
# Point to your own implementation of the check
class: Acme\GreatSuccessBundle\HealthCheck\DoctrineConnectionHealthCheck
- # Do not forget to apply the correct tag
- tags:
- - { name: openconext.monitor.health_check }
-
```
The rest of the service configuration is up to your own needs. You can inject arguments, factory calls and other service features as need be.
diff --git a/composer.json b/composer.json
index 2e70275..04a3f59 100644
--- a/composer.json
+++ b/composer.json
@@ -1,25 +1,21 @@
{
"name": "openconext/monitor-bundle",
"type": "symfony-bundle",
- "description": "A Symfony 5/6/7 bundle that facilitates health and info endpoints to a Symfony application.",
+ "description": "A Symfony 6/7 bundle that facilitates health and info endpoints to a Symfony application.",
"keywords": ["SURFnet", "StepUp", "OpenConext", "monitoring", "health"],
"license": "Apache-2.0",
"minimum-stability": "stable",
"require": {
"php": ">=8.2, <9.0-dev",
- "doctrine/orm": "^2.9",
- "doctrine/dbal": "^3.1",
+ "doctrine/dbal": "^3.1|^4.0",
"endroid/installer": "^1.4",
- "symfony/dependency-injection": "^5.4|^6.3|^7.0",
- "symfony/framework-bundle": "^5.4|^6.3|^7.0",
+ "symfony/dependency-injection": "^6.3|^7.0",
+ "symfony/framework-bundle": "^6.3|^7.0",
"webmozart/assert": "^1.10"
},
"require-dev": {
"php-parallel-lint/php-parallel-lint": "^1.3",
"phpmd/phpmd": "^2.13",
- "matthiasnoback/symfony-config-test": "^4.3",
- "phpdocumentor/reflection-docblock": "^5.2",
- "phpunit/php-token-stream": "^3.1.3|^4.0.4",
"phpunit/phpunit": "^9.6|^10.4",
"sebastian/phpcpd": "^4.1|^5.0|^6.0",
"squizlabs/php_codesniffer": "^3.6",
@@ -28,21 +24,21 @@
},
"autoload": {
"psr-4": {
- "OpenConext\\MonitorBundle\\": "src"
+ "OpenConext\\MonitorBundle\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
- "App\\Tests\\": "src/Tests/"
+ "App\\Tests\\": "tests/"
}
},
"scripts": {
"tests": {
- "docheader": "vendor/bin/docheader check src/",
- "phpcs": "vendor/bin/phpcs src --report=full --standard=phpcs.xml --extensions=php --warning-severity=0",
- "phpcpd": "vendor/bin/phpcpd src --exclude=src/Tests/*",
+ "docheader": "vendor/bin/docheader check src/ tests/",
+ "phpcs": "vendor/bin/phpcs src tests --report=full --standard=phpcs.xml --extensions=php --warning-severity=0",
+ "phpcpd": "vendor/bin/phpcpd src",
"phpunit": "vendor/bin/phpunit --coverage-text",
- "phpmd": "vendor/bin/phpmd src text phpmd.xml --exclude 'src/Tests/*'"
+ "phpmd": "vendor/bin/phpmd src text phpmd.xml"
},
"post-update-cmd": [
"@tests"
diff --git a/config/services.yaml b/config/services.yaml
new file mode 100644
index 0000000..7317294
--- /dev/null
+++ b/config/services.yaml
@@ -0,0 +1,8 @@
+services:
+ _defaults:
+ autowire: true # Automatically injects dependencies in your services.
+ autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
+
+ OpenConext\MonitorBundle\:
+ resource: '../src/'
+ exclude: '../src/{Entity,Repository,Tests,Resources,DependencyInjection,OpenConextMonitorBundle.php}'
diff --git a/phpunit.xml b/phpunit.xml
index ff1f970..18d25eb 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -13,32 +13,29 @@
>
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
- src
-
-
- .github
- src/Tests
-
-
-
-
- src/Tests
-
-
-
-
-
+
+
+ src
+
+
+ .github
+ tests
+
+
+
+
+ tests
+
+
+
+
+
diff --git a/src/Controller/HealthController.php b/src/Controller/HealthController.php
index e37f091..59f583a 100644
--- a/src/Controller/HealthController.php
+++ b/src/Controller/HealthController.php
@@ -21,6 +21,7 @@
use OpenConext\MonitorBundle\HealthCheck\HealthCheckChain;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\Routing\Attribute\Route;
/**
* Display the health state of the application.
@@ -35,6 +36,8 @@ public function __construct(
) {
}
+ #[Route('/health', name: 'monitor.health', methods: ['GET'])]
+ #[Route('/internal/health', name: 'monitor.internal_health', methods: ['GET'])]
public function __invoke(): JsonResponse
{
$statusResponse = $this->healthChecker->check();
diff --git a/src/Controller/InfoController.php b/src/Controller/InfoController.php
index c968f40..243ddf9 100644
--- a/src/Controller/InfoController.php
+++ b/src/Controller/InfoController.php
@@ -19,11 +19,11 @@
namespace OpenConext\MonitorBundle\Controller;
use OpenConext\MonitorBundle\Value\BuildInformationFactory;
-use OpenConext\MonitorBundle\Value\BuildPathFactory;
use OpenConext\MonitorBundle\Value\Information;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\JsonResponse;
-use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Routing\Attribute\Route;
/**
* Display specific information about the application.
@@ -43,21 +43,31 @@
class InfoController extends AbstractController
{
private array $systemInfo = [];
+ private readonly string $buildPath;
public function __construct(
- private readonly string $buildPath,
+ #[Autowire(param: 'kernel.project_dir')]
+ private readonly string $projectDir,
+ #[Autowire(param: 'kernel.environment')]
private readonly string $environment,
+ #[Autowire(param: 'kernel.debug')]
private readonly bool $debuggerEnabled,
+ #[Autowire(env: 'default::string:OPENCONEXT_APP_VERSION')]
private readonly ?string $version,
+ #[Autowire(env: 'default::string:OPENCONEXT_GIT_SHA')]
private readonly ?string $revision,
+ #[Autowire(env: 'default::string:OPENCONEXT_COMMIT_DATE')]
private readonly ?string $commitDate,
) {
+ $this->buildPath = basename(realpath($this->projectDir));
if (function_exists('opcache_get_status')) {
$this->systemInfo['opcache'] = opcache_get_status(false);
}
}
+ #[Route('/info', name: 'monitor.info', methods: ['GET'])]
+ #[Route('/internal/info', name: 'monitor.internal_info', methods: ['GET'])]
public function __invoke(): JsonResponse
{
$buildInformation = BuildInformationFactory::build(
diff --git a/src/DependencyInjection/Compiler/HealthCheckPass.php b/src/DependencyInjection/Compiler/HealthCheckPass.php
deleted file mode 100644
index 680fe6c..0000000
--- a/src/DependencyInjection/Compiler/HealthCheckPass.php
+++ /dev/null
@@ -1,48 +0,0 @@
-has('openconext.monitor.health_check_chain')) {
- return;
- }
-
- $definition = $container->findDefinition('openconext.monitor.health_check_chain');
-
- // find all service IDs with the openconext.monitor.health_check tag
- $taggedServices = $container->findTaggedServiceIds('openconext.monitor.health_check');
-
- foreach ($taggedServices as $id => $tags) {
- $definition->addMethodCall('addHealthCheck', array(new Reference($id)));
- }
- }
-}
diff --git a/src/DependencyInjection/OpenConextMonitorExtension.php b/src/DependencyInjection/OpenConextMonitorExtension.php
deleted file mode 100644
index ab1e0c4..0000000
--- a/src/DependencyInjection/OpenConextMonitorExtension.php
+++ /dev/null
@@ -1,56 +0,0 @@
-load('services.yml');
-
- $this->setInfoControllerArguments($container);
- }
-
- private function setInfoControllerArguments(ContainerBuilder $container)
- {
- // The buildPath is the installation directory of the project. And is derived from the kernel.project_dir
- // (which is the app folder).
- $rootDir = $container->getParameter('kernel.project_dir');
- $buildPath = basename(realpath($rootDir));
-
- $container
- ->getDefinition('openconext.monitor.controller.info')
- ->replaceArgument(0, $buildPath);
- }
-}
diff --git a/src/HealthCheck/DoctrineConnectionHealthCheck.php b/src/HealthCheck/DoctrineConnectionHealthCheck.php
index 3c204d6..af7a97a 100644
--- a/src/HealthCheck/DoctrineConnectionHealthCheck.php
+++ b/src/HealthCheck/DoctrineConnectionHealthCheck.php
@@ -18,9 +18,10 @@
namespace OpenConext\MonitorBundle\HealthCheck;
-use Doctrine\ORM\EntityManager;
+use Doctrine\DBAL\Connection;
use Exception;
use OpenConext\MonitorBundle\Value\HealthReport;
+use Symfony\Component\DependencyInjection\Attribute\Autowire;
/**
* Test if there is a working database connection.
@@ -30,34 +31,27 @@
*/
class DoctrineConnectionHealthCheck implements HealthCheckInterface
{
- /**
- * @var EntityManager|null
- */
- private $entityManager;
- /**
- * @param EntityManager $entityManager
- */
- public function setEntityManager(EntityManager $entityManager)
- {
- $this->entityManager = $entityManager;
+ public function __construct(
+ #[Autowire(service: 'doctrine.dbal.default_connection')]
+ private readonly ?Connection $connection
+ ) {
}
public function check(HealthReportInterface $report): HealthReportInterface
{
- // Was the entityManager injected? When it is not the project does not use Doctrine ORM
- if (!is_null($this->entityManager)) {
+ if (!is_null($this->connection)) {
try {
// Get the schema manager and grab the first table to later query on
- $sm = $this->entityManager->getConnection()->createSchemaManager();
+ $sm = $this->connection->createSchemaManager();
$tables = $sm->listTables();
if (!empty($tables)) {
$table = reset($tables);
// Perform a light-weight query on the chosen table
- $query = 'SELECT * FROM `%s` LIMIT 1';
- $this->entityManager->getConnection()->executeQuery(sprintf($query, $table->getName()));
+ $query = "SELECT * FROM %s LIMIT 1";
+ $this->connection->executeQuery(sprintf($query, $table->getName()));
}
- } catch (Exception $e) {
+ } catch (Exception) {
return HealthReport::buildStatusDown('Unable to execute a query on the database.');
}
}
diff --git a/src/HealthCheck/HealthCheckChain.php b/src/HealthCheck/HealthCheckChain.php
index acfa308..16e2940 100644
--- a/src/HealthCheck/HealthCheckChain.php
+++ b/src/HealthCheck/HealthCheckChain.php
@@ -19,25 +19,17 @@
namespace OpenConext\MonitorBundle\HealthCheck;
use OpenConext\MonitorBundle\Value\HealthReport;
+use Symfony\Component\DependencyInjection\Attribute\AutowireIterator;
/**
* Collect HealthCheck instances and checks them for UP or DOWN status.
*/
class HealthCheckChain
{
- /**
- * @var HealthCheckInterface[]
- */
- private $checks;
-
- public function __construct()
- {
- $this->checks = [];
- }
-
- public function addHealthCheck(HealthCheckInterface $healthCheck)
- {
- $this->checks[] = $healthCheck;
+ public function __construct(
+ #[AutowireIterator(tag: 'surfnet.monitor.health_check')]
+ private readonly iterable $healthChecks
+ ) {
}
/**
@@ -46,8 +38,8 @@ public function addHealthCheck(HealthCheckInterface $healthCheck)
public function check(): HealthReportInterface
{
$report = HealthReport::buildStatusUp();
- if (!empty($this->checks)) {
- foreach ($this->checks as $check) {
+ if (!empty($this->healthChecks)) {
+ foreach ($this->healthChecks as $check) {
$report = $check->check($report);
if ($report->isDown()) {
return $report;
diff --git a/src/HealthCheck/HealthCheckInterface.php b/src/HealthCheck/HealthCheckInterface.php
index bf49b49..aa002a5 100644
--- a/src/HealthCheck/HealthCheckInterface.php
+++ b/src/HealthCheck/HealthCheckInterface.php
@@ -18,9 +18,12 @@
namespace OpenConext\MonitorBundle\HealthCheck;
+use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
+
/**
* Contract for a HealthCheck.
*/
+#[AutoconfigureTag('surfnet.monitor.health_check')]
interface HealthCheckInterface
{
public function check(HealthReportInterface $report): HealthReportInterface;
diff --git a/src/OpenConextMonitorBundle.php b/src/OpenConextMonitorBundle.php
index 21f7513..04238d7 100644
--- a/src/OpenConextMonitorBundle.php
+++ b/src/OpenConextMonitorBundle.php
@@ -18,14 +18,14 @@
namespace OpenConext\MonitorBundle;
-use OpenConext\MonitorBundle\DependencyInjection\Compiler\HealthCheckPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\HttpKernel\Bundle\Bundle;
+use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
+use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
-class OpenConextMonitorBundle extends Bundle
+class OpenConextMonitorBundle extends AbstractBundle
{
- public function build(ContainerBuilder $container): void
+ public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
{
- $container->addCompilerPass(new HealthCheckPass());
+ $container->import('../config/services.yaml');
}
}
diff --git a/src/Resources/config/routing.yml b/src/Resources/config/routing.yml
deleted file mode 100644
index fcfda87..0000000
--- a/src/Resources/config/routing.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-# Deprecated routes: /info and /health are moving to /internal/info and internal/health respectively.
-monitor.info:
- path: /info
- methods: [GET]
- defaults:
- _controller: openconext.monitor.controller.info
-
-monitor.health:
- path: /health
- methods: [GET]
- defaults:
- _controller: openconext.monitor.controller.health
-
-monitor.internal_info:
- path: /internal/info
- methods: [GET]
- defaults:
- _controller: openconext.monitor.controller.info
-
-monitor.internal_health:
- path: /internal/health
- methods: [GET]
- defaults:
- _controller: openconext.monitor.controller.health
diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml
deleted file mode 100644
index bb09adf..0000000
--- a/src/Resources/config/services.yml
+++ /dev/null
@@ -1,35 +0,0 @@
-services:
- openconext.monitor.controller.info:
- class: OpenConext\MonitorBundle\Controller\InfoController
- arguments:
- - '~'
- - '%kernel.environment%'
- - '%kernel.debug%'
- - '%env(default::string:OPENCONEXT_APP_VERSION)%'
- - '%env(default::string:OPENCONEXT_GIT_SHA)%'
- - '%env(default::string:OPENCONEXT_COMMIT_DATE)%'
-
- autowire: true
- tags: ['controller.service_arguments', 'container.service_subscriber']
-
- openconext.monitor.controller.health:
- class: OpenConext\MonitorBundle\Controller\HealthController
- arguments:
- - '@openconext.monitor.health_check_chain'
- autowire: true
- tags: ['controller.service_arguments', 'container.service_subscriber']
-
- openconext.monitor.health_check_chain:
- class: OpenConext\MonitorBundle\HealthCheck\HealthCheckChain
-
- openconext.monitor.session_health_check:
- class: OpenConext\MonitorBundle\HealthCheck\SessionHealthCheck
- tags:
- - { name: openconext.monitor.health_check }
-
- openconext.monitor.database_health_check:
- class: OpenConext\MonitorBundle\HealthCheck\DoctrineConnectionHealthCheck
- calls:
- - [ setEntityManager, ['@?doctrine.orm.entity_manager']]
- tags:
- - { name: openconext.monitor.health_check }
diff --git a/src/Tests/HealthCheck/SessionHealthCheckChainTest.php b/tests/HealthCheck/SessionHealthCheckChainTest.php
similarity index 88%
rename from src/Tests/HealthCheck/SessionHealthCheckChainTest.php
rename to tests/HealthCheck/SessionHealthCheckChainTest.php
index 9e90502..7d72620 100644
--- a/src/Tests/HealthCheck/SessionHealthCheckChainTest.php
+++ b/tests/HealthCheck/SessionHealthCheckChainTest.php
@@ -18,6 +18,7 @@
namespace OpenConext\MonitorBundle\Tests\HealthCheck;
+use ArrayIterator;
use Mockery as m;
use OpenConext\MonitorBundle\HealthCheck\HealthCheckChain;
use OpenConext\MonitorBundle\HealthCheck\HealthCheckInterface;
@@ -57,10 +58,9 @@ public function testChain()
->once()
->andReturn($statusOk);
- $chain = new HealthCheckChain();
- $chain->addHealthCheck($checker1);
- $chain->addHealthCheck($checker2);
- $chain->addHealthCheck($checker3);
+ $iterator = new ArrayIterator([$checker1, $checker2, $checker3]);
+
+ $chain = new HealthCheckChain($iterator);
$result = $chain->check();
$this->assertEquals($statusOk, $result);
@@ -74,7 +74,7 @@ public function testChainStopsWhenCheckFails()
->andReturn(false);
$statusDown = m::mock(HealthReport::buildStatusDown('Lorem ipsum dolor sit'));
- $statusOk
+ $statusDown
->shouldReceive('isDown')
->andReturn(true);
@@ -93,10 +93,9 @@ public function testChainStopsWhenCheckFails()
$checker3 = m::mock(HealthCheckInterface::class);
$checker3->shouldNotReceive('check');
- $chain = new HealthCheckChain();
- $chain->addHealthCheck($checker1);
- $chain->addHealthCheck($checker2);
- $chain->addHealthCheck($checker3);
+ $iterator = new ArrayIterator([$checker1, $checker2, $checker3]);
+
+ $chain = new HealthCheckChain($iterator);
$result = $chain->check();
$this->assertEquals($statusDown, $result);
diff --git a/src/Tests/Value/BuildInformationFactoryTest.php b/tests/Value/BuildInformationFactoryTest.php
similarity index 100%
rename from src/Tests/Value/BuildInformationFactoryTest.php
rename to tests/Value/BuildInformationFactoryTest.php
diff --git a/src/Tests/Value/BuildPathFactoryTest.php b/tests/Value/BuildPathFactoryTest.php
similarity index 100%
rename from src/Tests/Value/BuildPathFactoryTest.php
rename to tests/Value/BuildPathFactoryTest.php
diff --git a/src/Tests/Value/EnvVarFactoryTest.php b/tests/Value/EnvVarFactoryTest.php
similarity index 100%
rename from src/Tests/Value/EnvVarFactoryTest.php
rename to tests/Value/EnvVarFactoryTest.php