Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion charts/cron-job/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ apiVersion: v2
description: EcoVadis Helm chart for K8s Cron Job
name: charts-cron-job
type: application
version: 2.10.0
version: 2.11.0
dependencies:
- name: charts-core
version: 2.4.2
Expand Down
61 changes: 61 additions & 0 deletions charts/cron-job/templates/helmtests/helm-test-rbac.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{{- if .Values.global.helmTest.enabled }}

{{- $cronjobs := $.Values.cronjobs }}
{{- if not $cronjobs }}
{{- $cronjobs = list (dict) }}
{{- end }}

{{- range $index, $cronjob := $cronjobs }}
{{- $cronjobName := include "charts-cron-job.fullname" (dict "root" $ "cronjob" $cronjob "index" $index) }}
{{- $saName := printf "%s-helm-test" $cronjobName | trunc 63 | trimSuffix "-" }}
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ $saName }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "charts-cron-job.labels" (dict "root" $ "cronjob" $cronjob) | nindent 4 }}
annotations:
helm.sh/hook: test
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ $saName }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "charts-cron-job.labels" (dict "root" $ "cronjob" $cronjob) | nindent 4 }}
annotations:
helm.sh/hook: test
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
rules:
- apiGroups: ["batch"]
resources: ["cronjobs"]
verbs: ["get"]
- apiGroups: ["batch"]
resources: ["jobs"]
verbs: ["get", "list", "watch", "create", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ $saName }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "charts-cron-job.labels" (dict "root" $ "cronjob" $cronjob) | nindent 4 }}
annotations:
helm.sh/hook: test
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: {{ $saName }}
subjects:
- kind: ServiceAccount
name: {{ $saName }}
namespace: {{ $.Release.Namespace }}
{{- end }}

{{- end }}
124 changes: 124 additions & 0 deletions charts/cron-job/templates/helmtests/helm-test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
{{- if .Values.global.helmTest.enabled }}

{{- $cronjobs := $.Values.cronjobs }}
{{- if not $cronjobs }}
{{- $cronjobs = list (dict) }}
{{- end }}

{{- range $index, $cronjob := $cronjobs }}
{{- $global := $.Values.global }}
{{- $concurrencyPolicy := $cronjob.concurrencyPolicy | default $global.concurrencyPolicy }}
{{- $activeDeadlineSeconds := $cronjob.activeDeadlineSeconds | default $global.activeDeadlineSeconds }}
{{- $cronjobName := include "charts-cron-job.fullname" (dict "root" $ "cronjob" $cronjob "index" $index) }}
{{- $saName := printf "%s-helm-test" $cronjobName | trunc 63 | trimSuffix "-" }}
{{- $executorName := printf "%s-helm-test-executor" $cronjobName | trunc 63 | trimSuffix "-" }}
{{- /* Spawned test Job name base: truncated to 46 chars so the runtime suffix
"-<10-digit-epoch>-<5-digit-random>" (17 chars max) fits within the
63-char Kubernetes resource name limit. */ -}}
{{- $jobNameBase := printf "%s-test" $cronjobName | trunc 46 | trimSuffix "-" }}
---
apiVersion: batch/v1
kind: Job
metadata:
name: {{ $executorName }}
namespace: {{ $.Release.Namespace }}
labels:
{{- include "charts-cron-job.labels" (dict "root" $ "cronjob" $cronjob) | nindent 4 }}
annotations:
helm.sh/hook: test
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
spec:
backoffLimit: 1
template:
metadata:
labels:
{{- include "charts-cron-job.labels" (dict "root" $ "cronjob" $cronjob) | nindent 8 }}
spec:
serviceAccountName: {{ $saName }}
restartPolicy: Never
containers:
- name: helm-test
image: {{ $.Values.global.image.repository }}/{{ $.Values.global.helmTest.image }}
command:
- /bin/sh
- -c
- |
set -e

CRONJOB="{{ $cronjobName }}"
NAMESPACE="{{ $.Release.Namespace }}"
POLICY="{{ $concurrencyPolicy }}"
TIMEOUT="{{ $activeDeadlineSeconds }}"

