Release v0.1.16
Dependency Audit / dependency-audit (push) Failing after 1m47s
Deployment Installer / deployment-installer (push) Successful in 6s
Security Audit / security-audit (push) Successful in 11m18s
Developer Meta-package Release / publish-package (push) Successful in 11s

This commit is contained in:
2026-08-05 19:52:32 +02:00
parent 61463a24cb
commit 3ca068f76a
42 changed files with 5433 additions and 189 deletions
@@ -14,6 +14,7 @@ from urllib.request import Request, urlopen
JsonObject = dict[str, Any]
CommandRunner = Callable[[Sequence[str]], JsonObject]
ActionRunner = Callable[[Sequence[str]], None]
JsonFetcher = Callable[[str, str], JsonObject]
@@ -26,11 +27,13 @@ def collect_kubernetes_evidence(
exercise_api_pod_loss: bool = False,
timeout_seconds: float = 180.0,
command_runner: CommandRunner | None = None,
action_runner: ActionRunner | None = None,
json_fetcher: JsonFetcher | None = None,
) -> JsonObject:
"""Inspect a live cluster and optionally exercise one API pod replacement."""
run_json = command_runner or _kubectl_json
run_action = action_runner or _kubectl_action
fetch_json = json_fetcher or _fetch_json
nodes = run_json(("get", "nodes", "-o", "json"))
pods = run_json(
@@ -76,6 +79,7 @@ def collect_kubernetes_evidence(
initial_pods=pods,
timeout_seconds=timeout_seconds,
run_json=run_json,
run_action=run_action,
fetch_json=fetch_json,
)
evidence = {
@@ -211,6 +215,7 @@ def _exercise_api_pod_loss(
initial_pods: Mapping[str, Any],
timeout_seconds: float,
run_json: CommandRunner,
run_action: ActionRunner,
fetch_json: JsonFetcher,
) -> JsonObject:
candidates = [
@@ -228,7 +233,7 @@ def _exercise_api_pod_loss(
victim = sorted(candidates, key=lambda item: item["name"])[0]
initial_uids = {item["uid"] for item in candidates}
desired_ready = len(candidates)
run_json(
run_action(
(
"-n",
namespace,
@@ -236,8 +241,6 @@ def _exercise_api_pod_loss(
"pod",
victim["name"],
"--wait=false",
"-o",
"json",
)
)
deadline = time.monotonic() + timeout_seconds
@@ -309,6 +312,22 @@ def _kubectl_json(arguments: Sequence[str]) -> JsonObject:
return payload
def _kubectl_action(arguments: Sequence[str]) -> None:
kubectl = shutil.which("kubectl")
if kubectl is None:
raise ValueError("kubectl is required for Kubernetes evidence collection")
result = subprocess.run(
(kubectl, *arguments),
check=False,
capture_output=True,
text=True,
timeout=60,
)
if result.returncode:
detail = result.stderr.strip() or result.stdout.strip()
raise ValueError(f"kubectl failed: {detail}")
def _fetch_json(url: str, api_key: str) -> JsonObject:
request = Request(
url,