Files
ansible-role-www/tests/validate_manifests.py
T
2026-10-23 12:49:53 -04:00

90 lines
5.3 KiB
Python
Executable File

"""Offline invariants for manifests rendered by real Ansible/Jinja."""
import json
from pathlib import Path
import yaml
from kubernetes_validate import validate
def docs(name):
return [doc for doc in yaml.safe_load_all(Path(f"/tmp/www-test-{name}.yml").read_text()) if doc]
def find(objects, kind, name):
return next(obj for obj in objects if obj["kind"] == kind and obj["metadata"]["name"] == name)
def pod_checks(pod, external=False):
assert pod["automountServiceAccountToken"] is False
assert pod["securityContext"]["runAsNonRoot"] is True
container = pod["containers"][0]
assert container["securityContext"]["readOnlyRootFilesystem"] is True
assert container["securityContext"]["capabilities"]["drop"] == ["ALL"]
assert container["envFrom"] == [{"configMapRef": {"name": "membership-config"}}, {"secretRef": {"name": "membership"}}]
assert not container.get("env")
assert container["resources"]["limits"]["memory"]
assert any(mount["mountPath"] == "/etc/membership/tiers" and mount["readOnly"] for mount in container["volumeMounts"])
if external:
assert pod["imagePullSecrets"] == [{"name": "registry-pull"}]
assert any(volume.get("secret", {}).get("secretName") == "private-ca" for volume in pod["volumes"])
assert any(mount["mountPath"] == "/etc/membership/ca" and mount["readOnly"] for mount in container["volumeMounts"])
all_objects = []
for name in ["website", "membership-config", "membership-postgres", "membership-migrate", "membership-workloads"]:
all_objects.extend(docs(name))
for path in Path('/tmp').glob('www-test-*.yml'):
for obj in yaml.safe_load_all(path.read_text()):
if obj and obj['kind'] != 'OnionService':
validate(obj, desired_version='1.31.0', strict=True)
assert all(obj["metadata"]["namespace"] == "www" for obj in all_objects)
assert len({(obj["kind"], obj["metadata"]["name"]) for obj in all_objects}) == len(all_objects)
config = find(all_objects, "ConfigMap", "membership-config")["data"]
assert all(isinstance(value, str) for value in config.values())
assert config["MEMBERSHIP_PUBLIC_URL"] == "https://www.eom.dev"
assert "MEMBERSHIP_SECRET_KEY" not in config and "SHKEEPER_API_KEY" not in config
tiers = json.loads(find(all_objects, "ConfigMap", "membership-tiers")["data"]["tiers.json"])
assert len(tiers) == 5 and tiers["level-4"]["usd_amount"] == "64.00"
for name in ["membership", "membership-worker"]:
deployment = find(all_objects, "Deployment", name)
pod_checks(deployment["spec"]["template"]["spec"])
web = find(all_objects, "Deployment", "membership")["spec"]["template"]["spec"]["containers"][0]
assert "assert r.status==200" in web["readinessProbe"]["exec"]["command"][-1]
worker = find(all_objects, "Deployment", "membership-worker")
assert worker["spec"]["replicas"] == 1 and worker["spec"]["strategy"]["type"] == "Recreate"
assert "sync_memberships" in worker["spec"]["template"]["spec"]["containers"][0]["command"]
job = next(obj for obj in all_objects if obj["kind"] == "Job")
assert job["metadata"]["name"] == "membership-migrate-" + "a" * 16
pod_checks(job["spec"]["template"]["spec"])
assert job["spec"]["template"]["spec"]["containers"][0]["command"] == ["python", "manage.py", "migrate", "--noinput"]
db = find(all_objects, "StatefulSet", "membership-db")
assert db["spec"]["volumeClaimTemplates"][0]["spec"]["accessModes"] == ["ReadWriteOnce"]
assert "persistentVolumeClaimRetentionPolicy" not in db["spec"]
assert find(all_objects, "Service", "membership-db")["spec"]["type"] == "ClusterIP"
assert find(all_objects, "Service", "membership")["spec"]["type"] == "ClusterIP"
policy = find(all_objects, "NetworkPolicy", "membership-web")
assert policy["spec"]["ingress"][0]["from"] == [{"podSelector": {"matchLabels": {"app": "httpd"}}}]
apache = find(all_objects, "ConfigMap", "httpd")["data"]
assert "http://membership:8000/members/" in apache["membership.conf"]
assert "X-Forwarded-Proto" in apache["membership.conf"]
assert "Require all denied" in apache["membership.conf"]
assert "env=!membership_sensitive" in apache["httpd.conf"]
assert "LoadModule proxy_http_module" in apache["httpd.conf"]
source = find(all_objects, "Deployment", "httpd")["spec"]["template"]["spec"]["initContainers"][0]
assert "rm -rf" not in source["command"][-1]
assert 'cp -a /tmp/www/htdocs/. /data/' in source["command"][-1]
ingress = find(all_objects, "Ingress", "www")
assert ingress["metadata"]["annotations"]["nginx.ingress.kubernetes.io/enable-access-log"] == "false"
assert ingress["spec"]["rules"][0]["http"]["paths"][0]["backend"]["service"]["name"] == "httpd"
for obj in docs("external-membership-workloads"):
if obj["kind"] == "Deployment":
pod_checks(obj["spec"]["template"]["spec"], external=True)
external_config = docs("external-membership-config")
assert not any(obj["kind"] == "NetworkPolicy" and obj["metadata"]["name"] == "membership-db" for obj in external_config)
assert find(external_config, "ConfigMap", "membership-config")["data"]["POSTGRES_SSLMODE"] == "verify-full"
disabled = docs("disabled-website")
assert not any(obj["kind"] == "OnionService" for obj in disabled)
assert "ProxyPass" not in find(disabled, "ConfigMap", "httpd")["data"]["membership.conf"]
print(f"PASS: {len(all_objects)} Kubernetes objects; default, external/CA/SHKeeper and disabled variants")