From 627bc6a1b6009d69192acff54a7f63e927163ee6 Mon Sep 17 00:00:00 2001 From: Public Source Publisher Date: Sat, 10 Oct 2026 23:22:07 +0000 Subject: [PATCH] Publish curated Unraid-AppOps-Gateway source from 4e5a8f24881de4bde2ef649d3bf3817715241e9d --- PUBLIC_SOURCE_EXPORT.md | 2 +- PUBLIC_SOURCE_MANIFEST.json | 14 +- backend/tests/test_runner_fleet_specialize.py | 57 +- .../tests/test_runner_resource_envelope.py | 516 +++++++++++++++++- ...st_runner_specialization_child_boundary.py | 17 +- 5 files changed, 586 insertions(+), 20 deletions(-) diff --git a/PUBLIC_SOURCE_EXPORT.md b/PUBLIC_SOURCE_EXPORT.md index 17bc425..40503a2 100644 --- a/PUBLIC_SOURCE_EXPORT.md +++ b/PUBLIC_SOURCE_EXPORT.md @@ -1,3 +1,3 @@ # Public source export -This source snapshot was generated from the private canonical repository at revision `ac2bae3fcb4ec64df12f9313d09a3128c7cc6614`. It contains no private Git history or operational evidence. Changes are published from the canonical repository. +This source snapshot was generated from the private canonical repository at revision `4e5a8f24881de4bde2ef649d3bf3817715241e9d`. It contains no private Git history or operational evidence. Changes are published from the canonical repository. diff --git a/PUBLIC_SOURCE_MANIFEST.json b/PUBLIC_SOURCE_MANIFEST.json index f907ae3..403cb51 100644 --- a/PUBLIC_SOURCE_MANIFEST.json +++ b/PUBLIC_SOURCE_MANIFEST.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "sourceRevision": "ac2bae3fcb4ec64df12f9313d09a3128c7cc6614", + "sourceRevision": "4e5a8f24881de4bde2ef649d3bf3817715241e9d", "files": [ { "path": ".dockerignore", @@ -1659,8 +1659,8 @@ }, { "path": "backend/tests/test_runner_fleet_specialize.py", - "sha256": "da0b0099ab3d8b5e651db0d69aa6470e67be911ec6f2814216c05889ad5f8065", - "bytes": 195475 + "sha256": "bdd7ac3ba94ddb2fc73e6db5744aa1ed363961631408bd8552077b3f0cc66199", + "bytes": 198961 }, { "path": "backend/tests/test_runner_fleet_v2_execution_surface.py", @@ -1684,8 +1684,8 @@ }, { "path": "backend/tests/test_runner_resource_envelope.py", - "sha256": "afa94e168af82c28f91f0423f593dd59f55874bd203ba1ba9ec57c7c4970a3ec", - "bytes": 70027 + "sha256": "6c3405c25d965a6c8e492385c03c465204f4723d85e9b81ed7320ffc2ecc0c1a", + "bytes": 98090 }, { "path": "backend/tests/test_runner_scheduler_liveness_recovery.py", @@ -1694,8 +1694,8 @@ }, { "path": "backend/tests/test_runner_specialization_child_boundary.py", - "sha256": "820faac9350a3c30613cc3fe928857cff29f3df364563a4ceb916b4b8b5e0c51", - "bytes": 8474 + "sha256": "62bd56ea8ac22c21b8c39dfeec885285063e778a3b8490f3a7a2229e3ca4121a", + "bytes": 8886 }, { "path": "backend/tests/test_runner_specialization_controller_surface.py", diff --git a/backend/tests/test_runner_fleet_specialize.py b/backend/tests/test_runner_fleet_specialize.py index 549c044..002e94b 100644 --- a/backend/tests/test_runner_fleet_specialize.py +++ b/backend/tests/test_runner_fleet_specialize.py @@ -3397,14 +3397,14 @@ def test_specialization_stage_fixture_quiesces_old_before_registering_and_never_ }, ) - def ensure_cgroup_family(**kwargs): - assert kwargs["allow_existing"] is recovering_cgroup_intent + def ensure_cgroup_family(current_journal): + assert current_journal["cgroupEnsurePending"] is True + assert "compose:runner-dind" in events events.append("cgroup-ensured") - return not recovering_cgroup_intent monkeypatch.setattr( specialize, - "_ensure_cgroup_family", + "_bind_owned_dind_family", ensure_cgroup_family, ) monkeypatch.setattr(specialize, "_prepare_profile_state", lambda _revision: None) @@ -3422,6 +3422,8 @@ def test_specialization_stage_fixture_quiesces_old_before_registering_and_never_ monkeypatch.setattr(specialize, "_run", lambda *_args, **_kwargs: "") monkeypatch.setattr(specialize, "_wait_healthy", lambda *_args, **_kwargs: None) monkeypatch.setattr(specialize, "_inspect", lambda _name: {"State": {"Running": True}}) + monkeypatch.setattr(specialize, "_validate_dind_outer", lambda *_args: False) + monkeypatch.setattr(specialize, "_is_captured_original_dind", lambda *_args: True) monkeypatch.setattr(specialize, "_require_dind_resource_support", lambda _model: None) monkeypatch.setattr(specialize, "_captured_compose_mapping", lambda *_args, **_kwargs: {"services": {}}) monkeypatch.setattr(specialize, "_load_images_into_dind", lambda *_args: None) @@ -3452,6 +3454,7 @@ def test_specialization_stage_fixture_quiesces_old_before_registering_and_never_ if current_journal.get("cgroupEnsurePending") is True: events.append("cgroup-intent-durable") if current_journal.get("cgroupCreated") is True: + assert current_journal["stage"] not in {"prepared", "state_prepared", "legacy_removed"} events.append("cgroup-result-durable") if current_journal.get("status") == "active": events.append("journal-active-durable") @@ -3563,6 +3566,9 @@ def test_specialization_stage_fixture_quiesces_old_before_registering_and_never_ assert maximum_running == 2 assert events.index("cgroup-intent-durable") < events.index("cgroup-ensured") + assert events.index("cgroup-intent-durable") < events.index("compose:runner-dind") + assert events.index("compose:runner-dind") < events.index("cgroup-ensured") + assert events.index("cgroup-ensured") < events.index("registrations-created") assert events.index("cgroup-ensured") < events.index("cgroup-result-durable") assert batches == [ tuple(specialize.PROFILE_IDS[0:2]), @@ -3578,6 +3584,49 @@ def test_specialization_stage_fixture_quiesces_old_before_registering_and_never_ assert not running +@pytest.mark.parametrize("defect", [None, "resume_same", "resume_wrong_pid", "foreign_cid", "restarted", "foreign_model", "workload", "bad_top", "duplicate_pid", "wrong_pid", "wrong_controls", "helper_refusal", "bad_result"]) +def test_owned_dind_family_binding_requires_fresh_exact_identity(specialize, monkeypatch, defect): + cid = "a" * 64 + state = {"Running": True, "Pid": 1234, "StartedAt": "2026-10-11T00:00:00Z"} + before = {"Id": cid, "State": state, "Image": specialize._DIND_IMAGE_ID} + after = copy.deepcopy(before) + if defect == "foreign_cid": + after["Id"] = "b" * 64 + if defect == "restarted": + after["State"]["StartedAt"] = "2026-10-11T00:01:00Z" + snapshots = iter([before, after]) + monkeypatch.setattr(specialize, "_inspect", lambda _name: next(snapshots)) + monkeypatch.setattr(specialize, "_write_journal", lambda _journal: None) + monkeypatch.setattr(specialize, "_captured_compose_mapping", lambda *_args, **_kwargs: {}) + monkeypatch.setattr(specialize, "_validate_dind_outer", lambda *_args: defect != "foreign_model") + monkeypatch.setattr(specialize, "_run_dind_docker", lambda *_args, **_kwargs: cid if defect == "workload" else "") + monkeypatch.setattr(specialize, "_run", lambda *_args, **_kwargs: "PID\ninvalid" if defect == "bad_top" else "PID\n1234\n1234" if defect == "duplicate_pid" else "PID\n9999" if defect == "wrong_pid" else "PID\n1234\n1235") + calls = [] + controls = ["cpu", "memory", "pids"] + + def bind(**kwargs): + calls.append(kwargs) + if defect == "helper_refusal": + raise RuntimeError("resource_cgroup_foreign_process") + if defect == "bad_result": + return None + return {"created": False, "rootSubtreeBefore": [] if defect == "wrong_controls" else controls} + + monkeypatch.setattr(specialize, "ensure_owned_dind_cgroup_family", bind) + journal = {"cgroupRootSubtree": controls, "cgroupEnsurePending": True, "cgroupFamilyInitiallyPresent": False, "cgroupDindBinding": None} + if defect in {"resume_same", "resume_wrong_pid"}: + journal["cgroupDindBinding"] = {"containerId": cid, "hostPid": 9999 if defect == "resume_wrong_pid" else 1234, "startedAt": state["StartedAt"], "imageId": specialize._DIND_IMAGE_ID} + if defect in {None, "resume_same"}: + specialize._bind_owned_dind_family(journal) + assert calls == [{"expected_container_id": cid, "expected_host_pid": 1234, "expected_process_ids": [1234, 1235], "allow_owned_partial": defect == "resume_same"}] + assert journal["cgroupDindBinding"] == {"containerId": cid, "hostPid": 1234, "startedAt": state["StartedAt"], "imageId": specialize._DIND_IMAGE_ID} + else: + with pytest.raises(specialize.RunnerFleetSpecializeError): + specialize._bind_owned_dind_family(journal) + if defect in {"foreign_model", "workload", "bad_top", "duplicate_pid", "wrong_pid", "resume_wrong_pid"}: + assert calls == [] + + @pytest.mark.parametrize("original_running", [False, True]) def test_migration_failure_restores_captured_dind_state_before_cgroup_cleanup( specialize, tmp_path, monkeypatch, original_running diff --git a/backend/tests/test_runner_resource_envelope.py b/backend/tests/test_runner_resource_envelope.py index 717d04f..0b0373d 100644 --- a/backend/tests/test_runner_resource_envelope.py +++ b/backend/tests/test_runner_resource_envelope.py @@ -373,6 +373,17 @@ def _snapshot(): } +def _fixture_outer_namespace(envelope, monkeypatch): + # These pre-existing fixtures isolate resource/config collection. The real + # namespace/Engine boundary is exercised by the dedicated tests below. + def proof(name): + item = envelope._inspect_container(name) + return envelope._engine_outer_binding(item), [(item["State"]["Pid"], 1)], { + "target": {"name": name, "id": item["Id"]}, "processCount": 1, "threadCount": 1, + } + monkeypatch.setattr(envelope, "_attest_outer_namespace", proof) + + def _install_classic_builder_collection(envelope, monkeypatch, builder_cpu_config): snapshot = _snapshot() dind_path = str(snapshot["dind"]["path"]) @@ -500,6 +511,7 @@ def _install_classic_builder_collection(envelope, monkeypatch, builder_cpu_confi return snapshot monkeypatch.setattr(envelope, "_run_host_probe", fake_probe) + _fixture_outer_namespace(envelope, monkeypatch) return snapshot, nested_items, probe_calls @@ -1213,6 +1225,492 @@ def test_host_family_rollback_is_fixed_and_rejects_untrusted_arguments(monkeypat envelope.rollback_host_cgroup_family(created="yes", initial_controllers=["pids"]) +def _owned_family_fixture(monkeypatch, *, exact=False, init=True, docker=True): + envelope = _module() + cid = "a" * 64 + leaf = f"/itworx-ci/{cid}" + paths = ["/itworx-ci", leaf] + if init: + paths.append(f"{leaf}/init") + if docker: + paths.append(f"{leaf}/docker") + rows = { + path: {"path": path, "cpuMax": "max 100000", "memoryMax": "max"} + for path in paths + } + rows[leaf].update(cpuMax="600000 100000", memoryMax=str(12 * 1024**3)) + if exact: + rows["/itworx-ci"].update(cpuMax="800000 100000", memoryMax=str(16 * 1024**3)) + processes = {path: "" for path in paths} + processes[f"{leaf}/init" if init else leaf] = "1 2" + descriptors = {path: index for index, path in enumerate(paths, start=101)} + reverse = {value: path for path, value in descriptors.items()} + required = {"cpu", "memory", "pids"} + controls = {"root": required.copy(), "family": required.copy()} + inodes = {path: value for path, value in descriptors.items()} + monkeypatch.setattr(envelope, "_probe_controllers", lambda _fd: (required, controls["root"])) + monkeypatch.setattr(envelope, "_probe_open_family", lambda _fd: 101) + monkeypatch.setattr(envelope, "_probe_walk", lambda _fd: [dict(row) for row in rows.values()]) + monkeypatch.setattr(envelope, "_probe_open_relative", lambda _fd, path: descriptors[path]) + monkeypatch.setattr(envelope.os, "dup", lambda fd: fd) + monkeypatch.setattr(envelope.os, "close", lambda _fd: None) + monkeypatch.setattr(envelope.os, "fstat", lambda fd: SimpleNamespace( + st_dev=36, st_ino=inodes[reverse[fd]], st_uid=0, st_mode=0o40755, + )) + monkeypatch.setattr(envelope, "_probe_stat", lambda _fd, _name: SimpleNamespace(st_dev=36, st_ino=inodes["/itworx-ci"])) + + def read(fd, name): + path = reverse[fd] + if name == "cgroup.procs": + return processes[path] + if name == "cgroup.threads": + return processes[path] + if name in {"pids.current", "cgroup.events"}: + count = sum(len(value.split()) for group, value in processes.items() if group == path or group.startswith(path + "/")) + return str(count) if name == "pids.current" else f"populated {int(count > 0)}\nfrozen 0" + if name == "cgroup.subtree_control": + return " ".join(sorted(controls["family"])) + raise AssertionError(name) + + writes = [] + + def write(fd, name, value): + assert fd == 101 + assert name in {"cpu.max", "memory.max"} + writes.append((name, value)) + rows["/itworx-ci"]["cpuMax" if name == "cpu.max" else "memoryMax"] = value + + monkeypatch.setattr(envelope, "_probe_read", read) + monkeypatch.setattr(envelope, "_probe_write", write) + claim = {"containerId": cid, "processCount": 2, "threadCount": 2} + return envelope, claim, rows, processes, controls, writes, inodes + + +@pytest.mark.parametrize("init,docker,exact", [(False, False, False), (True, False, False), (True, True, False), (True, True, True)]) +def test_owned_dind_family_binds_only_existing_exact_private_child(monkeypatch, init, docker, exact): + envelope, claim, _rows, _processes, _controls, writes, _inodes = _owned_family_fixture( + monkeypatch, init=init, docker=docker, exact=exact, + ) + result = envelope._probe_ensure_owned_dind_family(1, claim) + assert result == {"created": False, "rootSubtreeBefore": ["cpu", "memory", "pids"], + "familyDevice": 36, "familyInode": 101, + "familyCpuMax": "800000 100000", "familyMemoryMax": str(16 * 1024**3)} + assert writes == ([] if exact else [("cpu.max", "800000 100000"), ("memory.max", str(16 * 1024**3))]) + + +@pytest.mark.parametrize("mutation", ["missing", "foreign_group", "foreign_pid", "root_pid", "docker_pid", "zero_pid", + "pid_missing", "duplicate_pid", "dind_cpu", "dind_memory", "foreign_budget", + "mixed_budget", "root_controls", "family_controls", "unknown_controls"]) +def test_owned_dind_family_rejects_foreign_or_invisible_work_before_writes(monkeypatch, mutation): + envelope, claim, rows, processes, controls, writes, _inodes = _owned_family_fixture(monkeypatch) + leaf = f"/itworx-ci/{claim['containerId']}" + if mutation == "missing": + monkeypatch.setattr(envelope, "_probe_open_family", lambda _fd: None) + elif mutation == "foreign_group": + rows[f"{leaf}/docker/foreign"] = {"path": f"{leaf}/docker/foreign", "cpuMax": "max", "memoryMax": "max"} + elif mutation in {"foreign_pid", "zero_pid", "pid_missing", "duplicate_pid"}: + processes[f"{leaf}/init"] = {"foreign_pid": "1 0", "zero_pid": "0", "pid_missing": "2", + "duplicate_pid": "1 1"}[mutation] + elif mutation == "root_pid": + processes["/itworx-ci"] = "2" + elif mutation == "docker_pid": + processes[f"{leaf}/docker"] = "2" + elif mutation == "dind_cpu": + rows[leaf]["cpuMax"] = "max 100000" + elif mutation == "dind_memory": + rows[leaf]["memoryMax"] = "max" + elif mutation == "foreign_budget": + rows["/itworx-ci"]["cpuMax"] = "700000 100000" + elif mutation == "mixed_budget": + rows["/itworx-ci"]["cpuMax"] = "800000 100000" + elif mutation == "root_controls": + controls["root"].remove("memory") + elif mutation == "family_controls": + controls["family"].remove("pids") + elif mutation == "unknown_controls": + controls["family"].add("foreign") + with pytest.raises(RuntimeError, match="resource_cgroup_"): + envelope._probe_ensure_owned_dind_family(1, claim) + assert writes == [] + + +@pytest.mark.parametrize("boundary", ["before_cpu", "before_memory", "readback"]) +def test_owned_dind_family_rechecks_full_identity_at_each_write_boundary(monkeypatch, boundary): + envelope, claim, _rows, _processes, _controls, writes, inodes = _owned_family_fixture(monkeypatch) + original = envelope._probe_owned_dind_state + calls = 0 + + def changing(*args): + nonlocal calls + calls += 1 + if calls == {"before_cpu": 2, "before_memory": 3, "readback": 4}[boundary]: + inodes[f"/itworx-ci/{claim['containerId']}"] += 1 + return original(*args) + + monkeypatch.setattr(envelope, "_probe_owned_dind_state", changing) + with pytest.raises(RuntimeError, match="resource_cgroup_probe_path_untrusted"): + envelope._probe_ensure_owned_dind_family(1, claim) + assert len(writes) == {"before_cpu": 0, "before_memory": 1, "readback": 2}[boundary] + + +@pytest.mark.parametrize("mutation", ["pid", "controller", "budget"]) +def test_owned_dind_family_refuses_drift_after_cpu_intent(monkeypatch, mutation): + envelope, claim, rows, processes, controls, writes, _inodes = _owned_family_fixture(monkeypatch) + write = envelope._probe_write + + def changing(*args): + write(*args) + if mutation == "pid": + processes[f"/itworx-ci/{claim['containerId']}/init"] = "1 0" + elif mutation == "controller": + controls["root"].remove("pids") + else: + rows["/itworx-ci"]["cpuMax"] = "max 100000" + + monkeypatch.setattr(envelope, "_probe_write", changing) + with pytest.raises(RuntimeError, match="resource_cgroup_"): + envelope._probe_ensure_owned_dind_family(1, claim) + assert writes == [("cpu.max", "800000 100000")] + + +@pytest.mark.parametrize("mixed", [False, True]) +def test_owned_dind_dispatch_keeps_empty_ensure_contract_distinct(monkeypatch, mixed): + envelope, claim, _rows, _processes, _controls, writes, _inodes = _owned_family_fixture(monkeypatch) + monkeypatch.setattr(envelope, "Path", lambda value: SimpleNamespace(lstat=lambda: SimpleNamespace( + st_mode=0o40755, st_uid=0, + ))) + monkeypatch.setattr(envelope.os, "open", lambda *_args: 1) + monkeypatch.setattr(envelope.os, "mkdir", lambda *_args, **_kwargs: pytest.fail("owned ensure must not create a family")) + request = {"op": "ensure", "ownedDind": claim} + if mixed: + request["allowExisting"] = True + with pytest.raises(RuntimeError, match="^resource_cgroup_family_request_invalid$"): + envelope._host_probe_dispatch(request) + assert writes == [] + else: + assert envelope._host_probe_dispatch(request)["familyInode"] == 101 + + +@pytest.mark.parametrize("mutate", ["id", "pid_bool", "pid_negative", "process_bool", "duplicate", "missing_pid", "too_many", "extra"]) +def test_owned_dind_claim_is_typed_and_finite(monkeypatch, mutate): + envelope = _module() + claim = {"containerId": "a" * 64, "hostPid": 1234, "processIds": [1234]} + if mutate == "id": + claim["containerId"] = "private-canary" + elif mutate == "pid_bool": + claim["hostPid"] = True + elif mutate == "pid_negative": + claim["hostPid"] = -1 + elif mutate == "process_bool": + claim["processIds"] = [True] + elif mutate == "duplicate": + claim["processIds"] = [1234, 1234] + elif mutate == "missing_pid": + claim["processIds"] = [1235] + elif mutate == "too_many": + claim["processIds"] = [1234, *range(1, 257)] + elif mutate == "extra": + claim["path"] = "private-canary" + with pytest.raises(RuntimeError, match="^resource_cgroup_family_request_invalid$"): + envelope._owned_dind_claim(claim) + + +def test_owned_dind_public_api_uses_existing_fixed_ensure_only(monkeypatch): + envelope = _module() + calls = [] + result = {"created": False, "rootSubtreeBefore": ["cpu", "memory", "pids"], + "familyDevice": 36, "familyInode": 101, + "familyCpuMax": "800000 100000", "familyMemoryMax": str(16 * 1024**3)} + monkeypatch.setattr(envelope, "_engine_outer_population", lambda name: + ({"Id": "a" * 64, "State": {"Pid": 1234}}, [(1234, 1), (1235, 1)])) + monkeypatch.setattr(envelope, "_run_host_probe", lambda mode, request: calls.append((mode, request)) or result) + assert envelope.ensure_owned_dind_cgroup_family(expected_container_id="a" * 64, expected_host_pid=1234, + expected_process_ids=[1235, 1234]) == result + assert calls == [("ensure", {"ownedDind": {"containerId": "a" * 64, "processCount": 2, "threadCount": 2, "allowPartial": False}})] + with pytest.raises(RuntimeError, match="^resource_cgroup_family_request_invalid$"): + envelope.ensure_owned_dind_cgroup_family(expected_container_id="a" * 64, expected_host_pid=1234, + expected_process_ids="1234") + assert len(calls) == 1 + + +@pytest.mark.parametrize("mutation", ["created", "inode", "device", "cpu", "memory", "controllers", "extra"]) +def test_owned_dind_public_api_rejects_invalid_readback(monkeypatch, mutation): + envelope = _module() + result = {"created": False, "rootSubtreeBefore": ["cpu", "memory", "pids"], + "familyDevice": 36, "familyInode": 101, + "familyCpuMax": "800000 100000", "familyMemoryMax": str(16 * 1024**3)} + key, value = {"created": ("created", True), "inode": ("familyInode", True), + "device": ("familyDevice", -1), "cpu": ("familyCpuMax", "max 100000"), + "memory": ("familyMemoryMax", "max"), "controllers": ("rootSubtreeBefore", ["cpu", "pids"]), + "extra": ("private-canary", "secret-canary")}[mutation] + result[key] = value + monkeypatch.setattr(envelope, "_engine_outer_population", lambda name: + ({"Id": "a" * 64, "State": {"Pid": 1234}}, [(1234, 1)])) + monkeypatch.setattr(envelope, "_run_host_probe", lambda *_args: result) + with pytest.raises(RuntimeError, match="^resource_cgroup_family_readback_failed$"): + envelope.ensure_owned_dind_cgroup_family(expected_container_id="a" * 64, expected_host_pid=1234, + expected_process_ids=[1234]) + + +@pytest.mark.parametrize("pid_location", ["leaf", "init", "missing"]) +def test_snapshot_dind_pid_allows_only_exact_owned_init_or_leaf(monkeypatch, pid_location): + envelope, claim, rows, processes, _controls, _writes, _inodes = _owned_family_fixture(monkeypatch) + leaf = f"/itworx-ci/{claim['containerId']}" + processes[f"{leaf}/init"] = "" + if pid_location == "leaf": + processes[leaf] = "1 2" + elif pid_location == "init": + processes[f"{leaf}/init"] = "1 2" + outer = [ + {"name": name, "id": claim["containerId"] if name == envelope.DIND_NAME else f"{index:064x}", + "running": name == envelope.DIND_NAME, "pid": 1234 if name == envelope.DIND_NAME else None, + "processCount": 2 if name == envelope.DIND_NAME else None, "threadCount": 2 if name == envelope.DIND_NAME else None, + "configuredCpuMilli": 500, "configuredMemoryBytes": 512 * 1024**2} + for index, name in enumerate(envelope.FIXED_RUNNER_AND_DIND_NAMES, start=1) + ] + request = {"op": "snapshot", "outer": outer, "workloads": []} + if pid_location in {"leaf", "init"}: + result = envelope._probe_snapshot(request, 101) + assert result["dind"]["path"] == leaf + else: + with pytest.raises(RuntimeError, match="^resource_cgroup_process_mapping_invalid$"): + envelope._probe_snapshot(request, 101) + + +@pytest.mark.parametrize("suffix", ["init", "init/foreign", "foreign"]) +def test_attestor_allows_only_single_known_dind_init_group(suffix): + envelope = _module() + snapshot = _snapshot() + snapshot["groups"].append(_group(f"{snapshot['dind']['path']}/{suffix}", "max 100000", "max")) + result = envelope._evaluate_cgroup_snapshot(["build", "backend"], snapshot, + expected_host_budget={"cpuMilli": 8000, "memoryMiB": 16384}) + assert result["ready"] is (suffix == "init") + if suffix != "init": + assert result["reason"] == "resource_cgroup_unmapped_descendant" + + +@pytest.mark.parametrize("cpu,memory,partial", [("800000 100000", "max", False), ("800000 100000", "max", True), + ("max 100000", str(16 * 1024**3), False), + ("max 100000", str(16 * 1024**3), True)]) +def test_owned_partial_budget_requires_explicit_bound_resume(monkeypatch, cpu, memory, partial): + envelope, claim, rows, _processes, _controls, writes, _inodes = _owned_family_fixture(monkeypatch) + rows["/itworx-ci"].update(cpuMax=cpu, memoryMax=memory) + claim["allowPartial"] = partial + if not partial or memory != "max": + with pytest.raises(RuntimeError, match="^resource_cgroup_family_budget_conflict$"): + envelope._probe_ensure_owned_dind_family(1, claim) + assert writes == [] + else: + assert envelope._probe_ensure_owned_dind_family(1, claim)["familyInode"] == 101 + assert writes == ([("memory.max", str(16 * 1024**3))] if memory == "max" else [("cpu.max", "800000 100000")]) + + +@pytest.mark.parametrize("partial", [1, "true", None]) +def test_owned_partial_flag_is_not_coerced(monkeypatch, partial): + envelope, claim, _rows, _processes, _controls, writes, _inodes = _owned_family_fixture(monkeypatch) + claim["allowPartial"] = partial + with pytest.raises(RuntimeError, match="^resource_cgroup_family_request_invalid$"): + envelope._probe_ensure_owned_dind_family(1, claim) + with pytest.raises(RuntimeError, match="^resource_cgroup_family_request_invalid$"): + envelope.ensure_owned_dind_cgroup_family(expected_container_id="a" * 64, expected_host_pid=1234, + expected_process_ids=[1234], allow_owned_partial=partial) + assert writes == [] + + +@pytest.mark.parametrize("foreign", [False, True]) +def test_namespace_probe_rejects_foreign_zero_even_when_top_counts_match(monkeypatch, foreign): + envelope, claim, _rows, processes, _controls, _writes, _inodes = _owned_family_fixture(monkeypatch) + if foreign: + processes[f"/itworx-ci/{claim['containerId']}/init"] = "1 0" + request = {"op": "population", "target": {"name": envelope.DIND_NAME, "id": claim["containerId"]}, + "processCount": 2, "threadCount": 2} + if foreign: + with pytest.raises(RuntimeError, match="^resource_cgroup_process_mapping_invalid$"): + envelope._probe_namespace_population(101, request) + else: + assert envelope._probe_namespace_population(101, request) == {"namespaceOwned": True, "processCount": 2, "threadCount": 2} + + +def test_private_zero_is_only_an_opaque_population_entry(monkeypatch): + envelope, claim, _rows, processes, _controls, _writes, _inodes = _owned_family_fixture(monkeypatch) + processes[f"/itworx-ci/{claim['containerId']}/init"] = "0 0" + population = envelope._probe_population(103) + assert population == {"processCount": 2, "threadCount": 2, "hierarchicalThreads": 2, "populated": True} + assert envelope._probe_empty(103) is False + with pytest.raises(RuntimeError, match="^resource_cgroup_process_mapping_invalid$"): + envelope._probe_population(103, owned_namespace=True) + + +@pytest.mark.parametrize("mode", ["ensure", "population"]) +@pytest.mark.parametrize("drift", [None, "host", "private", "other_cid", "label", "mount"]) +def test_namespace_join_is_exact_bound_container_only(mode, drift): + envelope = _module() + base, image_id, source = _host_probe_fixture(envelope) + target = {"name": envelope.DIND_NAME, "id": "a" * 64} + args = envelope._host_probe_create_args(mode, image_id, source, namespace_target=target) + assert args[args.index("--pid") + 1] == "container:" + target["id"] + assert not any("/proc" in value or "--pid=host" in value or "docker.sock" in value for value in args) + namespace_labels = {"tech.itworx.probe.namespace-name": target["name"], "tech.itworx.probe.namespace-id": target["id"]} + base["Config"]["Labels"].update(namespace_labels) + base["HostConfig"]["PidMode"] = "container:" + target["id"] + expected_source = "/sys/fs/cgroup" if mode == "ensure" else "/sys/fs/cgroup/itworx-ci/" + target["id"] + base["Mounts"][0].update(Source=expected_source, RW=mode == "ensure") + base["HostConfig"]["Mounts"][0].update(Source=expected_source, ReadOnly=mode != "ensure") + base["HostConfig"]["CapAdd"] = ["DAC_OVERRIDE"] if mode == "ensure" else [] + if drift in {"host", "private", "other_cid"}: + base["HostConfig"]["PidMode"] = {"host": "host", "private": "", "other_cid": "container:" + "b" * 64}[drift] + elif drift == "label": + base["Config"]["Labels"]["tech.itworx.probe.namespace-id"] = "b" * 64 + elif drift == "mount": + base["HostConfig"]["Mounts"][0]["Source"] = "/proc" + assert envelope._validate_host_probe_inspect(base, mode=mode, image_id=image_id, code_source=source, + namespace_target=target) is (drift is None) + assert not envelope._validate_host_probe_inspect(base, mode=mode, image_id=image_id, code_source=source) + if drift is None: + assert envelope._stopped_probe_namespace(base) == target + + +@pytest.mark.parametrize("mode,name", [("snapshot", "gitea-runner-dind"), ("rollback", "gitea-runner-dind"), + ("family", "gitea-runner-dind"), ("ensure", "itworx-ci-build"), + ("population", "foreign")]) +def test_namespace_join_never_expands_other_modes_or_targets(mode, name): + envelope = _module() + _base, image_id, source = _host_probe_fixture(envelope) + with pytest.raises(RuntimeError, match="^resource_cgroup_probe_namespace_invalid$"): + envelope._host_probe_create_args(mode, image_id, source, namespace_target={"name": name, "id": "a" * 64}) + + +@pytest.mark.parametrize("drift", [None, "pid", "start", "image", "threads", "foreign_namespace"]) +def test_namespace_attestation_rechecks_engine_without_counts_only_adoption(monkeypatch, drift): + envelope = _module() + before = {"Id": "a" * 64, "Image": "sha256:" + "c" * 64, "State": {"Pid": 1234, "StartedAt": "fixed-start"}} + pairs = [(1234, 2), (1235, 1)] + after, after_pairs = copy.deepcopy(before), list(pairs) + if drift == "pid": + after["State"]["Pid"] = 1236 + elif drift == "start": + after["State"]["StartedAt"] = "another-start" + elif drift == "image": + after["Image"] = "sha256:" + "d" * 64 + elif drift == "threads": + after_pairs[0] = (1234, 3) + calls = [] + def engine(name): + assert name == envelope.DIND_NAME + calls.append(name) + return (before, pairs) if len(calls) == 1 else (after, after_pairs) + monkeypatch.setattr(envelope, "_engine_outer_population", engine) + def probe(mode, claim): + assert mode == "population" and claim["target"] == {"name": envelope.DIND_NAME, "id": "a" * 64} + assert claim["processCount"] == 2 and claim["threadCount"] == 3 + if drift == "foreign_namespace": + raise RuntimeError("resource_cgroup_process_mapping_invalid") + return {"namespaceOwned": True, "processCount": 2, "threadCount": 3} + monkeypatch.setattr(envelope, "_run_host_probe", probe) + if drift is None: + assert envelope._attest_outer_namespace(envelope.DIND_NAME)[0] == before + else: + with pytest.raises(RuntimeError, match="^resource_cgroup_process_mapping_invalid$"): + envelope._attest_outer_namespace(envelope.DIND_NAME) + + +@pytest.mark.parametrize("output", ["PID NLWP\n1234 2\n1235 1", "PID NLWP\n1234 0", "PID NLWP\n1234 2\n1234 1", + "PID NLWP\n1235 2", "PID COMMAND\n1234 private-canary", + "PID NLWP\n0 1", "PID NLWP\n1234 16385", "PID NLWP\n1234 1 extra"]) +def test_engine_population_reads_only_fixed_cid_numeric_pid_thread_columns(monkeypatch, output): + envelope = _module() + item = {"Name": "/" + envelope.DIND_NAME, "Id": "a" * 64, "Image": "sha256:" + "b" * 64, + "Created": "fixed-created", "State": {"Running": True, "Pid": 1234, "StartedAt": "fixed-start"}, + "HostConfig": {"CgroupParent": "itworx-ci"}} + monkeypatch.setattr(envelope, "_inspect_container", lambda name: item) + commands = [] + def run(args, **kwargs): + commands.append((args, kwargs)) + return output + monkeypatch.setattr(envelope, "_run", run) + if output == "PID NLWP\n1234 2\n1235 1": + binding, pairs = envelope._engine_outer_population(envelope.DIND_NAME) + assert binding["Id"] == "a" * 64 and pairs == [(1234, 2), (1235, 1)] + else: + with pytest.raises(RuntimeError, match="^resource_cgroup_process_mapping_invalid$"): + envelope._engine_outer_population(envelope.DIND_NAME) + assert commands == [(["docker", "top", "a" * 64, "-eo", "pid,nlwp"], {"timeout": 15})] + with pytest.raises(RuntimeError, match="^resource_cgroup_container_identity_invalid$"): + envelope._engine_outer_population("private-canary") + assert len(commands) == 1 + + +def test_engine_binding_preserves_all_mount_fields_but_not_incidental_order(): + envelope = _module() + item = {"Mounts": [{"Destination": "/a", "Source": "/fixed/a", "RW": True}, + {"Destination": "/b", "Source": "/fixed/b", "RW": False}], + "State": {"Running": True, "Pid": 1234, "Health": {"Log": ["volatile"]}}} + reordered = copy.deepcopy(item) + reordered["Mounts"].reverse() + reordered["State"]["Health"]["Log"] = ["changed"] + assert envelope._engine_outer_binding(item) == envelope._engine_outer_binding(reordered) + reordered["Mounts"][0]["RW"] = True + assert envelope._engine_outer_binding(item) != envelope._engine_outer_binding(reordered) + + +@pytest.mark.parametrize("drift", [False, True]) +def test_namespace_probe_lifecycle_rechecks_target_before_start(monkeypatch, drift): + envelope = _module() + base, image_id, source = _host_probe_fixture(envelope) + target = {"name": envelope.DIND_NAME, "id": "a" * 64} + base["Config"]["Labels"].update({"tech.itworx.probe.namespace-name": target["name"], + "tech.itworx.probe.namespace-id": target["id"]}) + base["HostConfig"]["PidMode"] = "container:" + target["id"] + base["HostConfig"]["Mounts"][0]["Source"] += "/" + target["id"] + base["Mounts"][0]["Source"] += "/" + target["id"] + monkeypatch.setattr(envelope, "_controller_runtime_identity", lambda: (image_id, source)) + monkeypatch.setattr(envelope, "_host_probe_image_labels", lambda _image: {}) + monkeypatch.setattr(envelope, "_inspect_host_probe_by_name", lambda: None) + engine_calls = [] + def engine(name): + assert name == envelope.DIND_NAME + engine_calls.append(name) + return {"Id": target["id"], "State": {"Pid": 1234, "StartedAt": "changed" if drift and len(engine_calls) > 1 else "fixed"}}, [(1234, 1)] + monkeypatch.setattr(envelope, "_engine_outer_population", engine) + commands = [] + def run(args, **_kwargs): + commands.append(args) + if args[1] == "create": + assert args[args.index("--pid") + 1] == "container:" + target["id"] + return base["Id"] + if args[1] == "inspect": + return json.dumps([base]) + assert args == ["docker", "rm", base["Id"]] + return "" + monkeypatch.setattr(envelope, "_run", run) + starts = [] + def start(args, **kwargs): + starts.append(args) + assert kwargs["timeout"] == 60 and b'"population"' in kwargs["input"] + return SimpleNamespace(returncode=0, stdout=b'{"ready":true,"result":{"namespaceOwned":true,"processCount":1,"threadCount":1}}') + monkeypatch.setattr(envelope.subprocess, "run", start) + payload = {"target": target, "processCount": 1, "threadCount": 1} + if drift: + with pytest.raises(RuntimeError, match="^resource_cgroup_probe_namespace_invalid$"): + envelope._run_host_probe("population", payload) + assert starts == [] + else: + assert envelope._run_host_probe("population", payload)["namespaceOwned"] is True + assert starts == [["docker", "start", "--attach", "--interactive", base["Id"]]] + assert commands[-1] == ["docker", "rm", base["Id"]] + + +@pytest.mark.parametrize("hidden", ["threads", "pids", "events", "opaque"]) +def test_empty_proof_never_uses_private_pid_visibility_as_absence(monkeypatch, hidden): + envelope = _module() + reads = {"cgroup.procs": "", "cgroup.threads": "", "pids.current": "0", "cgroup.events": "populated 0"} + key, value = {"threads": ("cgroup.threads", "0"), "pids": ("pids.current", "1"), + "events": ("cgroup.events", "populated 1"), "opaque": ("cgroup.procs", "0")}[hidden] + reads[key] = value + monkeypatch.setattr(envelope, "_probe_read", lambda _fd, name: reads[name]) + assert envelope._probe_empty(1) is False + + def test_host_family_inspection_only_allows_dind_start_from_exact_empty_family(monkeypatch): envelope = _module() required = {"cpu", "memory", "pids"} @@ -1227,7 +1725,8 @@ def test_host_family_inspection_only_allows_dind_start_from_exact_empty_family(m } ] monkeypatch.setattr(envelope, "_probe_walk", lambda _fd: list(groups)) - reads = {"cgroup.subtree_control": "cpu memory pids", "cgroup.procs": ""} + reads = {"cgroup.subtree_control": "cpu memory pids", "cgroup.procs": "", "cgroup.threads": "", + "pids.current": "0", "cgroup.events": "populated 0\nfrozen 0"} monkeypatch.setattr(envelope, "_probe_read", lambda _fd, name: reads[name]) ready = envelope._probe_family_info(1) @@ -1260,7 +1759,9 @@ def test_rollback_preserves_unraid_existing_hugetlb_controller(monkeypatch): "cpuMax": "800000 100000", "memoryMax": str(16 * 1024**3), }]) monkeypatch.setattr(envelope, "_probe_read", lambda fd, name: - "" if name == "cgroup.procs" else + "" if name in {"cgroup.procs", "cgroup.threads"} else + "0" if name == "pids.current" else + "populated 0\nfrozen 0" if name == "cgroup.events" else "cpu memory pids" if fd == 123 else " ".join(initial)) monkeypatch.setattr(envelope.os, "close", lambda _fd: None) monkeypatch.setattr(envelope.os, "rmdir", lambda *args, **kwargs: None) @@ -1344,11 +1845,17 @@ def test_nested_docker_pid_is_not_reinterpreted_as_host_pid(monkeypatch, tmp_pat # The host cgroup contains global PID 4321; nested Docker reports PID 7 # inside DinD's private namespace. Membership is proven by ID path and a # populated cgroup, never by comparing those unrelated PID numbers. - monkeypatch.setattr(envelope, "_probe_read", lambda _fd, name, **_kw: "4321" if name == "cgroup.procs" else "") + monkeypatch.setattr(envelope, "_probe_read", lambda _fd, name, **_kw: + "0" if name in {"cgroup.procs", "cgroup.threads"} else + "2" if name == "pids.current" else "populated 1\nfrozen 0") + monkeypatch.setattr(envelope, "_probe_population", lambda fd, **_kwargs: { + "processCount": 0 if fd == family_fd else 1, "threadCount": 0 if fd == family_fd else 1, + "hierarchicalThreads": 2, "populated": True, + }) outer = [] for index, name in enumerate(envelope.FIXED_RUNNER_AND_DIND_NAMES, start=1): if name == envelope.DIND_NAME: - outer.append({"name": name, "id": dind_id, "running": True, "pid": 4321}) + outer.append({"name": name, "id": dind_id, "running": True, "processCount": 2, "threadCount": 2}) else: outer.append( { @@ -1524,6 +2031,7 @@ def test_collect_snapshot_uses_host_helper_and_exports_real_family_counters(monk return snapshot monkeypatch.setattr(envelope, "_run_host_probe", fake_probe) + _fixture_outer_namespace(envelope, monkeypatch) real_read_text = Path.read_text def reject_local_proc_or_sys(path, *args, **kwargs): diff --git a/backend/tests/test_runner_specialization_child_boundary.py b/backend/tests/test_runner_specialization_child_boundary.py index 0fc12f6..b131047 100644 --- a/backend/tests/test_runner_specialization_child_boundary.py +++ b/backend/tests/test_runner_specialization_child_boundary.py @@ -116,11 +116,11 @@ def test_observed_hugetlb_inventory_and_legacy_records_remain_finite( expected = result error = "cgroup_family_result_invalid" else: - # Actual schema-v3 parser, including absent legacy-DinD state and its - # unchanged provenance, validates both historical and current inventories. + # The current schema retains absent legacy-DinD provenance and accepts + # only the finite controller inventories, independently of journal age. backup = specialize._SPECIALIZED_BACKUP_ROOT / "specialized-finite-fixture" journal = { - "schemaVersion": 3, "status": "in_progress", "stage": "prepared", + "schemaVersion": 4, "status": "in_progress", "stage": "prepared", "sourceScriptSha256": specialize._APPROVED_SCRIPT_SHA256, "sourceRevision": "a" * 40, "sourceTree": "b" * 40, "createdAt": "2026-10-10T00:00:00Z", "updatedAt": "2026-10-10T00:00:00Z", @@ -130,7 +130,8 @@ def test_observed_hugetlb_inventory_and_legacy_records_remain_finite( "originalDind": {"present": False, "running": False, "restartPolicy": "no", "imageId": None}, "guardFiles": [], "guardMarkers": {}, "cgroupCreated": False, "cgroupEnsurePending": False, "cgroupFamilyInitiallyPresent": False, - "cgroupRootSubtree": controllers, "newRegistrationIds": {}, + "cgroupRootSubtree": controllers, "cgroupDindBinding": None, + "newRegistrationIds": {}, "imageManifestSha256": None, "buildProbe": None, } monkeypatch.setattr(specialize, "_read_json_file", lambda *args, **kwargs: journal) @@ -140,6 +141,14 @@ def test_observed_hugetlb_inventory_and_legacy_records_remain_finite( error = "journal_invalid" if accepted: assert read() == expected + if boundary == "journal": + journal["schemaVersion"] = 3 + with pytest.raises(specialize.RunnerFleetSpecializeError, match=error): + read() + journal["schemaVersion"] = 4 + del journal["cgroupDindBinding"] + with pytest.raises(specialize.RunnerFleetSpecializeError, match=error): + read() else: with pytest.raises(specialize.RunnerFleetSpecializeError, match=error): read()