Skip to content

Release v0.12.0 (minor) - #581

Merged
slauger merged 15 commits into
mainfrom
develop
Sep 3, 2026
Merged

slauger merged 15 commits into
mainfrom
develop

Conversation

@github-actions

@github-actions github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

Features

  • feat(api): allow readOnlyRootFilesystem per Server (41b6fa0)

Fixes

  • fix(ca): keep infra_crl.pem across a CRL refresh (b81451f)
  • fix(deps): update module sigs.k8s.io/controller-runtime to v0.25.0 (c15048d)
  • fix(api): require an explicit certname instead of defaulting to puppet (db21eb3)
  • fix(certificate): refuse to share a certname with another Certificate (8d1dd28)
  • fix(api): make pullSecrets and the Server pull policy take effect (e76d9ef)

Refactoring

  • refactor(certificate): derive alt names from Pools instead of mutating them (1c78733)

Chores

  • chore(deps): update dependency conforma/cli to v0.10.2 (87313d1)
Files changed
 .github/workflows/_conforma-validate.yaml          |   2 +-
 api/v1alpha1/certificate_immutability_test.go      |  23 +-
 api/v1alpha1/certificate_types.go                  |  34 ++-
 api/v1alpha1/config_types.go                       |   7 +-
 api/v1alpha1/list_types_test.go                    |   2 +
 api/v1alpha1/server_types.go                       |  13 ++
 api/v1alpha1/zz_generated.deepcopy.go              |  10 +
 .../crds/openvox.voxpupuli.org_certificates.yaml   |  30 ++-
 .../crds/openvox.voxpupuli.org_configs.yaml        |  13 +-
 .../crds/openvox.voxpupuli.org_databases.yaml      |  13 +-
 .../crds/openvox.voxpupuli.org_servers.yaml        |  26 ++-
 charts/openvox-stack/templates/certificates.yaml   |   3 +
 .../bases/openvox.voxpupuli.org_certificates.yaml  |  30 ++-
 .../crd/bases/openvox.voxpupuli.org_configs.yaml   |  13 +-
 .../crd/bases/openvox.voxpupuli.org_databases.yaml |  13 +-
 .../crd/bases/openvox.voxpupuli.org_servers.yaml   |  26 ++-
 docs/reference/certificate.md                      |  26 ++-
 docs/reference/config.md                           |   9 +
 docs/reference/pool.md                             |   2 +-
 docs/reference/server.md                           |  22 +-
 go.mod                                             |   2 +-
 go.sum                                             |   4 +-
 internal/controller/certificate_controller.go      | 129 ++++++++++-
 internal/controller/certificate_derived.go         | 112 +++++++++
 internal/controller/certificate_derived_test.go    | 190 ++++++++++++++++
 internal/controller/certificate_signing.go         | 126 +++++++++--
 internal/controller/certificate_signing_test.go    |  53 ++++-
 internal/controller/certificate_spec_drift_test.go |  16 +-
 internal/controller/certificateauthority_crl.go    |   6 +-
 internal/controller/certificateauthority_job.go    |   3 +-
 internal/controller/certname_conflict_test.go      | 204 +++++++++++++++++
 internal/controller/database_deployment.go         |   5 +-
 internal/controller/helpers.go                     |  93 +++++++-
 internal/controller/image_pull_settings_test.go    | 252 +++++++++++++++++++++
 internal/controller/indexers.go                    |   9 +
 internal/controller/ownership_test.go              |  41 ++--
 internal/controller/pool_controller.go             |  57 -----
 internal/controller/pool_controller_test.go        |  93 --------
 internal/controller/server_controller.go           |  38 ++++
 internal/controller/server_deployment.go           |  19 +-
 internal/controller/testutil_test.go               |   7 +
 internal/webhook/certificate_webhook.go            |  53 +++++
 internal/webhook/certificate_webhook_test.go       |  76 +++++++
 tests/e2e/pool-gateway/chainsaw-test.yaml          |  19 +-
 44 files changed, 1651 insertions(+), 273 deletions(-)

Release prediction

Predicted release: v1.0.0 -> v1.1.0 (minor)
1 feature(s) 5 fix(es)


Source: develop | Target: main | Trigger: Successful CI on develop

Auto-generated by GitHub Actions - updated automatically on new commits.
Replace this text, for example with a Copilot-written summary, and it will be left alone.

slauger and others added 5 commits September 3, 2026 07:11
Three fields were declared and never reached a pod.

ImageSpec.PullSecrets was read nowhere. The only imagePullSecrets on any
pod came from code image entries, so a private registry for the server,
database or CA setup image could not work at all. All three pod specs now
carry them, deduplicated against the code image secrets.

Server.image.pullPolicy was ignored: both container specs read
cfg.Spec.Image.PullPolicy unconditionally. Fixing that required removing
the nested kubebuilder default, for the same reason #549 removed it from
repository and tag: a nested default is materialised into every Server, so
the field was never empty and could not express inherit. The fallback now
lives in code - Server, then Config, then IfNotPresent.

ConditionSSLBootstrapped existed as a constant and as a promise in
docs/reference/server.md, but nothing ever set it. It now reports whether
the referenced Certificate is usable, which also gives the Server a visible
status while it waits: a missing or unsigned Certificate previously left no
trace at all, so nothing happens looked the same as nothing is wrong.

Documented the resolution rules, including that pullSecrets on a Server
replaces the Config's list rather than extending it.
…condition

fix(api): make pullSecrets and the Server pull policy take effect
A certname identifies exactly one entry on the CA, so two Certificates
claiming the same one against the same CertificateAuthority are
indistinguishable to it. Nothing prevented that: the webhook validated the
certname format but not its uniqueness, there was no index on it, and the
CRD defaults it to puppet - so two Certificates created without one collide
by default rather than by mistake.

