package infra import ( "encoding/json" "fmt" ) // unibusHealth refleja la respuesta JSON del endpoint /healthz de un nodo del // cluster de mensajería unibus (membershipd). Forma verificada en producción: // // {"posture":{"enforce":true,"acl":true,"tls":true,"cluster":true,"store":"kv"},"status":"ok"} type unibusHealth struct { Status string `json:"status"` Posture struct { Enforce bool `json:"enforce"` ACL bool `json:"acl"` TLS bool `json:"tls"` Cluster bool `json:"cluster"` Store string `json:"store"` } `json:"posture"` } // ParseUnibusHealth convierte la respuesta JSON del endpoint /healthz de un nodo // del cluster de mensajería unibus en una serie de PromSample lista para empujar // a VictoriaMetrics, sin instrumentar el bus (solo lee su endpoint de salud). // // node es el nombre lógico del nodo (p.ej. "magnus"); se adjunta a cada serie // como las labels "node" e "instance" para distinguir los nodos cuando un único // exporter scrapea varios. La función SOLO debe llamarse cuando el nodo // respondió: el caso "no responde" (unibus_up=0) lo emite el llamador, no esta // función, porque sin cuerpo no hay nada que parsear. // // Devuelve siete series por nodo: // - unibus_up = 1 (si el body parseó, el nodo respondió) // - unibus_status_ok = 1 si status=="ok", si no 0 // - unibus_posture_enforce / _acl / _tls / _cluster = 1/0 según el booleano // - unibus_store_kv = 1 si posture.store=="kv", si no 0 // // Si el body no es JSON válido con la forma esperada, devuelve (nil, error). func ParseUnibusHealth(node string, body []byte) ([]PromSample, error) { var h unibusHealth if err := json.Unmarshal(body, &h); err != nil { return nil, fmt.Errorf("parse unibus healthz for node %q: %w", node, err) } b2f := func(b bool) float64 { if b { return 1 } return 0 } mk := func(name string, v float64) PromSample { return PromSample{ Name: name, Labels: map[string]string{"node": node, "instance": node}, Value: v, } } return []PromSample{ mk("unibus_up", 1), mk("unibus_status_ok", b2f(h.Status == "ok")), mk("unibus_posture_enforce", b2f(h.Posture.Enforce)), mk("unibus_posture_acl", b2f(h.Posture.ACL)), mk("unibus_posture_tls", b2f(h.Posture.TLS)), mk("unibus_posture_cluster", b2f(h.Posture.Cluster)), mk("unibus_store_kv", b2f(h.Posture.Store == "kv")), }, nil }