refactor: reorganize project in order to include automation (#1)
Reviewed-on: #1 Co-authored-by: Emmanuel BENOÎT <tseeker@nocternity.net> Co-committed-by: Emmanuel BENOÎT <tseeker@nocternity.net>
This commit is contained in:
parent
8feb34bbe6
commit
2fa0e37900
21 changed files with 2496 additions and 195 deletions
pkg/perfdata
199
pkg/perfdata/perfdata.go
Normal file
199
pkg/perfdata/perfdata.go
Normal file
|
@ -0,0 +1,199 @@
|
|||
// Package `perfdata` provides representations for a monitoring plugin's
|
||||
// performance data.
|
||||
package perfdata // import nocternity.net/gomonop/pkg/perfdata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Units of measurement, which may be used to qualify the performance data.
|
||||
type UnitOfMeasurement int
|
||||
|
||||
const (
|
||||
UomNone UnitOfMeasurement = iota
|
||||
UomSeconds
|
||||
UomPercent
|
||||
UomBytes
|
||||
UomKilobytes
|
||||
UomMegabytes
|
||||
UomGigabytes
|
||||
UomTerabytes
|
||||
UomCounter
|
||||
)
|
||||
|
||||
func (u UnitOfMeasurement) String() string {
|
||||
return [...]string{"", "s", "%", "B", "KB", "MB", "GB", "TB", "c"}[u]
|
||||
}
|
||||
|
||||
// Flags indicating which elements of performance data have been set.
|
||||
type perfDataBits int
|
||||
|
||||
const (
|
||||
PDatWarn perfDataBits = 1 << iota
|
||||
PDatCrit
|
||||
PDatMin
|
||||
PDatMax
|
||||
)
|
||||
|
||||
// Regexps used to check values and ranges in performance data records.
|
||||
var (
|
||||
// Common value check regexp.
|
||||
vcRegexp = `^-?(0(\.\d*)?|[1-9]\d*(\.\d*)?|\.\d+)$`
|
||||
// Compiled value check regexp.
|
||||
valueCheck = regexp.MustCompile(vcRegexp)
|
||||
// Compiled range min value check.
|
||||
rangeMinCheck = regexp.MustCompile(vcRegexp + `|^~$`)
|
||||
)
|
||||
|
||||
// Performance data range.
|
||||
type PDRange struct {
|
||||
start string
|
||||
end string
|
||||
inside bool
|
||||
}
|
||||
|
||||
// Creates a performance data range from -inf to 0 and from the specified
|
||||
// value to +inf.
|
||||
func PDRMax(max string) *PDRange {
|
||||
if !valueCheck.MatchString(max) {
|
||||
panic("invalid performance data range maximum value")
|
||||
}
|
||||
pdRange := &PDRange{}
|
||||
pdRange.start = "0"
|
||||
pdRange.end = max
|
||||
return pdRange
|
||||
}
|
||||
|
||||
// Creates a performance data range from -inf to the specified minimal value
|
||||
// and from the specified maximal value to +inf.
|
||||
func PDRMinMax(min, max string) *PDRange {
|
||||
if !valueCheck.MatchString(max) {
|
||||
panic("invalid performance data range maximum value")
|
||||
}
|
||||
if !rangeMinCheck.MatchString(min) {
|
||||
panic("invalid performance data range minimum value")
|
||||
}
|
||||
pdRange := &PDRange{}
|
||||
pdRange.start = min
|
||||
pdRange.end = max
|
||||
return pdRange
|
||||
}
|
||||
|
||||
// Inverts the range.
|
||||
func (r *PDRange) Inside() *PDRange {
|
||||
r.inside = true
|
||||
return r
|
||||
}
|
||||
|
||||
// Generates the range's string representation so it can be sent to the
|
||||
// monitoring system.
|
||||
func (r *PDRange) String() string {
|
||||
var start, inside string
|
||||
|
||||
switch r.start {
|
||||
case "":
|
||||
start = "~"
|
||||
case "0":
|
||||
start = ""
|
||||
default:
|
||||
start = r.start
|
||||
}
|
||||
|
||||
if r.inside {
|
||||
inside = "@"
|
||||
}
|
||||
|
||||
return inside + start + ":" + r.end
|
||||
}
|
||||
|
||||
// Performance data, including a label, units, a value, warning/critical
|
||||
// ranges and min/max boundaries.
|
||||
type PerfData struct {
|
||||
Label string
|
||||
units UnitOfMeasurement
|
||||
bits perfDataBits
|
||||
value string
|
||||
warn, crit PDRange
|
||||
min, max string
|
||||
}
|
||||
|
||||
// Create performance data using the specified label and units.
|
||||
func New(label string, units UnitOfMeasurement, value string) *PerfData {
|
||||
if value != "" && !valueCheck.MatchString(value) {
|
||||
panic("invalid value")
|
||||
}
|
||||
pdRange := &PerfData{}
|
||||
pdRange.Label = label
|
||||
pdRange.units = units
|
||||
if value == "" {
|
||||
pdRange.value = "U"
|
||||
} else {
|
||||
pdRange.value = value
|
||||
}
|
||||
return pdRange
|
||||
}
|
||||
|
||||
// Set the warning range for the performance data record.
|
||||
func (d *PerfData) SetWarn(r *PDRange) {
|
||||
d.warn = *r
|
||||
d.bits |= PDatWarn
|
||||
}
|
||||
|
||||
// Set the critical range for the performance data record.
|
||||
func (d *PerfData) SetCrit(r *PDRange) {
|
||||
d.crit = *r
|
||||
d.bits |= PDatCrit
|
||||
}
|
||||
|
||||
// Set the performance data's minimal value.
|
||||
func (d *PerfData) SetMin(min string) {
|
||||
if !valueCheck.MatchString(min) {
|
||||
panic("invalid value")
|
||||
}
|
||||
d.min = min
|
||||
d.bits |= PDatMin
|
||||
}
|
||||
|
||||
// Set the performance data's maximal value.
|
||||
func (d *PerfData) SetMax(max string) {
|
||||
if !valueCheck.MatchString(max) {
|
||||
panic("invalid value")
|
||||
}
|
||||
d.max = max
|
||||
d.bits |= PDatMax
|
||||
}
|
||||
|
||||
// Converts performance data to a string which may be read by the monitoring
|
||||
// system.
|
||||
func (d *PerfData) String() string {
|
||||
var strBuilder strings.Builder
|
||||
needsQuotes := strings.ContainsAny(d.Label, " '=\"")
|
||||
if needsQuotes {
|
||||
strBuilder.WriteString("'")
|
||||
}
|
||||
strBuilder.WriteString(strings.ReplaceAll(d.Label, "'", "''"))
|
||||
if needsQuotes {
|
||||
strBuilder.WriteString("'")
|
||||
}
|
||||
strBuilder.WriteString("=")
|
||||
strBuilder.WriteString(fmt.Sprintf("%s%s;", d.value, d.units.String()))
|
||||
if d.bits&PDatWarn != 0 {
|
||||
strBuilder.WriteString(d.warn.String())
|
||||
}
|
||||
strBuilder.WriteString(";")
|
||||
if d.bits&PDatCrit != 0 {
|
||||
strBuilder.WriteString(d.crit.String())
|
||||
}
|
||||
strBuilder.WriteString(";")
|
||||
if d.bits&PDatMin != 0 {
|
||||
strBuilder.WriteString(d.min)
|
||||
}
|
||||
strBuilder.WriteString(";")
|
||||
if d.bits&PDatMax != 0 {
|
||||
strBuilder.WriteString(d.max)
|
||||
}
|
||||
|
||||
return strBuilder.String()
|
||||
}
|
108
pkg/perfdata/perfdata_test.go
Normal file
108
pkg/perfdata/perfdata_test.go
Normal file
|
@ -0,0 +1,108 @@
|
|||
package perfdata // import nocternity.net/gomonop/pkg/perfdata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func assert(t *testing.T, check bool, msg string) {
|
||||
if !check {
|
||||
t.Errorf(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func assertPanic(t *testing.T, f func(), msg string) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Errorf(msg)
|
||||
}
|
||||
}()
|
||||
f()
|
||||
}
|
||||
|
||||
func TestValueCheckValid(t *testing.T) {
|
||||
validValues := []string{
|
||||
"0", "0.", "0.952", "1", "123", "123.", "123.45", ".1",
|
||||
"-0", "-0.", "-0.952", "-1", "-123", "-123.", "-123.45", "-.1",
|
||||
}
|
||||
|
||||
for _, value := range validValues {
|
||||
if !valueCheck.MatchString(value) {
|
||||
t.Errorf("'%s' is a valid value string", value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueCheckInvalid(t *testing.T) {
|
||||
invalidValues := []string{".", "-.", "a", " ", ""}
|
||||
|
||||
for _, value := range invalidValues {
|
||||
if valueCheck.MatchString(value) {
|
||||
t.Errorf("'%s' is an invalid value string", value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPdrMaxInvalid(t *testing.T) {
|
||||
assertPanic(
|
||||
t, func() { PDRMax("") },
|
||||
"Created PerfDataRange with invalid max value",
|
||||
)
|
||||
}
|
||||
|
||||
func TestPdrMax(t *testing.T) {
|
||||
value := "123"
|
||||
pdr := PDRMax(value)
|
||||
assert(t, pdr.start == "0", "Min value should be '0'")
|
||||
assert(t, pdr.end == value, "Max value not copied to PerfDataRange")
|
||||
assert(t, !pdr.inside, "Inside flag should not be set")
|
||||
}
|
||||
|
||||
func TestPdrMinMaxInvalid(t *testing.T) {
|
||||
assertPanic(
|
||||
t, func() { PDRMinMax("", "123") },
|
||||
"Created PerfDataRange with invalid min value",
|
||||
)
|
||||
assertPanic(
|
||||
t, func() { PDRMinMax("123", "") },
|
||||
"Created PerfDataRange with invalid max value",
|
||||
)
|
||||
}
|
||||
|
||||
func TestPdrMinMax(t *testing.T) {
|
||||
min, max := "123", "456"
|
||||
pdr := PDRMinMax(min, max)
|
||||
assert(t, pdr.start == min, "Min value not copied to PerfDataRange")
|
||||
assert(t, pdr.end == max, "Max value not copied to PerfDataRange")
|
||||
assert(t, !pdr.inside, "Inside flag should not be set")
|
||||
}
|
||||
|
||||
func TestPdrInside(t *testing.T) {
|
||||
pdr := &PDRange{}
|
||||
pdr = pdr.Inside()
|
||||
assert(t, pdr.inside, "Inside flag should be set")
|
||||
pdr = pdr.Inside()
|
||||
assert(t, pdr.inside, "Inside flag should still be set")
|
||||
}
|
||||
|
||||
func TestPdrString(t *testing.T) {
|
||||
type Test struct {
|
||||
pdr PDRange
|
||||
out string
|
||||
}
|
||||
tests := []Test{
|
||||
{pdr: PDRange{start: "Y", end: "X"}, out: "Y:X"},
|
||||
{pdr: PDRange{end: "X"}, out: "~:X"},
|
||||
{pdr: PDRange{start: "0", end: "X"}, out: ":X"},
|
||||
{pdr: PDRange{inside: true, start: "Y", end: "X"}, out: "@Y:X"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
result := test.pdr.String()
|
||||
assert(
|
||||
t,
|
||||
result == test.out,
|
||||
fmt.Sprintf("Expected '%s', got '%s'", test.out, result),
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue