feat: absorb cloudron kuma monitoring from PFVCluster [#727]
monitoring/ = cloudron-apps.txt (55-app inventory, #435) + kuma-cloudron-sync.py (coverage checker, path re-rooted). Removed from PFVCluster in the paired commit there. Meat: https://projects.knownelement.com/issues/727#note-3913
This commit is contained in:
@@ -5,3 +5,9 @@
|
|||||||
# When issued, store the material value in ~/.creds/cloudron.env as:
|
# When issued, store the material value in ~/.creds/cloudron.env as:
|
||||||
# CLOUDRON_API_TOKEN=<token>
|
# CLOUDRON_API_TOKEN=<token>
|
||||||
CLOUDRON_API_TOKEN=
|
CLOUDRON_API_TOKEN=
|
||||||
|
# Uptime Kuma (monitoring/kuma-cloudron-sync.py) — material value in ~/.creds/uptime-kuma.env
|
||||||
|
# UPTIME_KUMA_URL=
|
||||||
|
# UPTIME_KUMA_TOKEN=
|
||||||
|
# Uptime Kuma (monitoring/kuma-cloudron-sync.py) — material value in ~/.creds/uptime-kuma.env
|
||||||
|
# UPTIME_KUMA_URL=
|
||||||
|
# UPTIME_KUMA_TOKEN=
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ Postgres/MySQL/mail containers, monitored by Uptime Kuma (Cloudron group).
|
|||||||
| Path | What |
|
| Path | What |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `perf/` | [#685](https://projects.knownelement.com/issues/685) perf work: memory census, pg role-cap SQL, applied sysctl, planned docker daemon.json |
|
| `perf/` | [#685](https://projects.knownelement.com/issues/685) perf work: memory census, pg role-cap SQL, applied sysctl, planned docker daemon.json |
|
||||||
|
| `monitoring/` | Uptime Kuma Cloudron app coverage — 55-app inventory + sync checker (moved from PFVCluster, #435/#685/#727) |
|
||||||
| `ansible/` | Reserved — AWX/Ansible IaC convergence ([#454](https://projects.knownelement.com/issues/454)); playbooks land here as manual changes are codified |
|
| `ansible/` | Reserved — AWX/Ansible IaC convergence ([#454](https://projects.knownelement.com/issues/454)); playbooks land here as manual changes are codified |
|
||||||
| `scripts/` | Rule engine + git hooks (enforcement layer, ADOPTING pattern) |
|
| `scripts/` | Rule engine + git hooks (enforcement layer, ADOPTING pattern) |
|
||||||
| `tests/` | shellcheck wrapper (Docker — host stays clean) |
|
| `tests/` | shellcheck wrapper (Docker — host stays clean) |
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
ap4ap.org
|
||||||
|
bi.knownelement.com
|
||||||
|
booking.rackrental.net
|
||||||
|
booking.reachableceo.com
|
||||||
|
booking.roguetechnologies.net
|
||||||
|
booking.startinglineproductions.com
|
||||||
|
booking.teamrental.net
|
||||||
|
booking.thepeernet.com
|
||||||
|
bookmarks.knownelement.com
|
||||||
|
broadcast.thepeernet.com
|
||||||
|
broadcast.turnsys.com
|
||||||
|
building-management.knownelement.com
|
||||||
|
chat.knownelement.com
|
||||||
|
community.turnsys.com
|
||||||
|
design.knownelement.com
|
||||||
|
erp.knownelement.com
|
||||||
|
esign.knownelement.com
|
||||||
|
ezeda.org
|
||||||
|
ezpodstack.org
|
||||||
|
finance.turnsys.com
|
||||||
|
forms.knownelement.com
|
||||||
|
git.knownelement.com
|
||||||
|
grafana.knownelement.com
|
||||||
|
helpdesk.knownelement.com
|
||||||
|
hfnfc.net
|
||||||
|
hfnoc.net
|
||||||
|
ipam.knownelement.com
|
||||||
|
jupyter.knownelement.com
|
||||||
|
knownelement.com
|
||||||
|
learn.turnsys.com
|
||||||
|
marketing.turnsys.com
|
||||||
|
matrix.turnsys.com
|
||||||
|
meetmorse.com
|
||||||
|
merchantsofhope.org
|
||||||
|
my.knownelement.com
|
||||||
|
nextcloud.knownelement.com
|
||||||
|
paperless.knownelement.com
|
||||||
|
photos.knownelement.com
|
||||||
|
phplist.knownelement.com
|
||||||
|
projects.knownelement.com
|
||||||
|
pwvault.turnsys.com
|
||||||
|
rackrental.net
|
||||||
|
reachableceo.com
|
||||||
|
readlater.knownelement.com
|
||||||
|
recordings.thepeernet.com
|
||||||
|
redwfo.com
|
||||||
|
rss.knownelement.com
|
||||||
|
rwscp.net
|
||||||
|
sidedoorgroup.org
|
||||||
|
sol-calc.com
|
||||||
|
startinglineproductions.com
|
||||||
|
staticbits.knownelement.com
|
||||||
|
status.knownelement.com
|
||||||
|
suborbital-systems.com
|
||||||
|
surveys.knownelement.com
|
||||||
|
thepeernet.com
|
||||||
|
turnsys.com
|
||||||
|
uksrs.org
|
||||||
|
url.knownelement.com
|
||||||
|
webmail.knownelement.com
|
||||||
|
webstats.knownelement.com
|
||||||
|
whiteboard.knownelement.com
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""kuma-cloudron-sync.py — Uptime Kuma Cloudron app coverage sync [#435]
|
||||||
|
|
||||||
|
Compares the committed Cloudron app list (monitoring/cloudron-apps.txt, ex-PFVCluster) against
|
||||||
|
live Kuma http monitors, reports coverage gaps, and (with --add) creates the
|
||||||
|
missing monitors INSIDE the "Cloud Systems" group (never root — founder
|
||||||
|
ruling 2026-09-02: monitors go in groups).
|
||||||
|
|
||||||
|
App list provenance: CT-log enumeration + DNS resolve + HTTPS probe across
|
||||||
|
all 23 Kuma-monaged domains (2026-09-02, see #435 note). Re-derive with
|
||||||
|
/tmp-style probe pipeline or a read-only Cloudron API diff when a token
|
||||||
|
lands. Two apps (forms/learn) return 500 today — monitored red on purpose
|
||||||
|
(#681).
|
||||||
|
|
||||||
|
Env (from ~/.creds/uptime-kuma.env):
|
||||||
|
UPTIME_KUMA_URL, UPTIME_KUMA_TOKEN
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python3 kuma-cloudron-sync.py [--add] [--limit N] [--group-id ID]
|
||||||
|
--add create missing monitors
|
||||||
|
--limit N add at most N (canary mode)
|
||||||
|
--group-id ID parent group (default: 48 = "Cloud Systems")
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import socketio
|
||||||
|
|
||||||
|
HERE = Path(__file__).resolve().parent / "cloudron-apps.txt"
|
||||||
|
DEFAULT_GROUP = 277 # "Cloudron" group (founder-directed 2026-09-02)
|
||||||
|
|
||||||
|
|
||||||
|
def load_apps():
|
||||||
|
apps = []
|
||||||
|
for line in HERE.read_text().splitlines():
|
||||||
|
line = line.split("#", 1)[0].strip()
|
||||||
|
if line:
|
||||||
|
apps.append(line)
|
||||||
|
return apps
|
||||||
|
|
||||||
|
|
||||||
|
def kuma_connect():
|
||||||
|
sio = socketio.Client(reconnection=False)
|
||||||
|
state = {}
|
||||||
|
|
||||||
|
@sio.on("monitorList")
|
||||||
|
def _ml(d):
|
||||||
|
state["ml"] = d
|
||||||
|
|
||||||
|
sio.connect(
|
||||||
|
os.environ["UPTIME_KUMA_URL"], transports=["websocket"], wait_timeout=30
|
||||||
|
)
|
||||||
|
r = sio.call("loginByToken", os.environ["UPTIME_KUMA_TOKEN"], timeout=30)
|
||||||
|
if not (isinstance(r, dict) and r.get("ok")):
|
||||||
|
raise SystemExit(f"login failed: {r}")
|
||||||
|
sio.emit("monitorList")
|
||||||
|
for _ in range(20):
|
||||||
|
if "ml" in state:
|
||||||
|
break
|
||||||
|
time.sleep(1)
|
||||||
|
return sio, state.get("ml", {})
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser()
|
||||||
|
ap.add_argument("--add", action="store_true")
|
||||||
|
ap.add_argument("--limit", type=int, default=0)
|
||||||
|
ap.add_argument("--group-id", type=int, default=DEFAULT_GROUP)
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
apps = load_apps()
|
||||||
|
sio, mons = kuma_connect()
|
||||||
|
try:
|
||||||
|
covered = set()
|
||||||
|
for m in mons.values():
|
||||||
|
name = (m.get("name") or "").strip().lower()
|
||||||
|
url = (m.get("url") or "").strip().lower()
|
||||||
|
for fqdn in apps:
|
||||||
|
if fqdn == name or url.rstrip("/") in (
|
||||||
|
f"https://{fqdn}".rstrip("/"),
|
||||||
|
f"https://{fqdn}/".rstrip("/"),
|
||||||
|
):
|
||||||
|
covered.add(fqdn)
|
||||||
|
missing = [a for a in apps if a not in covered]
|
||||||
|
|
||||||
|
print(f"apps in list: {len(apps)} covered: {len(covered)} missing: {len(missing)}")
|
||||||
|
for fqdn in missing:
|
||||||
|
print(f" MISSING {fqdn}")
|
||||||
|
|
||||||
|
if args.add and missing:
|
||||||
|
batch = missing if not args.limit else missing[: args.limit]
|
||||||
|
for fqdn in batch:
|
||||||
|
payload = {
|
||||||
|
"type": "http",
|
||||||
|
"name": fqdn,
|
||||||
|
"url": f"https://{fqdn}/",
|
||||||
|
"interval": 60,
|
||||||
|
"retryInterval": 60,
|
||||||
|
"resendInterval": 0,
|
||||||
|
"maxretries": 2,
|
||||||
|
"notificationIDList": {"1": True, "2": True},
|
||||||
|
"upsideDown": False,
|
||||||
|
"description": "Cloudron app HTTP 200 (#435)",
|
||||||
|
"httpBodyEncoding": "json",
|
||||||
|
"accepted_statuscodes": ["200-299"],
|
||||||
|
"conditions": [],
|
||||||
|
"active": True,
|
||||||
|
"parent": args.group_id,
|
||||||
|
"maxredirects": 10,
|
||||||
|
}
|
||||||
|
r = sio.call("add", payload, timeout=30)
|
||||||
|
ok = isinstance(r, dict) and r.get("ok")
|
||||||
|
print(f" ADD {fqdn}: {'id=' + str(r.get('monitorID')) if ok else r}")
|
||||||
|
finally:
|
||||||
|
sio.disconnect()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user