From 54a533103ab961a1f2bfd49a3cf39b65f1a93d7f Mon Sep 17 00:00:00 2001 From: Lemon-miaow Date: Sat, 26 Sep 2026 10:24:14 +0800 Subject: [PATCH] =?UTF-8?q?fix(bootstrap):=20felis-link.properties=20?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E4=B8=8D=E5=8F=98=E5=B0=B1=E4=B8=8D=E9=87=8D?= =?UTF-8?q?=E5=86=99=EF=BC=8Cdomain=20check=20=E4=B8=8D=E5=86=8D=E8=AF=AF?= =?UTF-8?q?=E6=8A=A5=E4=BB=A3=E7=90=86=E8=90=BD=E5=90=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/bootstrap.sh | 17 ++++++++++++++++- deploy/bootstrap_test.sh | 40 +++++++++++++++++++++++++++++++++++++--- docs/operations.md | 6 ++++++ 3 files changed, 59 insertions(+), 4 deletions(-) diff --git a/deploy/bootstrap.sh b/deploy/bootstrap.sh index 1adbadd..86884b5 100644 --- a/deploy/bootstrap.sh +++ b/deploy/bootstrap.sh @@ -2364,6 +2364,21 @@ atomic_install_file() { mv -fT "$staged" "$target" } +# install_if_changed is atomic_install_file that leaves a target with the same bytes in +# place, fixing only its mode and owner, so the target's mtime keeps meaning "the content +# changed". felis domain check reads a proxy started before felis-link.properties' mtime +# as one still on the old names, and a re-run that rewrote the same bytes made every +# install look behind (and `felis domain set` restart the proxy for nothing). +install_if_changed() { + local source="$1" target="$2" mode="$3" owner="$4" group="$5" + if [ -f "$target" ] && [ ! -L "$target" ] && cmp -s "$source" "$target"; then + chown "${owner}:${group}" "$target" + chmod "$mode" "$target" + return 0 + fi + atomic_install_file "$@" +} + # build_velocity_plugin compiles plugins/velocity in the same gradle image the two # Dockerfiles use, and drops the jar where Velocity will look for it. Docker is the # toolchain here on purpose: the host needs no JDK and no gradle, only a JRE. Gradle @@ -2728,7 +2743,7 @@ EOF atomic_install_file "${tmp}/forwarding.secret" "${VELOCITY_DIR}/forwarding.secret" 0640 root "$VELOCITY_USER" atomic_install_file "${tmp}/velocity.toml" "${VELOCITY_DIR}/velocity.toml" 0640 "$VELOCITY_USER" "$VELOCITY_USER" - atomic_install_file "${tmp}/felis-link.properties" \ + install_if_changed "${tmp}/felis-link.properties" \ "${VELOCITY_DIR}/plugins/felis-link/felis-link.properties" 0640 root "$VELOCITY_USER" ok "velocity.toml + forwarding secret + felis-link.properties written (${VELOCITY_DIR})" } diff --git a/deploy/bootstrap_test.sh b/deploy/bootstrap_test.sh index bf83b8d..c1b27f2 100644 --- a/deploy/bootstrap_test.sh +++ b/deploy/bootstrap_test.sh @@ -1541,9 +1541,10 @@ panel_hostname = "console.r.example.com" # The proxy's link properties and the panel certificate take the carried names. wvblock="$(awk '/^write_velocity_config\(\) \{/,/^}/' "$BS")" ptblock="$(awk '/^ensure_panel_tls_cert\(\) \{/,/^}/' "$BS")" -{ [ -n "$wvblock" ] && [ -n "$ptblock" ]; } \ - || { echo "FAIL: write_velocity_config / ensure_panel_tls_cert not found in $BS"; exit 1; } -printf '%s\n' "$wvblock" "$ptblock" >> "$fnfile" +iicblock="$(awk '/^install_if_changed\(\) \{/,/^}/' "$BS")" +{ [ -n "$wvblock" ] && [ -n "$ptblock" ] && [ -n "$iicblock" ]; } \ + || { echo "FAIL: write_velocity_config / ensure_panel_tls_cert / install_if_changed not found in $BS"; exit 1; } +printf '%s\n' "$iicblock" "$wvblock" "$ptblock" >> "$fnfile" cat > "$adir/felis.host.toml" <<'TOML' [auth] admin_hostname = "ops.example.org" @@ -1601,6 +1602,39 @@ else echo "FAIL: reading a long [auth] printed:"; printf '%s\n' "$err" | head -5; fails=$((fails + 1)) fi +# A re-run that writes the same felis-link.properties leaves the file alone: felis domain +# check reads a proxy started before the file's mtime as still on the old names. +run_link() { # velocity-dir [root-domain] + VD="$1" RD="${2:-r.example.com}" TMPDIR="$1" FNFILE="$fnfile" bash -c ' + set -Eeuo pipefail + log() { :; }; ok() { :; }; remember_temp() { :; } + felis_internal_ip() { printf 10.43.0.1; } + prepare_velocity_layout() { :; } + atomic_install_file() { echo "REPLACED $(basename "$2")"; cp "$1" "$2"; } + chown() { echo "CHOWN $*"; }; chmod() { echo "CHMOD $*"; } + . "$FNFILE" + STATE_DIR="$VD" FELIS_ROOT_DOMAIN="$RD" FORWARDING_SECRET=f SERVICE_TOKEN=t LOGIN_SERVER=login \ + LOBBY_SERVER=lobby FELIS_GAME_PORT=25565 VELOCITY_DIR="$VD" VELOCITY_USER=v NODE_IP=10.0.0.5 + write_velocity_config' 2>&1 +} +ldir2="$(mktemp -d)" +mkdir -p "$ldir2/plugins/felis-link" +lprops="$ldir2/plugins/felis-link/felis-link.properties" +expect "a first write installs felis-link.properties" "REPLACED felis-link.properties" "$(run_link "$ldir2")" +touch -t 202001010000 "$lprops" +before="$(ls -l --time-style=+%s "$lprops" 2>/dev/null || stat -f '%m' "$lprops")" +out="$(run_link "$ldir2")" +case "$out" in + *"REPLACED felis-link.properties"*) echo "FAIL: a re-run with the same names replaced felis-link.properties"; fails=$((fails + 1)) ;; + *) echo "PASS a re-run with the same names leaves felis-link.properties in place" ;; +esac +expect "the re-run still fixes the owner" "CHOWN root:v $lprops" "$out" +expect "the re-run still fixes the mode" "CHMOD 0640 $lprops" "$out" +expect "the kept file keeps its mtime" "$before" "$(ls -l --time-style=+%s "$lprops" 2>/dev/null || stat -f '%m' "$lprops")" +expect "a re-run on other names replaces felis-link.properties" "REPLACED felis-link.properties" "$(run_link "$ldir2" other.example.net)" +expect "the replaced file has the new root domain" "root-domain=other.example.net" "$(grep '^root-domain=' "$lprops")" +rm -rf "$ldir2" + # A different FELIS_ROOT_DOMAIN on an installed host is refused with the way through. dnblock="$(awk '/^detect_node_ip\(\) \{/,/^}/' "$BS")" prdblock="$(awk '/^persisted_root_domain\(\) \{/,/^}/' "$BS")" diff --git a/docs/operations.md b/docs/operations.md index aaa694d..0efa668 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -506,3 +506,9 @@ names afterwards. A second `set -yes` changed and restarted nothing; the install with the old `FELIS_ROOT_DOMAIN` stopped at its first check; a full installer re-run kept the moved domain and left `check` clean; moving back restored every surface **[VM-VERIFIED]**. + +`check` reads the proxy as behind when `felis-velocity` started before +`felis-link.properties` last changed. Installers before this command rewrote that file +on every run, so a host upgraded from one can show that line once with the file already +on the names; `sudo systemctl restart felis-velocity` clears it. The installer now leaves +the file alone when its content is the same.