# Unique suffix: epoch seconds + shell $RANDOM (0-32767).
# Base is pre-truncated to 46 chars at render time so the full name
# stays within the 63-char Kubernetes resource name limit.
JOB_NAME="{{ $jobNameBase }}-$(date +%s)-${RANDOM}"

echo "==> Helm test for CronJob: ${CRONJOB}"
echo " ConcurrencyPolicy : ${POLICY}"
echo " Test Job name : ${JOB_NAME}"
echo " Timeout : ${TIMEOUT}s"

# ── Forbid: wait for any currently-active job to finish ──────────
if [ "$POLICY" = "Forbid" ]; then
echo "==> ConcurrencyPolicy=Forbid: waiting for active jobs to finish before creating test job..."

# Resolve the CronJob UID at runtime. Jobs spawned by this CronJob
# carry an ownerReference pointing to this UID — the authoritative
# way to identify all jobs that belong to a specific CronJob instance
# (as seen in the job's .metadata.ownerReferences[].uid field).
CRONJOB_UID=$(kubectl get cronjob "${CRONJOB}" -n "${NAMESPACE}" \
-o jsonpath="{.metadata.uid}")
echo " CronJob UID: ${CRONJOB_UID}"

ELAPSED=0
while true; do
# Emit "<ownerUID> <activeCount>" for every job, then keep only
# lines where the ownerUID matches this CronJob and activeCount > 0.
ACTIVE=$(kubectl get jobs -n "${NAMESPACE}" \
-o jsonpath="{range .items[*]}{.metadata.ownerReferences[0].uid}{' '}{.status.active}{'\n'}{end}" \
| awk -v uid="${CRONJOB_UID}" \
"NF==2 && \$1==uid && \$2+0>0 {count++} END {print count+0}")
if [ "${ACTIVE}" -eq 0 ]; then
echo " No active jobs found. Proceeding."
break
fi
if [ "${ELAPSED}" -ge "${TIMEOUT}" ]; then
echo "ERROR: Timed out after ${TIMEOUT}s waiting for active jobs to finish."
exit 1
fi
echo " ${ACTIVE} active job(s) still running. Waiting 10s... (${ELAPSED}s elapsed)"
sleep 10
ELAPSED=$((ELAPSED + 10))
done

# ── Replace: delete any active scheduler-spawned jobs first ──────
elif [ "$POLICY" = "Replace" ]; then
echo "==> ConcurrencyPolicy=Replace: deleting active jobs owned by this CronJob..."
CRONJOB_UID=$(kubectl get cronjob "${CRONJOB}" -n "${NAMESPACE}" \
-o jsonpath="{.metadata.uid}")
# Find names of all jobs whose ownerReference points to this CronJob
# and that currently have active pods, then delete them.
kubectl get jobs -n "${NAMESPACE}" \
-o jsonpath="{range .items[*]}{.metadata.ownerReferences[0].uid}{' '}{.status.active}{' '}{.metadata.name}{'\n'}{end}" \
| awk -v uid="${CRONJOB_UID}" "NF==3 && \$1==uid && \$2+0>0 {print \$3}" \
| xargs -r kubectl delete job -n "${NAMESPACE}" --ignore-not-found
echo " Done."
fi

# ── Create the test job from the CronJob ─────────────────────────
echo "==> Creating test job '${JOB_NAME}' from CronJob '${CRONJOB}'..."
kubectl create job "${JOB_NAME}" --from=cronjob/"${CRONJOB}" -n "${NAMESPACE}"

# ── Wait for completion ───────────────────────────────────────────
echo "==> Waiting for job '${JOB_NAME}' to complete (timeout: ${TIMEOUT}s)..."
kubectl wait job/"${JOB_NAME}" \
-n "${NAMESPACE}" \
--for=condition=complete \
--timeout="${TIMEOUT}s"

echo "==> Test job completed successfully."
{{- end }}

{{- end }}
6 changes: 5 additions & 1 deletion charts/cron-job/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,8 @@ global:
enabled: false

canary:
enabled: false
enabled: false

helmTest:
enabled: true
image: dockerhub/alpine/kubectl:latest # Image used by the helm test pod; must contain kubectl
Loading