From f4adc312154277c509f6f4a056f40e35b7870e17 Mon Sep 17 00:00:00 2001 From: Gmer4Lfe Date: Tue, 25 Aug 2026 16:51:14 -0400 Subject: [PATCH] Close the last four headers, and say what the destructive tools refuse to do --- Deployment/migrate_data_layout.sh | 55 ++++++++++++++++++++++++++++ Fallback/coverage_deploy.sh | 38 ++++++++++++++++++- Monitors/uptime_report.sh | 36 ++++++++++++++++++ Plugin/unraid/Tools/ui_map_build.php | 12 +++--- Tools/conf_container_prune.sh | 20 +++++++++- 5 files changed, 153 insertions(+), 8 deletions(-) diff --git a/Deployment/migrate_data_layout.sh b/Deployment/migrate_data_layout.sh index 5dd6e83..1e81df1 100755 --- a/Deployment/migrate_data_layout.sh +++ b/Deployment/migrate_data_layout.sh @@ -28,6 +28,42 @@ # pointing at a directory the data is not in is worse than not having started. # # ============================================================================================== +# OPERATIONAL MODEL +# ============================================================================================== +# +# Two halves, in order: move the files, then rewrite the conf keys that point at them. Doing it +# the other way round would leave every path variable naming a location nothing had reached yet, +# and any script that ran in between would create the old layout again underneath the new one. +# +# Idempotent. A path already under DATA_DIR is left alone, so a re-run after a partial migration +# finishes the job rather than moving things twice or failing on what is already done. +# +# One-time by intent, not by a marker file. There is no "already migrated" flag — the check is +# whether each individual path is already where it belongs, which is also what makes an +# interrupted run safe to repeat. +# +# ============================================================================================== +# DESIGN PRINCIPLES +# ============================================================================================== +# +# Existing keys are rewritten, which is why conf_upgrade cannot do this. +# conf_upgrade adds keys the template has and the installation does not, and never rewrites a +# value the operator already holds — correct for it, and exactly why it is the wrong tool here. +# STATE_DIR, BANDWIDTH_LOG, AI_INDEX_DB and two dozen more are existing keys whose values must +# change, or they would go on naming the old layout forever while the new directory variables +# sat beside them unused. +# +# Move, never copy-and-hope. +# The data being relocated is the only copy — statistics, histories, the AI index, arr caches. +# Everything is moved and the source is gone afterwards, so there is no second location that +# might still be written to by something that missed the change. +# +# The conf rewrite is the last thing, and the riskiest thing. +# Until it happens the installation still works from the old layout. That ordering means an +# abort partway through leaves a system that runs, rather than one whose paths point at +# nothing. +# +# ============================================================================================== # OPERATIONAL SAFEGUARDS # ============================================================================================== # @@ -49,6 +85,25 @@ # check costs nothing and the failure is silent otherwise. # # ============================================================================================== +# CONFIGURATION +# ============================================================================================== +# +# This script reads conf to find the old locations and rewrites conf to record the new ones. It +# is the one script here whose purpose is to change these values rather than obey them. +# +# Read to locate what moves +# STATE_DIR, BANDWIDTH_LOG, AI_INDEX_DB, AI_MEMORY_FILE, AI_TOKEN_DB, ARR_CLEANUP_STATS, +# ARR_SYNC_BLOCKLIST, CORRUPTION_SCAN_STATE_FILE, LIDARR_CACHE_FILE, ZFS_REPORT_LOG and the +# rest of the per-script path keys — roughly two dozen in total. +# +# Written as the new roots +# DATA_DIR and the directories beneath it: DB_DIR, STATE_DIR, AI_DATA_DIR, +# CACHE_BACKUP_DIR, ARR_CACHE_BACKUP_DIR, CONF_CACHE_BACKUP_DIR, LOG_ARCHIVE_DIR. +# +# Every rewritten value is expressed as ${DB_DIR}/… rather than an absolute path, so a later +# storage-mode migration moves them again by changing one variable. +# +# ============================================================================================== # RUNTIME MODES # ============================================================================================== # diff --git a/Fallback/coverage_deploy.sh b/Fallback/coverage_deploy.sh index 418bdd8..701ce07 100755 --- a/Fallback/coverage_deploy.sh +++ b/Fallback/coverage_deploy.sh @@ -42,7 +42,43 @@ # my-Foo.xml routinely holds a container called something else. Matching on the filename # silently pushes the wrong template, or nothing at all. # -# USAGE +# OPERATIONAL SAFEGUARDS +# Only in NORMAL state. FALLBACK_STATE_FILE is read before anything is pushed or removed, and +# any other state refuses the action. A push during a live failover would deploy a second copy +# of a container the partner is currently running on our behalf; a remove would delete the one +# doing the covering. +# +# --status is exempt from that gate, because it only reports. Refusing to answer "what is +# deployed over there" during a failover would withhold the information precisely when it is +# most wanted. +# +# Every deploy is verified stopped, and a container that will not stay stopped is an error +# rather than a warning — see DESIGN PRINCIPLES. A second live instance against the same data +# is the failure this whole script exists inside. +# +# Push and remove are explicit modes with no default. Running the script with no flag does +# nothing; neither action can be reached by accident, and neither is a side effect of editing +# the tier list. +# +# --dry-run works in every mode and touches nothing on either host — no container is built, +# started, stopped or removed, and no template is written or deleted. +# +# Remove deletes the container's appdata on the partner as well. That is deliberate and is the +# most destructive thing here; the NORMAL-state gate above is what keeps it away from a +# partner that is mid-handback. +# +# CONFIGURATION +# master.conf +# FALLBACK__TIER1..N the covered container names — what --push deploys and --status +# reports on. This script reads that list; it never edits it. +# +# host*.conf +# FALLBACK_STATE_FILE overrides where fallback.sh's state is read from. Defaults to +# STATE_DIR/fallback_state.db. A missing file reads as NORMAL, +# which is the correct default on a host where fallback has never +# run. +# +# RUNTIME MODES # coverage_deploy.sh --push deploy every covered container onto the partner (stopped) # coverage_deploy.sh --remove stop, remove, and delete the pushed template on the partner # coverage_deploy.sh --status report, per covered container, whether it exists there diff --git a/Monitors/uptime_report.sh b/Monitors/uptime_report.sh index 32eeb44..ce20c87 100755 --- a/Monitors/uptime_report.sh +++ b/Monitors/uptime_report.sh @@ -20,6 +20,42 @@ # decision to notify is the exit code rather than this script parsing the text it just printed. # # ============================================================================================== +# DESIGN PRINCIPLES +# ============================================================================================== +# +# Silence is the normal output. +# A report that always says something is a report nobody reads. A perfect week prints nothing +# and notifies nothing, so anything that does appear in the Sunday report is worth the glance. +# +# The exit code is the decision, not the text. +# uptime_probe.php --report exits 1 when it has something to say and 0 when it does not. This +# script never parses the output it just printed to work out whether to notify — a report whose +# wording changed would otherwise silently stop notifying. +# +# It reads; it never probes. +# The measurements are already taken, once a minute, by Tools/uptime_probe.sh. Re-probing at +# report time would describe Sunday morning rather than the week being reported on. +# +# ============================================================================================== +# OPERATIONAL SAFEGUARDS +# ============================================================================================== +# +# Read-only. Reads the stored history and prints; records nothing, and cannot alter the data it +# is reporting on. +# +# UPTIME_PROBE_ENABLED gates the whole run — with the probe off there is no history worth +# reporting, and this says nothing rather than reporting an empty week as a perfect one. +# +# ============================================================================================== +# RUNTIME MODES +# ============================================================================================== +# +# uptime_report.sh the weekly read. Silent when every domain was 100%. +# +# Called from COFFEE_REPORT_SCRIPTS; takes no arguments and has no other mode. For live figures +# or a per-domain table, use Tools/uptime_probe.sh --status. +# +# ============================================================================================== # CONFIGURATION # ============================================================================================== # diff --git a/Plugin/unraid/Tools/ui_map_build.php b/Plugin/unraid/Tools/ui_map_build.php index 3e43396..eab5f6a 100644 --- a/Plugin/unraid/Tools/ui_map_build.php +++ b/Plugin/unraid/Tools/ui_map_build.php @@ -5,6 +5,12 @@ // to reach it. Generated so the assistant can answer "how do I change X" with a path through // the pages instead of an instruction to open master.conf. // +// OPERATIONAL MODEL +// Reads the section registries and the conf files, resolves each setting to the page and card +// that renders it, and writes the whole map in one pass. Nothing is merged with what is +// already there — the output is derived entirely from the registries, so a stale entry cannot +// survive a rebuild. +// // DESIGN PRINCIPLES // The assistant cannot see the UI any other way. // The retrieval index reads git-tracked files. PHP body markup is not indexed and would be @@ -26,12 +32,6 @@ // with no route through the UI is a real finding, and this map is the only thing that // would ever notice. // -// OPERATIONAL MODEL -// Reads the section registries and the conf files, resolves each setting to the page and card -// that renders it, and writes the whole map in one pass. Nothing is merged with what is -// already there — the output is derived entirely from the registries, so a stale entry cannot -// survive a rebuild. -// // OPERATIONAL SAFEGUARDS // Writes exactly one file, pages/readme/ui-map.md, and nothing else. No conf is modified, no // page is touched, and the registries it reads are only read. diff --git a/Tools/conf_container_prune.sh b/Tools/conf_container_prune.sh index f2ffbec..242b0ee 100755 --- a/Tools/conf_container_prune.sh +++ b/Tools/conf_container_prune.sh @@ -23,6 +23,7 @@ # rsync would then stop nothing before copying a live database. They are excluded by name and # must stay excluded. # +# OPERATIONAL SAFEGUARDS # A docker that cannot be read is not a docker with nothing in it. # If `docker ps -a` fails, times out, or returns nothing on a host that is meant to run # containers, EVERY name looks missing and one run would strike the entire configuration. @@ -34,7 +35,24 @@ # verified by sourcing the result in a subshell and confirming the key still parses as an # array with exactly one fewer element. # -# USAGE +# Each removal is its own verified rewrite. +# Several names can reach the limit in one run, and each is removed and re-verified +# independently rather than batched into a single edit. A rewrite that fails verification +# therefore costs that one entry, not every entry the run intended to prune. +# +# --dry-run records no strike. A dry run that advanced the counter would eventually prune +# through repetition alone, which is the opposite of what it is for. +# +# CONFIGURATION +# master.conf +# CONF_PRUNE_STRIKE_LIMIT consecutive runs a name must be missing before it is removed. +# Seeing the container again resets its strike to zero immediately, +# so a rebuild costs one strike at most. +# +# The keys this may prune are an explicit allow list in the script, deliberately not a conf +# value — see DESIGN PRINCIPLES for why a pattern is the wrong shape here. +# +# RUNTIME MODES # conf_container_prune.sh strike, and prune anything at the limit # conf_container_prune.sh --dry-run report what would be struck and pruned, write nothing # conf_container_prune.sh --status show current strikes and stop