Files

131 lines
6.0 KiB
Python
Raw Permalink Normal View History

"""Offline overlay contract and installer staging; no Kubernetes executable needed."""
from __future__ import annotations
import runpy
from pathlib import Path
import pytest
import yaml
ROOT = Path(__file__).resolve().parents[1]
OVERLAY = ROOT / "ops/ax/substrate-kind-lean"
DISABLED = {
"OTEL_TRACES_EXPORTER": "none",
"OTEL_METRICS_EXPORTER": "none",
"OTEL_LOGS_EXPORTER": "none",
"OTEL_SDK_DISABLED": "true",
}
def test_ax_lean_overlay_removes_observability_and_replaces_export_config() -> None:
overlay = yaml.safe_load((OVERLAY / "kustomization.yaml").read_text())
assert overlay["resources"] == ["../kind-upstream"]
deletes = [p for p in overlay["patches"] if "target" in p]
assert [p["target"] for p in deletes] == [
{"namespace": "otel-system"},
{"kind": "Namespace", "name": "otel-system"},
{"kind": "ClusterRole", "name": "ate-prometheus"},
{"kind": "RoleBinding", "name": "ate-prometheus", "namespace": "ate-system"},
]
assert all(yaml.safe_load(p["patch"])["$patch"] == "delete" for p in deletes)
config = yaml.safe_load((OVERLAY / overlay["patches"][-1]["path"]).read_text())
assert config["metadata"] == {"name": "ate-otel-config", "namespace": "ate-system"}
# Whole-data replacement drops the upstream endpoint, interval and timeout,
# including any future keys that would otherwise survive a merge.
assert config["data"] == {"$patch": "replace", **DISABLED}
def test_ax_staging_preserves_upstream_and_installer_prerequisites(tmp_path: Path) -> None:
prepare = runpy.run_path(str(ROOT / "ops/ax/prepare-substrate.py"))["prepare"]
source = tmp_path / "source"
kind = source / "manifests/ate-install/kind"
kind.mkdir(parents=True)
original = "resources: [otel-collector.yaml, prometheus.yaml, ate-otel-config.yaml]\n"
(kind / "kustomization.yaml").write_text(original)
(kind / "ate-otel-config.yaml").write_text("upstream config")
(kind / "postgres").mkdir()
(kind / "postgres/kustomization.yaml").write_text("resources: [../../postgres]\n")
(source / "go.mod").write_text("module test\n")
(source / "hack").mkdir()
(source / "hack/install-ate-kind.sh").write_text("upstream installer")
stage = tmp_path / "stage"
prepare(source, stage)
install = stage / "manifests/ate-install"
assert (install / "kind-upstream/kustomization.yaml").read_text() == original
assert (install / "kind/postgres/kustomization.yaml").read_text() == "resources: [../../postgres]\n"
assert (kind / "kustomization.yaml").read_text() == original
assert (kind / "ate-otel-config.yaml").read_text() == "upstream config"
assert (stage / "hack/install-ate-kind.sh").read_text() == "upstream installer"
assert (stage / "go.mod").is_symlink()
config = yaml.safe_load((install / "kind/ate-otel-config.yaml").read_text())
assert config["data"] == DISABLED # direct installer apply must not contain $patch
overlay = yaml.safe_load((install / "kind/kustomization.yaml").read_text())
patch = yaml.safe_load((install / "kind" / overlay["patches"][-1]["path"]).read_text())
assert patch["data"] == {"$patch": "replace", **DISABLED}
with pytest.raises(ValueError, match="must be empty"):
prepare(source, stage)
@pytest.mark.parametrize("enabled", [False, True])
@pytest.mark.parametrize("existing", [False, True])
def test_ax_reconciles_observability_on_ready_clusters(tmp_path: Path, enabled: bool, existing: bool) -> None:
"""Execute the reconciliation shell with a function double, never real kubectl."""
import json
import os
import subprocess
import sys
script = (ROOT / "ops/ax/up.sh").read_text()
section = script.split("# Reconcile the choice", 1)[1].split('kubectl --context "$CTX" -n ate-system get pods', 1)[0]
section = "# Reconcile the choice" + section
source = tmp_path / "substrate/manifests/ate-install/kind"
source.mkdir(parents=True)
upstream_data = {"OTEL_EXPORTER_OTLP_ENDPOINT": "http://collector:4317", "OTEL_LOGS_EXPORTER": "otlp"}
(source / "ate-otel-config.yaml").write_text(yaml.safe_dump({"data": upstream_data}))
log = tmp_path / "calls"
consumer = {
"kind": "Deployment",
"metadata": {"name": "ate-controller"},
"spec": {"template": {"spec": {"containers": [{"envFrom": [{"configMapRef": {"name": "ate-otel-config"}}]}]}}},
}
nonconsumer = {"kind": "Deployment", "metadata": {"name": "unrelated"}, "spec": {"template": {"spec": {"containers": []}}}}
mock = """
set -euo pipefail
kubectl() {
printf '%s\\t' "$@" >> "$CALL_LOG"
printf '\\n' >> "$CALL_LOG"
case " $* " in
*" get deployment "*)
if [ "$EXISTING" = 1 ]; then printf 'deployment.apps/%s\\n' "$7"; fi ;;
*" get deployments,daemonsets "*) printf '%s' "$WORKLOADS" ;;
esac
return 0
}
"""
env = {
**os.environ,
"OBSERVABILITY": str(enabled).lower(),
"EXISTING": str(int(existing)),
"PY": sys.executable,
"CTX": "test-context",
"SRC": str(tmp_path),
"REPO_ROOT": str(ROOT),
"CALL_LOG": str(log),
"WORKLOADS": json.dumps({"items": [consumer, nonconsumer]}),
}
result = subprocess.run(["bash", "-c", mock + section], env=env, text=True, capture_output=True)
assert result.returncode == 0, result.stderr
calls = [line.rstrip("\t").split("\t") for line in log.read_text().splitlines()]
scales = [call for call in calls if "scale" in call]
assert len(scales) == (3 if existing and not enabled else 0)
assert all(call[-1] == "--replicas=0" for call in scales)
applies = [call for call in calls if "apply" in call]
assert len(applies) == (2 if enabled else 0)
config_patch = next(call[-1] for call in calls if "configmap" in call)
assert json.loads(config_patch) == [{"op": "replace", "path": "/data", "value": upstream_data if enabled else DISABLED}]
assert not any("deployment/unrelated" in call for call in calls)
assert any("deployment/ate-controller" in call for call in calls)
if not enabled:
assert "OUSAST_KIND_OBSERVABILITY=1" in result.stdout