126 lines
4.2 KiB
Python
126 lines
4.2 KiB
Python
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
|
|
|