feat: add silence enforcement rules (v1alpha2) - #698
Conversation
|
The overall direction does make some sense, and I understand the needs to be able to enforce some specific Silence matchers. I would suggest to give users the choice to add or not the specific namespace matcher. Instead I would suggest not to default to "namespace" when the 'matcherLabel` is empty, and rather not add it. An example of this would be
apiVersion: observability.giantswarm.io/v1alpha2
kind: Silence
metadata:
name: my-test
namespace: my-namespace # this namespace is labeled with team=platform
spec:
matchers:
- matchType: =
name: foo
value: bar
namespaceEnforcement:
matcherLabel: "" # Can also be omitted
rules:
- namespaceSelector:
matchLabels:
tenant-isolation: "enabled"
- namespaceSelector:
matchExpressions:
- key: team
operator: In
values: [platform]
matchers:
- name: cluster_id
value: prod
matchType: "="
apiVersion: observability.giantswarm.io/v1alpha2
kind: Silence
metadata:
name: my-test
namespace: my-namespace
spec:
matchers:
- matchType: =
name: foo
value: bar
- matchType: =
name: cluster_id
value: prodIf going with this then some renaming might be good, example:
|
|
Thanks for the review, @TheoBrigitte — I agree, as I am mainly in Kubernetes world, I'm probably biased a bit ;) I've reworked it to be generic, with namespace scoping strictly opt-in.
Renaming (as suggested)
|
Add a selector-gated, off-by-default multi-tenancy control for the observability.giantswarm.io/v1alpha2 API. When one or more rules are configured, a Silence created in a namespace matching a rule's namespaceSelector gets an authoritative "namespace=<namespace>" matcher (plus any custom matchers from the rule) injected into the Alertmanager silence, so it can only mute alerts from its own namespace. - pkg/enforce: config loader (mounted YAML) + first-match-wins rule evaluation and override-on-collision matcher application - v2 controller: apply enforcement at reconcile time (fail-closed on namespace lookup), emit a MatcherOverridden Event when a user matcher is overridden - alertmanager.NewMatcher: shared match-type conversion helper - Helm: namespaceEnforcement values, ConfigMap, volume mount, checksum rollout; RBAC read access to namespaces (also fixes the pre-existing gap for the namespaceSelector predicate) - Unit + envtest integration tests; README documentation Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The enforcement config loader imports sigs.k8s.io/yaml directly, so `go mod tidy` moves it out of the indirect block. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address review feedback: Alertmanager has no notion of "namespace", so the namespace matcher should not be hardwired. Make it opt-in and generalize the feature to arbitrary enforced matchers. - namespaceMatcherLabel (was matcherLabel): empty now means no namespace matcher is injected instead of defaulting to "namespace"; a matching rule then only injects its own matchers - rename config: namespaceEnforcement -> enforcementRules, flag --namespace-enforcement-config -> --enforcement-config, and the ConfigMap / volume / mount path accordingly - warn at load time for a rule that injects nothing - generalize log/Event wording (no longer namespace-specific) - update Helm values/templates, README, and unit + envtest tests; add cases covering the empty-label (no namespace matcher) behavior Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ca5831e to
c401409
Compare
|
Sounds good, can you please have a look at this comment also 5249e92#r3664473297 |
sure, but 🤔 i'm not seeing any comments there? what was it? |
TheoBrigitte
left a comment
There was a problem hiding this comment.
Sorry I forgot to publish it actually.
| // Match type strings understood by NewMatcher. These mirror the values of | ||
| // v1alpha2.MatchType so that both CR conversion and the enforcement config | ||
| // loader can share a single conversion routine. | ||
| const ( | ||
| MatchTypeEqual = "=" | ||
| MatchTypeNotEqual = "!=" | ||
| MatchTypeRegexMatch = "=~" | ||
| MatchTypeRegexNotMatch = "!~" | ||
| ) |
There was a problem hiding this comment.
I would rather keep the v1alpha2.MatchType instead of using redefining those variables. Unless I missed something this routine is not shared with the previous CR version.
There was a problem hiding this comment.
Good catch, thanks — you're right, that routine is only used by the v1alpha2 path (v2 controller + enforcement loader), the v1alpha1 controller builds matchers from its own bool fields. I've dropped the redefined constants and NewMatcher now takes a v1alpha2.MatchType directly, switching on the existing v1alpha2.Match* values. Pushed in 24720ba.
|
I've merge the main branch into your PR in order to fix the CI pipeline. |
Drop the duplicated match-type string constants in the alertmanager package and have NewMatcher take a v1alpha2.MatchType directly. NewMatcher is only used by the v1alpha2 path (v2 controller + enforcement loader), not the v1alpha1 controller, so there is no need for version-neutral constants. Addresses review feedback from @TheoBrigitte. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
This looks about right to me. I'll give it a test run soon. |
What
Adds a selector-gated, off-by-default multi-tenancy control for the
observability.giantswarm.io/v1alpha2API. When one or more rules are configured,a
Silencecreated in a namespace matching a rule'snamespaceSelectorgets anauthoritative
namespace="<namespace>"matcher (plus any custom matchers from therule) injected into the Alertmanager silence — so a silence can only ever mute
alerts belonging to its own namespace.
Why
By default a
Silencein one namespace can mute alerts from any namespace, sincea silence is just a set of Alertmanager matchers. This provides lightweight
namespace isolation for shared-Alertmanager, multi-tenant setups.
How it works
--namespace-enforcement-config), rendered byHelm from
namespaceEnforcementvalues into a ConfigMap.metav1.LabelSelector. A namespace is enforced if it matchesany rule (OR). Empty
rules⇒ no enforcement (the default).matchers apply.
overridden, logged, and surfaced as a
MatcherOverriddenKubernetes Event on theSilence.the silence is not synced.
Example