Skip to content

Commit 94f9121

Browse files
authored
CSPL-4631: add Helm chart support for feature gates
Add splunkOperator.featureGates map to values so operators can enable feature gates via helm install/upgrade without per-gate chart modifications. A helper template formats the map into a single --feature-gates=Key1=true,Key2=false arg; the deployment template conditionally includes it when the map is non-empty. Also introduces helm-unittest with Makefile targets (helm-lint, helm-test, helm-check) and fixes a pre-existing nindent bug in the extraEnvs rendering.
1 parent 95c0e6e commit 94f9121

8 files changed

Lines changed: 339 additions & 5 deletions

File tree

Makefile

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,21 @@ docs-preview: ## Preview documentation locally with Jekyll (requires Ruby and bu
149149
@cd docs && bundle exec jekyll serve --livereload
150150
@echo "Documentation available at http://localhost:4000/splunk-operator"
151151

152+
##@ Helm
153+
154+
HELM_OPERATOR_CHART = helm-chart/splunk-operator
155+
156+
.PHONY: helm-lint
157+
helm-lint: ## Lint Helm charts
158+
helm lint $(HELM_OPERATOR_CHART)
159+
160+
.PHONY: helm-test
161+
helm-test: setup/helm-unittest ## Run Helm chart unit tests
162+
helm unittest $(HELM_OPERATOR_CHART)
163+
164+
.PHONY: helm-check
165+
helm-check: helm-lint helm-test ## Run Helm lint and unit tests
166+
152167
##@ Build
153168

154169
build: setup/ginkgo manifests generate fmt vet ## Build manager binary.
@@ -226,6 +241,7 @@ $(LOCALBIN):
226241
KUSTOMIZE_VERSION ?= v5.4.3
227242
CONTROLLER_TOOLS_VERSION ?= v0.18.0
228243
GOLANGCI_LINT_VERSION ?= v2.1.0
244+
HELM_UNITTEST_VERSION ?= v1.0.3
229245

230246
CONTROLLER_GEN = $(LOCALBIN)/controller-gen
231247
controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary.
@@ -464,6 +480,11 @@ setup/ginkgo:
464480
@echo Installing gomega
465481
@go get github.com/onsi/gomega/...
466482

483+
.PHONY: setup/helm-unittest
484+
setup/helm-unittest:
485+
@helm plugin list 2>/dev/null | grep -q unittest || \
486+
helm plugin install https://github.com/helm-unittest/helm-unittest.git --version $(HELM_UNITTEST_VERSION)
487+
467488
.PHONY: build-installer
468489
build-installer: manifests generate kustomize
469490
mkdir -p dist

docs/FeatureGates.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,37 @@
1+
---
2+
title: Feature Gates
3+
parent: Reference
4+
nav_order: 7
5+
---
6+
17
# Feature Gates
28

