Publish curated Unraid-AppOps-Gateway source from 945a919d1a3bbd2bb5bd21eb221fc9b58d795d80
This commit is contained in:
1 parent
aa9f7d178c
commit
fd4b962bdd
10 files changed
+884
-37
No files matched your search
@@ -44,6 +44,89 @@ def controller_module(monkeypatch):
|
||||
)
|
||||
|
||||
|
||||
def _host_gate_sample(module, monkeypatch):
|
||||
now = [100.0]
|
||||
host = {"loadRatio": 0.62, "availableMemoryBytes": 19 * 1024**3,
|
||||
"cacheFreeBytes": 140 * 1024**3, "swapCritical": False,
|
||||
"swapSampleAt": now[0]}
|
||||
capacity = {"host": host, "decision": {"pressure": "headroom"}}
|
||||
controller = module.RunnerAdmissionController(clock=lambda: now[0])
|
||||
monkeypatch.setattr(controller, "_host_evidence", lambda: ("clear", None, capacity))
|
||||
return controller, now, host, capacity
|
||||
|
||||
|
||||
def test_safe_live_metric_fluctuations_do_not_restart_stable_headroom(controller_module, monkeypatch):
|
||||
module = controller_module
|
||||
controller, now, host, _ = _host_gate_sample(module, monkeypatch)
|
||||
state = {}
|
||||
assert controller._fresh_mutation_gate("backend", ["backend"], state)[0] == "runner_admission_host_headroom_not_stable"
|
||||
for at, load, memory, cache in [(130, 0.58, 18.5, 139.4), (160, 0.53, 18.2, 138.9)]:
|
||||
now[0] = at
|
||||
host.update(loadRatio=load, availableMemoryBytes=int(memory * 1024**3),
|
||||
cacheFreeBytes=int(cache * 1024**3), swapSampleAt=at)
|
||||
reason, _ = controller._fresh_mutation_gate("backend", ["backend"], state)
|
||||
assert reason is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize("change", ["load", "memory", "cache", "swap", "stale", "future", "pressure", "guard"])
|
||||
def test_unsafe_sample_restarts_the_entire_stability_window(controller_module, monkeypatch, change):
|
||||
module = controller_module
|
||||
controller, now, host, capacity = _host_gate_sample(module, monkeypatch)
|
||||
state = {}
|
||||
controller._fresh_mutation_gate("backend", ["backend"], state)
|
||||
now[0] = 130
|
||||
host["swapSampleAt"] = 130
|
||||
original = copy.deepcopy(capacity)
|
||||
if change == "load":
|
||||
host["loadRatio"] = 0.71
|
||||
elif change == "memory":
|
||||
host["availableMemoryBytes"] = 14 * 1024**3
|
||||
elif change == "cache":
|
||||
host["cacheFreeBytes"] = 29 * 1024**3
|
||||
elif change == "swap":
|
||||
host["swapCritical"] = True
|
||||
elif change == "stale":
|
||||
host["swapSampleAt"] = 1
|
||||
elif change == "future":
|
||||
host["swapSampleAt"] = 131
|
||||
elif change == "pressure":
|
||||
capacity["decision"]["pressure"] = "pressure"
|
||||
else:
|
||||
monkeypatch.setattr(controller, "_host_evidence", lambda: ("blocked", "host_guard_paused", capacity))
|
||||
assert controller._fresh_mutation_gate("backend", ["backend"], state)[0] is not None
|
||||
capacity.clear()
|
||||
capacity.update(original)
|
||||
host = capacity["host"]
|
||||
now[0] = 160
|
||||
host["swapSampleAt"] = 160
|
||||
monkeypatch.setattr(controller, "_host_evidence", lambda: ("clear", None, capacity))
|
||||
assert controller._fresh_mutation_gate("backend", ["backend"], state)[0] == "runner_admission_host_headroom_not_stable"
|
||||
now[0] = 220
|
||||
host["swapSampleAt"] = 220
|
||||
assert controller._fresh_mutation_gate("backend", ["backend"], state)[0] is None
|
||||
|
||||
|
||||
def test_missing_observation_interval_cannot_count_as_stable_headroom(controller_module, monkeypatch):
|
||||
module = controller_module
|
||||
controller, now, host, _ = _host_gate_sample(module, monkeypatch)
|
||||
state = {}
|
||||
controller._fresh_mutation_gate("backend", ["backend"], state)
|
||||
now[0] = 200
|
||||
host["swapSampleAt"] = 200
|
||||
assert controller._fresh_mutation_gate("backend", ["backend"], state)[0] == "runner_admission_host_headroom_not_stable"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("profile,claims", [("frontend", ["frontend"]), ("backend", ["backend", "checks"])])
|
||||
def test_profile_or_reservation_change_requires_new_stability_window(controller_module, monkeypatch, profile, claims):
|
||||
module = controller_module
|
||||
controller, now, host, _ = _host_gate_sample(module, monkeypatch)
|
||||
state = {}
|
||||
controller._fresh_mutation_gate("backend", ["backend"], state)
|
||||
now[0] = 160
|
||||
host["swapSampleAt"] = 160
|
||||
assert controller._fresh_mutation_gate(profile, claims, state)[0] == "runner_admission_host_headroom_not_stable"
|
||||
|
||||
|
||||
def _public_observation(module, now: float):
|
||||
rows = {}
|
||||
containers = {}
|
||||
@@ -257,6 +340,173 @@ def _lease_fixture(module, *, enabled=("backend", "frontend"), busy=(), queue=()
|
||||
return controller, state, observation, inventory_rows, containers
|
||||
|
||||
|
||||
def _retired_security_inventory(module):
|
||||
rows = [
|
||||
{"name": profile.display_name, "id": 101 + index, "status": "offline",
|
||||
"busy": False, "disabled": True,
|
||||
"labels": [profile.runner_label, *profile.legacy_labels]}
|
||||
for index, profile in enumerate(module.RUNNER_PROFILES.values())
|
||||
]
|
||||
rows.append({
|
||||
"name": "itworx-security-runner", "id": 9, "status": "offline",
|
||||
"busy": False, "disabled": True,
|
||||
"labels": ["linux-validation", "docker-validation", "ubuntu-latest",
|
||||
"ubuntu-24.04", "ubuntu-22.04", "mcp-validation", "unraid"],
|
||||
})
|
||||
return rows
|
||||
|
||||
|
||||
def test_exact_retired_v1_security_labels_do_not_block_new_profile_inventory(controller_module):
|
||||
module = controller_module
|
||||
rows, ids = module.RunnerAdmissionController._normalized_runner_rows(
|
||||
_retired_security_inventory(module)
|
||||
)
|
||||
claims, profile_ids, _ = module.RunnerAdmissionController._active_claims(rows, ids, {})
|
||||
assert claims == []
|
||||
assert set(profile_ids) == set(module.PROFILE_IDS)
|
||||
assert rows["itworx-security-runner"]["disabled"] is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize("change", ["enabled", "busy", "missing", "foreign", "replaced_id"])
|
||||
def test_retired_v1_security_variant_refuses_live_or_changed_registration(controller_module, change):
|
||||
module = controller_module
|
||||
inventory = _retired_security_inventory(module)
|
||||
old = inventory[-1]
|
||||
if change == "enabled":
|
||||
old["disabled"] = False
|
||||
elif change == "busy":
|
||||
old["busy"] = True
|
||||
elif change == "missing":
|
||||
old["labels"].remove("mcp-validation")
|
||||
elif change == "replaced_id":
|
||||
old["id"] = 99
|
||||
else:
|
||||
old["labels"].append("foreign-capability")
|
||||
with pytest.raises(module.RunnerAdmissionError, match="legacy_runner_labels_invalid"):
|
||||
module.RunnerAdmissionController._normalized_runner_rows(inventory)
|
||||
|
||||
|
||||
def test_retired_v1_security_active_job_still_refuses_admission(controller_module):
|
||||
module = controller_module
|
||||
rows, ids = module.RunnerAdmissionController._normalized_runner_rows(
|
||||
_retired_security_inventory(module)
|
||||
)
|
||||
with pytest.raises(module.RunnerAdmissionError, match="legacy_claim_unmapped"):
|
||||
module.RunnerAdmissionController._active_claims(rows, ids, {9: [{"id": "foreign-active"}]})
|
||||
|
||||
|
||||
def test_original_security_label_contract_is_preserved(controller_module):
|
||||
module = controller_module
|
||||
inventory = _retired_security_inventory(module)
|
||||
inventory[-1]["labels"] = sorted(module.LEGACY_REQUIRED_LABELS["itworx-security-runner"])
|
||||
rows, _ = module.RunnerAdmissionController._normalized_runner_rows(inventory)
|
||||
assert rows["itworx-security-runner"]["labels"] == set(inventory[-1]["labels"])
|
||||
|
||||
|
||||
def _migrated_tower_inventory(module):
|
||||
return [*_retired_security_inventory(module),
|
||||
{"name": "itworx-unraid-deploy", "id": 13, "status": "online", "busy": False,
|
||||
"disabled": False, "labels": ["unraid-deploy"]},
|
||||
{"name": "itworx-windows11-native", "id": 14, "status": "online", "busy": False,
|
||||
"disabled": False, "labels": ["windows-native"]}]
|
||||
|
||||
|
||||
def test_migrated_tower_preserves_idle_native_registrations_without_family_claims(controller_module):
|
||||
module = controller_module
|
||||
rows, ids = module.RunnerAdmissionController._normalized_runner_rows(_migrated_tower_inventory(module))
|
||||
claims, profiles, assigned = module.RunnerAdmissionController._active_claims(rows, ids, {})
|
||||
assert claims == []
|
||||
assert set(profiles) == set(module.PROFILE_IDS)
|
||||
assert 13 not in assigned and 14 not in assigned
|
||||
|
||||
|
||||
@pytest.mark.parametrize("index", [-2, -1])
|
||||
@pytest.mark.parametrize("change", ["id", "labels", "name"])
|
||||
def test_preserved_native_identity_or_capability_drift_refuses(controller_module, index, change):
|
||||
module = controller_module
|
||||
inventory = _migrated_tower_inventory(module)
|
||||
if change == "id":
|
||||
inventory[index]["id"] = 500
|
||||
elif change == "labels":
|
||||
inventory[index]["labels"].append("linux-validation")
|
||||
else:
|
||||
inventory[index]["name"] = "foreign-native"
|
||||
with pytest.raises(module.RunnerAdmissionError, match="preserved_native_identity_invalid"):
|
||||
module.RunnerAdmissionController._normalized_runner_rows(inventory)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("index", [-2, -1])
|
||||
@pytest.mark.parametrize("busy", [True, False])
|
||||
def test_active_preserved_native_blocks_family_admission_until_it_drains(controller_module, index, busy):
|
||||
module = controller_module
|
||||
inventory = _migrated_tower_inventory(module)
|
||||
row = inventory[index]
|
||||
row["busy"] = busy
|
||||
rows, ids = module.RunnerAdmissionController._normalized_runner_rows(inventory)
|
||||
active = {} if busy else {row["id"]: [{"id": "native-active"}]}
|
||||
with pytest.raises(module.RunnerAdmissionError, match="preserved_native_active"):
|
||||
module.RunnerAdmissionController._active_claims(rows, ids, active)
|
||||
|
||||
|
||||
def test_foreign_enabled_registration_still_refuses_with_preserved_natives(controller_module):
|
||||
module = controller_module
|
||||
inventory = _migrated_tower_inventory(module)
|
||||
inventory.append({"name": "foreign", "id": 500, "status": "online", "busy": False,
|
||||
"disabled": False, "labels": ["linux-validation"]})
|
||||
rows, ids = module.RunnerAdmissionController._normalized_runner_rows(inventory)
|
||||
with pytest.raises(module.RunnerAdmissionError, match="enabled_registration_unmapped"):
|
||||
module.RunnerAdmissionController._active_claims(rows, ids, {})
|
||||
|
||||
|
||||
def test_absent_retired_container_is_confirmed_by_successful_exact_inventory(controller_module, monkeypatch):
|
||||
module = controller_module
|
||||
calls = []
|
||||
def run(args, **_kwargs):
|
||||
calls.append(args)
|
||||
if args[1] == "inspect":
|
||||
return SimpleNamespace(returncode=1, stdout="[]\n", stderr="opaque failure")
|
||||
return SimpleNamespace(returncode=0, stdout="", stderr="")
|
||||
monkeypatch.setattr(module.subprocess, "run", run)
|
||||
assert module.RunnerAdmissionController()._container("itworx-security-runner") is None
|
||||
assert all(args[1] != "inspect" for args in calls)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("stdout,exit_code", [
|
||||
("", 1), ("foreign", 0), ("a" * 64 + " wrong-name\n", 0),
|
||||
(("a" * 64 + " itworx-security-runner\n") * 2, 0),
|
||||
])
|
||||
def test_retired_container_inventory_failure_or_ambiguity_refuses(controller_module, monkeypatch, stdout, exit_code):
|
||||
module = controller_module
|
||||
monkeypatch.setattr(module.subprocess, "run", lambda *_args, **_kwargs:
|
||||
SimpleNamespace(returncode=exit_code, stdout=stdout, stderr="opaque failure"))
|
||||
with pytest.raises(module.RunnerAdmissionError):
|
||||
module.RunnerAdmissionController()._container("itworx-security-runner")
|
||||
|
||||
|
||||
def test_present_retired_container_inspect_failure_is_not_absence(controller_module, monkeypatch):
|
||||
module = controller_module
|
||||
def run(args, **_kwargs):
|
||||
if args[1] == "inspect":
|
||||
return SimpleNamespace(returncode=1, stdout="[]\n", stderr="opaque failure")
|
||||
return SimpleNamespace(returncode=0, stdout="a" * 64 + " itworx-security-runner\n", stderr="")
|
||||
monkeypatch.setattr(module.subprocess, "run", run)
|
||||
with pytest.raises(module.RunnerAdmissionError, match="container_inspect_failed"):
|
||||
module.RunnerAdmissionController()._container("itworx-security-runner")
|
||||
|
||||
|
||||
def test_present_retired_container_replacement_refuses(controller_module, monkeypatch):
|
||||
module = controller_module
|
||||
def run(args, **_kwargs):
|
||||
if args[1] == "inspect":
|
||||
row = {"Id": "b" * 64, "Name": "/itworx-security-runner", "State": {"Running": False},
|
||||
"Config": {"Labels": {"com.docker.compose.project": module.COMPOSE_PROJECT}}}
|
||||
return SimpleNamespace(returncode=0, stdout=json.dumps([row]), stderr="")
|
||||
return SimpleNamespace(returncode=0, stdout="a" * 64 + " itworx-security-runner\n", stderr="")
|
||||
monkeypatch.setattr(module.subprocess, "run", run)
|
||||
with pytest.raises(module.RunnerAdmissionError, match="container_identity_invalid"):
|
||||
module.RunnerAdmissionController()._container("itworx-security-runner")
|
||||
|
||||
|
||||
def _raw_job(module, job, *, runner_id=None, active=False):
|
||||
row = {
|
||||
"id": job["id"],
|
||||
@@ -514,7 +764,7 @@ def test_runner_inventory_maps_exact_gitea_display_names_to_fixed_containers(
|
||||
"disabled": True,
|
||||
"labels": [profile.runner_label, *profile.legacy_labels],
|
||||
}
|
||||
for index, profile in enumerate(module.RUNNER_PROFILES.values(), start=10)
|
||||
for index, profile in enumerate(module.RUNNER_PROFILES.values(), start=100)
|
||||
]
|
||||
|
||||
by_name, by_id = module.RunnerAdmissionController._normalized_runner_rows(rows)
|
||||
|
||||
Reference in new issue
Block a user