Repository navigation
Description
Is there an existing issue already for this feature request/idea?
- I have searched for an existing issue, and could not find anything. I believe this is a new feature request to be evaluated.
What problem is this feature going to solve? Why should it be added?
spec.inheritedMetadata is documented as applying to everything the operator creates. The CRD field comment says "Metadata that will be inherited by all objects related to the Cluster", and the docs say labels and annotations are "inherited by all resources created by it (including pods)".
The PKI secrets are the one class of generated object that silently opts out.
In v1.27.1, inherited metadata is only ever applied through SetInheritedData() / SetInheritedDataAndOwnership() (api/v1/cluster_funcs.go:1257-1267, sourcing from spec.inheritedMetadata via GetFixedInheritedLabels() at :679). Those helpers are called for the credential secrets (internal/controller/cluster_create.go:190, :232), the image-pull secret (:697), Services (pkg/specs/services.go:183), Jobs (pkg/specs/jobs.go:383), PDBs (pkg/specs/poddisruptionbudget.go:58), PodMonitors (pkg/specs/podmonitor.go:49), PVCs (pkg/reconciler/persistentvolumeclaim/metadata.go:244), ServiceAccount and RBAC.
They are never called anywhere in internal/controller/cluster_pki.go. ensureCASecret() sets only ownership (:193), and so does ensureLeafCertificate() (:340). So <cluster>-ca, <cluster>-server and <cluster>-replication receive no inherited labels or annotations, while <cluster>-app and <cluster>-superuser — secrets of the same kind, from the same reconcile — do.
The practical consequence: any automation that needs to select the CA material by label cannot use the one mechanism the project documents for exactly that. A concrete example is publishing a key-free copy of ca.crt to consumers so they can verify the server without ever holding a private key — a label-driven controller is the natural implementation, and it is precisely the CA secret that cannot be labelled. The only workaround is to hardcode the <cluster>-ca name, which couples external tooling to an operator naming convention instead of to a declared contract.
Notably, ensureLeafCertificate() already accepts an additionalLabels map[string]string parameter (:319) — and both callers, ensureServerLeafCertificate() (:229) and ensureReplicationClientLeafCertificate() (:265), pass nil. The hook exists and is unreachable, which suggests the gap is an oversight rather than a deliberate exclusion.
Describe the solution you'd like
Make the behaviour match the documentation: apply spec.inheritedMetadata to the PKI secrets like every other generated object.
Minimal change — in cluster_pki.go, replace the bare utils.SetAsOwnedBy() calls with cluster.SetInheritedDataAndOwnership() in ensureCASecret() and ensureLeafCertificate(). The existing (currently unused) additionalLabels parameter can stay for operator-internal labels.
One detail worth deciding explicitly: the update paths (renewCASecret(), renewAndUpdateCertificate()) would also need to reconcile metadata, otherwise secrets created before the change never pick the labels up and behaviour differs between a fresh cluster and an upgraded one.
If you would rather not broaden inheritedMetadata (the "too much noise on every object" concern raised in #5883 and #10368), the alternative is a targeted spec.certificates.secretTemplate with labels / annotations, mirroring cert-manager's Certificate.spec.secretTemplate. That is more surface area but scopes the metadata to the certificates only. I have no strong preference and would follow your call.
Describe alternatives you've considered
spec.inheritedMetadata— the documented mechanism, and the subject of this report: it does not reach these secrets.- Hardcoding the
<cluster>-ca/<cluster>-servernames in downstream tooling — works, but replaces a declarative contract with a naming assumption, and has to be repeated per operator for anything managing more than one database engine. - Labelling the secrets out of band (a mutating policy, a sidecar controller) — fragile, racy at create time, and requires granting a second component write access to secrets that hold private keys.
- [Feature]: Add secret template for credentials secret #5883 requests a secret template for the credentials secret. It does not cover the PKI secrets, and its existence is itself evidence of the split: its author reports
inheritedMetadataworking as a workaround for the credentials secret, which is exactly the class of secret that does inherit.
Additional context
Verified against v1.27.1 and re-checked on main — cluster_pki.go still calls only utils.SetAsOwnedBy() in both places.
Related but distinct: #11121 (CA/leaf lifetimes), #10891 (CA from a ConfigMap), #10439 (warn on inheritedMetadata clashes).
Backport?
No
Are you willing to actively contribute to this feature?
Yes
Code of Conduct
- I agree to follow this project's Code of Conduct
Activity
devGPP23 commentedon Jul 14, 2026
Hey, I'd like to pick this up if it's still open — this would be my first contribution to CNPG.
Thanks for the writeup, @lexfrei , it made tracking down the actual code paths a lot faster.
Here's roughly what I'm thinking for the fix:
In cluster_pki.go, swap the bare utils.SetAsOwnedBy() calls in ensureCASecret() and ensureLeafCertificate() for cluster.SetInheritedDataAndOwnership(), keeping the existing additionalLabels param intact so internal labels still work.
Also touch renewCASecret() and renewAndUpdateCertificate() so existing clusters pick up the metadata on renewal instead of only new ones getting it — otherwise you'd get exactly the fresh-vs-upgraded inconsistency you flagged.
Add tests for both the create and renew paths.
I don't have a strong opinion on inheritedMetadata vs a dedicated secretTemplate — happy to go either way depending on what maintainers prefer, but the inheritedMetadata fix looks like the smaller, more consistent change given the existing additionalLabels hook is already there unused.
Let me know if I can get this assigned, and if there's anything I'm missing before I start.
LewisHaley commentedon Sep 28, 2026
This bug seems pretty clear cut. The write up above shows that the code seems to have intent for this to happen. It would be good to have a maintainer chime in on the issue so we can get it fixed!