39
The Splunk Operator uses the Kubernetes [FeatureGate](https://pkg.go.dev/k8s.io/component-base/featuregate) pattern to control rollout of new functionality. Feature gates allow new code to be merged to the main branch without activating in production, giving teams a safe, per-environment opt-in mechanism.
410

511
## Usage
612

7-
Enable or disable feature gates at operator startup:
13+
### Helm Chart
14+
15+
Set feature gates via the `splunkOperator.featureGates` map in your values file:
16+
17+
```yaml
18+
splunkOperator:
19+
featureGates:
20+
ValidationWebhook: true
21+
```
22+
23+
Or pass them on the command line:
24+
25+
```bash
26+
helm install splunk-operator splunk/splunk-operator \
27+
--set splunkOperator.featureGates.ValidationWebhook=true
28+
```
29+
30+
The chart formats the map into a single `--feature-gates=Key1=true,Key2=false` argument automatically. Adding a new gate requires no chart changes — just add an entry to the map.
31+
32+
### Direct Binary
33+
34+
When running the operator binary directly, pass feature gates at startup:
835

936
```bash
1037
/manager --feature-gates=ValidationWebhook=true

docs/ValidationWebhook.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,26 @@ If you prefer not to use cert-manager, you can provide your own TLS certificates
8383

8484
### Deployment Options
8585

86-
#### Option 1: Use the Webhook-Enabled Kustomize Overlay
86+
#### Option 1: Enable via Helm Feature Gates
87+
88+
If deploying with Helm, enable the feature gate through the `splunkOperator.featureGates` value:
89+
90+
```bash
91+
helm install splunk-operator splunk/splunk-operator \
92+
--set splunkOperator.featureGates.ValidationWebhook=true
93+
```
94+
95+
Or in your values file:
96+
97+
```yaml
98+
splunkOperator:
99+
featureGates:
100+
ValidationWebhook: true
101+
```
102+
103+
**Note:** This requires the webhook Kubernetes resources (Service, ValidatingWebhookConfiguration, TLS certificates) to be deployed separately.
104+
105+
#### Option 2: Use the Webhook-Enabled Kustomize Overlay
87106
88107
Deploy using the `config/default-with-webhook` overlay which includes all necessary webhook components and enables the `ValidationWebhook` feature gate automatically:
89108

@@ -94,7 +113,7 @@ make deploy IMG=<your-image> ENVIRONMENT=default-with-webhook \
94113

95114
This uses the same `make deploy` target as the standard deployment, which substitutes the `WATCH_NAMESPACE`, `SPLUNK_ENTERPRISE_IMAGE`, and `SPLUNK_GENERAL_TERMS` placeholder values before running `kustomize build`.
96115

97-
#### Option 2: Enable via Feature Gate on Existing Deployment
116+
#### Option 3: Enable via Feature Gate on Existing Deployment
98117

99118
If you already have the operator deployed with the webhook Kubernetes resources (Service, ValidatingWebhookConfiguration, TLS certificates), enable the feature gate by patching the container args:
100119

@@ -105,7 +124,7 @@ kubectl patch deployment splunk-operator-controller-manager -n splunk-operator \
105124

106125
**Note:** This requires the webhook service, ValidatingWebhookConfiguration, and TLS certificates to already be deployed. Use Option 1 for a complete deployment.
107126

108-
#### Option 3: Modify Default Kustomization
127+
#### Option 4: Modify Default Kustomization
109128

110129
Edit `config/default/kustomization.yaml` to uncomment the webhook-related sections:
111130

helm-chart/splunk-operator/templates/_helpers.tpl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,15 @@ Define namespace of release and allow for namespace override
7272
{{- default .Release.Namespace .Values.namespaceOverride }}
7373
{{- end }}
7474

75+
{{/*
76+
Format splunkOperator.featureGates map into a --feature-gates arg value.
77+
Produces: Key1=true,Key2=false
78+
*/}}
79+
{{- define "splunk-operator.featureGates" -}}
80+
{{- $pairs := list -}}
81+
{{- range $gate, $enabled := .Values.splunkOperator.featureGates -}}
82+
{{- $pairs = append $pairs (printf "%s=%t" $gate $enabled) -}}
83+
{{- end -}}
84+
{{- join "," $pairs -}}
85+
{{- end }}
86+

helm-chart/splunk-operator/templates/deployment.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ spec:
4848
args:
4949
- --leader-elect
5050
- --pprof
51+
{{- if .Values.splunkOperator.featureGates }}
52+
- --feature-gates={{ include "splunk-operator.featureGates" . }}
53+
{{- end }}
5154
command:
5255
- /manager
5356
{{- with .Values.splunkOperator.livenessProbe }}
@@ -78,7 +81,7 @@ spec:
7881
- name: SPLUNK_GENERAL_TERMS
7982
value: {{ .Values.splunkOperator.splunkGeneralTerms | default "" }}
8083
{{- with .Values.extraEnvs }}
81-
{{- toYaml . | trim | nindent 10 }}
84+
{{- toYaml . | trim | nindent 12 }}
8285
{{- end }}
8386
ports:
8487
{{- range .Values.splunkOperator.service.ports }}
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
suite: deployment
2+
templates:
3+
- templates/deployment.yaml
4+
tests:
5+
- it: should be a Deployment
6+
asserts:
7+
- isKind:
8+
of: Deployment
9+
10+
- it: should set the correct name
11+
asserts:
12+
- equal:
13+
path: metadata.name
14+
value: splunk-operator-controller-manager
15+
16+
- it: should use Recreate strategy
17+
asserts:
18+
- equal:
19+
path: spec.strategy.type
20+
value: Recreate
21+
22+
- it: should include default args without feature-gates
23+
asserts:
24+
- equal:
25+
path: spec.template.spec.containers[0].args
26+
value:
27+
- --leader-elect
28+
- --pprof
29+
30+
- it: should include feature-gates arg with a single gate
31+
set:
32+
splunkOperator.featureGates:
33+
ValidationWebhook: true
34+
asserts:
35+
- contains:
36+
path: spec.template.spec.containers[0].args
37+
content: --feature-gates=ValidationWebhook=true
38+
39+
- it: should include feature-gates arg with multiple gates
40+
set:
41+
splunkOperator.featureGates:
42+
ValidationWebhook: true
43+
MyNewFeature: false
44+
asserts:
45+
- matchRegex:
46+
path: spec.template.spec.containers[0].args[2]
47+
pattern: "^--feature-gates="
48+
- matchRegex:
49+
path: spec.template.spec.containers[0].args[2]
50+
pattern: "ValidationWebhook=true"
51+
- matchRegex:
52+
path: spec.template.spec.containers[0].args[2]
53+
pattern: "MyNewFeature=false"
54+
55+
- it: should not include feature-gates arg when featureGates is empty
56+
set:
57+
splunkOperator.featureGates: {}
58+
asserts:
59+
- equal:
60+
path: spec.template.spec.containers[0].args
61+
value:
62+
- --leader-elect
63+
- --pprof
64+
65+
- it: should always include --leader-elect and --pprof even with feature gates
66+
set:
67+
splunkOperator.featureGates:
68+
ValidationWebhook: true
69+
asserts:
70+
- contains:
71+
path: spec.template.spec.containers[0].args
72+
content: --leader-elect
73+
- contains:
74+
path: spec.template.spec.containers[0].args
75+
content: --pprof
76+
77+
- it: should set the command to /manager
78+
asserts:
79+
- equal:
80+
path: spec.template.spec.containers[0].command
81+
value:
82+
- /manager
83+
84+
- it: should use the configured image
85+
set:
86+
splunkOperator.image.repository: my-registry/splunk-operator:test
87+
asserts:
88+
- equal:
89+
path: spec.template.spec.containers[0].image
90+
value: my-registry/splunk-operator:test
91+
92+
- it: should set WATCH_NAMESPACE to watchNamespaces when clusterWideAccess is true
93+
set:
94+
splunkOperator.clusterWideAccess: true
95+
splunkOperator.watchNamespaces: "ns1,ns2"
96+
asserts:
97+
- contains:
98+
path: spec.template.spec.containers[0].env
99+
content:
100+
name: WATCH_NAMESPACE
101+
value: "ns1,ns2"
102+
103+
- it: should set WATCH_NAMESPACE to release namespace when clusterWideAccess is false
104+
release:
105+
namespace: my-namespace
106+
set:
107+
splunkOperator.clusterWideAccess: false
108+
asserts:
109+
- contains:
110+
path: spec.template.spec.containers[0].env
111+
content:
112+
name: WATCH_NAMESPACE
113+
value: my-namespace
114+
115+
- it: should add custom labels to deployment
116+
set:
117+
splunkOperator.labels:
118+
custom-label: custom-value
119+
asserts:
120+
- equal:
121+
path: metadata.labels.custom-label
122+
value: custom-value
123+
124+
- it: should add custom annotations to deployment
125+
set:
126+
splunkOperator.annotations:
127+
custom-annotation: custom-value
128+
asserts:
129+
- equal:
130+
path: metadata.annotations.custom-annotation
131+
value: custom-value
132+
133+
- it: should add pod labels
134+
set:
135+
splunkOperator.podLabels:
136+
pod-label: pod-value
137+
asserts:
138+
- equal:
139+
path: spec.template.metadata.labels.pod-label
140+
value: pod-value
141+
142+
- it: should add pod annotations
143+
set:
144+
splunkOperator.podAnnotations:
145+
pod-annotation: pod-value
146+
asserts:
147+
- equal:
148+
path: spec.template.metadata.annotations.pod-annotation
149+
value: pod-value
150+
151+
- it: should set security context defaults
152+
asserts:
153+
- equal:
154+
path: spec.template.spec.containers[0].securityContext.allowPrivilegeEscalation
155+
value: false
156+
- equal:
157+
path: spec.template.spec.containers[0].securityContext.readOnlyRootFilesystem
158+
value: true
159+
- equal:
160+
path: spec.template.spec.containers[0].securityContext.runAsNonRoot
161+
value: true
162+
163+
- it: should disable host namespaces by default
164+
asserts:
165+
- equal:
166+
path: spec.template.spec.hostNetwork
167+
value: false
168+
- equal:
169+
path: spec.template.spec.hostPID
170+
value: false
171+
- equal:
172+
path: spec.template.spec.hostIPC
173+
value: false
174+
175+
- it: should set extra environment variables
176+
set:
177+
extraEnvs:
178+
- name: MY_VAR
179+
value: my-value
180+
asserts:
181+
- contains:
182+
path: spec.template.spec.containers[0].env
183+
content:
184+
name: MY_VAR
185+
value: my-value

0 commit comments

Comments
 (0)