Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 21 additions & 40 deletions cmd/alert.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
package cmd

import (
"errors"
"fmt"
"regexp"
"slices"
"strings"

"github.com/NETWAYS/check_prometheus/internal/alert"
"github.com/NETWAYS/go-check"
"github.com/NETWAYS/go-check/perfdata"
"github.com/NETWAYS/go-check/result"
goresult "github.com/NETWAYS/go-check/result"
"github.com/prometheus/common/model"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -49,7 +47,7 @@
| total=2 firing=1 pending=0 inactive=1`,
Run: func(_ *cobra.Command, _ []string) {
// Convert --no-alerts-state to integer and validate input
noAlertsState, err := convertStateToInt(cliAlertConfig.NoAlertsState)
noAlertsState, err := check.NewStatusFromString(cliAlertConfig.NoAlertsState)
if err != nil {
check.ExitError(fmt.Errorf("invalid value for --no-alerts-state: %s", cliAlertConfig.NoAlertsState))
}
Expand Down Expand Up @@ -88,7 +86,7 @@
// If there are no rules we can exit early
if len(rules) == 0 {
// Just an empty PerfdataList to have consistent perfdata output
pdlist := perfdata.PerfdataList{
pdlist := check.PerfdataList{
{Label: "total", Value: 0},
{Label: "firing", Value: 0},
{Label: "pending", Value: 0},
Expand All @@ -98,10 +96,10 @@
// Since the user is expecting the state of a certain alert and
// it that is not present it might be noteworthy.
if cliAlertConfig.AlertName != nil {
check.ExitRaw(check.Unknown, "No such alert defined", "|", pdlist.String())
check.ExitWithPerfdata(check.Unknown, pdlist, "No such alert defined")
}

check.ExitRaw(noAlertsState, "No alerts defined", "|", pdlist.String())
check.ExitWithPerfdata(noAlertsState, pdlist, "No alerts defined")
}

// Set initial capacity to reduce memory allocations
Expand All @@ -110,7 +108,7 @@
l *= len(rl.AlertingRule.Alerts)
}

var overall result.Overall
var overall goresult.Overall

for _, rl := range rules {
// If it's not the Alert we're looking for, Skip!
Expand All @@ -128,7 +126,7 @@
alertMatchedExclude, regexErr := matches(rl.AlertingRule.Name, cliAlertConfig.ExcludeAlerts)

if regexErr != nil {
check.ExitRaw(check.Unknown, "Invalid regular expression provided:", regexErr.Error())
check.Exit(check.Unknown, "Invalid regular expression provided:", regexErr.Error())
}

if alertMatchedExclude {
Expand All @@ -140,7 +138,7 @@
labelsMatchedExclude, regexErr := matchesLabel(rl.AlertingRule.Labels, cliAlertConfig.ExcludeLabels)

if regexErr != nil {
check.ExitRaw(check.Unknown, "Invalid regular expression provided:", regexErr.Error())
check.Exit(check.Unknown, "Invalid regular expression provided:", regexErr.Error())
}

if len(cliAlertConfig.ExcludeLabels) > 0 && labelsMatchedExclude {
Expand All @@ -161,16 +159,16 @@
counterFiring++
}

sc := result.NewPartialResult()
sc := goresult.NewPartialResult()

rlStatus := rl.GetStatus(cliAlertConfig.StateLabelKey)
// If the negate flag is set we negate this state
if cliAlertConfig.FlipExitState {
rlStatus = negateStatus(rlStatus)
}

_ = sc.SetState(rlStatus)
sc.Output = rl.GetOutput()
sc.SetState(rlStatus)
sc.SetOutput(rl.GetOutput())
overall.AddSubcheck(sc)
}

Expand All @@ -192,7 +190,7 @@
labelsMatchedInclude, regexErr := matchesLabel(alert.Labels, cliAlertConfig.IncludeLabels)

if regexErr != nil {
check.ExitRaw(check.Unknown, "Invalid regular expression provided:", regexErr.Error())
check.Exit(check.Unknown, "Invalid regular expression provided:", regexErr.Error())
}

if len(cliAlertConfig.IncludeLabels) > 0 && !labelsMatchedInclude {
Expand All @@ -203,34 +201,34 @@
labelsMatchedExclude, regexErr := matchesLabel(alert.Labels, cliAlertConfig.ExcludeLabels)

if regexErr != nil {
check.ExitRaw(check.Unknown, "Invalid regular expression provided:", regexErr.Error())
check.Exit(check.Unknown, "Invalid regular expression provided:", regexErr.Error())
}

if len(cliAlertConfig.ExcludeLabels) > 0 && labelsMatchedExclude {
// If the alert labels matches here we can skip it.
continue
}

sc := result.NewPartialResult()
sc := goresult.NewPartialResult()

rlStatus := rl.GetStatus(cliAlertConfig.StateLabelKey)
// If the negate flag is set we negate this state
if cliAlertConfig.FlipExitState {
rlStatus = negateStatus(rlStatus)
}

_ = sc.SetState(rlStatus)
sc.SetState(rlStatus)
// Set the alert in the internal Type to generate the output
rl.Alert = alert
sc.Output = rl.GetOutput()
sc.SetOutput(rl.GetOutput())
overall.AddSubcheck(sc)
}
}
}

counterAlert := counterFiring + counterPending + counterInactive

perfList := perfdata.PerfdataList{
perfList := check.PerfdataList{
{Label: "total", Value: counterAlert},
{Label: "firing", Value: counterFiring},
{Label: "pending", Value: counterPending},
Expand All @@ -238,24 +236,24 @@
}

// When there are no alerts we add an empty PartialResult just to have consistent output
if len(overall.PartialResults) == 0 {

Check failure on line 239 in cmd/alert.go

View workflow job for this annotation

GitHub Actions / build

overall.PartialResults undefined (type result.Overall has no field or method PartialResults, but does have unexported field partialResults)
sc := result.NewPartialResult()
sc := goresult.NewPartialResult()
// We already make sure it's valid
//nolint: errcheck
sc.SetDefaultState(noAlertsState)
sc.Output = "No alerts retrieved"
sc.SetOutput("No alerts retrieved")
overall.AddSubcheck(sc)
}

overall.PartialResults[0].Perfdata = append(overall.PartialResults[0].Perfdata, perfList...)

Check failure on line 248 in cmd/alert.go

View workflow job for this annotation

GitHub Actions / build

overall.PartialResults undefined (type result.Overall has no field or method PartialResults, but does have unexported field partialResults)

overall.Summary = fmt.Sprintf("%d Alerts: %d Firing - %d Pending - %d Inactive",

Check failure on line 250 in cmd/alert.go

View workflow job for this annotation

GitHub Actions / build

overall.Summary undefined (type result.Overall has no field or method Summary)
counterAlert,
counterFiring,
counterPending,
counterInactive)

check.ExitRaw(overall.GetStatus(), overall.GetOutput())
check.Exit(overall.GetStatus(), overall.GetOutput())
},
}

Expand Down Expand Up @@ -299,23 +297,6 @@
"\nIf this flag is set the plugin looks for the strings 'warning/critical/ok' in the provided label key")
}

// Function to convert state to integer.
func convertStateToInt(state string) (int, error) {
state = strings.ToUpper(state)
switch state {
case "OK", "0":
return check.OK, nil
case "WARNING", "1":
return check.Warning, nil
case "CRITICAL", "2":
return check.Critical, nil
case "UNKNOWN", "3":
return check.Unknown, nil
default:
return check.Unknown, errors.New("invalid state")
}
}

// Matches a list of regular expressions against a string.
func matches(input string, regexToExclude []string) (bool, error) {
for _, regex := range regexToExclude {
Expand Down Expand Up @@ -364,7 +345,7 @@
}

// negateStatus turns an OK state into critical and a warning/critical state into OK
func negateStatus(state int) int {
func negateStatus(state check.Status) check.Status {
switch state {
case check.OK:
return check.Critical
Expand Down
24 changes: 11 additions & 13 deletions cmd/health.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ Ready: Checks the readiness of an endpoint, which returns OK if the Prometheus s
$ check_prometheus --bearer secrettoken health --ready
OK - Prometheus Server is Ready. | statuscode=200`,
Run: func(_ *cobra.Command, _ []string) {
var (
rc int
)
var rc check.Status

overall := result.Overall{}

Expand All @@ -49,11 +47,11 @@ Ready: Checks the readiness of an endpoint, which returns OK if the Prometheus s

partialResult := result.NewPartialResult()

_ = partialResult.SetState(rc)
partialResult.Output = output
partialResult.SetState(rc)
partialResult.SetOutput(output)
overall.AddSubcheck(partialResult)

check.ExitRaw(overall.GetStatus(), overall.GetOutput())
check.Exit(overall.GetStatus(), overall.GetOutput())
}

if cliConfig.Info {
Expand All @@ -65,18 +63,18 @@ Ready: Checks the readiness of an endpoint, which returns OK if the Prometheus s

partialResult := result.NewPartialResult()

_ = partialResult.SetState(rc)
partialResult.SetState(rc)

partialResult.Output = "Prometheus Server information\n\n" +
partialResult.SetOutput("Prometheus Server information\n\n" +
"Version: " + info.Version + "\n" +
"Branch: " + info.Branch + "\n" +
"BuildDate: " + info.BuildDate + "\n" +
"BuildUser: " + info.BuildUser + "\n" +
"Revision: " + info.Revision
"Revision: " + info.Revision)

overall.AddSubcheck(partialResult)

check.ExitRaw(overall.GetStatus(), overall.GetOutput())
check.Exit(overall.GetStatus(), overall.GetOutput())
}

// Getting the health status is the default
Expand All @@ -87,11 +85,11 @@ Ready: Checks the readiness of an endpoint, which returns OK if the Prometheus s
}

