plugin-barman-cloud/internal/cnpgi
Armando Ruocco a6ddc7828e fix: rework managed RBAC reconciliation
Adds the safety and robustness improvements identified during
review of the recommended-labels feature, all on the managed
Role and RoleBinding paths.

Labels: per-key overwrite. Keys the plugin manages overwrite
existing values; unrelated keys (anything outside the desired
set) are left alone. The plugin owns these objects exclusively
— OwnerReference points to the Cluster CR, the Pre hook
reconciles them on every call, and there is no realistic path
for an external manager (Helm, Kustomize, ArgoCD, …) to ever
touch these per-Cluster runtime-generated objects, so per-key
overwrite is the correct policy and version labels follow
plugin upgrades naturally.

Subjects: additive. The plugin guarantees its own ServiceAccount
Subject is bound, but never removes Subjects added by other
actors. A Subject is a grant of access; silently revoking
access an admin granted (e.g. a debugging ServiceAccount) is
the wrong default. This is intentionally asymmetric to the
label policy.

RoleRef: immutable in Kubernetes; a Patch cannot fix it.
Divergence at the canonical name is corruption regardless of
who wrote the existing object — fail loudly so the operator
notices and deletes the RoleBinding, and the next Pre call
recreates it correctly.

AlreadyExists tolerance: EnsureRoleBinding called c.Create
directly when Get returned NotFound and propagated whatever
Create returned. That's noisy on plugin pod startup: the
controller-runtime client is cache-backed by default, and during
the warm-up window the informer cache can return NotFound for a
RoleBinding that is actually present on the API server. The
follow-up Create then hits AlreadyExists and the whole Pre hook
returned an error to the cnpg-i framework. Mirror the pattern
used by ensureRoleExists: split the exists-or-create concern
into getOrCreateRoleBinding, swallow AlreadyExists, and re-Get
to return the racing winner so the caller can fall through to
reconcile.

Optimistic locking on patches: patchRole already wrapped its
Get+Patch in retry.RetryOnConflict, but used client.MergeFrom
which doesn't include resourceVersion — so the API server never
returns 409 and the retry never fires. Switch both Patch sites
to client.MergeFromWithOptions(WithOptimisticLock{}) so the
retry actually does what its caller assumes it does.
EnsureRoleBinding gets the same retry wrapper for parity.

Single-Get steady state: the previous structure did one Get to
check existence and a second Get inside the retry loop, even
when the object was already present and matched desired.
reconcileRoleBinding now receives the existing object as input
and uses it on the first attempt, only Get-ing again on actual
conflict-driven retries.

Helpers: extract mergeLabels (per-key overwrite), mergeSubjects
(additive append), containsSubject (semantic deep-equal), and
keep labelsNeedUpdate / roleBindingNeedsUpdate as no-Patch
short-circuits whose result mirrors the merge helpers exactly.

Tests: cover fresh creation, steady-state no-op (via Patch
interceptor counter, more reliable than the previous
ResourceVersion comparison which depended on fake-client RV
semantics that aren't part of the controller-runtime contract),
unrelated-label preservation, stale-label refresh, additive
subjects with user-added entries, AlreadyExists race during
pod startup (deterministic via interceptor), divergent
RoleRef error path, and the upgrade scenario for objects that
predate the recommended-labels feature.

Signed-off-by: Armando Ruocco <armando.ruocco@enterprisedb.com>
2026-04-29 10:54:54 +02:00
..
common fix(restore): use custom CNPG group and version for scheme registration (#847) 2026-04-13 13:20:38 +02:00
instance fix(restore): use custom CNPG group and version for scheme registration (#847) 2026-04-13 13:20:38 +02:00
metadata fix: align label values with issue #545 spec 2026-04-29 10:54:54 +02:00
operator fix: rework managed RBAC reconciliation 2026-04-29 10:54:54 +02:00
restore fix(restore): use custom CNPG group and version for scheme registration (#847) 2026-04-13 13:20:38 +02:00