3"""ARC activation ordering and held-admission reconciliation selftests."""
5from __future__
import annotations
7from collections.abc
import Callable, Sequence
8from typing
import Any, Protocol
10import fleet_reconcile_process
as frp
13class ApplyHost(Protocol):
14 """Apply and validate one host through the reconciler transaction."""
20 run: Callable[[Sequence[str]], frp.CommandResult],
22 expected_check_changes: int,
23 ) -> tuple[bool, int]:
24 """Return whether the host reached its declared safe state."""
28def _data() -> dict[str, Any]:
29 """Return one ARC producer fixture."""
31 "runner_image": {
"source_host":
"producer"},
35 "runners": {
"instances": 1},
36 "provisions": [
"one",
"two"],
42def _identity(argv: Sequence[str]) -> tuple[str, str]:
43 """Return the reconciler verb and host from fleet argv."""
45 "reconcile-parked-apply":
"parked-apply",
46 "reconcile-parked-check":
"parked-check",
47 "reconcile-activate":
"activate",
48 "reconcile-activation-check":
"activation-check",
49 "capacity-quarantine":
"quarantine",
50 "capacity-restore":
"restore",
52 return names.get(argv[2], argv[2]), argv[-1]
55def _clean() -> frp.CommandResult:
56 """Return the accepted two-play ARC check evidence."""
57 row =
"producer : ok=9 changed={} unreachable=0 failed=0 skipped=1 rescued=0 ignored=0\n"
58 return frp.CommandResult(0, row.format(1) + row.format(0),
"")
61def _success_and_failures(apply_host: ApplyHost, failures: list[str]) ->
None:
62 """Prove successful order and activation-check failure quarantine."""
64 calls: list[tuple[str, str]] = []
66 def success(argv: Sequence[str]) -> frp.CommandResult:
67 verb, host = _identity(argv)
68 calls.append((verb, host))
71 if verb
in {
"parked-check",
"activation-check"}
72 else frp.CommandResult(0,
"",
"")
75 if not apply_host(data,
"producer", success, expected_check_changes=1)[0]:
76 failures.append(
"ARC declarative activation failed")
78 (
"parked-apply",
"producer"),
79 (
"parked-check",
"producer"),
80 (
"activate",
"producer"),
81 (
"activation-check",
"producer"),
82 (
"restore",
"producer"),
85 failures.append(
"ARC activation/check/restore order drifted")
88 def failed_check(argv: Sequence[str]) -> frp.CommandResult:
89 verb, host = _identity(argv)
90 calls.append((verb, host))
91 if verb ==
"parked-check":
93 return frp.CommandResult(1
if verb ==
"activation-check" else 0,
"",
"")
95 if apply_host(data,
"producer", failed_check, expected_check_changes=1)[0]:
96 failures.append(
"failed held ARC activation check passed")
97 if calls[-2:] != [(
"activation-check",
"producer"), (
"quarantine",
"producer")]:
98 failures.append(
"failed ARC activation check did not retain zero")
101def _hard_kill_cut(apply_host: ApplyHost, failures: list[str]) ->
None:
102 """Prove controller death before validation leaves marker and live zero."""
104 class SimulatedHardKill(BaseException):
105 """Model controller death after held declarative activation."""
107 authority = {
"marker":
True,
"live_zero":
True,
"helm_declared":
False}
108 calls: list[tuple[str, str]] = []
110 def kill(argv: Sequence[str]) -> frp.CommandResult:
111 verb, host = _identity(argv)
112 calls.append((verb, host))
113 if verb ==
"parked-check":
115 if verb ==
"activate":
116 authority[
"helm_declared"] =
True
117 if verb ==
"activation-check":
118 if not authority[
"live_zero"]
or not authority[
"marker"]:
119 failures.append(
"ARC activation check ran without held zero admission")
120 raise SimulatedHardKill
121 return frp.CommandResult(0,
"",
"")
124 apply_host(_data(),
"producer", kill, expected_check_changes=1)
125 failures.append(
"post-activation hard-kill cut returned")
126 except SimulatedHardKill:
128 if not all(authority.values()):
129 failures.append(
"hard kill lost marker, zero ceiling, or Helm authority")
130 if calls[-2:] != [(
"activate",
"producer"), (
"activation-check",
"producer")]:
131 failures.append(
"ARC hard-kill cut did not precede sole opener")
134def run(apply_host: ApplyHost) -> list[str]:
135 """Return every ARC activation state-machine failure."""
136 failures: list[str] = []
137 _success_and_failures(apply_host, failures)
138 _hard_kill_cut(apply_host, failures)