feat(cli): apply coordinated updates during maintenance

This commit is contained in:
Lemon-miaow committed 2026-10-04 17:36:21 +08:00
1 parent cb3ed94026
commit 4d8e9af240
21 files changed
+990 -457

No files matched your search

+8 -8
View File
@@ -31,19 +31,19 @@ A grep across `*.md` and `*.go` returns both sets; only the Go ones are seams.
update` passes nil deliberately. The notification is the panel instead:
`felis-update-check.timer` runs `felis update --record` daily on the host, which
stores the report under `platform_settings.update_report`, and **Admin → Updates →
Component versions** shows it with the command that applies each update.
- `internal/updates/seams.go:43` — `Applier`. Nothing applies an update anywhere. A
nil applier is not silent — `Run` records `errNoApplier` against every planned
apply, so a mis-scheduled apply is loud rather than lost.
Component versions** shows it with the read-only command that prepares an update.
- `internal/updates/seams.go:43` — `Applier`. No unattended runner adapts this interface.
The host CLI implements explicit `felis update --apply` separately, using the target
installer after window checks and backup. A nil applier in the read-only runner
still records `errNoApplier` against a mistakenly scheduled apply.
- `internal/updater/gatherer_integration.go:22` — the two current-version seams
`NewSysGatherer` leaves nil, for the in-cluster path: the k8s read of the
control-plane Deployment image, and the Velocity jar inspection. Both are answered
on the host path (see "Built" below), so this gap is specific to a caller that has
a cluster client instead of the node.
- `internal/api/handlers_updates.go` — the maintenance window is advisory: no
in-cluster runner applies updates. `felis update` reads the stored window, prints
where now sits against it and warns before an apply outside it; the runner itself
still runs with a zero window, so no path can claim an apply is under way.
- `internal/api/handlers_updates.go` — no in-cluster runner applies updates. The host
`felis update --apply` now consumes the stored window and refuses outside it unless
explicit `--now` starts manual maintenance. The check/record runner remains read-only.
- `internal/submit/blobstore.go` — CLOSED 2026-09-22. The uploads PVC still cannot
cross namespaces, so the transport went through the API instead of a mount: the
derived context ref is now the internal-face URL
+6 -9
View File
@@ -4185,15 +4185,12 @@ paths:
operationId: getUpdateWindow
summary: Read the SysAdmin-set auto-update maintenance window (admin).
description: >-
Advisory: Felis applies no update on its own. `felis update` on the
host reads this window, reports where now sits against it, and warns
before an apply outside it.
The single platform-wide maintenance window during which Felis may apply a
Scheduled component's update to itself (decision core internal/updates). An
unset window — never set, or explicitly cleared — reads back as
{start:null,end:null}. API+persistence only: nothing consumes the window
until the INTEGRATION runner and executors are wired, so setting it changes
no behavior yet.
Felis applies no update on its own. `felis update` checks versions and
prints an explicit apply command. `felis update --apply` reads this
platform-wide [start,end) window before backup and before installation,
refusing outside it unless `--now` explicitly starts manual maintenance.
An unreadable window is always a refusal, including with `--now` or
`--force`. An unset window reads back as {start:null,end:null}.
x-felis-face: [external]
x-felis-tier: admin
security: [{ sessionCookie: [] }]
+41 -10
View File
@@ -501,8 +501,8 @@ store=/var/lib/rancher/k3s/storage
## 4. Upgrading the pieces around Felis
A rerun of the installer upgrades Felis itself (§15). The components it installs keep
the version they were installed with unless noted:
The host updater reuses the installer to reconcile Felis itself and its core components
(§15). The components it installs keep the version they were installed with unless noted:
| Component | How a rerun treats it | Upgrade |
|---|---|---|
@@ -513,23 +513,54 @@ the version they were installed with unless noted:
| PostgreSQL | follows the image the release pins | a minor release comes with a Felis release, and the rerun restarts felis-postgres on it (a few seconds without the API); a major version is a dump and restore (below) |
| Docker, git, nftables | distribution packages | the package manager |
`felis update` is a read-only check. It reports upstream availability, resolves the
Felis target, downloads and syntax-checks its matching installer, and prints an explicit
apply command. The upstream table is advisory: applying uses the target's compatible
pins, rather than installing each component's newest upstream version independently.
`--k3s`, `--cloudflared`, `--jre` and `--postgres` narrow the report; applying any core
selector reconciles the whole platform bundle. `--all` also enables the release-pinned
k3s and cloudflared upgrades. Minecraft user server images stay pinned.
```sh
curl -fsSL https://raw.githubusercontent.com/FelisMC/Felis/main/deploy/bootstrap.sh \
| sudo FELIS_UPGRADE_DEPS=1 bash
sudo felis update # newest stable release; no installation
sudo felis update --all # also plan pinned host dependency upgrades
sudo felis update --version v0.2.0 # inspect a named published release
sudo felis update --dev # inspect the latest main commit
sudo felis update --ref <commit-or-tag> # inspect a specific source tree
```
`sudo felis update` reports Felis, Velocity, k3s, cloudflared, the JRE and PostgreSQL
against their newest releases; `--k3s`, `--cloudflared`, `--jre` and `--postgres` narrow
it to one. PostgreSQL is read from the felis-postgres container and compared within its
major, since a minor release arrives with a Felis release, and a major past its end of life
gets a note naming the current one.
Review the target, full commit, scope and restart impact, then run the exact `--apply`
command printed by the check. A source apply uses the full SHA, while a release apply
also includes `--expect-commit` so a moved tag is refused. `--apply --dev` is supported
for deliberately resolving main at execution time; the printed command pins the commit
you inspected instead.
Set the maintenance window in **Admin → Updates** first. Application reads that window
before backup and again before installation: unset, future or expired windows refuse
application. `--apply --now` explicitly starts one-off manual maintenance instead;
an unreadable window is always refused. The daily `--record` timer never applies.
`--force` reinstalls the same version or permits an intentional Felis downgrade; it
never bypasses the window, backup or component compatibility guards.
Before installation the running binary takes a database + `/etc/felis` + MinecraftServer
specification backup; failure stops the update. Worlds are covered separately by server
backups (§16), not this control-plane snapshot. Application then streams the existing
installer's progress, reconciles the CLI, API/operator/panel, manifests/RBAC, plugins,
proxy and system images, and verifies the installed binary's version. Installer rollout
checks still gate success. A failed installer can leave some components changed: retain
the pre-update backup and follow troubleshooting §15/§16; schema rollback is not automatic.
PostgreSQL major changes require dump/restore, and k3s upgrades cannot skip a minor version.
Older host binaries whose `update -h` has no `--apply` need one installer run to acquire
this updater. Published assets are checksum-verified; older releases without the required
installer options must be selected through `--ref` for a source build instead.
The installer also sets up `felis-update-check.timer`, which runs `felis update --record`
once a day around 05:30 (and at boot after a missed run). `--record` stores the result
in `platform_settings`, and the panel's **Admin → Updates → Component versions** card
shows it: each component's installed and newest version, and for the ones with a newer
release the `sudo felis update --<component>` line that prints how to apply it. Felis
applies nothing on its own; the installer re-run above is the apply path. The card turns
applies nothing on its own; the explicit `--apply` command above is the apply path. The card turns
red when the newest record is older than 26 hours, meaning the timer stopped:
```sh
+12 -8
View File
@@ -2136,14 +2136,18 @@ annotated Service endpoints picks them up as is.
## 15. Control-plane upgrades, and rolling back a bad one
There is no in-place updater: an upgrade is re-running the installer
(`curl -fsSL <installer URL> | sudo bash`), which imports the release's images
(or rebuilds them on the host, operations §1 "Where the binary and the images come
from") and re-applies the bundle. `felis update --panel` prints that command with the
script read at the newest release's tag, so the installer and the binary it
downloads come from the same release. (`sudo felis setup` is not this path; on a completed
install it only opens the config console.) The channel is not persisted across
the re-run, so pass `FELIS_VERSION_BOOTSTRAP=dev` on a host that tracks main.
`felis update` checks only; it prints a target and explicit `--apply` command. The host
updater downloads the installer from the target's resolved full commit, backs up the
database and deployment state, then uses that installer to reconcile the CLI, core
services, plugins and system images. See operations §4 for `--version`, `--ref`, `--dev`
and `--all`. Use the exact printed command to retain the reviewed target. Application
requires an active maintenance window or explicit `--now`; `--force` never skips the
backup, an unreadable window or compatibility guards.
A host whose `felis update -h` lacks `--apply` still needs one installer run
(`curl -fsSL <installer URL> | sudo bash`) to acquire the new CLI. `sudo felis setup`
on a completed install opens the configuration console, so it is not an upgrade path.
The updater reuses the same checksum, image import/build and rollout checks as that installer.
Two properties of the control plane matter when you do:
- Both Deployments use strategy **Recreate** (single replica, no leader election: