XS✔ ◾ feat: HA & Raft quorum safety guardrails#2
Merged
Conversation
…with quorum validation
PR Metrics✔ Thanks for keeping your pull request small.
Metrics computed by PR Metrics. Add it to your Azure DevOps and GitHub PRs! |
… null in schema annotations
…ffinity default to {}
maxUnavailable: 0 means auto-calculate as floor(replicaCount/2). This gives
proper integer typing in schema and docs. affinity: {} enables correct object
type inference by helm-docs.
…ateStrategy.type - Change maxUnavailable default from 0 to -1 so users can explicitly set 0 to block all voluntary disruptions - Add template validation for updateStrategy.type (must be RollingUpdate or OnDelete) - Fix changelog to reflect actual defaults (was incorrectly referencing null)
vlasopoulos
reviewed
Mar 20, 2026
templates/pdb.yaml
Outdated
Comment on lines
+4
to
+7
| {{- $maxUnavailable := int .Values.pdb.maxUnavailable -}} | ||
| {{- if lt $maxUnavailable 0 }} | ||
| {{- $maxUnavailable = $maxFault -}} | ||
| {{- end }} |
There was a problem hiding this comment.
Suggested change
| {{- $maxUnavailable := int .Values.pdb.maxUnavailable -}} | |
| {{- if lt $maxUnavailable 0 }} | |
| {{- $maxUnavailable = $maxFault -}} | |
| {{- end }} | |
| {{- $maxUnavailable := .Values.pdb.maxUnavailable }} | |
| {{- if and (kindIs "string" .Values.pdb.maxUnavailable) (eq .Values.pdb.maxUnavailable "auto") }} | |
| {{- $maxUnavailable = $maxFault }} | |
| {{- end }} |
values.yaml
Outdated
Comment on lines
+176
to
+183
| # type: integer | ||
| # minimum: 1 | ||
| # minimum: -1 | ||
| # @schema | ||
| # -- Maximum number of pods that can be unavailable during disruption | ||
| maxUnavailable: 1 | ||
| # -- Maximum number of pods that can be unavailable during disruption. | ||
| # Set to -1 (default) to auto-calculate as floor(replicaCount/2), preserving Raft quorum. | ||
| # Set to 0 to block all voluntary disruptions. Any positive value is used directly | ||
| # and must not exceed floor(replicaCount/2). | ||
| maxUnavailable: -1 |
There was a problem hiding this comment.
Suggested change
| # type: integer | |
| # minimum: 1 | |
| # minimum: -1 | |
| # @schema | |
| # -- Maximum number of pods that can be unavailable during disruption | |
| maxUnavailable: 1 | |
| # -- Maximum number of pods that can be unavailable during disruption. | |
| # Set to -1 (default) to auto-calculate as floor(replicaCount/2), preserving Raft quorum. | |
| # Set to 0 to block all voluntary disruptions. Any positive value is used directly | |
| # and must not exceed floor(replicaCount/2). | |
| maxUnavailable: -1 | |
| # type: integer, string | |
| # minimum: -1 | |
| # @schema | |
| # -- Maximum number of pods that can be unavailable during disruption. | |
| # Set to "auto" (default) to auto-calculate as floor(replicaCount/2), preserving Raft quorum. | |
| # Set to 0 to block all voluntary disruptions. Any positive value is used directly | |
| # and must not exceed floor(replicaCount/2). | |
| maxUnavailable: auto |
…else for updateStrategy - maxUnavailable defaults to -1 (auto-calculate), 0 blocks all disruptions - Schema enforces updateStrategy.type enum (RollingUpdate/OnDelete) - Schema conditionally requires rollingUpdate only for RollingUpdate type - Template validation kept as defense in depth - Fix changelog to reflect actual defaults
Collaborator
Author
I've missed your comment and went on a different way. Do you think that makes more sense using |
…ma validation - maxUnavailable defaults to "auto" (auto-calculate as floor(replicaCount/2)) - Integer values are used directly, 0 blocks all voluntary disruptions - Schema enforces anyOf [integer >= 0, "auto"] for maxUnavailable - Schema enforces updateStrategy.type enum (RollingUpdate/OnDelete) - Schema uses if/then to conditionally require rollingUpdate for RollingUpdate - Update changelog to reflect "auto" defaults
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.
Summary
maxUnavailablefor both PDB and StatefulSetupdateStrategybased on Raft fault tolerance (floor(replicaCount/2)), with user override support and quorum validationupdateStrategyon StatefulSet withRollingUpdatedefault andOnDeletesupport, with schema-level enum and if/then/else conditional validationpreferredDuringSchedulingIgnoredDuringExecution) onkubernetes.io/hostnamefor multi-node clustersmaxUnavailablevalues exceeding Raft fault tolerance and invalidupdateStrategy.typevalues (defense in depth)Quorum safety
maxUnavailableBoth
pdb.maxUnavailableandupdateStrategy.rollingUpdate.maxUnavailabledefault to"auto"(auto-calculated asfloor(replicaCount/2)). Users can set an integer value:0to block all voluntary disruptions, or any positive value up tofloor(replicaCount/2). The template fails with a descriptive error if the override exceeds the Raft fault tolerance.Breaking changes
pdb.enableddefault changed fromfalsetotruepdb.maxUnavailabledefault changed from1to"auto"(auto-calculate asfloor(replicaCount/2))updateStrategyblock added withtype: RollingUpdateandrollingUpdate.maxUnavailable: "auto"affinityis unset or empty ({}) andreplicaCount > 1Schema validation
updateStrategy.typeenforced as enum:RollingUpdateorOnDeletemaxUnavailablevalidated asanyOf: [integer >= 0, "auto"]rollingUpdateconditionally required only whentypeisRollingUpdate(via if/then)affinitytyped asobjectTest plan
helm lint .passes