-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Suport multiple DB connections by naming respective health-provider a…
…nd composing them Issue #96 asked to support for showing health of multiple DB engines/endpoints. To address this, Pyctuator now supports `ComposeiteHealthProvider` which wraps around a list of health-providers. Also, the built-in health-providers now support overrding the default name. Setting health check for multiple DB engines can be done as follows: ```python pyctuator.register_health_provider( CompositeHealthProvider( "db", DbHealthProvider(db_engine, "db1"), DbHealthProvider(db_engine, "db2"), ) ) ```
- Loading branch information
michael.yak
committed
Oct 6, 2023
1 parent
995413d
commit 13f456b
Showing
6 changed files
with
136 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
from dataclasses import dataclass | ||
from typing import Mapping | ||
|
||
from pyctuator.health.health_provider import HealthProvider, HealthStatus, Status | ||
|
||
|
||
@dataclass | ||
class CompositeHealthStatus(HealthStatus): | ||
status: Status | ||
details: Mapping[str, HealthStatus] # type: ignore[assignment] | ||
|
||
|
||
class CompositeHealthProvider(HealthProvider): | ||
|
||
def __init__(self, name: str, *health_providers: HealthProvider) -> None: | ||
super().__init__() | ||
self.name = name | ||
self.health_providers = health_providers | ||
|
||
def is_supported(self) -> bool: | ||
return True | ||
|
||
def get_name(self) -> str: | ||
return self.name | ||
|
||
def get_health(self) -> CompositeHealthStatus: | ||
health_statuses: Mapping[str, HealthStatus] = { | ||
provider.get_name(): provider.get_health() | ||
for provider in self.health_providers | ||
if provider.is_supported() | ||
} | ||
|
||
# Health is UP if no provider is registered | ||
if not health_statuses: | ||
return CompositeHealthStatus(Status.UP, health_statuses) | ||
|
||
# If there's at least one provider and any of the providers is DOWN, the service is DOWN | ||
service_is_down = any(health_status.status == Status.DOWN for health_status in health_statuses.values()) | ||
if service_is_down: | ||
return CompositeHealthStatus(Status.DOWN, health_statuses) | ||
|
||
# If there's at least one provider and none of the providers is DOWN and at least one is UP, the service is UP | ||
service_is_up = any(health_status.status == Status.UP for health_status in health_statuses.values()) | ||
if service_is_up: | ||
return CompositeHealthStatus(Status.UP, health_statuses) | ||
|
||
# else, all providers are unknown so the service is UNKNOWN | ||
return CompositeHealthStatus(Status.UNKNOWN, health_statuses) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
from dataclasses import dataclass | ||
|
||
from pyctuator.health.composite_health_provider import CompositeHealthProvider, CompositeHealthStatus | ||
from pyctuator.health.health_provider import HealthProvider, HealthStatus, Status, HealthDetails | ||
|
||
|
||
@dataclass | ||
class CustomHealthDetails(HealthDetails): | ||
details: str | ||
|
||
|
||
class CustomHealthProvider(HealthProvider): | ||
|
||
def __init__(self, name: str, status: HealthStatus) -> None: | ||
super().__init__() | ||
self.name = name | ||
self.status = status | ||
|
||
def is_supported(self) -> bool: | ||
return True | ||
|
||
def get_name(self) -> str: | ||
return self.name | ||
|
||
def get_health(self) -> HealthStatus: | ||
return self.status | ||
|
||
|
||
def test_composite_health_provider_no_providers() -> None: | ||
health_provider = CompositeHealthProvider( | ||
"comp1", | ||
) | ||
|
||
assert health_provider.get_name() == "comp1" | ||
|
||
assert health_provider.get_health() == CompositeHealthStatus( | ||
status=Status.UP, | ||
details={} | ||
) | ||
|
||
|
||
def test_composite_health_provider_all_up() -> None: | ||
health_provider = CompositeHealthProvider( | ||
"comp2", | ||
CustomHealthProvider("hp1", HealthStatus(Status.UP, CustomHealthDetails("d1"))), | ||
CustomHealthProvider("hp2", HealthStatus(Status.UP, CustomHealthDetails("d2"))), | ||
) | ||
|
||
assert health_provider.get_name() == "comp2" | ||
|
||
assert health_provider.get_health() == CompositeHealthStatus( | ||
status=Status.UP, | ||
details={ | ||
"hp1": HealthStatus(Status.UP, CustomHealthDetails("d1")), | ||
"hp2": HealthStatus(Status.UP, CustomHealthDetails("d2")), | ||
} | ||
) | ||
|
||
|
||
def test_composite_health_provider_one_down() -> None: | ||
health_provider = CompositeHealthProvider( | ||
"comp3", | ||
CustomHealthProvider("hp1", HealthStatus(Status.UP, CustomHealthDetails("d1"))), | ||
CustomHealthProvider("hp2", HealthStatus(Status.DOWN, CustomHealthDetails("d2"))), | ||
) | ||
|
||
assert health_provider.get_name() == "comp3" | ||
|
||
assert health_provider.get_health() == CompositeHealthStatus( | ||
status=Status.DOWN, | ||
details={ | ||
"hp1": HealthStatus(Status.UP, CustomHealthDetails("d1")), | ||
"hp2": HealthStatus(Status.DOWN, CustomHealthDetails("d2")), | ||
} | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters