Skip to content

Commit

Permalink
refactor: use hathor-core health endpoint (#109)
Browse files Browse the repository at this point in the history
  • Loading branch information
luislhl committed Feb 22, 2024
1 parent 68bd025 commit e0a2d4d
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 46 deletions.
16 changes: 8 additions & 8 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ structlog = "~22.3.0"
prometheus-client = "^0.9.0"
idna_ssl = "^1.1.0"
asynctest = "^0.13.0"
hathorlib = {version = "^0.5.1", extras = ["client"]}
hathorlib = {version = "^0.6.0", extras = ["client"]}
dataclasses = {version = "^0.8", python = ">=3.6,<3.7"}
python-healthchecklib = "^0.1.0"

Expand Down
72 changes: 48 additions & 24 deletions tests/test_healthcheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
class HathorClientMock:
_base_url = "http://localhost:8080"

async def version(self):
return {"version": "1.0.0"}
async def health(self):
return {"status": "pass"}


class TestFullnodeHealthCheck(asynctest.TestCase): # type: ignore[misc]
Expand All @@ -26,26 +26,54 @@ def setUp(self) -> None:

async def test_get_health_check_with_a_healthy_fullnode(self):
"""Test the response we should generated for a healthy fullnode"""
# Mock the implementation of the hathor_client.version.
# Mock the implementation of the hathor_client.health.
async def side_effect():
return {"version": "1.0.0"}
return {"status": "pass"}

self.mock_hathor_client.version.side_effect = side_effect
self.mock_hathor_client.health.side_effect = side_effect
self.mock_hathor_client._base_url = "http://localhost:8080"

result = await self.fullnode_health_check.get_health_check()
self.assertEqual(result.status, "pass")
self.assertEqual(result.output, "Fullnode is responding correctly")
self.assertEqual(result.output, "Fullnode is healthy")

async def test_get_health_check_with_an_unhealthy_fullnode(self):
async def test_get_health_check_raises_exception(self):
"""Test the response we should generated for an unhealthy fullnode"""
self.mock_hathor_client.version.side_effect = Exception("error")
self.mock_hathor_client.health.side_effect = Exception("error")
self.mock_hathor_client._base_url = "http://localhost:8080"

result = await self.fullnode_health_check.get_health_check()
self.assertEqual(result.status, "fail")
self.assertEqual(result.output, "Couldn't connect to fullnode: error")

async def test_get_health_check_invalid_status(self):
"""Test the response we should generated for an unhealthy fullnode"""
# Mock the implementation of the hathor_client.health.
async def side_effect():
return {"status": "invalid"}

self.mock_hathor_client.health.side_effect = side_effect
self.mock_hathor_client._base_url = "http://localhost:8080"

result = await self.fullnode_health_check.get_health_check()
self.assertEqual(result.status, "fail")
self.assertEqual(
result.output, "Fullnode returned invalid status: {'status': 'invalid'}"
)

async def test_get_health_check_unhealthy_fullnode(self):
"""Test the response we should generated for an unhealthy fullnode"""
# Mock the implementation of the hathor_client.health.
async def side_effect():
return {"status": "fail"}

self.mock_hathor_client.health.side_effect = side_effect
self.mock_hathor_client._base_url = "http://localhost:8080"

result = await self.fullnode_health_check.get_health_check()
self.assertEqual(result.status, "fail")
self.assertEqual(result.output, "Fullnode is not healthy: {'status': 'fail'}")


class TestMiningHealthCheck(asynctest.TestCase): # type: ignore[misc]
def setUp(self):
Expand Down Expand Up @@ -157,12 +185,12 @@ def setUp(self):

async def test_get_health_check_success(self):
"""Tests the response we should generate when everything is ok"""
# Mock the implementation of the hathor_client.version.
# Mock the implementation of the hathor_client.health.
async def side_effect():
return {"version": "1.0.0"}
return {"status": "pass"}

self.mock_hathor_client.version = MagicMock()
self.mock_hathor_client.version.side_effect = side_effect
self.mock_hathor_client.health = MagicMock()
self.mock_hathor_client.health.side_effect = side_effect

self.mock_hathor_client._base_url = "http://localhost:8080"

Expand All @@ -184,15 +212,13 @@ async def side_effect():
)
self.assertEqual(result.checks["fullnode"][0].component_name, "fullnode")
self.assertEqual(result.checks["fullnode"][0].component_type, "http")
self.assertEqual(
result.checks["fullnode"][0].output, "Fullnode is responding correctly"
)
self.assertEqual(result.checks["fullnode"][0].output, "Fullnode is healthy")
self.assertEqual(result.status, HealthcheckStatus.PASS)

