Repository navigation
Request a rolling restart whenever a live node certificate is regenerated - #1169
Merged
Merged
Conversation
This was referenced Oct 22, 2026
Open
|
|
||
| # [Unreleased](https://github.com/cockroachdb/cockroach-operator/compare/v2.18.5...master) | ||
| * Fixed the node certificate being regenerated without a rolling restart when `ingress.sql.host` is first added to a running cluster, which left the pods serving the old certificate. | ||
|
|
Contributor
There was a problem hiding this comment.
Could the changelog entry also note that adding ingress.sql.host to a running cluster now triggers a rolling restart, and a note saying that clusters already affected by #1168(this issue) aren't self-healed and they need a manual restart or a remove/re-add of the host?
Contributor
Author
There was a problem hiding this comment.
Added both: the entry now says that adding ingress.sql.host to a running cluster triggers a rolling restart, and a separate Note says clusters already affected by #1168 are not repaired by the upgrade, because the certificate is not regenerated again, and need a rolling restart or a remove/re-add of the host. Rebased onto master for CI.
isaactwong
force-pushed
the
paired-path-e-ingress-cert-restart
branch
2 times, most recently
from
October 30, 2026 18:04
6a6a0ac to
ede2aa3
Compare
Drive the certificate actor through a cluster's life against a fake API server with real certificate generation: first generation, then adding the first SQL ingress host, which regenerates the node certificate the pods already hold. Record today's behavior: the certificate changes but no rolling restart is requested, because the restart decision keys on the SQL-ingress condition, which the ingress actor sets only after the certificate actor has run. The next commit fixes that and flips the assertion, so the fix shows in its own diff. The test shells out to the cockroach binary, so the actor test target gains the //hack/bin:cockroach data dependency and a TestMain that puts it on PATH, the same pattern pkg/security uses. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The certificate actor decided whether to request a rolling restart from one input, the SQL-ingress condition, while generateNodeCert decided whether to regenerate an existing certificate from another, a change in the SQL host. The ingress actor runs after the certificate actor, so on the first exposure the condition was still false: the node certificate was reissued with the new host and no restart followed. Pods copy certificates into an emptyDir at init and never re-read them, so they kept serving the old certificate and verify-full clients through the ingress failed until an unrelated restart. Changing an already-exposed host worked only because the condition was true by then. Have generateNodeCert report whether it replaced a certificate a ready secret already held, and request the restart from that fact. First generation has no pods to restart and requests none; any future reason to regenerate a live certificate gets the restart without a second decision to keep in sync. A run that regenerates nothing no longer requests one, where the old condition-based check re-set the annotation on every run with ingress exposed. While here, guard the status.sqlHost write on the object whose host was baked into the certificate; the fetched object can gain an ingress mid-run, and the old guard then dereferenced a nil spec on the original. Fixes #1168 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
isaactwong
force-pushed
the
paired-path-e-ingress-cert-restart
branch
from
October 30, 2026 18:48
ede2aa3 to
4570a92
Compare
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adding
spec.ingress.sql.hostto a running secure cluster regenerates the node certificate to include the new host, but the pods keep serving the old certificate.verify-fullclients connecting through the ingress fail hostname verification until something unrelated restarts the pods.Root cause
The certificate actor made two decisions from two different inputs.
generateNodeCertregenerated an existing certificate whenstatus.sqlHostdiffered from the spec.Actrequested a rolling restart when theCrdbSQLIngressExposedcondition was true. The ingress actor sets that condition, and it runs after the certificate actor, so on the first exposure the condition was still false: the certificate was reissued, no restart was requested, and pods, which copy certificates into an emptyDir once at init, never saw it. Changing an already-exposed host worked only because the condition was true by then.Fix
generateNodeCertnow reports whether it replaced a certificate that a ready secret already held, andActrequests the rolling restart from that fact. One decision, one input. First generation has no pods to restart and requests none. Any future reason to regenerate a live certificate gets the restart without a second decision to keep in sync.Commits
Test whether regenerating the node certificate requests a restart. Drives the actor against a fake API server with real certificate generation and records today's behavior: certificate regenerated, no restart. Passes onmaster. Adds the//hack/bin:cockroachdata dependency and aTestMainto the actor tests, the same patternpkg/securityuses.Request a restart whenever a live node certificate is regenerated. The fix. Flips the test and adds: a no-change run with the ingress condition true requests nothing; a later host change still restarts; a cluster created with the ingress host already in its spec requests no restart on first generation. Reinstating the old condition check in OR form fails the no-change assertion, which was verified by trying it.Behavior changes
status.sqlHostwrite is now guarded on the object whose host was baked into the certificate. The old guard checked the freshly fetched object and could dereference a nil ingress on the original if the ingress was added mid-run.Known limitation
The proxy for "pods may hold this certificate" is a ready node secret. A node secret deleted by hand and regenerated during a host change reports no replacement, so pods keep the old certificate until something else restarts them. The director does not route to the certificate actor for a deleted secret alone, so this needs a deletion and a host change in the same window.
For reviewers
The annotation is honored by the restart actor only when the
ClusterRestartandCrdbVersionValidatorfeature gates are on (both default on) and the version has been checked. With a gate off, the annotation sits unconsumed, exactly as it did for the old condition-based path. Separately, the restart actor logs but does not propagate a failure to clear the annotation after a restart, which can cause a second roll; pre-existing, left for a follow-up.Testing
bazel test //pkg/actor:go_default_testpasses on both commits. The new tests shell out to the realcockroach certcommand, provided as bazel test data. No e2e covers ingress.Fixes #1168
Epic: none
🤖 Generated with Claude Code
Part of a sweep of fixes for one bug shape: two code paths that should build the same thing from the same inputs were written as separate copies and drifted. Each PR in the set follows one arc: the first commit adds a test that exercises both paths side by side and records today's divergence (it passes on
master); the second commit makes the paths share one definition and flips the test to equality.Siblings: cockroach-operator #1167, cockroach-operator #1172, helm-charts #689, helm-charts #691.