partialResult := result.NewPartialResult()
_ = partialResult.SetState(rc)
partialResult.Output = output
partialResult.SetState(rc)
partialResult.SetOutput(output)
overall.AddSubcheck(partialResult)

check.ExitRaw(overall.GetStatus(), overall.GetOutput())
check.Exit(overall.GetStatus(), overall.GetOutput())
},
}

Expand Down
27 changes: 13 additions & 14 deletions cmd/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
"time"

"github.com/NETWAYS/go-check"
"github.com/NETWAYS/go-check/perfdata"
goresult "github.com/NETWAYS/go-check/result"
"github.com/prometheus/common/model"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -41,9 +40,9 @@
~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64
}

func generatePerfdata[T Number](metric string, value T, warning, critical *check.Threshold) perfdata.Perfdata {
func generatePerfdata[T Number](metric string, value T, warning, critical *check.Threshold) check.Perfdata {
// We trim the trailing "} from the string, so that the Perfdata won't have a trailing _
return perfdata.Perfdata{
return check.Perfdata{
Label: replacer.Replace(metric),
Value: value,
Warn: warning,
Expand Down Expand Up @@ -120,23 +119,23 @@
partial := goresult.NewPartialResult()

if crit.DoesViolate(numberValue) {
_ = partial.SetState(check.Critical)
partial.SetState(check.Critical)
} else if warn.DoesViolate(numberValue) {
_ = partial.SetState(check.Warning)
partial.SetState(check.Warning)
} else {
_ = partial.SetState(check.OK)
partial.SetState(check.OK)
}

// Format the metric and RC output for console output
partial.Output = generateMetricOutput(sample.Metric.String(), sample.Value.String())
partial.SetOutput(generateMetricOutput(sample.Metric.String(), sample.Value.String()))

// Generate Perfdata from API return
if math.IsInf(numberValue, 0) || math.IsNaN(numberValue) {
continue
}

perf := generatePerfdata(sample.Metric.String(), numberValue, warn, crit)
partial.Perfdata.Add(&perf)
partial.AddPerfdata(&perf)
overall.AddSubcheck(partial)
}

Expand All @@ -155,15 +154,15 @@
partial := goresult.NewPartialResult()

if crit.DoesViolate(numberValue) {
_ = partial.SetState(check.Critical)
partial.SetState(check.Critical)
} else if warn.DoesViolate(numberValue) {
_ = partial.SetState(check.Warning)
partial.SetState(check.Warning)
} else {
_ = partial.SetState(check.OK)
partial.SetState(check.OK)
}

// Format the metric and RC output for console output
partial.Output = generateMetricOutput(samplepair.String(), samplepair.Value.String())
partial.SetOutput(generateMetricOutput(samplepair.String(), samplepair.Value.String()))

valueString := samplepair.Value.String()

Expand All @@ -173,7 +172,7 @@

// Generate Perfdata from API return
if !math.IsInf(numberValue, 0) && !math.IsNaN(numberValue) {
partial.Perfdata.Add(&pd)
partial.AddPerfdata(&pd)
}
}

Expand All @@ -183,10 +182,10 @@

if len(warnings) != 0 {
appendum := fmt.Sprintf("HTTP Warnings: %v", strings.Join(warnings, ", "))
overall.Summary = overall.GetOutput() + appendum

Check failure on line 185 in cmd/query.go

View workflow job for this annotation

GitHub Actions / build

overall.Summary undefined (type result.Overall has no field or method Summary)
}

check.ExitRaw(overall.GetStatus(), overall.GetOutput())
check.Exit(overall.GetStatus(), overall.GetOutput())
},
}

Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
module github.com/NETWAYS/check_prometheus