async def test_get_health_check_fullnode_failure(self):
"""Tests the response we should generate when the fullnode is unhealthy"""
self.mock_hathor_client.version = MagicMock()
self.mock_hathor_client.version.side_effect = Exception("error")
self.mock_hathor_client.health = MagicMock()
self.mock_hathor_client.health.side_effect = Exception("error")
self.mock_hathor_client._base_url = "http://localhost:8080"

self.mock_manager.has_any_miner.return_value = True
Expand All @@ -213,12 +239,12 @@ async def test_get_health_check_fullnode_failure(self):

async def test_get_health_check_mining_failure(self):
"""Tests the response we should generate when the mining is unhealthy"""
# Mock the implementation of the hathor_client.version.
# Mock the implementation of the hathor_client.health.
async def side_effect():
return {"version": "1.0.0"}
return {"status": "pass"}

self.mock_hathor_client.version = MagicMock()
self.mock_hathor_client.version.side_effect = side_effect
self.mock_hathor_client.health = MagicMock()
self.mock_hathor_client.health.side_effect = side_effect
self.mock_hathor_client._base_url = "http://localhost:8080"

self.mock_manager.has_any_miner.return_value = True
Expand All @@ -231,7 +257,5 @@ async def side_effect():
"No miners submitted a job in the last 1 hour",
)
self.assertEqual(result.checks["fullnode"][0].status, HealthcheckStatus.PASS)
self.assertEqual(
result.checks["fullnode"][0].output, "Fullnode is responding correctly"
)
self.assertEqual(result.checks["fullnode"][0].output, "Fullnode is healthy")
self.assertEqual(result.status, HealthcheckStatus.FAIL)
34 changes: 21 additions & 13 deletions txstratum/healthcheck/healthcheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,21 +71,29 @@ def __init__(self, backend: "HathorClient") -> None:

async def get_health_check(self) -> HealthcheckCallbackResponse:
"""Return the fullnode health check status."""
response = HealthcheckCallbackResponse(
status=HealthcheckStatus.PASS,
output="Fullnode is responding correctly",
)

try:
# TODO: We need to get the health information from the fullnode, but it's not implemented yet
await self.backend.version()

return response
health = await self.backend.health()

if health["status"] in [HealthcheckStatus.FAIL, HealthcheckStatus.WARN]:
return HealthcheckCallbackResponse(
status=HealthcheckStatus(health["status"]),
output="Fullnode is not healthy: %s" % str(health),
)
elif health["status"] == HealthcheckStatus.PASS:
return HealthcheckCallbackResponse(
status=HealthcheckStatus.PASS,
output="Fullnode is healthy",
)
else:
return HealthcheckCallbackResponse(
status=HealthcheckStatus.FAIL,
output="Fullnode returned invalid status: %s" % str(health),
)
except Exception as e:
response.status = HealthcheckStatus.FAIL
response.output = f"Couldn't connect to fullnode: {str(e)}"

return response
return HealthcheckCallbackResponse(
status=HealthcheckStatus.FAIL,
output=f"Couldn't connect to fullnode: {str(e)}",
)


class MiningHealthCheck(ComponentHealthCheckInterface):
Expand Down

0 comments on commit e0a2d4d

Please sign in to comment.