Skip to content

Commit

Permalink
feat(status-code): configure api and command status codes for warning…
Browse files Browse the repository at this point in the history
… and critical

feat(status-code): configure api and command status codes for warning and critical

#1
  • Loading branch information
tuxes3 authored Jul 11, 2023
1 parent 034110f commit b59fcd0
Show file tree
Hide file tree
Showing 47 changed files with 478 additions and 103 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
matrix:
operating-system: [ ubuntu-latest ]
php: [ '8.1', '8.2' ]
symfony: ['5.3.*', '5.4.*', '6.0.*', '6.1.*', '6.2.*']
symfony: ['6.3.*']

steps:
- uses: actions/checkout@master
Expand Down
14 changes: 7 additions & 7 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,23 @@
],
"require": {
"php": ">=8.1",
"symfony/framework-bundle": "^5.3|^6.0",
"symfony/serializer": "^5.3|^6.0"
"symfony/framework-bundle": "^6.3",
"symfony/serializer": "^6.3"
},
"require-dev": {
"nyholm/symfony-bundle-test": "^2.0",
"zenstruck/console-test": "^v1.1.0",
"whatwedo/php-coding-standard": "^1.0",
"phpstan/phpstan": "^1.7",
"phpunit/phpunit": "^9.5",
"symfony/browser-kit": "^5.3|^6.0",
"symfony/css-selector": "^5.3|^6.0",
"symfony/phpunit-bridge": "^6.2",
"symfony/browser-kit": "^6.3",
"symfony/css-selector": "^6.3",
"symfony/phpunit-bridge": "^6.3",
"doctrine/doctrine-bundle": "^2.7",
"doctrine/doctrine-migrations-bundle": "^3.2",
"doctrine/orm": "^2.14",
"symfony/twig-bundle": "^5.3|^6.0",
"symfony/messenger": "^5.3|^6.0",
"symfony/twig-bundle": "^6.3",
"symfony/messenger": "^6.3",
"symfony/mercure-bundle": "^0.3"
},
"autoload": {
Expand Down
4 changes: 2 additions & 2 deletions docs/attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ declare(strict_types=1);
namespace App\Monitoring\Sensor\ThirdParty;

use App\Api\ServerApi;
use whatwedo\MonitorBundle\Enum\SensorStateEnum;
use whatwedo\MonitorBundle\Enums\SensorStateEnum;
use whatwedo\MonitorBundle\Monitoring\Sensor\AbstractSensor;

class MyApi extends AbstractSensor
Expand Down Expand Up @@ -82,7 +82,7 @@ declare(strict_types=1);
namespace App\Monitoring\Sensor\Service;

use App\Repository\EmailRepository;
use whatwedo\MonitorBundle\Enum\SensorStateEnum;
use whatwedo\MonitorBundle\Enums\SensorStateEnum;
use whatwedo\MonitorBundle\Monitoring\Metric\AbstractMetric;

class EmailQueue extends AbstractMetric
Expand Down
2 changes: 1 addition & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ parameters:
- src
- tests
bootstrapFiles:
- vendor/bin/.phpunit/phpunit-9.5-0/vendor/autoload.php
- vendor/bin/.phpunit/phpunit-9.6-0/vendor/autoload.php
6 changes: 5 additions & 1 deletion src/Command/CheckCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use whatwedo\MonitorBundle\Manager\MonitoringManager;
use whatwedo\MonitorBundle\Monitoring\Metric\AbstractMetric;
use whatwedo\MonitorBundle\Monitoring\Sensor\AbstractSensor;
use whatwedo\MonitorBundle\Util\StatusCodeDecider;

#[AsCommand(
name: 'whatwedo:monitor:check',
Expand All @@ -20,6 +21,8 @@
class CheckCommand extends Command
{
public function __construct(
protected int $warningExitCode,
protected int $criticalExitCode,
protected MonitoringManager $monitoringManager
) {
parent::__construct();
Expand All @@ -30,7 +33,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$io = new SymfonyStyle($input, $output);
$this->printResult($io, $this->monitoringManager->getResult());

return $this->monitoringManager->isSuccessful() ? static::SUCCESS : static::FAILURE;
$decider = new StatusCodeDecider($this->monitoringManager, self::SUCCESS, $this->warningExitCode, $this->criticalExitCode);
return $decider->decide();
}

private function printResult(SymfonyStyle $io, $result, $previousGroup = null, $level = 0): void
Expand Down
10 changes: 7 additions & 3 deletions src/Controller/ApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,30 @@
use Symfony\Component\Serializer\Encoder\JsonEncode;
use Symfony\Component\Serializer\SerializerInterface;
use whatwedo\MonitorBundle\Manager\MonitoringManager;
use whatwedo\MonitorBundle\Util\StatusCodeDecider;

class ApiController extends AbstractController
{
public function __construct(
protected int $warningExitCode,
protected int $criticalExitCode,
protected ?string $authToken = null
) {
}

public function __invoke(Request $request, MonitoringManager $monitoringManager, SerializerInterface $serializer): Response
{
if ($this->authToken !== null &&
$request->headers->get('X-Auth-Token', '') !== $this->authToken) {
if ($this->authToken !== null
&& $request->headers->get('X-Auth-Token', '') !== $this->authToken) {
return new Response('Unauthorized', 401);
}

$decider = new StatusCodeDecider($monitoringManager, Response::HTTP_OK, $this->warningExitCode, $this->criticalExitCode);
return new Response(
$serializer->serialize($monitoringManager->getResult(), $request->attributes->get('_format'), [
JsonEncode::OPTIONS => $request->query->has('pretty') ? JSON_PRETTY_PRINT : 0,
]),
$monitoringManager->isSuccessful() ? Response::HTTP_OK : Response::HTTP_SERVICE_UNAVAILABLE,
$decider->decide(),
[
'Content-type' => match ($request->attributes->get('_format')) {
'json' => 'application/json',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

class AttributeCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
public function process(ContainerBuilder $container): void
{
$definition = $container->getDefinition(MonitoringManager::class);

Expand Down
24 changes: 24 additions & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ public function getConfigTreeBuilder(): TreeBuilder
->defaultNull()
->info('Auth token for the api endpoint, must be sent with the X-Auth-Token HTTP header (default null / disabled)')
->end()
->arrayNode('http_status_code')
->children()
->integerNode('warning')
->defaultValue(503)
->info('HTTP status code for warning state (default 503)')
->end()
->integerNode('critical')
->defaultValue(503)
->info('HTTP status code for critical state (default 503)')
->end()
->end()
->end()
->end()
->end()
->arrayNode('controller')
Expand All @@ -48,6 +60,18 @@ public function getConfigTreeBuilder(): TreeBuilder
->defaultTrue()
->info('Enable the command endpoint (default true)')
->end()
->arrayNode('exit_code')
->children()
->integerNode('warning')
->defaultValue(1)
->info('Exit code for warning state (default 1)')
->end()
->integerNode('critical')
->defaultValue(1)
->info('Exit code for critical state (default 1)')
->end()
->end()
->end()
->end()
->end()
->end()
Expand Down
19 changes: 14 additions & 5 deletions src/DependencyInjection/whatwedoMonitorExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace whatwedo\MonitorBundle\DependencyInjection;

use PHPUnit\Framework\MockObject\Api;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
Expand All @@ -23,12 +22,12 @@
*/
class whatwedoMonitorExtension extends Extension implements PrependExtensionInterface
{
public function load(array $configs, ContainerBuilder $container)
public function load(array $configs, ContainerBuilder $container): void
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);

$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yaml');

// tag monitoring attributes (sensors and metrics)
Expand All @@ -43,7 +42,10 @@ public function configureEndpoints(ContainerBuilder $container, array $config)
{
// register API
$container->findDefinition(ApiController::class)
->addArgument($config['endpoint']['api']['auth_token'] ?? null);
->addArgument($config['endpoint']['api']['http_status_code']['warning'] ?? 503)
->addArgument($config['endpoint']['api']['http_status_code']['critical'] ?? 503)
->addArgument($config['endpoint']['api']['auth_token'] ?? null)
;

if (isset($config['endpoint']['api']['enabled'])
&& ! $config['endpoint']['api']['enabled']) {
Expand All @@ -68,9 +70,16 @@ public function configureMonitoring(ContainerBuilder $container, array $config)
$container->findDefinition(QueuedMessages::class)
->setArgument(0, $config['monitoring']['metric']['messenger']['queued_messages']['warning_threshold'] ?? 5)
->setArgument(1, $config['monitoring']['metric']['messenger']['queued_messages']['critical_threshold'] ?? 10);
if (!(isset($config['endpoint']['command']['enabled'])
&& ! $config['endpoint']['command']['enabled'])) {
$container->findDefinition(CheckCommand::class)
->setArgument(0, $config['endpoint']['command']['exit_code']['warning'] ?? 1)
->setArgument(1, $config['endpoint']['command']['exit_code']['critical'] ?? 1)
;
}
}

public function prepend(ContainerBuilder $container)
public function prepend(ContainerBuilder $container): void
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace whatwedo\MonitorBundle\Enum;
namespace whatwedo\MonitorBundle\Enums;

enum MetricStateEnum: string
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace whatwedo\MonitorBundle\Enum;
namespace whatwedo\MonitorBundle\Enums;

enum SensorStateEnum: string
{
Expand Down
41 changes: 34 additions & 7 deletions src/Manager/MonitoringManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@

namespace whatwedo\MonitorBundle\Manager;

use whatwedo\MonitorBundle\Enum\MetricStateEnum;
use whatwedo\MonitorBundle\Enum\SensorStateEnum;
use whatwedo\MonitorBundle\Enums\MetricStateEnum;
use whatwedo\MonitorBundle\Enums\SensorStateEnum;
use whatwedo\MonitorBundle\Monitoring\AttributeInterface;
use whatwedo\MonitorBundle\Monitoring\Metric\AbstractMetric;
use whatwedo\MonitorBundle\Monitoring\Sensor\AbstractSensor;

class MonitoringManager
Expand All @@ -19,19 +18,24 @@ class MonitoringManager

protected ?bool $isSuccessful = null;

protected ?bool $hasWarnings = null;

public function run(): void
{
$this->isSuccessful = true;
$this->hasWarnings = false;

foreach ($this->attributes as $attribute) {
if ($attribute->isEnabled()) {
$attribute->run();
if (($attribute instanceof AbstractSensor
&& $attribute->getState() !== SensorStateEnum::SUCCESSFUL)
|| ($attribute instanceof AbstractMetric
&& $attribute->getState() !== MetricStateEnum::OK)) {
$wasSuccessful = $this->wasSuccessful($attribute);
$wasWarning = $this->wasWarning($attribute);
if (!$wasSuccessful && !$wasWarning) {
$this->isSuccessful = false;
}
if ($wasWarning) {
$this->hasWarnings = true;
}
}
}
}
Expand Down Expand Up @@ -81,6 +85,15 @@ public function isSuccessful(): bool
return $this->isSuccessful;
}

public function isWarning(): bool
{
if ($this->hasWarnings === null) {
$this->run();
}

return $this->hasWarnings;
}

public function addAttribute(AttributeInterface $attribute): void
{
$this->attributes[$attribute::class] = $attribute;
Expand All @@ -90,4 +103,18 @@ public function getAttribute(string $className): AttributeInterface
{
return $this->attributes[$className];
}

private function wasSuccessful(AttributeInterface $attribute): bool
{
return $attribute instanceof AbstractSensor
? $attribute->getState() === SensorStateEnum::SUCCESSFUL
: $attribute->getState() === MetricStateEnum::OK;
}

private function wasWarning(AttributeInterface $abstract): bool
{
return $abstract instanceof AbstractSensor
? $abstract->getState() === SensorStateEnum::WARNING
: $abstract->getState() === MetricStateEnum::WARNING;
}
}
4 changes: 2 additions & 2 deletions src/Monitoring/Metric/AbstractMetric.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace whatwedo\MonitorBundle\Monitoring\Metric;

use whatwedo\MonitorBundle\Enum\MetricStateEnum;
use whatwedo\MonitorBundle\Enums\MetricStateEnum;
use whatwedo\MonitorBundle\Monitoring\AttributeInterface;

abstract class AbstractMetric implements AttributeInterface
Expand All @@ -16,7 +16,7 @@ abstract class AbstractMetric implements AttributeInterface
public function getState(): MetricStateEnum
{
if ($this->state === null) {
throw new \RuntimeException(__CLASS__ . '::$state is not set.');
throw new \RuntimeException(__CLASS__.'::$state is not set.');
}

return $this->state;
Expand Down
2 changes: 1 addition & 1 deletion src/Monitoring/Metric/Messenger/QueuedMessages.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\Messenger\Transport\Receiver\MessageCountAwareInterface;
use Symfony\Component\Messenger\Transport\TransportInterface;
use whatwedo\MonitorBundle\Enum\MetricStateEnum;
use whatwedo\MonitorBundle\Enums\MetricStateEnum;
use whatwedo\MonitorBundle\Monitoring\Metric\AbstractMetric;

class QueuedMessages extends AbstractMetric
Expand Down
4 changes: 2 additions & 2 deletions src/Monitoring/Sensor/AbstractSensor.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace whatwedo\MonitorBundle\Monitoring\Sensor;

use whatwedo\MonitorBundle\Enum\SensorStateEnum;
use whatwedo\MonitorBundle\Enums\SensorStateEnum;
use whatwedo\MonitorBundle\Monitoring\AttributeInterface;

abstract class AbstractSensor implements AttributeInterface
Expand All @@ -16,7 +16,7 @@ abstract class AbstractSensor implements AttributeInterface
public function getState(): SensorStateEnum
{
if ($this->state === null) {
throw new \RuntimeException(__CLASS__ . '::$state is not set.');
throw new \RuntimeException(__CLASS__.'::$state is not set.');
}

return $this->state;
Expand Down
4 changes: 2 additions & 2 deletions src/Monitoring/Sensor/Database/DoctrineDbal.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use Doctrine\Persistence\ManagerRegistry;
use Psr\Container\ContainerInterface;
use Symfony\Contracts\Service\ServiceSubscriberInterface;
use whatwedo\MonitorBundle\Enum\SensorStateEnum;
use whatwedo\MonitorBundle\Enums\SensorStateEnum;
use whatwedo\MonitorBundle\Monitoring\Sensor\AbstractSensor;

class DoctrineDbal extends AbstractSensor implements ServiceSubscriberInterface
Expand Down Expand Up @@ -52,7 +52,7 @@ public function run(): void
public static function getSubscribedServices(): array
{
return [
'?' . ManagerRegistry::class,
'?'.ManagerRegistry::class,
];
}
}
2 changes: 1 addition & 1 deletion src/Monitoring/Sensor/Database/DoctrineMigrations.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use Doctrine\Migrations\DependencyFactory;
use Doctrine\Migrations\Version\MigrationStatusCalculator;
use whatwedo\MonitorBundle\Enum\SensorStateEnum;
use whatwedo\MonitorBundle\Enums\SensorStateEnum;
use whatwedo\MonitorBundle\Monitoring\Sensor\AbstractSensor;

class DoctrineMigrations extends AbstractSensor
Expand Down
Loading

0 comments on commit b59fcd0

Please sign in to comment.