158 lines
3.4 KiB
Go
158 lines
3.4 KiB
Go
// SPDX-License-Identifier: Apache-2.0
|
|
// Copyright Authors of K9s
|
|
|
|
package model
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/derailed/k9s/internal/client"
|
|
"github.com/derailed/k9s/internal/dao"
|
|
"github.com/derailed/k9s/internal/health"
|
|
"github.com/derailed/k9s/internal/model1"
|
|
"github.com/derailed/k9s/internal/render"
|
|
"github.com/rs/zerolog/log"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
)
|
|
|
|
// 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,
|
|
}
|
|
}
|
|
|
|
// List returns a canned collection of resources health.
|
|
func (h *PulseHealth) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
|
gvrs := []string{
|
|
"v1/pods",
|
|
"v1/events",
|
|
"apps/v1/replicasets",
|
|
"apps/v1/deployments",
|
|
"apps/v1/statefulsets",
|
|
"apps/v1/daemonsets",
|
|
"batch/v1/jobs",
|
|
"v1/persistentvolumes",
|
|
}
|
|
|
|
hh := make([]runtime.Object, 0, 10)
|
|
for _, gvr := range gvrs {
|
|
c, err := h.check(ctx, ns, gvr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
hh = append(hh, c)
|
|
}
|
|
|
|
mm, err := h.checkMetrics(ctx)
|
|
if err != nil {
|
|
return hh, err
|
|
}
|
|
for _, m := range mm {
|
|
hh = append(hh, m)
|
|
}
|
|
|
|
return hh, nil
|
|
}
|
|
|
|
func (h *PulseHealth) checkMetrics(ctx context.Context) (health.Checks, error) {
|
|
dial := client.DialMetrics(h.factory.Client())
|
|
|
|
nn, err := dao.FetchNodes(ctx, h.factory, "")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
nmx, err := dial.FetchNodesMetrics(ctx)
|
|
if err != nil {
|
|
log.Error().Err(err).Msgf("Fetching metrics")
|
|
return nil, err
|
|
}
|
|
|
|
mx := make(client.NodesMetrics, len(nn.Items))
|
|
dial.NodesMetrics(nn, nmx, mx)
|
|
|
|
var ccpu, cmem, acpu, amem, tcpu, tmem int64
|
|
for _, m := range mx {
|
|
ccpu += m.CurrentCPU
|
|
cmem += m.CurrentMEM
|
|
acpu += m.AllocatableCPU
|
|
amem += m.AllocatableMEM
|
|
tcpu += m.TotalCPU
|
|
tmem += m.TotalMEM
|
|
}
|
|
c1 := health.NewCheck("cpu")
|
|
c1.Set(health.S1, ccpu)
|
|
c1.Set(health.S2, acpu)
|
|
c1.Set(health.S3, tcpu)
|
|
c2 := health.NewCheck("mem")
|
|
c2.Set(health.S1, cmem)
|
|
c2.Set(health.S2, amem)
|
|
c2.Set(health.S3, tmem)
|
|
|
|
return health.Checks{c1, c2}, nil
|
|
}
|
|
|
|
func (h *PulseHealth) check(ctx context.Context, ns, gvr string) (*health.Check, 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, client.NewGVR(gvr))
|
|
oo, err := meta.DAO.List(ctx, ns)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
c := health.NewCheck(gvr)
|
|
|
|
if meta.Renderer.IsGeneric() {
|
|
table, ok := oo[0].(*metav1.Table)
|
|
if !ok {
|
|
return nil, fmt.Errorf("expecting a meta table but got %T", oo[0])
|
|
}
|
|
rows := make(model1.Rows, len(table.Rows))
|
|
re, _ := meta.Renderer.(model1.Generic)
|
|
re.SetTable(ns, table)
|
|
for i, row := range table.Rows {
|
|
if err := re.Render(row, ns, &rows[i]); err != nil {
|
|
return nil, err
|
|
}
|
|
if !model1.IsValid(ns, re.Header(ns), rows[i]) {
|
|
c.Inc(health.S2)
|
|
continue
|
|
}
|
|
c.Inc(health.S1)
|
|
}
|
|
c.Total(int64(len(table.Rows)))
|
|
return c, nil
|
|
}
|
|
c.Total(int64(len(oo)))
|
|
rr, re := make(model1.Rows, len(oo)), meta.Renderer
|
|
for i, o := range oo {
|
|
if err := re.Render(o, ns, &rr[i]); err != nil {
|
|
return nil, err
|
|
}
|
|
if !model1.IsValid(ns, re.Header(ns), rr[i]) {
|
|
c.Inc(health.S2)
|
|
continue
|
|
}
|
|
c.Inc(health.S1)
|
|
}
|
|
|
|
return c, nil
|
|
}
|