diff --git a/PUBLIC_SOURCE_EXPORT.md b/PUBLIC_SOURCE_EXPORT.md index d6716b6..53c2914 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 `ef07e484d2ff369f8d57d6cf3c10bd00c59b5fe1`. 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 `07800de5cf3d27b80dbdebc13025bddeb58a07e3`. 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 763cfe5..49a5d2e 100644 --- a/PUBLIC_SOURCE_MANIFEST.json +++ b/PUBLIC_SOURCE_MANIFEST.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "sourceRevision": "ef07e484d2ff369f8d57d6cf3c10bd00c59b5fe1", + "sourceRevision": "07800de5cf3d27b80dbdebc13025bddeb58a07e3", "files": [ { "path": ".dockerignore", @@ -539,8 +539,8 @@ }, { "path": "backend/app/services/deployment_broker.py", - "sha256": "9d01d9e3d3b3218824ee6ef3ad561f3805f8f7b6493cfd2cebf62111b39395e0", - "bytes": 46623 + "sha256": "953da2ccf1fe73181fe973aec97485d65b084333ff074d9603a08bb0951f3bbb", + "bytes": 46624 }, { "path": "backend/app/services/deployment_control_plane.py", @@ -949,8 +949,8 @@ }, { "path": "backend/tests/test_ci_job_docker.py", - "sha256": "7292a3ad3e52e934e6a410fdea8adb6a00dc90fe8de28d8159205ec069951d73", - "bytes": 27439 + "sha256": "1ca4e7c38a13a88840d412f203fe312a762fc8b2499bf3e3f004fe1fd3e78ea0", + "bytes": 30346 }, { "path": "backend/tests/test_container_restart.py", @@ -1694,8 +1694,8 @@ }, { "path": "backend/tests/test_runner_specialization_child_boundary.py", - "sha256": "06a38a118334f69d8ef0f991c3568bac6a5666f2254cafca8185de22cd43d147", - "bytes": 12516 + "sha256": "b20a84c7fccd9b1dbe7f87d6a4135df8b2173b0d9d91eeeb6c396bbb1db51c66", + "bytes": 13927 }, { "path": "backend/tests/test_runner_specialization_controller_surface.py", @@ -1704,8 +1704,8 @@ }, { "path": "backend/tests/test_runner_specialization_transport.py", - "sha256": "e40860e2ce7ca3ddd2bdb4742a0fb8f1188032ce912aeab3ebbd3b581f69de8a", - "bytes": 3644 + "sha256": "ab1b4395bed2304f582bff607ad6417a3583c801499f097873e73f562b8572fe", + "bytes": 3645 }, { "path": "backend/tests/test_runner_statistics_binding.py", diff --git a/backend/app/services/deployment_broker.py b/backend/app/services/deployment_broker.py index ac2c156..99e82cd 100644 --- a/backend/app/services/deployment_broker.py +++ b/backend/app/services/deployment_broker.py @@ -982,7 +982,7 @@ class DeploymentBrokerClient: result = self._request( "POST", "/v1/appops/runner-fleet/specialize", secret=settings.mcp_token or "", payload={"reason": reason[:1000]}, - request_timeout=httpx.Timeout(connect=5, write=5, pool=5, read=350), + request_timeout=httpx.Timeout(connect=5, write=5, pool=5, read=3700), ) # The controller can outlive a lost HTTP response. Never translate a # transport failure into proof of no host effects or permission to retry. diff --git a/backend/tests/test_ci_job_docker.py b/backend/tests/test_ci_job_docker.py index 6add6eb..f122ea3 100644 --- a/backend/tests/test_ci_job_docker.py +++ b/backend/tests/test_ci_job_docker.py @@ -548,3 +548,55 @@ def test_job_docker_daemon_command_uses_fixed_rootlesskit_and_unix_only_engine() assert "--storage-driver=overlay2" in daemon_args assert "--feature=containerd-snapshotter=false" in daemon_args assert "--data-root=/home/itworx-builder/.local/share/docker" in daemon_args + + +def test_nested_build_subordinate_ranges_cover_mapped_nonroot_ids_only(): + dockerfile = (ROOT / "ops/runner-task-images/build/Dockerfile").read_text() + assert "'ubuntu:1:999' 'ubuntu:1001:64536'" in dockerfile + assert "sed -i '/^ubuntu:/d'" in dockerfile + pins = json.loads((ROOT / "ops/runner-task-images/pins.json").read_text()) + ranges = pins["jobLocalDocker"]["subordinateIdRanges"] + assert ranges == [{"start": 1, "count": 999}, {"start": 1001, "count": 64536}] + subordinate = {value for row in ranges for value in range(row["start"], row["start"] + row["count"])} + assert len(subordinate) == 65535 + assert not {0, 1000} & subordinate + assert min(subordinate) == 1 and max(subordinate) == 65536 + # All normal image IDs, including nobody=65534, fit in the inner namespace. + assert len(subordinate) >= 65534 + + +@pytest.mark.skipif(os.name == "nt", reason="rootless Docker wrapper is Linux-only") +@pytest.mark.parametrize("bad", ["", "0 0 4294967295\n", "0 1000 1\n1 100000 65535\n", + "0 1000 1\n100000 100000 65536\n", "SECRET-CANARY\n", + "0 1000 1\n1 100000 65536\n2 2 1\n", "x" * 257]) +@pytest.mark.parametrize("kind", ["uid", "gid"]) +def test_private_engine_refuses_wrong_parent_mapping_before_runtime_effects(tmp_path, monkeypatch, bad, kind): + sys.path.insert(0, str(ROOT / "ops/runner-task-images/build")) + import job_docker + for name in ("uid", "gid"): + path = tmp_path / (name + "_map") + path.write_text(bad if name == kind else "0 1000 1\n1 100000 65536\n") + monkeypatch.setattr(job_docker, name.upper() + "_MAP_PATH", path) + effects = [] + monkeypatch.setattr(job_docker, "_ensure_runtime", lambda: effects.append("runtime")) + monkeypatch.setattr(job_docker, "_launch_daemon", lambda: effects.append("daemon")) + with pytest.raises(job_docker.WrapperError) as caught: + job_docker._ensure_daemon() + assert str(caught.value) in {"private Docker parent ID mapping invalid", + "private Docker parent ID mapping unavailable"} + assert "SECRET-CANARY" not in str(caught.value) + assert effects == [] + + +@pytest.mark.skipif(os.name == "nt", reason="rootless Docker wrapper is Linux-only") +def test_private_engine_accepts_own_supported_parent_maps_and_refuses_missing_map(tmp_path, monkeypatch): + sys.path.insert(0, str(ROOT / "ops/runner-task-images/build")) + import job_docker + for kind in ("uid", "gid"): + path = tmp_path / kind + path.write_text(" 0 1000 1\n 1 100000 65536\n") + monkeypatch.setattr(job_docker, kind.upper() + "_MAP_PATH", path) + job_docker._validate_parent_id_maps() + job_docker.GID_MAP_PATH.unlink() + with pytest.raises(job_docker.WrapperError, match="mapping unavailable"): + job_docker._validate_parent_id_maps() diff --git a/backend/tests/test_runner_specialization_child_boundary.py b/backend/tests/test_runner_specialization_child_boundary.py index 55928e1..42d9d01 100644 --- a/backend/tests/test_runner_specialization_child_boundary.py +++ b/backend/tests/test_runner_specialization_child_boundary.py @@ -237,3 +237,25 @@ def test_new_finite_prejournal_failure_codes_cross_both_boundaries(specialize, m result = SimpleNamespace(returncode=1, stdout=captured.out, stderr=captured.err) assert specialize._script_failure_code(result) == code assert specialize.public_specialization_failure(specialize.RunnerFleetSpecializeError(code)) == code + + +def test_fixed_build_failure_codes_survive_child_and_private_receipt_boundary(specialize, monkeypatch, capsys): + import ast + tree = ast.parse(Path(specialize.__file__).read_text()) + codes = {node.args[0].value for node in ast.walk(tree) + if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) + and node.func.id == "RunnerFleetSpecializeError" and node.args + and isinstance(node.args[0], ast.Constant) and isinstance(node.args[0].value, str) + and node.args[0].value.startswith("runner_fleet_specialization_build_")} + assert len(codes) >= 40 + for code in codes: + def fail(code=code): + raise specialize.RunnerFleetSpecializeError(code) + monkeypatch.setattr(specialize, "_apply_from_fixed_script", fail) + assert specialize._main(["--apply-fixed", "--confirm", specialize.CONFIRMATION]) == 1 + captured = capsys.readouterr() + assert captured.out == "" + assert specialize._script_failure_code(SimpleNamespace(returncode=1, stdout="", stderr=captured.err)) == code + assert specialize.public_specialization_failure(specialize.RunnerFleetSpecializeError(code)) == code + assert specialize.public_specialization_failure(specialize.RunnerFleetSpecializeError( + "runner_fleet_specialization_build_unknown_SECRET-CANARY")) == "runner_fleet_specialization_script_execution_failed" diff --git a/backend/tests/test_runner_specialization_transport.py b/backend/tests/test_runner_specialization_transport.py index b0ec4ad..6a8a2f2 100644 --- a/backend/tests/test_runner_specialization_transport.py +++ b/backend/tests/test_runner_specialization_transport.py @@ -38,7 +38,7 @@ def test_actual_signed_specialization_transport_preserves_outcome(monkeypatch, o assert len(observed) == 1 # No automatic replay after transport loss/refusal. assert observed[0]["verify"] is True assert observed[0]["timeout"].as_dict() == { - "connect": 5, "read": 350, "write": 5, "pool": 5, + "connect": 5, "read": 3700, "write": 5, "pool": 5, } assert "SECRET-CANARY" not in str(result) if outcome == "timeout":