wip: nixpkgs versions + infra network + monitoring
Signed-off-by: Jeltz <jeltz@federez.net>
This commit is contained in:
parent
01b5a0fe25
commit
a64b34810d
24 changed files with 1363 additions and 513 deletions
23
pkgs/alertbot/default.nix
Normal file
23
pkgs/alertbot/default.nix
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
lib,
|
||||
python3,
|
||||
}:
|
||||
|
||||
python3.pkgs.buildPythonApplication rec {
|
||||
pname = "alertbot";
|
||||
version = "1.0.0";
|
||||
pyproject = true;
|
||||
|
||||
disabled = python3.pythonOlder "3.12";
|
||||
|
||||
src = ./src;
|
||||
|
||||
build-system = [ python3.pkgs.hatchling ];
|
||||
|
||||
dependencies = with python3.pkgs; [ pydantic aiohttp matrix-nio jinja2 ];
|
||||
|
||||
meta = {
|
||||
description = "Alertmanager Matrix Bot";
|
||||
license = lib.licenses.agpl3Only;
|
||||
};
|
||||
}
|
1
pkgs/alertbot/src/README.md
Normal file
1
pkgs/alertbot/src/README.md
Normal file
|
@ -0,0 +1 @@
|
|||
# Alertbot
|
83
pkgs/alertbot/src/pyproject.toml
Normal file
83
pkgs/alertbot/src/pyproject.toml
Normal file
|
@ -0,0 +1,83 @@
|
|||
[project]
|
||||
name = "alertbot"
|
||||
version = "1.0.0"
|
||||
description = "Alertmanager Matrix Bot"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
license = "AGPL-3.0"
|
||||
authors = [
|
||||
{ name = "Tom Barthe", email = "tba@federez.net" },
|
||||
]
|
||||
classifiers = [
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
]
|
||||
dependencies = [
|
||||
"aiohttp",
|
||||
"pydantic >= 2.0.0",
|
||||
"matrix-nio",
|
||||
"jinja2",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project.scripts]
|
||||
alertbot = "alertbot.__main__:main"
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["src/alertbot"]
|
||||
|
||||
[tool.mypy]
|
||||
strict = true
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 79
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = [
|
||||
"F", # Pyflakes
|
||||
"E", # pycodestyle errors
|
||||
"W", # pycodestyle warnings
|
||||
"I", # isort
|
||||
"N", # pep8-naming
|
||||
# "D", # pydocstyle
|
||||
"UP", # pyupgrade
|
||||
"YTT", # flake8-2020
|
||||
"ANN", # flake8-annotations
|
||||
"ASYNC", # flake8-async
|
||||
"S", # flake8-bandit
|
||||
"BLE", # flake8-blind-except
|
||||
"A", # flake8-builtins
|
||||
"C4", # flake8-comprehensions
|
||||
"DTZ", # flake8-datetimez
|
||||
"LOG", # flak8-logging
|
||||
"G", # flak8-logging-format
|
||||
"INP", # flak8-no-pep420
|
||||
"PIE", # flak8-pie
|
||||
"PYI", # flak8-pyi
|
||||
"Q", # flak8-quotes
|
||||
"RSE", # flake8-raise
|
||||
"RET", # flake8-return
|
||||
"SLF", # flake8-self
|
||||
"SLOT", # flake8-slots
|
||||
"SIM", # flake8-simplify
|
||||
"TID", # flake8-tidy-imports
|
||||
"ARG", # flake8-unused-arguments
|
||||
"PTH", # flake8-use-pathlib
|
||||
#"TD", # flake8-todos
|
||||
"FIX", # flake8-fixme
|
||||
"ERA", # eradicate
|
||||
"PLC", # Pylint convention
|
||||
"PLE", # Pylint error
|
||||
"PLR", # Pylint refactor
|
||||
"PLW", # Pylint warning
|
||||
#"TRY", # tryceratops
|
||||
"FLY", # flynt
|
||||
"PERF", # Perflint
|
||||
"FURB", # refurb
|
||||
"RUF", # Ruff
|
||||
]
|
||||
ignore = []
|
0
pkgs/alertbot/src/src/alertbot/__init__.py
Normal file
0
pkgs/alertbot/src/src/alertbot/__init__.py
Normal file
180
pkgs/alertbot/src/src/alertbot/__main__.py
Normal file
180
pkgs/alertbot/src/src/alertbot/__main__.py
Normal file
|
@ -0,0 +1,180 @@
|
|||
import logging
|
||||
from argparse import ArgumentParser
|
||||
from asyncio import CancelledError, Queue, create_task
|
||||
from contextlib import asynccontextmanager, suppress
|
||||
from functools import cache
|
||||
from os import environ
|
||||
from pathlib import Path
|
||||
from tomllib import load
|
||||
from typing import Any
|
||||
|
||||
from aiohttp.web import AppKey, Application, Request, Response, post, run_app
|
||||
from jinja2 import Environment
|
||||
from nio import AsyncClient
|
||||
from pydantic import BaseModel
|
||||
|
||||
TEMPLATE_TEXT = (
|
||||
"{% if status == 'resolved' %}"
|
||||
"\x02\x0303RÉSOLU\x03\x02 "
|
||||
"{% elif labels.severity == 'critical' %}"
|
||||
"\x02\x0304CRITIQUE\x03\x02 "
|
||||
"{% elif labels.severity == 'warning' %}"
|
||||
"\x02\x0307ATTENTION\x03\x02 "
|
||||
"{% endif %}"
|
||||
"\x02{{ labels.alertname }}\x02"
|
||||
"{% if labels.instance is defined %} {{ labels.instance }}{% endif %}"
|
||||
"{% if annotations.summary is defined %}"
|
||||
"\n{{ annotations.summary }}"
|
||||
"{% else %}"
|
||||
"{% for key, value in annotations.items() %}"
|
||||
"\n \x02{{ key }} :\x02 {{ value }}"
|
||||
"{% endfor %}"
|
||||
"{% endif %}"
|
||||
)
|
||||
|
||||
TEMPLATE_HTML = (
|
||||
"{% if status == 'resolved' %}"
|
||||
"<b><font color='green'>RÉSOLU</font></b> "
|
||||
"{% elif labels.severity == 'critical' %}"
|
||||
"@room <b><font color='red'>CRITIQUE</font></b> "
|
||||
"{% elif labels.severity == 'warning' %}"
|
||||
"<b><font color='orange'>ATTENTION</font></b> "
|
||||
"{% endif %}"
|
||||
"<b>{{ labels.alertname }}</b>"
|
||||
"{% if labels.instance is defined %} {{ labels.instance }}{% endif %}"
|
||||
"{% if annotations.summary is defined %}"
|
||||
"<br/><blockquote>{{ annotations.summary }}</blockquote>"
|
||||
"{% else %}"
|
||||
"<br/>"
|
||||
"<blockquote>"
|
||||
"{% for key, value in annotations.items() %}"
|
||||
"<b>{{ key | capitalize }} :</b> {{ value }}<br/>"
|
||||
"{% endfor %}"
|
||||
"</ul>"
|
||||
"</blockquote>"
|
||||
"{% endif %}"
|
||||
)
|
||||
|
||||
|
||||
class MatrixConfig(BaseModel):
|
||||
homeserver: str
|
||||
user: str
|
||||
password_cred: str
|
||||
room_id: str
|
||||
|
||||
|
||||
class Config(BaseModel):
|
||||
matrix: MatrixConfig
|
||||
listen_port: int
|
||||
|
||||
|
||||
alert_queue = AppKey("alert_queue", Queue[Any])
|
||||
config = AppKey("config", Config)
|
||||
|
||||
|
||||
@cache
|
||||
def read_cred(name: str) -> str:
|
||||
creds_dir = Path(environ["CREDENTIALS_DIRECTORY"])
|
||||
return (creds_dir / name).read_text()
|
||||
|
||||
|
||||
async def handle_webhook(request: Request) -> Response:
|
||||
message = await request.json()
|
||||
alerts = message.get("alerts", [])
|
||||
logging.info("Incoming message received: %s", message)
|
||||
|
||||
for alert in alerts:
|
||||
await request.app[alert_queue].put(alert)
|
||||
|
||||
return Response()
|
||||
|
||||
|
||||
async def post_alerts(
|
||||
client: AsyncClient, queue: Queue[Any], room_id: str
|
||||
) -> None:
|
||||
env = Environment(autoescape=True)
|
||||
text = env.from_string(TEMPLATE_TEXT)
|
||||
html = env.from_string(TEMPLATE_HTML)
|
||||
|
||||
while True:
|
||||
alert = await queue.get()
|
||||
logging.info("Posting alert: %s", alert)
|
||||
try:
|
||||
await client.room_send(
|
||||
room_id,
|
||||
message_type="m.room.message",
|
||||
content={
|
||||
"msgtype": "m.text",
|
||||
"format": "org.matrix.custom.html",
|
||||
"body": text.render(**alert),
|
||||
"formatted_body": html.render(**alert),
|
||||
},
|
||||
)
|
||||
except Exception:
|
||||
logging.exception("Error while posting alert")
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def make_matrix_client(
|
||||
homeserver: str, user: str, password: str, device_name: str
|
||||
) -> AsyncClient:
|
||||
client = AsyncClient(homeserver, user)
|
||||
try:
|
||||
logging.info("Logging in to %s as %s", homeserver, user)
|
||||
await client.login(password, device_name=device_name)
|
||||
yield client
|
||||
finally:
|
||||
logging.info("Closing matrix client session")
|
||||
await client.close()
|
||||
|
||||
|
||||
async def message_ctx_cleanup(app: Application) -> None:
|
||||
homeserver = app[config].matrix.homeserver
|
||||
user = app[config].matrix.user
|
||||
password = read_cred(app[config].matrix.password_cred)
|
||||
queue = Queue()
|
||||
|
||||
async with make_matrix_client(
|
||||
homeserver, user, password, "Alertbot"
|
||||
) as client:
|
||||
task = create_task(
|
||||
post_alerts(client, queue, app[config].matrix.room_id)
|
||||
)
|
||||
app[alert_queue] = queue
|
||||
logging.info("Post alerts task created")
|
||||
|
||||
yield
|
||||
|
||||
logging.info("Cancelling post alert task")
|
||||
task.cancel()
|
||||
with suppress(CancelledError):
|
||||
await task
|
||||
|
||||
|
||||
def main() -> None:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--config",
|
||||
type=Path,
|
||||
default="config.toml",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
app = Application()
|
||||
|
||||
with args.config.open("rb") as f:
|
||||
app[config] = Config.model_validate(load(f))
|
||||
|
||||
app.add_routes([post("/webhook", handle_webhook)])
|
||||
app.cleanup_ctx.append(message_ctx_cleanup)
|
||||
|
||||
port = app[config].listen_port
|
||||
logging.info("Starting Alertbot on port %s", port)
|
||||
run_app(app, port=port, handler_cancellation=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
20
pkgs/indico/package_lock_git.patch
Normal file
20
pkgs/indico/package_lock_git.patch
Normal file
|
@ -0,0 +1,20 @@
|
|||
--- a/package-lock.json 2025-02-16 07:50:02.223758771 +0100
|
||||
+++ b/package-lock.json 2025-02-16 07:50:54.208768359 +0100
|
||||
@@ -57,7 +57,7 @@
|
||||
"process": "^0.11.10",
|
||||
"prop-types": "^15.8.1",
|
||||
"qs": "^6.11.0",
|
||||
- "qtip2": "git+https://indico@github.com/indico/qTip2.git#8951e5538a5c0833021b2d2b5d8a587a2c24faae",
|
||||
+ "qtip2": "file://@qTip2Tarball@",
|
||||
"rc-time-picker": "^3.7.3",
|
||||
"react": "^17.0.2",
|
||||
"react-charts": "2.0.0-beta.7",
|
||||
@@ -14265,7 +14265,7 @@
|
||||
},
|
||||
"node_modules/qtip2": {
|
||||
"version": "3.0.3",
|
||||
- "resolved": "git+https://indico@github.com/indico/qTip2.git#8951e5538a5c0833021b2d2b5d8a587a2c24faae",
|
||||
+ "resolved": "file://@qTip2Tarball@",
|
||||
"integrity": "sha512-U/oUhSv0FpWevgmJFbv4g2+Gl4HKcl4MmnlRSbuKVlgD+fu77Pzstw2FMOwKQMwXYmjiYJ6cMn638s6WiGuRqA==",
|
||||
"dependencies": {
|
||||
"imagesloaded": ">=3.0.0",
|
Loading…
Add table
Add a link
Reference in a new issue