Compare commits

..

No commits in common. "a9748f409d42f1be80d66987f388b014a3034559" and "27441bb2d542268aa552b3413c55e0fcd68604fa" have entirely different histories.

6 changed files with 59 additions and 194 deletions

View file

@ -37,7 +37,7 @@ class Alert:
self.description = annotations["description"] self.description = annotations["description"]
if self.firing: if self.firing:
self.status = self.labels.get("severity", "unknown_alert") self.status = self.labels["severity"]
else: else:
self.status = "resolved" self.status = "resolved"
@ -55,11 +55,11 @@ class Alert:
@property @property
def emoji(self) -> str: def emoji(self) -> str:
return self.EMOJIS.get(self.status, "❓️") return self.EMOJIS[self.status]
@property @property
def color(self) -> str: def color(self) -> str:
return self.COLORS.get(self.status, "ffff0c") return self.COLORS[self.status]
def match_label(self, label_name: str, pattern: re.Pattern[str]) -> bool: def match_label(self, label_name: str, pattern: re.Pattern[str]) -> bool:
if label_name not in self.labels: if label_name not in self.labels:

View file

@ -4,14 +4,6 @@ import logging
import re import re
from diskcache import Cache from diskcache import Cache
from nio import (
AccountDataEvent,
EphemeralEvent,
Event,
PresenceEvent,
Response,
ToDeviceEvent,
)
from nio.client import AsyncClient from nio.client import AsyncClient
from nio.events import ( from nio.events import (
InviteMemberEvent, InviteMemberEvent,
@ -533,38 +525,3 @@ class Callbacks:
raise SendRetryError( raise SendRetryError(
f"{response_event.status_code} - {response_event.message}" f"{response_event.status_code} - {response_event.message}"
) )
async def debug_room_event(self, room: MatrixRoom, event: Event):
logger.debug(
f"Bot {self.matrix_client.user_id} | Room ID {room.room_id} | {type(event).__name__} | Event ID {event.event_id} | Received room event: {event.source}"
)
async def debug_presence(self, event: PresenceEvent):
logger.debug(
f"Bot {self.matrix_client.user_id} | User ID {event.user_id} | Received presence event: {event.presence}"
)
async def debug_ephemeral(self, room: MatrixRoom, event: EphemeralEvent):
logger.debug(
f"Bot {self.matrix_client.user_id} | Room ID {room.room_id} | {type(event).__name__} | Received ephemeral event: {event}"
)
async def debug_account_data(self, event: AccountDataEvent):
logger.debug(
f"Bot {self.matrix_client.user_id} | {type(event).__name__} | Received account data event: {event}"
)
async def debug_room_account_data(self, room: MatrixRoom, event: AccountDataEvent):
logger.debug(
f"Bot {self.matrix_client.user_id} | Room ID {room.room_id} | {type(event).__name__} | Received room account data event: {event}"
)
async def debug_to_device(self, event: ToDeviceEvent):
logger.debug(
f"Bot {self.matrix_client.user_id} | {type(event).__name__} | Sender {event.sender} | Received to device event: {event.source}"
)
async def debug_response(self, response: Response):
logger.debug(
f"Bot {self.matrix_client.user_id} | {type(response).__name__} | Received response: {response}"
)

View file

@ -10,16 +10,7 @@ from typing import Dict, List, Optional, Tuple
from aiohttp import ClientConnectionError, ServerDisconnectedError from aiohttp import ClientConnectionError, ServerDisconnectedError
from diskcache import Cache from diskcache import Cache
from nio import ( from nio import RoomPreset, RoomVisibility
AccountDataEvent,
EphemeralEvent,
Event,
PresenceEvent,
Response,
RoomPreset,
RoomVisibility,
ToDeviceEvent,
)
from nio.client import AsyncClient, AsyncClientConfig from nio.client import AsyncClient, AsyncClientConfig
from nio.events import ( from nio.events import (
InviteMemberEvent, InviteMemberEvent,
@ -188,21 +179,6 @@ class MatrixClientPool:
matrix_client.add_to_device_callback( matrix_client.add_to_device_callback(
callbacks.key_verification_end, (KeyVerificationMac,) callbacks.key_verification_end, (KeyVerificationMac,)
) )
matrix_client.add_event_callback(callbacks.debug_room_event, (Event,))
matrix_client.add_presence_callback(callbacks.debug_presence, (PresenceEvent,))
matrix_client.add_ephemeral_callback(
callbacks.debug_ephemeral, (EphemeralEvent,)
)
matrix_client.add_global_account_data_callback(
callbacks.debug_account_data, (AccountDataEvent,)
)
matrix_client.add_room_account_data_callback(
callbacks.debug_room_account_data, (AccountDataEvent,)
)
matrix_client.add_to_device_callback(
callbacks.debug_to_device, (ToDeviceEvent,)
)
matrix_client.add_response_callback(callbacks.debug_response, (Response,))
return matrix_client return matrix_client
@ -230,9 +206,6 @@ class MatrixClientPool:
f"Bot {account.id} | Found {len(room_members)} room members in {room_id}" f"Bot {account.id} | Found {len(room_members)} room members in {room_id}"
) )
if len(room_members) > len(self._matrix_clients) + 1:
continue
all_accounts_in_room = True all_accounts_in_room = True
for user_id in unactive_user_ids: for user_id in unactive_user_ids:
if user_id not in room_members: if user_id not in room_members:
@ -263,7 +236,7 @@ class MatrixClientPool:
) -> None: ) -> None:
async with self._lock: async with self._lock:
if matrix_client is self.matrix_client: if matrix_client is self.matrix_client:
unactive_user_ids = self.unactive_user_ids() unactive_accounts = self.unactive_user_ids()
self.dm_rooms = await self.find_existing_dm_rooms( self.dm_rooms = await self.find_existing_dm_rooms(
account=account, matrix_client=matrix_client, config=config account=account, matrix_client=matrix_client, config=config
@ -289,16 +262,13 @@ class MatrixClientPool:
logger.info( logger.info(
f"Bot {account.id} | Creating direct room with user {user_id}" f"Bot {account.id} | Creating direct room with user {user_id}"
) )
invitations = unactive_user_ids + [user_id] invitations = unactive_accounts + [user_id]
room_user_ids = invitations + [account.id]
power_levels = {"users": dict.fromkeys(room_user_ids, 100)}
create_room_response = await matrix_client.room_create( create_room_response = await matrix_client.room_create(
visibility=RoomVisibility.private, visibility=RoomVisibility.private,
name=room_title, name=room_title,
invite=invitations, invite=invitations,
is_direct=True, is_direct=True,
preset=RoomPreset.private_chat, preset=RoomPreset.private_chat,
power_level_override=power_levels,
) )
if isinstance(create_room_response, RoomCreateError): if isinstance(create_room_response, RoomCreateError):
error = create_room_response.message error = create_room_response.message

View file

@ -118,27 +118,24 @@ async def create_alerts(request: web_request.Request) -> web.Response:
body=f"An error occured with Alertmanager when handling alert with fingerprint {alert.fingerprint}.", body=f"An error occured with Alertmanager when handling alert with fingerprint {alert.fingerprint}.",
) )
except (SendRetryError, LocalProtocolError, ClientError) as e: except (SendRetryError, LocalProtocolError, ClientError) as e:
logger.exception( logger.error(
f"Unable to send alert {alert.fingerprint} to Matrix room {room_id}", f"Unable to send alert {alert.fingerprint} to Matrix room {room_id}: {e}"
exc_info=e,
) )
return web.Response( return web.Response(
status=500, status=500,
body=f"An error occured when sending alert with fingerprint '{alert.fingerprint}' to Matrix room.", body=f"An error occured when sending alert with fingerprint '{alert.fingerprint}' to Matrix room.",
) )
except MatrixClientError as e: except MatrixClientError as e:
logger.exception( logger.error(
f"Unable to send alert {alert.fingerprint} to Matrix room {room_id}", f"Unable to send alert {alert.fingerprint} to Matrix room {room_id}: {e}"
exc_info=e,
) )
return web.Response( return web.Response(
status=500, status=500,
body=f"An error occured when sending alert with fingerprint '{alert.fingerprint}' to Matrix room.", body=f"An error occured when sending alert with fingerprint '{alert.fingerprint}' to Matrix room.",
) )
except Exception as e: except Exception as e:
logger.exception( logger.error(
f"Unable to send alert {alert.fingerprint} to Matrix room {room_id}", f"Unable to send alert {alert.fingerprint} to Matrix room {room_id}: {e}"
exc_info=e,
) )
return web.Response( return web.Response(
status=500, status=500,
@ -157,28 +154,20 @@ async def create_alert(
cache: Cache = request.app["cache"] cache: Cache = request.app["cache"]
config: Config = request.app["config"] config: Config = request.app["config"]
if alert.match_all_labels(config.dm_filter_labels): if config.dm_select_label and config.dm_select_label in alert.labels:
logger.info("Found all DM filter labels in alert labels") if alert.match_all_labels(config.dm_filter_labels):
if config.dm_select_label and config.dm_select_label not in alert.labels: dm_select_value = alert.labels[config.dm_select_label]
logger.warning( if dm_select_value not in config.dm_users:
f"Dismissing alert: Cannot find select label {config.dm_select_label} in alert labels" logger.warning(
) f"Cannot find user with label {config.dm_select_label}={dm_select_value}"
return )
return
dm_select_value = alert.labels[config.dm_select_label] user_id = config.dm_users[dm_select_value]
if dm_select_value not in config.dm_users: if user_id not in matrix_client_pool.dm_rooms:
logger.warning( logger.warning(f"Cannot find a matrix room for user {user_id}")
f"Dismissing alert: Cannot find user with label {config.dm_select_label}={dm_select_value}" return
) room_id = matrix_client_pool.dm_rooms[user_id]
return
user_id = config.dm_users[dm_select_value]
if user_id not in matrix_client_pool.dm_rooms:
logger.warning(
f"Dismissing alert: Cannot find a matrix room for user {user_id}"
)
return
room_id = matrix_client_pool.dm_rooms[user_id]
if alert.firing: if alert.firing:
try: try:
@ -204,9 +193,6 @@ async def create_alert(
event = await send_text_to_room( event = await send_text_to_room(
matrix_client_pool.matrix_client, room_id, plaintext, html, notice=False matrix_client_pool.matrix_client, room_id, plaintext, html, notice=False
) )
logger.info(
f"Sent alert {alert.fingerprint} to room {room_id} with event ID {event.event_id}"
)
else: else:
raise MatrixClientError("No matrix client available") raise MatrixClientError("No matrix client available")

View file

@ -304,8 +304,8 @@ class MatrixClientPoolTestCase(unittest.IsolatedAsyncioTestCase):
self.fake_account_config_1.homeserver_url, matrix_client_1.homeserver self.fake_account_config_1.homeserver_url, matrix_client_1.homeserver
) )
self.assertEqual(self.fake_config.store_dir, matrix_client_1.store_path) self.assertEqual(self.fake_config.store_dir, matrix_client_1.store_path)
self.assertEqual(7, len(matrix_client_1.event_callbacks)) self.assertEqual(6, len(matrix_client_1.event_callbacks))
self.assertEqual(5, len(matrix_client_1.to_device_callbacks)) self.assertEqual(4, len(matrix_client_1.to_device_callbacks))
fake_async_client_config.assert_has_calls( fake_async_client_config.assert_has_calls(
[ [
@ -348,8 +348,8 @@ class MatrixClientPoolTestCase(unittest.IsolatedAsyncioTestCase):
self.fake_account_config_1.homeserver_url, matrix_client_1.homeserver self.fake_account_config_1.homeserver_url, matrix_client_1.homeserver
) )
self.assertEqual(self.fake_config.store_dir, matrix_client_1.store_path) self.assertEqual(self.fake_config.store_dir, matrix_client_1.store_path)
self.assertEqual(7, len(matrix_client_1.event_callbacks)) self.assertEqual(6, len(matrix_client_1.event_callbacks))
self.assertEqual(5, len(matrix_client_1.to_device_callbacks)) self.assertEqual(4, len(matrix_client_1.to_device_callbacks))
self.assertEqual(5, matrix_client_1.config.max_limit_exceeded) self.assertEqual(5, matrix_client_1.config.max_limit_exceeded)
self.assertEqual(3, matrix_client_1.config.max_timeouts) self.assertEqual(3, matrix_client_1.config.max_timeouts)
self.assertTrue(matrix_client_1.config.store_sync_tokens) self.assertTrue(matrix_client_1.config.store_sync_tokens)
@ -476,13 +476,6 @@ class MatrixClientPoolTestCase(unittest.IsolatedAsyncioTestCase):
invite=["@other_user:chat.example.com", "@fake_dm_user:example.com"], invite=["@other_user:chat.example.com", "@fake_dm_user:example.com"],
is_direct=True, is_direct=True,
preset=RoomPreset.private_chat, preset=RoomPreset.private_chat,
power_level_override={
"users": {
"@fake_user:matrix.example.com": 100,
"@other_user:chat.example.com": 100,
"@fake_dm_user:example.com": 100,
}
},
) )
self.assertDictEqual( self.assertDictEqual(
{ {
@ -535,13 +528,6 @@ class MatrixClientPoolTestCase(unittest.IsolatedAsyncioTestCase):
invite=["@other_user:chat.example.com", "@fake_dm_user:example.com"], invite=["@other_user:chat.example.com", "@fake_dm_user:example.com"],
is_direct=True, is_direct=True,
preset=RoomPreset.private_chat, preset=RoomPreset.private_chat,
power_level_override={
"users": {
"@fake_user:matrix.example.com": 100,
"@other_user:chat.example.com": 100,
"@fake_dm_user:example.com": 100,
}
},
) )
self.assertDictEqual( self.assertDictEqual(
{ {
@ -595,13 +581,6 @@ class MatrixClientPoolTestCase(unittest.IsolatedAsyncioTestCase):
invite=["@other_user:chat.example.com", "@fake_dm_user:example.com"], invite=["@other_user:chat.example.com", "@fake_dm_user:example.com"],
is_direct=True, is_direct=True,
preset=RoomPreset.private_chat, preset=RoomPreset.private_chat,
power_level_override={
"users": {
"@fake_user:matrix.example.com": 100,
"@other_user:chat.example.com": 100,
"@fake_dm_user:example.com": 100,
}
},
) )
self.assertDictEqual( self.assertDictEqual(
{ {
@ -652,13 +631,6 @@ class MatrixClientPoolTestCase(unittest.IsolatedAsyncioTestCase):
invite=["@other_user:chat.example.com", "@fake_dm_user:example.com"], invite=["@other_user:chat.example.com", "@fake_dm_user:example.com"],
is_direct=True, is_direct=True,
preset=RoomPreset.private_chat, preset=RoomPreset.private_chat,
power_level_override={
"users": {
"@fake_user:matrix.example.com": 100,
"@other_user:chat.example.com": 100,
"@fake_dm_user:example.com": 100,
}
},
) )
self.assertDictEqual( self.assertDictEqual(
{ {

View file

@ -56,7 +56,7 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
self.fake_config.cache_expire_time = 0 self.fake_config.cache_expire_time = 0
self.fake_config.template_dir = None self.fake_config.template_dir = None
self.fake_config.dm_select_label = "uuid" self.fake_config.dm_select_label = "uuid"
self.fake_config.dm_filter_labels = {"matrix": re.compile("dm")} self.fake_config.dm_filter_labels = {"matrix-alertbot": re.compile("dm")}
self.fake_config.dm_users = BiDict( self.fake_config.dm_users = BiDict(
{"a7b37c33-574c-45ac-bb07-a3b314c2da54": "@fake_dm_user:example.com"} {"a7b37c33-574c-45ac-bb07-a3b314c2da54": "@fake_dm_user:example.com"}
) )
@ -107,7 +107,7 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
"severity": "warning", "severity": "warning",
"job": "job", "job": "job",
"uuid": "a7b37c33-574c-45ac-bb07-a3b314c2da54", "uuid": "a7b37c33-574c-45ac-bb07-a3b314c2da54",
"matrix": "dm", "matrix-alertbot": "dm",
}, },
"annotations": {"description": "some description"}, "annotations": {"description": "some description"},
} }
@ -159,8 +159,7 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
"some description2", "some description2",
notice=False, notice=False,
), ),
], ]
any_order=True,
) )
self.fake_cache.set.assert_called_once_with( self.fake_cache.set.assert_called_once_with(
fake_send_text_to_room.return_value.event_id, fake_send_text_to_room.return_value.event_id,
@ -210,8 +209,7 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
"some description2", "some description2",
notice=False, notice=False,
), ),
], ]
any_order=True,
) )
self.fake_cache.set.assert_called_once_with( self.fake_cache.set.assert_called_once_with(
fake_send_text_to_room.return_value.event_id, fake_send_text_to_room.return_value.event_id,
@ -452,9 +450,8 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
self.fake_cache.set.assert_not_called() self.fake_cache.set.assert_not_called()
self.fake_cache.delete.assert_called_once_with("fingerprint1") self.fake_cache.delete.assert_called_once_with("fingerprint1")
fake_logger.exception.assert_called_once_with( fake_logger.error.assert_called_once_with(
"Unable to send alert fingerprint1 to Matrix room !abcdefg:example.com", "Unable to send alert fingerprint1 to Matrix room !abcdefg:example.com: Local protocol error"
exc_info=fake_send_text_to_room.side_effect,
) )
@patch.object(matrix_alertbot.webhook, "logger", autospec=True) @patch.object(matrix_alertbot.webhook, "logger", autospec=True)
@ -483,9 +480,8 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
) )
fake_create_alert.assert_called_once() fake_create_alert.assert_called_once()
fake_logger.exception.assert_called_once_with( fake_logger.error.assert_called_once_with(
"Unable to send alert fingerprint1 to Matrix room !abcdefg:example.com", "Unable to send alert fingerprint1 to Matrix room !abcdefg:example.com: Matrix client error"
exc_info=fake_create_alert.side_effect,
) )
@patch.object(matrix_alertbot.webhook, "logger", autospec=True) @patch.object(matrix_alertbot.webhook, "logger", autospec=True)
@ -516,19 +512,15 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
self.fake_cache.set.assert_not_called() self.fake_cache.set.assert_not_called()
self.fake_cache.delete.assert_called_once_with("fingerprint1") self.fake_cache.delete.assert_called_once_with("fingerprint1")
fake_logger.exception.assert_called_once_with( fake_logger.error.assert_called_once_with(
"Unable to send alert fingerprint1 to Matrix room !abcdefg:example.com", "Unable to send alert fingerprint1 to Matrix room !abcdefg:example.com: Exception"
exc_info=fake_send_text_to_room.side_effect,
) )
async def test_create_alert_update_silence(self) -> None: async def test_create_alert_update_silence(self) -> None:
fake_alert = Alert( fake_alert = Mock(spec=Alert)
fingerprint="fingerprint", fake_alert.firing = True
url="https://example.com", fake_alert.fingerprint = "fingerprint"
firing=True, fake_alert.labels = []
labels={"severity": "critical"},
annotations={"description": "dummy description"},
)
await create_alert(fake_alert, self.fake_room_id, self.fake_request) await create_alert(fake_alert, self.fake_room_id, self.fake_request)
@ -541,13 +533,10 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
async def test_create_alert_with_silence_not_found_error( async def test_create_alert_with_silence_not_found_error(
self, fake_send_text_to_room: Mock self, fake_send_text_to_room: Mock
) -> None: ) -> None:
fake_alert = Alert( fake_alert = Mock(spec=Alert)
fingerprint="fingerprint", fake_alert.firing = True
url="https://example.com", fake_alert.fingerprint = "fingerprint"
firing=True, fake_alert.labels = []
labels={"severity": "critical"},
annotations={"description": "dummy description"},
)
self.fake_alertmanager_client.update_silence.side_effect = SilenceNotFoundError self.fake_alertmanager_client.update_silence.side_effect = SilenceNotFoundError
@ -573,13 +562,10 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
async def test_create_alert_with_silence_extend_error( async def test_create_alert_with_silence_extend_error(
self, fake_send_text_to_room: Mock self, fake_send_text_to_room: Mock
) -> None: ) -> None:
fake_alert = Alert( fake_alert = Mock(spec=Alert)
fingerprint="fingerprint", fake_alert.firing = True
url="https://example.com", fake_alert.fingerprint = "fingerprint"
firing=True, fake_alert.labels = []
labels={"severity": "critical"},
annotations={"description": "dummy description"},
)
self.fake_alertmanager_client.update_silence.side_effect = SilenceExtendError self.fake_alertmanager_client.update_silence.side_effect = SilenceExtendError
@ -603,13 +589,10 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
@patch.object(matrix_alertbot.webhook, "send_text_to_room", autospec=True) @patch.object(matrix_alertbot.webhook, "send_text_to_room", autospec=True)
async def test_create_alert_not_firing(self, fake_send_text_to_room: Mock) -> None: async def test_create_alert_not_firing(self, fake_send_text_to_room: Mock) -> None:
fake_alert = Alert( fake_alert = Mock(spec=Alert)
fingerprint="fingerprint", fake_alert.firing = False
url="https://example.com", fake_alert.fingerprint = "fingerprint"
firing=False, fake_alert.labels = []
labels={},
annotations={"description": "dummy description"},
)
await create_alert(fake_alert, self.fake_room_id, self.fake_request) await create_alert(fake_alert, self.fake_room_id, self.fake_request)
@ -627,13 +610,10 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
async def test_create_alert_not_firing_raise_matrix_client_error( async def test_create_alert_not_firing_raise_matrix_client_error(
self, fake_send_text_to_room: Mock self, fake_send_text_to_room: Mock
) -> None: ) -> None:
fake_alert = Alert( fake_alert = Mock(spec=Alert)
fingerprint="fingerprint", fake_alert.firing = False
url="https://example.com", fake_alert.fingerprint = "fingerprint"
firing=False, fake_alert.labels = []
labels={},
annotations={"description": "dummy description"},
)
self.fake_matrix_client_pool.matrix_client = None self.fake_matrix_client_pool.matrix_client = None