improve errors for alertmanager
This commit is contained in:
parent
5d2d109da1
commit
687afe32ef
5 changed files with 37 additions and 156 deletions
|
@ -9,7 +9,7 @@ from aiohttp import ClientError
|
||||||
from diskcache import Cache
|
from diskcache import Cache
|
||||||
|
|
||||||
from matrix_alertbot.errors import (
|
from matrix_alertbot.errors import (
|
||||||
AlertmanagerError,
|
AlertmanagerServerError,
|
||||||
AlertNotFoundError,
|
AlertNotFoundError,
|
||||||
SilenceNotFoundError,
|
SilenceNotFoundError,
|
||||||
)
|
)
|
||||||
|
@ -30,7 +30,9 @@ class AlertmanagerClient:
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
return await response.json()
|
return await response.json()
|
||||||
except ClientError as e:
|
except ClientError as e:
|
||||||
raise AlertmanagerError(f"Cannot fetch alerts from Alertmanager") from e
|
raise AlertmanagerServerError(
|
||||||
|
f"Cannot fetch alerts from Alertmanager"
|
||||||
|
) from e
|
||||||
|
|
||||||
async def get_alert(self, fingerprint: str) -> Dict:
|
async def get_alert(self, fingerprint: str) -> Dict:
|
||||||
alerts = await self.get_alerts()
|
alerts = await self.get_alerts()
|
||||||
|
@ -66,7 +68,7 @@ class AlertmanagerClient:
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
data = await response.json()
|
data = await response.json()
|
||||||
except ClientError as e:
|
except ClientError as e:
|
||||||
raise AlertmanagerError(
|
raise AlertmanagerServerError(
|
||||||
f"Cannot create silence for alert fingerprint {fingerprint}"
|
f"Cannot create silence for alert fingerprint {fingerprint}"
|
||||||
) from e
|
) from e
|
||||||
|
|
||||||
|
@ -93,7 +95,9 @@ class AlertmanagerClient:
|
||||||
) as response:
|
) as response:
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
except ClientError as e:
|
except ClientError as e:
|
||||||
raise AlertmanagerError(f"Cannot delete silence with ID {silence}") from e
|
raise AlertmanagerServerError(
|
||||||
|
f"Cannot delete silence with ID {silence}"
|
||||||
|
) from e
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _find_alert(fingerprint: str, alerts: List[Dict]) -> Dict:
|
def _find_alert(fingerprint: str, alerts: List[Dict]) -> Dict:
|
||||||
|
|
|
@ -6,11 +6,7 @@ from nio import AsyncClient, MatrixRoom, RoomMessageText
|
||||||
from matrix_alertbot.alertmanager import AlertmanagerClient
|
from matrix_alertbot.alertmanager import AlertmanagerClient
|
||||||
from matrix_alertbot.chat_functions import react_to_event, send_text_to_room
|
from matrix_alertbot.chat_functions import react_to_event, send_text_to_room
|
||||||
from matrix_alertbot.config import Config
|
from matrix_alertbot.config import Config
|
||||||
from matrix_alertbot.errors import (
|
from matrix_alertbot.errors import AlertmanagerError
|
||||||
AlertmanagerError,
|
|
||||||
AlertNotFoundError,
|
|
||||||
SilenceNotFoundError,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -93,7 +89,7 @@ class Command:
|
||||||
alert_fingerprint, duration, self.room.user_name(self.event.sender)
|
alert_fingerprint, duration, self.room.user_name(self.event.sender)
|
||||||
)
|
)
|
||||||
count_created_silences += 1
|
count_created_silences += 1
|
||||||
except (AlertNotFoundError, AlertmanagerError) as e:
|
except AlertmanagerError as e:
|
||||||
logger.exception(f"Unable to create silence: {e}", exc_info=e)
|
logger.exception(f"Unable to create silence: {e}", exc_info=e)
|
||||||
|
|
||||||
await send_text_to_room(
|
await send_text_to_room(
|
||||||
|
@ -128,7 +124,7 @@ class Command:
|
||||||
alert_fingerprint
|
alert_fingerprint
|
||||||
)
|
)
|
||||||
count_removed_silences += len(removed_silences)
|
count_removed_silences += len(removed_silences)
|
||||||
except (AlertNotFoundError, SilenceNotFoundError, AlertmanagerError) as e:
|
except AlertmanagerError as e:
|
||||||
logger.exception(f"Unable to delete silence: {e}", exc_info=e)
|
logger.exception(f"Unable to delete silence: {e}", exc_info=e)
|
||||||
|
|
||||||
await send_text_to_room(
|
await send_text_to_room(
|
||||||
|
|
|
@ -1,29 +1,31 @@
|
||||||
# This file holds custom error types that you can define for your application.
|
# This file holds custom error types that you can define for your application.
|
||||||
|
|
||||||
|
|
||||||
class ConfigError(RuntimeError):
|
class ConfigError(Exception):
|
||||||
"""An error encountered during reading the config file.
|
"""An error encountered during reading the config file."""
|
||||||
|
|
||||||
Args:
|
|
||||||
msg: The message displayed to the user on error.
|
|
||||||
"""
|
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class AlertNotFoundError(RuntimeError):
|
class AlertmanagerError(Exception):
|
||||||
"""An error encountered when an alert cannot be found in database.
|
"""An error encountered with Alertmanager."""
|
||||||
|
|
||||||
Args:
|
|
||||||
msg: The message displayed to the user on error.
|
|
||||||
"""
|
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SilenceNotFoundError(RuntimeError):
|
class AlertNotFoundError(AlertmanagerError):
|
||||||
|
"""An error encountered when an alert cannot be found in Alertmanager."""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class AlertmanagerError(RuntimeError):
|
class SilenceNotFoundError(AlertmanagerError):
|
||||||
|
"""An error encountered when a silence cannot be found in Alertmanager."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AlertmanagerServerError(AlertmanagerError):
|
||||||
|
"""An error encountered with Alertmanager server."""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -13,7 +13,7 @@ from diskcache import Cache
|
||||||
|
|
||||||
from matrix_alertbot.alertmanager import AlertmanagerClient
|
from matrix_alertbot.alertmanager import AlertmanagerClient
|
||||||
from matrix_alertbot.errors import (
|
from matrix_alertbot.errors import (
|
||||||
AlertmanagerError,
|
AlertmanagerServerError,
|
||||||
AlertNotFoundError,
|
AlertNotFoundError,
|
||||||
SilenceNotFoundError,
|
SilenceNotFoundError,
|
||||||
)
|
)
|
||||||
|
@ -163,7 +163,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
f"http://localhost:{port}", self.fake_cache
|
f"http://localhost:{port}", self.fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager) as alertmanager:
|
async with aiotools.closing_async(alertmanager) as alertmanager:
|
||||||
with self.assertRaises(AlertmanagerError):
|
with self.assertRaises(AlertmanagerServerError):
|
||||||
await alertmanager.get_alerts()
|
await alertmanager.get_alerts()
|
||||||
|
|
||||||
async def test_get_alert_happy(self) -> None:
|
async def test_get_alert_happy(self) -> None:
|
||||||
|
@ -200,7 +200,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
f"http://localhost:{port}", self.fake_cache
|
f"http://localhost:{port}", self.fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager) as alertmanager:
|
async with aiotools.closing_async(alertmanager) as alertmanager:
|
||||||
with self.assertRaises(AlertmanagerError):
|
with self.assertRaises(AlertmanagerServerError):
|
||||||
await alertmanager.get_alert("fingerprint1")
|
await alertmanager.get_alert("fingerprint1")
|
||||||
|
|
||||||
async def test_create_silence_happy(self) -> None:
|
async def test_create_silence_happy(self) -> None:
|
||||||
|
@ -234,7 +234,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
async with aiotools.closing_async(alertmanager) as alertmanager:
|
async with aiotools.closing_async(alertmanager) as alertmanager:
|
||||||
await alertmanager.get_alert("fingerprint1")
|
await alertmanager.get_alert("fingerprint1")
|
||||||
|
|
||||||
with self.assertRaises(AlertmanagerError):
|
with self.assertRaises(AlertmanagerServerError):
|
||||||
await alertmanager.create_silence("fingerprint1", "1d", "user")
|
await alertmanager.create_silence("fingerprint1", "1d", "user")
|
||||||
|
|
||||||
async def test_delete_silences_happy(self) -> None:
|
async def test_delete_silences_happy(self) -> None:
|
||||||
|
@ -276,7 +276,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
async with aiotools.closing_async(alertmanager) as alertmanager:
|
async with aiotools.closing_async(alertmanager) as alertmanager:
|
||||||
await alertmanager.get_alert("fingerprint1")
|
await alertmanager.get_alert("fingerprint1")
|
||||||
|
|
||||||
with self.assertRaises(AlertmanagerError):
|
with self.assertRaises(AlertmanagerServerError):
|
||||||
await alertmanager.delete_silences("fingerprint2")
|
await alertmanager.delete_silences("fingerprint2")
|
||||||
|
|
||||||
async def test_find_alert_happy(self) -> None:
|
async def test_find_alert_happy(self) -> None:
|
||||||
|
|
|
@ -8,20 +8,16 @@ from diskcache import Cache
|
||||||
import matrix_alertbot.callback
|
import matrix_alertbot.callback
|
||||||
from matrix_alertbot.alertmanager import AlertmanagerClient
|
from matrix_alertbot.alertmanager import AlertmanagerClient
|
||||||
from matrix_alertbot.command import Command
|
from matrix_alertbot.command import Command
|
||||||
from matrix_alertbot.errors import (
|
from matrix_alertbot.errors import AlertmanagerError
|
||||||
AlertmanagerError,
|
|
||||||
AlertNotFoundError,
|
|
||||||
SilenceNotFoundError,
|
|
||||||
)
|
|
||||||
|
|
||||||
from tests.utils import make_awaitable
|
from tests.utils import make_awaitable
|
||||||
|
|
||||||
|
|
||||||
async def create_silence_raise_alert_not_found(
|
async def create_silence_raise_alert_manager_error(
|
||||||
fingerprint: str, duration: str, user: str
|
fingerprint: str, duration: str, user: str
|
||||||
) -> str:
|
) -> str:
|
||||||
if fingerprint == "fingerprint1":
|
if fingerprint == "fingerprint1":
|
||||||
raise AlertNotFoundError
|
raise AlertmanagerError
|
||||||
return "silence1"
|
return "silence1"
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,22 +29,10 @@ async def create_silence_raise_alertmanager_error(
|
||||||
return "silence2"
|
return "silence2"
|
||||||
|
|
||||||
|
|
||||||
async def delete_silence_raise_silence_not_found(fingerprint: str) -> List[str]:
|
|
||||||
if fingerprint == "fingerprint1":
|
|
||||||
raise SilenceNotFoundError
|
|
||||||
return ["silence1"]
|
|
||||||
|
|
||||||
|
|
||||||
async def delete_silence_raise_alert_not_found(fingerprint: str) -> List[str]:
|
|
||||||
if fingerprint == "fingerprint1":
|
|
||||||
raise AlertNotFoundError
|
|
||||||
return ["silence1", "silence2"]
|
|
||||||
|
|
||||||
|
|
||||||
async def delete_silence_raise_alertmanager_error(fingerprint: str) -> List[str]:
|
async def delete_silence_raise_alertmanager_error(fingerprint: str) -> List[str]:
|
||||||
if fingerprint == "fingerprint1":
|
if fingerprint == "fingerprint1":
|
||||||
raise AlertmanagerError
|
raise AlertmanagerError
|
||||||
return ["silence1", "silence2", "silence3"]
|
return ["silence1"]
|
||||||
|
|
||||||
|
|
||||||
class CommandTestCase(unittest.IsolatedAsyncioTestCase):
|
class CommandTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
@ -283,47 +267,6 @@ class CommandTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
"Created 2 silences with a duration of 2d.",
|
"Created 2 silences with a duration of 2d.",
|
||||||
)
|
)
|
||||||
|
|
||||||
@patch.object(matrix_alertbot.command, "send_text_to_room")
|
|
||||||
async def test_ack_raise_alert_not_found(
|
|
||||||
self, fake_send_text_to_room: Mock
|
|
||||||
) -> None:
|
|
||||||
"""Tests the callback for InviteMemberEvents"""
|
|
||||||
# Tests that the bot attempts to join a room after being invited to it
|
|
||||||
|
|
||||||
self.fake_message_event.source = self.fake_source_in_reply
|
|
||||||
|
|
||||||
command = Command(
|
|
||||||
self.fake_client,
|
|
||||||
self.fake_cache,
|
|
||||||
self.fake_alertmanager,
|
|
||||||
self.fake_config,
|
|
||||||
"ack",
|
|
||||||
self.fake_room,
|
|
||||||
self.fake_message_event,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.fake_alertmanager.create_silence.side_effect = (
|
|
||||||
create_silence_raise_alert_not_found
|
|
||||||
)
|
|
||||||
await command._ack()
|
|
||||||
|
|
||||||
# Check that we attempted to create silences
|
|
||||||
self.fake_alertmanager.create_silence.assert_has_calls(
|
|
||||||
[
|
|
||||||
call(
|
|
||||||
fingerprint,
|
|
||||||
"1d",
|
|
||||||
self.fake_message_event.sender,
|
|
||||||
)
|
|
||||||
for fingerprint in self.fake_fingerprints
|
|
||||||
]
|
|
||||||
)
|
|
||||||
fake_send_text_to_room.assert_called_once_with(
|
|
||||||
self.fake_client,
|
|
||||||
self.fake_room.room_id,
|
|
||||||
"Created 1 silences with a duration of 1d.",
|
|
||||||
)
|
|
||||||
|
|
||||||
@patch.object(matrix_alertbot.command, "send_text_to_room")
|
@patch.object(matrix_alertbot.command, "send_text_to_room")
|
||||||
async def test_ack_raise_alertmanager_error(
|
async def test_ack_raise_alertmanager_error(
|
||||||
self, fake_send_text_to_room: Mock
|
self, fake_send_text_to_room: Mock
|
||||||
|
@ -391,70 +334,6 @@ class CommandTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
self.fake_client, self.fake_room.room_id, "Removed 4 silences."
|
self.fake_client, self.fake_room.room_id, "Removed 4 silences."
|
||||||
)
|
)
|
||||||
|
|
||||||
@patch.object(matrix_alertbot.command, "send_text_to_room")
|
|
||||||
async def test_unack_raise_silence_not_found(
|
|
||||||
self, fake_send_text_to_room: Mock
|
|
||||||
) -> None:
|
|
||||||
"""Tests the callback for InviteMemberEvents"""
|
|
||||||
# Tests that the bot attempts to join a room after being invited to it
|
|
||||||
|
|
||||||
self.fake_message_event.source = self.fake_source_in_reply
|
|
||||||
|
|
||||||
command = Command(
|
|
||||||
self.fake_client,
|
|
||||||
self.fake_cache,
|
|
||||||
self.fake_alertmanager,
|
|
||||||
self.fake_config,
|
|
||||||
"unack",
|
|
||||||
self.fake_room,
|
|
||||||
self.fake_message_event,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.fake_alertmanager.delete_silences.side_effect = (
|
|
||||||
delete_silence_raise_silence_not_found
|
|
||||||
)
|
|
||||||
await command._unack()
|
|
||||||
|
|
||||||
# Check that we attempted to create silences
|
|
||||||
self.fake_alertmanager.delete_silences.assert_has_calls(
|
|
||||||
[call(fingerprint) for fingerprint in self.fake_fingerprints]
|
|
||||||
)
|
|
||||||
fake_send_text_to_room.assert_called_with(
|
|
||||||
self.fake_client, self.fake_room.room_id, "Removed 1 silences."
|
|
||||||
)
|
|
||||||
|
|
||||||
@patch.object(matrix_alertbot.command, "send_text_to_room")
|
|
||||||
async def test_unack_raise_alert_not_found(
|
|
||||||
self, fake_send_text_to_room: Mock
|
|
||||||
) -> None:
|
|
||||||
"""Tests the callback for InviteMemberEvents"""
|
|
||||||
# Tests that the bot attempts to join a room after being invited to it
|
|
||||||
|
|
||||||
self.fake_message_event.source = self.fake_source_in_reply
|
|
||||||
|
|
||||||
command = Command(
|
|
||||||
self.fake_client,
|
|
||||||
self.fake_cache,
|
|
||||||
self.fake_alertmanager,
|
|
||||||
self.fake_config,
|
|
||||||
"unack",
|
|
||||||
self.fake_room,
|
|
||||||
self.fake_message_event,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.fake_alertmanager.delete_silences.side_effect = (
|
|
||||||
delete_silence_raise_alert_not_found
|
|
||||||
)
|
|
||||||
await command._unack()
|
|
||||||
|
|
||||||
# Check that we attempted to create silences
|
|
||||||
self.fake_alertmanager.delete_silences.assert_has_calls(
|
|
||||||
[call(fingerprint) for fingerprint in self.fake_fingerprints]
|
|
||||||
)
|
|
||||||
fake_send_text_to_room.assert_called_with(
|
|
||||||
self.fake_client, self.fake_room.room_id, "Removed 2 silences."
|
|
||||||
)
|
|
||||||
|
|
||||||
@patch.object(matrix_alertbot.command, "send_text_to_room")
|
@patch.object(matrix_alertbot.command, "send_text_to_room")
|
||||||
async def test_unack_silence_raise_alertmanager_error(
|
async def test_unack_silence_raise_alertmanager_error(
|
||||||
self, fake_send_text_to_room: Mock
|
self, fake_send_text_to_room: Mock
|
||||||
|
@ -484,7 +363,7 @@ class CommandTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
[call(fingerprint) for fingerprint in self.fake_fingerprints]
|
[call(fingerprint) for fingerprint in self.fake_fingerprints]
|
||||||
)
|
)
|
||||||
fake_send_text_to_room.assert_called_with(
|
fake_send_text_to_room.assert_called_with(
|
||||||
self.fake_client, self.fake_room.room_id, "Removed 3 silences."
|
self.fake_client, self.fake_room.room_id, "Removed 1 silences."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue