Files
felhom-controller/REPORT.md
T

4.3 KiB

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

Repo: felhom-controller · Version: v0.84.0v0.85.0 (+ v0.85.1 version-only validation target) · Date: 2026-06-26

What & why

The self-update button was dead in the LXC architecture: selfupdate/updater.go drove the old bare-metal 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. Phase 1: the controller pulls the target image in-guest, then delegates the container swap to the host agent (which owns restart + verify + rollback). The controller never docker rm/recreates itself.

Baseline

felhom-controller e0cf78b v0.84.0 → v0.85.0. Cross-repo: felhom-agent v0.42.0 (POST /controller/swap).

Changes

  • internal/selfupdate/updater.go — rewrote the install path. Kept CheckForUpdate/queryRegistry/ VerifyStartup/state. performUpdate now: pullImage (docker login --password-stdin → pull → logout, in-guest over the shared socket) → agent.SwapController(targetImage). Removed updateComposeFile + composePath + the docker compose up -d flow. NewUpdater takes an AgentSwapper (nil → update unavailable) instead of a compose path. DryRun reports agent_reachable + pull_capable. Added small test seams (queryFn/pullFn).
  • internal/agentapi/client.goSwapController (POST /controller/swap, 202) + SwapStatus.
  • cmd/controller/main.go — build a (nil-able) agent client from the provisioned local-API config and pass it to NewUpdater.
  • UI + router unchanged: the existing button (triggerUpdate → POST /api/selfupdate/update) + poll (pollUntilBack) already drive it; success/rollback is detected by the existing VerifyStartup (running version vs target). Latest-only.

Tests (internal/selfupdate/updater_test.go) — all green (was 0 → 4)

up-to-date → no pull / no agent (Scenario C); pull-fails → agent never called, state failed (Scenario D); happy → pull then exactly one SwapController with the right ref; no-agent → unavailable. go build ./... && go vet ./... && go test ./... — all ok.

Build / deploy

Built + pushed gitea.dooplex.hu/admin/felhom-controller:0.85.0 (and :0.85.1, a version-only build, as the live update target). The demo was not manually deployed — the update feature itself brought 9201 to latest.

Live test on demo 9201 (operator-authorized; method per check)

  • Demo started at controller 0.77.0 (old dead self-update). Brought to 0.85.0 (new code) via the agent primitive (felhom-agent --selftest=controller-swap), each hop verified healthy.
  • E2E via the real Settings button (claude-in-chrome on felhom.demo-felhom.eu/settings): clicked "Frissítés keresése" → the new controller's queryRegistry detected 0.85.1 • Frissítés elérhető; clicked "Frissítés telepítése" → the controller pulled 0.85.1 in-guest and called the agent → the page polled /api/health and reloaded showing current 0.85.1 and "Utolsó frissítés: Sikeres (0.85.0 → 0.85.1)". /api/selfupdate/statuslast_state.status = "success". No compose error.
  • Host-side (authoritative): running container …:0.85.1 Up (healthy), /etc/felhom-controller-image = 0.85.1, agent log "controller-swap: new controller healthy".
  • Hub: the controller's report path shows 0.84.0 → 0.85.0 → 0.85.1.

Observations (out of scope — not acted on)

  • Phase 2 (next task): hub per-customer version floor (default global) + operator desired-version fleet-push. An existing 0.84.0 customer still has the OLD dead button — the first hop onto new-code is operator/agent-driven (the agent primitive), which Phase 2 formalizes.
  • Golden freshness: the golden bakes 0.77.0; a golden rebuild at current is wanted so fresh provisions start current (independent of this feature).
  • DryRun's auto_update scheduled path is unchanged structurally; this task covered the manual button.

Commits

  • 3c1e91b — v0.85.0 self-update rework + agentapi + tests.

No secrets committed; the registry token is used only for the in-guest docker login --password-stdin (logged out after the pull) and is referenced out-of-band.