Skip to content

Commit e5f6f11

Browse files
Merge pull request #207 from EcovadisCode/feat/cronjob-test
Cronjob tests
2 parents b921e61 + 44d5417 commit e5f6f11

4 files changed

Lines changed: 191 additions & 2 deletions

File tree

charts/cron-job/Chart.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ apiVersion: v2
22
description: EcoVadis Helm chart for K8s Cron Job
33
name: charts-cron-job
44
type: application
5-
version: 2.10.0
5+
version: 2.11.0
66
dependencies:
77
- name: charts-core
88
version: 2.4.2
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{{- if .Values.global.helmTest.enabled }}
2+
3+
{{- $cronjobs := $.Values.cronjobs }}
4+
{{- if not $cronjobs }}
5+
{{- $cronjobs = list (dict) }}
6+
{{- end }}
7+
8+
{{- range $index, $cronjob := $cronjobs }}
9+
{{- $cronjobName := include "charts-cron-job.fullname" (dict "root" $ "cronjob" $cronjob "index" $index) }}
10+
{{- $saName := printf "%s-helm-test" $cronjobName | trunc 63 | trimSuffix "-" }}
11+
---
12+
apiVersion: v1
13+
kind: ServiceAccount
14+
metadata:
15+
name: {{ $saName }}
16+
namespace: {{ $.Release.Namespace }}
17+
labels:
18+
{{- include "charts-cron-job.labels" (dict "root" $ "cronjob" $cronjob) | nindent 4 }}
19+
annotations:
20+
helm.sh/hook: test
21+
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
22+
---
23+
apiVersion: rbac.authorization.k8s.io/v1
24+
kind: Role
25+
metadata:
26+
name: {{ $saName }}
27+
namespace: {{ $.Release.Namespace }}
28+
labels:
29+
{{- include "charts-cron-job.labels" (dict "root" $ "cronjob" $cronjob) | nindent 4 }}
30+
annotations:
31+
helm.sh/hook: test
32+
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
33+
rules:
34+
- apiGroups: ["batch"]
35+
resources: ["cronjobs"]
36+
verbs: ["get"]
37+
- apiGroups: ["batch"]
38+
resources: ["jobs"]
39+
verbs: ["get", "list", "watch", "create", "delete"]
40+
---
41+
apiVersion: rbac.authorization.k8s.io/v1
42+
kind: RoleBinding
43+
metadata:
44+
name: {{ $saName }}
45+
namespace: {{ $.Release.Namespace }}
46+
labels:
47+
{{- include "charts-cron-job.labels" (dict "root" $ "cronjob" $cronjob) | nindent 4 }}
48+
annotations:
49+
helm.sh/hook: test
50+
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
51+
roleRef:
52+
apiGroup: rbac.authorization.k8s.io
53+
kind: Role
54+
name: {{ $saName }}
55+
subjects:
56+
- kind: ServiceAccount
57+
name: {{ $saName }}
58+
namespace: {{ $.Release.Namespace }}
59+
{{- end }}
60+
61+
{{- end }}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
{{- if .Values.global.helmTest.enabled }}
2+
3+
{{- $cronjobs := $.Values.cronjobs }}
4+
{{- if not $cronjobs }}
5+
{{- $cronjobs = list (dict) }}
6+
{{- end }}
7+
8+
{{- range $index, $cronjob := $cronjobs }}
9+
{{- $global := $.Values.global }}
10+
{{- $concurrencyPolicy := $cronjob.concurrencyPolicy | default $global.concurrencyPolicy }}
11+
{{- $activeDeadlineSeconds := $cronjob.activeDeadlineSeconds | default $global.activeDeadlineSeconds }}
12+
{{- $cronjobName := include "charts-cron-job.fullname" (dict "root" $ "cronjob" $cronjob "index" $index) }}
13+
{{- $saName := printf "%s-helm-test" $cronjobName | trunc 63 | trimSuffix "-" }}
14+
{{- $executorName := printf "%s-helm-test-executor" $cronjobName | trunc 63 | trimSuffix "-" }}
15+
{{- /* Spawned test Job name base: truncated to 46 chars so the runtime suffix
16+
"-<10-digit-epoch>-<5-digit-random>" (17 chars max) fits within the
17+
63-char Kubernetes resource name limit. */ -}}
18+
{{- $jobNameBase := printf "%s-test" $cronjobName | trunc 46 | trimSuffix "-" }}
19+
---
20+
apiVersion: batch/v1
21+
kind: Job
22+
metadata:
23+
name: {{ $executorName }}
24+
namespace: {{ $.Release.Namespace }}
25+
labels:
26+
{{- include "charts-cron-job.labels" (dict "root" $ "cronjob" $cronjob) | nindent 4 }}
27+
annotations:
28+
helm.sh/hook: test
29+
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
30+
spec:
31+
backoffLimit: 1
32+
template:
33+
metadata:
34+
labels:
35+
{{- include "charts-cron-job.labels" (dict "root" $ "cronjob" $cronjob) | nindent 8 }}
36+
spec:
37+
serviceAccountName: {{ $saName }}
38+
restartPolicy: Never
39+
containers:
40+
- name: helm-test
41+
image: {{ $.Values.global.image.repository }}/{{ $.Values.global.helmTest.image }}
42+
command:
43+
- /bin/sh
44+
- -c
45+
- |
46+
set -e
47+
48+
CRONJOB="{{ $cronjobName }}"
49+
NAMESPACE="{{ $.Release.Namespace }}"
50+
POLICY="{{ $concurrencyPolicy }}"
51+
TIMEOUT="{{ $activeDeadlineSeconds }}"
52+
53+
# Unique suffix: epoch seconds + shell $RANDOM (0-32767).
54+
# Base is pre-truncated to 46 chars at render time so the full name
55+
# stays within the 63-char Kubernetes resource name limit.
56+
JOB_NAME="{{ $jobNameBase }}-$(date +%s)-${RANDOM}"
57+
58+
echo "==> Helm test for CronJob: ${CRONJOB}"
59+
echo " ConcurrencyPolicy : ${POLICY}"
60+
echo " Test Job name : ${JOB_NAME}"
61+
echo " Timeout : ${TIMEOUT}s"
62+
63+
# ── Forbid: wait for any currently-active job to finish ──────────
64+
if [ "$POLICY" = "Forbid" ]; then
65+
echo "==> ConcurrencyPolicy=Forbid: waiting for active jobs to finish before creating test job..."
66+
67+
# Resolve the CronJob UID at runtime. Jobs spawned by this CronJob
68+
# carry an ownerReference pointing to this UID — the authoritative
69+
# way to identify all jobs that belong to a specific CronJob instance
70+
# (as seen in the job's .metadata.ownerReferences[].uid field).
71+
CRONJOB_UID=$(kubectl get cronjob "${CRONJOB}" -n "${NAMESPACE}" \
72+
-o jsonpath="{.metadata.uid}")
73+
echo " CronJob UID: ${CRONJOB_UID}"
74+
75+
ELAPSED=0
76+
while true; do
77+
# Emit "<ownerUID> <activeCount>" for every job, then keep only
78+
# lines where the ownerUID matches this CronJob and activeCount > 0.
79+
ACTIVE=$(kubectl get jobs -n "${NAMESPACE}" \
80+
-o jsonpath="{range .items[*]}{.metadata.ownerReferences[0].uid}{' '}{.status.active}{'\n'}{end}" \
81+
| awk -v uid="${CRONJOB_UID}" \
82+
"NF==2 && \$1==uid && \$2+0>0 {count++} END {print count+0}")
83+
if [ "${ACTIVE}" -eq 0 ]; then
84+
echo " No active jobs found. Proceeding."
85+
break
86+
fi
87+
if [ "${ELAPSED}" -ge "${TIMEOUT}" ]; then
88+
echo "ERROR: Timed out after ${TIMEOUT}s waiting for active jobs to finish."
89+
exit 1
90+
fi
91+
echo " ${ACTIVE} active job(s) still running. Waiting 10s... (${ELAPSED}s elapsed)"
92+
sleep 10
93+
ELAPSED=$((ELAPSED + 10))
94+
done
95+
96+
# ── Replace: delete any active scheduler-spawned jobs first ──────
97+
elif [ "$POLICY" = "Replace" ]; then
98+
echo "==> ConcurrencyPolicy=Replace: deleting active jobs owned by this CronJob..."
99+
CRONJOB_UID=$(kubectl get cronjob "${CRONJOB}" -n "${NAMESPACE}" \
100+
-o jsonpath="{.metadata.uid}")
101+
# Find names of all jobs whose ownerReference points to this CronJob
102+
# and that currently have active pods, then delete them.
103+
kubectl get jobs -n "${NAMESPACE}" \
104+
-o jsonpath="{range .items[*]}{.metadata.ownerReferences[0].uid}{' '}{.status.active}{' '}{.metadata.name}{'\n'}{end}" \
105+
| awk -v uid="${CRONJOB_UID}" "NF==3 && \$1==uid && \$2+0>0 {print \$3}" \
106+
| xargs -r kubectl delete job -n "${NAMESPACE}" --ignore-not-found
107+
echo " Done."
108+
fi
109+
110+
# ── Create the test job from the CronJob ─────────────────────────
111+
echo "==> Creating test job '${JOB_NAME}' from CronJob '${CRONJOB}'..."
112+
kubectl create job "${JOB_NAME}" --from=cronjob/"${CRONJOB}" -n "${NAMESPACE}"
113+
114+
# ── Wait for completion ───────────────────────────────────────────
115+
echo "==> Waiting for job '${JOB_NAME}' to complete (timeout: ${TIMEOUT}s)..."
116+
kubectl wait job/"${JOB_NAME}" \
117+
-n "${NAMESPACE}" \
118+
--for=condition=complete \
119+
--timeout="${TIMEOUT}s"
120+
121+
echo "==> Test job completed successfully."
122+
{{- end }}
123+
124+
{{- end }}

charts/cron-job/values.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,8 @@ global:
156156
enabled: false
157157

158158
canary:
159-
enabled: false
159+
enabled: false
160+
161+
helmTest:
162+
enabled: true
163+
image: dockerhub/alpine/kubectl:latest # Image used by the helm test pod; must contain kubectl

0 commit comments

Comments
 (0)