import subprocess import yaml import tempfile import time def create_backup(namespace: str, pvc: str, backup_name: str): temp_pvc_name = f"snapix-bkp-temp-{backup_name}" pvc_manifest = { "apiVersion": "v1", "kind": "PersistentVolumeClaim", "metadata": {"name": temp_pvc_name}, "spec": { "accessModes": ["ReadWriteMany"], "resources": {"requests": {"storage": "5Gi"}}, "storageClassName": "nfs-client" } } with tempfile.NamedTemporaryFile("w", delete=False) as f: yaml.dump(pvc_manifest, f) pvc_file = f.name subprocess.run(["kubectl", "apply", "-f", pvc_file, "-n", namespace], check=True) for _ in range(30): status = subprocess.check_output([ "kubectl", "get", "pvc", temp_pvc_name, "-n", namespace, "-o", "jsonpath={.status.phase}" ]).decode().strip() if status == "Bound": break time.sleep(1) else: raise TimeoutError(f"PVC {temp_pvc_name} did not reach Bound state") job_spec = { "apiVersion": "batch/v1", "kind": "Job", "metadata": { "name": f"snapix-backup-{backup_name}", "namespace": namespace }, "spec": { "template": { "spec": { "restartPolicy": "Never", "containers": [ { "name": "backup", "image": "busybox", "command": ["sh", "-c", f"mkdir -p /backup/{backup_name} && cp -r /source/* /backup/{backup_name}/"], "volumeMounts": [ {"mountPath": "/source", "name": "source-vol"}, {"mountPath": "/backup", "name": "backup-vol"} ] } ], "volumes": [ {"name": "source-vol", "persistentVolumeClaim": {"claimName": pvc}}, {"name": "backup-vol", "persistentVolumeClaim": {"claimName": temp_pvc_name}} ] } } } } try: subprocess.run( ["kubectl", "apply", "-f", "-", "-n", namespace], input=yaml.dump(job_spec).encode(), check=True, capture_output=True ) except subprocess.CalledProcessError as e: print("❌ kubectl apply failed:") print(e.stderr.decode()) raise def restore_backup(namespace: str, target_pvc: str, backup_name: str): job_spec = { "apiVersion": "batch/v1", "kind": "Job", "metadata": { "name": f"snapix-restore-{backup_name}", "namespace": namespace }, "spec": { "template": { "spec": { "restartPolicy": "Never", "containers": [ { "name": "restore", "image": "busybox", "command": ["sh", "-c", f"cp -r /backup/{backup_name}/* /restore/"], "volumeMounts": [ {"mountPath": "/restore", "name": "restore-vol"}, {"mountPath": "/backup", "name": "backup-vol"} ] } ], "volumes": [ {"name": "restore-vol", "persistentVolumeClaim": {"claimName": target_pvc}}, {"name": "backup-vol", "persistentVolumeClaim": {"claimName": f"snapix-bkp-temp-{backup_name}"}} ] } } } } try: subprocess.run( ["kubectl", "apply", "-f", "-", "-n", namespace], input=yaml.dump(job_spec).encode(), check=True, capture_output=True ) except subprocess.CalledProcessError as e: print("❌ kubectl apply failed:") print("STDOUT:\n", e.stdout.decode()) print("STDERR:\n", e.stderr.decode()) print("YAML:\n", yaml.dump(job_spec)) raise