Skip to content

Commit 5208bea

Browse files
committed
Add health endpoint
1 parent 97569a2 commit 5208bea

File tree

6 files changed

+101
-7
lines changed

6 files changed

+101
-7
lines changed

api_server/main.py

+14-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
#!/usr/bin/env python3
2+
import os
23
import sys
34

45
import connexion
5-
import os
66

77
from config import CONFIGURATION, TEST_CONFIGURATION
8-
from src.interface_adapter.sql.account_type_repository import AccountTypeSQLRepository
98
from src.interface_adapter.elasticsearch.repository import ElasticSearchRepository
109
from src.interface_adapter.http_api.account import AccountHandler
1110
from src.interface_adapter.http_api.account_type import AccountTypeHandler
1211
from src.interface_adapter.http_api.device import DeviceHandler
12+
from src.interface_adapter.http_api.health import HealthHandler
1313
from src.interface_adapter.http_api.member import MemberHandler
1414
from src.interface_adapter.http_api.payment_method import PaymentMethodHandler
1515
from src.interface_adapter.http_api.port import PortHandler
@@ -19,27 +19,30 @@
1919
from src.interface_adapter.http_api.temporary_account import TemporaryAccountHandler
2020
from src.interface_adapter.http_api.transaction import TransactionHandler
2121
from src.interface_adapter.snmp.switch_network_manager import SwitchSNMPNetworkManager
22+
from src.interface_adapter.sql.account_repository import AccountSQLRepository
23+
from src.interface_adapter.sql.account_type_repository import AccountTypeSQLRepository
2224
from src.interface_adapter.sql.device_repository import DeviceSQLRepository
2325
from src.interface_adapter.sql.member_repository import MemberSQLRepository
2426
from src.interface_adapter.sql.model.database import Database
2527
from src.interface_adapter.sql.money_repository import MoneySQLRepository
2628
from src.interface_adapter.sql.network_object_repository import NetworkObjectSQLRepository
2729
from src.interface_adapter.sql.payment_method_repository import PaymentMethodSQLRepository
28-
from src.interface_adapter.sql.room_repository import RoomSQLRepository
29-
from src.interface_adapter.sql.account_repository import AccountSQLRepository
30+
from src.interface_adapter.sql.ping_repository import PingSQLRepository
3031
from src.interface_adapter.sql.product_repository import ProductSQLRepository
32+
from src.interface_adapter.sql.room_repository import RoomSQLRepository
3133
from src.interface_adapter.sql.transaction_repository import TransactionSQLRepository
3234
from src.resolver import ADHResolver
35+
from src.use_case.account_manager import AccountManager
36+
from src.use_case.account_type_manager import AccountTypeManager
3337
from src.use_case.device_manager import DeviceManager
38+
from src.use_case.health_manager import HealthManager
3439
from src.use_case.member_manager import MemberManager
3540
from src.use_case.payment_method_manager import PaymentMethodManager
3641
from src.use_case.port_manager import PortManager
42+
from src.use_case.product_manager import ProductManager
3743
from src.use_case.room_manager import RoomManager
3844
from src.use_case.switch_manager import SwitchManager
39-
from src.use_case.account_manager import AccountManager
40-
from src.use_case.product_manager import ProductManager
4145
from src.use_case.transaction_manager import TransactionManager
42-
from src.use_case.account_type_manager import AccountTypeManager
4346

4447

4548
def init(testing=True):
@@ -54,6 +57,7 @@ def init(testing=True):
5457
Database.init_db(configuration.DATABASE, testing=testing)
5558

5659
# Repositories:
60+
ping_repository = PingSQLRepository()
5761
member_sql_repository = MemberSQLRepository()
5862
network_object_sql_repository = NetworkObjectSQLRepository()
5963
device_sql_repository = DeviceSQLRepository()
@@ -68,6 +72,7 @@ def init(testing=True):
6872
account_type_sql_repository = AccountTypeSQLRepository()
6973

