score-k8s - Add a CNPG provisioner for postgres - #61
Conversation
Provisions a dedicated CNPG Cluster per postgres resource, with the same outputs as the built-in score-k8s postgres provisioner so it is a drop-in replacement. Closes score-spec#45. Signed-off-by: Abhishek <abhishekup082@gmail.com>
|
Hi @mathieu-benoit, This is the CNPG provisioner for #45, ready for your review. I followed the CNPG docs you linked and kept the outputs identical to the built-in Two things I deliberately left out, let me know if you'd like either in this PR or as a follow-up:
Happy to adjust anything naming, defaults, or the scope of the params. |
Closes #45.
Adds a CloudNativePG provisioner for the
postgresresource type inscore-k8s.Instead of the single-replica
StatefulSetthat the built-inpostgresprovisioner generates, eachpostgresresource gets its own CNPGCluster, so the operator owns the storage, the failover, the-rw/-ro/-rServices and, if configured, the backups.What's in here
postgres/score-k8s/10-cnpg-postgres.provisioners.yamlbasic-authSecret + apostgresql.cnpg.io/v1Clusterpostgres/score.yamlpsqlneeds no connection stringpostgres/score-k8s/README.mdREADME.mdscore-k8stableDesign notes
A few decisions worth calling out for review:
The outputs match the built-in
postgresprovisioner exactlyhost,port,name,database,username,passwordso this is a drop-in replacement and existing Score files don't need to change.hostpoints at the<cluster>-rwService, which always follows the current primary.The credentials are generated by the provisioner, not by the operator. Left to itself, CNPG creates its own
<cluster>-appSecret with a password that isn't knowable at generate time. Instead the provisioner creates akubernetes.io/basic-authSecret and pointsbootstrap.initdb.secretat it. That keeps the outputs stable acrossscore-k8s generateruns, and letspasswordflow throughencodeSecretRefso it lands in the workload as asecretKeyRefrather than plain text.The defaults are
app/apprather than random names. The built-in provisioner randomises the database and user because it shares one PostgreSQL instance across workloads. Here every resource gets its own Cluster, so that collision doesn't exist, andapp/appis what the CNPG docs use throughout.Params
All optional:
databaseappusernameappinstances13for a primary and two hot standbys.storage1GiimageNameUsage
The CloudNativePG operator needs to be installed in the target cluster, since it owns the
ClusterCRD:Testing
.scripts/test-provisioners.shpasses for the newpostgres/type.basic-auth, the Cluster references it, and the workload receivesPGPASSWORDviasecretKeyRefwith no plain-text password anywhere.instancesemitted as an integer andimageNameomitted cleanly when unset.generateruns produce byte-identical manifests, so the password isn't rotated on regeneration.clusters.postgresql.cnpg.ioCRD schema fromcnpg-1.25.4. This matters because a mistyped CRD field is silently pruned by the API server rather than rejected, so it would have looked fine and quietly not worked.One thing that came out of that last check: CNPG's validating webhook rejects Cluster names longer than 50 characters, and
pg-<workload>-<guid>overflows that for workload names over 38 characters. It would have generated fine and then failed atkubectl apply. The workload name is now truncated so the result stays within the limit, with the guid suffix still keeping it unique.