- Move flat service manifests into structured subdirs (apps/, bootstrap-jobs/, repair-jobs/, migration-jobs/, validation-jobs/, node-ops/, networking/) - Retire oneoffs/ directories across services - Remove oceanus cluster and its host roles; add aether cluster + terraform scaffolding - Reorganize scripts/ into ops/, render/, sync/, manual-tests/ - Add Makefile with render/validate/test/flux targets and repo-structure tests - Update flux-system application CRs to the new paths - Add hermes-automated-triage-24h-plan knowledge doc (+ comms mirror) - Refresh knowledge catalogs, dashboards, vmalert rules, quality contract Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
127 lines
4.5 KiB
Python
127 lines
4.5 KiB
Python
"""Repository structure guardrails for the GitOps control plane."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import datetime as dt
|
|
import json
|
|
import stat
|
|
from pathlib import Path
|
|
|
|
import yaml
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
|
|
|
|
def _yaml_docs(path: Path) -> list[dict]:
|
|
docs = []
|
|
for doc in yaml.safe_load_all(path.read_text(encoding="utf-8")):
|
|
if isinstance(doc, dict):
|
|
docs.append(doc)
|
|
return docs
|
|
|
|
|
|
def test_root_jenkinsfile_mirrors_canonical_pipeline() -> None:
|
|
"""The root Jenkinsfile exists for discovery, but CI logic lives in ci/."""
|
|
root = (REPO_ROOT / "Jenkinsfile").read_text(encoding="utf-8").splitlines()
|
|
canonical = (REPO_ROOT / "ci/Jenkinsfile.titan-iac").read_text(
|
|
encoding="utf-8"
|
|
).splitlines()
|
|
|
|
if root and root[0].startswith("// Mirror of "):
|
|
root = root[1:]
|
|
|
|
assert root == canonical
|
|
|
|
|
|
def test_suspended_flux_kustomizations_have_reasons() -> None:
|
|
"""Suspended reconciliations should say why they are parked."""
|
|
missing = []
|
|
for path in sorted((REPO_ROOT / "clusters/atlas/flux-system").rglob("*.yaml")):
|
|
for doc in _yaml_docs(path):
|
|
if doc.get("kind") != "Kustomization":
|
|
continue
|
|
if not str(doc.get("apiVersion") or "").startswith(
|
|
"kustomize.toolkit.fluxcd.io/"
|
|
):
|
|
continue
|
|
spec = doc.get("spec") or {}
|
|
if spec.get("suspend") is not True:
|
|
continue
|
|
annotations = (doc.get("metadata") or {}).get("annotations") or {}
|
|
if not annotations.get("atlas.bstein.dev/suspend-reason"):
|
|
missing.append(str(path.relative_to(REPO_ROOT)))
|
|
|
|
assert missing == []
|
|
|
|
|
|
def test_trivy_waiver_targets_still_exist() -> None:
|
|
"""Waivers should stay temporary and point at files that still exist."""
|
|
path = REPO_ROOT / "ci/titan-iac-trivy-waivers.json"
|
|
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
expires = dt.date.fromisoformat(payload["default_expires_at"])
|
|
assert expires >= dt.date.today()
|
|
|
|
missing = []
|
|
for entry in payload.get("misconfigurations", []):
|
|
for target in entry.get("targets", []):
|
|
if target and not (REPO_ROOT / target).exists():
|
|
missing.append(f"{entry.get('id')}:{target}")
|
|
|
|
assert missing == []
|
|
|
|
|
|
def test_oceanus_is_not_a_standalone_flux_cluster() -> None:
|
|
"""Oceanus now belongs to Atlas capacity, not a separate Flux root."""
|
|
assert not (REPO_ROOT / "clusters/oceanus").exists()
|
|
assert not (REPO_ROOT / "infrastructure/modules/profiles/oceanus-validator").exists()
|
|
|
|
|
|
def test_service_job_directories_use_lifecycle_names() -> None:
|
|
"""Manual job folders should say when they are used."""
|
|
old_dirs = list((REPO_ROOT / "services").rglob("oneoffs"))
|
|
assert old_dirs == []
|
|
|
|
stale_refs = []
|
|
for root in ["clusters", "services", "ci", "testing"]:
|
|
for path in (REPO_ROOT / root).rglob("*"):
|
|
if not path.is_file():
|
|
continue
|
|
if path == Path(__file__).resolve():
|
|
continue
|
|
if path.suffix not in {".json", ".py", ".yaml", ".yml"}:
|
|
continue
|
|
if "oneoffs" in path.read_text(encoding="utf-8"):
|
|
stale_refs.append(str(path.relative_to(REPO_ROOT)))
|
|
|
|
assert stale_refs == []
|
|
|
|
|
|
def test_knowledge_service_mirror_matches_source() -> None:
|
|
"""Atlasbot should serve the generated knowledge tree, not a fork."""
|
|
source = REPO_ROOT / "knowledge"
|
|
mirror = REPO_ROOT / "services/comms/knowledge"
|
|
source_files = sorted(path.relative_to(source) for path in source.rglob("*") if path.is_file())
|
|
mirror_files = sorted(path.relative_to(mirror) for path in mirror.rglob("*") if path.is_file())
|
|
|
|
assert mirror_files == source_files
|
|
mismatched = [
|
|
str(rel)
|
|
for rel in source_files
|
|
if (source / rel).read_bytes() != (mirror / rel).read_bytes()
|
|
]
|
|
assert mismatched == []
|
|
|
|
|
|
def test_scripts_shell_entrypoints_are_executable_with_shebangs() -> None:
|
|
"""Runnable shell and Fish helpers should not need tribal chmod knowledge."""
|
|
broken = []
|
|
for path in sorted((REPO_ROOT / "scripts").rglob("*")):
|
|
if path.suffix not in {".sh", ".fish"} or not path.is_file():
|
|
continue
|
|
mode = path.stat().st_mode
|
|
first_line = path.read_text(encoding="utf-8").splitlines()[0]
|
|
if not mode & stat.S_IXUSR or not first_line.startswith("#!"):
|
|
broken.append(str(path.relative_to(REPO_ROOT)))
|
|
|
|
assert broken == []
|