7074
# Managers
75+
health_manager = HealthManager(ping_repository)
7176
switch_manager = SwitchManager(
7277
switch_repository=network_object_sql_repository,
7378
)
@@ -110,6 +115,7 @@ def init(testing=True):
110115
)
111116

112117
# HTTP Handlers:
118+
health_handler = HealthHandler(health_manager)
113119
transaction_handler = TransactionHandler(transaction_manager)
114120
member_handler = MemberHandler(member_manager)
115121
device_handler = DeviceHandler(device_manager)
@@ -130,6 +136,7 @@ def init(testing=True):
130136
app.add_api('swagger.yaml',
131137
# resolver=RestyResolver('src.interface_adapter.http_api'),
132138
resolver=ADHResolver({
139+
'health': health_handler,
133140
'transaction': transaction_handler,
134141
'member': member_handler,
135142
'device': device_handler,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from connexion import NoContent
2+
3+
from src.interface_adapter.http_api.decorator.with_context import with_context
4+
from src.interface_adapter.sql.decorator.sql_session import require_sql
5+
from src.use_case.health_manager import HealthManager
6+
from src.util.context import log_extra
7+
from src.util.log import LOG
8+
9+
10+
class HealthHandler:
11+
def __init__(self, health_manager: HealthManager):
12+
self.health_manager = health_manager
13+
14+
@with_context
15+
@require_sql
16+
def health(self, ctx):
17+
LOG.debug("http_health_called", extra=log_extra(ctx))
18+
19+
if self.health_manager.is_healthy(ctx):
20+
return "OK", 200
21+
else:
22+
return "FAILURE", 503
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from sqlalchemy.exc import SQLAlchemyError
2+
3+
from src.constants import CTX_SQL_SESSION
4+
from src.use_case.interface.ping_repository import PingRepository
5+
from src.util.context import log_extra
6+
from src.util.log import LOG
7+
8+
9+
class PingSQLRepository(PingRepository):
10+
def ping(self, ctx) -> bool:
11+
LOG.debug("sql_ping", extra=log_extra(ctx))
12+
13+
s = ctx.get(CTX_SQL_SESSION)
14+
try:
15+
result = s.execute('SELECT 42').fetchall()
16+
if 1 != len(result):
17+
return False
18+
19+
return [('42', 42)] == result[0].items()
20+
21+
except SQLAlchemyError:
22+
return False
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from src.use_case.interface.ping_repository import PingRepository
2+
from src.util.context import log_extra
3+
from src.util.log import LOG
4+
5+
6+
class HealthManager:
7+
"""
8+
Response to health requests.
9+
"""
10+
11+
def __init__(self, repository: PingRepository):
12+
self.health_repository = repository
13+
14+
def is_healthy(self, ctx) -> bool:
15+
db_health = self.health_repository.ping(ctx)
16+
if not db_health:
17+
LOG.error("health_check_db_not_healthy", extra=log_extra(ctx))
18+
return False
19+
20+
# TODO: add more health checks?
21+
22+
LOG.info("health_check_success", extra=log_extra(ctx))
23+
return True
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import abc
2+
3+
4+
class PingRepository(metaclass=abc.ABCMeta):
5+
"""
6+
Abstract interface to get the health of the DB.
7+
"""
8+
9+
@abc.abstractmethod
10+
def ping(self, ctx) -> bool:
11+
pass # pragma: no cover

openapi/spec.yaml

+9
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@ info:
1212
servers:
1313
- url: /api
1414
paths:
15+
/health/:
16+
get:
17+
operationId: health
18+
responses:
19+
200:
20+
description: ADH6 is alive!
21+
503:
22+
description: Database is down.
23+
summary: Health endpoint, query this with zabbix to know if ADH6 is running or not.
1524
/account/:
1625
get:
1726
parameters:

0 commit comments

Comments
 (0)