import asyncio import json import pytest from gitea_dynamic_runner.models import RunnerRequest from gitea_dynamic_runner import opensandbox_worker from gitea_dynamic_runner.sandbox_kubernetes import allocated_pod_name def request() -> RunnerRequest: return RunnerRequest( job_id=47, run_id=12, backend="vm", repository="panxiao81/example", job_name="publish image", labels=("self-hosted", "vm"), ) def test_sandbox_request_uses_pool_and_identity_gate(monkeypatch): monkeypatch.setattr(opensandbox_worker, "OPENSANDBOX_POOL", "ci-vm") document = opensandbox_worker.sandbox_request(request(), "gitea-vm-abcd") assert document["pool"] == "ci-vm" assert document["entrypoint"] == [ "/usr/local/libexec/gitea-opensandbox-runner" ] assert document["env"]["GITEA_RUNNER_NAME"] == "gitea-vm-abcd" assert document["env"]["GITEA_RUNNER_LABELS"] == "self-hosted:host,vm:host" assert document["env"]["CI_SPIFFE_ID"].startswith( "spiffe://ddupan.top/ci/panxiao81/example/publish-image-" ) assert document["metadata"]["ci.ddupan.top/job-id"] == "47" def test_identity_entry_is_bound_to_kata_pod_uid(monkeypatch): monkeypatch.setattr(opensandbox_worker, "SPIRE_CLUSTER_NAME", "sandbox-kata") monkeypatch.setattr(opensandbox_worker, "RUNNER_UID", 2000) manifest = opensandbox_worker.identity_entry( request(), sandbox_id="sandbox-123", pod_uid="pod-uid-456" ) assert manifest["metadata"]["name"] == opensandbox_worker.entry_name( "sandbox-123" ) spec = manifest["spec"] assert spec["parentID"] == ( "spiffe://ddupan.top/spire/agent/k8s_psat/" "sandbox-kata/pod/pod-uid-456" ) assert spec["selectors"] == ["unix:uid:2000"] assert spec["spiffeID"].startswith( "spiffe://ddupan.top/ci/panxiao81/example/publish-image-" ) @pytest.mark.parametrize( ("annotation", "expected"), [ (json.dumps({"pods": ["pool-pod-1"], "poolRef": "ci-vm"}), "pool-pod-1"), (json.dumps({"pods": []}), None), (json.dumps({"pods": ["one", "two"]}), None), ("not-json", None), ], ) def test_allocated_pod_name(annotation, expected): batchsandbox = { "metadata": { "annotations": {"sandbox.opensandbox.io/alloc-status": annotation} } } assert allocated_pod_name(batchsandbox) == expected class FakeKubernetesClient: def __init__(self): self.calls = 0 async def get_batchsandbox(self, sandbox_id): self.calls += 1 if self.calls == 1: return {"metadata": {"annotations": {}}} return { "metadata": { "annotations": { "sandbox.opensandbox.io/alloc-status": json.dumps( {"pods": ["pool-pod-1"], "poolRef": "ci-vm"} ) } } } async def get_pod(self, name): assert name == "pool-pod-1" return {"metadata": {"uid": "pod-uid-1"}} async def test_wait_for_allocation_returns_real_pod_uid(monkeypatch): async def no_sleep(_): return None monkeypatch.setattr(asyncio, "sleep", no_sleep) client = FakeKubernetesClient() assert await opensandbox_worker.wait_for_allocation(client, "sandbox-1") == ( "pool-pod-1", "pod-uid-1", ) def test_entry_name_is_stable_and_dns_safe(): name = opensandbox_worker.entry_name("sandbox/with unsafe characters") assert name == opensandbox_worker.entry_name("sandbox/with unsafe characters") assert name.startswith("gitea-ci-") assert "/" not in name