Skip to content

Commit

Permalink
chore: use hathor-core's health endpoint (#311)
Browse files Browse the repository at this point in the history
  • Loading branch information
luislhl authored Feb 16, 2024
1 parent c166744 commit a4426d2
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 60 deletions.
1 change: 1 addition & 0 deletions gateways/clients/hathor_core_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
TX_ACC_WEIGHT_ENDPOINT = "/v1a/transaction_acc_weight"
VERSION_ENDPOINT = "/v1a/version"
FEATURE_ENDPOINT = "/v1a/feature"
HEALTH_ENDPOINT = "/v1a/health"


class HathorCoreAsyncClient:
Expand Down
8 changes: 4 additions & 4 deletions gateways/healthcheck_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from common.configuration import ELASTIC_INDEX
from gateways.clients.cache_client import CacheClient
from gateways.clients.elastic_search_client import ElasticSearchClient
from gateways.clients.hathor_core_client import VERSION_ENDPOINT, HathorCoreAsyncClient
from gateways.clients.hathor_core_client import HEALTH_ENDPOINT, HathorCoreAsyncClient
from gateways.clients.wallet_service_db_client import WalletServiceDBClient

# The default lambda timeout for the Healtcheck Lambda is set to
Expand Down Expand Up @@ -32,11 +32,11 @@ def __init__(
wallet_service_db_client or WalletServiceDBClient()
)

async def get_hathor_core_version(self) -> Optional[dict]:
"""Retrieve hathor-core version information"""
async def get_hathor_core_health(self) -> Optional[dict]:
"""Retrieve hathor-core health information"""

return await self.hathor_core_async_client.get(
VERSION_ENDPOINT, timeout=HEALTHCHECK_CLIENT_TIMEOUT_IN_SECONDS
HEALTH_ENDPOINT, timeout=HEALTHCHECK_CLIENT_TIMEOUT_IN_SECONDS
)

def ping_redis(self) -> bool:
Expand Down
14 changes: 7 additions & 7 deletions tests/unit/gateways/test_healthcheck_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ def setUp(self):
wallet_service_db_client=self.wallet_service_db_client,
)

async def test_get_hathor_core_version(self):
async def mock_get_hathor_core_version(endpoint, **kwargs):
return {"version": "0.39.0"}
async def test_get_hathor_core_health(self):
async def mock_get_hathor_core_health(endpoint, **kwargs):
return {"status": "pass"}

self.hathor_core_async_client.get.side_effect = mock_get_hathor_core_version
result = await self.healthcheck_gateway.get_hathor_core_version()
self.assertEqual(result, {"version": "0.39.0"})
self.hathor_core_async_client.get.side_effect = mock_get_hathor_core_health
result = await self.healthcheck_gateway.get_hathor_core_health()
self.assertEqual(result, {"status": "pass"})
self.hathor_core_async_client.get.assert_called_once_with(
"/v1a/version", timeout=5
"/v1a/health", timeout=5
)

def test_ping_redis(self):
Expand Down
70 changes: 35 additions & 35 deletions tests/unit/usecases/test_healthcheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ def setUp(self):
)

def test_all_components_healthy(self):
async def mock_get_hathor_core_version():
return {"version": "0.38.0"}
async def mock_get_hathor_core_health():
return {"status": "pass"}

self.mock_healthcheck_gateway.get_hathor_core_version.side_effect = (
mock_get_hathor_core_version
self.mock_healthcheck_gateway.get_hathor_core_health.side_effect = (
mock_get_hathor_core_health
)
self.mock_healthcheck_gateway.ping_wallet_service_db.return_value = (True, "1")
self.mock_healthcheck_gateway.ping_redis.return_value = True
Expand Down Expand Up @@ -80,11 +80,11 @@ async def mock_get_hathor_core_version():
)

def test_hathor_core_returns_error(self):
async def mock_get_hathor_core_version():
async def mock_get_hathor_core_health():
return {"error": "Unable to connect"}

self.mock_healthcheck_gateway.get_hathor_core_version.side_effect = (
mock_get_hathor_core_version
self.mock_healthcheck_gateway.get_hathor_core_health.side_effect = (
mock_get_hathor_core_health
)
self.mock_healthcheck_gateway.ping_wallet_service_db.return_value = (True, "1")
self.mock_healthcheck_gateway.ping_redis.return_value = True
Expand Down Expand Up @@ -142,11 +142,11 @@ async def mock_get_hathor_core_version():
)

