Skip to content

Commit

Permalink
Implement common Healthcheck & Prefix attributes as abstract prop…
Browse files Browse the repository at this point in the history
…erties, removing `__init__` from base classes (#20)
  • Loading branch information
SRv6d authored Dec 5, 2023
1 parent fa816e4 commit 51e96a4
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 22 deletions.
8 changes: 7 additions & 1 deletion src/anycastd/healthcheck/_cabourotte/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,26 @@ class CabourotteHealthcheck(Healthcheck):
url: str

def __init__(self, name: str, *, url: str, interval: datetime.timedelta):
super().__init__(interval)
if not isinstance(interval, datetime.timedelta):
raise TypeError("Interval must be a timedelta.")
if not isinstance(name, str):
raise TypeError("Name must be a string.")
if not isinstance(url, str):
raise TypeError("URL must be a string.")
self.name = name
self.url = url
self.__interval = interval

def __repr__(self) -> str:
return (
f"CabourotteHealthcheck(name={self.name!r}, url={self.url!r}, "
f"interval={self.interval!r})"
)

@property
def interval(self) -> datetime.timedelta:
return self.__interval

async def _check(self) -> bool:
"""Return whether the healthcheck is healthy or not."""
result = await get_result(self.name, url=self.url)
Expand Down
11 changes: 5 additions & 6 deletions src/anycastd/healthcheck/_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@
class Healthcheck(ABC):
"""A healthcheck that represents a status."""

interval: datetime.timedelta

_last_checked: datetime.datetime | None = None
_last_healthy: bool = False

def __init__(self, interval: datetime.timedelta):
if not isinstance(interval, datetime.timedelta):
raise TypeError("Interval must be a timedelta.")
self.interval = interval
@property
@abstractmethod
def interval(self) -> datetime.timedelta:
"""The interval between checks."""
raise NotImplementedError

@final
async def is_healthy(self) -> bool:
Expand Down
8 changes: 7 additions & 1 deletion src/anycastd/prefix/_frrouting/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ def __init__(
to validate the prefix against the FRRouting configuration, avoiding
potential errors later in runtime.
"""
super().__init__(prefix)
if not any((isinstance(prefix, IPv4Network), isinstance(prefix, IPv6Network))):
raise TypeError("Prefix must be an IPv4 or IPv6 network.")
self.__prefix = prefix
self.vrf = vrf
self.vtysh = vtysh
self.executor = executor
Expand All @@ -45,6 +47,10 @@ def __repr__(self) -> str:
f"vtysh={self.vtysh!r}, executor={self.executor!r})"
)

@property
def prefix(self) -> IPv4Network | IPv6Network:
return self.__prefix

async def is_announced(self) -> bool:
"""Returns True if the prefix is announced.
Expand Down
11 changes: 5 additions & 6 deletions src/anycastd/prefix/_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@
class Prefix(ABC):
"""An IP prefix that can be announced or denounced."""

prefix: IPv4Network | IPv6Network

def __init__(self, prefix: IPv4Network | IPv6Network):
if not any((isinstance(prefix, IPv4Network), isinstance(prefix, IPv6Network))):
raise TypeError("Prefix must be an IPv4 or IPv6 network.")
self.prefix = prefix
@property
@abstractmethod
def prefix(self) -> IPv4Network | IPv6Network:
"""The IP prefix."""
raise NotImplementedError

@abstractmethod
async def is_announced(self) -> bool:
Expand Down
17 changes: 17 additions & 0 deletions tests/dummy.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import datetime
from ipaddress import IPv4Network, IPv6Network

from anycastd.healthcheck import Healthcheck
from anycastd.prefix import Prefix


class DummyHealthcheck(Healthcheck):
"""A dummy healthcheck to test the abstract base class."""

def __init__(self, interval: datetime.timedelta, *args, **kwargs):
self.__interval = interval

@property
def interval(self) -> datetime.timedelta:
return self.__interval

async def _check(self) -> bool:
"""Always healthy."""
return True
Expand All @@ -13,6 +23,13 @@ async def _check(self) -> bool:
class DummyPrefix(Prefix):
"""A dummy prefix to test the abstract base class."""

def __init__(self, prefix: IPv4Network | IPv6Network, *args, **kwargs):
self.__prefix = prefix

@property
def prefix(self) -> IPv4Network | IPv6Network:
return self.__prefix

async def is_announced(self) -> bool:
"""Always announced."""
return True
Expand Down
8 changes: 0 additions & 8 deletions tests/prefix/test_base.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
from ipaddress import IPv4Network, IPv6Network

import pytest

from tests.dummy import DummyPrefix


def test__init___non_network_raises_type_error():
"""Passing a non network prefix raises a TypeError."""
with pytest.raises(TypeError):
DummyPrefix("2001:db8::/32") # type: ignore


def test__init__ip_network(example_networks: IPv4Network | IPv6Network):
"""IPv4Network and IPv6Network prefixes are accepted."""
DummyPrefix(example_networks)

0 comments on commit 51e96a4

Please sign in to comment.