Skip to content
Closed
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
16 changes: 16 additions & 0 deletions pkg/management/alert_rule_id_match.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package management

import (
alertrule "github.com/openshift/monitoring-plugin/pkg/alert_rule"
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
)

// ruleMatchesAlertRuleID returns true when the provided rule's computed, deterministic
// alert rule id matches the requested id.
//
// Note: we intentionally compute the id from the rule spec rather than trusting any
// label value, since labels can be user-controlled/tampered with.
func ruleMatchesAlertRuleID(rule monitoringv1.Rule, alertRuleId string) bool {
return alertRuleId != "" && alertRuleId == alertrule.GetAlertingRuleId(&rule)
}

98 changes: 98 additions & 0 deletions pkg/management/alert_rule_preconditions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package management

import (
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"

osmv1 "github.com/openshift/api/monitoring/v1"

"github.com/openshift/monitoring-plugin/pkg/k8s"
"github.com/openshift/monitoring-plugin/pkg/managementlabels"
)

// Standardized NotAllowed errors
func notAllowedGitOpsEdit() error {
return &NotAllowedError{Message: "This alert is managed by GitOps; edit it in Git."}
}
func notAllowedGitOpsRemove() error {
return &NotAllowedError{Message: "This alert is managed by GitOps; remove it in Git."}
}
func notAllowedOperatorUpdate() error {
return &NotAllowedError{Message: "This alert is managed by an operator; it can't be updated and can only be silenced."}
}
func notAllowedOperatorDelete() error {
return &NotAllowedError{Message: "This alert is managed by an operator; it can't be deleted and can only be silenced."}
}

// isRuleManagedByGitOpsLabel returns true if the relabeled rule indicates GitOps management via its managed-by label.
func isRuleManagedByGitOpsLabel(relabeled monitoringv1.Rule) bool {
if relabeled.Labels == nil {
return false
}
return relabeled.Labels[managementlabels.RuleManagedByLabel] == managementlabels.ManagedByGitOps
}

// isRuleManagedByOperator returns true if the relabeled rule indicates operator management via its managed-by label.
func isRuleManagedByOperator(relabeled monitoringv1.Rule) bool {
return relabeled.Labels != nil && relabeled.Labels[managementlabels.RuleManagedByLabel] == managementlabels.ManagedByOperator
}

// validateUserDeletePreconditions enforces common label-based constraints for user-source delete.
func validateUserDeletePreconditions(relabeled monitoringv1.Rule) error {
if isRuleManagedByGitOpsLabel(relabeled) {
return notAllowedGitOpsRemove()
}
if isRuleManagedByOperator(relabeled) {
return notAllowedOperatorDelete()
}
return nil
}

// validateUserUpdatePreconditions enforces common constraints for user-source update.
func validateUserUpdatePreconditions(relabeled monitoringv1.Rule, pr *monitoringv1.PrometheusRule) error {
if isRuleManagedByGitOpsLabel(relabeled) {
return notAllowedGitOpsEdit()
}
if isRuleManagedByOperator(relabeled) {
return notAllowedOperatorUpdate()
}
// Authoritative operator-managed check on PR owner references if provided
if pr != nil {
if _, operatorManaged := k8s.IsExternallyManagedObject(pr); operatorManaged {
return notAllowedOperatorUpdate()
}
}
return nil
}

// validatePlatformDeletePreconditions enforces constraints before mutating the owning AlertingRule.
func validatePlatformDeletePreconditions(ar *osmv1.AlertingRule) error {
// Block if owning AR is externally managed (GitOps or operator)
if ar != nil {
if gitOpsManaged, operatorManaged := k8s.IsExternallyManagedObject(ar); gitOpsManaged {
return notAllowedGitOpsRemove()
} else if operatorManaged {
return notAllowedOperatorDelete()
}
}
return nil
}

// validatePlatformUpdatePreconditions enforces constraints before ARC-based update.
// pr may be nil if not fetched yet; arc may be nil if absent.
func validatePlatformUpdatePreconditions(relabeled monitoringv1.Rule, pr *monitoringv1.PrometheusRule, arc *osmv1.AlertRelabelConfig) error {
// Rule-level GitOps block
if isRuleManagedByGitOpsLabel(relabeled) {
return notAllowedGitOpsEdit()
}
// PR metadata GitOps block
if pr != nil {
if gitOpsManaged, _ := k8s.IsExternallyManagedObject(pr); gitOpsManaged {
return notAllowedGitOpsEdit()
}
}
// ARC metadata GitOps block
if arc != nil && k8s.IsManagedByGitOps(arc.Annotations, arc.Labels) {
return notAllowedGitOpsEdit()
}
return nil
}
44 changes: 44 additions & 0 deletions pkg/management/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package management

import "fmt"

type NotFoundError struct {
Resource string
Id string

AdditionalInfo string
}

func (r *NotFoundError) Error() string {
s := fmt.Sprintf("%s with id %s not found", r.Resource, r.Id)

if r.AdditionalInfo != "" {
s += fmt.Sprintf(": %s", r.AdditionalInfo)
}

return s
}

type NotAllowedError struct {
Message string
}

func (r *NotAllowedError) Error() string {
return r.Message
}

type ValidationError struct {
Message string
}

func (e *ValidationError) Error() string {
return e.Message
}

type ConflictError struct {
Message string
}

func (e *ConflictError) Error() string {
return e.Message
}
29 changes: 29 additions & 0 deletions pkg/management/label_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package management

import (
"github.com/openshift/monitoring-plugin/pkg/k8s"
"github.com/openshift/monitoring-plugin/pkg/managementlabels"
)

// isProtectedLabel returns true for labels we will not modify via ARC for platform rules.
// These carry provenance or rule identity and must remain intact.
var protectedLabels = map[string]bool{
managementlabels.AlertNameLabel: true,
k8s.AlertRuleLabelId: true,
}

func isProtectedLabel(label string) bool {
return protectedLabels[label]
}

// isValidSeverity validates allowed severity values.
var validSeverities = map[string]bool{
"critical": true,
"warning": true,
"info": true,
"none": true,
}

func isValidSeverity(s string) bool {
return validSeverities[s]
}
15 changes: 15 additions & 0 deletions pkg/management/management.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package management

import (
"k8s.io/apimachinery/pkg/types"

"github.com/openshift/monitoring-plugin/pkg/k8s"
)

type client struct {
k8sClient k8s.Client
}

func (c *client) IsPlatformAlertRule(prId types.NamespacedName) bool {
return c.k8sClient.Namespace().IsClusterMonitoringNamespace(prId.Namespace)
}
Loading