fix(backrest): register the backup-contract hooks, don't just write them (2.1.0) - #162
Merged
Merged
Conversation
…hem (#159) 2.0 wrote /config/hola/backup-{prepare,finalize}.sh and stopped. Backrest has no global hooks — `Hook` hangs off `Repo` and `Plan` only — so a script on disk is never called until it is attached to a repository, and at install time there is no repository to attach it to. Unless the operator hand-wired two Command hooks in the UI, the broker was never invoked and every snapshot was a live-file copy, while Hola's dashboard (which only knows the app *declares* `provides: backup@1`) reported the accepting apps as quiesced. Both facts the operator saw were wrong in the reassuring direction. Adds `backrest-hola-autowire`, a reconciler that attaches both hooks to every repository through Backrest's own API and keeps them attached, so a repository created next month is wired within 30 seconds. Through the API, not the file: ConfigManager caches the parsed config in memory and invalidates only on its own writes, so an edit to config.json under a running Backrest is read back only after a restart and the next UI save overwrites it. SetConfig is the path the UI uses — `modno` gives optimistic concurrency, and GetConfig does not redact repository passwords, so a read-modify-write round-trip preserves them. At repo level, not plan level: TasksTriggeredByEvent iterates the repo's hooks for every plan backing onto it, so one wiring covers every plan the operator ever adds. `mikefarah/yq` is the image Homepage's bolt-on already uses; it carries yq (native JSON), BusyBox wget (POST with headers) and a shell — no custom build. `BACKREST_HOLA_AUTOWIRE=false` hands the hooks back to the operator: nothing is added, nothing already present is removed. Hand-wired hooks are recognised by command path and left alone. Verified against a real backrest v1.14.1 with a real restic repo and a stub broker: hooks registered idempotently; a repository added later wired on the next pass; SNAPSHOT_START fired the prepare script, which POSTed with its Bearer token and polled to completion; SNAPSHOT_END fired finalize; with the broker unreachable the prepare hook exited 1 and ON_ERROR_CANCEL cancelled the snapshot, so fail-closed holds; repository password survived the round-trip; opt-out and an unreachable Backrest both stay running without crash-looping. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vck5KSX2CLxhohx14nb5Sh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #159.
The gap
2.0 writes
/config/hola/backup-prepare.shandbackup-finalize.shand stops.Backrest has no global hooks.
Hookhangs offRepoandPlanonly (proto/v1/config.proto), so a script on disk is never called until it is attached to a repository — and at install time there is no repository to attach it to; the operator creates one later, in the UI.So unless the operator hand-wired two Command hooks, the broker was never invoked and every snapshot was a live-file copy. Meanwhile Hola's dashboard — which only knows this app declares
provides: backup@1— reported the accepting apps as quiesced. Both facts the operator saw were wrong in the reassuring direction.The fix
A third service,
backrest-hola-autowire, that attaches both hooks to every repository and keeps them attached. A repository created next month is wired within 30 seconds.Through Backrest's API, not the config file.
ConfigManagercaches the parsed config in memory and invalidates only on its own writes, so editingconfig.jsonunder a running Backrest is read back only after a restart, and the next UI save silently overwrites it.SetConfigis the same path the UI takes:modnogives optimistic concurrency (a mismatch is refused; the next pass re-reads), andGetConfigdoes not redact repository passwords, so a read-modify-write round-trip preserves them.At repository level, not plan level.
TasksTriggeredByEventiterates the repo's hooks for every plan backing onto it, so one wiring covers every plan the operator ever adds.mikefarah/yqis the image Homepage's bolt-on already uses. It carriesyq(native JSON), BusyBoxwget(POST with headers) and a shell — the three things this needs, digest-pinned, no custom build.BACKREST_HOLA_AUTOWIRE=falsehands the hooks back to the operator: nothing is added, nothing already present is removed. Hand-wired hooks are recognised by command path and left alone, so an operator who followed the old README sees no change.Backrest's own login is disabled by default (
config.NewDefaultConfigsetsauth.disabled) and Hola gates the app at Traefik, so the call needs no credential. If an operator enables Backrest's login, the reconciler says so once and keeps trying.Verified, not assumed
Against a real
backrest:v1.14.1with a real restic repo and a stub broker:CONDITION_SNAPSHOT_STARTfired the prepare script, which POSTed/api/contracts/backup/preparewith its Bearer token, polled/status/:iduntilcompleted, and let the snapshot proceedCONDITION_SNAPSHOT_ENDfired finalizeON_ERROR_CANCELcancelled the backup —snapshot start hook: task cancelled: exit status 1, no snapshot takenClean against
bin/validate-manifest.mjsand Hola'svalidateComposeDocument.Still open after this
apps#153 (postiz's second Postgres has no participation) and its blocker apps#152 (the schema forbids plural participations). Separately, try-hola/hola#470 fixes the coverage recogniser that made postiz read
quiescedinstead ofpartial.🤖 Generated with Claude Code
https://claude.ai/code/session_01Vck5KSX2CLxhohx14nb5Sh