The consequences were both silent. Signing and fetching both address the CA
by certname, so a Certificate could adopt a certificate issued for another
resource's key; the mismatch would only surface later as a failed TLS
handshake. And handleCertificateCleanup revokes by certname, so deleting
either Certificate revokes the entry the other one depends on.

Three layers, because webhooks are disabled by default:

- the admission webhook rejects a duplicate at creation, naming the holder
- the controller refuses to sign and reports CertnameConflict, permanently:
  retrying cannot free a name, only a spec change can
- a certificate returned by the CA is verified against the private key it
  was requested for, so a foreign certificate under the same name is never
  written to a Secret

A terminating Certificate releases its claim, since its finalizer cleans the
CA entry, and the rule is per CA rather than per namespace.

The full-flow test now signs the submitted CSR instead of returning a canned
certificate. It passed before only because nothing verified the pairing.
The kubebuilder default was the reason the collision was easy to hit: two
Certificates created without a certname both landed on puppet, so they
shared one CA entry by default rather than by mistake.

It was also wrong on its own terms. A certname is an identity, and puppet
is only the right identity for the main server - PuppetDB and any further
certificate need their own. The names agents connect through, a load
balancer or a service address, belong in dnsAltNames, which the chart
already derives from the Pool Services a server joins.

certname is now required with MinLength=1. Nothing in the repository relied
on the default: the chart writes the value unconditionally and already
fails the render when the Database has none, all sixteen CI value files set
it explicitly, and so does the sample. The same guard now covers server
entries, which were the one path that could still fall through to it.

Also folds five copies of the puppet fallback in certificate_signing.go
into certnameOf. The two remaining checks stay as they are: they return an
error rather than substituting a name, which is correct for renewal and
cleanup.

Existing resources are unaffected - they carry certname: puppet
materialised from the default, and the field is immutable anyway. New
manifests that omit it are rejected, which is the point.
slauger and others added 5 commits September 3, 2026 19:15
The setup Job writes both ca_crl.pem and infra_crl.pem into the {ca}-ca-crl
Secret. The periodic refresh writes only the first, and
createOrUpdateSecret replaces the data map wholesale, so infra_crl.pem
survived exactly until the first refresh and then disappeared for the
lifetime of the CA.

Deployments running with enable-infra-crl lose infrastructure-node
revocation that way, and nothing reports it: the refresh succeeds, the
Secret still exists, only one of its keys is gone.

createOrUpdateSecret now takes an optional list of keys to carry across.
The carry-over reads from the object CreateOrUpdate just fetched, so it
cannot race with a concurrent write.

Closes #574
The setting lived on ConfigSpec but was applied to Server pods. One Config
backs several Servers with different roles, typically the CA and the
compilers, so a single Server that needs a writable root forced the
hardening off for every Server under that Config, the CA included.

ServerSpec now carries an optional override that falls back to the Config,
the same shape image uses since #549. It is a pointer for the same reason
the other booleans are: with a true default on the Config, a plain bool
cannot express false.

Closes #575
…g them

PoolReconciler.injectDNSAltNames wrote the route hostname into
Certificate.spec.dnsAltNames of every Server in the Pool - a resource the
Pool does not own. Under Argo CD or Flux that turned into a loop: the Pool
added the name, the source of truth reverted it, the Pool added it again.
Since #549 each round changes a signing-relevant field, so the churn also
re-signs the certificate and rolls the pods.

The relationship is now the other way round. The Certificate controller
derives its effective alt names from its own spec plus the route hostname
of every Pool that asks for injection and is joined by a Server using this
Certificate. Nothing foreign is written, the result is idempotent, and
status.effectiveDNSAltNames makes it visible.

The signing hash and the CSR both use the derived names, so a Pool joined
later still triggers a re-sign - the mechanism stays, only its trigger
moves from a foreign write to observed state. The Certificate controller
watches Pools and Servers so a hostname added later reaches it at all.

injectDNSAltName stays as an API field; its meaning changes from mutates
to is taken into account. The pool-gateway e2e assertion moves with it:
the spec now holds what the chart wrote, the hostname appears in status.

Closes #552
fix(certificate): refuse to share a certname, and require an explicit one
fix: three follow-ups from the second review (#574, #575, #552)
@github-actions github-actions Bot changed the title Release v1.0.1 (patch) Release v1.1.0 (minor) Sep 3, 2026
renovate Bot and others added 5 commits September 3, 2026 17:25
chore(deps): update dependency conforma/cli to v0.10.2
fix(deps): update module sigs.k8s.io/controller-runtime to v0.25.0
The CRDs are v1alpha1 and the project is deliberately pre-1.0, so a
breaking change is expected rather than exceptional. semantic-release did
not know that: three BREAKING CHANGE footers in the API hardening work
turned the release of 2026-09-02 into 1.0.0, which signals a stability
promise that is not intended yet.

Map breaking changes to a minor bump so the intent lives in the repository
instead of in everyone's head. Remove this rule when 1.0.0 is a deliberate
decision; from then on a breaking change should mean what it says.

Verified with semantic-release --dry-run against a clone whose v1.0.0 tag
was removed: with the rule the same 74 commits produce 0.12.0, without it
1.0.0.
ci: keep breaking changes at a minor bump while below 1.0
@slauger
slauger merged commit 308afe9 into main Sep 3, 2026
85 checks passed
@slauger slauger changed the title Release v1.1.0 (minor) Release v0.12.0 (minor) Sep 3, 2026
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.

1 participant