k9s/internal/model/pulse_health.go

135 lines
2.4 KiB
Go

package model
import (
"context"
"log/slog"
"time"
"github.com/derailed/k9s/internal"
"github.com/derailed/k9s/internal/client"
"github.com/derailed/k9s/internal/dao"
"github.com/derailed/k9s/internal/render"
"github.com/derailed/k9s/internal/slogs"
)
const pulseRate = 15 * time.Second
type HealthPoint struct {
GVR *client.GVR
Total, Faults int
}
type GVRs []*client.GVR
var PulseGVRs = client.GVRs{
client.NodeGVR,
client.NsGVR,
client.SvcGVR,
client.EvGVR,
client.PodGVR,
client.DpGVR,
client.StsGVR,
client.DsGVR,
client.JobGVR,
client.CjGVR,
client.PvGVR,
client.PvcGVR,
client.HpaGVR,
client.IngGVR,
client.NpGVR,
client.SaGVR,
}
func (g GVRs) First() *client.GVR {
return g[0]
}
func (g GVRs) Last() *client.GVR {
return g[len(g)-1]
}
func (g GVRs) Index(gvr *client.GVR) int {
for i := range g {
if g[i] == gvr {
return i
}
}
return -1
}
// PulseHealth tracks resources health.
type PulseHealth struct {
factory dao.Factory
}
// NewPulseHealth returns a new instance.
func NewPulseHealth(f dao.Factory) *PulseHealth {
return &PulseHealth{factory: f}
}
func (h *PulseHealth) Watch(ctx context.Context, ns string) HealthChan {
c := make(HealthChan, 2)
ctx = context.WithValue(ctx, internal.KeyWithMetrics, false)
go func(ctx context.Context, ns string, c HealthChan) {
if err := h.checkPulse(ctx, ns, c); err != nil {
slog.Error("Pulse check failed", slogs.Error, err)
}
for {
select {
case <-ctx.Done():
close(c)
return
case <-time.After(pulseRate):
if err := h.checkPulse(ctx, ns, c); err != nil {
slog.Error("Pulse check failed", slogs.Error, err)
}
}
}
}(ctx, ns, c)
return c
}
func (h *PulseHealth) checkPulse(ctx context.Context, ns string, c HealthChan) error {
for _, gvr := range PulseGVRs {
check, err := h.check(ctx, ns, gvr)
if err != nil {
return err
}
c <- check
}
return nil
}
func (h *PulseHealth) check(ctx context.Context, ns string, gvr *client.GVR) (HealthPoint, error) {
meta, ok := Registry[gvr]
if !ok {
meta = ResourceMeta{
DAO: &dao.Table{},
Renderer: &render.Generic{},
}
}
if meta.DAO == nil {
meta.DAO = &dao.Resource{}
}
meta.DAO.Init(h.factory, gvr)
oo, err := meta.DAO.List(ctx, ns)
if err != nil {
return HealthPoint{}, err
}
c := HealthPoint{GVR: gvr, Total: len(oo)}
for _, o := range oo {
if err := meta.Renderer.Healthy(ctx, o); err != nil {
c.Faults++
}
}
return c, nil
}