mirror of
https://github.com/dredx/prole.git
synced 2026-09-24 14:04:31 +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",
|
"values": "/Users/chrisfu/dev/prole/supabase/helm/generated/values.generated.json",
|
||||||
"manifests": "/Users/chrisfu/dev/prole/supabase/k8s/supabase-helm.yaml",
|
"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"
|
"supabase_namespace": "supabase"
|
||||||
}
|
}
|
||||||
@ -21,6 +21,8 @@ import urllib.parse
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
# Allow imports from the repo root
|
# Allow imports from the repo root
|
||||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||||
sys.path.insert(0, str(REPO_ROOT))
|
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]]:
|
def _extract_k8s_docs(rendered_manifest: str) -> list[dict[str, Any]]:
|
||||||
docs: list[dict[str, Any]] = []
|
docs: list[dict[str, Any]] = []
|
||||||
for chunk in rendered_manifest.split("\n---"):
|
for parsed in yaml.safe_load_all(rendered_manifest):
|
||||||
text = chunk.strip()
|
if isinstance(parsed, dict) and parsed.get("kind") and parsed.get("apiVersion"):
|
||||||
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"):
|
|
||||||
docs.append(parsed)
|
docs.append(parsed)
|
||||||
return docs
|
return docs
|
||||||
|
|
||||||
@ -197,7 +161,46 @@ def _dump_manifest_docs(path: Path, docs: list[dict[str, Any]]) -> None:
|
|||||||
path.write_text(f"{payload}\n")
|
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)
|
docs = _extract_k8s_docs(rendered_manifest)
|
||||||
studio_kinds = {"Deployment", "Service", "Ingress"}
|
studio_kinds = {"Deployment", "Service", "Ingress"}
|
||||||
kong_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_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 "")
|
kind = str(doc.get("kind") or "")
|
||||||
meta = doc.get("metadata") or {}
|
meta = doc.get("metadata") or {}
|
||||||
name = ""
|
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_studio = name in studio_names and kind in studio_kinds
|
||||||
is_kong = name in kong_names and kind in kong_kinds
|
is_kong = name in kong_names and kind in kong_kinds
|
||||||
if is_studio or is_kong:
|
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_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
|
return app_docs, frontdoor_docs
|
||||||
|
|
||||||
|
|
||||||
@ -907,7 +918,7 @@ def render(args: argparse.Namespace) -> None:
|
|||||||
raise SystemExit(f"Helm template failed (code {proc.returncode})")
|
raise SystemExit(f"Helm template failed (code {proc.returncode})")
|
||||||
rendered_path.write_text(proc.stdout)
|
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"
|
app_rendered_path = gen_dir / "supabase-helm.app.yaml"
|
||||||
frontdoor_rendered_path = gen_dir / "supabase-helm.frontdoor-db.yaml"
|
frontdoor_rendered_path = gen_dir / "supabase-helm.frontdoor-db.yaml"
|
||||||
_dump_manifest_docs(app_rendered_path, app_docs)
|
_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(
|
rendered_manifest = "\n---\n".join(
|
||||||
(
|
(
|
||||||
'{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"supabase-auth","namespace":"supabase"}}',
|
'{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"supabase-auth","namespace":"supabase"}}',
|
||||||
'{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"supabase-kong","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"}}',
|
'{"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"}}',
|
'{"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"}}',
|
'{"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"}}',
|
'{"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-auth" in app_names
|
||||||
assert "supabase-kong" in frontdoor_names
|
assert "supabase-kong" in frontdoor_names
|
||||||
assert "supabase-studio" 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