Mirror update events were filtered out, so an in-place edit of a
mirrored copy was invisible to the controller. The periodic resync did
not catch it either: NeedsSync only compares the source content hash
with the recorded annotation, and tampered mirror data leaves both
unchanged.
Changes:
- The mirror watch now passes real update events (resourceVersion
changed, object is a managed mirror) and maps them to the source.
Informer resync replays keep an unchanged resourceVersion and stay
filtered.
- reconcileMirror detects in-place drift by rebuilding the expected
mirror in memory and comparing content hashes. Metadata differences
never count as drift, so the controller's own writes converge in one
no-op pass.
- The resync-period default moves from 10m to 1h. Watches now cover
source changes, mirror deletion, mirror drift, and namespace changes,
so the resync is only a safety net for missed watch events. The chart
default moves with it.
- Add predicate and tamper-repair regression tests; drift repair is
verified to make no direct apiserver reads.
The mirror reconcile path issued one direct apiserver GET per target
namespace on every pass. With N sources and M target namespaces each
resync produced N*M GET requests. On a 65-namespace cluster this
generated 63 GET/s and overloaded one control-plane node.
Changes:
- reconcileMirror now trusts the informer cache. Content drift is
detected via the source hash annotation. A stale resourceVersion
makes Update return a conflict, which requeues the source.
- The verify-source-freshness flag now defaults to false. The Helm
chart passes the value explicitly, so the chart default of false
matches the binary behaviour.
- Reconcile performs one namespace LIST per pass. Target resolution
and orphan cleanup share the result.
- Add load regression tests that count direct API reads in the
reconcile hot path.
Correct two wrong CLI flag defaults (leader-elect, verify-source-freshness) and add missing flags/Helm values. Remove documented-but-nonexistent annotations (excluded-namespaces, namespace-pattern) that silently did nothing, document the real exclude/paused and glob targeting, fix the mirror ownership-check example, and add the missing charts/kubemirror/README.md that README linked to.
kubemirror overrides controller names to <Kind>.<version>.<group> (e.g. Secret.v1.), but the dashboards, alerts, and recording rules filtered controller=~"secret|configmap" (the controller-runtime default name), so every panel and alert matched nothing. Switch all selectors to Secret.*|ConfigMap.* and document the naming convention.
The startup log hardcoded version="dev", so release builds always reported dev. Use the ldflags-set Version variable. Also modernise interface{} to any.
exclude="true" opts a resource out of mirroring and tears down its existing mirrors; paused="true" freezes mirrors in place (no updates, no cleanup). Both respected by the source and namespace reconcilers. Also dedup target-namespace resolution via delegation, remove 8 unused annotation constants and their misleading godoc, and silence gosec G101 false positives on base64 test fixtures.
Replace the triplicated ownership-verified delete guard with deleteOwnedMirror, and hand-built controller-name strings with gvkControllerName. Use constants.Domain/ControllerName over magic strings and maps.Copy/Clone for plain copy loops. Behaviour-preserving; tests green.
Discloses the single anonymous adoption ping sent on startup and points
users to the upstream README section for full opt-out instructions
instead of duplicating the table here.