Add automatic cluster pool allocation.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
keyhan
2026-05-18 23:08:06 +03:30
parent 97e4c865b6
commit 72a1519ea0
19 changed files with 983 additions and 151 deletions
@@ -16,8 +16,10 @@ export default function AdminPoolsPage() {
const [form, setForm] = useState({
name: '',
description: '',
strategy: 'least-apps' as 'least-apps' | 'round-robin',
strategy: 'weighted-resource' as 'least-apps' | 'round-robin' | 'weighted-resource',
clusterIds: [] as string[],
isDefault: false,
priority: 100,
});
const { data: pools = [], isLoading } = useQuery<ClusterPool[]>({
@@ -66,7 +68,7 @@ export default function AdminPoolsPage() {
const resetForm = () => {
setShowForm(false);
setEditingPool(null);
setForm({ name: '', description: '', strategy: 'least-apps', clusterIds: [] });
setForm({ name: '', description: '', strategy: 'weighted-resource', clusterIds: [], isDefault: false, priority: 100 });
};
const startEdit = (pool: ClusterPool) => {
@@ -76,6 +78,8 @@ export default function AdminPoolsPage() {
description: pool.description || '',
strategy: pool.strategy,
clusterIds: pool.clusterIds,
isDefault: pool.isDefault || false,
priority: pool.priority || 100,
});
setShowForm(true);
};
@@ -138,10 +142,21 @@ export default function AdminPoolsPage() {
value={form.strategy}
onChange={(e) => setForm({ ...form, strategy: e.target.value as any })}
>
<option value="weighted-resource">Weighted Resource prefer healthy capacity and higher weights</option>
<option value="least-apps">Least Apps deploy to cluster with fewest apps</option>
<option value="round-robin">Round Robin rotate across clusters evenly</option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Priority</label>
<input
type="number"
min={1}
className="input-field"
value={form.priority}
onChange={(e) => setForm({ ...form, priority: Number(e.target.value) || 100 })}
/>
</div>
</div>
<div>
@@ -154,6 +169,15 @@ export default function AdminPoolsPage() {
/>
</div>
<label className="flex items-center gap-2 text-sm text-gray-700">
<input
type="checkbox"
checked={form.isDefault}
onChange={(e) => setForm({ ...form, isDefault: e.target.checked })}
/>
Use as default allocator pool
</label>
{/* Cluster selection */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
@@ -195,7 +219,7 @@ export default function AdminPoolsPage() {
)}
</p>
<p className="text-xs text-gray-500">
{cluster.provider || 'Unknown'} · {cluster.region || 'N/A'}
{cluster.provider || 'Unknown'} · {cluster.region || 'N/A'} · weight {cluster.weight || 1}
</p>
</div>
</div>
@@ -206,6 +230,17 @@ export default function AdminPoolsPage() {
}`}>
{cluster.status}
</span>
<span className={`px-2 py-0.5 rounded-full text-xs font-medium ${
cluster.healthStatus === 'healthy'
? 'bg-green-100 text-green-700'
: cluster.healthStatus === 'degraded'
? 'bg-yellow-100 text-yellow-700'
: cluster.healthStatus === 'unhealthy'
? 'bg-red-100 text-red-700'
: 'bg-gray-100 text-gray-600'
}`}>
{cluster.healthStatus || 'unknown'}
</span>
</div>
</button>
);
@@ -278,8 +313,14 @@ export default function AdminPoolsPage() {
{pool.isActive ? 'Active' : 'Inactive'}
</span>
<span className="badge badge-purple flex items-center gap-1">
{pool.strategy === 'least-apps' ? <><BarChart3 className="w-3 h-3" /> Least Apps</> : <><RotateCw className="w-3 h-3" /> Round Robin</>}
{pool.strategy === 'weighted-resource'
? <><BarChart3 className="w-3 h-3" /> Weighted Resource</>
: pool.strategy === 'least-apps'
? <><BarChart3 className="w-3 h-3" /> Least Apps</>
: <><RotateCw className="w-3 h-3" /> Round Robin</>}
</span>
{pool.isDefault && <span className="badge badge-blue">Default Pool</span>}
<span className="badge badge-gray">Priority {pool.priority || 100}</span>
</div>
{pool.description && (
<p className="text-sm text-gray-500 mb-3">{pool.description}</p>
@@ -299,7 +340,7 @@ export default function AdminPoolsPage() {
<span>{cluster.status === 'active' ? <CheckCircle className="w-3 h-3" /> : <XCircle className="w-3 h-3" />}</span>
<span>{cluster.name}</span>
<span className="text-gray-400">
({cluster.provider || 'N/A'} · {cluster.region || 'N/A'})
({cluster.provider || 'N/A'} · {cluster.region || 'N/A'} · weight {cluster.weight || 1} · {cluster.healthStatus || 'unknown'})
</span>
</div>
)) : (