agentctl / backend /routers /apply.py
GitHub Actions
Sync from GitHub on master (Wed Nov 19 12:57:50 UTC 2025)
267fc17
Raw History Blame Contribute Delete
2.32 kB
# backend/routers/apply.py
from fastapi import APIRouter
from pydantic import BaseModel
import uuid
import yaml
from agentctl.k8s_client import K8sClient
router = APIRouter(tags=["Apply Manifest"])
class ApplyRequest(BaseModel):
yaml: str
class ApplyResponse(BaseModel):
success: bool
message: str
raw: str | None = None
def _inject_unique_name(yaml_text: str) -> str:
"""
Add a short UUID suffix to metadata.name for Job/CronJob/Deployment
to avoid 409 AlreadyExists errors when users re-apply the same YAML.
"""
try:
data = yaml.safe_load(yaml_text)
if not isinstance(data, dict):
return yaml_text
kind = data.get("kind")
if kind not in {"Job", "CronJob", "Deployment"}:
return yaml_text
meta = data.get("metadata") or {}
base_name = meta.get("name")
if not base_name:
return yaml_text
suffix = str(uuid.uuid4())[:5]
new_name = f"{base_name}-{suffix}"
meta["name"] = new_name
data["metadata"] = meta
# For CronJob make sure template labels are consistent
if kind == "CronJob":
tmpl = (
data.get("spec", {})
.get("jobTemplate", {})
.get("spec", {})
.get("template", {})
)
if isinstance(tmpl, dict):
md = tmpl.get("metadata") or {}
labels = md.get("labels") or {}
labels["app"] = new_name
md["labels"] = labels
tmpl["metadata"] = md
return yaml.safe_dump(data)
except Exception:
# on failure, just return original yaml
return yaml_text
@router.post("/apply", response_model=ApplyResponse)
def apply_manifest(req: ApplyRequest):
"""
Apply a YAML manifest to the Kubernetes cluster via K8sClient.
"""
# Lazy K8sClient instantiation so missing env vars don't crash import
client = K8sClient()
final_yaml = _inject_unique_name(req.yaml)
result = client.apply_manifest(final_yaml)
try:
raw_str = str(result.raw_response)
except Exception:
raw_str = None
return ApplyResponse(
success=result.success,
message=result.message,
raw=raw_str,
)