Merge pull request 'fix-select-ns' (#2) from fix-select-ns into master

Reviewed-on: #2
This commit is contained in:
dvirlabs 2025-06-11 02:28:43 +00:00
commit bbe2bc4a01
6 changed files with 76 additions and 28 deletions

View File

@ -1,5 +1,6 @@
import subprocess import subprocess
import json import json
import re
def get_namespaces(): def get_namespaces():
output = subprocess.check_output(["kubectl", "get", "ns", "-o", "json"]) output = subprocess.check_output(["kubectl", "get", "ns", "-o", "json"])
@ -10,3 +11,18 @@ def get_pvcs(namespace: str):
output = subprocess.check_output(["kubectl", "get", "pvc", "-n", namespace, "-o", "json"]) output = subprocess.check_output(["kubectl", "get", "pvc", "-n", namespace, "-o", "json"])
data = json.loads(output) data = json.loads(output)
return [item["metadata"]["name"] for item in data["items"]] return [item["metadata"]["name"] for item in data["items"]]
def get_all_backup_pvcs():
output = subprocess.check_output(["kubectl", "get", "pvc", "-A", "-o", "json"])
data = json.loads(output)
backup_pvcs = []
for item in data["items"]:
name = item["metadata"]["name"]
namespace = item["metadata"]["namespace"]
if re.match(r"^snapix-bkp-temp-", name):
backup_pvcs.append({
"name": name,
"namespace": namespace
})
return backup_pvcs

View File

@ -1,7 +1,7 @@
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from models import BackupRequest, RestoreRequest from models import BackupRequest, RestoreRequest
from k8s_utils import get_namespaces, get_pvcs from k8s_utils import get_namespaces, get_pvcs, get_all_backup_pvcs
from backup_manager import create_backup, restore_backup from backup_manager import create_backup, restore_backup
app = FastAPI() app = FastAPI()
@ -33,6 +33,11 @@ def restore_pvc(request: RestoreRequest):
return {"message": "Restore job created."} return {"message": "Restore job created."}
@app.get("/backup-pvcs")
def list_backup_pvcs():
return get_all_backup_pvcs()
if __name__ == "__main__": if __name__ == "__main__":
import uvicorn import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True) uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)

View File

@ -8,3 +8,5 @@ export const getNamespaces = () => api.get('/namespaces');
export const getPVCs = (namespace) => api.get(`/pvcs/${namespace}`); export const getPVCs = (namespace) => api.get(`/pvcs/${namespace}`);
export const createBackup = (payload) => api.post('/backup', payload); export const createBackup = (payload) => api.post('/backup', payload);
export const restoreBackup = (payload) => api.post('/restore', payload); export const restoreBackup = (payload) => api.post('/restore', payload);
export const getBackupPVCs = () => api.get('/backup-pvcs');

View File

@ -1,12 +1,30 @@
import { useState } from 'react'; import { useEffect, useState } from 'react';
import { restoreBackup } from '../api/snapix'; import { restoreBackup, getNamespaces, getBackupPVCs } from '../api/snapix';
import { Button, Input } from '@mui/base' import { Button } from '@mui/base';
import '../style/RestoreForm.css'; // Assuming you have some styles for the form import '../style/RestoreForm.css';
export default function RestoreForm() { export default function RestoreForm() {
const [backupName, setBackupName] = useState(''); const [backupName, setBackupName] = useState('');
const [targetNs, setTargetNs] = useState(''); const [targetNs, setTargetNs] = useState('');
const [targetPvc, setTargetPvc] = useState(''); const [targetPvc, setTargetPvc] = useState('');
const [namespaces, setNamespaces] = useState([]);
const [backupPvcs, setBackupPvcs] = useState([]);
useEffect(() => {
getNamespaces().then(res => setNamespaces(res.data));
}, []);
useEffect(() => {
if (targetNs) {
getBackupPVCs().then(res => {
// Filter backup PVCs only for the selected namespace
const filtered = res.data.filter(pvc => pvc.namespace === targetNs);
setBackupPvcs(filtered);
});
} else {
setBackupPvcs([]);
}
}, [targetNs]);
const handleRestore = () => { const handleRestore = () => {
if (backupName && targetNs && targetPvc) { if (backupName && targetNs && targetPvc) {
@ -15,45 +33,52 @@ export default function RestoreForm() {
target_namespace: targetNs, target_namespace: targetNs,
target_pvc: targetPvc, target_pvc: targetPvc,
}).then(() => { }).then(() => {
alert("Restore started!"); alert('Restore started!');
}); });
} }
}; };
return ( return (
<div className='form-group'> <div className="form-group">
<h2>🔁 Restore PVC</h2> <h2>🔁 Restore PVC</h2>
<div className='form-content'>
<label>Backup Name:</label> <div className="form-content">
<Input <label>Target Namespace:</label>
type="text" <select value={targetNs} onChange={e => setTargetNs(e.target.value)}>
<option value="">-- Select Namespace --</option>
{namespaces.map(ns => (
<option key={ns} value={ns}>{ns}</option>
))}
</select>
</div>
<div className="form-content">
<label>Backup PVC:</label>
<select
value={backupName} value={backupName}
onChange={e => setBackupName(e.target.value)} onChange={e => setBackupName(e.target.value)}
className='restore-input' disabled={!targetNs}
/> >
<option value="">-- Select Backup PVC --</option>
{backupPvcs.map(({ name }) => (
<option key={name} value={name}>{name}</option>
))}
</select>
</div> </div>
<div className='form-content'> <div className="form-content">
<label>Target Namespace:</label> <label>Target PVC:</label>
<Input <input
type="text"
value={targetNs}
onChange={e => setTargetNs(e.target.value)}
className='restore-input'
/>
</div>
<div className='form-content'>
<label>Target PVC:</label>
<Input
type="text" type="text"
value={targetPvc} value={targetPvc}
onChange={e => setTargetPvc(e.target.value)} onChange={e => setTargetPvc(e.target.value)}
className='restore-input' placeholder="the exact name of the pvc of your app"
/> />
</div> </div>
<Button id='restore-btn' className='btn' onClick={handleRestore}>Restore</Button> <Button id="restore-btn" className="btn" onClick={handleRestore}>
Restore
</Button>
</div> </div>
); );
} }