* Add davemhammer/k8s-status Kubernetes nodes/pods/deployments monitor with panel and /kube launcher. * k8s-status v1.1.5: fix stuck Querying cluster Parallel kubectl refresh, hang recovery, safer loading UI.
792 lines
20 KiB
Luau
792 lines
20 KiB
Luau
--!nonstrict
|
|
-- /kube launcher: drill into pods/nodes/deploys with autocomplete + actions.
|
|
--
|
|
-- /kube categories
|
|
-- /kube pods [filter] list pods
|
|
-- /kube pods ns/name actions (logs, shell, …)
|
|
-- /kube problems problem pods only
|
|
-- /kube nodes|deploys|ns same pattern
|
|
-- /kube panel|k9s|status|refresh
|
|
|
|
local STATE_KEY = "k8s_snapshot"
|
|
local COMMAND_KEY = "k8s_command"
|
|
local PANEL_ID = "davemhammer/k8s-status:manager"
|
|
local MAX_ROWS = 40
|
|
|
|
local snapshot = noctalia.state.get(STATE_KEY) or {
|
|
available = false,
|
|
loading = true,
|
|
pods = {},
|
|
nodes = {},
|
|
deployments = {},
|
|
namespaces = {},
|
|
readyNodes = 0,
|
|
nodeCount = 0,
|
|
problemPods = 0,
|
|
podCount = 0,
|
|
context = "",
|
|
error = "",
|
|
}
|
|
|
|
noctalia.state.watch(STATE_KEY, function(value)
|
|
if type(value) == "table" then
|
|
snapshot = value
|
|
end
|
|
end)
|
|
|
|
local function trim(s)
|
|
return noctalia.string.trim(tostring(s or ""))
|
|
end
|
|
|
|
local function lower(s)
|
|
return string.lower(tostring(s or ""))
|
|
end
|
|
|
|
local function send(action, values)
|
|
local command = { action = action, requestId = "launcher-" .. tostring(os.time()) }
|
|
if type(values) == "table" then
|
|
for k, v in pairs(values) do
|
|
command[k] = v
|
|
end
|
|
end
|
|
noctalia.state.set(COMMAND_KEY, command)
|
|
end
|
|
|
|
local function scoreText(filter, ...)
|
|
if filter == "" then
|
|
return 1
|
|
end
|
|
local best = nil
|
|
for i = 1, select("#", ...) do
|
|
local text = tostring(select(i, ...) or "")
|
|
if text ~= "" then
|
|
local s = noctalia.fuzzyScore(filter, text)
|
|
if s ~= nil and (best == nil or s > best) then
|
|
best = s
|
|
end
|
|
-- also plain substring
|
|
if best == nil and lower(text):find(lower(filter), 1, true) then
|
|
best = 0.5
|
|
end
|
|
end
|
|
end
|
|
return best
|
|
end
|
|
|
|
local function statusRow(title, subtitle, glyph)
|
|
return {
|
|
id = "",
|
|
title = title,
|
|
subtitle = subtitle,
|
|
glyph = glyph or "hexagon",
|
|
}
|
|
end
|
|
|
|
local function ensureSnapshot()
|
|
if snapshot.loading or (not snapshot.available and (snapshot.podCount or 0) == 0) then
|
|
send("refresh")
|
|
end
|
|
end
|
|
|
|
local function topCategories()
|
|
local ctx = snapshot.context ~= "" and snapshot.context or "—"
|
|
local summary = `{snapshot.readyNodes or 0}/{snapshot.nodeCount or 0} nodes · {snapshot.problemPods or 0} problems · {snapshot.podCount or 0} pods`
|
|
return {
|
|
{
|
|
id = "cat:pods",
|
|
title = noctalia.tr("launcher.cat.pods"),
|
|
subtitle = noctalia.tr("launcher.cat.pods-sub"),
|
|
glyph = "box",
|
|
score = 100,
|
|
},
|
|
{
|
|
id = "cat:problems",
|
|
title = noctalia.tr("launcher.cat.problems"),
|
|
subtitle = noctalia.tr("launcher.cat.problems-sub") .. " · " .. tostring(snapshot.problemPods or 0),
|
|
glyph = "circle-x",
|
|
score = 95,
|
|
},
|
|
{
|
|
id = "cat:nodes",
|
|
title = noctalia.tr("launcher.cat.nodes"),
|
|
subtitle = noctalia.tr("launcher.cat.nodes-sub"),
|
|
glyph = "server",
|
|
score = 90,
|
|
},
|
|
{
|
|
id = "cat:deploys",
|
|
title = noctalia.tr("launcher.cat.deploys"),
|
|
subtitle = noctalia.tr("launcher.cat.deploys-sub"),
|
|
glyph = "packages",
|
|
score = 85,
|
|
},
|
|
{
|
|
id = "cat:ns",
|
|
title = noctalia.tr("launcher.cat.ns"),
|
|
subtitle = noctalia.tr("launcher.cat.ns-sub"),
|
|
glyph = "folder",
|
|
score = 80,
|
|
},
|
|
{
|
|
id = "act:status",
|
|
title = noctalia.tr("launcher.cat.status"),
|
|
subtitle = summary .. " · " .. ctx,
|
|
glyph = "info-circle",
|
|
score = 70,
|
|
},
|
|
{
|
|
id = "act:panel",
|
|
title = noctalia.tr("launcher.cat.panel"),
|
|
subtitle = noctalia.tr("launcher.cat.panel-sub"),
|
|
glyph = "layout-dashboard",
|
|
score = 60,
|
|
},
|
|
{
|
|
id = "act:k9s",
|
|
title = noctalia.tr("launcher.cat.k9s"),
|
|
subtitle = noctalia.tr("launcher.cat.k9s-sub"),
|
|
glyph = "terminal-2",
|
|
score = 50,
|
|
},
|
|
{
|
|
id = "act:refresh",
|
|
title = noctalia.tr("launcher.cat.refresh"),
|
|
subtitle = noctalia.tr("launcher.cat.refresh-sub"),
|
|
glyph = "refresh",
|
|
score = 40,
|
|
},
|
|
}
|
|
end
|
|
|
|
local function podActions(ns, name)
|
|
local ref = ns .. "/" .. name
|
|
return {
|
|
{
|
|
id = "podact:logs:" .. ref,
|
|
title = noctalia.tr("launcher.action.logs"),
|
|
subtitle = ref .. " · " .. noctalia.tr("launcher.action.logs-sub"),
|
|
glyph = "file-text",
|
|
score = 100,
|
|
},
|
|
{
|
|
id = "podact:shell:" .. ref,
|
|
title = noctalia.tr("launcher.action.shell"),
|
|
subtitle = ref .. " · " .. noctalia.tr("launcher.action.shell-sub"),
|
|
glyph = "terminal",
|
|
score = 95,
|
|
},
|
|
{
|
|
id = "podact:describe:" .. ref,
|
|
title = noctalia.tr("launcher.action.describe"),
|
|
subtitle = noctalia.tr("launcher.action.describe-sub"),
|
|
glyph = "file-description",
|
|
score = 90,
|
|
},
|
|
{
|
|
id = "podact:yaml:" .. ref,
|
|
title = noctalia.tr("launcher.action.yaml"),
|
|
subtitle = noctalia.tr("launcher.action.yaml-sub"),
|
|
glyph = "code",
|
|
score = 85,
|
|
},
|
|
{
|
|
id = "podact:portforward:" .. ref,
|
|
title = noctalia.tr("launcher.action.portforward"),
|
|
subtitle = noctalia.tr("launcher.action.portforward-sub"),
|
|
glyph = "arrows-exchange",
|
|
score = 80,
|
|
},
|
|
{
|
|
id = "podact:copy:" .. ref,
|
|
title = noctalia.tr("launcher.action.copy"),
|
|
subtitle = noctalia.tr("launcher.action.copy-sub"),
|
|
glyph = "copy",
|
|
score = 75,
|
|
},
|
|
{
|
|
id = "podact:delete:" .. ref,
|
|
title = noctalia.tr("launcher.action.delete"),
|
|
subtitle = noctalia.tr("launcher.action.delete-sub"),
|
|
glyph = "trash",
|
|
score = 10,
|
|
},
|
|
}
|
|
end
|
|
|
|
local function nodeActions(name)
|
|
return {
|
|
{
|
|
id = "nodeact:describe:" .. name,
|
|
title = noctalia.tr("launcher.action.describe"),
|
|
subtitle = name,
|
|
glyph = "file-description",
|
|
score = 100,
|
|
},
|
|
{
|
|
id = "nodeact:yaml:" .. name,
|
|
title = noctalia.tr("launcher.action.yaml"),
|
|
subtitle = name,
|
|
glyph = "code",
|
|
score = 90,
|
|
},
|
|
{
|
|
id = "nodeact:copy:" .. name,
|
|
title = noctalia.tr("launcher.action.copy"),
|
|
subtitle = name,
|
|
glyph = "copy",
|
|
score = 80,
|
|
},
|
|
}
|
|
end
|
|
|
|
local function deployActions(ns, name)
|
|
local ref = ns .. "/" .. name
|
|
return {
|
|
{
|
|
id = "depact:restart:" .. ref,
|
|
title = noctalia.tr("launcher.action.restart"),
|
|
subtitle = ref .. " · " .. noctalia.tr("launcher.action.restart-sub"),
|
|
glyph = "refresh",
|
|
score = 100,
|
|
},
|
|
{
|
|
id = "depact:describe:" .. ref,
|
|
title = noctalia.tr("launcher.action.describe"),
|
|
subtitle = ref,
|
|
glyph = "file-description",
|
|
score = 90,
|
|
},
|
|
{
|
|
id = "depact:yaml:" .. ref,
|
|
title = noctalia.tr("launcher.action.yaml"),
|
|
subtitle = ref,
|
|
glyph = "code",
|
|
score = 85,
|
|
},
|
|
{
|
|
id = "depact:copy:" .. ref,
|
|
title = noctalia.tr("launcher.action.copy"),
|
|
subtitle = ref,
|
|
glyph = "copy",
|
|
score = 80,
|
|
},
|
|
}
|
|
end
|
|
|
|
local function filterActions(actions, filter)
|
|
if filter == "" then
|
|
return actions
|
|
end
|
|
local out = {}
|
|
for _, row in ipairs(actions) do
|
|
local s = scoreText(filter, row.title, row.id)
|
|
if s ~= nil then
|
|
row.score = s
|
|
table.insert(out, row)
|
|
end
|
|
end
|
|
return out
|
|
end
|
|
|
|
local function listPods(filter, problemsOnly)
|
|
local rows = {}
|
|
for _, p in ipairs(snapshot.pods or {}) do
|
|
if problemsOnly and not p.problem then
|
|
-- skip
|
|
else
|
|
local ref = p.namespace .. "/" .. p.name
|
|
local s = scoreText(filter, p.name, p.namespace, ref, p.status, p.node)
|
|
if s ~= nil then
|
|
table.insert(rows, {
|
|
id = "pod:" .. ref,
|
|
title = ref,
|
|
subtitle = `{p.ready} · {p.status} · r{p.restarts}` .. (p.node ~= "" and (" · " .. p.node) or ""),
|
|
glyph = p.problem and "circle-x" or "box",
|
|
score = s + (p.problem and 5 or 0),
|
|
})
|
|
end
|
|
end
|
|
end
|
|
table.sort(rows, function(a, b)
|
|
if (a.score or 0) == (b.score or 0) then
|
|
return a.title < b.title
|
|
end
|
|
return (a.score or 0) > (b.score or 0)
|
|
end)
|
|
while #rows > MAX_ROWS do
|
|
table.remove(rows)
|
|
end
|
|
if #rows == 0 then
|
|
rows[1] = statusRow(noctalia.tr("launcher.no-matches"), filter, "search")
|
|
end
|
|
return rows
|
|
end
|
|
|
|
local function listNodes(filter)
|
|
local rows = {}
|
|
for _, n in ipairs(snapshot.nodes or {}) do
|
|
local s = scoreText(filter, n.name, n.status, n.version, n.ip)
|
|
if s ~= nil then
|
|
table.insert(rows, {
|
|
id = "node:" .. n.name,
|
|
title = n.name,
|
|
subtitle = `{n.status} · {n.version} · {n.ip}`,
|
|
glyph = "server",
|
|
score = s,
|
|
})
|
|
end
|
|
end
|
|
table.sort(rows, function(a, b)
|
|
return (a.score or 0) > (b.score or 0)
|
|
end)
|
|
if #rows == 0 then
|
|
rows[1] = statusRow(noctalia.tr("launcher.no-matches"), filter, "search")
|
|
end
|
|
return rows
|
|
end
|
|
|
|
local function listDeploys(filter)
|
|
local rows = {}
|
|
for _, d in ipairs(snapshot.deployments or {}) do
|
|
local ref = d.namespace .. "/" .. d.name
|
|
local s = scoreText(filter, d.name, d.namespace, ref)
|
|
if s ~= nil then
|
|
table.insert(rows, {
|
|
id = "deploy:" .. ref,
|
|
title = ref,
|
|
subtitle = `{d.ready}/{d.desired} ready`,
|
|
glyph = "packages",
|
|
score = s + (d.problem and 5 or 0),
|
|
})
|
|
end
|
|
end
|
|
table.sort(rows, function(a, b)
|
|
return (a.score or 0) > (b.score or 0)
|
|
end)
|
|
while #rows > MAX_ROWS do
|
|
table.remove(rows)
|
|
end
|
|
if #rows == 0 then
|
|
rows[1] = statusRow(noctalia.tr("launcher.no-matches"), filter, "search")
|
|
end
|
|
return rows
|
|
end
|
|
|
|
local function listNamespaces(filter)
|
|
local rows = {}
|
|
for _, n in ipairs(snapshot.namespaces or {}) do
|
|
local s = scoreText(filter, n.name, n.phase)
|
|
if s ~= nil then
|
|
table.insert(rows, {
|
|
id = "ns:" .. n.name,
|
|
title = n.name,
|
|
subtitle = n.phase,
|
|
glyph = "folder",
|
|
score = s,
|
|
})
|
|
end
|
|
end
|
|
table.sort(rows, function(a, b)
|
|
return (a.score or 0) > (b.score or 0)
|
|
end)
|
|
if #rows == 0 then
|
|
rows[1] = statusRow(noctalia.tr("launcher.no-matches"), filter, "search")
|
|
end
|
|
return rows
|
|
end
|
|
|
|
local function findPod(ref)
|
|
ref = trim(ref)
|
|
for _, p in ipairs(snapshot.pods or {}) do
|
|
local id = p.namespace .. "/" .. p.name
|
|
if id == ref or p.name == ref then
|
|
return p
|
|
end
|
|
end
|
|
-- partial unique match
|
|
local hits = {}
|
|
local q = lower(ref)
|
|
for _, p in ipairs(snapshot.pods or {}) do
|
|
local id = p.namespace .. "/" .. p.name
|
|
if lower(id):find(q, 1, true) or lower(p.name):find(q, 1, true) then
|
|
table.insert(hits, p)
|
|
end
|
|
end
|
|
if #hits == 1 then
|
|
return hits[1]
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function findDeploy(ref)
|
|
ref = trim(ref)
|
|
for _, d in ipairs(snapshot.deployments or {}) do
|
|
local id = d.namespace .. "/" .. d.name
|
|
if id == ref or d.name == ref then
|
|
return d
|
|
end
|
|
end
|
|
local hits = {}
|
|
local q = lower(ref)
|
|
for _, d in ipairs(snapshot.deployments or {}) do
|
|
local id = d.namespace .. "/" .. d.name
|
|
if lower(id):find(q, 1, true) or lower(d.name):find(q, 1, true) then
|
|
table.insert(hits, d)
|
|
end
|
|
end
|
|
if #hits == 1 then
|
|
return hits[1]
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function findNode(name)
|
|
name = trim(name)
|
|
for _, n in ipairs(snapshot.nodes or {}) do
|
|
if n.name == name then
|
|
return n
|
|
end
|
|
end
|
|
local hits = {}
|
|
local q = lower(name)
|
|
for _, n in ipairs(snapshot.nodes or {}) do
|
|
if lower(n.name):find(q, 1, true) then
|
|
table.insert(hits, n)
|
|
end
|
|
end
|
|
if #hits == 1 then
|
|
return hits[1]
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local POD_ACTION_WORDS = {
|
|
logs = true,
|
|
log = true,
|
|
shell = true,
|
|
sh = true,
|
|
exec = true,
|
|
describe = true,
|
|
desc = true,
|
|
yaml = true,
|
|
yml = true,
|
|
pf = true,
|
|
portforward = true,
|
|
forward = true,
|
|
copy = true,
|
|
delete = true,
|
|
del = true,
|
|
rm = true,
|
|
}
|
|
|
|
local function normalizePodAction(word)
|
|
word = lower(word)
|
|
if word == "log" then return "logs" end
|
|
if word == "sh" or word == "exec" then return "shell" end
|
|
if word == "desc" then return "describe" end
|
|
if word == "yml" then return "yaml" end
|
|
if word == "pf" or word == "forward" then return "portforward" end
|
|
if word == "del" or word == "rm" then return "delete" end
|
|
if POD_ACTION_WORDS[word] then
|
|
return word
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function runPodAction(action, ns, name)
|
|
if action == "logs" then
|
|
send("logs", { namespace = ns, name = name })
|
|
elseif action == "shell" then
|
|
send("shell", { namespace = ns, name = name })
|
|
elseif action == "describe" then
|
|
send("describe", { kind = "pod", namespace = ns, name = name })
|
|
elseif action == "yaml" then
|
|
send("yaml", { kind = "pod", namespace = ns, name = name })
|
|
elseif action == "portforward" then
|
|
send("port_forward", { namespace = ns, name = name, ports = "8080:8080" })
|
|
elseif action == "copy" then
|
|
send("copy_name", { namespace = ns, name = name })
|
|
elseif action == "delete" then
|
|
send("delete_pod", { namespace = ns, name = name })
|
|
end
|
|
end
|
|
|
|
-- tokens after /kube
|
|
local function parseTokens(text)
|
|
local tokens = {}
|
|
for t in trim(text):gmatch("%S+") do
|
|
table.insert(tokens, t)
|
|
end
|
|
return tokens
|
|
end
|
|
|
|
function onQuery(query)
|
|
ensureSnapshot()
|
|
|
|
if not snapshot.available and snapshot.loading then
|
|
launcher.setResults(query, {
|
|
statusRow(noctalia.tr("launcher.loading"), snapshot.context, "loader"),
|
|
})
|
|
return
|
|
end
|
|
|
|
if not snapshot.available then
|
|
launcher.setResults(query, {
|
|
statusRow(noctalia.tr("launcher.unavailable"), snapshot.error or "", "cloud-off"),
|
|
{
|
|
id = "act:refresh",
|
|
title = noctalia.tr("launcher.cat.refresh"),
|
|
subtitle = noctalia.tr("launcher.cat.refresh-sub"),
|
|
glyph = "refresh",
|
|
},
|
|
})
|
|
return
|
|
end
|
|
|
|
local text = trim(query)
|
|
if text == "" then
|
|
launcher.setResults(query, topCategories())
|
|
return
|
|
end
|
|
|
|
local tokens = parseTokens(text)
|
|
local head = lower(tokens[1] or "")
|
|
|
|
-- fuzzy match top categories if first token is incomplete
|
|
local function isKind(name, aliases)
|
|
if head == name then
|
|
return true
|
|
end
|
|
for _, a in ipairs(aliases) do
|
|
if head == a then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
if isKind("pods", { "pod", "po", "p" }) or isKind("problems", { "problem", "bad", "fail" }) then
|
|
local problemsOnly = isKind("problems", { "problem", "bad", "fail" })
|
|
local rest = {}
|
|
for i = 2, #tokens do
|
|
table.insert(rest, tokens[i])
|
|
end
|
|
|
|
-- /kube pods ns/name action
|
|
if #rest >= 1 then
|
|
local maybeAction = normalizePodAction(rest[#rest])
|
|
local refParts = {}
|
|
local endIdx = #rest
|
|
if maybeAction and #rest >= 2 then
|
|
endIdx = #rest - 1
|
|
else
|
|
maybeAction = nil
|
|
end
|
|
for i = 1, endIdx do
|
|
table.insert(refParts, rest[i])
|
|
end
|
|
local ref = table.concat(refParts, " ")
|
|
|
|
local pod = findPod(ref)
|
|
if pod and maybeAction then
|
|
-- show only matching action (or run via activate of filtered list)
|
|
local actions = filterActions(podActions(pod.namespace, pod.name), maybeAction)
|
|
if #actions == 0 then
|
|
actions = podActions(pod.namespace, pod.name)
|
|
end
|
|
launcher.setResults(query, actions)
|
|
return
|
|
end
|
|
|
|
if pod and not maybeAction and (#rest >= 1) then
|
|
-- exact/unique pod selected via typing full ref — show actions
|
|
-- if filter still ambiguous, list pods
|
|
local exact = false
|
|
local full = pod.namespace .. "/" .. pod.name
|
|
if lower(ref) == lower(full) or lower(ref) == lower(pod.name) then
|
|
exact = true
|
|
end
|
|
-- if only one fuzzy hit for ref, treat as drill-in when user typed a slash ref
|
|
if exact or ref:find("/", 1, true) then
|
|
launcher.setResults(query, podActions(pod.namespace, pod.name))
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
local filter = table.concat(rest, " ")
|
|
launcher.setResults(query, listPods(filter, problemsOnly))
|
|
return
|
|
end
|
|
|
|
if isKind("nodes", { "node", "no" }) then
|
|
local rest = table.concat(tokens, " ", 2)
|
|
local node = findNode(rest)
|
|
if node and (lower(rest) == lower(node.name) or rest:find(node.name, 1, true)) and rest ~= "" then
|
|
-- if unique and reasonably exact, show actions
|
|
if lower(rest) == lower(node.name) then
|
|
launcher.setResults(query, nodeActions(node.name))
|
|
return
|
|
end
|
|
end
|
|
launcher.setResults(query, listNodes(rest))
|
|
return
|
|
end
|
|
|
|
if isKind("deploys", { "deploy", "deployment", "deployments", "dep", "d" }) then
|
|
local rest = table.concat(tokens, " ", 2)
|
|
local dep = findDeploy(rest)
|
|
if dep and rest:find("/", 1, true) then
|
|
launcher.setResults(query, deployActions(dep.namespace, dep.name))
|
|
return
|
|
end
|
|
if dep and lower(rest) == lower(dep.name) then
|
|
launcher.setResults(query, deployActions(dep.namespace, dep.name))
|
|
return
|
|
end
|
|
launcher.setResults(query, listDeploys(rest))
|
|
return
|
|
end
|
|
|
|
if isKind("ns", { "namespace", "namespaces", "n" }) then
|
|
local rest = table.concat(tokens, " ", 2)
|
|
launcher.setResults(query, listNamespaces(rest))
|
|
return
|
|
end
|
|
|
|
-- fallback: filter top categories + quick pod search
|
|
local rows = {}
|
|
for _, row in ipairs(topCategories()) do
|
|
local s = scoreText(text, row.title, row.id)
|
|
if s ~= nil then
|
|
row.score = s
|
|
table.insert(rows, row)
|
|
end
|
|
end
|
|
-- also inject matching pods for convenience
|
|
for _, p in ipairs(listPods(text, false)) do
|
|
if p.id ~= "" then
|
|
table.insert(rows, p)
|
|
end
|
|
end
|
|
if #rows == 0 then
|
|
rows[1] = statusRow(noctalia.tr("launcher.no-matches"), text, "search")
|
|
end
|
|
launcher.setResults(query, rows)
|
|
end
|
|
|
|
function onActivate(id)
|
|
if id == nil or id == "" then
|
|
return
|
|
end
|
|
|
|
if id == "cat:pods" then
|
|
launcher.setQuery("pods ")
|
|
return
|
|
end
|
|
if id == "cat:problems" then
|
|
launcher.setQuery("problems ")
|
|
return
|
|
end
|
|
if id == "cat:nodes" then
|
|
launcher.setQuery("nodes ")
|
|
return
|
|
end
|
|
if id == "cat:deploys" then
|
|
launcher.setQuery("deploys ")
|
|
return
|
|
end
|
|
if id == "cat:ns" then
|
|
launcher.setQuery("ns ")
|
|
return
|
|
end
|
|
|
|
if id == "act:panel" then
|
|
noctalia.togglePanel(PANEL_ID)
|
|
return
|
|
end
|
|
if id == "act:k9s" then
|
|
send("k9s", {})
|
|
return
|
|
end
|
|
if id == "act:refresh" then
|
|
send("refresh")
|
|
noctalia.notify(noctalia.tr("title"), noctalia.tr("widget.refresh_requested"))
|
|
return
|
|
end
|
|
if id == "act:status" then
|
|
local body = noctalia.tr("panel.summary", {
|
|
ready = snapshot.readyNodes or 0,
|
|
nodes = snapshot.nodeCount or 0,
|
|
problems = snapshot.problemPods or 0,
|
|
pods = snapshot.podCount or 0,
|
|
})
|
|
local ctx = snapshot.context ~= "" and snapshot.context or "default"
|
|
noctalia.notify(noctalia.tr("title") .. " · " .. ctx, body)
|
|
return
|
|
end
|
|
|
|
local podRef = id:match("^pod:(.+)$")
|
|
if podRef then
|
|
launcher.setQuery("pods " .. podRef .. " ")
|
|
return
|
|
end
|
|
|
|
local nodeName = id:match("^node:(.+)$")
|
|
if nodeName then
|
|
launcher.setQuery("nodes " .. nodeName .. " ")
|
|
return
|
|
end
|
|
|
|
local deployRef = id:match("^deploy:(.+)$")
|
|
if deployRef then
|
|
launcher.setQuery("deploys " .. deployRef .. " ")
|
|
return
|
|
end
|
|
|
|
local nsName = id:match("^ns:(.+)$")
|
|
if nsName then
|
|
noctalia.copyToClipboard(nsName, "text/plain")
|
|
noctalia.notify(noctalia.tr("title"), noctalia.tr("result.copied", { name = nsName }))
|
|
return
|
|
end
|
|
|
|
local podAct, pref = id:match("^podact:([%w]+):(.+)$")
|
|
if podAct and pref then
|
|
local ns, name = pref:match("^([^/]+)/(.+)$")
|
|
if ns and name then
|
|
runPodAction(podAct, ns, name)
|
|
end
|
|
return
|
|
end
|
|
|
|
local nodeAct, nname = id:match("^nodeact:([%w]+):(.+)$")
|
|
if nodeAct and nname then
|
|
if nodeAct == "describe" then
|
|
send("describe", { kind = "node", name = nname })
|
|
elseif nodeAct == "yaml" then
|
|
send("yaml", { kind = "node", name = nname })
|
|
elseif nodeAct == "copy" then
|
|
send("copy_name", { name = nname })
|
|
end
|
|
return
|
|
end
|
|
|
|
local depAct, dref = id:match("^depact:([%w]+):(.+)$")
|
|
if depAct and dref then
|
|
local ns, name = dref:match("^([^/]+)/(.+)$")
|
|
if ns and name then
|
|
if depAct == "restart" then
|
|
send("restart_deploy", { namespace = ns, name = name })
|
|
elseif depAct == "describe" then
|
|
send("describe", { kind = "deploy", namespace = ns, name = name })
|
|
elseif depAct == "yaml" then
|
|
send("yaml", { kind = "deploy", namespace = ns, name = name })
|
|
elseif depAct == "copy" then
|
|
send("copy_name", { namespace = ns, name = name })
|
|
end
|
|
end
|
|
return
|
|
end
|
|
end
|