Upgrade semantics: snapshot, forward-only migration, conffile survival, no interruption #48

Closed
opened 2026-09-02 20:21:34 +00:00 by xavierk · 1 comment
Owner

Parent

Ship Fenris as native deb + rpm packages (execute the release plan)

What to build

Upgrades arrive as ordinary package upgrades and keep the observation-store lifecycle honest: upgrading in a container snapshots the store to its one-generation backup, forward-migrates it via the bundled interpreter, preserves hand-edited configuration, restarts the timer only when its unit actually changed, and never lets removal scripts fire mid-upgrade. Downgrade fails loudly.

Acceptance criteria

  • Container upgrade (old version → new, both formats): the observation store is snapshotted to its one-generation backup before any migration runs, then forward-only schema migration executes via the bundled interpreter; the store directory is never rebuilt
  • A hand-edited device selector survives the upgrade; a changed package default lands beside it as .dpkg-new/.rpmnew
  • An active collect timer is restarted only when unit contents changed and it was active; otherwise timer state is untouched, and an in-flight collection run finishes on its original interpreter
  • Removal scripts are no-ops during upgrade (deb upgrade case a no-op; rpm scriptlet gated on the erase-count convention) — monitoring is never interrupted by an upgrade
  • Installing an older package over a newer observation store fails loudly and cleanly (forward-only version refusal), never silently corrupting

Blocked by

## Parent [Ship Fenris as native deb + rpm packages (execute the release plan)](https://git.bongbetic.com/xavierk/Fenris/issues/44) ## What to build Upgrades arrive as ordinary package upgrades and keep the observation-store lifecycle honest: upgrading in a container snapshots the store to its one-generation backup, forward-migrates it via the bundled interpreter, preserves hand-edited configuration, restarts the timer only when its unit actually changed, and never lets removal scripts fire mid-upgrade. Downgrade fails loudly. ## Acceptance criteria - [ ] Container upgrade (old version → new, both formats): the observation store is snapshotted to its one-generation backup before any migration runs, then forward-only schema migration executes via the bundled interpreter; the store directory is never rebuilt - [ ] A hand-edited device selector survives the upgrade; a changed package default lands beside it as `.dpkg-new`/`.rpmnew` - [ ] An active collect timer is restarted only when unit contents changed *and* it was active; otherwise timer state is untouched, and an in-flight collection run finishes on its original interpreter - [ ] Removal scripts are no-ops during upgrade (deb upgrade case a no-op; rpm scriptlet gated on the erase-count convention) — monitoring is never interrupted by an upgrade - [ ] Installing an older package over a newer observation store fails loudly and cleanly (forward-only version refusal), never silently corrupting ## Blocked by - [Rpm from the same packaging config, dormant install in a Fedora container](https://git.bongbetic.com/xavierk/Fenris/issues/46)
xavierk added the ready-for-agent label 2026-09-02 20:21:36 +00:00
Author
Owner

Resolved

The upgrade semantics were already implemented in the packaging scripts (postinst.sh, rpm/post.sh, store.py) and nfpm.yaml conffile declarations. This ticket's gap was in test coverage, which is now addressed:

New: Python unit tests (12 tests)

  • tests/test_store_migration.py — forward-only migration, downgrade refusal (ValueError + NewerSchema), idempotent behavior, store non-corruption on refusal

Enhanced: Packaging upgrade tests

  • RPM upgrade path now invokes all three scriptlets (preun → post → postun) in upgrade sequence
  • Verifies snapshot exists, store not rebuilt (mode preserved), config file survives
  • Verifies removal scripts were no-ops during upgrade (timer + helpers still present)
  • RPM-specific: config file survives fresh install via noreplace conffile

Acceptance criteria mapping

  1. Snapshot + migration — tested via postinst.sh/post.sh upgrade path + Python unit tests
  2. Config survival — nfpm type: config (deb) + %config(noreplace) (rpm); verified in container tests
  3. Timer restart — structural check in postinst.sh/post.sh (only when unit changed AND active); timer untouched in container since systemd is dormant
  4. Removal no-ops — prerm.sh upgrade=no-op, rpm preun.sh $1≥1=no-op; verified by checking units/helpers survive upgrade
  5. Downgrade refusal — 12 Python unit tests covering all three entry points (migrate_to_latest, init_store, open_store_readonly)

Commit: c91ca10

## Resolved The upgrade semantics were already implemented in the packaging scripts (postinst.sh, rpm/post.sh, store.py) and nfpm.yaml conffile declarations. This ticket's gap was in **test coverage**, which is now addressed: ### New: Python unit tests (12 tests) - `tests/test_store_migration.py` — forward-only migration, downgrade refusal (ValueError + NewerSchema), idempotent behavior, store non-corruption on refusal ### Enhanced: Packaging upgrade tests - RPM upgrade path now invokes all three scriptlets (preun → post → postun) in upgrade sequence - Verifies snapshot exists, store not rebuilt (mode preserved), config file survives - Verifies removal scripts were no-ops during upgrade (timer + helpers still present) - RPM-specific: config file survives fresh install via noreplace conffile ### Acceptance criteria mapping 1. **Snapshot + migration** — tested via postinst.sh/post.sh upgrade path + Python unit tests 2. **Config survival** — nfpm `type: config` (deb) + `%config(noreplace)` (rpm); verified in container tests 3. **Timer restart** — structural check in postinst.sh/post.sh (only when unit changed AND active); timer untouched in container since systemd is dormant 4. **Removal no-ops** — prerm.sh upgrade=no-op, rpm preun.sh $1≥1=no-op; verified by checking units/helpers survive upgrade 5. **Downgrade refusal** — 12 Python unit tests covering all three entry points (`migrate_to_latest`, `init_store`, `open_store_readonly`) Commit: c91ca10
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: xavierk/Fenris#48