Repository navigation
feat: per-peer / per-instance routeLiveness (cascade over cluster default) - #5
Merged
Merged
Conversation
…Peer (per-peer) — enum disabled|passive|active
…r default); controller branches IsLive per mode, probes only active peers, gates only gated peers
…o KRO can render ""=inherit; agent validates
mxhob1
added a commit
that referenced
this pull request
Jun 6, 2026
Routes declared on a WireguardPeer (spec.routes/routesV6) are installed
unconditionally today. This adds an agent-side liveness gate so a peer's
routes are withdrawn from both AllowedIPs and the kernel route table when the
peer goes unreachable, and re-attached when it recovers — letting cluster→
tunnel traffic fail over to a broader, still-live peer (longest-prefix).
Modes (cascade: per-peer spec.routeLiveness > per-instance spec.routeLiveness
> cluster default WG_ROUTE_LIVENESS env):
- disabled — routes always installed (current static behaviour; default)
- passive — withdraw when inbound goes silent (ReceiveBytes/handshake stall
over a per-peer N*keepalive window)
- active — passive + a /32 UDP handshake probe; down after N unanswered,
revived on progress
Gated peers start dead until confirmed reachable, so a broader live peer wins
the longest-prefix match during the initial bring-up window rather than
blackholing through a peer that hasn't completed a handshake yet.
Design notes:
- The /32 peer address is always kept in AllowedIPs; only spec.routes are
gated, so the control channel to the peer never drops.
- New env knobs are delivered via the agent deployment template (operator env
passthrough), not the CRD: WG_ROUTE_LIVENESS, WG_ROUTE_FAILURE_COUNT,
WG_ROUTE_CHECK_INTERVAL, WG_ROUTE_PROBE_INTERVAL. Unset ⇒ disabled ⇒
byte-identical to current behaviour.
- The controller reconciles these WG_ROUTE_* env vars onto existing agent
Deployments (by name), so a Deployment created before the setting was
applied (or before it changed) picks it up instead of staying inert; no
churn once they match.
- spec.routeLiveness is a plain optional string (not apiserver-enum) so a
renderer can always emit ""=inherit; the agent validates (unknown ⇒
inherit, fail-safe).
- Adds a wireguard_peer_routes_active gauge + transition logging.
Squashed from internal PRs #4 (core), #5 (per-peer/instance cascade), #6
(start-dead) and #7 (reconcile env on existing Deployments). Depends on the
peer Routes/RoutesV6 feature.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #4. Makes liveness gating selectable at three levels with a cascade — peer > instance > cluster-default — so it can be enabled surgically instead of fleet-wide.
CRD
Wireguard.spec.routeLiveness(instance default) andWireguardPeer.spec.routeLiveness(per-peer), enumdisabled|passive|active, optional. CRDs regenerated.Agent
{peer, instance}else the cluster default (WG_ROUTE_LIVENESSenv). Empty = inherit; an explicitdisabledoverrides downward (e.g. a fallback gateway forced always-on under anactiveinstance).activepeers, andIsLivebranches per mode (disabled→always-live/ungated,passive→stall,active→stall+probe). Only gated peers drive transition→wg.Sync.disabled); when everything resolves todisabledit's a cheap no-op read loop → routes static → byte-identical to current behavior.Tests
Cascade resolution, per-peer mode resolution + ungated fallback, gated-only transitions, active probe/revive.
go build/vet/golangci-lint-fullgreen; non-envtest unit packages pass (controller envtest + e2e in CI).🤖 Generated with Claude Code