Skip to content

Commit 4dc7966

Browse files
committed
test(helm): add helm test hook + kind apiserver dry-run in CI
- New templates/tests/test-connection.yaml renders a Pod with helm.sh/hook=test that wgets the app Service (and realtime when enabled). Lets users run `helm test <release>` after install for a real in-cluster connectivity check. Restricted PSS context. - tests.* values block (image, timeoutSeconds, resources) is the knob to disable or tune the probe; documented in values.schema.json. - 3 helm-unittest tests cover the hook annotations, PSS context, and tests.enabled=false skip path (41 tests total). - New CI job spins up a kind v1.30 cluster and runs `kubectl apply --dry-run=server` against the rendered manifests for the CRD-free ci fixtures (default / existing-secret / external-db). Catches admission and validation issues the static kubeconform schema check can't see.
1 parent 716a677 commit 4dc7966

5 files changed

Lines changed: 187 additions & 1 deletion

File tree

.github/workflows/helm-chart.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,44 @@ jobs:
7070
-summary
7171
echo "::endgroup::"
7272
done
73+
74+
apiserver-dryrun:
75+
name: API-server dry-run on kind
76+
runs-on: ubuntu-latest
77+
steps:
78+
- name: Checkout
79+
uses: actions/checkout@v4
80+
81+
- name: Set up Helm
82+
uses: azure/setup-helm@v4
83+
with:
84+
version: v3.16.2
85+
86+
- name: Create kind cluster
87+
uses: helm/kind-action@v1
88+
with:
89+
version: v0.24.0
90+
node_image: kindest/node:v1.30.4
91+
wait: 120s
92+
93+
- name: Helm dependency build
94+
run: helm dependency build helm/sim
95+
96+
- name: Server-side dry-run for CRD-free ci values
97+
# Skips fixtures that reference CRDs (ExternalSecret, ServiceMonitor)
98+
# the kind cluster does not have installed. Those are still covered
99+
# by kubeconform in the lint-test job.
100+
run: |
101+
set -euo pipefail
102+
kubectl create namespace sim
103+
for f in \
104+
helm/sim/ci/default-values.yaml \
105+
helm/sim/ci/existing-secret-values.yaml \
106+
helm/sim/ci/external-db-values.yaml; do
107+
echo "::group::dry-run $f"
108+
helm template release helm/sim \
109+
--namespace sim \
110+
-f "$f" \
111+
| kubectl apply --namespace sim --dry-run=server -f -
112+
echo "::endgroup::"
113+
done
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{{- if .Values.tests.enabled }}
2+
apiVersion: v1
3+
kind: Pod
4+
metadata:
5+
name: {{ include "sim.fullname" . }}-test-connection
6+
namespace: {{ .Release.Namespace }}
7+
labels:
8+
{{- include "sim.labels" . | nindent 4 }}
9+
app.kubernetes.io/component: test
10+
annotations:
11+
"helm.sh/hook": test
12+
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
13+
spec:
14+
restartPolicy: Never
15+
{{- with .Values.tests.image.pullSecrets }}
16+
imagePullSecrets:
17+
{{- toYaml . | nindent 4 }}
18+
{{- end }}
19+
securityContext:
20+
runAsNonRoot: true
21+
runAsUser: 65534
22+
runAsGroup: 65534
23+
fsGroup: 65534
24+
seccompProfile:
25+
type: RuntimeDefault
26+
containers:
27+
- name: probe
28+
image: "{{ .Values.tests.image.repository }}:{{ .Values.tests.image.tag }}"
29+
imagePullPolicy: {{ .Values.tests.image.pullPolicy }}
30+
securityContext:
31+
allowPrivilegeEscalation: false
32+
readOnlyRootFilesystem: true
33+
runAsNonRoot: true
34+
runAsUser: 65534
35+
capabilities:
36+
drop:
37+
- ALL
38+
command:
39+
- sh
40+
- -c
41+
- |
42+
set -eu
43+
APP_URL="http://{{ include "sim.fullname" . }}-app:{{ .Values.app.service.port }}/"
44+
echo "Probing $APP_URL"
45+
wget -q -T {{ .Values.tests.timeoutSeconds }} --tries=1 --spider "$APP_URL"
46+
{{- if .Values.realtime.enabled }}
47+
RT_URL="http://{{ include "sim.fullname" . }}-realtime:{{ .Values.realtime.service.port }}/health"
48+
echo "Probing $RT_URL"
49+
wget -q -T {{ .Values.tests.timeoutSeconds }} --tries=1 --spider "$RT_URL"
50+
{{- end }}
51+
echo "OK"
52+
resources:
53+
{{- toYaml .Values.tests.resources | nindent 8 }}
54+
{{- end }}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
suite: helm test hook (helm test) connectivity probe
2+
release:
3+
name: t
4+
namespace: sim
5+
defaults: &defaults
6+
app.env.BETTER_AUTH_SECRET: x
7+
app.env.ENCRYPTION_KEY: x
8+
app.env.INTERNAL_API_SECRET: x
9+
app.env.CRON_SECRET: x
10+
postgresql.auth.password: x
11+
12+
tests:
13+
- it: renders the test pod with helm.sh/hook=test by default
14+
template: tests/test-connection.yaml
15+
set:
16+
<<: *defaults
17+
asserts:
18+
- isKind:
19+
of: Pod
20+
- equal:
21+
path: 'metadata.annotations["helm.sh/hook"]'
22+
value: test
23+
- equal:
24+
path: 'metadata.annotations["helm.sh/hook-delete-policy"]'
25+
value: before-hook-creation,hook-succeeded
26+
- equal:
27+
path: spec.restartPolicy
28+
value: Never
29+
30+
- it: applies restricted PSS context to the test pod
31+
template: tests/test-connection.yaml
32+
set:
33+
<<: *defaults
34+
asserts:
35+
- equal:
36+
path: spec.securityContext.runAsNonRoot
37+
value: true
38+
- equal:
39+
path: spec.containers[0].securityContext.allowPrivilegeEscalation
40+
value: false
41+
- equal:
42+
path: spec.containers[0].securityContext.readOnlyRootFilesystem
43+
value: true
44+
- contains:
45+
path: spec.containers[0].securityContext.capabilities.drop
46+
content: ALL
47+
48+
- it: skips rendering when tests.enabled=false
49+
template: tests/test-connection.yaml
50+
set:
51+
<<: *defaults
52+
tests.enabled: false
53+
asserts:
54+
- hasDocuments:
55+
count: 0

