Files
universe/scripts/check_image_match.sh
zeekayandHanzo Dev 965241ab76 one image per pod: what stages the plugins is what runs them
A node's C-Chain is a plugin, copied onto the volume by an init container at
boot. Those containers named their own image, and drifted: devnet staged from
v1.36.25 under a v1.36.123 node, mainnet from v1.36.88 under v1.36.116, testnet
from v1.34.0 — a tag with no image at all — under v1.36.123. Between 28 and 98
releases apart.

So every roll moved luxd and left the C-Chain built from an old evm. The plugin
files are rewritten on each boot, which makes them look current by any timestamp
you can check; they were written from a stale source. That is how four fleets ran
a two-month-old plugin under a current binary, and why a state-sync default
shipped in the node image did not reach the chain that needed it.

An artifact check cannot see this. The image is internally consistent — the
deployment swaps the plugin out afterwards — so it is a fact about the manifest
and is checked there: scripts/check_image_match.sh refuses a StatefulSet whose
init containers stage from anything other than the image its node runs.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-28 12:41:28 -07:00

52 lines
2.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# One image per pod: whatever stages the plugins must be what runs them.
#
# A node's C-Chain is a plugin, and the plugin is copied onto the volume by an
# init container at boot. When that container names a different image than the
# node, a roll moves the binary and leaves the plugin built from whatever the
# init container was pinned at — freshly written each boot, from a stale source,
# which reads as current by every mtime you can look at.
#
# The image is internally consistent, so a check on the artifact cannot see this:
# the deployment swaps the plugin out afterwards. It is a manifest fact, so it is
# checked here.
#
# scripts/check_image_match.sh [manifest ...] # default: every luxd StatefulSet
set -euo pipefail
cd "$(git rev-parse --show-toplevel)"
if [ $# -gt 0 ]; then
set -- "$@"
else
set -- $(grep -rl 'kind: StatefulSet' k8s/ deploy/ 2>/dev/null | sort)
fi
fail=0
for f in "$@"; do
python3 - "$f" <<'PY' || fail=1
import sys, yaml
path = sys.argv[1]
for doc in yaml.safe_load_all(open(path)):
if not doc or doc.get('kind') != 'StatefulSet':
continue
spec = doc['spec']['template']['spec']
name = doc['metadata']['name']
# Only containers that stage into the shared volume matter. Anything not
# built from the node image is a different concern and is left alone.
node = [c['image'] for c in spec.get('containers', []) if 'luxfi/node' in c.get('image', '')]
init = [(c.get('name'), c['image']) for c in spec.get('initContainers', []) if 'luxfi/node' in c.get('image', '')]
if not node or not init:
continue
want = node[0]
for cname, image in init:
if image != want:
print(f"FAIL {path} [{name}]: initContainer {cname} stages from")
print(f" {image}")
print(f" but the node runs")
print(f" {want}")
sys.exit(1)
print(f"ok {path} [{name}]: {len(init)} init container(s) match the node image")
PY
done
exit $fail