go 1.25.0
go 1.26

require (
github.com/NETWAYS/go-check v0.6.4
github.com/NETWAYS/go-check v1.0.0
github.com/prometheus/client_golang v1.23.2
github.com/prometheus/common v0.69.0
github.com/spf13/cobra v1.10.2
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
github.com/NETWAYS/go-check v0.6.4 h1:4WETSVNZNEP0Yudcp5xlvxq6RGn920cmUKq4fz/P1GQ=
github.com/NETWAYS/go-check v0.6.4/go.mod h1:8/GWnq8SirreAixgRmcp82JG16NnEl38rHq9phICy9s=
github.com/NETWAYS/go-check v1.0.0 h1:YkzTwFfGR+Z+mK3Wsqpnu8wibzsB30im19iPNfCOsMQ=
github.com/NETWAYS/go-check v1.0.0/go.mod h1:8/GWnq8SirreAixgRmcp82JG16NnEl38rHq9phICy9s=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
Expand Down
2 changes: 1 addition & 1 deletion internal/alert/alert.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func FlattenRules(groups []v1.RuleGroup, wantedGroups []string, alerts []v1.Aler
return rules
}

func (a *Rule) GetStatus(labelKey string) (status int) {
func (a *Rule) GetStatus(labelKey string) (status check.Status) {
state := a.AlertingRule.State

switch state {
Expand Down
2 changes: 1 addition & 1 deletion internal/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (c *Client) Connect() error {
return nil
}

func (c *Client) GetStatus(ctx context.Context, endpoint string) (returncode int, statuscode int, body string, err error) {
func (c *Client) GetStatus(ctx context.Context, endpoint string) (returncode check.Status, statuscode int, body string, err error) {
// Parses the response from the Prometheus /healthy and /ready endpoint
// Return: Exit Status Code, HTTP Status Code, HTTP Body, Error
// Building the final URL with the endpoint parameter
Expand Down
Loading