helm/sim/values.schema.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,24 @@
691691
}
692692
}
693693
},
694+
"tests": {
695+
"type": "object",
696+
"description": "Helm test hook configuration (helm test)",
697+
"properties": {
698+
"enabled": { "type": "boolean" },
699+
"image": {
700+
"type": "object",
701+
"properties": {
702+
"repository": { "type": "string" },
703+
"tag": { "type": "string" },
704+
"pullPolicy": { "type": "string", "enum": ["Always", "IfNotPresent", "Never"] },
705+
"pullSecrets": { "type": "array", "items": { "type": "object" } }
706+
}
707+
},
708+
"timeoutSeconds": { "type": "integer", "minimum": 1 },
709+
"resources": { "type": "object" }
710+
}
711+
},
694712
"sharedStorage": {
695713
"type": "object",
696714
"properties": {

helm/sim/values.yaml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1585,4 +1585,22 @@ certManager:
15851585
# CA ClusterIssuer configuration
15861586
# This is the issuer that applications should reference for obtaining certificates
15871587
caIssuer:
1588-
name: "sim-ca-issuer"
1588+
name: "sim-ca-issuer"
1589+
# Helm test hook (helm test) connectivity probes.
1590+
# Renders a Pod with helm.sh/hook=test that checks the app and realtime
1591+
# Services are reachable from inside the cluster. Run with: helm test <release>
1592+
tests:
1593+
enabled: true
1594+
image:
1595+
repository: busybox
1596+
tag: "1.36"
1597+
pullPolicy: IfNotPresent
1598+
pullSecrets: []
1599+
timeoutSeconds: 5
1600+
resources:
1601+
limits:
1602+
cpu: 100m
1603+
memory: 64Mi
1604+
requests:
1605+
cpu: 10m
1606+
memory: 16Mi

0 commit comments

Comments
 (0)