Files
Felis/internal/updater/doc.go
flyemoji afdbfac7a8 docs: index the deferred integration seams and correct two stale markers
INTEGRATION-ONLY and KNOWN-LIMITATION are grep-able, but the grep answers the
wrong question. Thirty-four Go sites share the two markers and they carry four
different meanings: "declared, nothing implements it" reads exactly like
"implemented, only its I/O is unreachable from here", and neither reads
differently from a limitation that was accepted on purpose and is not coming
back. docs/deferred-seams.md sorts them, following the bucketed shape
internal/updater/doc.go already uses for its own package rather than starting a
second convention.

Sorting them turned up two markers that had outlived the condition they describe.

config.go called the modpack upload transport a deferred integration after both
backends had shipped -- LocalContextStore and S3ContextStore, selected in
cmd/felis by the shape of user_uploads_context, with the uploads PVC mounted and
the felis-uploads-s3 Secret rendered. What is still deferred is the far end:
Kaniko reading that context from inside the build Pod.

updater/doc.go listed the `felis update` CLI and the off-cluster Velocity jar read
under REMAINING INTEGRATION. Both exist -- cmd/felis/update.go, and
gatherer_host.go, which answers Velocity from the installed jar's manifest and
felis-api from the running binary's build stamp. The two nil seams that bullet
also names are real, but they belong to the in-cluster gatherer only, so the
bullet now says which caller has what and which is still empty.

The index also records the collision that makes a naive grep misleading:
docs/troubleshooting.md uses [INTEGRATION-ONLY] for something else, defined in its
own opening at :19 -- the symptom is produced by the kubelet, kaniko or a live
handshake, so it cannot be reproduced from the repository. Those twelve marks say
where a failure comes from, not that something is unbuilt, and are excluded.

Both code changes are comments. Every file:line the index cites was checked
against the line it points at.
2026-07-28 17:27:01 +09:00

56 lines
4.0 KiB
Go

// Package updater is the integration/caller side of the component self-update
// subsystem — it gives the pure decision core in internal/updates its first real
// caller. internal/updates declares the seams (ReleaseSource / Notifier / Applier)
// and orchestrates them (updates.Run) but performs no I/O; this package supplies the
// concrete wiring: the platform topology (which components Felis tracks and under
// what policy), the upstream release sources, and the Runner that gathers current
// versions, runs the plan against a maintenance window, and renders the "版本号状态"
// report.
//
// Verification boundary — stated honestly so a green test suite is not mistaken for
// "the updater works against real infra":
//
// - BUILT + UNIT-VERIFIED (Go tests, WSL oracle): the topology, BOTH release
// sources (PaperMC Fill v3 for Velocity; GitHub Releases for felis-api, k3s and
// cloudflared), the routing source, and the Runner's report-only composition. Each
// source's parser is a contract test whose fixture is captured from — and whose
// live call was exercised out-of-band against — the real endpoint: PaperMC on
// 2026-07-04 (3.4.0 is newest stable, 3.5.0-SNAPSHOT filtered), GitHub on
// 2026-07-05 (cloudflared 2026.6.1; k3s v1.36.2+k3s1, its "+k3s1"/"v" tolerated and
// its "-rcN"/prerelease builds rejected). These prove the parse/plan/compose LOGIC
// and that the core is wired to a caller for every tracked component.
//
// - CAVEATS on what the tests do NOT prove: they run against httptest, not the live
// hosts, so future upstream shape drift is not caught. On User-Agent the two APIs
// differ and the code reflects it: GitHub ENFORCES a UA (a bare request is 403'd,
// verified 2026-07-05) so Felis's UA is load-bearing there; PaperMC does NOT
// enforce (a bare request got HTTP 200 on 2026-07-04) so its UA is only etiquette.
// Also: felis-api's topology coord is now the real repository slug, not a placeholder,
// so that component resolves at runtime like the others — but only with a credential.
// The repo is private, so an unauthenticated poll gets GitHub's 404-for-hidden-repo and
// degrades to "latest unknown"; FELIS_GITHUB_TOKEN is what lights it up. k3s and
// cloudflared are public and need none.
//
// - ALSO BUILT + UNIT-VERIFIED: the VersionGatherer's extraction core and dispatch.
// Three pure extractors turn raw system text into a Version — a `--version` banner
// (k3s, cloudflared), a container image tag (felis-api), a proxy jar filename
// (velocity) — and sysGatherer routes each component to the right one over an
// injected seam, all exercised with a fake runner (gatherer_test.go). The
// load-bearing case is proven: k3s's registry tag "v1.36.2-k3s1" is repaired to the
// "+k3s1" build form the binary reports (a Docker tag cannot hold '+'), so an image
// read and a CLI read agree instead of the image masquerading as a prerelease. The
// CLI seam (execRunner) is wired for real; only its exec I/O is un-verified here.
//
// - REMAINING INTEGRATION (genuinely I/O-bound — needs a cluster/mailbox): the two
// current-version PRODUCING seams NewSysGatherer still leaves nil — the k8s read of
// the control-plane Deployment's image (felis-api) and the Velocity jar inspection —
// so an IN-CLUSTER caller surfaces a gather error rather than a wrong version. The
// ON-HOST caller has both: NewHostGatherer (gatherer_host.go) answers felis-api from
// the running binary's build stamp and Velocity from the installed jar's manifest,
// and that is what the built `felis update` CLI runs on. Still absent: the concrete
// Notifier (SMTP + in-game) and Applier (control-plane image bump, cloudflared swap)
// — the CLI passes nil for both on purpose, so it reports and never applies — the
// in-cluster CronJob entry point, and the runtime append of the live Pinned
// Minecraft fleet.
package updater