fix(bootstrap): re-runs keep the operator's [registry] overrides

§15's upgrade path is "re-run the installer", but write_felis_toml rewrote the
[registry] table from scratch — url + build_namespace only. Everything else an
operator put there (the §8e build-lane executor mirrors, the resource caps, the
uploads backend stamped by the storage wizard, [registry.s3]) was silently
reverted on every re-run: builds went back to the denied upstream executors and
an S3-backed install flipped to local storage, with nothing pointing at why.

Found while landing the registry-hosting work, which depends on those same
keys surviving.

- persisted_registry_block carries the operator-owned [registry] keys and the
  [registry.s3] subtable forward, same first-readable-file rule as
  persisted_smtp_block; url/build_namespace stay installer-owned (they must
  match REGISTRY_URL/BUILD_NS, so a stale value must NOT survive).
- The s3 subtable header is re-emitted with its keys, so nothing carried lands
  as an unknown key under [registry].
- bootstrap_test.sh pins the carry, the installer-owned exclusion, and
  idempotence (a second re-run writes a byte-identical file).
This commit is contained in:
Lemon-miaow committed 2026-09-23 19:05:54 +08:00
1 parent fa0e8d7d97
commit 765a8923a4
2 files changed
+99 -3

No files matched your search

+36 -3
View File
@@ -2129,17 +2129,50 @@ persisted_auth_source_blocks() {
'url = "https://littleskin.cn/api/yggdrasil/sessionserver/session/minecraft/hasJoined"'
}
# persisted_registry_block echoes the operator-owned [registry] keys an earlier run
# left behind — the build-lane executor mirrors, the resource caps, the uploads
# backend and its [registry.s3] subtable — so §15's upgrade path (re-run the
# installer) does not silently revert them. Nothing in this script's inputs
# derives these: they are hand-written per docs/troubleshooting.md §8e or stamped
# by the storage wizard. url and build_namespace are NOT carried: this script
# owns them (they must match REGISTRY_URL / BUILD_NS). Same first-readable-file
# rule as persisted_smtp_block.
persisted_registry_block() {
local f out
for f in "${STATE_DIR}/felis.host.toml" "${STATE_DIR}/felis.pod.toml"; do
[ -r "$f" ] || continue
out="$(awk '
/^[[:space:]]*\[/ { sect = $0; next }
sect ~ /^[[:space:]]*\[registry\][[:space:]]*$/ &&
/^[[:space:]]*(kaniko_image|trivy_image|trivy_db_repository|build_cpu_limit|build_mem_limit|user_uploads_context)[[:space:]]*=/ { print }
sect ~ /^[[:space:]]*\[registry\.s3\][[:space:]]*$/ && /^[[:space:]]*[A-Za-z_]+[[:space:]]*=/ {
if (!s3hdr) { printf "[registry.s3]\n"; s3hdr = 1 }
print
}
' "$f")"
[ -n "$out" ] || continue
printf '%s\n' "$out"
return 0
done
}
write_felis_toml() {
local target="$1" db_host="$2" smtp_block auth_source_blocks
local target="$1" db_host="$2" smtp_block auth_source_blocks registry_block
smtp_block="$(persisted_smtp_block)"
if [ -n "$smtp_block" ]; then
log "carrying forward the configured [smtp] relay"
smtp_block="${smtp_block}"$'\n' # keep a blank line before the next section
fi
auth_source_blocks="$(persisted_auth_source_blocks)"
registry_block="$(persisted_registry_block)"
if [ -n "$registry_block" ]; then
log "carrying forward the configured [registry] overrides"
registry_block="${registry_block}"$'\n' # keep a blank line before the next section
fi
cat > "$target" <<EOF
# Generated by deploy/bootstrap.sh; rerun the installer to regenerate. Hand edits are
# overwritten, except [smtp] and [[auth_source]], which carry forward.
# overwritten, except [smtp], [[auth_source]], and the operator-owned [registry]
# overrides, which carry forward.
[server]
listen = "0.0.0.0:8080"
root_domain = "${FELIS_ROOT_DOMAIN}"
@@ -2160,7 +2193,7 @@ lobby_image = "${FELIS_LOBBY_IMAGE}"
[registry]
url = "${REGISTRY_URL}"
build_namespace = "${BUILD_NS}"
${registry_block}
[archive]
store = "tarLocal"
local_path = "${FELIS_ARCHIVE_LOCAL_PATH}"
+63
View File
@@ -725,6 +725,69 @@ case "$out" in
*DOCKER*) echo "FAIL: a present registry:2 must not trigger a docker pull"; fails=$((fails + 1)) ;;
esac
# --- installer re-runs keep the operator's [registry] overrides --------------------------
# §15's upgrade path is re-running the installer, but the build-lane mirrors and the
# uploads backend live in [registry] as hand-written keys (docs/troubleshooting.md §8e or
# the storage wizard) that nothing in this script's inputs derives. A re-run must carry
# them forward — without letting a stale url/build_namespace survive (installer-owned).
wrblock="$(awk '/^write_felis_toml\(\) \{/,/^}/' "$BS")"
prblock="$(awk '/^persisted_registry_block\(\) \{/,/^}/' "$BS")"
[ -n "$wrblock" ] && [ -n "$prblock" ] \
|| { echo "FAIL: write_felis_toml / persisted_registry_block not found in $BS"; exit 1; }
# The blocks quote themselves (the awk program uses single quotes), so they are
# sourced from a file instead of being spliced into a single-quoted bash -c.
fnfile="$(mktemp)"
printf '%s\n%s\n' "$prblock" "$wrblock" > "$fnfile"
rdir="$(mktemp -d)"
cat > "$rdir/felis.host.toml" <<'TOML'
[registry]
url = "stale.invalid:5000"
build_namespace = "stale-ns"
kaniko_image = "registry.felis.svc:5000/mirror/kaniko-executor:v1.24.0"
trivy_db_repository = "registry.felis.svc:5000/mirror/trivy-db:2"
[registry.s3]
endpoint = "https://s3.example"
region = "us-east-1"
TOML
run_write() { # out-file
STATE_DIR="$rdir" OUT_TOML="$1" FNFILE="$fnfile" bash -c '
log() { :; }
persisted_smtp_block() { :; }
persisted_auth_source_blocks() { :; }
. "$FNFILE"
FELIS_ROOT_DOMAIN=r.example.com DB_USER=u DB_PASSWORD=p DB_NAME=d MINECRAFT_NS=minecraft \
FELIS_EGRESS_MODE=nodeport FELIS_LIMBO_IMAGE=li FELIS_LOBBY_IMAGE=lo \
REGISTRY_URL=registry.felis.svc:5000 BUILD_NS=felis-build FELIS_ARCHIVE_LOCAL_PATH=/a \
write_felis_toml "$OUT_TOML" 127.0.0.1'
}
run_write "$rdir/out.toml"
out="$(cat "$rdir/out.toml")"
expect "a re-run carries the build-lane executor mirrors" \
'kaniko_image = "registry.felis.svc:5000/mirror/kaniko-executor:v1.24.0"' "$out"
expect "a re-run carries the [registry.s3] uploads subtable" "[registry.s3]" "$out"
expect "the carried subtable keeps its keys" 'endpoint = "https://s3.example"' "$out"
expect "url stays installer-owned" 'url = "registry.felis.svc:5000"' "$out"
case "$out" in
*stale.invalid* | *stale-ns*) echo "FAIL: stale installer-owned registry values survived the re-run"; fails=$((fails + 1)) ;;
esac
cp "$rdir/out.toml" "$rdir/felis.host.toml"
run_write "$rdir/out2.toml"
if cmp -s "$rdir/out.toml" "$rdir/out2.toml"; then
echo "PASS a carried-forward config converges (the second re-run is a no-op)"
else
echo "FAIL: carrying [registry] overrides is not idempotent"
diff "$rdir/out.toml" "$rdir/out2.toml" | head
fails=$((fails + 1))
fi
rm -f "$fnfile"
# ---------------------------------------------------------------------------------------
if [ "$fails" -eq 0 ]; then
echo "ALL PASS"