def test_wallet_service_db_raises_exception(self):
async def mock_get_hathor_core_version():
return {"version": "0.38.0"}
async def mock_get_hathor_core_health():
return {"status": "pass"}

self.mock_healthcheck_gateway.get_hathor_core_version.side_effect = (
mock_get_hathor_core_version
self.mock_healthcheck_gateway.get_hathor_core_health.side_effect = (
mock_get_hathor_core_health
)
self.mock_healthcheck_gateway.ping_wallet_service_db.side_effect = Exception(
"Unable to connect"
Expand Down Expand Up @@ -206,11 +206,11 @@ async def mock_get_hathor_core_version():
)

def test_wallet_service_db_reports_unhealthy(self):
async def mock_get_hathor_core_version():
return {"version": "0.38.0"}
async def mock_get_hathor_core_health():
return {"status": "pass"}

self.mock_healthcheck_gateway.get_hathor_core_version.side_effect = (
mock_get_hathor_core_version
self.mock_healthcheck_gateway.get_hathor_core_health.side_effect = (
mock_get_hathor_core_health
)
self.mock_healthcheck_gateway.ping_wallet_service_db.return_value = (
False,
Expand Down Expand Up @@ -271,11 +271,11 @@ async def mock_get_hathor_core_version():
)

def test_redis_raises_exception(self):
async def mock_get_hathor_core_version():
return {"version": "0.38.0"}
async def mock_get_hathor_core_health():
return {"status": "pass"}

self.mock_healthcheck_gateway.get_hathor_core_version.side_effect = (
mock_get_hathor_core_version
self.mock_healthcheck_gateway.get_hathor_core_health.side_effect = (
mock_get_hathor_core_health
)
self.mock_healthcheck_gateway.ping_wallet_service_db.return_value = (True, "1")
self.mock_healthcheck_gateway.ping_redis.side_effect = Exception(
Expand Down Expand Up @@ -335,11 +335,11 @@ async def mock_get_hathor_core_version():
)

def test_redis_reports_unhealthy(self):
async def mock_get_hathor_core_version():
return {"version": "0.38.0"}
async def mock_get_hathor_core_health():
return {"status": "pass"}

self.mock_healthcheck_gateway.get_hathor_core_version.side_effect = (
mock_get_hathor_core_version
self.mock_healthcheck_gateway.get_hathor_core_health.side_effect = (
mock_get_hathor_core_health
)
self.mock_healthcheck_gateway.ping_wallet_service_db.return_value = (True, "1")
self.mock_healthcheck_gateway.ping_redis.return_value = False
Expand Down Expand Up @@ -397,11 +397,11 @@ async def mock_get_hathor_core_version():
)

def test_elasticsearch_raises_exception(self):
async def mock_get_hathor_core_version():
return {"version": "0.38.0"}
async def mock_get_hathor_core_health():
return {"status": "pass"}

self.mock_healthcheck_gateway.get_hathor_core_version.side_effect = (
mock_get_hathor_core_version
self.mock_healthcheck_gateway.get_hathor_core_health.side_effect = (
mock_get_hathor_core_health
)
self.mock_healthcheck_gateway.ping_wallet_service_db.return_value = (True, "1")
self.mock_healthcheck_gateway.ping_redis.return_value = True
Expand Down Expand Up @@ -459,11 +459,11 @@ async def mock_get_hathor_core_version():
)

def test_elasticsearch_reports_unhealthy(self):
async def mock_get_hathor_core_version():
return {"version": "0.38.0"}
async def mock_get_hathor_core_health():
return {"status": "pass"}

self.mock_healthcheck_gateway.get_hathor_core_version.side_effect = (
mock_get_hathor_core_version
self.mock_healthcheck_gateway.get_hathor_core_health.side_effect = (
mock_get_hathor_core_health
)
self.mock_healthcheck_gateway.ping_wallet_service_db.return_value = (True, "1")
self.mock_healthcheck_gateway.ping_redis.return_value = True
Expand Down Expand Up @@ -521,11 +521,11 @@ async def mock_get_hathor_core_version():
)

def test_elasticsearch_report_yellow(self):
async def mock_get_hathor_core_version():
return {"version": "0.38.0"}
async def mock_get_hathor_core_health():
return {"status": "pass"}

self.mock_healthcheck_gateway.get_hathor_core_version.side_effect = (
mock_get_hathor_core_version
self.mock_healthcheck_gateway.get_hathor_core_health.side_effect = (
mock_get_hathor_core_health
)
self.mock_healthcheck_gateway.ping_wallet_service_db.return_value = (True, "1")
self.mock_healthcheck_gateway.ping_redis.return_value = True
Expand Down
44 changes: 30 additions & 14 deletions usecases/get_healthcheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
HealthcheckCallbackResponse,
HealthcheckDatastoreComponent,
HealthcheckHTTPComponent,
HealthcheckStatus,
)

from common.configuration import (
Expand Down Expand Up @@ -54,31 +55,46 @@ def __init__(
self.healthcheck.add_component(component)

async def _get_fullnode_health(self):
# TODO: We need to use the hathor-core's /health endpoint when it's available
version_response = await self.healthcheck_gateway.get_hathor_core_version()
health_response = await self.healthcheck_gateway.get_hathor_core_health()

if "error" in version_response:
if "error" in health_response:
return HealthcheckCallbackResponse(
status="fail",
output=f"Fullnode healthcheck errored: {version_response['error']}",
status=HealthcheckStatus.FAIL,
output=f"Fullnode healthcheck errored: {health_response['error']}",
)

status = health_response["status"]

# Here we're assuming that a 'warn' status will be considered as unhealthy
is_healthy = status == HealthcheckStatus.PASS
is_unhealthy = status in [HealthcheckStatus.FAIL, HealthcheckStatus.WARN]

if is_unhealthy:
output = f"Fullnode is not healthy: {str(health_response)}"
elif is_healthy:
output = "Fullnode is healthy"
else:
status = HealthcheckStatus.FAIL
output = (
f"Fullnode returned an unexpected health status: {str(health_response)}"
)

return HealthcheckCallbackResponse(
status="pass",
output="Fullnode is healthy",
status=status,
output=output,
)

async def _get_wallet_service_db_health(self):
try:
is_healthy, output = self.healthcheck_gateway.ping_wallet_service_db()
except Exception as e:
return HealthcheckCallbackResponse(
status="fail",
status=HealthcheckStatus.FAIL,
output=f"Wallet service DB healthcheck errored: {repr(e)}",
)

return HealthcheckCallbackResponse(
status="pass" if is_healthy else "fail",
status=HealthcheckStatus.PASS if is_healthy else HealthcheckStatus.FAIL,
output="Wallet service DB is healthy"
if is_healthy
else f"Wallet service DB didn't respond as expected: {output}",
Expand All @@ -89,12 +105,12 @@ async def _get_redis_health(self):
is_healthy = self.healthcheck_gateway.ping_redis()
except Exception as e:
return HealthcheckCallbackResponse(
status="fail",
status=HealthcheckStatus.FAIL,
output=f"Redis healthcheck errored: {repr(e)}",
)

return HealthcheckCallbackResponse(
status="pass" if is_healthy else "fail",
status=HealthcheckStatus.PASS if is_healthy else HealthcheckStatus.FAIL,
output="Redis is healthy" if is_healthy else "Redis reported as unhealthy",
)

Expand All @@ -103,13 +119,13 @@ async def _get_elasticsearch_health(self):
elasticsearch_info = self.healthcheck_gateway.get_elasticsearch_health()
except Exception as e:
return HealthcheckCallbackResponse(
status="fail",
status=HealthcheckStatus.FAIL,
output=f"Elasticsearch healthcheck errored: {repr(e)}",
)

if elasticsearch_info["status"] == "red":
return HealthcheckCallbackResponse(
status="fail",
status=HealthcheckStatus.FAIL,
output=f"Elasticsearch is not healthy: {str(elasticsearch_info)}",
)
if elasticsearch_info["status"] == "yellow":
Expand All @@ -119,7 +135,7 @@ async def _get_elasticsearch_health(self):
)

return HealthcheckCallbackResponse(
status="pass",
status=HealthcheckStatus.PASS,
output=str(elasticsearch_info),
)

Expand Down

0 comments on commit a4426d2

Please sign in to comment.