fix(lux): the reported version is the one that is running

lux-operator is scaled to 0, so the LuxNetwork CR's image tag is only a
declaration and can be arbitrarily stale. Mainnet's CR says v1.34.0 — a tag
that does not exist in the registry — while all five nodes answer
info.getNodeVersion with luxd/1.36.2; testnet's says v1.32.12 against a
running 1.36.24.

Reporting the CR tag as `version` would put a fabricated number on the
dashboard. `version` is now the observed value and `image` stays as declared,
so a drift between intent and reality is visible instead of hidden.

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
2026-07-27 19:26:09 -07:00
co-authored by Hanzo Dev
parent 1f30a4c21c
commit 3f54647e51
2 changed files with 35 additions and 7 deletions
+7 -6
View File
@@ -437,14 +437,15 @@ async def _collect_network(
_probe_node_rpc(http, _pod_proxy(ns, n.pod_name, http_port), n) for n in nodes
])
# Without a CR, derive the declared shape from what is actually running.
# The CR declares intent; the nodes report what is running. When they
# disagree — an unreconciled CR — the running version is the true one, so
# ``version`` is observed and ``image`` stays as declared.
if not fleet.validators:
fleet.validators = len(nodes)
if not fleet.version:
ver = next((n.version for n in nodes if n.version), None)
if ver:
fleet.version = ver
fleet.image = ver
observed = next((n.version for n in nodes if n.version), None)
if observed:
fleet.version = observed
fleet.image = fleet.image or observed
if fleet.phase == "Unknown" and any(n.rpc_reachable for n in nodes):
fleet.phase = "Running"
+28 -1
View File
@@ -116,7 +116,8 @@ class TestCollectNetwork:
assert fleet.network == "devnet"
assert fleet.namespace == "lux-devnet"
assert fleet.version == "v1.36.25" # from CR
assert fleet.version == "luxd/1.36.25" # observed, not the CR tag
assert fleet.image == "ghcr.io/luxfi/node:v1.36.25" # declared
assert fleet.plugin_set == "s3://lux-plugins-devnet/v1.3.1/"
assert fleet.validators == 5
assert fleet.nodes_total == 5
@@ -152,6 +153,32 @@ class TestCollectNetwork:
assert fleet.phase == "Running"
assert fleet.total_peers == 7
@pytest.mark.asyncio
async def test_running_version_wins_over_a_stale_cr(self):
"""lux-operator is scaled to 0, so a CR tag can be arbitrarily stale.
Mainnet's CR says v1.34.0 — a tag that does not exist — while all five
nodes run luxd/1.36.2. The dashboard must show what is running."""
pods = {i: {"phase": "Running", "ready": True, "ip": f"10.2.0.{i}"}
for i in range(5)}
stale_cr = {"spec": {"validators": 5,
"image": {"repository": "ghcr.io/luxfi/node",
"tag": "v1.34.0"}},
"status": {"phase": "Running"}}
async def fake_probe(http, base, node: LuxNode):
node.rpc_reachable = True
node.version = "luxd/1.36.2"
with patch.object(lux_fleet, "_read_luxnetwork_cr", return_value=stale_cr), \
patch.object(lux_fleet, "_list_luxd_pods", return_value=pods), \
patch.object(lux_fleet, "_probe_node_rpc", side_effect=fake_probe), \
patch.object(lux_fleet, "_dex_summary", side_effect=lambda n, net: _dex_stub()):
fleet = await lux_fleet._collect_network(
"mainnet", custom_api=object(), core_api=object(), http=object())
assert fleet.version == "luxd/1.36.2"
assert fleet.image == "ghcr.io/luxfi/node:v1.34.0"
@pytest.mark.asyncio
async def test_unreachable_cluster_reports_error_not_silence(self):
def boom(*a, **kw):