gomonop/cmd/matches/results.go
Emmanuel BENOîT c46c9d76d9
All checks were successful
Run tests and linters / test (push) Successful in 50s
Run tests and linters / build (push) Successful in 48s
Run tests and linters / lint (push) Successful in 1m27s
feat: add the check_output_matches plugin (#5)
This PR adds the `check_output_matches` plugin, which can be used to count regexp or substring matches from either text files or command outputs and determine the final status based on the amount of matches that were found.

Reviewed-on: #5
Co-authored-by: Emmanuel BENOÎT <tseeker@nocternity.net>
Co-committed-by: Emmanuel BENOÎT <tseeker@nocternity.net>
2024-07-20 22:57:10 +02:00

125 lines
3.4 KiB
Go

package matches // import nocternity.net/gomonop/cmd/matches
import (
"fmt"
"strconv"
"strings"
"nocternity.net/gomonop/pkg/perfdata"
"nocternity.net/gomonop/pkg/status"
)
// resultsInfo contains information about results being processed.
type resultsInfo struct {
nWarns, nCrits, nUnknowns uint
}
// updateResults updates the result counters based on the specified status.
func (info *resultsInfo) updateFrom(itemStatus status.Status) {
switch itemStatus {
case status.StatusCritical:
info.nCrits++
case status.StatusWarning:
info.nWarns++
case status.StatusUnknown:
info.nUnknowns++
case status.StatusOK:
// do nothing
}
}
// toProblems generate the list of problems found while running the plugin,
// as a string which can be returned to the monitoring server.
func (info *resultsInfo) toProblems() string {
problems := []string{}
if info.nCrits > 0 {
problems = append(problems,
fmt.Sprintf("%d value(s) critical", info.nCrits),
)
}
if info.nWarns > 0 {
problems = append(problems,
fmt.Sprintf("%d value(s) warning", info.nWarns),
)
}
if info.nUnknowns > 0 {
problems = append(problems,
fmt.Sprintf("%d value(s) unknown", info.nUnknowns),
)
}
if len(problems) == 0 {
problems = append(problems, "No match errors")
}
return strings.Join(problems, ", ")
}
// toStatus computes the final status of the plugin based on the status
// counters.
func (info *resultsInfo) toStatus() status.Status {
switch {
case info.nCrits > 0:
return status.StatusCritical
case info.nWarns > 0:
return status.StatusWarning
case info.nUnknowns > 0:
return status.StatusUnknown
default:
return status.StatusOK
}
}
// checkCounters checks the match counters against their configured thresholds,
// if any, and adds the corresponding perf data to the result. It initializes a
// resultsInfo structure containing the counts of critical, warning and unknown
// statuses gathered so far.
func (pluginInst *matchesPlugin) checkCounters() resultsInfo {
info := resultsInfo{}
for index := range pluginInst.counters {
config := &pluginInst.matches[index]
var nature string
if config.isRegexp {
nature = "regexp"
} else {
nature = "substring"
}
label := fmt.Sprintf("#%2d : %s %s", index+1, nature, config.matchString)
value := strconv.Itoa(pluginInst.counters[index])
pdat := perfdata.New(label, perfdata.UomNone, value)
pdat.SetWarn(config.warn)
pdat.SetCrit(config.crit)
pluginInst.results.AddPerfData(pdat)
info.updateFrom(pdat.GetStatus())
}
return info
}
// checkUnmatched checks the unmatched lines counters, applying a limit if
// strict mode is enabled. The corresponding performance data is added to the
// result.
func (pluginInst *matchesPlugin) checkUnmatched(info *resultsInfo) {
umlPdat := perfdata.New("unmatched", perfdata.UomNone, strconv.Itoa(pluginInst.unmatchedLines))
if pluginInst.strict {
umlPdat.SetCrit(perfdata.RangeMinMax("~", "0"))
}
info.updateFrom(umlPdat.GetStatus())
pluginInst.results.AddPerfData(umlPdat)
}
// checkResults checks the various counters against their configured thresholds,
// and the strict mode failure count.
func (pluginInst *matchesPlugin) checkResults(readError error) {
info := pluginInst.checkCounters()
pluginInst.checkUnmatched(&info)
if readError != nil {
pluginInst.results.AddLinef("Error while reading data: %s", readError.Error())
info.nUnknowns++
}
pluginInst.results.SetState(info.toStatus(), info.toProblems())
}