k9s/internal/render/popeye.go

190 lines
4.2 KiB
Go

package render
import (
"fmt"
"math"
"strconv"
"strings"
"github.com/derailed/popeye/pkg/config"
"github.com/derailed/tview"
"github.com/gdamore/tcell"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// Popeye renders a sanitizer to screen.
type Popeye struct{}
// ColorerFunc colors a resource row.
func (Popeye) ColorerFunc() ColorerFunc {
return func(ns string, h Header, re RowEvent) tcell.Color {
c := DefaultColorer(ns, h, re)
warnCol := h.IndexOf("WARNING", true)
status, _ := strconv.Atoi(strings.TrimSpace(re.Row.Fields[warnCol]))
if status > 0 {
c = tcell.ColorOrange
}
errCol := h.IndexOf("ERROR", true)
status, _ = strconv.Atoi(strings.TrimSpace(re.Row.Fields[errCol]))
if status > 0 {
c = ErrColor
}
return c
}
}
// Header returns a header row.
func (Popeye) Header(ns string) Header {
return Header{
HeaderColumn{Name: "RESOURCE"},
HeaderColumn{Name: "SCORE%", Align: tview.AlignRight},
HeaderColumn{Name: "SCANNED", Align: tview.AlignRight},
HeaderColumn{Name: "OK", Align: tview.AlignRight},
HeaderColumn{Name: "INFO", Align: tview.AlignRight},
HeaderColumn{Name: "WARNING", Align: tview.AlignRight},
HeaderColumn{Name: "ERROR", Align: tview.AlignRight},
}
}
// Render renders a K8s resource to screen.
func (Popeye) Render(o interface{}, ns string, r *Row) error {
s, ok := o.(Section)
if !ok {
return fmt.Errorf("expected Section, but got %T", o)
}
r.ID = s.Title
r.Fields = append(r.Fields,
s.Title,
strconv.Itoa(s.Tally.Score()),
strconv.Itoa(s.Tally.OK+s.Tally.Info+s.Tally.Warning+s.Tally.Error),
strconv.Itoa(s.Tally.OK),
strconv.Itoa(s.Tally.Info),
strconv.Itoa(s.Tally.Warning),
strconv.Itoa(s.Tally.Error),
)
return nil
}
// ----------------------------------------------------------------------------
// Helpers...
// BOZO!! export!
type (
// Builder represents sanitizer
Builder struct {
Report Report `json:"popeye" yaml:"popeye"`
}
// Report represents the output of a sanitization pass.
Report struct {
Score int `json:"score" yaml:"score"`
Grade string `json:"grade" yaml:"grade"`
Sections Sections `json:"sanitizers,omitempty" yaml:"sanitizers,omitempty"`
}
// Sections represents a collection of sections.
Sections []Section
// Section represents a sanitizer pass
Section struct {
Title string `json:"sanitizer" yaml:"sanitizer"`
GVR string `yaml:"gvr" json:"gvr"`
Tally *Tally `json:"tally" yaml:"tally"`
Outcome Outcome `json:"issues,omitempty" yaml:"issues,omitempty"`
}
Outcome map[string]Issues
Issues []Issue
Issue struct {
Group string `yaml:"group" json:"group"`
GVR string `yaml:"gvr" json:"gvr"`
Level config.Level `yaml:"level" json:"level"`
Message string `yaml:"message" json:"message"`
}
Tally struct {
OK, Info, Warning, Error int
Count int
}
)
func (t *Tally) Sum() int {
return t.OK + t.Info + t.Warning + t.Error
}
func (t *Tally) Score() int {
oks := t.OK + t.Info
return toPerc(float64(oks), float64(oks+t.Warning+t.Error))
}
func toPerc(v1, v2 float64) int {
if v2 == 0 {
return 0
}
return int(math.Floor((v1 / v2) * 100))
}
func (s Sections) Len() int {
return len(s)
}
func (s Sections) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s Sections) Less(i, j int) bool {
t1, t2 := s[i].Tally, s[j].Tally
return t1.Score() < t2.Score()
}
// GetObjectKind returns a schema object.
func (Section) GetObjectKind() schema.ObjectKind {
return nil
}
// DeepCopyObject returns a container copy.
func (s Section) DeepCopyObject() runtime.Object {
return s
}
// MaxSeverity gather the max severity in a collection of issues.
func (s Section) MaxSeverity() config.Level {
max := config.OkLevel
for _, issues := range s.Outcome {
m := issues.MaxSeverity()
if m > max {
max = m
}
}
return max
}
// MaxSeverity gather the max severity in a collection of issues.
func (i Issues) MaxSeverity() config.Level {
max := config.OkLevel
for _, is := range i {
if is.Level > max {
max = is.Level
}
}
return max
}
// CountSeverity counts severity level instances
func (i Issues) CountSeverity(l config.Level) int {
var count int
for _, is := range i {
if is.Level == l {
count++
}
}
return count
}