From e46ad1594ae0c4d4e9d9f81f645e6a801659f72a Mon Sep 17 00:00:00 2001 From: Daniel Schlatter Date: Tue, 18 Aug 2026 12:47:36 -0600 Subject: [PATCH] Make spire-server rollout strategy configurable (#924) Signed-off-by: Daniel Schlatter Co-authored-by: kfox1111 --- charts/spire/charts/spire-server/README.md | 1 + .../templates/server-resource.yaml | 8 ++++ charts/spire/charts/spire-server/values.yaml | 3 ++ tests/unit/spire_test.go | 48 +++++++++++++++++++ 4 files changed, 60 insertions(+) diff --git a/charts/spire/charts/spire-server/README.md b/charts/spire/charts/spire-server/README.md index ba80fab..7c17a2b 100644 --- a/charts/spire/charts/spire-server/README.md +++ b/charts/spire/charts/spire-server/README.md @@ -87,6 +87,7 @@ In order to run Tornjak with simple HTTP Connection only, make sure you don't cr | `image.pullPolicy` | The image pull policy | `IfNotPresent` | | `image.tag` | Overrides the image tag whose default is the chart appVersion | `""` | | `kind` | Define SPIRE server deployment type. Can be statefulset/deployment. Defaults to statefulset if not set. This feature is experimental. | `statefulset` | +| `updateStrategy` | Rollout strategy for the server, mapped to spec.updateStrategy when kind is "statefulset" and to spec.strategy when kind is "deployment". Left empty the Kubernetes default applies, which for a Deployment surges a second server before the old one goes away. Set `{type: Recreate}` to keep at most one server running, as a memory keyManager or an in-memory datastore requires. | `{}` | | `externalServer` | Deploy only the bundle ConfigMap, RBAC rules, and identity documents but not the server. Use in a nested setup where the server is external. | `false` | | `externalServerSubject.kind` | RBAC subject kind the external (nested) server's downstream bindings are granted to. One of "User" (client-certificate identity, the historical default), "Group", or "ServiceAccount" (e.g. for a static-token kubeconfig). Only used when externalServer is true. | `User` | | `externalServerSubject.name` | Name of the subject. For kind "User" it must match the CN of the client certificate the external server presents; for kind "Group" it is the group name (e.g. a certificate O value); for kind "ServiceAccount" it is the name of the (operator-managed, out-of-band) ServiceAccount. | `spire-root` | diff --git a/charts/spire/charts/spire-server/templates/server-resource.yaml b/charts/spire/charts/spire-server/templates/server-resource.yaml index be1a6c7..64307bd 100644 --- a/charts/spire/charts/spire-server/templates/server-resource.yaml +++ b/charts/spire/charts/spire-server/templates/server-resource.yaml @@ -90,6 +90,14 @@ spec: {{- end }} replicas: {{ .Values.replicaCount }} {{- end }} + {{- with .Values.updateStrategy }} + {{- if eq $.Values.kind "statefulset" }} + updateStrategy: + {{- else }} + strategy: + {{- end }} + {{- toYaml . | nindent 4 }} + {{- end }} {{- if eq .Values.kind "statefulset" }} serviceName: {{ include "spire-server.fullname" . }} {{- end }} diff --git a/charts/spire/charts/spire-server/values.yaml b/charts/spire/charts/spire-server/values.yaml index da39180..1fdfeb0 100644 --- a/charts/spire/charts/spire-server/values.yaml +++ b/charts/spire/charts/spire-server/values.yaml @@ -23,6 +23,9 @@ image: ## @param kind Define SPIRE server deployment type. Can be statefulset/deployment. Defaults to statefulset if not set. This feature is experimental. kind: statefulset +## @param updateStrategy [object] Rollout strategy for the server, mapped to spec.updateStrategy when kind is "statefulset" and to spec.strategy when kind is "deployment". Left empty the Kubernetes default applies, which for a Deployment surges a second server before the old one goes away. Set `{type: Recreate}` to keep at most one server running, as a memory keyManager or an in-memory datastore requires. +updateStrategy: {} + ## @param externalServer Deploy only the bundle ConfigMap, RBAC rules, and identity documents but not the server. Use in a nested setup where the server is external. externalServer: false diff --git a/tests/unit/spire_test.go b/tests/unit/spire_test.go index 24964e3..1eccff9 100644 --- a/tests/unit/spire_test.go +++ b/tests/unit/spire_test.go @@ -380,4 +380,52 @@ spire-server: Expect(roles).Should(ContainSubstring(`name: "spire-admins"`)) }) }) + Describe("spire-server.updateStrategy", func() { + It("maps to spec.strategy when kind is deployment", func() { + objs, err := ValueStringRender(chart, ` +spire-server: + kind: deployment + persistence: + type: emptyDir + keyManager: + disk: + enabled: false + memory: + enabled: true + dataStore: + sql: + databaseType: postgres + host: db.example.org + updateStrategy: + type: Recreate +`) + Expect(err).Should(Succeed()) + serverResource := objs["spire/charts/spire-server/templates/server-resource.yaml"] + Expect(serverResource).Should(ContainSubstring("kind: Deployment")) + Expect(serverResource).Should(ContainSubstring("\n strategy:\n type: Recreate\n")) + }) + + It("maps to spec.updateStrategy when kind is statefulset", func() { + objs, err := ValueStringRender(chart, ` +spire-server: + updateStrategy: + type: OnDelete +`) + Expect(err).Should(Succeed()) + serverResource := objs["spire/charts/spire-server/templates/server-resource.yaml"] + Expect(serverResource).Should(ContainSubstring("kind: StatefulSet")) + Expect(serverResource).Should(ContainSubstring("\n updateStrategy:\n type: OnDelete\n")) + }) + + It("renders neither field when left unset", func() { + objs, err := ValueStringRender(chart, ` +spire-server: + replicaCount: 1 +`) + Expect(err).Should(Succeed()) + serverResource := objs["spire/charts/spire-server/templates/server-resource.yaml"] + Expect(serverResource).ShouldNot(ContainSubstring("\n strategy:")) + Expect(serverResource).ShouldNot(ContainSubstring("\n updateStrategy:")) + }) + }) })