Skip to content

score-k8s - Add a CNPG provisioner for postgres - #61

Open
Abhishek9639 wants to merge 1 commit into
score-spec:mainfrom
Abhishek9639:add-cnpg-postgres-provisioner
Open

score-k8s - Add a CNPG provisioner for postgres#61
Abhishek9639 wants to merge 1 commit into
score-spec:mainfrom
Abhishek9639:add-cnpg-postgres-provisioner

Conversation

@Abhishek9639

Copy link
Copy Markdown
Contributor

Closes #45.

Adds a CloudNativePG provisioner for the postgres resource type in score-k8s.

Instead of the single-replica StatefulSet that the built-in postgres provisioner generates, each postgres resource gets its own CNPG Cluster, so the operator owns the storage, the failover, the -rw/-ro/-r Services and, if configured, the backups.

What's in here

File
postgres/score-k8s/10-cnpg-postgres.provisioners.yaml The provisioner: a basic-auth Secret + a postgresql.cnpg.io/v1 Cluster
postgres/score.yaml Example workload, wired through the libpq env vars so psql needs no connection string
postgres/score-k8s/README.md Operator prerequisite, outputs, params, caveats
README.md One row in the score-k8s table

Design notes

A few decisions worth calling out for review:

The outputs match the built-in postgres provisioner exactly host, port, name, database, username, password so this is a drop-in replacement and existing Score files don't need to change. host points at the <cluster>-rw Service, 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>-app Secret with a password that isn't knowable at generate time. Instead the provisioner creates a kubernetes.io/basic-auth Secret and points bootstrap.initdb.secret at it. That keeps the outputs stable across score-k8s generate runs, and lets password flow through encodeSecretRef so it lands in the workload as a secretKeyRef rather than plain text.

The defaults are app/app rather 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, and app/app is what the CNPG docs use throughout.

Params

All optional:

Param Default Description
database app Name of the database created at bootstrap.
username app Owner of that database.
instances 1 Number of PostgreSQL instances. Use 3 for a primary and two hot standbys.
storage 1Gi Size of the volume of each instance.
imageName operator default PostgreSQL image, to pin the major version.

Usage

score-k8s init --provisioners https://raw.githubusercontent.com/score-spec/community-provisioners/refs/heads/main/postgres/score-k8s/10-cnpg-postgres.provisioners.yaml

The CloudNativePG operator needs to be installed in the target cluster, since it owns the Cluster CRD:

kubectl apply --server-side -f \
  https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.25/releases/cnpg-1.25.4.yaml

Testing

  • .scripts/test-provisioners.sh passes for the new postgres/ type.
  • Verified the rendered manifests: the Secret is basic-auth, the Cluster references it, and the workload receives PGPASSWORD via secretKeyRef with no plain-text password anywhere.
  • All five params render correctly, with instances emitted as an integer and imageName omitted cleanly when unset.
  • Three consecutive generate runs produce byte-identical manifests, so the password isn't rotated on regeneration.
  • Checked every field path emitted against the real clusters.postgresql.cnpg.io CRD schema from cnpg-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 at kubectl apply. The workload name is now truncated so the result stays within the limit, with the guid suffix still keeping it unique.

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>
@Abhishek9639

Copy link
Copy Markdown
Contributor Author

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 score-k8s postgres provisioner, so it can be swapped in without touching any existing Score file. The main decision I'd like your opinion on is that the provisioner generates the application credentials into its own basic-auth Secret and passes it to bootstrap.initdb.secret, rather than letting the operator create <cluster>-app. I went that way because the operator-generated password isn't readable at generate time, which would make the outputs unstable across runs but if you'd prefer to lean on the operator's own Secret, I'm happy to rework it.

Two things I deliberately left out, let me know if you'd like either in this PR or as a follow-up:

  • Installing the CNPG operator in .scripts/setup-kind-cluster.sh. CI only runs generate, so nothing needs it today, but it would let the provisioner be tested end-to-end.
  • A NetworkPolicy variant, along the lines of the existing -with-netpol provisioners.

Happy to adjust anything naming, defaults, or the scope of the params.
Thanks for assigning this to me!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

score-k8s - Add a CNPG example for postgres

1 participant