hub 0.167.0 + DooPlex push: the hub DB snapshot every 6 hours (D34, operator yes 2026-10-23, R-232 G5) — slots 02/08/14/20, catch-up 6 h, push :30 after each, refuse a copy older than 2 h; tests red-proved
gates / gates (push) Successful in 16m20s

This commit is contained in:
2026-10-23 09:12:08 +02:00
parent 83388c088a
commit be7e97dc1a
8 changed files with 111 additions and 28 deletions
+4 -3
View File
@@ -1,6 +1,7 @@
#!/bin/sh
# felhom-hub-db-backup — push the hub's newest nightly snapshot to ep0's PBS, encrypted (R-173, decision A).
# Runs on DooPlex as root from felhom-hub-db-backup.timer (02:30; the hub writes the snapshot at 02:00).
# felhom-hub-db-backup — push the hub's newest snapshot to ep0's PBS, encrypted (R-173, decision A).
# Runs on DooPlex as root from felhom-hub-db-backup.timer (02:30, 08:30, 14:30, 20:30; the hub writes a snapshot at
# 02:00, 08:00, 14:00 and 20:00 since hub 0.167.0 — D34).
# Runbook: documentation/runbooks/RUNBOOK-hub-db-offsite-backup.md Step 4. Pinned by test_hub_db_backup.py.
#
# Refuses to push — and so never writes the success signal — when: no snapshot exists; the newest is older than
@@ -11,7 +12,7 @@ set -eu
CONF=${FELHOM_HUBBK_CONF:-/etc/felhom-hub-backup}
STATE=${FELHOM_HUBBK_STATE:-/var/lib/felhom-hub-backup}
TEXTFILE_DIR=${FELHOM_HUBBK_TEXTFILE_DIR:-/var/lib/node_exporter/textfile_collector}
MAX_AGE_H=${FELHOM_HUBBK_MAX_AGE_H:-26}
MAX_AGE_H=${FELHOM_HUBBK_MAX_AGE_H:-2} # D34: the newest is ~30 min old at each push; 2 h refuses a missed slot
NOW=${FELHOM_HUBBK_NOW:-$(date +%s)}
. "$CONF/env" # PBS_REPOSITORY_PUSH, PBS_FINGERPRINT (no secrets in this file)
@@ -1,9 +1,13 @@
# Versioned in felhom.eu/scripts/hub-db-backup/ (R-231); installed by install.sh.
[Unit]
Description=Felhom: push the hub DB snapshot to ep0 (R-173) — schedule
Description=Felhom: push the hub DB snapshot to ep0 (R-173, D34) — every 6 hours
[Timer]
# D34 (operator yes 2026-10-23): every 6 h, :30 after each hub snapshot slot (hub dbsnap.Slots 02/08/14/20:00).
OnCalendar=*-*-* 02:30:00
OnCalendar=*-*-* 08:30:00
OnCalendar=*-*-* 14:30:00
OnCalendar=*-*-* 20:30:00
Persistent=true
RandomizedDelaySec=2min
+18 -1
View File
@@ -8,6 +8,7 @@ was written. Run: python3 scripts/hub-db-backup/test_hub_db_backup.py
"""
import json
import os
import re
import shutil
import sqlite3
import stat
@@ -184,6 +185,15 @@ class Push(Base):
self.assertEqual(self.pushed(), [])
self.assertFalse(self.signal("felhom_hub_db_backup.prom"))
def test_d34_a_missed_slot_is_not_pushed_again(self):
# D34: snapshots every 6 h, so a newest copy 3 h old means the hub missed a slot — refuse, as for a day
self.snapshot(age_s=3 * 3600)
r = self.run_script(PUSH)
self.assertNotEqual(r.returncode, 0)
self.assertIn("stopped snapshotting", r.stderr)
self.assertEqual(self.pushed(), [])
self.assertFalse(self.signal("felhom_hub_db_backup.prom"))
def test_truncated_copy_is_not_pushed(self):
self.snapshot()
r = self.run_script(PUSH, FAKE_TRUNCATE="1")
@@ -268,7 +278,14 @@ class Units(unittest.TestCase):
return open(os.path.join(HERE, n)).read()
def test_schedules(self):
self.assertIn("OnCalendar=*-*-* 02:30:00", self.read("felhom-hub-db-backup.timer"))
# D34: the push runs :30 after EVERY hub snapshot slot — read from the hub's own source, so a slot moved on
# one side only fails here (RED-PROOF: drop one OnCalendar line, or change a hub slot → fails).
src = open(os.path.join(HERE, "..", "..", "hub", "internal", "dbsnap", "dbsnap.go")).read()
slots = re.findall(r'"(\d\d):(\d\d)"', re.search(r"var Slots = \[\]string\{([^}]*)\}", src).group(1))
self.assertEqual(len(slots), 4)
want = sorted("OnCalendar=*-*-* %02d:%02d:00" % (int(h), int(m) + 30) for h, m in slots)
got = sorted(l.strip() for l in self.read("felhom-hub-db-backup.timer").splitlines() if l.startswith("OnCalendar="))
self.assertEqual(got, want)
self.assertIn("OnCalendar=Sun *-*-* 04:30:00", self.read("felhom-hub-db-restore-test.timer"))
def test_units_run_the_installed_scripts(self):