Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release: v0.14.0 #313

Merged
merged 5 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,4 @@ If you add a new API, be careful to add ```request.header.origin``` as cacheKey,

## Deploying

Deploys are automated using Github Actions and Flux.

To deploy to the `testnet` environment, simply commit to the `main` branch.

To deploy to the `mainnet` environment, create a release in Github using a tag in the format `v0.0.0`

Currently we do not have an automated mechanism to be warned if some automated deployment fails. So, after triggering the deploy, you should check if a commit was made by `fluxcdbot` in https://github.com/HathorNetwork/ops-tools/commits/master, updating the project's manifests with the new Docker image tag.
See in the [SOP](https://github.com/HathorNetwork/ops-tools/blob/master/docs/sops/hathor-explorer-service.md#deployment).
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
4 changes: 2 additions & 2 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hathor-explorer-service",
"version": "0.13.0",
"version": "0.14.0",
"description": "Hathor Explorer Service Serverless deps",
"dependencies": {
"@apidevtools/swagger-cli": "^4.0.4",
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "hathor-explorer-service"
version = "0.13.0"
version = "0.14.0"
description = ""
authors = ["Hathor Labs <[email protected]>"]
license = "MIT"
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
Loading