Files
CosmicClash/scripts/verify_agones_allocation_response.py
Josh Creek 52ee181042 fix(kind): validate the allocation response Agones actually returns
With the Fleet readiness wait corrected, the gate reached the allocation
check for the first time and failed with "allocation did not return a
GameServer" -- while the cluster dump shows the allocation plainly
succeeded: one GameServer Allocated, Fleet reporting ALLOCATED 1.

GameServerAllocationStatus is flat: state, gameServerName, address,
ports, nodeName. It does not embed the allocated GameServer. The
validator read status.gameServer.metadata.name and
status.gameServer.status.{address,ports}, a shape Agones never sends,
and its unit tests asserted that same invented shape -- so validator and
tests agreed with each other while both disagreed with Agones. Nothing
caught it because the gate had never once allocated anything.

Read the real fields, keeping every existing check: non-empty name,
address neither blank nor unspecified, exactly one named "game" port in
range.

Also print the response body when validation fails. work_dir is removed
by the EXIT trap, so a shape mismatch was otherwise invisible from CI --
which is how this survived. If the shape is still not what I expect, the
next run says so instead of costing another round trip.
2026-09-05 22:03:41 +01:00

60 lines
2.5 KiB
Python

#!/usr/bin/env python3
"""Validate the small Agones allocation response surface used by the smoke gate."""
import json
import sys
from typing import Any
def validate_allocation(document: dict[str, Any]) -> tuple[str, int]:
status = document.get("status")
if not isinstance(status, dict) or status.get("state") != "Allocated":
raise ValueError(f"allocation state is {status.get('state') if isinstance(status, dict) else None!r}, expected 'Allocated'")
# GameServerAllocationStatus is flat: state, gameServerName, address,
# ports, nodeName. It does not embed the allocated GameServer object. This
# validator originally read status.gameServer.metadata.name and
# status.gameServer.status.{address,ports}, and its tests asserted that
# same invented shape, so both agreed with each other and neither agreed
# with Agones -- undetected because the gate never once got far enough to
# allocate anything.
name = status.get("gameServerName")
if not isinstance(name, str) or not name.strip():
raise ValueError("allocation did not return a gameServerName")
address = status.get("address")
if not isinstance(address, str) or not address.strip() or any(char.isspace() for char in address):
raise ValueError(f"allocation returned an invalid address: {address!r}")
if address in {"0.0.0.0", "::"}:
raise ValueError(f"allocation returned an unspecified address: {address!r}")
ports = status.get("ports")
if not isinstance(ports, list):
raise ValueError("allocation returned no ports")
game_ports = [
entry.get("port")
for entry in ports
if isinstance(entry, dict) and entry.get("name") == "game"
]
if len(game_ports) != 1 or not isinstance(game_ports[0], int) or not 1 <= game_ports[0] <= 65535:
raise ValueError(f"allocation did not return exactly one valid named game port: {ports!r}")
return name, game_ports[0]
def main() -> int:
if len(sys.argv) != 2:
print(f"usage: {sys.argv[0]} allocation.json", file=sys.stderr)
return 2
try:
with open(sys.argv[1], encoding="utf-8") as handle:
name, port = validate_allocation(json.load(handle))
except (OSError, ValueError, json.JSONDecodeError) as error:
print(f"8.49 allocation validation failed: {error}", file=sys.stderr)
return 1
print(f"8.49 PASS: Fleet became ready; GameServer {name} returned game UDP port {port}")
return 0
if __name__ == "__main__":
raise SystemExit(main())