Files
bakery-ia/services/demo_session/app/services/k8s_job_cloner.py
2025-10-03 14:09:34 +02:00

167 lines
6.3 KiB
Python

"""
Kubernetes Job-based Demo Data Cloner
Triggers a K8s Job to clone demo data at the database level
"""
import httpx
import structlog
from typing import Dict, Any
import os
logger = structlog.get_logger()
class K8sJobCloner:
"""Triggers Kubernetes Jobs to clone demo data"""
def __init__(self):
self.k8s_api_url = os.getenv("KUBERNETES_SERVICE_HOST")
self.namespace = os.getenv("POD_NAMESPACE", "bakery-ia")
self.clone_job_image = os.getenv("CLONE_JOB_IMAGE", "bakery/inventory-service:latest")
# Service account token for K8s API access
with open("/var/run/secrets/kubernetes.io/serviceaccount/token", "r") as f:
self.token = f.read()
async def clone_tenant_data(
self,
session_id: str,
base_demo_tenant_id: str,
virtual_tenant_id: str,
demo_account_type: str
) -> Dict[str, Any]:
"""
Clone demo data by creating a Kubernetes Job
Args:
session_id: Session ID
base_demo_tenant_id: Base demo tenant UUID (not used in job approach)
virtual_tenant_id: Virtual tenant UUID for this session
demo_account_type: Type of demo account
Returns:
Job creation status
"""
logger.info(
"Triggering demo data cloning job",
session_id=session_id,
virtual_tenant_id=virtual_tenant_id,
demo_account_type=demo_account_type,
clone_image=self.clone_job_image
)
job_name = f"demo-clone-{virtual_tenant_id[:8]}"
# Create Job manifest
job_manifest = {
"apiVersion": "batch/v1",
"kind": "Job",
"metadata": {
"name": job_name,
"namespace": self.namespace,
"labels": {
"app": "demo-clone",
"session-id": session_id,
"component": "runtime"
}
},
"spec": {
"ttlSecondsAfterFinished": 3600,
"backoffLimit": 2,
"template": {
"metadata": {
"labels": {"app": "demo-clone"}
},
"spec": {
"restartPolicy": "Never",
"containers": [{
"name": "clone-data",
"image": self.clone_job_image, # Configured via environment variable
"imagePullPolicy": "IfNotPresent", # Don't pull if image exists locally
"command": ["python", "/app/scripts/demo/clone_demo_tenant.py"],
"env": [
{"name": "VIRTUAL_TENANT_ID", "value": virtual_tenant_id},
{"name": "DEMO_ACCOUNT_TYPE", "value": demo_account_type},
{
"name": "INVENTORY_DATABASE_URL",
"valueFrom": {
"secretKeyRef": {
"name": "database-secrets",
"key": "INVENTORY_DATABASE_URL"
}
}
},
{
"name": "SALES_DATABASE_URL",
"valueFrom": {
"secretKeyRef": {
"name": "database-secrets",
"key": "SALES_DATABASE_URL"
}
}
},
{
"name": "ORDERS_DATABASE_URL",
"valueFrom": {
"secretKeyRef": {
"name": "database-secrets",
"key": "ORDERS_DATABASE_URL"
}
}
},
{"name": "LOG_LEVEL", "value": "INFO"}
],
"resources": {
"requests": {"memory": "256Mi", "cpu": "100m"},
"limits": {"memory": "512Mi", "cpu": "500m"}
}
}]
}
}
}
}
try:
# Create the Job via K8s API
async with httpx.AsyncClient(verify=False, timeout=30.0) as client:
response = await client.post(
f"https://{self.k8s_api_url}/apis/batch/v1/namespaces/{self.namespace}/jobs",
json=job_manifest,
headers={
"Authorization": f"Bearer {self.token}",
"Content-Type": "application/json"
}
)
if response.status_code == 201:
logger.info(
"Demo clone job created successfully",
job_name=job_name,
session_id=session_id
)
return {
"success": True,
"job_name": job_name,
"method": "kubernetes_job"
}
else:
logger.error(
"Failed to create demo clone job",
status_code=response.status_code,
response=response.text
)
return {
"success": False,
"error": f"K8s API returned {response.status_code}"
}
except Exception as e:
logger.error(
"Error creating demo clone job",
error=str(e),
exc_info=True
)
return {
"success": False,
"error": str(e)
}