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 }