orama/pkg/cli/monitor/tui/namespaces.go

159 lines
3.5 KiB
Go

package tui
import (
"fmt"
"sort"
"strings"
"github.com/DeBrosOfficial/network/pkg/cli/monitor"
)
// renderNamespacesTab renders per-namespace health across all nodes.
func renderNamespacesTab(snap *monitor.ClusterSnapshot, width int) string {
if snap == nil {
return styleMuted.Render("Collecting cluster data...")
}
reports := snap.Healthy()
if len(reports) == 0 {
return styleMuted.Render("No healthy nodes to display.")
}
var b strings.Builder
b.WriteString(styleBold.Render("Namespace Health"))
b.WriteString("\n")
b.WriteString(separator(width))
b.WriteString("\n\n")
// Collect unique namespace names
nsSet := make(map[string]bool)
for _, r := range reports {
for _, ns := range r.Namespaces {
nsSet[ns.Name] = true
}
}
nsNames := make([]string, 0, len(nsSet))
for name := range nsSet {
nsNames = append(nsNames, name)
}
sort.Strings(nsNames)
if len(nsNames) == 0 {
return styleMuted.Render("No namespaces found on any node.")
}
// Header
header := fmt.Sprintf(" %-20s", headerStyle.Render("NAMESPACE"))
for _, r := range reports {
host := nodeHost(r)
if len(host) > 15 {
host = host[:15]
}
header += fmt.Sprintf(" %-17s", headerStyle.Render(host))
}
b.WriteString(header)
b.WriteString("\n")
// Build lookup: host -> ns name -> NamespaceReport
type nsKey struct {
host string
name string
}
nsMap := make(map[nsKey]nsStatus)
for _, r := range reports {
host := nodeHost(r)
for _, ns := range r.Namespaces {
nsMap[nsKey{host, ns.Name}] = nsStatus{
gateway: ns.GatewayUp,
rqlite: ns.RQLiteUp,
rqliteState: ns.RQLiteState,
rqliteReady: ns.RQLiteReady,
olric: ns.OlricUp,
}
}
}
// Rows
for _, nsName := range nsNames {
row := fmt.Sprintf(" %-20s", nsName)
for _, r := range reports {
host := nodeHost(r)
ns, ok := nsMap[nsKey{host, nsName}]
if !ok {
row += fmt.Sprintf(" %-17s", styleMuted.Render("-"))
continue
}
row += fmt.Sprintf(" %-17s", renderNsCell(ns))
}
b.WriteString(row)
b.WriteString("\n")
}
// Detailed per-namespace view
b.WriteString("\n")
b.WriteString(styleBold.Render("Namespace Details"))
b.WriteString("\n")
b.WriteString(separator(width))
b.WriteString("\n")
for _, nsName := range nsNames {
b.WriteString(fmt.Sprintf("\n %s\n", styleBold.Render(nsName)))
for _, r := range reports {
host := nodeHost(r)
for _, ns := range r.Namespaces {
if ns.Name != nsName {
continue
}
b.WriteString(fmt.Sprintf(" %-18s gw=%s rqlite=%s",
host,
statusStr(ns.GatewayUp),
statusStr(ns.RQLiteUp),
))
if ns.RQLiteState != "" {
b.WriteString(fmt.Sprintf("(%s)", ns.RQLiteState))
}
b.WriteString(fmt.Sprintf(" olric=%s", statusStr(ns.OlricUp)))
if ns.PortBase > 0 {
b.WriteString(fmt.Sprintf(" port=%d", ns.PortBase))
}
b.WriteString("\n")
}
}
}
return b.String()
}
// nsStatus holds a namespace's health indicators for one node.
type nsStatus struct {
gateway bool
rqlite bool
rqliteState string
rqliteReady bool
olric bool
}
// renderNsCell renders a compact cell for the namespace matrix.
func renderNsCell(ns nsStatus) string {
if ns.gateway && ns.rqlite && ns.olric {
return styleHealthy.Render("OK")
}
if !ns.gateway && !ns.rqlite {
return styleCritical.Render("DOWN")
}
// Partial
parts := []string{}
if !ns.gateway {
parts = append(parts, "gw")
}
if !ns.rqlite {
parts = append(parts, "rq")
}
if !ns.olric {
parts = append(parts, "ol")
}
return styleWarning.Render("!" + strings.Join(parts, ","))
}