v0.85.0: self-update reworked — in-guest pull + agent swap (Phase 1)

Replaces the dead in-container docker-compose self-update (composePath doesn't
exist in the LXC guest). The controller now docker-logins+pulls the target image
in-guest (shared socket, its registry token), then delegates the container swap to
the host agent (agentapi.SwapController -> POST /controller/swap), which owns the
restart + health-verify + rollback. Removed performUpdate compose flow /
updateComposeFile / composePath. NewUpdater takes an AgentSwapper. DryRun reports
agent_reachable + pull_capable. UI button + poll unchanged; latest-only.

Tests: up-to-date no-op / pull-fail agent-not-called / happy pull-then-swap /
no-agent unavailable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TtXesNa2LGbMmE4DNL6SE7
This commit is contained in:
2026-06-26 21:26:14 +02:00
parent e0cf78bb90
commit 3c1e91b5f0
5 changed files with 325 additions and 126 deletions
+22
View File
@@ -1,5 +1,27 @@
## Changelog
### v0.85.0 — Self-update reworked: in-guest pull + agent swap (Phase 1) (2026-06-26)
- **Problem:** the self-update button was dead in the LXC architecture — `selfupdate/updater.go` drove
the old bare-metal flow (`docker compose -f /opt/docker/felhom-controller/docker-compose.yml up -d`),
a path that doesn't exist in the guest ("docker-compose.yml nem elérhető"). The stranded 0.77.0 demo
could detect 0.84 but not install it.
- **Fix (Phase 1):** the controller now **pulls** the target image in-guest (its existing registry token
via `docker login --password-stdin``docker pull``docker logout`, over the shared docker socket),
then **delegates the swap to the host agent** (`agentapi.Client.SwapController` → agent
`POST /controller/swap`). The agent — external to the controller container — rewrites
`/etc/felhom-controller-image`, restarts the bootstrap unit, verifies health, and **rolls back** if the
new controller doesn't come up. The controller never `docker rm`/recreates itself.
- **Removed** the dead compose path: `performUpdate`/`updateComposeFile`/`composePath` and the
`docker compose up -d` flow are gone. `DryRun` now reports `agent_reachable` + `pull_capable` instead of
`compose_writable`.
- Success/failure is detected on the **next boot** by the existing `VerifyStartup` (running version vs
target) — a rollback lands the previous version → "failed (version mismatch)". The UI button + poll
(`triggerUpdate`/`pollUntilBack`) are unchanged. **Latest-only** (no version picker — Phase 2).
- `agentapi`: new `SwapController` (202) + `SwapStatus`. `NewUpdater` takes an `AgentSwapper` (nil on an
un-provisioned guest → update unavailable) instead of a compose path.
- Tests (`internal/selfupdate/updater_test.go`): up-to-date → no pull/no agent; pull-fails → agent never
called; happy → pull then one `SwapController` with the right ref; no-agent → unavailable.
### v0.84.0 — Show an app's auto-generated initial login on its page (catalog-driven) (2026-06-26)
- **Problem:** some apps generate a random first-login password into a file at first boot (Crafty →
`/crafty/app/config/default-creds.txt`) instead of taking it from a deploy field. Customers had to