From 7162f489c935d4786fc80c2ca3a08f6a269c7e3c Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Fri, 25 Sep 2026 13:30:08 +0200 Subject: [PATCH] README + REUSE: the PostgreSQL conversion (v0.273.0) Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS --- REUSE.md | 1 + controller/README.md | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/REUSE.md b/REUSE.md index 961cbb8..37165ad 100644 --- a/REUSE.md +++ b/REUSE.md @@ -303,6 +303,7 @@ | `offboxCeremonyWaitState` + `escrowCeremonyGraceWindow` | controller/internal/web/handlers.go | pure pick: (awaiting, timedOut) from `OffboxTarget.{EscrowState,CeremonyCompletedAt}` — the v0.138.0 "megerősítésre vár" card. Stamp SET on claim (escrow_handlers.go), CLEARED on the flip (main.go Flip + offbox_handlers.go manual confirm) | escrow_wait_state_test.go truth table (escrowed/unstamped/unparseable → plain CTA; boundary via `>=`) | | `Manager.sambaUpFn` / `sambaPasswdFn` / `sambaRunFn` / `sambaAddrFn` (func seams) | controller/internal/stacks/manager.go (fields) + samba.go | nil → `composeUp` / `docker exec smbpasswd` (STDIN) / `containerRunning("felhom-samba")` / `docker exec felhom-samba ip -4 -o addr show eth0` | injected in controller/internal/stacks/samba_test.go — the idempotency test asserts the up-seam is called **zero** times when config is unchanged; the passwd seam means no unit test ever handles a real secret or touches docker. **`sambaRunFn` has an EXPORTED setter (`SetSambaRunProbe`)** — internal/web's status-contract tests need a live-container world from another package. `sambaAddrFn` backs `SambaLANAddress()` (v0.151.0); its parse is separately pinned in samba_lanaddr_test.go and it returns "" on any failure — the page omits a line rather than printing a wrong address | | `volumeCopier` + `Manager.undoCopier` / `updateUndoHealthFn` (v0.263.0) | controller/internal/stacks/undo.go | nil → `dockerVolumeCopier` (alpine helper: `cp -a` named volume → `.pre-update-`, finished-marker LAST; restore re-checks the marker in the same shell) / nil → `waitUpdateHealthyMeta` with the OLD `.felhom.yml` | `fakeCopier` in controller/internal/stacks/undo_test.go — volume CONTENT as strings, so "the data came back" is a compare; a cut-off copy is a copy without its marker. **Judge a copy by the helper's own exit + the marker, never by the client** (killing `docker run` leaves the container copying — measured) | +| `pgConverter` + `Manager.pgConv` / `convertFreeFn` (v0.273.0) | controller/internal/stacks/pgconvert.go | nil → `dockerPGConverter` (docker exec psql/pg_dumpall over 127.0.0.1 — the entrypoint's temporary init server listens on the socket only; `Empty` re-checks the undo copy's marker in the same helper) / nil → statfs of the stack dir | `fakePG` in controller/internal/stacks/pgconvert_test.go — works on `fakeCopier`'s volume strings. **Never convert without the ladder's `engine_conversion` mark** (`planEngineConversion` refuses a PostgreSQL major move without it); `isPostgresImage` must match `appbackup.dbTypeForImage` (pinned by a source-reading test) | | `Manager.SambaLANAddress()` | controller/internal/stacks/samba.go | `() string` — the guest's LAN IPv4 for the Megosztás connect card (v0.151.0, S-2) | Read from the SAMBA container's netns (`network_mode: host`), never `net.InterfaceAddrs()` — the controller is on a docker BRIDGE and would answer 172.x (the same trap `setup.DetectLocalIPs` needs `HOST_IP` for). **NEVER cache/persist it** — the guest holds it by DHCP (S-5); callers re-derive per render. `""` = omit the line | | `Server.sambaAddrFn` (func seam) | controller/internal/web/server.go (field) + sharing_handlers.go `sambaLANAddress()` | nil → `stackMgr.SambaLANAddress()` | The web-side half of the connect card. Tests inject a COUNTED fn — the fresh-per-render assertion is what stops anyone memoizing a DHCP lease | | `Manager.guestNetExecFn` (func seam) + `GuestGateway()` / `GuestNetSnapshot()` | controller/internal/stacks/manager.go (field) + guestnet.go | nil → `docker exec felhom-samba ` — ONE seam for all R-66 guest-netns reads (route/link/addr/resolv.conf); tests script canned outputs per argv | guestnet_test.go. **The netns door rule:** the controller's OWN netns is the docker bridge, so any in-process read (`net.Interfaces`, `/proc/net/route`, its own `/etc/resolv.conf` = 127.0.0.11) is the S-2 wrong answer — guest-net reads MUST go through the samba (`network_mode: host`) exec door. Megosztás off ⇒ door closed ⇒ "" / per-item error strings; NEVER substitute an in-process value. Same S-5 law as SambaLANAddress: live per render, never cached/persisted. Parsers (`parseDefaultRoute`, `parseGuestInterfaces`, `parseResolvConf`) are pure + separately pinned | diff --git a/controller/README.md b/controller/README.md index 5d21b54..10fc6d0 100644 --- a/controller/README.md +++ b/controller/README.md @@ -577,7 +577,7 @@ job, and answers **202**. The page polls `GET /api/stacks/{name}`. | field | meaning | |---|---| | `updating` | a guarded update is in progress | -| `update_phase` / `update_phase_label` | `checking`, `backing-up`, `safety-dump`, `pinning`, `pulling`, `copying` (v0.263.0), `starting`, `verifying`, `done`, `undoing` / `undone` (v0.263.0), `failed` — and the Hungarian label for each | +| `update_phase` / `update_phase_label` | `checking`, `backing-up`, `safety-dump`, `pinning`, `pulling`, `copying` (v0.263.0), `converting` (v0.273.0, a PostgreSQL major step only), `starting`, `verifying`, `done`, `undoing` / `undone` (v0.263.0), `failed` — and the Hungarian label for each | | `update_error` | the customer sentence when the update did not complete | | `hold_reason` | the hold's sentence while the app is held (failed update OR failed restore) | @@ -650,6 +650,24 @@ the old version back; a power cut while undoing resumes the undo. Reasoning and `felhom.eu/documentation/architecture/09-update-architecture.md` §6.1a, `felhom.eu/documentation/audits/undo-bakeoff-2026-09-23/`. +**PostgreSQL majors are converted by the box (v0.273.0, `09` §3 decisions 35–38, §6.4 part 10).** Only on a +step whose ladder entry carries the harness's mark `engine_conversion {service, engine: postgres, from, to}`. +After the undo copy, a new phase `converting` („Adatbázis átalakítása" / "Converting the database"): the OLD +database container alone → the check (per database owner/encoding/collation, every role, every extension, every +table's row count) → `pg_dumpall` into `/pre-update-convert/`, refused without its completion line → the +old engine stops → **the DB volume is emptied only after the copy's finished-marker is validated again** (and +inside the emptying helper) → the NEW engine alone on the empty volume (`up -d --no-deps `) → the +entrypoint's empty databases dropped, `CREATE ROLE` skipped for roles that exist → the load with `ON_ERROR_STOP` +→ the check again, plus `$PGDATA/PG_VERSION` = the mark's `to`. Any failure, and a controller restart during +`converting`, runs the existing undo. The space check (the dump's bound = the DB volume's size × 1.25, plus the +2 GB floor, beside the stack dir) and the mark check refuse before anything moves: „A(z) %s adatbázisának +átalakításához %s szabad hely kell, de csak %s van. Nem változott semmi." / „Ez a lépés a(z) %s adatbázisának fő +verzióját váltaná, de nincs róla próba. Nem változott semmi." — a PostgreSQL major move WITHOUT the mark is +refused by the preflight and by the job. After success the old datadir's copy is kept (`app.yaml` +`conversion_copy`) until a backup is proven after the conversion; the hourly `conversion-copy-release` job then +removes it, logged by name. PostgreSQL 18 mounts its volume at `/var/lib/postgresql` — the step's definition +carries that. Code: `internal/stacks/pgconvert.go`. Proof: `felhom.eu/documentation/audits/night-2026-09-26/`. + **Held apps say so (v0.265.0, R-625).** While a hold stands, the update badge reads „Megállítva — visszaállítás szükséges" / "Stopped — restore needed" (`tag-error`, title = the hold sentence's first sentence, in the reader's language) and no Update button is rendered; the API still answers 409 `held`. A