From c47bf0ea165e6e397a5b3ce45123d2922aaaa35e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Stach?= Date: Fri, 9 Oct 2026 17:32:46 +0200 Subject: [PATCH] feat: add cordon/uncordon endpoints and node action toggles Co-authored-by: aider (openrouter/thinkingmachines/inkling-small:free) --- backend/main.py | 10 ++++++++++ frontend/app.js | 42 ++++++++++++++++++++++++++++++++++++++++++ frontend/index.html | 2 ++ 3 files changed, 54 insertions(+) diff --git a/backend/main.py b/backend/main.py index 78a0a9a..0b07b83 100644 --- a/backend/main.py +++ b/backend/main.py @@ -115,3 +115,13 @@ def exec_pod(pod: str, namespace: str = "default", context: str = None, containe cmd += ["--", "sh", "-c", command] result = run_kubectl(cmd, context=context) return {"output": result.stdout, "stderr": result.stderr, "returncode": result.returncode} + +@app.get("/cordon") +def cordon(node: str, context: str = None): + result = run_kubectl(["cordon", node], context=context) + return {"output": result.stdout, "stderr": result.stderr, "returncode": result.returncode} + +@app.get("/uncordon") +def uncordon(node: str, context: str = None): + result = run_kubectl(["uncordon", node], context=context) + return {"output": result.stdout, "stderr": result.stderr, "returncode": result.returncode} diff --git a/frontend/app.js b/frontend/app.js index d5d0979..7725793 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -29,6 +29,15 @@ document.addEventListener('DOMContentLoaded', () => { if (res) resourceSelect.value = res; } + function updateActionVisibility() { + const isNode = resourceSelect.value === 'nodes'; + document.getElementById('btn-delete').style.display = isNode ? 'none' : 'inline-block'; + document.getElementById('btn-logs').style.display = isNode ? 'none' : 'inline-block'; + document.getElementById('btn-exec').style.display = isNode ? 'none' : 'inline-block'; + document.getElementById('btn-cordon').style.display = isNode ? 'inline-block' : 'none'; + document.getElementById('btn-uncordon').style.display = isNode ? 'inline-block' : 'none'; + } + async function loadContexts() { try { const res = await fetch('http://localhost:8000/contexts'); @@ -112,6 +121,7 @@ document.addEventListener('DOMContentLoaded', () => { currentData = []; } renderList(); + updateActionVisibility(); } catch (e) { console.error('Failed to load resources', e); resourceList.innerHTML = '
  • Error loading resources
  • '; @@ -253,5 +263,37 @@ document.addEventListener('DOMContentLoaded', () => { } }); + document.getElementById('btn-cordon').addEventListener('click', async () => { + const node = actionName.value.trim(); + if (!node) return; + const ctx = contextSelect.value || null; + try { + const url = new URL('http://localhost:8000/cordon'); + url.searchParams.set('node', node); + if (ctx) url.searchParams.set('context', ctx); + const res = await fetch(url); + const data = await res.json(); + actionOutput.textContent = data.output || data.error || JSON.stringify(data); + } catch (e) { + actionOutput.textContent = 'Error: ' + e; + } + }); + + document.getElementById('btn-uncordon').addEventListener('click', async () => { + const node = actionName.value.trim(); + if (!node) return; + const ctx = contextSelect.value || null; + try { + const url = new URL('http://localhost:8000/uncordon'); + url.searchParams.set('node', node); + if (ctx) url.searchParams.set('context', ctx); + const res = await fetch(url); + const data = await res.json(); + actionOutput.textContent = data.output || data.error || JSON.stringify(data); + } catch (e) { + actionOutput.textContent = 'Error: ' + e; + } + }); + loadContexts(); }); diff --git a/frontend/index.html b/frontend/index.html index 109ef2e..402c415 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -52,6 +52,8 @@ + +