dive-zfs/runtime/ci/evaluator.go
2019-07-20 17:57:47 -04:00

126 lines
2.7 KiB
Go

package ci
import (
"bytes"
"fmt"
"github.com/logrusorgru/aurora"
"github.com/spf13/viper"
"github.com/wagoodman/dive/image"
"io/ioutil"
"sort"
"strings"
)
func NewEvaluator() *Evaluator {
ciConfig := viper.New()
ciConfig.SetConfigType("yaml")
ciConfig.SetDefault("rules.lowestEfficiency", 0.9)
ciConfig.SetDefault("rules.highestWastedBytes", "disabled")
ciConfig.SetDefault("rules.highestUserWastedPercent", 0.1)
return &Evaluator{
Config: ciConfig,
Rules: loadCiRules(),
Results: make(map[string]RuleResult),
Pass: true,
}
}
func (ci *Evaluator) LoadConfig(configFile string) error {
fileBytes, err := ioutil.ReadFile(configFile)
if err != nil {
return err
}
err = ci.Config.ReadConfig(bytes.NewBuffer(fileBytes))
if err != nil {
return err
}
return nil
}
func (ci *Evaluator) isRuleEnabled(rule Rule) bool {
value := ci.Config.GetString(rule.Key())
return value != "disabled"
}
func (ci *Evaluator) Evaluate(analysis *image.AnalysisResult) bool {
for _, rule := range ci.Rules {
if ci.isRuleEnabled(rule) {
value := ci.Config.GetString(rule.Key())
status, message := rule.Evaluate(analysis, value)
if _, exists := ci.Results[rule.Key()]; exists {
panic(fmt.Errorf("CI rule result recorded twice: %s", rule.Key()))
}
if status == RuleFailed {
ci.Pass = false
}
ci.Results[rule.Key()] = RuleResult{
status: status,
message: message,
}
} else {
ci.Results[rule.Key()] = RuleResult{
status: RuleDisabled,
message: "rule disabled",
}
}
}
ci.Tally.Total = len(ci.Results)
for rule, result := range ci.Results {
switch result.status {
case RulePassed:
ci.Tally.Pass++
case RuleFailed:
ci.Tally.Fail++
case RuleWarning:
ci.Tally.Warn++
case RuleDisabled:
ci.Tally.Skip++
default:
panic(fmt.Errorf("unknown test status (rule='%v'): %v", rule, result.status))
}
}
return ci.Pass
}
func (ci *Evaluator) Report() {
status := "PASS"
rules := make([]string, 0, len(ci.Results))
for name := range ci.Results {
rules = append(rules, name)
}
sort.Strings(rules)
if ci.Tally.Fail > 0 {
status = "FAIL"
}
for _, rule := range rules {
result := ci.Results[rule]
name := strings.TrimPrefix(rule, "rules.")
if result.message != "" {
fmt.Printf(" %s: %s: %s\n", result.status.String(), name, result.message)
} else {
fmt.Printf(" %s: %s\n", result.status.String(), name)
}
}
summary := fmt.Sprintf("Result:%s [Total:%d] [Passed:%d] [Failed:%d] [Warn:%d] [Skipped:%d]", status, ci.Tally.Total, ci.Tally.Pass, ci.Tally.Fail, ci.Tally.Warn, ci.Tally.Skip)
if ci.Pass {
fmt.Println(aurora.Green(summary))
} else if ci.Pass && ci.Tally.Warn > 0 {
fmt.Println(aurora.Blue(summary))
} else {
fmt.Println(aurora.Red(summary))
}
}