Files
dokku/plugins/scheduler-k3s/templates/chart/cron-job.yaml
Jose Diaz-Gonzalez 96d726c43c fix: retire cron containers past their active deadline
Cron containers were never reaped once they exceeded their active deadline, so a hung cron task ran indefinitely instead of being retired after 24 hours as documented. `cron:run` now also accepts a `--ttl-seconds` argument, matching the one `dokku run` already takes.
2026-08-12 13:11:34 -04:00

189 lines
8.5 KiB
YAML

{{- range $processName, $config := .Values.processes }}
{{- if not (and (hasKey $config "cron") (hasKey $config.cron "id")) }}
# Skip {{ $processName }} as it is not a cron job
{{- continue }}
{{- end }}
---
apiVersion: batch/v1
kind: CronJob
metadata:
annotations:
app.kubernetes.io/version: {{ $.Values.global.deployment_id | quote }}
dokku.com/builder-type: {{ $.Values.global.image.type | quote }}
dokku.com/cron-hash: {{ $config.cron.hash | quote }}
dokku.com/cron-id: {{ $config.cron.id | quote }}
dokku.com/job-suffix: {{ $config.cron.suffix | quote }}
dokku.com/managed: "true"
kubectl.kubernetes.io/default-container: {{ printf "%s-cron" $.Values.global.app_name | quote }}
{{ include "print.annotations" (dict "config" $.Values.global "key" "cronjob") | indent 4 }}
{{ include "print.annotations" (dict "config" $config "key" "cronjob") | indent 4 }}
labels:
app.kubernetes.io/instance: {{ printf "%s-cron-%s" $.Values.global.app_name $config.cron.suffix | quote }}
app.kubernetes.io/name: cron
app.kubernetes.io/part-of: {{ $.Values.global.app_name | quote }}
dokku.com/cron-hash: {{ $config.cron.hash | quote }}
{{ include "print.labels" (dict "config" $.Values.global "key" "cronjob") | indent 4 }}
{{ include "print.labels" (dict "config" $config "key" "cronjob") | indent 4 }}
name: {{ $.Values.global.app_name }}-cron-{{ $config.cron.suffix }}
namespace: {{ $.Values.global.namespace }}
spec:
concurrencyPolicy: {{ $config.cron.concurrency_policy }}
failedJobsHistoryLimit: 10
jobTemplate:
metadata:
annotations:
app.kubernetes.io/version: {{ $.Values.global.deployment_id | quote }}
dokku.com/builder-type: {{ $.Values.global.image.type | quote }}
dokku.com/cron-hash: {{ $config.cron.hash | quote }}
dokku.com/cron-id: {{ $config.cron.id | quote }}
dokku.com/job-suffix: {{ $config.cron.suffix | quote }}
dokku.com/managed: "true"
kubectl.kubernetes.io/default-container: {{ printf "%s-cron" $.Values.global.app_name | quote }}
{{ include "print.annotations" (dict "config" $.Values.global "key" "job") | indent 8 }}
{{ include "print.annotations" (dict "config" $config "key" "job") | indent 8 }}
labels:
app.kubernetes.io/instance: {{ printf "%s-cron-%s" $.Values.global.app_name $config.cron.suffix | quote }}
app.kubernetes.io/name: cron
app.kubernetes.io/part-of: {{ $.Values.global.app_name | quote }}
dokku.com/cron-hash: {{ $config.cron.hash | quote }}
{{ include "print.labels" (dict "config" $.Values.global "key" "job") | indent 8 }}
{{ include "print.labels" (dict "config" $config "key" "job") | indent 8 }}
spec:
backoffLimit: 0
podReplacementPolicy: Failed
ttlSecondsAfterFinished: 60
activeDeadlineSeconds: {{ $config.cron.active_deadline_seconds | default 86400 }}
template:
metadata:
annotations:
app.kubernetes.io/version: {{ $.Values.global.deployment_id | quote }}
dokku.com/builder-type: {{ $.Values.global.image.type | quote }}
dokku.com/cron-hash: {{ $config.cron.hash | quote }}
dokku.com/cron-id: {{ $config.cron.id | quote }}
dokku.com/job-suffix: {{ $config.cron.suffix | quote }}
dokku.com/managed: "true"
kubectl.kubernetes.io/default-container: {{ printf "%s-cron" $.Values.global.app_name | quote }}
{{ include "print.annotations" (dict "config" $.Values.global "key" "pod") | indent 12 }}
{{ include "print.annotations" (dict "config" $config "key" "pod") | indent 12 }}
labels:
app.kubernetes.io/instance: {{ printf "%s-cron-%s" $.Values.global.app_name $config.cron.suffix | quote }}
app.kubernetes.io/name: cron
app.kubernetes.io/part-of: {{ $.Values.global.app_name | quote }}
dokku.com/cron-hash: {{ $config.cron.hash | quote }}
{{ include "print.labels" (dict "config" $.Values.global "key" "pod") | indent 12 }}
{{ include "print.labels" (dict "config" $config "key" "pod") | indent 12 }}
spec:
{{- if and (hasKey $.Values.global "security_context") $.Values.global.security_context.sysctls }}
securityContext:
sysctls:
{{- range $.Values.global.security_context.sysctls }}
- name: {{ .name }}
value: {{ .value | quote }}
{{- end }}
{{- end }}
containers:
- args:
{{- range $config.args }}
- {{ . }}
{{- end }}
{{- if eq $.Values.global.image.type "pack" }}
command:
- launcher
{{- end }}
envFrom:
- secretRef:
name: config-{{ $.Values.global.app_name }}
optional: true
image: {{ $.Values.global.image.name }}
imagePullPolicy: Always
name: {{ $.Values.global.app_name }}-cron
{{- if and $config.resources (or $config.resources.limits $config.resources.requests) }}
resources:
{{- if $config.resources.limits }}
limits:
{{- if $config.resources.limits.cpu }}
cpu: {{ $config.resources.limits.cpu }}
{{- end }}
{{- if $config.resources.limits.memory }}
memory: {{ $config.resources.limits.memory }}
{{- end }}
{{- end }}
{{- if $config.resources.requests }}
requests:
{{- if $config.resources.requests.cpu }}
cpu: {{ $config.resources.requests.cpu }}
{{- end }}
{{- if $config.resources.requests.memory }}
memory: {{ $config.resources.requests.memory }}
{{- end }}
{{- end }}
{{- end }}
{{- if hasKey $.Values.global "security_context" }}
securityContext:
{{- if $.Values.global.security_context.privileged }}
privileged: true
{{- end }}
{{- if hasKey $.Values.global.security_context "capabilities" }}
capabilities:
{{- if hasKey $.Values.global.security_context.capabilities "add" }}
add:
{{- range $.Values.global.security_context.capabilities.add }}
- {{ . }}
{{- end }}
{{- end }}
{{- if hasKey $.Values.global.security_context.capabilities "drop" }}
drop:
{{- range $.Values.global.security_context.capabilities.drop }}
- {{ . }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- if $.Values.global.image.working_dir }}
workingDir: {{ $.Values.global.image.working_dir }}
{{- end }}
{{- if $config.volumes }}
volumeMounts:
{{- range $volume := $config.volumes }}
- name: {{ $volume.name }}
mountPath: {{ $volume.mount_path }}
{{- if $volume.sub_path }}
subPath: {{ $volume.sub_path }}
{{- end }}
{{- if $volume.read_only }}
readOnly: true
{{- end }}
{{- end }}
{{- end }}
{{- if $.Values.global.image.image_pull_secrets }}
imagePullSecrets:
- name: {{ $.Values.global.image.image_pull_secrets }}
{{- end }}
restartPolicy: Never
serviceAccountName: {{ $.Values.global.app_name }}
{{- if $config.volumes }}
volumes:
{{- range $volume := $config.volumes }}
- name: {{ $volume.name }}
{{- if $volume.empty_dir }}
emptyDir:
{{- if $volume.empty_dir.medium }}
medium: {{ $volume.empty_dir.medium }}
{{- end }}
{{- if $volume.empty_dir.size_limit }}
sizeLimit: {{ $volume.empty_dir.size_limit }}
{{- end }}
{{- end }}
{{- if $volume.persistent_claim }}
persistentVolumeClaim:
claimName: {{ $volume.persistent_claim.claim_name }}
{{- end }}
{{- end }}
{{- end }}
schedule: {{ $config.cron.schedule }}
startingDeadlineSeconds: 60
successfulJobsHistoryLimit: 10
suspend: {{ $config.cron.suspend }}
timeZone: Etc/UTC
{{- end }}