150 lines
8 KiB
Python
150 lines
8 KiB
Python
import copy
|
|
import importlib.util
|
|
import json
|
|
from pathlib import Path
|
|
import tempfile
|
|
import unittest
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
spec = importlib.util.spec_from_file_location("weekly", ROOT / "scripts/weekly_allocation_report.py")
|
|
weekly = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(weekly)
|
|
|
|
|
|
class WeeklyAllocationTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.end = "2026-10-19T06:00:00+00:00"
|
|
self.resource = "resource:railiance:reef-railiance:k3s"
|
|
self.cluster = {
|
|
"schema_version": "railiance.cluster-resource-observation.v1",
|
|
"resource_id": self.resource, "captured_at": self.end,
|
|
"utilization": {"workloads": [{"namespace": "pilot", "workload": "app", "pods": 1,
|
|
"requests": {"cpu_millicores": 60, "memory_bytes": 256},
|
|
"limits": {"cpu_millicores": 1000, "memory_bytes": 1024}}]},
|
|
}
|
|
self.allocation = {
|
|
"schema": "custodian.allocation-reconcile.v1", "captured_at": self.end,
|
|
"host": {"cluster_resource_id": self.resource}, "count_host_and_cluster_cpus_once": True,
|
|
"workloads": [{"namespace": "pilot", "workload": "app", "owner": "railiance-apps"}],
|
|
}
|
|
self.telemetry = {"captured_at": self.end, "window": "7d", "step": "5m", "queries": {}}
|
|
self.inventory = {"id": self.resource}
|
|
self.provenance = {"source_observation": self.end, "revisions": {"collector": "a" * 40}}
|
|
|
|
def add_metric(self, name="cpu_p95_m", value="1.5", workload=True):
|
|
labels = {"namespace": "pilot"}
|
|
if workload:
|
|
labels["workload"] = "app"
|
|
self.telemetry["queries"][name] = {"promql": f"aggregate_{name}[7d:5m]",
|
|
"response": {"status": "success", "data": {"resultType": "vector", "result": [
|
|
{"metric": labels, "value": [weekly.timestamp(self.end).timestamp(), value]}]}}}
|
|
|
|
def report(self, **kwargs):
|
|
return weekly.build_report(self.cluster, self.allocation, self.telemetry, self.inventory,
|
|
self.provenance, as_of=kwargs.pop("as_of", self.end), **kwargs)
|
|
|
|
def test_namespace_is_not_workload_demand(self):
|
|
self.add_metric(workload=False)
|
|
report = self.report()
|
|
self.assertEqual(report["namespace_context"]["pilot"]["cpu_p95_m"]["value"], 1.5)
|
|
self.assertIsNone(report["workloads"][0]["metrics"]["cpu_p95_m"]["value"])
|
|
self.assertEqual(report["recommendations"][0]["decision"], "investigate")
|
|
|
|
def test_missing_zero_and_nonfinite_are_distinct(self):
|
|
self.add_metric(value="0")
|
|
self.add_metric("memory_peak_bytes", value="NaN")
|
|
row = self.report()["workloads"][0]
|
|
self.assertEqual(row["metrics"]["cpu_p95_m"]["value"], 0)
|
|
self.assertIsNone(row["metrics"]["memory_peak_bytes"]["value"])
|
|
self.assertIsNone(row["metrics"]["errors"]["value"])
|
|
|
|
def test_duplicate_or_extra_labels_do_not_aggregate(self):
|
|
self.add_metric()
|
|
samples = self.telemetry["queries"]["cpu_p95_m"]["response"]["data"]["result"]
|
|
samples.append(copy.deepcopy(samples[0]))
|
|
self.assertIsNone(self.report()["workloads"][0]["metrics"]["cpu_p95_m"]["value"])
|
|
samples.pop()
|
|
samples[0]["metric"]["pod"] = "app-123"
|
|
self.assertIsNone(self.report()["workloads"][0]["metrics"]["cpu_p95_m"]["value"])
|
|
|
|
def test_failure_response_is_missing(self):
|
|
self.add_metric()
|
|
self.telemetry["queries"]["cpu_p95_m"]["response"]["status"] = "error"
|
|
self.assertIsNone(self.report()["workloads"][0]["metrics"]["cpu_p95_m"]["value"])
|
|
|
|
def test_stable_recommendation_and_only_comparable_weeks(self):
|
|
self.add_metric(value="2")
|
|
current = self.report()
|
|
previous = copy.deepcopy(current)
|
|
previous["period"]["capture_end"] = "2026-10-12T06:00:00+00:00"
|
|
previous["workloads"][0]["metrics"]["cpu_p95_m"]["value"] = 1
|
|
previous["workloads"][0]["metrics"]["cpu_p95_m"].update(
|
|
window_end="2026-10-12T06:00:00+00:00", window_start="2026-10-05T06:00:00+00:00")
|
|
previous["recommendations"][0]["receiving_task"] = "OWNER-WP-0001-T01"
|
|
compared = self.report(previous=previous)
|
|
self.assertEqual(compared["workloads"][0]["week_on_week"]["cpu_p95_m"], 1)
|
|
self.assertEqual(compared["recommendations"][0]["id"], previous["recommendations"][0]["id"])
|
|
self.assertEqual(compared["recommendations"][0]["receiving_task"], "OWNER-WP-0001-T01")
|
|
previous["query_signature"] = "changed"
|
|
self.assertIsNone(self.report(previous=previous)["workloads"][0]["week_on_week"]["cpu_p95_m"])
|
|
|
|
def test_capture_consecutive_but_metric_windows_not_consecutive(self):
|
|
self.add_metric(value="2")
|
|
previous = copy.deepcopy(self.report())
|
|
previous["period"]["capture_end"] = "2026-10-12T06:00:00+00:00"
|
|
previous["workloads"][0]["metrics"]["cpu_p95_m"].update(
|
|
window_end="2026-10-14T06:00:00+00:00", window_start="2026-10-07T06:00:00+00:00")
|
|
row = self.report(previous=previous)["workloads"][0]
|
|
self.assertIsNone(row["week_on_week"]["cpu_p95_m"])
|
|
self.assertIn("not consecutive", row["comparison_reasons"]["cpu_p95_m"])
|
|
|
|
def test_future_and_stale_observations_stay_unaccepted(self):
|
|
for time, state in [("2026-10-18T06:00:00+00:00", "future"), ("2026-10-22T06:00:00+00:00", "stale")]:
|
|
report = self.report(as_of=time)
|
|
self.assertEqual(report["freshness"]["telemetry"]["status"], state)
|
|
self.assertEqual(report["recommendations"][0]["decision"], "investigate")
|
|
|
|
def test_matching_owner_review_required_for_keep(self):
|
|
for metric in weekly.METRICS:
|
|
self.add_metric(metric)
|
|
rid = self.report()["recommendations"][0]["id"]
|
|
review = {"decision": "keep", "owner": "railiance-apps", "evidence_refs": ["run:pilot"],
|
|
"receiving_task": "CUST-WP-0071-T04", "configuration": {"cpu_request_m": 60},
|
|
"rollback_ref": "unchanged-config", "reviewed_at": self.end, "period_end": self.end,
|
|
"coverage_ref": "coverage:pilot", "service_acceptance_ref": "acceptance:pilot", "margin_ref": "budget:pilot"}
|
|
self.assertEqual(self.report(reviews={rid: review})["recommendations"][0]["decision"], "keep")
|
|
review["owner"] = "other"
|
|
self.assertEqual(self.report(reviews={rid: review})["recommendations"][0]["decision"], "investigate")
|
|
|
|
def test_identity_and_snapshot_mismatch_refused(self):
|
|
self.inventory["id"] = "another-cluster"
|
|
with self.assertRaises(ValueError):
|
|
self.report()
|
|
self.inventory["id"] = self.resource
|
|
self.allocation["captured_at"] = "2026-10-12T06:00:00+00:00"
|
|
with self.assertRaises(ValueError):
|
|
self.report()
|
|
|
|
def test_retention_retries_and_revisions(self):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
output = Path(directory) / "week.json"
|
|
report = self.report()
|
|
weekly.retain(output, report)
|
|
weekly.retain(output, report)
|
|
report["as_of"] = "different"
|
|
with self.assertRaises(ValueError):
|
|
weekly.retain(output, report)
|
|
self.assertEqual(json.loads(output.read_text())["as_of"], self.end)
|
|
self.assertEqual(list(Path(directory).iterdir()), [output])
|
|
|
|
def test_historical_fixture_preserves_known_limits(self):
|
|
load = lambda suffix: json.loads((ROOT / f"docs/evidence/2026-09-28-{suffix}.json").read_text())
|
|
report = weekly.build_report(load("cluster-observation"), load("allocation-reconcile"),
|
|
load("allocation-telemetry"), self.inventory, load("allocation-provenance"), as_of=self.end)
|
|
self.assertEqual(report["node"]["reservation_residual_cpu_m"], 580)
|
|
self.assertEqual(report["namespace_context"]["forgejo"]["cpu_p95_m"]["value"], 1454.3502378724597)
|
|
self.assertTrue(all(r["decision"] == "investigate" for r in report["recommendations"]))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|