Packages
Kubernetes cluster health monitor, with its dashboard panel bundled in the same package as a separate module (Integrations.Kubernetes.Display, #299) — one install, both halves; a release without raven_web simply runs the monitor headless.
Current section
Files
Jump to
Current section
Files
lib/integrations/kubernetes/display.ex
defmodule Integrations.Kubernetes.Display do
@moduledoc """
Dashboard panel for `Integrations.Kubernetes` (same package).
Nested under the monitor's own namespace deliberately — the package's
top-level module name never changes, and this module's name is
guaranteed distinct from any separately-published standalone display
package. `Display.BundledDefault` auto-hooks this whenever
`Integrations.Kubernetes` starts, the same as if they were still one module.
"""
use CodeNameRaven.Display, category: :infrastructure, size_hint: :full
@impl true
def display_name, do: "Kubernetes"
@impl true
def compatible_monitors, do: [Integrations.Kubernetes]
@impl true
def render(assigns) do
result = assigns[:result]
nodes = (result && result.nodes) || []
deps = (result && result.deployments) || []
pods = (result && result.pods) || []
not_ready_nodes = Enum.filter(nodes, &(&1.ready != true))
degraded_deps = Enum.filter(deps, fn d -> d.available < d.desired end)
problem_pods = Enum.filter(pods, fn p -> p.crash_looping or p.phase == "Failed" end)
assigns = Map.merge(assigns, %{
nodes: nodes,
deps: deps,
pods: pods,
not_ready_nodes: not_ready_nodes,
degraded_deps: degraded_deps,
problem_pods: problem_pods
})
~H"""
<div class="space-y-4">
<div class="flex items-start justify-between gap-2">
<h2 class="text-sm font-semibold text-base-content">Kubernetes</h2>
<CodeNameRaven.MonitorComponents.status_chip status={@status} />
</div>
<div class="grid grid-cols-3 gap-2">
<CodeNameRaven.MonitorComponents.stat
label="Nodes"
value={"#{Enum.count(@nodes, &(&1.ready == true))}/#{length(@nodes)} Ready"} />
<CodeNameRaven.MonitorComponents.stat
label="Deployments"
value={"#{Enum.count(@deps, fn d -> d.available == d.desired end)}/#{length(@deps)} OK"} />
<CodeNameRaven.MonitorComponents.stat
label="Pods"
value={"#{Enum.count(@pods, &(&1.phase == "Running"))}/#{length(@pods)} Running"} />
</div>
<div :if={@not_ready_nodes != []} class="space-y-1">
<p class="text-xs font-semibold text-error">Not Ready Nodes</p>
<div :for={n <- @not_ready_nodes}
class="text-xs font-mono text-base-content/60 bg-error/10 rounded px-2 py-1">
<%= n.name %>
<span :if={n.roles != ""} class="text-base-content/40 ml-1">(<%= n.roles %>)</span>
</div>
</div>
<div :if={@degraded_deps != []} class="space-y-1">
<p class="text-xs font-semibold text-warning">Degraded Deployments</p>
<div :for={d <- @degraded_deps}
class="text-xs font-mono text-base-content/60 bg-warning/10 rounded px-2 py-1">
<%= d.namespace %>/<%= d.name %>
<span class="text-warning ml-1">(<%= d.available %>/<%= d.desired %> available)</span>
</div>
</div>
<div :if={@problem_pods != []} class="space-y-1">
<p class="text-xs font-semibold text-warning">Problem Pods</p>
<div :for={p <- @problem_pods}
class="text-xs font-mono text-base-content/60 bg-warning/10 rounded px-2 py-1">
<%= p.namespace %>/<%= p.name %>
<span class="text-error ml-1">
<%= if p.crash_looping, do: "CrashLoopBackOff", else: p.phase %>
</span>
</div>
</div>
<CodeNameRaven.MonitorComponents.error_message message={@last_error} />
<CodeNameRaven.MonitorComponents.check_time checked_at={@last_checked_at} />
</div>
"""
end
end