mirror of
https://forgejo.altau.su/lego/lego-monitoring.git
synced 2026-03-10 04:41:10 +00:00
prepare config for healthchecks integration
This commit is contained in:
parent
4558cf9e6f
commit
c01ab8303c
8 changed files with 89 additions and 34 deletions
|
|
@ -31,9 +31,15 @@ package:
|
||||||
serviceConfigFile = json.generate "config.json" {
|
serviceConfigFile = json.generate "config.json" {
|
||||||
enabled_check_sets = cfg.enabledCheckSets;
|
enabled_check_sets = cfg.enabledCheckSets;
|
||||||
log_level = cfg.logLevel;
|
log_level = cfg.logLevel;
|
||||||
telegram = with cfg.telegram; {
|
alert_channels = {
|
||||||
|
telegram = with cfg.alertChannels.telegram; if enable then
|
||||||
|
{
|
||||||
creds_secret_path = credsSecretPath;
|
creds_secret_path = credsSecretPath;
|
||||||
room_id = roomId;
|
room_id = roomId;
|
||||||
|
} else null;
|
||||||
|
healthchecks = with cfg.alertChannels.healthchecks; if enable then {
|
||||||
|
pinging_keys_secret_path = pingingKeysSecretPath;
|
||||||
|
} else null;
|
||||||
};
|
};
|
||||||
checks = {
|
checks = {
|
||||||
temp.sensors = lib.mapAttrs (_: sensorCfg: {
|
temp.sensors = lib.mapAttrs (_: sensorCfg: {
|
||||||
|
|
|
||||||
|
|
@ -50,16 +50,34 @@ in
|
||||||
* vulnix -- periodically scans system for known CVEs, alerts if any are found (NixOS only)'';
|
* vulnix -- periodically scans system for known CVEs, alerts if any are found (NixOS only)'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
alertChannels = {
|
||||||
telegram = {
|
telegram = {
|
||||||
|
enable = lib.mkEnableOption "Telegram notification channel";
|
||||||
credsSecretPath = lib.mkOption {
|
credsSecretPath = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
|
default = "";
|
||||||
description = "Path to a file containing Telegram api_id, api_hash, and bot token, separated by the `,` character.";
|
description = "Path to a file containing Telegram api_id, api_hash, and bot token, separated by the `,` character.";
|
||||||
};
|
};
|
||||||
roomId = lib.mkOption {
|
roomId = lib.mkOption {
|
||||||
type = lib.types.int;
|
type = lib.types.int;
|
||||||
|
default = 0;
|
||||||
description = "ID of chat where to send alerts.";
|
description = "ID of chat where to send alerts.";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
healthchecks = {
|
||||||
|
enable = lib.mkEnableOption "[Healthchecks](https://healthchecks.io) notification channel";
|
||||||
|
pingingKeysSecretPath = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "";
|
||||||
|
description = ''
|
||||||
|
Path to a file containing the pinging keys in a `slug:key` format, one on each line (ex: `lego-cpu:aaaaaaaaaaaaaaaaaaaaaa`).
|
||||||
|
Specify `default` as the slug to use this key for check types that don't have a key explicitly assigned to them.
|
||||||
|
|
||||||
|
If you are unsure of the exact slug a check will generate, it is recommended to try it out with the default key first, before
|
||||||
|
assigning a specific one.'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
checks = {
|
checks = {
|
||||||
temp = {
|
temp = {
|
||||||
|
|
|
||||||
|
|
@ -49,11 +49,6 @@ async def async_main():
|
||||||
|
|
||||||
logging.basicConfig(level=config.log_level)
|
logging.basicConfig(level=config.log_level)
|
||||||
|
|
||||||
tg_client = await sender.get_client()
|
|
||||||
|
|
||||||
cvars.tg_client.set(tg_client)
|
|
||||||
my_username = (await tg_client.get_me()).username
|
|
||||||
logging.info(f"Logged in as @{my_username}")
|
|
||||||
check_sets = config_enums.CheckSet
|
check_sets = config_enums.CheckSet
|
||||||
|
|
||||||
checker_sets: dict[config_enums.CheckSet, list[Coroutine | BaseChecker]] = {
|
checker_sets: dict[config_enums.CheckSet, list[Coroutine | BaseChecker]] = {
|
||||||
|
|
@ -94,8 +89,18 @@ async def async_main():
|
||||||
|
|
||||||
checker_sets[check_sets.REMIND][0].check_args = [checkers]
|
checker_sets[check_sets.REMIND][0].check_args = [checkers]
|
||||||
|
|
||||||
|
if config.alert_channels.telegram is not None:
|
||||||
|
tg_client = await sender.get_client()
|
||||||
|
my_username = (await tg_client.get_me()).username
|
||||||
|
logging.info(f"Logged in as @{my_username}")
|
||||||
|
|
||||||
command_manager = CommandHandlerManager(checkers)
|
command_manager = CommandHandlerManager(checkers)
|
||||||
await command_manager.attach_handlers(tg_client)
|
await command_manager.attach_handlers(tg_client)
|
||||||
|
else:
|
||||||
|
logging.info("Telegram integration is disabled")
|
||||||
|
tg_client = None
|
||||||
|
|
||||||
|
cvars.tg_client.set(tg_client)
|
||||||
|
|
||||||
signal.signal(signal.SIGTERM, stop_gracefully)
|
signal.signal(signal.SIGTERM, stop_gracefully)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ from .sender import format_message
|
||||||
def admin_chat_only(
|
def admin_chat_only(
|
||||||
handler: Callable[[events.NewMessage.Event], Awaitable[None]],
|
handler: Callable[[events.NewMessage.Event], Awaitable[None]],
|
||||||
) -> Callable[[events.NewMessage.Event], Awaitable[None]]:
|
) -> Callable[[events.NewMessage.Event], Awaitable[None]]:
|
||||||
admin_room_id = cvars.config.get().telegram.room_id
|
admin_room_id = cvars.config.get().alert_channels.telegram.room_id
|
||||||
|
|
||||||
async def safe_handler(event: events.NewMessage.Event) -> None:
|
async def safe_handler(event: events.NewMessage.Event) -> None:
|
||||||
if event.chat_id == admin_room_id:
|
if event.chat_id == admin_room_id:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
from dataclasses import dataclass
|
|
||||||
|
|
||||||
from telethon import TelegramClient
|
from telethon import TelegramClient
|
||||||
from telethon.sessions import MemorySession
|
from telethon.sessions import MemorySession
|
||||||
|
|
||||||
|
|
@ -10,7 +8,7 @@ from .enum import SEVERITY_TO_EMOJI, AlertType, Severity
|
||||||
|
|
||||||
async def get_client() -> TelegramClient:
|
async def get_client() -> TelegramClient:
|
||||||
config = cvars.config.get()
|
config = cvars.config.get()
|
||||||
api_id, api_hash, bot_token = config.telegram.creds.split(",")
|
api_id, api_hash, bot_token = config.alert_channels.telegram.creds.split(",")
|
||||||
client = await TelegramClient(MemorySession(), api_id, api_hash, connection_retries=None).start(bot_token=bot_token)
|
client = await TelegramClient(MemorySession(), api_id, api_hash, connection_retries=None).start(bot_token=bot_token)
|
||||||
client.parse_mode = "html"
|
client.parse_mode = "html"
|
||||||
return client
|
return client
|
||||||
|
|
@ -38,13 +36,17 @@ async def send_alert(alert: Alert, note: str = "") -> None:
|
||||||
raise NotImplementedError # TODO
|
raise NotImplementedError # TODO
|
||||||
else:
|
else:
|
||||||
... # temp_client = False
|
... # temp_client = False
|
||||||
room_id = cvars.config.get().telegram.room_id
|
if client is not None:
|
||||||
|
room_id = cvars.config.get().alert_channels.telegram.room_id
|
||||||
message = format_message(alert, note)
|
message = format_message(alert, note)
|
||||||
await client.send_message(entity=room_id, message=message)
|
await client.send_message(entity=room_id, message=message)
|
||||||
# if temp_client:
|
# if temp_client:
|
||||||
# await client.close()
|
# await client.close()
|
||||||
|
# TODO ping healthchecks if enabled
|
||||||
|
|
||||||
|
|
||||||
|
# TODO service itself has to be monitored like everything else - with regular pinging - if we're
|
||||||
|
# using healthchecks
|
||||||
async def send_start_alert() -> None:
|
async def send_start_alert() -> None:
|
||||||
config = cvars.config.get()
|
config = cvars.config.get()
|
||||||
await send_alert(
|
await send_alert(
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ from typing import Optional
|
||||||
from alt_utils import NestedDeserializableDataclass
|
from alt_utils import NestedDeserializableDataclass
|
||||||
|
|
||||||
from . import enums
|
from . import enums
|
||||||
|
from .alert_channels import AlertChannelsConfig
|
||||||
from .checks.cpu import CpuCheckConfig
|
from .checks.cpu import CpuCheckConfig
|
||||||
from .checks.net import NetCheckConfig
|
from .checks.net import NetCheckConfig
|
||||||
from .checks.ram import RamCheckConfig
|
from .checks.ram import RamCheckConfig
|
||||||
|
|
@ -21,16 +22,10 @@ class ChecksConfig(NestedDeserializableDataclass):
|
||||||
net: NetCheckConfig = field(default_factory=NetCheckConfig)
|
net: NetCheckConfig = field(default_factory=NetCheckConfig)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class TelegramConfig:
|
|
||||||
creds: str
|
|
||||||
room_id: int
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Config(NestedDeserializableDataclass):
|
class Config(NestedDeserializableDataclass):
|
||||||
checks: ChecksConfig
|
checks: ChecksConfig
|
||||||
telegram: TelegramConfig
|
alert_channels: AlertChannelsConfig
|
||||||
enabled_check_sets: list[enums.CheckSet] = field(default_factory=list)
|
enabled_check_sets: list[enums.CheckSet] = field(default_factory=list)
|
||||||
log_level: enums.LogLevelName = enums.LogLevelName.INFO
|
log_level: enums.LogLevelName = enums.LogLevelName.INFO
|
||||||
|
|
||||||
|
|
|
||||||
28
src/lego_monitoring/config/alert_channels.py
Normal file
28
src/lego_monitoring/config/alert_channels.py
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from alt_utils import NestedDeserializableDataclass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TelegramConfig:
|
||||||
|
creds: str
|
||||||
|
room_id: int
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class HealthchecksConfig:
|
||||||
|
pinging_keys: str | dict[str, str]
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
lines = self.pinging_keys.split()
|
||||||
|
self.pinging_keys = {}
|
||||||
|
for l in lines:
|
||||||
|
slug, key = l.split(":")
|
||||||
|
self.pinging_keys[slug] = key
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AlertChannelsConfig(NestedDeserializableDataclass):
|
||||||
|
telegram: Optional[TelegramConfig] = None
|
||||||
|
healthchecks: Optional[HealthchecksConfig] = None
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
from contextvars import ContextVar
|
from contextvars import ContextVar
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from telethon import TelegramClient
|
from telethon import TelegramClient
|
||||||
|
|
||||||
|
|
@ -7,5 +8,5 @@ from lego_monitoring.alerting.current import CurrentAlerts
|
||||||
from ..config import Config
|
from ..config import Config
|
||||||
|
|
||||||
config: ContextVar[Config] = ContextVar("config")
|
config: ContextVar[Config] = ContextVar("config")
|
||||||
tg_client: ContextVar[TelegramClient] = ContextVar("tg_client")
|
tg_client: ContextVar[Optional[TelegramClient]] = ContextVar("tg_client")
|
||||||
current_alerts: ContextVar[list[CurrentAlerts]] = ContextVar("current_alerts", default=[])
|
current_alerts: ContextVar[list[CurrentAlerts]] = ContextVar("current_alerts", default=[])
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue