mirror of
https://github.com/dredx/prole.git
synced 2026-09-23 11:03:59 +00:00
chore: refactor Supabase manifest generation and split frontdoor logic
- Updated `_extract_k8s_docs` to use `yaml.safe_load_all` for improved parsing and reliability. - Enhanced `_split_frontdoor_docs` with stricter validation of Deployment, Service, and Ingress specs. - Added namespace enforcement for frontdoor resources during manifest splitting. - Refactored and optimized test cases to cover new validation and splitting functionality.
This commit is contained in:
parent
7fadc8d00c
commit
c07b50d7c3
@ -1,6 +1,8 @@
|
||||
{
|
||||
"values": "/Users/chrisfu/dev/prole/supabase/helm/generated/values.generated.json",
|
||||
"manifests": "/Users/chrisfu/dev/prole/supabase/k8s/supabase-helm.yaml",
|
||||
"db_host": "10.180.15.236",
|
||||
"manifests_app": "/Users/chrisfu/dev/prole/supabase/helm/generated/supabase-helm.app.yaml",
|
||||
"manifests_frontdoor_db": "/Users/chrisfu/dev/prole/supabase/helm/generated/supabase-helm.frontdoor-db.yaml",
|
||||
"db_host": "knoe-db-rw.knoe-db-0.svc.cluster.local",
|
||||
"supabase_namespace": "supabase"
|
||||
}
|
||||
@ -21,6 +21,8 @@ import urllib.parse
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
|
||||
import yaml
|
||||
|
||||
# Allow imports from the repo root
|
||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(REPO_ROOT))
|
||||
@ -133,46 +135,8 @@ def _normalize_public_hosts(raw_hosts: str, fallback_host: str) -> tuple[list[st
|
||||
|
||||
def _extract_k8s_docs(rendered_manifest: str) -> list[dict[str, Any]]:
|
||||
docs: list[dict[str, Any]] = []
|
||||
for chunk in rendered_manifest.split("\n---"):
|
||||
text = chunk.strip()
|
||||
if not text:
|
||||
continue
|
||||
try:
|
||||
parsed = json.loads(text)
|
||||
except Exception:
|
||||
lines = text.splitlines()
|
||||
api_version = ""
|
||||
kind = ""
|
||||
name = ""
|
||||
namespace = ""
|
||||
for idx, raw_line in enumerate(lines):
|
||||
stripped = raw_line.strip()
|
||||
if stripped.startswith("apiVersion:"):
|
||||
api_version = stripped.split(":", 1)[1].strip()
|
||||
elif stripped.startswith("kind:"):
|
||||
kind = stripped.split(":", 1)[1].strip()
|
||||
elif stripped == "metadata:":
|
||||
j = idx + 1
|
||||
while j < len(lines):
|
||||
meta_line = lines[j]
|
||||
if meta_line and not meta_line.startswith(" "):
|
||||
break
|
||||
meta_stripped = meta_line.strip()
|
||||
if meta_stripped.startswith("name:"):
|
||||
name = meta_stripped.split(":", 1)[1].strip()
|
||||
elif meta_stripped.startswith("namespace:"):
|
||||
namespace = meta_stripped.split(":", 1)[1].strip()
|
||||
j += 1
|
||||
if kind:
|
||||
docs.append(
|
||||
{
|
||||
"apiVersion": api_version,
|
||||
"kind": kind,
|
||||
"metadata": {"name": name, "namespace": namespace},
|
||||
}
|
||||
)
|
||||
continue
|
||||
if isinstance(parsed, dict) and parsed.get("kind"):
|
||||
for parsed in yaml.safe_load_all(rendered_manifest):
|
||||
if isinstance(parsed, dict) and parsed.get("kind") and parsed.get("apiVersion"):
|
||||
docs.append(parsed)
|
||||
return docs
|
||||
|
||||
@ -197,7 +161,46 @@ def _dump_manifest_docs(path: Path, docs: list[dict[str, Any]]) -> None:
|
||||
path.write_text(f"{payload}\n")
|
||||
|
||||
|
||||
def _split_frontdoor_docs(rendered_manifest: str) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
|
||||
def _is_valid_frontdoor_doc(doc: dict[str, Any]) -> bool:
|
||||
kind = str(doc.get("kind") or "")
|
||||
spec = doc.get("spec")
|
||||
if kind not in {"Deployment", "Service", "Ingress"}:
|
||||
return True
|
||||
if not isinstance(spec, dict):
|
||||
return False
|
||||
|
||||
if kind == "Service":
|
||||
ports = spec.get("ports")
|
||||
return isinstance(ports, list) and len(ports) > 0
|
||||
|
||||
if kind == "Deployment":
|
||||
selector = spec.get("selector")
|
||||
template = spec.get("template")
|
||||
if not isinstance(selector, dict) or not isinstance(template, dict):
|
||||
return False
|
||||
match_labels = selector.get("matchLabels")
|
||||
template_meta = template.get("metadata")
|
||||
template_spec = template.get("spec")
|
||||
if not isinstance(match_labels, dict) or not match_labels:
|
||||
return False
|
||||
if not isinstance(template_meta, dict):
|
||||
return False
|
||||
labels = template_meta.get("labels")
|
||||
if not isinstance(labels, dict) or not labels:
|
||||
return False
|
||||
if not isinstance(template_spec, dict):
|
||||
return False
|
||||
containers = template_spec.get("containers")
|
||||
return isinstance(containers, list) and len(containers) > 0
|
||||
|
||||
rules = spec.get("rules")
|
||||
default_backend = spec.get("defaultBackend")
|
||||
has_rules = isinstance(rules, list) and len(rules) > 0
|
||||
has_default_backend = isinstance(default_backend, dict) and len(default_backend) > 0
|
||||
return has_rules or has_default_backend
|
||||
|
||||
|
||||
def _split_frontdoor_docs(rendered_manifest: str, namespace: str = "") -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
|
||||
docs = _extract_k8s_docs(rendered_manifest)
|
||||
studio_kinds = {"Deployment", "Service", "Ingress"}
|
||||
kong_kinds = {"Deployment", "Service", "Ingress"}
|
||||
@ -209,9 +212,9 @@ def _split_frontdoor_docs(rendered_manifest: str) -> tuple[list[dict[str, Any]],
|
||||
}
|
||||
|
||||
frontdoor_docs: list[dict[str, Any]] = []
|
||||
frontdoor_keys: set[tuple[str, str, str, str]] = set()
|
||||
frontdoor_indexes: set[int] = set()
|
||||
|
||||
for doc in docs:
|
||||
for idx, doc in enumerate(docs):
|
||||
kind = str(doc.get("kind") or "")
|
||||
meta = doc.get("metadata") or {}
|
||||
name = ""
|
||||
@ -221,10 +224,18 @@ def _split_frontdoor_docs(rendered_manifest: str) -> tuple[list[dict[str, Any]],
|
||||
is_studio = name in studio_names and kind in studio_kinds
|
||||
is_kong = name in kong_names and kind in kong_kinds
|
||||
if is_studio or is_kong:
|
||||
frontdoor_indexes.add(idx)
|
||||
if not _is_valid_frontdoor_doc(doc):
|
||||
continue
|
||||
if namespace:
|
||||
if not isinstance(meta, dict):
|
||||
meta = {}
|
||||
doc["metadata"] = meta
|
||||
if not str(meta.get("namespace") or "").strip():
|
||||
meta["namespace"] = namespace
|
||||
frontdoor_docs.append(doc)
|
||||
frontdoor_keys.add(_doc_key(doc))
|
||||
|
||||
app_docs = [doc for doc in docs if _doc_key(doc) not in frontdoor_keys]
|
||||
app_docs = [doc for idx, doc in enumerate(docs) if idx not in frontdoor_indexes]
|
||||
return app_docs, frontdoor_docs
|
||||
|
||||
|
||||
@ -907,7 +918,7 @@ def render(args: argparse.Namespace) -> None:
|
||||
raise SystemExit(f"Helm template failed (code {proc.returncode})")
|
||||
rendered_path.write_text(proc.stdout)
|
||||
|
||||
app_docs, frontdoor_docs = _split_frontdoor_docs(proc.stdout)
|
||||
app_docs, frontdoor_docs = _split_frontdoor_docs(proc.stdout, namespace=meta["supabase_namespace"])
|
||||
app_rendered_path = gen_dir / "supabase-helm.app.yaml"
|
||||
frontdoor_rendered_path = gen_dir / "supabase-helm.frontdoor-db.yaml"
|
||||
_dump_manifest_docs(app_rendered_path, app_docs)
|
||||
|
||||
@ -276,11 +276,11 @@ def test_render_supabase_splits_frontdoor_manifests_for_db_cluster_rollout():
|
||||
rendered_manifest = "\n---\n".join(
|
||||
(
|
||||
'{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"supabase-auth","namespace":"supabase"}}',
|
||||
'{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"supabase-kong","namespace":"supabase"}}',
|
||||
'{"apiVersion":"v1","kind":"Service","metadata":{"name":"supabase-kong","namespace":"supabase"}}',
|
||||
'{"apiVersion":"networking.k8s.io/v1","kind":"Ingress","metadata":{"name":"supabase-kong","namespace":"supabase"}}',
|
||||
'{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"supabase-studio","namespace":"supabase"}}',
|
||||
'{"apiVersion":"networking.k8s.io/v1","kind":"Ingress","metadata":{"name":"supabase-studio","namespace":"supabase"}}',
|
||||
'{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"supabase-kong","namespace":"supabase"},"spec":{"selector":{"matchLabels":{"app":"supabase-kong"}},"template":{"metadata":{"labels":{"app":"supabase-kong"}},"spec":{"containers":[{"name":"kong","image":"kong:latest"}]}}}}',
|
||||
'{"apiVersion":"v1","kind":"Service","metadata":{"name":"supabase-kong","namespace":"supabase"},"spec":{"ports":[{"name":"proxy","port":8000}]}}',
|
||||
'{"apiVersion":"networking.k8s.io/v1","kind":"Ingress","metadata":{"name":"supabase-kong","namespace":"supabase"},"spec":{"rules":[{"host":"api.0.knoe.dev"}]}}',
|
||||
'{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"supabase-studio","namespace":"supabase"},"spec":{"selector":{"matchLabels":{"app":"supabase-studio"}},"template":{"metadata":{"labels":{"app":"supabase-studio"}},"spec":{"containers":[{"name":"studio","image":"supabase/studio:latest"}]}}}}',
|
||||
'{"apiVersion":"networking.k8s.io/v1","kind":"Ingress","metadata":{"name":"supabase-studio","namespace":"supabase"},"spec":{"rules":[{"host":"db.0.knoe.dev"}]}}',
|
||||
)
|
||||
)
|
||||
|
||||
@ -292,3 +292,102 @@ def test_render_supabase_splits_frontdoor_manifests_for_db_cluster_rollout():
|
||||
assert "supabase-auth" in app_names
|
||||
assert "supabase-kong" in frontdoor_names
|
||||
assert "supabase-studio" in frontdoor_names
|
||||
|
||||
|
||||
def test_render_supabase_split_frontdoor_preserves_complete_yaml_docs():
|
||||
rendered_manifest = """
|
||||
# Source: knoe-supabase/templates/kong-service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: supabase-kong
|
||||
spec:
|
||||
selector:
|
||||
app.kubernetes.io/name: kong
|
||||
ports:
|
||||
- name: proxy
|
||||
port: 8000
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: supabase-kong
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: supabase-kong
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: supabase-kong
|
||||
spec:
|
||||
containers:
|
||||
- name: kong
|
||||
image: kong:latest
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: supabase-kong
|
||||
spec:
|
||||
rules:
|
||||
- host: api.0.knoe.dev
|
||||
"""
|
||||
|
||||
app_docs, frontdoor_docs = _split_frontdoor_docs(rendered_manifest, namespace="supabase")
|
||||
|
||||
assert app_docs == []
|
||||
assert len(frontdoor_docs) == 3
|
||||
assert {doc["kind"] for doc in frontdoor_docs} == {"Service", "Deployment", "Ingress"}
|
||||
for doc in frontdoor_docs:
|
||||
assert doc["metadata"]["name"] == "supabase-kong"
|
||||
assert doc["metadata"]["namespace"] == "supabase"
|
||||
assert "spec" in doc
|
||||
|
||||
|
||||
def test_render_supabase_split_frontdoor_skips_invalid_kong_stubs():
|
||||
rendered_manifest = """
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: supabase-auth
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: supabase-kong
|
||||
spec: {}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: supabase-kong
|
||||
spec:
|
||||
ports: []
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: supabase-kong
|
||||
spec: {}
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: supabase-studio
|
||||
spec:
|
||||
rules:
|
||||
- host: db.0.knoe.dev
|
||||
"""
|
||||
|
||||
app_docs, frontdoor_docs = _split_frontdoor_docs(rendered_manifest)
|
||||
|
||||
app_names = {doc["metadata"]["name"] for doc in app_docs}
|
||||
frontdoor_name_kind = {(doc["metadata"]["name"], doc["kind"]) for doc in frontdoor_docs}
|
||||
|
||||
assert "supabase-auth" in app_names
|
||||
assert "supabase-kong" not in app_names
|
||||
assert ("supabase-kong", "Deployment") not in frontdoor_name_kind
|
||||
assert ("supabase-kong", "Service") not in frontdoor_name_kind
|
||||
assert ("supabase-kong", "Ingress") not in frontdoor_name_kind
|
||||
assert ("supabase-studio", "Ingress") in frontdoor_name_kind
|
||||
|
||||
Loading…
Reference in New Issue
Block a user