diff --git a/Exit.go b/Exit.go index 5c92cec..8d2c5b3 100644 --- a/Exit.go +++ b/Exit.go @@ -19,15 +19,20 @@ func ErrorExit(err error) { // Exit returns with the given returncode and message and optional PerformanceData func Exit(state State, msg string) { - LongExit(state, msg, "") + LongExit(state, msg, "", nil) } // LongExit returns with the given returncode and message and optional PerformanceData and long message -func LongExit(state State, msg, longMsg string) { - if perf := PrintPerformanceData(); perf == "" { - fmt.Printf("%s - %s\n%s", state.name, msg, longMsg) +func LongExit(state State, msg, longMsg string, collection *PerformanceDataCollection) { + perfString := "" + if collection != nil { + perfString = collection.PrintAllPerformanceData() + } + + if perfString == "" { + fmt.Printf("%s - %s\n%s", state.Name, msg, longMsg) } else { - fmt.Printf("%s - %s|%s\n%s", state.name, msg, perf, longMsg) + fmt.Printf("%s - %s|%s\n%s", state.Name, msg, perfString, longMsg) } - os.Exit(state.code) + os.Exit(state.Code) } diff --git a/PerformanceData.go b/PerformanceData.go index 2361a55..75c0c9a 100644 --- a/PerformanceData.go +++ b/PerformanceData.go @@ -7,68 +7,123 @@ import ( "sync" ) -type PerformanceData map[string]interface{} +// PerformanceData is a map with string keys and any values, essentially a dictionary +// Since its an dictionary, always add add a label +// "label" = "health_rate" +// "value" = 80 +// "critical" = 160 +type PerformanceData map[string]any -var ( - p []PerformanceData = []PerformanceData{} - pMutex = &sync.Mutex{} -) +// This is a struct that users of the library should use. +// They have to manage this object through functions in this file +// Library clients should get an instance of this and keep calling its methods +type PerformanceDataCollection struct { + data []PerformanceData + dataMutex *sync.Mutex +} + +// Returns an empty PerformanceDataCollection +func NewPerformanceDataCollection() PerformanceDataCollection { + return PerformanceDataCollection{ + data: make([]PerformanceData, 0), + dataMutex: &sync.Mutex{}, + } +} -// NewPerformanceData adds a PerformanceData object which can be expanded with further information -func NewPerformanceData(label string, value float64) *PerformanceData { - return NewPerformanceDataString(label, strconv.FormatFloat(value, 'f', -1, 64)) +// Adds a new PerformanceData element to the data array +// Use the label to get the reference to the PerformanceData later on +func (collection *PerformanceDataCollection) AddPerformanceData(label string, value string) { + collection.dataMutex.Lock() + collection.data = append(collection.data, PerformanceData{"label": label, "value": value}) + collection.dataMutex.Unlock() } -// NewPerformanceDataString adds a PerformanceData object which can be expanded with further information -func NewPerformanceDataString(label, value string) *PerformanceData { - pMutex.Lock() - p = append(p, PerformanceData{"label": label, "value": value}) - newOne := &(p[len(p)-1]) - pMutex.Unlock() - return newOne +// Calls collection.AddPerformanceData after converting float to string +func (collection *PerformanceDataCollection) AddPerformanceDataFloat64(label string, value float64) { + collection.AddPerformanceData(label, strconv.FormatFloat(value, 'f', -1, 64)) } -// Unit adds an unit string to the PerformanceData -func (p PerformanceData) Unit(unit string) PerformanceData { - p["unit"] = unit - return p +// Internal function to find a PerformanceData with specified label +func (collection *PerformanceDataCollection) findPerformanceData(label string) (*PerformanceData, error) { + for index, pd := range collection.data { + if pd_label, ok := pd["label"]; ok && pd_label == label { + return &collection.data[index], nil + } + } + return nil, fmt.Errorf("no performance data with the label '%s' found", label) +} + +// Adds a field called "unit" to the PerformanceData with the label +func (collection *PerformanceDataCollection) Unit(label string, unit string) error { + collection.dataMutex.Lock() + defer collection.dataMutex.Unlock() + pd, err := collection.findPerformanceData(label) + if err != nil { + return err + } + (*pd)["unit"] = unit + return nil } -// Warn adds the threshold to the PerformanceData -func (p PerformanceData) Warn(warn *Threshold) PerformanceData { - p["warn"] = warn - return p +// Adds a field called "unit" to the PerformanceData with the label +func (collection *PerformanceDataCollection) Warn(label string, warn *Threshold) error { + collection.dataMutex.Lock() + defer collection.dataMutex.Unlock() + pd, err := collection.findPerformanceData(label) + if err != nil { + return err + } + (*pd)["warn"] = warn + return nil } -// Crit adds the threshold to the PerformanceData -func (p PerformanceData) Crit(crit *Threshold) PerformanceData { - p["crit"] = crit - return p +// Adds a field called "unit" to the PerformanceData with the label +func (collection *PerformanceDataCollection) Crit(label string, crit *Threshold) error { + collection.dataMutex.Lock() + defer collection.dataMutex.Unlock() + pd, err := collection.findPerformanceData(label) + if err != nil { + return err + } + (*pd)["crit"] = crit + return nil } -// Min adds the float64 to the PerformanceData -func (p PerformanceData) Min(min float64) PerformanceData { - p["min"] = min - return p +// Adds a field called "unit" to the PerformanceData with the label +func (collection *PerformanceDataCollection) Min(label string, min float64) error { + collection.dataMutex.Lock() + defer collection.dataMutex.Unlock() + pd, err := collection.findPerformanceData(label) + if err != nil { + return err + } + (*pd)["min"] = min + return nil } -// Min adds the float64 to the PerformanceData -func (p PerformanceData) Max(max float64) PerformanceData { - p["max"] = max - return p +// Adds a field called "unit" to the PerformanceData with the label +func (collection *PerformanceDataCollection) Max(label string, max float64) error { + collection.dataMutex.Lock() + defer collection.dataMutex.Unlock() + pd, err := collection.findPerformanceData(label) + if err != nil { + return err + } + (*pd)["max"] = max + return nil } -// toString prints this PerformanceData -func (p PerformanceData) toString() string { +// internal function to print a PerformanceData +func (pd PerformanceData) toString() string { var toPrint bytes.Buffer - toPrint.WriteString(fmt.Sprintf("'%s'=%s", p["label"], p["value"])) - if unit, ok := p["unit"]; ok { + toPrint.WriteString(fmt.Sprintf("'%s'=%s", pd["label"], pd["value"])) + if unit, ok := pd["unit"]; ok { toPrint.WriteString(unit.(string)) } toPrint.WriteString(";") addThreshold := func(key string) { - if value, ok := p[key]; ok && value != nil { + if value, ok := pd[key]; ok && value != nil { if t := value.(*Threshold); t != nil { toPrint.WriteString(t.input) } @@ -79,7 +134,7 @@ func (p PerformanceData) toString() string { addThreshold("crit") addFloat := func(key string) { - if value, ok := p[key]; ok { + if value, ok := pd[key]; ok { toPrint.WriteString(strconv.FormatFloat(value.(float64), 'f', -1, 64)) } } @@ -90,14 +145,33 @@ func (p PerformanceData) toString() string { return toPrint.String() } -// PrintPerformanceData prints all PerformanceData -func PrintPerformanceData() string { +// Finds and prints the PerformanceData found in this collection +func (collection *PerformanceDataCollection) PrintPerformanceData(label string) (string, error) { + collection.dataMutex.Lock() + defer collection.dataMutex.Unlock() + pd, err := collection.findPerformanceData(label) + if err != nil { + return "", err + } + + return (*pd).toString(), nil +} + +// Prints all PerformanceData to a string in this collection +func (collection *PerformanceDataCollection) PrintAllPerformanceData() string { + collection.dataMutex.Lock() + defer collection.dataMutex.Unlock() var toPrint bytes.Buffer - pMutex.Lock() - for _, perfData := range p { + for _, perfData := range collection.data { toPrint.WriteString(perfData.toString()) toPrint.WriteString(" ") } - pMutex.Unlock() return toPrint.String() } + +// Clears all PerformanceData stored in this collection +func (collection *PerformanceDataCollection) ClearPerformanceCollection() { + collection.dataMutex.Lock() + defer collection.dataMutex.Unlock() + collection.data = make([]PerformanceData, 0) +} diff --git a/PerformanceData_test.go b/PerformanceData_test.go index a0d50c3..da8b566 100644 --- a/PerformanceData_test.go +++ b/PerformanceData_test.go @@ -1,76 +1,123 @@ package check_x import ( - "reflect" "testing" ) var perfdataToString = []struct { - f func() PerformanceData + f func() PerformanceDataCollection expected string }{ - {func() PerformanceData { - return *NewPerformanceDataString("a", "1") + {func() PerformanceDataCollection { + col := NewPerformanceDataCollection() + col.AddPerformanceData("a", "1") + return col }, "'a'=1;;;;"}, - {func() PerformanceData { + {func() PerformanceDataCollection { warn, _ := NewThreshold("10:") - return NewPerformanceDataString("a", "2").Warn(warn) + col := NewPerformanceDataCollection() + col.AddPerformanceData("a", "2") + col.Warn("a", warn) + return col }, "'a'=2;10:;;;"}, - {func() PerformanceData { + {func() PerformanceDataCollection { warn, _ := NewThreshold("10:") crit, _ := NewThreshold("@10:20") - return NewPerformanceDataString("a", "3").Warn(warn).Crit(crit) + col := NewPerformanceDataCollection() + col.AddPerformanceData("a", "3") + col.Warn("a", warn) + col.Crit("a", crit) + return col }, "'a'=3;10:;@10:20;;"}, - {func() PerformanceData { + {func() PerformanceDataCollection { warn, _ := NewThreshold("10:") crit, _ := NewThreshold("@10:20") - return NewPerformanceDataString("a", "3").Warn(warn).Crit(crit).Min(0) + col := NewPerformanceDataCollection() + col.AddPerformanceData("a", "3") + col.Warn("a", warn) + col.Crit("a", crit) + col.Min("a", 0) + return col }, "'a'=3;10:;@10:20;0;"}, - {func() PerformanceData { + {func() PerformanceDataCollection { warn, _ := NewThreshold("10:") crit, _ := NewThreshold("@10:20") - return NewPerformanceDataString("a", "4").Warn(warn).Crit(crit).Min(0).Max(100) + col := NewPerformanceDataCollection() + col.AddPerformanceData("a", "4") + col.Warn("a", warn) + col.Crit("a", crit) + col.Min("a", 0) + col.Max("a", 100) + return col }, "'a'=4;10:;@10:20;0;100"}, - {func() PerformanceData { + {func() PerformanceDataCollection { warn, _ := NewThreshold("10:") crit, _ := NewThreshold("@10:20") - return NewPerformanceDataString("a", "5").Warn(warn).Crit(crit).Min(0).Max(100).Unit("C") + col := NewPerformanceDataCollection() + col.AddPerformanceData("a", "5") + col.Warn("a", warn) + col.Crit("a", crit) + col.Min("a", 0) + col.Max("a", 100) + col.Unit("a", "C") + return col }, "'a'=5C;10:;@10:20;0;100"}, - {func() PerformanceData { + {func() PerformanceDataCollection { warn, _ := NewThreshold("10:") crit, _ := NewThreshold("@10:20") - return NewPerformanceData("a", 6).Warn(warn).Crit(crit).Min(0).Max(100).Unit("C") + col := NewPerformanceDataCollection() + col.AddPerformanceDataFloat64("a", 6) + col.Warn("a", warn) + col.Crit("a", crit) + col.Min("a", 0) + col.Max("a", 100) + col.Unit("a", "C") + return col }, "'a'=6C;10:;@10:20;0;100"}, - {func() PerformanceData { + {func() PerformanceDataCollection { warn, _ := NewThreshold("") crit, _ := NewThreshold("@10:20") - return NewPerformanceData("a", 6).Warn(warn).Crit(crit).Min(0).Max(100).Unit("C") + col := NewPerformanceDataCollection() + col.AddPerformanceDataFloat64("a", 6) + col.Warn("a", warn) + col.Crit("a", crit) + col.Min("a", 0) + col.Max("a", 100) + col.Unit("a", "C") + return col }, "'a'=6C;;@10:20;0;100"}, - {func() PerformanceData { + {func() PerformanceDataCollection { crit, _ := NewThreshold("@10:20") - return NewPerformanceData("a", 6).Warn(nil).Crit(crit).Min(0).Max(100).Unit("C") + col := NewPerformanceDataCollection() + col.AddPerformanceDataFloat64("a", 6) + col.Warn("a", nil) + col.Crit("a", crit) + col.Min("a", 0) + col.Max("a", 100) + col.Unit("a", "C") + return col }, "'a'=6C;;@10:20;0;100"}, } func TestPerformanceData_toString(t *testing.T) { for i, data := range perfdataToString { - result := data.f() - resultString := result.toString() - if resultString != data.expected { - t.Errorf("%d - Expected: %s, got: %s", i, data.expected, resultString) + collection := data.f() + collectionString, err := collection.PrintPerformanceData("a") + if err != nil { + t.Errorf("Error when finding the performance data: %s", err.Error()) } - if !reflect.DeepEqual(p[i], result) { - t.Errorf("%d - Expected: %s, got: %s", i, p[i], result) + if collectionString != data.expected { + t.Errorf("%d - Expected: %s, got: %s", i, data.expected, collectionString) } } } func TestPrintPerformanceData(t *testing.T) { - p = []PerformanceData{} - NewPerformanceDataString("a", "1") - NewPerformanceDataString("b", "2") + col := NewPerformanceDataCollection() + col.AddPerformanceData("a", "1") + col.AddPerformanceData("b", "2") expected := "'a'=1;;;;" + " " + "'b'=2;;;; " - if expected != PrintPerformanceData() { - t.Errorf("Expected: %s, got: %s", expected, PrintPerformanceData()) + if expected != col.PrintAllPerformanceData() { + t.Errorf("Expected: %s, got: %s", expected, col.PrintAllPerformanceData()) } } diff --git a/States.go b/States.go index eb76d6e..c45c6a1 100644 --- a/States.go +++ b/States.go @@ -8,8 +8,8 @@ import ( // State represents an Nagioskind returncode type State struct { - name string - code int + Name string + Code int } // StateFromInt creates an known state if code is 0-3, else a new State will be returned @@ -24,7 +24,7 @@ func StateFromInt(code int) State { case 3: return Unknown default: - return State{code: code} + return State{Code: code} } } @@ -41,24 +41,24 @@ func StateFromString(name string) State { case "unknown": return Unknown default: - return State{name: name} + return State{Name: name} } } // String prints the name of the state func (s State) String() string { - return s.name + return s.Name } var ( // OK - returncode: 0 - OK = State{name: "OK", code: 0} + OK = State{Name: "OK", Code: 0} // Warning - returncode: 1 - Warning = State{name: "WARNING", code: 1} + Warning = State{Name: "WARNING", Code: 1} // Critical - returncode: 2 - Critical = State{name: "CRITICAL", code: 2} + Critical = State{Name: "CRITICAL", Code: 2} // Unknown - returncode: 3 - Unknown = State{name: "UNKNOWN", code: 3} + Unknown = State{Name: "UNKNOWN", Code: 3} ) // States is a list of state @@ -74,7 +74,7 @@ func (s States) Len() int { // Less for Sort interface func (s States) Less(i, j int) bool { - return s[i].code < s[j].code + return s[i].Code < s[j].Code } // Swap for Sort interface diff --git a/States_test.go b/States_test.go index 256d3ed..d735e86 100644 --- a/States_test.go +++ b/States_test.go @@ -31,7 +31,7 @@ var intToState = []struct { {1, Warning}, {2, Critical}, {3, Unknown}, - {4, State{code: 4}}, + {4, State{Code: 4}}, } func TestStateFromInt(t *testing.T) { @@ -50,7 +50,7 @@ var stringToState = []struct { {"Warning", Warning}, {"Critical", Critical}, {"Unknown", Unknown}, - {"FOO", State{name: "FOO"}}, + {"FOO", State{Name: "FOO"}}, } func TestStateFromString(t *testing.T) {