fix(images): 服务器镜像在创建时固定到仓库 digest,更换镜像需确认备份,安装器重建前先固定旧服并推送不可变版本标签
This commit is contained in:
29 files changed
+1149
-29
No files matched your search
@@ -1007,6 +1007,27 @@ pin_registry_images() {
|
||||
done
|
||||
}
|
||||
|
||||
# pin_user_server_images fixes every user server still naming a tag in the platform
|
||||
# registry (felis/paper:demo) to the digest that tag names now. It has to run before
|
||||
# build_game_stack and push_images_to_registry put new builds under those tags: a
|
||||
# server left on the bare tag would boot the new build on its next wake and open its
|
||||
# world with a newer Minecraft version, and chunk upgrades cannot be undone. felis-api
|
||||
# pins every server it creates; this catches the ones created before it did. A fresh
|
||||
# install has no CRD, so nothing to pin; a running server restarts once onto the
|
||||
# build it already runs.
|
||||
pin_user_server_images() {
|
||||
kube get crd minecraftservers.felis.lolicon.best >/dev/null 2>&1 || return 0
|
||||
# The registry answers the lookups, and a k3s restart above may have left its pod
|
||||
# still starting. A registry that never comes up fails the lookups below, loudly.
|
||||
kube -n "$CONTROL_NS" rollout status deployment/registry --timeout=180s >/dev/null 2>&1 || true
|
||||
if "$HOST_BIN" pin-images --namespace "$MINECRAFT_NS" \
|
||||
--registry "$REGISTRY_URL" --endpoint "$REGISTRY_PUSH_HOST"; then
|
||||
ok "user servers pinned to the builds they run"
|
||||
else
|
||||
warn "could not pin every user server listed above to its current build: each one still names a tag this run is about to point at a new build, so its next start may open its world with a newer Minecraft version. Pin it before starting it again: sudo felis pin-images (docs/troubleshooting.md §15b)"
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 5. Source/binary + image build + containerd import
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -2584,9 +2605,30 @@ push_images_to_registry() {
|
||||
[ -n "$img" ] || continue
|
||||
push_image_to_registry "$img"
|
||||
done
|
||||
for img in "$FELIS_LIMBO_IMAGE" "$FELIS_LOBBY_IMAGE" "$FELIS_PAPER_IMAGE"; do
|
||||
[ -n "$img" ] || continue
|
||||
push_version_tag "$img"
|
||||
done
|
||||
systemctl stop docker docker.socket 2>/dev/null || true
|
||||
}
|
||||
|
||||
# push_version_tag mirrors a game image a second time under a tag no later run
|
||||
# rewrites: <Minecraft version>-<first 12 hex of the image id>, e.g.
|
||||
# felis/paper:26.2-3f9c0a1b2c4d. The :demo tag moves with every run, and servers are
|
||||
# pinned to the digest it named when they were created, so this is the readable name
|
||||
# for each build: an admin can whitelist it to create servers on that exact
|
||||
# Minecraft version long after :demo has moved on.
|
||||
push_version_tag() {
|
||||
local ref="$1" id versioned
|
||||
[ -n "${MC_VERSION:-}" ] || return 0
|
||||
id="$(docker image inspect -f '{{.Id}}' "$ref" 2>/dev/null)" || return 0
|
||||
id="${id#sha256:}"
|
||||
versioned="${ref%:*}:${MC_VERSION}-${id:0:12}"
|
||||
docker tag "$ref" "$versioned" || die "could not tag ${ref} as ${versioned} — is docker healthy?"
|
||||
push_image_to_registry "$versioned"
|
||||
docker rmi "$versioned" >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
# The login/lobby images use mutable :demo tags. Importing/pushing a replacement
|
||||
# updates containerd, but an existing StatefulSet template is byte-for-byte
|
||||
# unchanged and Kubernetes will not roll it. Recreate only the two always-on
|
||||
@@ -3014,6 +3056,9 @@ main() {
|
||||
build_image
|
||||
# After build_image imported the felis image: the registry pod's gate runs it.
|
||||
pin_registry_images
|
||||
# Before build_game_stack: the builds user servers run must be read off the
|
||||
# registry's tags before new ones replace them.
|
||||
pin_user_server_images
|
||||
build_game_stack
|
||||
install_postgres
|
||||
configure_postgres
|
||||
|
||||
@@ -824,6 +824,7 @@ out="$(
|
||||
FELIS_IMAGE=a FELIS_LIMBO_IMAGE=b FELIS_LOBBY_IMAGE=c FELIS_PAPER_IMAGE=d bash -c '
|
||||
systemctl() { printf "SYSTEMCTL %s\n" "$*"; }
|
||||
push_image_to_registry() { printf "PUSH %s\n" "$1"; }
|
||||
push_version_tag() { printf "VERSION %s\n" "$1"; }
|
||||
registry_docker_login() { printf "LOGIN\n"; }
|
||||
'"$wiblock"'
|
||||
push_images_to_registry'
|
||||
@@ -836,6 +837,34 @@ stops="$(printf '%s\n' "$out" | grep -c 'SYSTEMCTL stop docker')"
|
||||
[ "$starts" = 1 ] && [ "$stops" = 1 ] && [ "$(printf '%s\n' "$out" | grep -c '^PUSH')" = 4 ] \
|
||||
&& echo "PASS the batch wraps all four pushes in ONE docker start/stop" \
|
||||
|| { echo "FAIL: expected 1 start / 1 stop / 4 pushes, got:"; printf '%s\n' "$out"; fails=$((fails + 1)); }
|
||||
[ "$(printf '%s\n' "$out" | grep '^VERSION' | tr '\n' ' ')" = "VERSION b VERSION c VERSION d " ] \
|
||||
&& echo "PASS the three game images, and only they, also get a version tag" \
|
||||
|| { echo "FAIL: expected version tags for b c d only, got:"; printf '%s\n' "$out"; fails=$((fails + 1)); }
|
||||
|
||||
# Each game build is also mirrored under <MC version>-<image id>, a tag no later run
|
||||
# rewrites, so an admin can still name that exact build after :demo moves on.
|
||||
vtblock="$(awk '/^push_version_tag\(\) \{/,/^}/' "$BS")"
|
||||
[ -n "$vtblock" ] || { echo "FAIL: no push_version_tag found in $BS"; exit 1; }
|
||||
run_version_tag() { # MC_VERSION
|
||||
MC_VERSION="$1" bash -c '
|
||||
die() { printf "DIE: %s\n" "$*"; exit 1; }
|
||||
docker() {
|
||||
case "$1" in
|
||||
image) printf "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\n" ;;
|
||||
*) printf "DOCKER %s\n" "$*" ;;
|
||||
esac
|
||||
}
|
||||
push_image_to_registry() { printf "PUSH %s\n" "$1"; }
|
||||
'"$vtblock"'
|
||||
push_version_tag registry.felis.svc:5000/felis/paper:demo'
|
||||
}
|
||||
out="$(run_version_tag 26.2)"
|
||||
expect "a game build is pushed under its version tag" "PUSH registry.felis.svc:5000/felis/paper:26.2-0123456789ab" "$out"
|
||||
expect "the version tag is created from the built image" "DOCKER tag registry.felis.svc:5000/felis/paper:demo registry.felis.svc:5000/felis/paper:26.2-0123456789ab" "$out"
|
||||
case "$(run_version_tag '')" in
|
||||
*PUSH*) echo "FAIL: no Minecraft version, no version tag"; fails=$((fails + 1)) ;;
|
||||
*) echo "PASS without a resolved Minecraft version no version tag is pushed" ;;
|
||||
esac
|
||||
|
||||
# The registry refuses anonymous writes, and the platform token must never reach
|
||||
# docker's argv (ps) or root's ~/.docker: stdin into a throwaway --config dir.
|
||||
@@ -888,6 +917,44 @@ case "$out" in
|
||||
*"limbo:demo io.cri"*) echo "FAIL: only the registry pod's images may be pinned or unpinned"; fails=$((fails + 1)) ;;
|
||||
esac
|
||||
|
||||
# User servers still on a bare registry tag are pinned to the build it names BEFORE
|
||||
# this run builds and pushes new ones over it; a fresh install has nothing to pin.
|
||||
puiblock="$(awk '/^pin_user_server_images\(\) \{/,/^}/' "$BS")"
|
||||
[ -n "$puiblock" ] || { echo "FAIL: no pin_user_server_images found in $BS"; exit 1; }
|
||||
run_pin_user() { # crd-present(0/1) pin-exit
|
||||
CALLS="$calls" CRD="$1" PIN_EXIT="$2" HOST_BIN=felis CONTROL_NS=felis MINECRAFT_NS=minecraft \
|
||||
REGISTRY_URL=registry.felis.svc:5000 REGISTRY_PUSH_HOST=127.0.0.1:5000 bash -c '
|
||||
ok() { printf "OK: %s\n" "$*"; }
|
||||
warn() { printf "WARN: %s\n" "$*"; }
|
||||
kube() {
|
||||
case "$*" in
|
||||
"get crd"*) [ "$CRD" = 1 ] ;;
|
||||
*) printf "KUBE %s\n" "$*" >>"$CALLS" ;;
|
||||
esac
|
||||
}
|
||||
felis() { printf "FELIS %s\n" "$*"; return "$PIN_EXIT"; }
|
||||
'"$puiblock"'
|
||||
pin_user_server_images'
|
||||
}
|
||||
calls="$(mktemp)"
|
||||
case "$(run_pin_user 0 0)" in
|
||||
*FELIS*) echo "FAIL: without the CRD there is nothing to pin"; fails=$((fails + 1)) ;;
|
||||
*) echo "PASS a fresh install skips pinning" ;;
|
||||
esac
|
||||
out="$(run_pin_user 1 0)$(printf '\n'; cat "$calls")"
|
||||
expect "pinning reaches the registry through its loopback hostPort" "FELIS pin-images --namespace minecraft --registry registry.felis.svc:5000 --endpoint 127.0.0.1:5000" "$out"
|
||||
expect "pinning waits for the registry first" "KUBE -n felis rollout status deployment/registry" "$out"
|
||||
out="$(run_pin_user 1 1)"
|
||||
expect "a failed pin warns with the consequence" "WARN: could not pin every user server" "$out"
|
||||
rm -f "$calls"
|
||||
|
||||
mainblock="$(awk '/^main\(\) \{/,/^}/' "$BS")"
|
||||
line_of() { printf '%s\n' "$mainblock" | grep -n "^ $1\$" | head -n 1 | cut -d: -f1; }
|
||||
p="$(line_of pin_user_server_images)"; b="$(line_of build_game_stack)"; u="$(line_of push_images_to_registry)"
|
||||
[ -n "$p" ] && [ -n "$b" ] && [ -n "$u" ] && [ "$p" -lt "$b" ] && [ "$b" -lt "$u" ] \
|
||||
&& echo "PASS user servers are pinned before the game images are rebuilt and pushed" \
|
||||
|| { echo "FAIL: main must run pin_user_server_images before build_game_stack and push_images_to_registry (lines: $p $b $u)"; fails=$((fails + 1)); }
|
||||
|
||||
# --- the registry's own image must not be re-pulled on every run --------------------------
|
||||
iblock="$(awk '/^import_registry_image\(\) \{/,/^}/' "$BS")"
|
||||
[ -n "$iblock" ] || { echo "FAIL: no import_registry_image found in $BS"; exit 1; }
|
||||
|
||||
Reference in new issue
Block a user