132 lines
4.1 KiB
Python
132 lines
4.1 KiB
Python
import paramiko
|
|
import sys
|
|
import time
|
|
|
|
sys.stdout.reconfigure(encoding='utf-8')
|
|
|
|
ssh = paramiko.SSHClient()
|
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
ssh.connect('140.44.4.41', username='root', key_filename=r'C:\Users\Snarf\.ssh\id_ed25519_snarfnet', timeout=10)
|
|
|
|
def run(cmd):
|
|
print(f"\n=== {cmd} ===")
|
|
_, out, err = ssh.exec_command(cmd)
|
|
res = out.read().decode('utf-8', errors='replace').strip()
|
|
if res:
|
|
print(res)
|
|
e = err.read().decode('utf-8', errors='replace').strip()
|
|
if e:
|
|
print("ERR:", e)
|
|
|
|
print("[1] Creating backup directory on Spaceport master...")
|
|
run("mkdir -p /var/backups/coder_home")
|
|
|
|
print("[2] Getting current Coder pod name...")
|
|
_, out, _ = ssh.exec_command("kubectl get pod -n infrastructure -l app.kubernetes.io/name=coder -o jsonpath='{.items[0].metadata.name}'")
|
|
pod_name = out.read().decode().strip()
|
|
print(f"Active Coder pod: {pod_name}")
|
|
|
|
print("[3] Copying /home/coder from active pod to /var/backups/coder_home...")
|
|
run(f"kubectl exec -n infrastructure {pod_name} -- tar -czf /tmp/coder_backup.tar.gz -C /home/coder .")
|
|
run(f"kubectl cp infrastructure/{pod_name}:/tmp/coder_backup.tar.gz /var/backups/coder_home/coder_backup.tar.gz")
|
|
run("ls -lh /var/backups/coder_home/coder_backup.tar.gz")
|
|
|
|
print("[4] Creating PersistentVolumeClaim 'coder-data' (10Gi, Longhorn)...")
|
|
pvc_yaml = """cat <<EOF | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: PersistentVolumeClaim
|
|
metadata:
|
|
name: coder-data
|
|
namespace: infrastructure
|
|
spec:
|
|
accessModes:
|
|
- ReadWriteOnce
|
|
storageClassName: longhorn
|
|
resources:
|
|
requests:
|
|
storage: 10Gi
|
|
EOF"""
|
|
run(pvc_yaml)
|
|
|
|
print("[5] Launching temporary population pod to unpack backup into PVC...")
|
|
helper_yaml = """cat <<EOF | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
name: coder-pvc-populator
|
|
namespace: infrastructure
|
|
spec:
|
|
restartPolicy: Never
|
|
containers:
|
|
- name: populator
|
|
image: alpine:latest
|
|
command: ["sleep", "300"]
|
|
volumeMounts:
|
|
- name: data
|
|
mountPath: /data
|
|
volumes:
|
|
- name: data
|
|
persistentVolumeClaim:
|
|
claimName: coder-data
|
|
EOF"""
|
|
run(helper_yaml)
|
|
|
|
print("Waiting for populator pod to be Ready...")
|
|
run("kubectl wait --for=condition=Ready pod/coder-pvc-populator -n infrastructure --timeout=60s")
|
|
|
|
print("[6] Copying tarball into populator pod and unpacking with correct permissions (1000:1000)...")
|
|
run("kubectl cp /var/backups/coder_home/coder_backup.tar.gz infrastructure/coder-pvc-populator:/data/coder_backup.tar.gz")
|
|
run("kubectl exec -n infrastructure coder-pvc-populator -- tar -xzf /data/coder_backup.tar.gz -C /data/")
|
|
run("kubectl exec -n infrastructure coder-pvc-populator -- rm /data/coder_backup.tar.gz")
|
|
run("kubectl exec -n infrastructure coder-pvc-populator -- chown -R 1000:1000 /data")
|
|
run("kubectl exec -n infrastructure coder-pvc-populator -- ls -la /data")
|
|
|
|
print("[7] Deleting helper populator pod...")
|
|
run("kubectl delete pod coder-pvc-populator -n infrastructure --grace-period=0 --force")
|
|
|
|
print("[8] Patching Coder deployment with persistent volume and strategy=Recreate...")
|
|
patch_json = '''kubectl patch deployment coder -n infrastructure --patch '
|
|
{
|
|
"spec": {
|
|
"strategy": {
|
|
"type": "Recreate",
|
|
"rollingUpdate": null
|
|
},
|
|
"template": {
|
|
"spec": {
|
|
"containers": [
|
|
{
|
|
"name": "coder",
|
|
"volumeMounts": [
|
|
{
|
|
"name": "coder-data",
|
|
"mountPath": "/home/coder"
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"volumes": [
|
|
{
|
|
"name": "coder-data",
|
|
"persistentVolumeClaim": {
|
|
"claimName": "coder-data"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}' '''
|
|
run(patch_json)
|
|
|
|
print("[9] Awaiting Coder rollout...")
|
|
run("kubectl rollout status deployment/coder -n infrastructure --timeout=180s")
|
|
run("kubectl get pods -n infrastructure -l app.kubernetes.io/name=coder -o wide")
|
|
|
|
print("[10] Verifying Coder health and database persistence...")
|
|
run("curl -s https://coder.snarfnet.net/api/v2/users/first")
|
|
run("curl -k -s -I https://coder.snarfnet.net | head -n 8")
|
|
|
|
ssh.close()
|
|
|