From 8a7572ecc3e62cc1eec121c0ff429cad2644b60d Mon Sep 17 00:00:00 2001 From: Jan Scheufler Date: Thu, 7 Nov 2024 19:40:26 +0100 Subject: [PATCH 1/5] add new parameter "URL" to access Prometheus behind reverse proxy --- README.md | 2 ++ cmd/config.go | 2 ++ cmd/root.go | 2 ++ 3 files changed, 6 insertions(+) diff --git a/README.md b/README.md index 3afbb20..553e816 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Available Commands: Flags: -H, --hostname string Hostname of the Prometheus server (CHECK_PROMETHEUS_HOSTNAME) (default "localhost") -p, --port int Port of the Prometheus server (default 9090) + -U, --url string URL/Path to append to the Promethes Hostname (default "/") -s, --secure Use a HTTPS connection -i, --insecure Skip the verification of the server's TLS certificate -b, --bearer string Specify the Bearer Token for server authentication (CHECK_PROMETHEUS_BEARER) @@ -161,6 +162,7 @@ CRITICAL - 6 Alerts: 3 Firing - 0 Pending - 3 Inactive | total=6 firing=3 pending=0 inactive=3 ``` + #### Checking multiple alerts ```bash diff --git a/cmd/config.go b/cmd/config.go index 35314f7..dd7ad57 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -28,6 +28,7 @@ type Config struct { CertFile string `env:"CHECK_PROMETHEUS_CERT_FILE"` KeyFile string `env:"CHECK_PROMETHEUS_KEY_FILE"` Hostname string `env:"CHECK_PROMETHEUS_HOSTNAME"` + URL string `env:"CHECK_PROMETHEUS_URL"` Port int Info bool Insecure bool @@ -65,6 +66,7 @@ func (c *Config) NewClient() *client.Client { u := url.URL{ Scheme: "http", Host: c.Hostname + ":" + strconv.Itoa(c.Port), + Path: c.URL, } if c.Secure { diff --git a/cmd/root.go b/cmd/root.go index c77f630..49baa0d 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -43,6 +43,8 @@ func init() { "Hostname of the Prometheus server (CHECK_PROMETHEUS_HOSTNAME)") pfs.IntVarP(&cliConfig.Port, "port", "p", 9090, "Port of the Prometheus server") + pfs.StringVarP(&cliConfig.URL, "url", "U", "/", + "URL/Path to append to the Promethes Hostname") pfs.BoolVarP(&cliConfig.Secure, "secure", "s", false, "Use a HTTPS connection") pfs.BoolVarP(&cliConfig.Insecure, "insecure", "i", false, From fb30917db273f432ff9909adc81bb156679c3ce1 Mon Sep 17 00:00:00 2001 From: Jan Scheufler Date: Fri, 8 Nov 2024 09:32:40 +0100 Subject: [PATCH 2/5] fix config_test for new default --- cmd/config_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/config_test.go b/cmd/config_test.go index 07d74b4..52a4dae 100644 --- a/cmd/config_test.go +++ b/cmd/config_test.go @@ -6,8 +6,8 @@ import ( func TestConfig(t *testing.T) { c := cliConfig.NewClient() - expected := "http://localhost:9090" - if c.URL != "http://localhost:9090" { + expected := "http://localhost:9090/" + if c.URL != "http://localhost:9090/" { t.Error("\nActual: ", c.URL, "\nExpected: ", expected) } } From 639deaf6b08300a80d886cb9a8c73939f239ba18 Mon Sep 17 00:00:00 2001 From: Jan Scheufler Date: Fri, 8 Nov 2024 12:58:52 +0100 Subject: [PATCH 3/5] add entry in README.md and mention env var for --url --- README.md | 13 ++++++++++++- cmd/root.go | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 553e816..32f61a5 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Available Commands: Flags: -H, --hostname string Hostname of the Prometheus server (CHECK_PROMETHEUS_HOSTNAME) (default "localhost") -p, --port int Port of the Prometheus server (default 9090) - -U, --url string URL/Path to append to the Promethes Hostname (default "/") + -U, --url string URL/Path to append to the Promethes Hostname (CHECK_PROMETHEUS_URL) (default "/") -s, --secure Use a HTTPS connection -i, --insecure Skip the verification of the server's TLS certificate -b, --bearer string Specify the Bearer Token for server authentication (CHECK_PROMETHEUS_BEARER) @@ -179,6 +179,17 @@ $ check_prometheus alert --name "HostHighCpuLoad" --name "PrometheusTargetMissin OK - Alerts inactive | total=2 firing=0 pending=0 inactive=2 ``` +### Special cases + +#### Your Prometheus runs behind a reverse proxy + +>Example: + +```bash +$ check_prometheus health --hostname 'monitoring.example.com' --port 443 --secure --url /subpath +OK - Prometheus Server is Healthy. | statuscode=200 +``` + ## License Copyright (c) 2022 [NETWAYS GmbH](mailto:info@netways.de) diff --git a/cmd/root.go b/cmd/root.go index 49baa0d..1b859a0 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -44,7 +44,7 @@ func init() { pfs.IntVarP(&cliConfig.Port, "port", "p", 9090, "Port of the Prometheus server") pfs.StringVarP(&cliConfig.URL, "url", "U", "/", - "URL/Path to append to the Promethes Hostname") + "URL/Path to append to the Promethes Hostname (CHECK_PROMETHEUS_URL)") pfs.BoolVarP(&cliConfig.Secure, "secure", "s", false, "Use a HTTPS connection") pfs.BoolVarP(&cliConfig.Insecure, "insecure", "i", false, From 78d170797ece4e5be083e27a92f75cf526090cb5 Mon Sep 17 00:00:00 2001 From: Jan Scheufler Date: Sun, 22 Dec 2024 14:00:04 +0100 Subject: [PATCH 4/5] add new parameter "group" to filter alert rules --- README.md | 14 +- cmd/alert.go | 7 +- cmd/alert_test.go | 265 +++++++++++++++++++++++++++++++++-- internal/alert/alert.go | 36 +++-- internal/alert/alert_test.go | 2 +- 5 files changed, 294 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index a6fa0b3..04fba06 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,9 @@ Flags: -n, --name strings The name of one or more specific alerts to check. This parameter can be repeated e.G.: '--name alert1 --name alert2' If no name is given, all alerts will be evaluated + -g, --group strings The name of one or more specific groups to check. + This parameter can be repeated e.G.: '--group group1 --group group2' + If no group is given, all groups will be scanned for alerts -T, --no-alerts-state string State to assign when no alerts are found (0, 1, 2, 3, OK, WARNING, CRITICAL, UNKNOWN). If not set this defaults to OK (default "OK") -P, --problems Display only alerts which status is not inactive/OK. Note that in combination with the --name flag this might result in no alerts being displayed ``` @@ -190,17 +193,6 @@ $ check_prometheus alert --name "HostHighCpuLoad" --name "PrometheusTargetMissin OK - Alerts inactive | total=2 firing=0 pending=0 inactive=2 ``` -### Special cases - -#### Your Prometheus runs behind a reverse proxy - ->Example: - -```bash -$ check_prometheus health --hostname 'monitoring.example.com' --port 443 --secure --url /subpath -OK - Prometheus Server is Healthy. | statuscode=200 -``` - ## License Copyright (c) 2022 [NETWAYS GmbH](mailto:info@netways.de) diff --git a/cmd/alert.go b/cmd/alert.go index 31a02d1..04392eb 100644 --- a/cmd/alert.go +++ b/cmd/alert.go @@ -83,7 +83,7 @@ inactive = 0`, } // Get all rules from all groups into a single list - rules := alert.FlattenRules(alerts.Groups) + rules := alert.FlattenRules(alerts.Groups, cliAlertConfig.Group) // If there are no rules we can exit early if len(rules) == 0 { @@ -217,6 +217,11 @@ func init() { "\nThis parameter can be repeated e.G.: '--name alert1 --name alert2'"+ "\nIf no name is given, all alerts will be evaluated") + fs.StringSliceVarP(&cliAlertConfig.Group, "group", "g", nil, + "The name of one or more specific groups to check for alerts."+ + "\nThis parameter can be repeated e.G.: '--group group1 --group group2'"+ + "\nIf no group is given, all groups will be scanned for alerts") + fs.BoolVarP(&cliAlertConfig.ProblemsOnly, "problems", "P", false, "Display only alerts which status is not inactive/OK. Note that in combination with the --name flag this might result in no alerts being displayed") } diff --git a/cmd/alert_test.go b/cmd/alert_test.go index e585a40..9f4cfa4 100644 --- a/cmd/alert_test.go +++ b/cmd/alert_test.go @@ -1,6 +1,7 @@ package cmd import ( + "fmt" "net/http" "net/http/httptest" "net/url" @@ -30,6 +31,224 @@ type AlertTest struct { } func TestAlertCmd(t *testing.T) { + + alertTestDataSet1 := fmt.Sprintf(` + { + "status": "success", + "data": { + "groups": [ + { + "name": "Foo", + "file": "alerts.yaml", + "rules": [ + { + "state": "inactive", + "name": "HostOutOfMemory", + "query": "up", + "duration": 120, + "labels": { + "severity": "critical" + }, + "annotations": { + "description": "Foo", + "summary": "Foo" + }, + "alerts": [], + "health": "ok", + "evaluationTime": 0.000553928, + "lastEvaluation": "2022-11-24T14:08:17.597083058Z", + "type": "alerting" + } + ], + "interval": 10, + "limit": 0, + "evaluationTime": 0.000581212, + "lastEvaluation": "2022-11-24T14:08:17.59706083Z" + }, + { + "name": "SQL", + "file": "alerts.yaml", + "rules": [ + { + "state": "pending", + "name": "SqlAccessDeniedRate", + "query": "mysql", + "duration": 17280000, + "labels": { + "severity": "warning" + }, + "annotations": { + "description": "MySQL", + "summary": "MySQL" + }, + "alerts": [ + { + "labels": { + "alertname": "SqlAccessDeniedRate", + "instance": "localhost", + "job": "mysql", + "severity": "warning" + }, + "annotations": { + "description": "MySQL", + "summary": "MySQL" + }, + "state": "pending", + "activeAt": "2022-11-21T10:38:35.373483748Z", + "value": "4.03448275862069e-01" + } + ], + "health": "ok", + "evaluationTime": 0.002909617, + "lastEvaluation": "2022-11-24T14:08:25.375220595Z", + "type": "alerting" + } + ], + "interval": 10, + "limit": 0, + "evaluationTime": 0.003046259, + "lastEvaluation": "2022-11-24T14:08:25.375096825Z" + }, + { + "name": "TLS", + "file": "alerts.yaml", + "rules": [ + { + "state": "firing", + "name": "BlackboxTLS", + "query": "SSL", + "duration": 0, + "labels": { + "severity": "critical" + }, + "annotations": { + "description": "TLS", + "summary": "TLS" + }, + "alerts": [ + { + "labels": { + "alertname": "TLS", + "instance": "https://localhost:443", + "job": "blackbox", + "severity": "critical" + }, + "annotations": { + "description": "TLS", + "summary": "TLS" + }, + "state": "firing", + "activeAt": "2022-11-24T05:11:27.211699259Z", + "value": "-6.065338210999966e+06" + } + ], + "health": "ok", + "evaluationTime": 0.000713955, + "lastEvaluation": "2022-11-24T14:08:17.212720815Z", + "type": "alerting" + } + ], + "interval": 10, + "limit": 0, + "evaluationTime": 0.000738927, + "lastEvaluation": "2022-11-24T14:08:17.212700182Z" + } + ] + } + }`) + + alertTestDataSet2 := fmt.Sprintf(` + { + "status": "success", + "data": { + "groups": [ + { + "name": "Foo", + "file": "alerts.yaml", + "rules": [ + { + "state": "inactive", + "name": "InactiveAlert", + "query": "foo", + "duration": 120, + "labels": { + "severity": "critical" + }, + "annotations": { + "description": "Inactive", + "summary": "Inactive" + }, + "alerts": [], + "health": "ok", + "evaluationTime": 0.000462382, + "lastEvaluation": "2022-11-18T14:01:07.597034323Z", + "type": "alerting" + } + ], + "interval": 10, + "limit": 0, + "evaluationTime": 0.000478395, + "lastEvaluation": "2022-11-18T14:01:07.597021953Z" + } + ] + } + }`) + + alertTestDataSet3 := fmt.Sprintf(` + { + "status": "success", + "data": { + "groups": [ + { + "name": "k8s", + "file": "/etc/prometheus/rules/al.yaml", + "rules": [ + { + "state": "inactive", + "name": "NodeHasMemoryPressure", + "query": "kube_node{condition=\"MemoryPressure\",status=\"true\"} == 1", + "duration": 300, + "keepFiringFor": 0, + "labels": {}, + "annotations": { + "summary": "Memory pressure on instance {{ $labels.instance }}" + }, + "alerts": [], + "health": "ok", + "evaluationTime": 0.00023339, + "lastEvaluation": "2024-12-18T17:50:01.483161228Z", + "type": "alerting" + } + ], + "interval": 15, + "limit": 0, + "evaluationTime": 0.000262616, + "lastEvaluation": "2024-12-18T17:50:01.483135426Z" + }, + { + "name": "example", + "file": "/etc/prometheus/rules/rec.yaml", + "rules": [ + { + "name": "rule:prometheus_http_requests_total:sum", + "query": "sum by (code) (rate(prometheus_http_requests_total[5m]))", + "health": "ok", + "evaluationTime": 0.000472562, + "lastEvaluation": "2024-12-18T17:50:12.420737469Z", + "type": "recording" + } + ], + "interval": 15, + "limit": 0, + "evaluationTime": 0.000497618, + "lastEvaluation": "2024-12-18T17:50:12.42071533Z" + } + ], + "groupNextToken:omitempty": "" + } + } + `) + tests := []AlertTest{ { name: "alert-none", @@ -71,7 +290,7 @@ func TestAlertCmd(t *testing.T) { name: "alert-default", server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) - w.Write([]byte(`{"status":"success","data":{"groups":[{"name":"Foo","file":"alerts.yaml","rules":[{"state":"inactive","name":"HostOutOfMemory","query":"up","duration":120,"labels":{"severity":"critical"},"annotations":{"description":"Foo","summary":"Foo"},"alerts":[],"health":"ok","evaluationTime":0.000553928,"lastEvaluation":"2022-11-24T14:08:17.597083058Z","type":"alerting"}],"interval":10,"limit":0,"evaluationTime":0.000581212,"lastEvaluation":"2022-11-24T14:08:17.59706083Z"},{"name":"SQL","file":"alerts.yaml","rules":[{"state":"pending","name":"SqlAccessDeniedRate","query":"mysql","duration":17280000,"labels":{"severity":"warning"},"annotations":{"description":"MySQL","summary":"MySQL"},"alerts":[{"labels":{"alertname":"SqlAccessDeniedRate","instance":"localhost","job":"mysql","severity":"warning"},"annotations":{"description":"MySQL","summary":"MySQL"},"state":"pending","activeAt":"2022-11-21T10:38:35.373483748Z","value":"4.03448275862069e-01"}],"health":"ok","evaluationTime":0.002909617,"lastEvaluation":"2022-11-24T14:08:25.375220595Z","type":"alerting"}],"interval":10,"limit":0,"evaluationTime":0.003046259,"lastEvaluation":"2022-11-24T14:08:25.375096825Z"},{"name":"TLS","file":"alerts.yaml","rules":[{"state":"firing","name":"BlackboxTLS","query":"SSL","duration":0,"labels":{"severity":"critical"},"annotations":{"description":"TLS","summary":"TLS"},"alerts":[{"labels":{"alertname":"TLS","instance":"https://localhost:443","job":"blackbox","severity":"critical"},"annotations":{"description":"TLS","summary":"TLS"},"state":"firing","activeAt":"2022-11-24T05:11:27.211699259Z","value":"-6.065338210999966e+06"}],"health":"ok","evaluationTime":0.000713955,"lastEvaluation":"2022-11-24T14:08:17.212720815Z","type":"alerting"}],"interval":10,"limit":0,"evaluationTime":0.000738927,"lastEvaluation":"2022-11-24T14:08:17.212700182Z"}]}}`)) + w.Write([]byte(alertTestDataSet1)) })), args: []string{"run", "../main.go", "alert"}, expected: `[CRITICAL] - 3 Alerts: 1 Firing - 1 Pending - 1 Inactive @@ -87,7 +306,7 @@ exit status 2 name: "alert-problems-only", server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) - w.Write([]byte(`{"status":"success","data":{"groups":[{"name":"Foo","file":"alerts.yaml","rules":[{"state":"inactive","name":"HostOutOfMemory","query":"up","duration":120,"labels":{"severity":"critical"},"annotations":{"description":"Foo","summary":"Foo"},"alerts":[],"health":"ok","evaluationTime":0.000553928,"lastEvaluation":"2022-11-24T14:08:17.597083058Z","type":"alerting"}],"interval":10,"limit":0,"evaluationTime":0.000581212,"lastEvaluation":"2022-11-24T14:08:17.59706083Z"},{"name":"SQL","file":"alerts.yaml","rules":[{"state":"pending","name":"SqlAccessDeniedRate","query":"mysql","duration":17280000,"labels":{"severity":"warning"},"annotations":{"description":"MySQL","summary":"MySQL"},"alerts":[{"labels":{"alertname":"SqlAccessDeniedRate","instance":"localhost","job":"mysql","severity":"warning"},"annotations":{"description":"MySQL","summary":"MySQL"},"state":"pending","activeAt":"2022-11-21T10:38:35.373483748Z","value":"4.03448275862069e-01"}],"health":"ok","evaluationTime":0.002909617,"lastEvaluation":"2022-11-24T14:08:25.375220595Z","type":"alerting"}],"interval":10,"limit":0,"evaluationTime":0.003046259,"lastEvaluation":"2022-11-24T14:08:25.375096825Z"},{"name":"TLS","file":"alerts.yaml","rules":[{"state":"firing","name":"BlackboxTLS","query":"SSL","duration":0,"labels":{"severity":"critical"},"annotations":{"description":"TLS","summary":"TLS"},"alerts":[{"labels":{"alertname":"TLS","instance":"https://localhost:443","job":"blackbox","severity":"critical"},"annotations":{"description":"TLS","summary":"TLS"},"state":"firing","activeAt":"2022-11-24T05:11:27.211699259Z","value":"-6.065338210999966e+06"}],"health":"ok","evaluationTime":0.000713955,"lastEvaluation":"2022-11-24T14:08:17.212720815Z","type":"alerting"}],"interval":10,"limit":0,"evaluationTime":0.000738927,"lastEvaluation":"2022-11-24T14:08:17.212700182Z"}]}}`)) + w.Write([]byte(alertTestDataSet1)) })), args: []string{"run", "../main.go", "alert", "--problems"}, expected: `[CRITICAL] - 2 Alerts: 1 Firing - 1 Pending - 0 Inactive @@ -95,6 +314,34 @@ exit status 2 \_ [CRITICAL] [BlackboxTLS] - Job: [blackbox] on Instance: [https://localhost:443] is firing - value: -6065338.00 |total=2 firing=1 pending=1 inactive=0 +exit status 2 +`, + }, + { + name: "alert-problems-only-with-exlude-on-one-group", + server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte(alertTestDataSet1)) + })), + args: []string{"run", "../main.go", "alert", "--problems", "-g", "TLS"}, + expected: `[CRITICAL] - 1 Alerts: 1 Firing - 0 Pending - 0 Inactive +\_ [CRITICAL] [BlackboxTLS] - Job: [blackbox] on Instance: [https://localhost:443] is firing - value: -6065338.00 + +exit status 2 +`, + }, + { + name: "alert-problems-only-with-exlude-on-two-groups", + server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte(alertTestDataSet1)) + })), + args: []string{"run", "../main.go", "alert", "--problems", "-g", "SQL", "-g", "TLS"}, + expected: `[CRITICAL] - 2 Alerts: 1 Firing - 1 Pending - 0 Inactive +\_ [WARNING] [SqlAccessDeniedRate] - Job: [mysql] on Instance: [localhost] is pending - value: 0.40 +\_ [CRITICAL] [BlackboxTLS] - Job: [blackbox] on Instance: [https://localhost:443] is firing - value: -6065338.00 +|total=2 firing=1 pending=1 inactive=0 + exit status 2 `, }, @@ -102,7 +349,7 @@ exit status 2 name: "alert-problems-only-with-exlude", server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) - w.Write([]byte(`{"status":"success","data":{"groups":[{"name":"Foo","file":"alerts.yaml","rules":[{"state":"inactive","name":"HostOutOfMemory","query":"up","duration":120,"labels":{"severity":"critical"},"annotations":{"description":"Foo","summary":"Foo"},"alerts":[],"health":"ok","evaluationTime":0.000553928,"lastEvaluation":"2022-11-24T14:08:17.597083058Z","type":"alerting"}],"interval":10,"limit":0,"evaluationTime":0.000581212,"lastEvaluation":"2022-11-24T14:08:17.59706083Z"},{"name":"SQL","file":"alerts.yaml","rules":[{"state":"pending","name":"SqlAccessDeniedRate","query":"mysql","duration":17280000,"labels":{"severity":"warning"},"annotations":{"description":"MySQL","summary":"MySQL"},"alerts":[{"labels":{"alertname":"SqlAccessDeniedRate","instance":"localhost","job":"mysql","severity":"warning"},"annotations":{"description":"MySQL","summary":"MySQL"},"state":"pending","activeAt":"2022-11-21T10:38:35.373483748Z","value":"4.03448275862069e-01"}],"health":"ok","evaluationTime":0.002909617,"lastEvaluation":"2022-11-24T14:08:25.375220595Z","type":"alerting"}],"interval":10,"limit":0,"evaluationTime":0.003046259,"lastEvaluation":"2022-11-24T14:08:25.375096825Z"},{"name":"TLS","file":"alerts.yaml","rules":[{"state":"firing","name":"BlackboxTLS","query":"SSL","duration":0,"labels":{"severity":"critical"},"annotations":{"description":"TLS","summary":"TLS"},"alerts":[{"labels":{"alertname":"TLS","instance":"https://localhost:443","job":"blackbox","severity":"critical"},"annotations":{"description":"TLS","summary":"TLS"},"state":"firing","activeAt":"2022-11-24T05:11:27.211699259Z","value":"-6.065338210999966e+06"}],"health":"ok","evaluationTime":0.000713955,"lastEvaluation":"2022-11-24T14:08:17.212720815Z","type":"alerting"}],"interval":10,"limit":0,"evaluationTime":0.000738927,"lastEvaluation":"2022-11-24T14:08:17.212700182Z"}]}}`)) + w.Write([]byte(alertTestDataSet1)) })), args: []string{"run", "../main.go", "alert", "--problems", "--exclude-alert", "Sql.*DeniedRate"}, expected: `[CRITICAL] - 1 Alerts: 1 Firing - 0 Pending - 0 Inactive @@ -115,7 +362,7 @@ exit status 2 name: "alert-with-exclude-error", server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) - w.Write([]byte(`{"status":"success","data":{"groups":[{"name":"k8s","file":"/etc/prometheus/rules/al.yaml","rules":[{"state":"inactive","name":"NodeHasMemoryPressure","query":"kube_node{condition=\"MemoryPressure\",status=\"true\"} == 1","duration":300,"keepFiringFor":0,"labels":{},"annotations":{"summary":"Memory pressure on instance {{ $labels.instance }}"},"alerts":[],"health":"ok","evaluationTime":0.00023339,"lastEvaluation":"2024-12-18T17:50:01.483161228Z","type":"alerting"}],"interval":15,"limit":0,"evaluationTime":0.000262616,"lastEvaluation":"2024-12-18T17:50:01.483135426Z"},{"name":"example","file":"/etc/prometheus/rules/rec.yaml","rules":[{"name":"rule:prometheus_http_requests_total:sum","query":"sum by (code) (rate(prometheus_http_requests_total[5m]))","health":"ok","evaluationTime":0.000472562,"lastEvaluation":"2024-12-18T17:50:12.420737469Z","type":"recording"}],"interval":15,"limit":0,"evaluationTime":0.000497618,"lastEvaluation":"2024-12-18T17:50:12.42071533Z"}],"groupNextToken:omitempty":""}}`)) + w.Write([]byte(alertTestDataSet3)) })), args: []string{"run", "../main.go", "alert", "--exclude-alert", "[a-z"}, expected: "[UNKNOWN] - Invalid regular expression provided: error parsing regexp: missing closing ]: `[a-z`\nexit status 3\n", @@ -124,7 +371,7 @@ exit status 2 name: "alert-no-such-alert", server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) - w.Write([]byte(`{"status":"success","data":{"groups":[{"name":"Foo","file":"alerts.yaml","rules":[{"state":"inactive","name":"InactiveAlert","query":"foo","duration":120,"labels":{"severity":"critical"},"annotations":{"description":"Inactive","summary":"Inactive"},"alerts":[],"health":"ok","evaluationTime":0.000462382,"lastEvaluation":"2022-11-18T14:01:07.597034323Z","type":"alerting"}],"interval":10,"limit":0,"evaluationTime":0.000478395,"lastEvaluation":"2022-11-18T14:01:07.597021953Z"}]}}`)) + w.Write([]byte(alertTestDataSet2)) })), args: []string{"run", "../main.go", "alert", "--name", "NoSuchAlert"}, expected: "[UNKNOWN] - 0 Alerts: 0 Firing - 0 Pending - 0 Inactive\n\nexit status 3\n", @@ -133,7 +380,7 @@ exit status 2 name: "alert-inactive-with-problems", server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) - w.Write([]byte(`{"status":"success","data":{"groups":[{"name":"Foo","file":"alerts.yaml","rules":[{"state":"inactive","name":"InactiveAlert","query":"foo","duration":120,"labels":{"severity":"critical"},"annotations":{"description":"Inactive","summary":"Inactive"},"alerts":[],"health":"ok","evaluationTime":0.000462382,"lastEvaluation":"2022-11-18T14:01:07.597034323Z","type":"alerting"}],"interval":10,"limit":0,"evaluationTime":0.000478395,"lastEvaluation":"2022-11-18T14:01:07.597021953Z"}]}}`)) + w.Write([]byte(alertTestDataSet2)) })), args: []string{"run", "../main.go", "alert", "--name", "InactiveAlert", "--problems"}, expected: "[UNKNOWN] - 0 Alerts: 0 Firing - 0 Pending - 0 Inactive\n\nexit status 3\n", @@ -142,7 +389,7 @@ exit status 2 name: "alert-multiple-alerts", server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) - w.Write([]byte(`{"status":"success","data":{"groups":[{"name":"Foo","file":"alerts.yaml","rules":[{"state":"inactive","name":"HostOutOfMemory","query":"up","duration":120,"labels":{"severity":"critical"},"annotations":{"description":"Foo","summary":"Foo"},"alerts":[],"health":"ok","evaluationTime":0.000553928,"lastEvaluation":"2022-11-24T14:08:17.597083058Z","type":"alerting"}],"interval":10,"limit":0,"evaluationTime":0.000581212,"lastEvaluation":"2022-11-24T14:08:17.59706083Z"},{"name":"SQL","file":"alerts.yaml","rules":[{"state":"pending","name":"SqlAccessDeniedRate","query":"mysql","duration":17280000,"labels":{"severity":"warning"},"annotations":{"description":"MySQL","summary":"MySQL"},"alerts":[{"labels":{"alertname":"SqlAccessDeniedRate","instance":"localhost","job":"mysql","severity":"warning"},"annotations":{"description":"MySQL","summary":"MySQL"},"state":"pending","activeAt":"2022-11-21T10:38:35.373483748Z","value":"4.03448275862069e-01"}],"health":"ok","evaluationTime":0.002909617,"lastEvaluation":"2022-11-24T14:08:25.375220595Z","type":"alerting"}],"interval":10,"limit":0,"evaluationTime":0.003046259,"lastEvaluation":"2022-11-24T14:08:25.375096825Z"},{"name":"TLS","file":"alerts.yaml","rules":[{"state":"firing","name":"BlackboxTLS","query":"SSL","duration":0,"labels":{"severity":"critical"},"annotations":{"description":"TLS","summary":"TLS"},"alerts":[{"labels":{"alertname":"TLS","instance":"https://localhost:443","job":"blackbox","severity":"critical"},"annotations":{"description":"TLS","summary":"TLS"},"state":"firing","activeAt":"2022-11-24T05:11:27.211699259Z","value":"-6.065338210999966e+06"}],"health":"ok","evaluationTime":0.000713955,"lastEvaluation":"2022-11-24T14:08:17.212720815Z","type":"alerting"}],"interval":10,"limit":0,"evaluationTime":0.000738927,"lastEvaluation":"2022-11-24T14:08:17.212700182Z"}]}}`)) + w.Write([]byte(alertTestDataSet1)) })), args: []string{"run", "../main.go", "alert", "--name", "HostOutOfMemory", "--name", "BlackboxTLS"}, expected: `[CRITICAL] - 2 Alerts: 1 Firing - 0 Pending - 1 Inactive @@ -157,7 +404,7 @@ exit status 2 name: "alert-multiple-alerts-problems-only", server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) - w.Write([]byte(`{"status":"success","data":{"groups":[{"name":"Foo","file":"alerts.yaml","rules":[{"state":"inactive","name":"HostOutOfMemory","query":"up","duration":120,"labels":{"severity":"critical"},"annotations":{"description":"Foo","summary":"Foo"},"alerts":[],"health":"ok","evaluationTime":0.000553928,"lastEvaluation":"2022-11-24T14:08:17.597083058Z","type":"alerting"}],"interval":10,"limit":0,"evaluationTime":0.000581212,"lastEvaluation":"2022-11-24T14:08:17.59706083Z"},{"name":"SQL","file":"alerts.yaml","rules":[{"state":"pending","name":"SqlAccessDeniedRate","query":"mysql","duration":17280000,"labels":{"severity":"warning"},"annotations":{"description":"MySQL","summary":"MySQL"},"alerts":[{"labels":{"alertname":"SqlAccessDeniedRate","instance":"localhost","job":"mysql","severity":"warning"},"annotations":{"description":"MySQL","summary":"MySQL"},"state":"pending","activeAt":"2022-11-21T10:38:35.373483748Z","value":"4.03448275862069e-01"}],"health":"ok","evaluationTime":0.002909617,"lastEvaluation":"2022-11-24T14:08:25.375220595Z","type":"alerting"}],"interval":10,"limit":0,"evaluationTime":0.003046259,"lastEvaluation":"2022-11-24T14:08:25.375096825Z"},{"name":"TLS","file":"alerts.yaml","rules":[{"state":"firing","name":"BlackboxTLS","query":"SSL","duration":0,"labels":{"severity":"critical"},"annotations":{"description":"TLS","summary":"TLS"},"alerts":[{"labels":{"alertname":"TLS","instance":"https://localhost:443","job":"blackbox","severity":"critical"},"annotations":{"description":"TLS","summary":"TLS"},"state":"firing","activeAt":"2022-11-24T05:11:27.211699259Z","value":"-6.065338210999966e+06"}],"health":"ok","evaluationTime":0.000713955,"lastEvaluation":"2022-11-24T14:08:17.212720815Z","type":"alerting"}],"interval":10,"limit":0,"evaluationTime":0.000738927,"lastEvaluation":"2022-11-24T14:08:17.212700182Z"}]}}`)) + w.Write([]byte(alertTestDataSet1)) })), args: []string{"run", "../main.go", "alert", "--name", "HostOutOfMemory", "--name", "BlackboxTLS", "--problems"}, expected: `[CRITICAL] - 1 Alerts: 1 Firing - 0 Pending - 0 Inactive @@ -171,7 +418,7 @@ exit status 2 name: "alert-inactive", server: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) - w.Write([]byte(`{"status":"success","data":{"groups":[{"name":"Foo","file":"alerts.yaml","rules":[{"state":"inactive","name":"InactiveAlert","query":"foo","duration":120,"labels":{"severity":"critical"},"annotations":{"description":"Inactive","summary":"Inactive"},"alerts":[],"health":"ok","evaluationTime":0.000462382,"lastEvaluation":"2022-11-18T14:01:07.597034323Z","type":"alerting"}],"interval":10,"limit":0,"evaluationTime":0.000478395,"lastEvaluation":"2022-11-18T14:01:07.597021953Z"}]}}`)) + w.Write([]byte(alertTestDataSet2)) })), args: []string{"run", "../main.go", "alert", "--name", "InactiveAlert"}, expected: "[OK] - 1 Alerts: 0 Firing - 0 Pending - 1 Inactive\n\\_ [OK] [InactiveAlert] is inactive\n|firing=0 pending=0 inactive=1\n\n", diff --git a/internal/alert/alert.go b/internal/alert/alert.go index 097a705..af8a687 100644 --- a/internal/alert/alert.go +++ b/internal/alert/alert.go @@ -2,6 +2,7 @@ package alert import ( "fmt" + "slices" "strconv" "strings" @@ -17,13 +18,19 @@ type Rule struct { Alert *v1.Alert } -func FlattenRules(groups []v1.RuleGroup) []Rule { +func FlattenRules(groups []v1.RuleGroup, wantedGroups []string) []Rule { // Flattens a list of RuleGroup containing a list of Rules into // a list of internal Alertingrules. var l int // Set initial capacity to reduce memory allocations. for _, grp := range groups { - l += len(grp.Rules) + if wantedGroups != nil { + if slices.Contains(wantedGroups, grp.Name) { + l += len(grp.Rules) + } + } else { + l += len(grp.Rules) + } } rules := make([]Rule, 0, l) @@ -31,12 +38,25 @@ func FlattenRules(groups []v1.RuleGroup) []Rule { var r Rule for _, grp := range groups { - for _, rl := range grp.Rules { - // For now we only care about AlertingRules, - // since RecodingRules can simply be queried. - if _, ok := rl.(v1.AlertingRule); ok { - r.AlertingRule = rl.(v1.AlertingRule) - rules = append(rules, r) + if wantedGroups != nil { + if slices.Contains(wantedGroups, grp.Name) { + for _, rl := range grp.Rules { + // For now we only care about AlertingRules, + // since RecodingRules can simply be queried. + if _, ok := rl.(v1.AlertingRule); ok { + r.AlertingRule = rl.(v1.AlertingRule) + rules = append(rules, r) + } + } + } + } else { + for _, rl := range grp.Rules { + // For now we only care about AlertingRules, + // since RecodingRules can simply be queried. + if _, ok := rl.(v1.AlertingRule); ok { + r.AlertingRule = rl.(v1.AlertingRule) + rules = append(rules, r) + } } } } diff --git a/internal/alert/alert_test.go b/internal/alert/alert_test.go index 697da24..d8a2bdd 100644 --- a/internal/alert/alert_test.go +++ b/internal/alert/alert_test.go @@ -179,7 +179,7 @@ func TestFlattenRules(t *testing.T) { }, } - fr := FlattenRules(rg) + fr := FlattenRules(rg, nil) if len(fr) != 1 { t.Error("\nActual: ", fr) } From 6177d23f73dcd32434b8ff1421e6250f05929b9f Mon Sep 17 00:00:00 2001 From: Jan Scheufler Date: Mon, 23 Dec 2024 17:01:34 +0100 Subject: [PATCH 5/5] alert.go - reduce code redundancy and simplify FlattenRules --- internal/alert/alert.go | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/internal/alert/alert.go b/internal/alert/alert.go index af8a687..589c998 100644 --- a/internal/alert/alert.go +++ b/internal/alert/alert.go @@ -25,12 +25,12 @@ func FlattenRules(groups []v1.RuleGroup, wantedGroups []string) []Rule { // Set initial capacity to reduce memory allocations. for _, grp := range groups { if wantedGroups != nil { - if slices.Contains(wantedGroups, grp.Name) { - l += len(grp.Rules) + if !slices.Contains(wantedGroups, grp.Name) { + continue } - } else { - l += len(grp.Rules) } + + l += len(grp.Rules) } rules := make([]Rule, 0, l) @@ -39,24 +39,17 @@ func FlattenRules(groups []v1.RuleGroup, wantedGroups []string) []Rule { for _, grp := range groups { if wantedGroups != nil { - if slices.Contains(wantedGroups, grp.Name) { - for _, rl := range grp.Rules { - // For now we only care about AlertingRules, - // since RecodingRules can simply be queried. - if _, ok := rl.(v1.AlertingRule); ok { - r.AlertingRule = rl.(v1.AlertingRule) - rules = append(rules, r) - } - } + if !slices.Contains(wantedGroups, grp.Name) { + continue } - } else { - for _, rl := range grp.Rules { - // For now we only care about AlertingRules, - // since RecodingRules can simply be queried. - if _, ok := rl.(v1.AlertingRule); ok { - r.AlertingRule = rl.(v1.AlertingRule) - rules = append(rules, r) - } + } + + for _, rl := range grp.Rules { + // For now we only care about AlertingRules, + // since RecodingRules can simply be queried. + if _, ok := rl.(v1.AlertingRule); ok { + r.AlertingRule = rl.(v1.AlertingRule) + rules = append(rules, r) } } }