mirror of
https://github.com/DeBrosOfficial/orama.git
synced 2026-03-17 13:56:57 +00:00
132 lines
3.1 KiB
Go
132 lines
3.1 KiB
Go
package display
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/DeBrosOfficial/network/pkg/cli/monitor"
|
|
)
|
|
|
|
// ServiceTable prints a cross-node service status matrix to w.
|
|
func ServiceTable(snap *monitor.ClusterSnapshot, w io.Writer) error {
|
|
fmt.Fprintf(w, "%s\n", styleBold.Render(
|
|
fmt.Sprintf("Service Status Matrix \u2014 %s", snap.Environment)))
|
|
fmt.Fprintln(w, strings.Repeat("\u2550", 36))
|
|
fmt.Fprintln(w)
|
|
|
|
// Collect all service names and build per-host maps
|
|
type hostServices struct {
|
|
host string
|
|
shortIP string
|
|
services map[string]string // name -> active_state
|
|
}
|
|
|
|
var hosts []hostServices
|
|
serviceSet := map[string]bool{}
|
|
|
|
for _, cs := range snap.Nodes {
|
|
if cs.Error != nil || cs.Report == nil || cs.Report.Services == nil {
|
|
continue
|
|
}
|
|
hs := hostServices{
|
|
host: cs.Node.Host,
|
|
shortIP: shortIP(cs.Node.Host),
|
|
services: make(map[string]string),
|
|
}
|
|
for _, svc := range cs.Report.Services.Services {
|
|
hs.services[svc.Name] = svc.ActiveState
|
|
serviceSet[svc.Name] = true
|
|
}
|
|
hosts = append(hosts, hs)
|
|
}
|
|
|
|
// Sort service names
|
|
var svcNames []string
|
|
for name := range serviceSet {
|
|
svcNames = append(svcNames, name)
|
|
}
|
|
sort.Strings(svcNames)
|
|
|
|
if len(hosts) == 0 || len(svcNames) == 0 {
|
|
fmt.Fprintln(w, styleMuted.Render(" No service data available"))
|
|
return nil
|
|
}
|
|
|
|
// Header: SERVICE + each host short IP
|
|
hdr := fmt.Sprintf("%-22s", styleHeader.Render("SERVICE"))
|
|
for _, h := range hosts {
|
|
hdr += fmt.Sprintf("%-12s", styleHeader.Render(h.shortIP))
|
|
}
|
|
fmt.Fprintln(w, hdr)
|
|
fmt.Fprintln(w, separator(22+12*len(hosts)))
|
|
|
|
// Rows
|
|
for _, name := range svcNames {
|
|
row := fmt.Sprintf("%-22s", name)
|
|
for _, h := range hosts {
|
|
state, ok := h.services[name]
|
|
if !ok {
|
|
row += fmt.Sprintf("%-12s", styleMuted.Render("--"))
|
|
} else {
|
|
row += fmt.Sprintf("%-12s", colorServiceState(state))
|
|
}
|
|
}
|
|
fmt.Fprintln(w, row)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ServiceJSON writes the service matrix as JSON.
|
|
func ServiceJSON(snap *monitor.ClusterSnapshot, w io.Writer) error {
|
|
type svcEntry struct {
|
|
Host string `json:"host"`
|
|
Services map[string]string `json:"services"`
|
|
}
|
|
|
|
var entries []svcEntry
|
|
for _, cs := range snap.Nodes {
|
|
if cs.Error != nil || cs.Report == nil || cs.Report.Services == nil {
|
|
continue
|
|
}
|
|
e := svcEntry{
|
|
Host: cs.Node.Host,
|
|
Services: make(map[string]string),
|
|
}
|
|
for _, svc := range cs.Report.Services.Services {
|
|
e.Services[svc.Name] = svc.ActiveState
|
|
}
|
|
entries = append(entries, e)
|
|
}
|
|
|
|
return writeJSON(w, entries)
|
|
}
|
|
|
|
// shortIP truncates an IP to the first 3 octets for compact display.
|
|
func shortIP(ip string) string {
|
|
parts := strings.Split(ip, ".")
|
|
if len(parts) == 4 {
|
|
return parts[0] + "." + parts[1] + "." + parts[2]
|
|
}
|
|
if len(ip) > 12 {
|
|
return ip[:12]
|
|
}
|
|
return ip
|
|
}
|
|
|
|
// colorServiceState renders a service state with appropriate color.
|
|
func colorServiceState(state string) string {
|
|
switch state {
|
|
case "active":
|
|
return styleGreen.Render("ACTIVE")
|
|
case "failed":
|
|
return styleRed.Render("FAILED")
|
|
case "inactive":
|
|
return styleMuted.Render("inactive")
|
|
default:
|
|
return styleYellow.Render(state)
|
|
}
|
|
}
|