"""Exact-source, Harbor, and Flux contracts for the chat-router release lane.""" from __future__ import annotations import importlib.util import io import json from pathlib import Path import sys import urllib.parse import pytest import yaml ROOT = Path(__file__).resolve().parents[2] PIPELINE = ROOT / "ci/Jenkinsfile.hermes-chat-router-image" RELEASE = ROOT / "ci/scripts/hermes_chat_router_release.py" PROMOTE = ROOT / "ci/scripts/hermes_oci_promote.py" MANIFEST = ROOT / "services/hermes/chat-router.yaml" IMAGE_POLICY = ROOT / "services/hermes/image.yaml" JENKINS = ROOT / "services/jenkins/configmap-jcasc.yaml" TRIGGER = ROOT / "services/hermes/scripts/jenkins_image_build_trigger.py" STATUS = ROOT / "services/hermes/scripts/hermes_image_release_status.py" HARBOR_JOB = ROOT / "services/harbor/hermes-chat-router-immutability-job.yaml" HARBOR_POLICY = ( ROOT / "services/harbor/scripts/harbor_hermes_chat_router_immutability_ensure.py" ) HARBOR_GENERIC = ROOT / "services/harbor/scripts/harbor_immutable_rule_ensure.py" def _load(path: Path, name: str): spec = importlib.util.spec_from_file_location(name, path) assert spec is not None and spec.loader is not None module = importlib.util.module_from_spec(spec) sys.modules[name] = module spec.loader.exec_module(module) return module class Response(io.BytesIO): """Minimal context-managed urllib response.""" def __init__(self, status: int, body: bytes = b"", headers=None): super().__init__(body) self.status = status self.headers = headers or {} def __enter__(self): return self def __exit__(self, *_args): self.close() def _fixture(tmp_path: Path): module = _load(RELEASE, f"router_release_{tmp_path.name}") digest = "sha256:" + "7" * 64 revision = "8" * 40 build = "23" destination = f"{module.DEFAULT_IMAGE}:git-{revision}-build-{build}" digest_file = tmp_path / "digest" image_file = tmp_path / "image" digest_file.write_text(digest + "\n", encoding="utf-8") image_file.write_text(f"{destination}@{digest}\n", encoding="utf-8") output = tmp_path / "release" kwargs = { "digest": digest, "source_revision": revision, "build_number": build, "destination": destination, "manifest": MANIFEST, "output_dir": output, } module.write_release_artifacts(**kwargs) return module, digest_file, image_file, kwargs def test_pipeline_builds_one_exact_reviewed_router_revision() -> None: """The job detaches the reviewed main ancestor and never mutates Git or K8s.""" source = PIPELINE.read_text(encoding="utf-8") assert 'test "${CONFIRM_PUBLISH}" = "PUBLISH HERMES CHAT ROUTER"' in source assert 'test "${main_revision}" = "$(git rev-parse origin/main)"' in source assert 'git merge-base --is-ancestor "${EXPECTED_SOURCE_REVISION}"' in source assert 'git checkout --detach "${EXPECTED_SOURCE_REVISION}"' in source assert 'test "${actual_revision}" = "${EXPECTED_SOURCE_REVISION}"' in source assert "dockerfiles/Dockerfile.hermes-chat-router" in source assert "GO111MODULE=off CGO_ENABLED=0 go test ./..." in source assert "ci/scripts/hermes_chat_router_release.py assert-absent" in source assert "ci/scripts/hermes_oci_promote.py" in source assert "--digest-file=" in source assert "--image-name-tag-with-digest-file=" in source for label in ( "org.opencontainers.image.revision=${source_revision}", "org.opencontainers.image.source=https://scm.bstein.dev/atlas/titan-iac", "org.opencontainers.image.title=hermes-chat-router", ): assert label in source for forbidden in ("kubectl ", "flux reconcile", "git push", "git commit"): assert forbidden not in source pod_yaml = source.split('yaml """', 1)[1].split('"""', 1)[0] pod = yaml.safe_load(pod_yaml)["spec"] assert pod["serviceAccountName"] == "hermes-image-builder" assert pod["automountServiceAccountToken"] is False assert pod["enableServiceLinks"] is False containers = {item["name"]: item for item in pod["containers"]} assert set(containers) == {"jnlp", "python", "golang", "kaniko"} assert pod["affinity"]["nodeAffinity"][ "requiredDuringSchedulingIgnoredDuringExecution" ]["nodeSelectorTerms"][0]["matchExpressions"][0]["values"] == ["titan-20"] assert "docker.sock" not in source and "hostPath" not in source for container in containers.values(): security = container["securityContext"] assert security["allowPrivilegeEscalation"] is False assert security["capabilities"]["drop"] == ["ALL"] def test_jenkins_job_is_token_guarded_main_only_and_non_concurrent() -> None: """Only the fixed router job and fixed confirmation can enter the lane.""" config = yaml.safe_load(JENKINS.read_text(encoding="utf-8")) jobs = config["data"]["jobs.yaml"] assert jobs.count("pipelineJob('hermes-chat-router-image')") == 1 block = jobs.split("pipelineJob('hermes-chat-router-image')", 1)[1].split( "pipelineJob('hermes-voice-image')", 1 )[0] assert "branches('*/main')" in block assert "scriptPath('ci/Jenkinsfile.hermes-chat-router-image')" in block assert "authenticationToken(System.getenv('HERMES_AGENT_IMAGE_BUILD_TOKEN'))" in block assert "PUBLISH HERMES CHAT ROUTER" in block assert "pipelineTriggers" not in block pipeline = PIPELINE.read_text(encoding="utf-8") assert "disableConcurrentBuilds()" in pipeline assert "artifactDaysToKeepStr: '30'" in pipeline def test_release_renders_exactly_one_flux_consumer_and_revalidates(tmp_path: Path) -> None: """A release handoff contains only the one router Deployment mutation.""" module, digest_file, image_file, kwargs = _fixture(tmp_path) output = kwargs["output_dir"] patch = (output / "hermes-chat-router-image-update.patch").read_text() assert patch.count(f"+ image: {module.DEFAULT_IMAGE}@{kwargs['digest']}") == 1 assert "services/hermes/chat-router.yaml" in patch assert "chat-statefulset.yaml" not in patch metadata = json.loads((output / "hermes-chat-router-image.json").read_text()) assert metadata["source_revision"] == kwargs["source_revision"] assert metadata["flux_targets"] == [ "apps/Deployment/hermes/hermes-chat-router" ] module.validate_release_artifacts(**kwargs) assert module.validate_kaniko_evidence( digest_text=digest_file.read_text(), image_text=image_file.read_text(), destination=kwargs["destination"], ) == kwargs["digest"] (output / "extra").write_text("no\n") with pytest.raises(ValueError, match="exactly three"): module.validate_release_artifacts(**kwargs) @pytest.mark.parametrize( "source", [ "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: wrong\n", "# services/hermes/chat-router.yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: hermes-chat-router\n", ], ) def test_renderer_fails_closed_on_target_drift(source: str) -> None: """Identity or consumer-count drift cannot silently broaden promotion.""" module = _load(RELEASE, f"router_drift_{len(source)}") with pytest.raises(ValueError): module.render_workload(source, "sha256:" + "a" * 64) def test_harbor_candidate_evidence_and_policy_are_exact() -> None: """Candidate acceptance requires an immutable tag, digest, and OCI revision.""" module = _load(RELEASE, "router_registry_contract") revision = "a" * 40 digest = "sha256:" + "b" * 64 destination = f"{module.DEFAULT_IMAGE}:git-{revision}-build-9" seen = [] def artifact_open(request, timeout): seen.append((request, timeout)) return Response( 200, json.dumps( { "digest": digest, "tags": [ {"name": f"git-{revision}-build-9", "immutable": True} ], "extra_attrs": { "config": { "Labels": { "org.opencontainers.image.revision": revision } } }, } ).encode(), ) module.verify_registry_digest( destination, digest, revision, username="robot", password="private", opener=artifact_open, ) assert "/repositories/hermes-chat-router/artifacts/" in seen[0][0].full_url assert seen[0][0].get_header("Authorization").startswith("Basic ") def rule_open(_request, _timeout): expected = { "disabled": False, "action": "immutable", "template": "immutable_template", "tag_selectors": [ { "kind": "doublestar", "decoration": "matches", "pattern": "git-*-build-*", } ], "scope_selectors": { "repository": [ { "kind": "doublestar", "decoration": "repoMatches", "pattern": "hermes-chat-router", } ] }, } return Response( 200, json.dumps([expected]).encode(), {"X-Total-Count": "1"} ) module.verify_immutable_policy( username="robot", password="private", opener=rule_open ) assert module.assert_tag_absent( destination, username="robot", password="private", opener=lambda *_args: Response(404), ) is None with pytest.raises(RuntimeError, match="already exists"): module.assert_tag_absent( destination, username="robot", password="private", opener=lambda *_args: Response(200), ) def test_harbor_policy_is_flux_tracked_and_vault_injected() -> None: """The preflight rule is desired state and no credential is stored in Git.""" app = yaml.safe_load( (ROOT / "clusters/atlas/flux-system/applications/harbor/kustomization.yaml") .read_text(encoding="utf-8") ) assert { "apiVersion": "batch/v1", "kind": "Job", "name": "harbor-hermes-chat-router-immutability-ensure-1", "namespace": "harbor", } in app["spec"]["healthChecks"] _load(HARBOR_GENERIC, "harbor_immutable_rule_ensure") policy = _load(HARBOR_POLICY, "router_harbor_policy") assert policy.REPOSITORY_PATTERN == "hermes-chat-router" assert policy.TAG_PATTERN == "git-*-build-*" job = yaml.safe_load(HARBOR_JOB.read_text(encoding="utf-8")) template = job["spec"]["template"] annotations = template["metadata"]["annotations"] assert annotations["vault.hashicorp.com/role"] == "harbor-policy-bootstrap" assert annotations[ "vault.hashicorp.com/agent-inject-secret-harbor-admin-password" ] == "kv/data/atlas/harbor/harbor-core" container = template["spec"]["containers"][0] assert "@sha256:" in container["image"] assert container["securityContext"]["readOnlyRootFilesystem"] is True assert container["securityContext"]["runAsNonRoot"] is True source = HARBOR_POLICY.read_text().lower() + HARBOR_GENERIC.read_text().lower() assert "private-token" not in source and "password123" not in source def test_harbor_policy_helper_creates_once_and_rejects_weakened_rule() -> None: """Creation is verified by ID; a disabled matching rule fails closed.""" module = _load(HARBOR_GENERIC, "router_harbor_helper") expected = module.expected_rule("hermes-chat-router", "git-*-build-*") created = {**expected, "id": 19} class Client: origin = module.EXPECTED_ORIGIN def __init__(self): self.calls = 0 def request(self, method, path, payload=None): self.calls += 1 if self.calls == 1: return 200, b"[]", {"X-Total-Count": "0"} if self.calls == 2: assert method == "POST" and payload == expected return 201, b"", {"Location": "/api/v2.0" + path + "/19"} return 200, json.dumps([created]).encode(), {"X-Total-Count": "1"} client = Client() assert module.ensure_rule( client, project="bstein", repository="hermes-chat-router", tag_pattern="git-*-build-*", ) == 19 weakened = {**created, "disabled": True} class Weakened: origin = module.EXPECTED_ORIGIN def request(self, *_args, **_kwargs): return 200, json.dumps([weakened]).encode(), {"X-Total-Count": "1"} with pytest.raises(RuntimeError, match="not enabled and exact"): module.ensure_rule( Weakened(), project="bstein", repository="hermes-chat-router", tag_pattern="git-*-build-*", ) def test_flux_policy_and_marker_select_only_router_releases() -> None: """Candidates remain invisible until the exact release suffix exists.""" documents = list(yaml.safe_load_all(IMAGE_POLICY.read_text(encoding="utf-8"))) repository = next( item for item in documents if item["kind"] == "ImageRepository" and item["metadata"]["name"] == "hermes-chat-router-release" ) policy = next( item for item in documents if item["kind"] == "ImagePolicy" and item["metadata"]["name"] == "hermes-chat-router-release" ) assert repository["spec"]["image"] == ( "registry.bstein.dev/bstein/hermes-chat-router" ) assert policy["spec"]["filterTags"]["pattern"].endswith("-release$") assert policy["spec"]["digestReflectionPolicy"] == "Always" marker = '"$imagepolicy": "hermes:hermes-chat-router-release"' assert MANIFEST.read_text(encoding="utf-8").count(marker) == 1 def test_runtime_trigger_and_status_support_only_fixed_router_contract( tmp_path: Path, ) -> None: """Hermes can request and follow router delivery without choosing a job.""" trigger = _load(TRIGGER, "router_release_trigger") token = tmp_path / "token" token.write_text("private-token\n", encoding="utf-8") captured = {} def opener(request, timeout): captured["request"] = request assert timeout == 20 return Response( 201, headers={"Location": "https://ci.bstein.dev/queue/item/42/"} ) revision = "c" * 40 result = trigger.trigger_build( revision, component="router", token_file=token, opener=opener ) fields = urllib.parse.parse_qs(captured["request"].data.decode()) assert fields["job"] == ["hermes-chat-router-image"] assert fields["CONFIRM_PUBLISH"] == ["PUBLISH HERMES CHAT ROUTER"] assert "private-token" not in json.dumps(result) assert result["follow_command"].endswith( f"--component router --revision {revision} --wait" ) status = _load(STATUS, "router_release_status") assert status.COMPONENTS["router"] == { "policy": "hermes-chat-router-release", "repository": "registry.bstein.dev/bstein/hermes-chat-router", "workloads": ( ("deployment", "hermes-chat-router", "hermes-chat-router"), ), } def test_generic_promoter_accepts_router_and_rejects_other_repositories() -> None: """The manifest copier allow-list adds only chat-router.""" module = _load(PROMOTE, "router_promote_contract") revision = "d" * 40 digest = "sha256:" + "e" * 64 destination = ( "registry.bstein.dev/bstein/hermes-chat-router:" f"git-{revision}-build-7" ) component, tag, observed = module._validated_release( destination, digest, revision, "7" ) assert component == "hermes-chat-router" assert tag == f"git-{revision}-build-7" assert observed == digest with pytest.raises(ValueError): module._validated_release( "registry.bstein.dev/bstein/not-router:git-" + revision + "-build-7", digest, revision, "7", )