Skip to content

Commit

Permalink
Added healthcheck endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
vtsykun committed Sep 2, 2023
1 parent 0f2dd8b commit ca553e4
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 1 deletion.
1 change: 1 addition & 0 deletions config/packages/security.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ security:
- { path: ^/login$, roles: PUBLIC_ACCESS }
- { path: (^(/login/|/oauth2/))+, roles: PUBLIC_ACCESS }
- { path: ^/reset-password, roles: PUBLIC_ACCESS }
- { path: ^/api/healthz, roles: PUBLIC_ACCESS }

# Packagist
- { path: (^(/change-password|/profile|/logout|/subrepository/))+, roles: ROLE_USER }
Expand Down
1 change: 1 addition & 0 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ services:
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
bind:
$redis: '@snc_redis.default'
Redis: '@snc_redis.default'
$jwtTokenConfig: '%packeton_jws_config%'
$jwtSignAlgorithm: '%packeton_jws_algo%'
$mirrorRepoMetaDir: '%mirror_repos_meta_dir%'
Expand Down
82 changes: 82 additions & 0 deletions src/Controller/Api/ApiHealthController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

declare(strict_types=1);

namespace Packeton\Controller\Api;

use Doctrine\DBAL\Connection;
use Doctrine\Persistence\ManagerRegistry;
use Packeton\Composer\JsonResponse;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\Cache\CacheInterface;

#[Route('/api', name: 'api_', defaults: ['_format' => 'json'])]
class ApiHealthController extends AbstractController
{
#[Route('/healthz', name: 'health', methods: ['GET'])]
public function health(): Response
{
if (false === $this->getParameter('packeton_health_check')) {
return new JsonResponse([], 404);
}

$checks = [
'database:ping' => function() {
/** @var Connection $conn */
$conn = $this->container->get(ManagerRegistry::class)->getConnection();
$conn->executeQuery('SELECT 1');
},
'redis:ping' => function() {
/** @var \Redis $redis */
$redis = $this->container->get(\Redis::class);
$redis->get('packages-last-modify');
},
'cache:ping' => function() {
$this->container->get(CacheInterface::class)->get('cache:ping', fn () => []);
},
];

$status = true;
$checksResult = [];
foreach ($checks as $name => $fn) {
$item = $checksResult[$name] = $this->checkHealth($fn);
$status = $status && ($item['status'] !== 'fail');
}

$result = [
'status' => $status ? 'pass' : 'fail',
'checks' => $checksResult,
];

return new Response(json_encode($result, 448), $status ? 200 : 500, ['Content-Type' => 'application/json']);
}

protected function checkHealth(callable $check): array
{
$result = ['time' => (new \DateTime('now', new \DateTimeZone('UTC')))->format('Y-m-d\TH:i:s\Z')];

try {
$start = microtime(true);
$result += $check() ?: [];
$result['status'] = 'pass';
$ping = microtime(true) - $start;

$result += ['observedValue' => (float)round(1000*$ping, 2), 'observedUnit' => 'ms'];
} catch (\Throwable $e) {
$result['status'] = 'fail';
}

return $result;
}

public static function getSubscribedServices(): array
{
return parent::getSubscribedServices() + [
\Redis::class,
ManagerRegistry::class,
CacheInterface::class
];
}
}
2 changes: 1 addition & 1 deletion src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public function getConfigTreeBuilder(): TreeBuilder
->scalarNode('content_type')->end()
->end()
->end()

->booleanNode('health_check')->defaultTrue()->end()
->booleanNode('archive')
->defaultFalse()
->end()
Expand Down
1 change: 1 addition & 0 deletions src/DependencyInjection/PacketonExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public function load(array $configs, ContainerBuilder $container): void
$container->setParameter('packeton_artifact_storage', $config['artifacts']['artifact_storage'] ?? null);
$container->setParameter('packeton_artifact_types', $config['artifacts']['support_types'] ?? []);
$container->setParameter('packeton_web_protection', $config['web_protection'] ?? null);
$container->setParameter('packeton_health_check', $config['health_check'] ?? true);

$container->registerAttributeForAutoconfiguration(AsWorker::class, static function (ChildDefinition $definition, AsWorker $attribute) {
$attributes = get_object_vars($attribute);
Expand Down
1 change: 1 addition & 0 deletions src/EventListener/SubRepositoryListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class SubRepositoryListener
'logout' => 1,
'subrepository_switch' => 1,
'subrepository_switch_root' => 1,
'api_health' => 1,
];

public function __construct(
Expand Down
4 changes: 4 additions & 0 deletions swagger/packages-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ paths:
required: true
type: "string"

'/api/healthz':
get:
summary: 'Health check'

'/api/update-package':
post:
tags: [ Packages ]
Expand Down

0 comments on commit ca553e4

Please sign in to comment.