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>
This commit is contained in:
parent
9fac656cdf
commit
c46c9d76d9
22 changed files with 1063 additions and 55 deletions
cmd
95
cmd/matches/cmdline.go
Normal file
95
cmd/matches/cmdline.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
package matches // import nocternity.net/gomonop/cmd/matches
|
||||
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/karrick/golf"
|
||||
|
||||
"nocternity.net/gomonop/pkg/perfdata"
|
||||
)
|
||||
|
||||
// pluginFlags represent command line flags that have been parsed.
|
||||
type pluginFlags struct {
|
||||
isFile bool // Are we reading from a file?
|
||||
dataSource string // The file or command to read from
|
||||
timeout time.Duration // A timeout for the command, or 0 to disable
|
||||
matches []matchConfig // Configuration for the matches to check
|
||||
strict bool // Reject lines that don't match anything
|
||||
}
|
||||
|
||||
// matchConfig is the configuration for a single match to check.
|
||||
type matchConfig struct {
|
||||
isRegexp bool // Are we checking against a regular expression?
|
||||
matchString string // The string or regexp to match
|
||||
compiledRe *regexp.Regexp // The compiled regexp
|
||||
warn *perfdata.Range // Warning range
|
||||
crit *perfdata.Range // Critical range
|
||||
}
|
||||
|
||||
// parseArguments parses command line arguments for the plugin.
|
||||
func (p *pluginFlags) parseArguments() {
|
||||
golf.BoolVarP(&p.isFile, 'f', "is-file", false, "Are we reading from a file?")
|
||||
golf.StringVarP(&p.dataSource, 's', "source", "", "The file or command to read from")
|
||||
golf.DurationVarP(&p.timeout, 'T', "timeout", 0, "A timeout for the command, or 0 to disable")
|
||||
golf.BoolVarP(&p.strict, 'S', "strict", false, "Reject lines that do not match anything")
|
||||
|
||||
isRegexp := golf.BoolP('R', "no-regexp", true, "Following match argument will be a basic string")
|
||||
golf.BoolVarP(isRegexp, 'r', "regexp", false, "Following match argument will be a regexp")
|
||||
|
||||
var wRange *perfdata.Range
|
||||
golf.StringFuncP('w', "warn", "", "Warning range, in Nagios-compatible format", func(s string) error {
|
||||
locRange, err := perfdata.ParseRange(s)
|
||||
if err == nil {
|
||||
wRange = locRange
|
||||
}
|
||||
return err
|
||||
})
|
||||
golf.BoolFuncP('W', "no-warn", false, "Clear warning range", func(bool) error {
|
||||
wRange = nil
|
||||
return nil
|
||||
})
|
||||
|
||||
var cRange *perfdata.Range
|
||||
golf.StringFuncP('c', "critical", "", "Critical range, in Nagios-compatible format", func(s string) error {
|
||||
locRange, err := perfdata.ParseRange(s)
|
||||
if err == nil {
|
||||
cRange = locRange
|
||||
}
|
||||
return err
|
||||
})
|
||||
golf.BoolFuncP('C', "no-critical", false, "Clear warning range", func(bool) error {
|
||||
cRange = nil
|
||||
return nil
|
||||
})
|
||||
|
||||
golf.StringFuncP('m', "match", "", "Match string", func(s string) error {
|
||||
p.matches = append(p.matches, matchConfig{
|
||||
isRegexp: *isRegexp,
|
||||
matchString: s,
|
||||
warn: wRange,
|
||||
crit: cRange,
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
golf.Parse()
|
||||
}
|
||||
|
||||
// makeContext generates a context based on the timeout, if one is set.
|
||||
func (p *pluginFlags) makeContext() (context.Context, context.CancelFunc) {
|
||||
if p.timeout == 0 {
|
||||
return context.Background(), func() {}
|
||||
}
|
||||
return context.WithTimeout(context.Background(), p.timeout)
|
||||
}
|
||||
|
||||
// matches check if the specified string matches a configuration.
|
||||
func (p *matchConfig) matches(s string) bool {
|
||||
if p.isRegexp {
|
||||
return p.compiledRe.MatchString(s)
|
||||
}
|
||||
return strings.Contains(s, p.matchString)
|
||||
}
|
118
cmd/matches/extprogram.go
Normal file
118
cmd/matches/extprogram.go
Normal file
|
@ -0,0 +1,118 @@
|
|||
package matches // import nocternity.net/gomonop/pkg/matches
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"io"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// streamObtainer refers to functions that may return either an input stream or
|
||||
// an error.
|
||||
type streamObtainer func() (io.ReadCloser, error)
|
||||
|
||||
// pipeCopy encapsulates the structures that are used to transfer data from a
|
||||
// single stream to the reader.
|
||||
type pipeCopy struct {
|
||||
reader io.ReadCloser // The input stream being read from
|
||||
dataPipe chan string // Channel that receives the data we read
|
||||
aborter chan struct{} // Channel that causes the copy to abort
|
||||
readResult chan error // Channel that returns results from reading
|
||||
}
|
||||
|
||||
// startPipeCopy starts a goroutine that copies data from the reader to the
|
||||
// dataPipe.
|
||||
func startPipeCopy(obtainer streamObtainer, pipes *readerPipes) *pipeCopy {
|
||||
stream, err := obtainer()
|
||||
if err != nil {
|
||||
pipes.done <- err
|
||||
return nil
|
||||
}
|
||||
|
||||
pipeCopyData := &pipeCopy{
|
||||
reader: stream,
|
||||
dataPipe: pipes.data,
|
||||
aborter: make(chan struct{}),
|
||||
readResult: make(chan error),
|
||||
}
|
||||
|
||||
go pipeCopyData.run()
|
||||
|
||||
return pipeCopyData
|
||||
}
|
||||
|
||||
// run runs the copy operation from the input stream to the data pipe. It is
|
||||
// meant to be executed as a goroutine.
|
||||
func (pc *pipeCopy) run() {
|
||||
scanner := bufio.NewScanner(pc.reader)
|
||||
for scanner.Scan() {
|
||||
select {
|
||||
case <-pc.aborter:
|
||||
return
|
||||
case pc.dataPipe <- scanner.Text():
|
||||
}
|
||||
}
|
||||
pc.readResult <- scanner.Err()
|
||||
}
|
||||
|
||||
// close closes the stream and channels used by a copy pipe.
|
||||
func (pc *pipeCopy) close() {
|
||||
pc.abort()
|
||||
_ = pc.reader.Close()
|
||||
close(pc.aborter)
|
||||
close(pc.readResult)
|
||||
}
|
||||
|
||||
// abort causes the pipe copy to be aborted.
|
||||
func (pc *pipeCopy) abort() {
|
||||
pc.aborter <- struct{}{}
|
||||
}
|
||||
|
||||
// readFromProgram starts a goroutine that controls the program, sending lines
|
||||
// from both stderr and stdout to the dataPipe.
|
||||
func (pluginInst *matchesPlugin) readFromProgram(ctx context.Context, pipes *readerPipes) {
|
||||
go func() {
|
||||
args := strings.Split(pluginInst.dataSource, " ")
|
||||
cmd := exec.Command(args[0], args[1:]...) //nolint:gosec // Command is in fact user-provided
|
||||
|
||||
outs := startPipeCopy(func() (io.ReadCloser, error) { return cmd.StdoutPipe() }, pipes)
|
||||
defer outs.close()
|
||||
errs := startPipeCopy(func() (io.ReadCloser, error) { return cmd.StderrPipe() }, pipes)
|
||||
defer errs.close()
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
pipes.done <- err
|
||||
return
|
||||
}
|
||||
|
||||
abort := func(err error) {
|
||||
_ = cmd.Process.Kill()
|
||||
pipes.done <- err
|
||||
}
|
||||
|
||||
errComplete := false
|
||||
outComplete := false
|
||||
for !(errComplete && outComplete) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
abort(ctx.Err())
|
||||
return
|
||||
case err := <-outs.readResult:
|
||||
if err != nil {
|
||||
abort(err)
|
||||
return
|
||||
}
|
||||
outComplete = true
|
||||
case err := <-errs.readResult:
|
||||
if err != nil {
|
||||
abort(err)
|
||||
return
|
||||
}
|
||||
errComplete = true
|
||||
}
|
||||
}
|
||||
|
||||
pipes.done <- cmd.Wait()
|
||||
}()
|
||||
}
|
76
cmd/matches/plugin.go
Normal file
76
cmd/matches/plugin.go
Normal file
|
@ -0,0 +1,76 @@
|
|||
// The matches package contains the implementation of the `check_output_matches`
|
||||
// plugin, which can be used to run an arbitrary command and check its output
|
||||
// against various patterns. It can also be configured to do the same on an
|
||||
// arbitrary file.
|
||||
package matches // import nocternity.net/gomonop/cmd/matches
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"nocternity.net/gomonop/pkg/plugin"
|
||||
"nocternity.net/gomonop/pkg/results"
|
||||
"nocternity.net/gomonop/pkg/status"
|
||||
)
|
||||
|
||||
// Program data for the string matches plugin.
|
||||
type matchesPlugin struct {
|
||||
pluginFlags // Flags from the command line
|
||||
results *results.Results // Plugin output state
|
||||
counters []int // Counters for each match
|
||||
unmatchedLines int // Number of lines that didn't match anything
|
||||
}
|
||||
|
||||
// Initialise the plugin.
|
||||
func NewPlugin() plugin.Plugin {
|
||||
pluginInst := &matchesPlugin{
|
||||
results: results.New("String matches counter"),
|
||||
}
|
||||
pluginInst.parseArguments()
|
||||
return pluginInst
|
||||
}
|
||||
|
||||
// Return the program's output value.
|
||||
func (pluginInst *matchesPlugin) Results() *results.Results {
|
||||
return pluginInst.results
|
||||
}
|
||||
|
||||
// Check the values that were specified from the command line. Returns true
|
||||
// if the arguments made sense.
|
||||
func (pluginInst *matchesPlugin) CheckArguments() bool {
|
||||
if pluginInst.dataSource == "" {
|
||||
pluginInst.results.SetState(status.StatusUnknown, "no data source specified")
|
||||
return false
|
||||
}
|
||||
|
||||
if !pluginInst.strict && len(pluginInst.matches) == 0 {
|
||||
pluginInst.results.SetState(status.StatusUnknown, "would match anything")
|
||||
return false
|
||||
}
|
||||
|
||||
for index := range pluginInst.matches {
|
||||
if pluginInst.matches[index].matchString == "" {
|
||||
pluginInst.results.SetState(status.StatusUnknown, "empty match string")
|
||||
pluginInst.results.AddLinef("(At match %d)", index+1)
|
||||
return false
|
||||
}
|
||||
if pluginInst.matches[index].isRegexp {
|
||||
rexp, err := regexp.Compile(pluginInst.matches[index].matchString)
|
||||
if err != nil {
|
||||
pluginInst.results.SetState(status.StatusUnknown, "Invalid regular expression")
|
||||
pluginInst.results.AddLine(err.Error())
|
||||
pluginInst.results.AddLinef("(At match %d)", index+1)
|
||||
return false
|
||||
}
|
||||
pluginInst.matches[index].compiledRe = rexp
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Run the check.
|
||||
func (pluginInst *matchesPlugin) RunCheck() {
|
||||
pluginInst.counters = make([]int, len(pluginInst.matches))
|
||||
err := pluginInst.processData()
|
||||
pluginInst.checkResults(err)
|
||||
}
|
95
cmd/matches/reader.go
Normal file
95
cmd/matches/reader.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
package matches // import nocternity.net/gomonop/pkg/matches
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"os"
|
||||
)
|
||||
|
||||
// readerPipes encapsulates the two channels used to communicate data and state
|
||||
// from the functions that acquire the data.
|
||||
type readerPipes struct {
|
||||
done chan error
|
||||
data chan string
|
||||
}
|
||||
|
||||
// makeReaderPipes initializes the channels.
|
||||
func makeReaderPipes() *readerPipes {
|
||||
return &readerPipes{
|
||||
done: make(chan error),
|
||||
data: make(chan string),
|
||||
}
|
||||
}
|
||||
|
||||
// close closes the channels.
|
||||
func (rdp *readerPipes) close() {
|
||||
close(rdp.done)
|
||||
close(rdp.data)
|
||||
}
|
||||
|
||||
// readFromFile starts a goroutine that reads from the file and sends each line
|
||||
// to the dataPipe.
|
||||
func (pluginInst *matchesPlugin) readFromFile(ctx context.Context, pipes *readerPipes) {
|
||||
go func() {
|
||||
file, err := os.Open(pluginInst.dataSource)
|
||||
if err != nil {
|
||||
pipes.done <- err
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
pipes.done <- ctx.Err()
|
||||
return
|
||||
case pipes.data <- scanner.Text():
|
||||
}
|
||||
}
|
||||
pipes.done <- scanner.Err()
|
||||
}()
|
||||
}
|
||||
|
||||
// startReading starts reading data from either a file or a program.
|
||||
func (pluginInst *matchesPlugin) startReading(ctx context.Context, pipes *readerPipes) {
|
||||
if pluginInst.isFile {
|
||||
pluginInst.readFromFile(ctx, pipes)
|
||||
} else {
|
||||
pluginInst.readFromProgram(ctx, pipes)
|
||||
}
|
||||
}
|
||||
|
||||
// processLine processes a single line obtained from the program or file.
|
||||
func (pluginInst *matchesPlugin) processLine(line string) {
|
||||
hadMatch := false
|
||||
for index := range pluginInst.matches {
|
||||
if pluginInst.matches[index].matches(line) {
|
||||
pluginInst.counters[index]++
|
||||
hadMatch = true
|
||||
}
|
||||
}
|
||||
if !hadMatch {
|
||||
pluginInst.unmatchedLines++
|
||||
}
|
||||
}
|
||||
|
||||
// processData processes data from either the program or the file.
|
||||
func (pluginInst *matchesPlugin) processData() error {
|
||||
ctx, cancel := pluginInst.makeContext()
|
||||
defer cancel()
|
||||
|
||||
pipes := makeReaderPipes()
|
||||
defer pipes.close()
|
||||
|
||||
pluginInst.startReading(ctx, pipes)
|
||||
|
||||
for {
|
||||
select {
|
||||
case line := <-pipes.data:
|
||||
pluginInst.processLine(line)
|
||||
case err := <-pipes.done:
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
125
cmd/matches/results.go
Normal file
125
cmd/matches/results.go
Normal file
|
@ -0,0 +1,125 @@
|
|||
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())
|
||||
}
|
|
@ -17,6 +17,7 @@ import (
|
|||
"nocternity.net/gomonop/pkg/perfdata"
|
||||
"nocternity.net/gomonop/pkg/plugin"
|
||||
"nocternity.net/gomonop/pkg/results"
|
||||
"nocternity.net/gomonop/pkg/status"
|
||||
)
|
||||
|
||||
//--------------------------------------------------------------------------------------------------------
|
||||
|
@ -224,20 +225,20 @@ func (program *checkProgram) Results() *results.Results {
|
|||
// if the arguments made sense.
|
||||
func (program *checkProgram) CheckArguments() bool {
|
||||
if program.hostname == "" {
|
||||
program.plugin.SetState(results.StatusUnknown, "no hostname specified")
|
||||
program.plugin.SetState(status.StatusUnknown, "no hostname specified")
|
||||
return false
|
||||
}
|
||||
if program.port < 1 || program.port > 65535 {
|
||||
program.plugin.SetState(results.StatusUnknown, "invalid or missing port number")
|
||||
program.plugin.SetState(status.StatusUnknown, "invalid or missing port number")
|
||||
return false
|
||||
}
|
||||
if program.warn != -1 && program.crit != -1 && program.warn <= program.crit {
|
||||
program.plugin.SetState(results.StatusUnknown, "nonsensical thresholds")
|
||||
program.plugin.SetState(status.StatusUnknown, "nonsensical thresholds")
|
||||
return false
|
||||
}
|
||||
if _, ok := certGetters[program.startTLS]; !ok {
|
||||
errstr := "unsupported StartTLS protocol " + program.startTLS
|
||||
program.plugin.SetState(results.StatusUnknown, errstr)
|
||||
program.plugin.SetState(status.StatusUnknown, errstr)
|
||||
return false
|
||||
}
|
||||
program.hostname = strings.ToLower(program.hostname)
|
||||
|
@ -262,13 +263,13 @@ func (program *checkProgram) getCertificate() error {
|
|||
// matches the requested host name.
|
||||
func (program *checkProgram) checkSANlessCertificate() bool {
|
||||
if !program.ignoreCnOnly || len(program.extraNames) != 0 {
|
||||
program.plugin.SetState(results.StatusWarning,
|
||||
program.plugin.SetState(status.StatusWarning,
|
||||
"certificate doesn't have SAN domain names")
|
||||
return false
|
||||
}
|
||||
dn := strings.ToLower(program.certificate.Subject.String())
|
||||
if !strings.HasPrefix(dn, fmt.Sprintf("cn=%s,", program.hostname)) {
|
||||
program.plugin.SetState(results.StatusCritical, "incorrect certificate CN")
|
||||
program.plugin.SetState(status.StatusCritical, "incorrect certificate CN")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
@ -298,7 +299,7 @@ func (program *checkProgram) checkNames() bool {
|
|||
certificateIsOk = program.checkHostName(name) && certificateIsOk
|
||||
}
|
||||
if !certificateIsOk {
|
||||
program.plugin.SetState(results.StatusCritical, "names missing from SAN domain names")
|
||||
program.plugin.SetState(status.StatusCritical, "names missing from SAN domain names")
|
||||
}
|
||||
return certificateIsOk
|
||||
}
|
||||
|
@ -306,26 +307,26 @@ func (program *checkProgram) checkNames() bool {
|
|||
// Check a certificate's time to expiry against the warning and critical
|
||||
// thresholds, returning a status code and description based on these
|
||||
// values.
|
||||
func (program *checkProgram) checkCertificateExpiry(tlDays int) (results.Status, string) {
|
||||
func (program *checkProgram) checkCertificateExpiry(tlDays int) (status.Status, string) {
|
||||
if tlDays <= 0 {
|
||||
return results.StatusCritical, "certificate expired"
|
||||
return status.StatusCritical, "certificate expired"
|
||||
}
|
||||
|
||||
var limitStr string
|
||||
var state results.Status
|
||||
var state status.Status
|
||||
|
||||
switch {
|
||||
case program.crit > 0 && tlDays <= program.crit:
|
||||
limitStr = fmt.Sprintf(" (<= %d)", program.crit)
|
||||
state = results.StatusCritical
|
||||
state = status.StatusCritical
|
||||
|
||||
case program.warn > 0 && tlDays <= program.warn:
|
||||
limitStr = fmt.Sprintf(" (<= %d)", program.warn)
|
||||
state = results.StatusWarning
|
||||
state = status.StatusWarning
|
||||
|
||||
default:
|
||||
limitStr = ""
|
||||
state = results.StatusOK
|
||||
state = status.StatusOK
|
||||
}
|
||||
|
||||
statusString := fmt.Sprintf("certificate will expire in %d days%s",
|
||||
|
@ -351,7 +352,7 @@ func (program *checkProgram) setPerfData(tlDays int) {
|
|||
func (program *checkProgram) RunCheck() {
|
||||
err := program.getCertificate()
|
||||
if err != nil {
|
||||
program.plugin.SetState(results.StatusUnknown, err.Error())
|
||||
program.plugin.SetState(status.StatusUnknown, err.Error())
|
||||
} else if program.checkNames() {
|
||||
timeLeft := time.Until(program.certificate.NotAfter)
|
||||
tlDays := int((timeLeft + 86399*time.Second) / (24 * time.Hour))
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"nocternity.net/gomonop/pkg/perfdata"
|
||||
"nocternity.net/gomonop/pkg/plugin"
|
||||
"nocternity.net/gomonop/pkg/results"
|
||||
"nocternity.net/gomonop/pkg/status"
|
||||
)
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------
|
||||
|
@ -93,23 +94,23 @@ func (program *checkProgram) Results() *results.Results {
|
|||
// Check the values that were specified from the command line. Returns true if the arguments made sense.
|
||||
func (program *checkProgram) CheckArguments() bool {
|
||||
if program.hostname == "" {
|
||||
program.plugin.SetState(results.StatusUnknown, "no DNS hostname specified")
|
||||
program.plugin.SetState(status.StatusUnknown, "no DNS hostname specified")
|
||||
return false
|
||||
}
|
||||
if program.port < 1 || program.port > 65535 {
|
||||
program.plugin.SetState(results.StatusUnknown, "invalid DNS port number")
|
||||
program.plugin.SetState(status.StatusUnknown, "invalid DNS port number")
|
||||
return false
|
||||
}
|
||||
if program.zone == "" {
|
||||
program.plugin.SetState(results.StatusUnknown, "no DNS zone specified")
|
||||
program.plugin.SetState(status.StatusUnknown, "no DNS zone specified")
|
||||
return false
|
||||
}
|
||||
if program.rsHostname == "" {
|
||||
program.plugin.SetState(results.StatusUnknown, "no reference DNS hostname specified")
|
||||
program.plugin.SetState(status.StatusUnknown, "no reference DNS hostname specified")
|
||||
return false
|
||||
}
|
||||
if program.rsPort < 1 || program.rsPort > 65535 {
|
||||
program.plugin.SetState(results.StatusUnknown, "invalid reference DNS port number")
|
||||
program.plugin.SetState(status.StatusUnknown, "invalid reference DNS port number")
|
||||
return false
|
||||
}
|
||||
program.hostname = strings.ToLower(program.hostname)
|
||||
|
@ -176,12 +177,12 @@ func (program *checkProgram) RunCheck() {
|
|||
cOk, cSerial := program.getSerial("checked", checkResponse)
|
||||
rOk, rSerial := program.getSerial("reference", refResponse)
|
||||
if !(cOk && rOk) {
|
||||
program.plugin.SetState(results.StatusUnknown, "could not read serials")
|
||||
program.plugin.SetState(status.StatusUnknown, "could not read serials")
|
||||
return
|
||||
}
|
||||
if cSerial == rSerial {
|
||||
program.plugin.SetState(results.StatusOK, "serials match")
|
||||
program.plugin.SetState(status.StatusOK, "serials match")
|
||||
} else {
|
||||
program.plugin.SetState(results.StatusCritical, "serials mismatch")
|
||||
program.plugin.SetState(status.StatusCritical, "serials mismatch")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue