keel: use +() anchors on policy/match-tag so per-workload overrides stick

Without the anchor, each policy update fires mutateExistingOnPolicyUpdate,
which OVERWRITES existing keel.sh/policy annotations back to 'force'. That
broke the phased rollout — bulk-setting workloads to 'never' didn't stick
because the next policy update reset them.

With +() anchors, the mutate only adds the annotation if missing. New
workloads (in enrolled namespaces) get force+match-tag; existing workloads
with explicit policy=never (out-of-band, for phased rollout) stay never.

Phase 1 rollout state (2026-05-17):
  - 10 workloads on force+match-tag in 10 namespaces (Phase 1)
    enrolled via keel.sh/enrolled=true namespace label:
      linkwarden, excalidraw, diun, echo, foolery, city-guesser,
      jsoncrack, privatebin, ntfy, speedtest
  - 216 workloads on policy=never (out-of-band kubectl annotate)
  - 31 critical namespaces excluded at policy level

Expand to Phase 2 by labeling more namespaces `keel.sh/enrolled=true`
and clearing the `never` annotation off their workloads.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Viktor Barzin 2026-05-17 00:32:06 +00:00
parent 05473ab501
commit 32db6760cc

View file

@ -165,8 +165,13 @@ resource "kubernetes_manifest" "policy_inject_keel_annotations" {
# to bypass this mutation)
# Per-namespace opt-out:
# Remove the `keel.sh/enrolled=true` namespace label.
"keel.sh/policy" = "force"
"keel.sh/match-tag" = "true"
# `+(...)` anchor only add if not present. This preserves
# per-workload overrides set out-of-band (e.g. `never` for
# phased rollout). Without the anchor, every policy update
# would overwrite existing annotations, breaking the phased
# rollout state.
"+(keel.sh/policy)" = "force"
"+(keel.sh/match-tag)" = "true"
"+(keel.sh/trigger)" = "poll"
"+(keel.sh/pollSchedule)" = "@every 1h"
}