diff --git a/pkg/testutil/alert_custom_validations.go b/pkg/testutil/alert_custom_validations.go new file mode 100644 index 0000000..073eced --- /dev/null +++ b/pkg/testutil/alert_custom_validations.go @@ -0,0 +1,72 @@ +package testutil + +import promv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + +func ValidateAlertNameLength(alert *promv1.Rule) []Problem { + var result []Problem + + if len(alert.Alert) > 50 { + result = append(result, Problem{ + ResourceName: alert.Alert, + Description: "alert name exceeds 50 characters", + }) + } + + return result +} + +func ValidateAlertRunbookURLAnnotation(alert *promv1.Rule) []Problem { + var result []Problem + + runbookURL := alert.Annotations["runbook_url"] + if runbookURL == "" { + result = append(result, Problem{ + ResourceName: alert.Alert, + Description: "alert must have a runbook_url annotation", + }) + } + + return result +} + +func ValidateHealthImpactLabel(alert *promv1.Rule) []Problem { + var result []Problem + + healthImpact := alert.Labels["operator_health_impact"] + if healthImpact == "" { + result = append(result, Problem{ + ResourceName: alert.Alert, + Description: "alert must have a operator_health_impact label", + }) + } + + return result +} + +func ValidateAlertPartOfLabel(alert *promv1.Rule) []Problem { + var result []Problem + + partOf := alert.Labels["kubernetes_operator_part_of"] + if partOf == "" { + result = append(result, Problem{ + ResourceName: alert.Alert, + Description: "alert must have a kubernetes_operator_part_of label", + }) + } + + return result +} + +func ValidateAlertComponentLabel(alert *promv1.Rule) []Problem { + var result []Problem + + partOf := alert.Labels["kubernetes_operator_component"] + if partOf == "" { + result = append(result, Problem{ + ResourceName: alert.Alert, + Description: "alert must have a kubernetes_operator_component label", + }) + } + + return result +} diff --git a/pkg/testutil/alert_validation_test.go b/pkg/testutil/alert_validation_test.go index e451c06..a5b1f16 100644 --- a/pkg/testutil/alert_validation_test.go +++ b/pkg/testutil/alert_validation_test.go @@ -16,7 +16,7 @@ var _ = Describe("Validators", func() { linter = New() }) - Context("Alert Validation", func() { + Context("Default alert Validations", func() { It("should validate alert with valid input", func() { alert := &promv1.Rule{ Alert: "ExampleAlert", @@ -116,4 +116,136 @@ var _ = Describe("Validators", func() { Expect(problems[0].Description).To(ContainSubstring("alert must have a description annotation")) }) }) + + Context("Custom alert Validations", func() { + It("should not return error if all custom validations were added and all exist", func() { + alert := &promv1.Rule{ + Alert: "ExampleAlert", + Expr: intstr.FromString("sum(rate(http_requests_total[5m]))"), + Labels: map[string]string{ + "severity": "critical", + "operator_health_impact": "example_health_impact", + "kubernetes_operator_part_of": "example_part_of", + "kubernetes_operator_component": "example_component", + }, + Annotations: map[string]string{ + "summary": "Example summary", + "description": "Example description", + "runbook_url": "example_runbook_url", + }, + } + linter.AddCustomAlertValidations(ValidateAlertNameLength, ValidateAlertRunbookURLAnnotation, + ValidateHealthImpactLabel, ValidateAlertPartOfLabel, ValidateAlertComponentLabel) + problems := linter.LintAlert(alert) + Expect(problems).To(BeEmpty()) + }) + + It("should return error if alert name length custom validation was added and alert name is too long", func() { + alert := &promv1.Rule{ + Alert: "ExampleAlertWithVeryLongNameExtendedToMeetFiftyOneLength", + Expr: intstr.FromString("sum(rate(http_requests_total[5m]))"), + Labels: map[string]string{ + "severity": "critical", + }, + Annotations: map[string]string{ + "summary": "Example summary", + "description": "Example description", + }, + } + linter.AddCustomAlertValidations(ValidateAlertNameLength) + problems := linter.LintAlert(alert) + Expect(problems).To(HaveLen(1)) + Expect(problems[0].Description).To(ContainSubstring("alert name exceeds 50 characters")) + }) + + It("should return error if runbook_url custom validation was added and is missing", func() { + alert := &promv1.Rule{ + Alert: "ExampleAlert", + Expr: intstr.FromString("sum(rate(http_requests_total[5m]))"), + Labels: map[string]string{ + "severity": "critical", + }, + Annotations: map[string]string{ + "summary": "Example summary", + "description": "Example description", + }, + } + linter.AddCustomAlertValidations(ValidateAlertRunbookURLAnnotation) + problems := linter.LintAlert(alert) + Expect(problems).To(HaveLen(1)) + Expect(problems[0].Description).To(ContainSubstring("alert must have a runbook_url annotation")) + }) + + It("should return error if kubernetes_operator_part_of custom validation was added and is missing", func() { + alert := &promv1.Rule{ + Alert: "ExampleAlert", + Expr: intstr.FromString("sum(rate(http_requests_total[5m]))"), + Labels: map[string]string{ + "severity": "critical", + }, + Annotations: map[string]string{ + "summary": "Example summary", + "description": "Example description", + }, + } + linter.AddCustomAlertValidations(ValidateAlertPartOfLabel) + problems := linter.LintAlert(alert) + Expect(problems).To(HaveLen(1)) + Expect(problems[0].Description).To(ContainSubstring("alert must have a kubernetes_operator_part_of label")) + }) + + It("should return error if operator_health_impact custom validation was added and is missing", func() { + alert := &promv1.Rule{ + Alert: "ExampleAlert", + Expr: intstr.FromString("sum(rate(http_requests_total[5m]))"), + Labels: map[string]string{ + "severity": "critical", + }, + Annotations: map[string]string{ + "summary": "Example summary", + "description": "Example description", + }, + } + linter.AddCustomAlertValidations(ValidateHealthImpactLabel) + problems := linter.LintAlert(alert) + Expect(problems).To(HaveLen(1)) + Expect(problems[0].Description).To(ContainSubstring("alert must have a operator_health_impact label")) + }) + + It("should return error if kubernetes_operator_component custom validation was added and is missing", func() { + alert := &promv1.Rule{ + Alert: "ExampleAlert", + Expr: intstr.FromString("sum(rate(http_requests_total[5m]))"), + Labels: map[string]string{ + "severity": "critical", + }, + Annotations: map[string]string{ + "summary": "Example summary", + "description": "Example description", + }, + } + linter.AddCustomAlertValidations(ValidateAlertComponentLabel) + problems := linter.LintAlert(alert) + Expect(problems).To(HaveLen(1)) + Expect(problems[0].Description).To(ContainSubstring("alert must have a kubernetes_operator_component label")) + }) + + It("should return error if kubernetes_operator_component custom validation was added and is missing", func() { + alert := &promv1.Rule{ + Alert: "ExampleAlert", + Expr: intstr.FromString("sum(rate(http_requests_total[5m]))"), + Labels: map[string]string{ + "severity": "critical", + }, + Annotations: map[string]string{ + "summary": "Example summary", + "description": "Example description", + }, + } + linter.AddCustomAlertValidations(ValidateAlertComponentLabel) + problems := linter.LintAlert(alert) + Expect(problems).To(HaveLen(1)) + Expect(problems[0].Description).To(ContainSubstring("alert must have a kubernetes_operator_component label")) + }) + }) })