diff --git a/library/common-test/tests/cronjob/metadata_test.yaml b/library/common-test/tests/cronjob/metadata_test.yaml new file mode 100644 index 0000000000..bf8cf89b15 --- /dev/null +++ b/library/common-test/tests/cronjob/metadata_test.yaml @@ -0,0 +1,57 @@ +suite: cronjob metadata test +templates: + - common.yaml +chart: + appVersion: &appVer v9.9.9 +tests: + - it: should pass with cronjob created with labels and annotations + set: + label1: label1 + label2: global_label2 + annotation1: annotation1 + annotation2: global_annotation2 + global: + labels: + g_label1: global_label1 + g_label2: "{{ .Values.label2 }}" + annotations: + g_annotation1: global_annotation1 + g_annotation2: "{{ .Values.annotation2 }}" + controllers: + controller-name: + enabled: true + primary: true + type: CronJob + schedule: "*/1 * * * *" + labels: + label1: "{{ .Values.label1 }}" + label2: label2 + annotations: + annotation1: "{{ .Values.annotation1 }}" + annotation2: annotation2 + asserts: + - documentIndex: &cronJobDoc 0 + isKind: + of: CronJob + - documentIndex: *cronJobDoc + equal: + path: metadata.annotations + value: + annotation1: annotation1 + annotation2: annotation2 + g_annotation1: global_annotation1 + g_annotation2: global_annotation2 + - documentIndex: *cronJobDoc + equal: + path: metadata.labels + value: + app: common-test-1.0.0 + release: release-name + helm-revision: 0 + helm.sh/chart: common-test-1.0.0 + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: *appVer + g_label1: global_label1 + g_label2: global_label2 + label1: label1 + label2: label2 diff --git a/library/common-test/tests/cronjob/spec_test.yaml b/library/common-test/tests/cronjob/spec_test.yaml new file mode 100644 index 0000000000..f6e4a539d9 --- /dev/null +++ b/library/common-test/tests/cronjob/spec_test.yaml @@ -0,0 +1,81 @@ +suite: cronjob spec test +templates: + - common.yaml +tests: + - it: should pass with controller enabled + set: + controllers: + controller-name: + enabled: true + primary: true + type: CronJob + schedule: "* * * * *" + asserts: + - documentIndex: &cronJobDoc 0 + isKind: + of: CronJob + - documentIndex: *cronJobDoc + isAPIVersion: + of: batch/v1 + - documentIndex: *cronJobDoc + isSubset: + path: spec + content: + schedule: "* * * * *" + timeZone: UTC + concurrencyPolicy: Forbid + failedJobsHistoryLimit: 1 + successfulJobsHistoryLimit: 3 + startingDeadlineSeconds: + - documentIndex: *cronJobDoc + isSubset: + path: spec.jobTemplate.spec + content: + backoffLimit: 5 + completionMode: NonIndexed + completions: + parallelism: 1 + ttlSecondsAfterFinished: 120 + + - it: should apply spec correctly + set: + cron: "*/5 * * * *" + someTZ: America/New_York + controllers: + controller-name: + enabled: true + primary: true + type: CronJob + schedule: "{{ .Values.cron }}" + timezone: "{{ .Values.someTZ }}" + concurrencyPolicy: Allow + failedJobsHistoryLimit: 2 + successfulJobsHistoryLimit: 4 + startingDeadlineSeconds: 100 + backoffLimit: 5 + completionMode: Indexed + completions: 5 + parallelism: 5 + ttlSecondsAfterFinished: 100 + activeDeadlineSeconds: 100 + asserts: + - documentIndex: *cronJobDoc + isSubset: + path: spec + content: + schedule: "*/5 * * * *" + concurrencyPolicy: Allow + failedJobsHistoryLimit: 2 + successfulJobsHistoryLimit: 4 + startingDeadlineSeconds: 100 + timeZone: America/New_York + - documentIndex: *cronJobDoc + isSubset: + path: spec.jobTemplate.spec + content: + backoffLimit: 5 + completionMode: Indexed + completions: 5 + parallelism: 5 + ttlSecondsAfterFinished: 100 + activeDeadlineSeconds: 100 diff --git a/library/common-test/tests/cronjob/validation_test.yaml b/library/common-test/tests/cronjob/validation_test.yaml new file mode 100644 index 0000000000..06bebfcb3c --- /dev/null +++ b/library/common-test/tests/cronjob/validation_test.yaml @@ -0,0 +1,27 @@ +suite: cronjob validation test +templates: + - common.yaml +tests: + - it: should fail with invalid concurrencyPolicy + set: + controllers: + controller-name: + enabled: true + primary: true + type: CronJob + concurrencyPolicy: not-a-policy + asserts: + - failedTemplate: + errorMessage: CronJob - Expected to be one of [Allow, Forbid, Replace], but got [not-a-policy] + + - it: should fail with empty schedule + set: + controllers: + controller-name: + enabled: true + primary: true + type: CronJob + schedule: + asserts: + - failedTemplate: + errorMessage: CronJob - Expected non-empty diff --git a/library/common/1.0.0/README.md b/library/common/1.0.0/README.md index dd18b33b62..d21b7bc486 100644 --- a/library/common/1.0.0/README.md +++ b/library/common/1.0.0/README.md @@ -13,3 +13,6 @@ - Primary: `$FullName` - Others: `$FullName-$PodName` - Containers: `$ContainerName` + +> Full name -> `$ReleaseName-$ChartName` +> Any name that exceeds 63 characters, will throw an error diff --git a/library/common/1.0.0/docs/cronjob.md b/library/common/1.0.0/docs/cronjob.md new file mode 100644 index 0000000000..49eca4e782 --- /dev/null +++ b/library/common/1.0.0/docs/cronjob.md @@ -0,0 +1,69 @@ +# CronJob + +| Key | Type | Required | Helm Template | Default | Description | +| :------------------------------------------------------- | :-------: | :------: | :-----------: | :----------------: | :---------------------------------------------------- | +| controllers | `dict` | ❌ | ❌ | `{}` | Define the controllers as dicts | +| controllers.[controller-name] | `dict` | ✅ | ❌ | `{}` | Holds controller definition | +| controllers.[controller-name].enabled | `boolean` | ✅ | ❌ | `false` | Enables or Disables the controller | +| controllers.[controller-name].primary | `boolean` | ✅ | ❌ | `false` | Sets the controller as primary | +| controllers.[controller-name].labels | `dict` | ❌ | ✅ | `{}` | Additional labels for controller | +| controllers.[controller-name].annotations | `dict` | ❌ | ✅ | `{}` | Additional annotations for controller | +| controllers.[controller-name].type | `string` | ✅ | ❌ | `""` | Define the type (kind) of the controller | +| controllers.[controller-name].schedule | `string` | ✅ | ✅ | `""` | Define the schedule | +| controllers.[controller-name].timezone | `string` | ❌ | ✅ | `{{ .Values.TZ }}` | Define the timezone | +| controllers.[controller-name].concurrencyPolicy | `string` | ❌ | ✅ | `Forbid` | Define the concurrencyPolicy (Allow, Replace, Forbid) | +| controllers.[controller-name].failedJobsHistoryLimit | `int` | ❌ | ❌ | `1` | Define the failedJobsHistoryLimit | +| controllers.[controller-name].successfulJobsHistoryLimit | `int` | ❌ | ❌ | `3` | Define the successfulJobsHistoryLimit | +| controllers.[controller-name].startingDeadlineSeconds | `int` | ❌ | ❌ | | Define the startingDeadlineSeconds | +| controllers.[controller-name].completionMode | `string` | ❌ | ❌ | `NonIndexed` | Define the completionMode (Indexed, NonIndexed) | +| controllers.[controller-name].backoffLimit | `int` | ❌ | ❌ | `5` | Define the backoffLimit | +| controllers.[controller-name].completions | `int` | ❌ | ❌ | | Define the completions | +| controllers.[controller-name].parallelism | `int` | ❌ | ❌ | `1` | Define the parallelism | +| controllers.[controller-name].ttlSecondsAfterFinished | `int` | ❌ | ❌ | `120` | Define the ttlSecondsAfterFinished | +| controllers.[controller-name].activeDeadlineSeconds | `int` | ❌ | ❌ | | Define the activeDeadlineSeconds | + +--- + +Appears in: + +- `.Values.controllers` + +--- + +Naming scheme: + +- Primary: `$FullName` (release-name-chart-name) +- Non-Primary: `$FullName-$ControllerName` (release-name-chart-name-controller-name) + +--- + +Examples: + +```yaml +controllers: + controller-name: + enabled: true + primary: true + type: CronJob + labels: {} + annotations: {} + schedule: "{{ .Values.cron }}" + timezone: "{{ .Values.someTZ }}" + concurrencyPolicy: Allow + failedJobsHistoryLimit: 2 + successfulJobsHistoryLimit: 4 + startingDeadlineSeconds: 100 + backoffLimit: 5 + completionMode: Indexed + completions: 5 + parallelism: 5 + ttlSecondsAfterFinished: 100 + activeDeadlineSeconds: 100 + other-controller-name: + enabled: true + primary: false + type: CronJob + labels: {} + annotations: {} + schedule: "* * * * *" +``` diff --git a/library/common/1.0.0/templates/classes/_cronjob.tpl b/library/common/1.0.0/templates/classes/_cronjob.tpl index e69de29bb2..0cbce5a2f2 100644 --- a/library/common/1.0.0/templates/classes/_cronjob.tpl +++ b/library/common/1.0.0/templates/classes/_cronjob.tpl @@ -0,0 +1,32 @@ +{{/* CronJob Class */}} +{{/* Call this template: +{{ include "ix.v1.common.class.cronjob" (dict "objectData" $objectData "rootCtx" $) }} + +rootCtx: The root context of the template. It is used to access the global context. +objectData: The object data to be used to render the CronJob. +*/}} + +{{- define "ix.v1.common.class.cronjob" -}} + + {{- $rootCtx := .rootCtx -}} + {{- $objectData := .objectData -}} + {{- include "ix.v1.common.lib.controller.cronjobValidation" (dict "objectData" $objectData) }} +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: {{ $objectData.name }} + {{- $labels := (mustMerge ($objectData.labels | default dict) (include "ix.v1.common.lib.metadata.allLabels" $rootCtx | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "labels" $labels) | trim) }} + labels: + {{- . | nindent 4 }} + {{- end -}} + {{- $annotations := (mustMerge ($objectData.annotations | default dict) (include "ix.v1.common.lib.metadata.allAnnotations" $rootCtx | fromYaml)) -}} + {{- with (include "ix.v1.common.lib.metadata.render" (dict "rootCtx" $rootCtx "annotations" $annotations) | trim) }} + annotations: + {{- . | nindent 4 }} + {{- end }} +spec: + {{- include "ix.v1.common.lib.controller.cronjobSpec" (dict "rootCtx" $rootCtx "objectData" $objectData) | nindent 2 }} + +{{- end -}} diff --git a/library/common/1.0.0/templates/classes/_daemonset.tpl b/library/common/1.0.0/templates/classes/_daemonset.tpl index 777a792677..8a4a9fc82e 100644 --- a/library/common/1.0.0/templates/classes/_daemonset.tpl +++ b/library/common/1.0.0/templates/classes/_daemonset.tpl @@ -3,13 +3,7 @@ {{ include "ix.v1.common.class.deployment" (dict "objectData" $objectData "rootCtx" $) }} rootCtx: The root context of the template. It is used to access the global context. -objectData: - name: The name of the daemonset. - labels: The labels of the daemonset. - annotations: The annotations of the daemonset. - replicas: The number of replicas. - revisionHistoryLimit: The number of old ReplicaSets to retain to allow rollback. - strategy: The deployment strategy. +objectData: The object data to be used to render the DaemonSet. */}} {{- define "ix.v1.common.class.daemonset" -}} diff --git a/library/common/1.0.0/templates/classes/_deployment.tpl b/library/common/1.0.0/templates/classes/_deployment.tpl index bc3cb1c3f5..7260910d57 100644 --- a/library/common/1.0.0/templates/classes/_deployment.tpl +++ b/library/common/1.0.0/templates/classes/_deployment.tpl @@ -3,13 +3,7 @@ {{ include "ix.v1.common.class.deployment" (dict "objectData" $objectData "rootCtx" $) }} rootCtx: The root context of the template. It is used to access the global context. -objectData: - name: The name of the deployment. - labels: The labels of the deployment. - annotations: The annotations of the deployment. - replicas: The number of replicas. - revisionHistoryLimit: The number of old ReplicaSets to retain to allow rollback. - strategy: The deployment strategy. +objectData: The object data to be used to render the Deployment. */}} {{- define "ix.v1.common.class.deployment" -}} diff --git a/library/common/1.0.0/templates/classes/_job.tpl b/library/common/1.0.0/templates/classes/_job.tpl index f24dab7d5e..e845cec101 100644 --- a/library/common/1.0.0/templates/classes/_job.tpl +++ b/library/common/1.0.0/templates/classes/_job.tpl @@ -3,13 +3,7 @@ {{ include "ix.v1.common.class.job" (dict "objectData" $objectData "rootCtx" $) }} rootCtx: The root context of the template. It is used to access the global context. -objectData: - name: The name of the job. - labels: The labels of the job. - annotations: The annotations of the job. - replicas: The number of replicas. - revisionHistoryLimit: The number of old ReplicaSets to retain to allow rollback. - strategy: The job strategy. +objectData: The object data to be used to render the Job. */}} {{- define "ix.v1.common.class.job" -}} diff --git a/library/common/1.0.0/templates/classes/_statefulset.tpl b/library/common/1.0.0/templates/classes/_statefulset.tpl index e2c7df36c5..86bedbf987 100644 --- a/library/common/1.0.0/templates/classes/_statefulset.tpl +++ b/library/common/1.0.0/templates/classes/_statefulset.tpl @@ -3,13 +3,7 @@ {{ include "ix.v1.common.class.deployment" (dict "objectData" $objectData "rootCtx" $) }} rootCtx: The root context of the template. It is used to access the global context. -objectData: - name: The name of the statefulset. - labels: The labels of the statefulset. - annotations: The annotations of the statefulset. - replicas: The number of replicas. - revisionHistoryLimit: The number of old ReplicaSets to retain to allow rollback. - strategy: The deployment strategy. +objectData: The object data to be used to render the StatefulSet. */}} {{- define "ix.v1.common.class.statefulset" -}} diff --git a/library/common/1.0.0/templates/lib/controller/_cronjobSpec.tpl b/library/common/1.0.0/templates/lib/controller/_cronjobSpec.tpl new file mode 100644 index 0000000000..7e92beb472 --- /dev/null +++ b/library/common/1.0.0/templates/lib/controller/_cronjobSpec.tpl @@ -0,0 +1,26 @@ +{{/* CronJob Spec */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.controller.cronjobSpec" (dict "objectData" $objectData "rootCtx" $rootCtx) -}} +rootCtx: The root context of the template. It is used to access the global context. +objectData: + schedule: The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. + concurrencyPolicy: Allow, Forbid, or Replace. Defaults to Allow. + failedJobsHistoryLimit: The number of failed finished jobs to retain. Defaults to 1. + successfulJobsHistoryLimit: The number of successful finished jobs to retain. Defaults to 3. + startingDeadlineSeconds: Optional deadline in seconds for starting the job if it misses scheduled time for any reason. Defaults to nil. + timezone: The timezone name. Defaults to .Values.TZ. + +jobSpec data +*/}} +{{- define "ix.v1.common.lib.controller.cronjobSpec" -}} + {{- $objectData := .objectData -}} + {{- $rootCtx := .rootCtx -}} +timeZone: {{ (tpl ($objectData.timezone | default $rootCtx.Values.TZ) $rootCtx) | quote }} +schedule: {{ (tpl $objectData.schedule $rootCtx) | quote }} +concurrencyPolicy: {{ $objectData.concurrencyPolicy | default "Forbid" }} +failedJobsHistoryLimit: {{ $objectData.failedJobsHistoryLimit | default 1 }} +successfulJobsHistoryLimit: {{ $objectData.successfulJobsHistoryLimit | default 3 }} +startingDeadlineSeconds: {{ $objectData.startingDeadlineSeconds | default nil }} +jobTemplate: + spec: + {{- include "ix.v1.common.lib.controller.jobSpec" (dict "objectData" $objectData "rootCtx" $rootCtx) | nindent 4 }} +{{- end -}} diff --git a/library/common/1.0.0/templates/lib/controller/_jobSpec.tpl b/library/common/1.0.0/templates/lib/controller/_jobSpec.tpl index 94c2780f5c..29eb533ebc 100644 --- a/library/common/1.0.0/templates/lib/controller/_jobSpec.tpl +++ b/library/common/1.0.0/templates/lib/controller/_jobSpec.tpl @@ -15,7 +15,7 @@ objectData: {{- $rootCtx := .rootCtx -}} backoffLimit: {{ $objectData.backoffLimit | default 5 }} completionMode: {{ $objectData.completionMode | default "NonIndexed" }} -completions: {{ $objectData.completions }}{{/* Can be nil too */}} +completions: {{ $objectData.completions | default nil }} parallelism: {{ $objectData.parallelism | default 1 }} ttlSecondsAfterFinished: {{ $objectData.ttlSecondsAfterFinished | default 120 }} {{- with $objectData.activeDeadlineSeconds }} diff --git a/library/common/1.0.0/templates/lib/controller/validation/_cronjobValidation.tpl b/library/common/1.0.0/templates/lib/controller/validation/_cronjobValidation.tpl new file mode 100644 index 0000000000..b18d025567 --- /dev/null +++ b/library/common/1.0.0/templates/lib/controller/validation/_cronjobValidation.tpl @@ -0,0 +1,29 @@ +{{/* CronJob Validation */}} +{{/* Call this template: +{{ include "ix.v1.common.lib.controller.cronjobValidation" (dict "objectData" $objectData) -}} +rootCtx: The root context of the template. It is used to access the global context. +objectData: + completionMode: The completionMode of the object. + completions: The completions of the object. + parallelism: The parallelism of the object. +*/}} +{{- define "ix.v1.common.lib.controller.cronjobValidation" -}} + {{- $objectData := .objectData -}} + + {{- if $objectData.concurrencyPolicy -}} + {{- $concurrencyPolicy := $objectData.concurrencyPolicy -}} + + {{- if not (mustHas $concurrencyPolicy (list "Allow" "Forbid" "Replace")) -}} + {{- fail (printf "CronJob - Expected to be one of [Allow, Forbid, Replace], but got [%v]" $concurrencyPolicy) -}} + {{- end -}} + + {{- end -}} + + {{- if not $objectData.schedule -}} + {{- fail "CronJob - Expected non-empty " -}} + {{- end -}} + + {{/* CronJob contains a job inside, so we validate job values too */}} + {{- include "ix.v1.common.lib.controller.jobValidation" (dict "objectData" $objectData) -}} +{{- end -}} +{{/* TODO: Extend validation for other values of cronjob */}} diff --git a/library/common/1.0.0/templates/lib/controller/validation/_jobValidation.tpl b/library/common/1.0.0/templates/lib/controller/validation/_jobValidation.tpl index b89a948953..d18f7dcd50 100644 --- a/library/common/1.0.0/templates/lib/controller/validation/_jobValidation.tpl +++ b/library/common/1.0.0/templates/lib/controller/validation/_jobValidation.tpl @@ -1,4 +1,4 @@ -{{/* Deployment Validation */}} +{{/* Job Validation */}} {{/* Call this template: {{ include "ix.v1.common.lib.controller.jobValidation" (dict "objectData" $objectData) -}} rootCtx: The root context of the template. It is used to access the global context. diff --git a/library/common/1.0.0/templates/spawner/_controller.tpl b/library/common/1.0.0/templates/spawner/_controller.tpl index 701d29bcf3..3381423d2b 100644 --- a/library/common/1.0.0/templates/spawner/_controller.tpl +++ b/library/common/1.0.0/templates/spawner/_controller.tpl @@ -38,7 +38,7 @@ {{- else if eq $objectData.type "Job" -}} {{- include "ix.v1.common.class.job" (dict "objectData" $objectData "rootCtx" $) -}} {{- else if eq $objectData.type "CronJob" -}} - {{- include "ix.v1.common.class.cronJob" (dict "objectData" $objectData "rootCtx" $) -}} + {{- include "ix.v1.common.class.cronjob" (dict "objectData" $objectData "rootCtx" $) -}} {{- end -}} {{- end -}} diff --git a/library/common/1.0.0/values.yaml b/library/common/1.0.0/values.yaml index 18a41be8d8..75a82929a7 100644 --- a/library/common/1.0.0/values.yaml +++ b/library/common/1.0.0/values.yaml @@ -375,7 +375,7 @@ controllers: # -- Primary controller primary: true # -- Controller type - type: Job + type: CronJob # -- Controller labels labels: {} # -- Controller annotations @@ -398,7 +398,7 @@ controllers: # -- CronJob # - CronJob schedule - schedule: "" + schedule: "5" # - CronJob concurrency policy concurrencyPolicy: "" # - CronJob timezone @@ -410,19 +410,19 @@ controllers: # - CronJob starting deadline seconds startingDeadlineSeconds: 0 - # -- Job - # - Job active deadline seconds - activeDeadlineSeconds: 0 - # - Job backoff limit - backoffLimit: 6 - # - Job completions - completions: 1 - # - Job parallelism - parallelism: 1 - # - Job ttl seconds after finished - ttlSecondsAfterFinished: 0 - # - Job completion mode - completionMode: NonIndexed + # # -- Job + # # - Job active deadline seconds + # activeDeadlineSeconds: 0 + # # - Job backoff limit + # backoffLimit: 6 + # # - Job completions + # completions: 1 + # # - Job parallelism + # parallelism: 1 + # # - Job ttl seconds after finished + # ttlSecondsAfterFinished: 0 + # # - Job completion mode + # completionMode: NonIndexed # -- Pod Spec podSpec: