Publish curated Unraid-AppOps-Gateway source from 1c6a58589e215177617aa20bfa9d877c67874950
This commit is contained in:
1 parent
fd4b962bdd
commit
84a7616341
3 files changed
+226
-4
No files matched your search
@@ -55,6 +55,228 @@ def _host_gate_sample(module, monkeypatch):
|
||||
return controller, now, host, capacity
|
||||
|
||||
|
||||
def test_durable_saves_preserve_pending_and_profile_references(
|
||||
controller_module, monkeypatch, tmp_path
|
||||
):
|
||||
module = controller_module
|
||||
tmp_path.chmod(0o700)
|
||||
monkeypatch.setattr(module, "STATE_ROOT", tmp_path)
|
||||
monkeypatch.setattr(module, "STATE_FILE", tmp_path / "admission.json")
|
||||
controller = module.RunnerAdmissionController()
|
||||
state = module._initial_state()
|
||||
entry = state["profiles"]["frontend"]
|
||||
pending = {"profileId": "frontend", "stage": "reserved"}
|
||||
state["pending"] = pending
|
||||
|
||||
controller._save_state(state)
|
||||
pending["stage"] = "enable_requested"
|
||||
controller._save_state(state)
|
||||
entry["enabledByController"] = True
|
||||
entry["observed"] = True
|
||||
state["pending"] = None
|
||||
controller._save_state(state)
|
||||
|
||||
saved = controller._read_state()
|
||||
assert saved["revision"] == state["revision"] == 3
|
||||
assert saved["profiles"]["frontend"]["enabledByController"] is True
|
||||
assert saved["profiles"]["frontend"]["observed"] is True
|
||||
assert saved["pending"] is None
|
||||
|
||||
|
||||
def test_durable_save_preserves_drain_entry_updates(
|
||||
controller_module, monkeypatch, tmp_path
|
||||
):
|
||||
module = controller_module
|
||||
tmp_path.chmod(0o700)
|
||||
monkeypatch.setattr(module, "STATE_ROOT", tmp_path)
|
||||
monkeypatch.setattr(module, "STATE_FILE", tmp_path / "admission.json")
|
||||
controller = module.RunnerAdmissionController()
|
||||
state = module._initial_state()
|
||||
entry = state["profiles"]["frontend"]
|
||||
entry.update(enabledByController=True, drainOwned=True)
|
||||
controller._save_state(state)
|
||||
entry.update(enabledByController=False, drainOwned=False, idleSince=None)
|
||||
controller._save_state(state)
|
||||
|
||||
saved = controller._read_state()
|
||||
assert saved["profiles"]["frontend"]["enabledByController"] is False
|
||||
assert saved["profiles"]["frontend"]["drainOwned"] is False
|
||||
|
||||
|
||||
def _operator_handover_fixture(module, monkeypatch):
|
||||
controller, state, observation, _, _ = _lease_fixture(module, enabled=("frontend",))
|
||||
entry = state["profiles"]["frontend"]
|
||||
entry.update(enabledByController=False, operatorEnabled=True)
|
||||
state["bootstrapReceiptSha256"] = "a" * 64
|
||||
observation.update(specialization="active", resourceProof={"ready": True},
|
||||
taskImagesVerified=True, guardState="clear")
|
||||
observation["containers"][module.RUNNER_PROFILES["frontend"].container_name].update(
|
||||
pid=1234, startedAt=module._utc_iso())
|
||||
monkeypatch.setattr(module.os, "geteuid", lambda: 0, raising=False)
|
||||
artifact = {"sourceRevision": "b" * 40, "artifactSha256": "c" * 64}
|
||||
monkeypatch.setitem(sys.modules, "status_server", SimpleNamespace(_require_controller_release=lambda: artifact))
|
||||
monkeypatch.setattr(controller, "_process_lock", lambda: nullcontext())
|
||||
monkeypatch.setattr(controller, "_read_state", lambda: state)
|
||||
monkeypatch.setattr(controller, "_observe", lambda: observation)
|
||||
written = []
|
||||
|
||||
def persist(value, *, expected_revision):
|
||||
assert value["revision"] == expected_revision
|
||||
saved = json.loads(json.dumps(value))
|
||||
saved["revision"] += 1
|
||||
written.append(saved)
|
||||
return saved
|
||||
|
||||
monkeypatch.setattr(controller, "_write_state", persist)
|
||||
monkeypatch.setattr(controller, "_set_disabled", lambda *_args: pytest.fail("handover must not patch Gitea"))
|
||||
monkeypatch.setattr(controller, "_docker_run", lambda *_args, **_kw: pytest.fail("handover must not mutate Docker"))
|
||||
return controller, state, observation, written, artifact
|
||||
|
||||
|
||||
def test_explicit_operator_handover_is_bound_and_only_transfers_metadata(controller_module, monkeypatch):
|
||||
controller, state, _, written, _ = _operator_handover_fixture(controller_module, monkeypatch)
|
||||
preflight = controller.operator_lease_handover("frontend")
|
||||
assert preflight["executed"] is False
|
||||
assert written == []
|
||||
assert state["profiles"]["frontend"]["operatorEnabled"] is True
|
||||
result = controller.operator_lease_handover("frontend", expected_preflight=preflight["preflightSha256"])
|
||||
assert result["status"] == "handed_over" and result["executed"] is True
|
||||
assert len(written) == 1
|
||||
entry = written[0]["profiles"]["frontend"]
|
||||
assert entry["operatorEnabled"] is False and entry["enabledByController"] is True
|
||||
assert entry["lastOperatorHandover"]["containerId"] == preflight["containerId"]
|
||||
assert all(written[0]["profiles"][p] == state["profiles"][p] for p in controller_module.PROFILE_IDS if p != "frontend")
|
||||
with pytest.raises(controller_module.RunnerAdmissionError, match="preconditions_failed"):
|
||||
controller.operator_lease_handover("frontend", expected_preflight=preflight["preflightSha256"])
|
||||
assert len(written) == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize("change", ["busy", "active", "disabled", "operator_disabled", "controller_owned",
|
||||
"pending", "inactive", "bootstrap", "identity", "stopped", "pid", "started", "resource", "images", "guard"])
|
||||
def test_operator_handover_refuses_unsafe_or_unowned_observations(controller_module, monkeypatch, change):
|
||||
controller, state, observation, written, _ = _operator_handover_fixture(controller_module, monkeypatch)
|
||||
entry = state["profiles"]["frontend"]
|
||||
row = observation["runnerRows"][controller_module.RUNNER_PROFILES["frontend"].container_name]
|
||||
container = observation["containers"][controller_module.RUNNER_PROFILES["frontend"].container_name]
|
||||
if change == "busy":
|
||||
row["busy"] = True
|
||||
elif change == "active":
|
||||
observation["activeJobs"] = {999: [{"id": "foreign-job"}]}
|
||||
elif change == "disabled":
|
||||
row["disabled"] = True
|
||||
elif change == "operator_disabled":
|
||||
entry["operatorDisabled"] = True
|
||||
elif change == "controller_owned":
|
||||
entry["enabledByController"] = True
|
||||
elif change == "pending":
|
||||
state["pending"] = {"profileId": "build"}
|
||||
elif change == "inactive":
|
||||
observation["specialization"] = "legacy"
|
||||
elif change == "bootstrap":
|
||||
state["bootstrapReceiptSha256"] = None
|
||||
elif change == "identity":
|
||||
row["id"] += 1
|
||||
elif change == "stopped":
|
||||
container["running"] = False
|
||||
elif change == "pid":
|
||||
container["pid"] = 0
|
||||
elif change == "started":
|
||||
container["startedAt"] = None
|
||||
elif change == "resource":
|
||||
observation["resourceProof"] = None
|
||||
elif change == "images":
|
||||
observation["taskImagesVerified"] = False
|
||||
else:
|
||||
observation["guardState"] = "paused"
|
||||
before = copy.deepcopy(state)
|
||||
with pytest.raises(controller_module.RunnerAdmissionError, match="preconditions_failed"):
|
||||
controller.operator_lease_handover("frontend")
|
||||
assert state == before and written == []
|
||||
|
||||
|
||||
@pytest.mark.parametrize("change", ["state", "container", "artifact", "labels", "restart"])
|
||||
def test_operator_handover_rechecks_current_fingerprint(controller_module, monkeypatch, change):
|
||||
controller, state, observation, written, artifact = _operator_handover_fixture(controller_module, monkeypatch)
|
||||
preflight = controller.operator_lease_handover("frontend")
|
||||
name = controller_module.RUNNER_PROFILES["frontend"].container_name
|
||||
if change == "state":
|
||||
state["revision"] += 1
|
||||
elif change == "container":
|
||||
observation["containers"][name]["id"] = "f" * 64
|
||||
elif change == "artifact":
|
||||
artifact["sourceRevision"] = "d" * 40
|
||||
elif change == "restart":
|
||||
observation["containers"][name]["pid"] += 1
|
||||
else:
|
||||
observation["runnerRows"][name]["labels"].add("self-hosted")
|
||||
with pytest.raises(controller_module.RunnerAdmissionError, match="preflight_changed"):
|
||||
controller.operator_lease_handover("frontend", expected_preflight=preflight["preflightSha256"])
|
||||
assert written == []
|
||||
|
||||
|
||||
def test_operator_handover_requires_root_before_reading_controller_or_credentials(controller_module, monkeypatch):
|
||||
controller = controller_module.RunnerAdmissionController()
|
||||
monkeypatch.setattr(controller_module.os, "geteuid", lambda: 10001, raising=False)
|
||||
monkeypatch.setattr(controller, "_read_state", lambda: pytest.fail("non-root must not read private state"))
|
||||
with pytest.raises(controller_module.RunnerAdmissionError, match="requires_root"):
|
||||
controller.operator_lease_handover("frontend")
|
||||
|
||||
|
||||
def test_operator_handover_cas_conflict_does_not_claim_publication(controller_module, monkeypatch):
|
||||
controller, _, _, written, _ = _operator_handover_fixture(controller_module, monkeypatch)
|
||||
preflight = controller.operator_lease_handover("frontend")
|
||||
|
||||
def conflict(*_args, **_kwargs):
|
||||
raise controller_module.RunnerAdmissionError("runner_admission_state_conflict")
|
||||
|
||||
monkeypatch.setattr(controller, "_write_state", conflict)
|
||||
with pytest.raises(controller_module.RunnerAdmissionError, match="state_conflict"):
|
||||
controller.operator_lease_handover("frontend", expected_preflight=preflight["preflightSha256"])
|
||||
assert written == []
|
||||
|
||||
|
||||
def test_operator_handover_reports_uncertain_durable_write(controller_module, monkeypatch):
|
||||
controller, _, _, _, _ = _operator_handover_fixture(controller_module, monkeypatch)
|
||||
preflight = controller.operator_lease_handover("frontend")
|
||||
|
||||
def uncertain(*_args, **_kwargs):
|
||||
raise controller_module.RunnerAdmissionError("runner_admission_state_write_failed")
|
||||
|
||||
monkeypatch.setattr(controller, "_write_state", uncertain)
|
||||
with pytest.raises(controller_module.RunnerAdmissionError, match="handover_outcome_unknown"):
|
||||
controller.operator_lease_handover("frontend", expected_preflight=preflight["preflightSha256"])
|
||||
|
||||
|
||||
@pytest.mark.parametrize("args", [["--operator-handover", "frontend"],
|
||||
["--preflight-operator-handover", "frontend", "--confirm", "RETURN_CI_RUNNER_TO_MANAGED_ADMISSION"]])
|
||||
def test_handover_cli_refuses_missing_or_inappropriate_mutation_arguments(controller_module, args):
|
||||
with pytest.raises(SystemExit) as exc:
|
||||
controller_module.main(args)
|
||||
assert exc.value.code == 2
|
||||
|
||||
|
||||
@pytest.mark.parametrize("error", [RuntimeError("fake-secret-never-output"), "uncertain"])
|
||||
def test_handover_cli_redacts_errors_and_does_not_misreport_unknown_writes(controller_module, monkeypatch, capsys, error):
|
||||
controller = controller_module.RunnerAdmissionController()
|
||||
|
||||
def refused(*_args, **_kwargs):
|
||||
if error == "uncertain":
|
||||
raise controller_module.RunnerAdmissionError("runner_admission_handover_outcome_unknown")
|
||||
raise error
|
||||
|
||||
monkeypatch.setattr(controller, "operator_lease_handover", refused)
|
||||
monkeypatch.setattr(controller_module, "RunnerAdmissionController", lambda: controller)
|
||||
assert controller_module.main(["--preflight-operator-handover", "frontend"]) == 1
|
||||
raw = capsys.readouterr().out
|
||||
assert "fake-secret-never-output" not in raw
|
||||
result = json.loads(raw)
|
||||
if error == "uncertain":
|
||||
assert result["status"] == "outcome_unknown" and result["executed"] is None
|
||||
assert result["noMutation"] is False
|
||||
else:
|
||||
assert result["status"] == "blocked" and result["executed"] is False
|
||||
|
||||
|
||||
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)
|
||||
|
||||
Reference in new issue
Block a user