From 1e27518c2de65e4194f2edd43f818686eb150c78 Mon Sep 17 00:00:00 2001 From: Aaron Chen Date: Tue, 3 Nov 2026 09:35:26 -0800 Subject: [PATCH] Run Kerberos worker sidecars as Kubernetes native sidecars (#72555) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Run Kerberos worker sidecars as Kubernetes native sidecars * Enhance tests for Kerberos sidecar to assert restart policy and startup probe * Refactor Kerberos initialization tests to use parameterization for better coverage and clarity * Update Kerberos init container logic and descriptions to clarify behavior with sidecar settings * Remove Kerberos init container in favor of native worker sidecars * Describe Kerberos sidecar startup probe per worker component * remove unnecessary test assertion * restore _helpers.yaml * Update chart/tests/helm_tests/airflow_aux/test_pod_template_file.py Co-authored-by: Przemysław Mirowski <17602603+Miretpl@users.noreply.github.com> * Refactor Kerberos sidecar tests to improve clarity and coverage for enabled/disabled states * Remove deprecated kerberosInitContainer configuration and update schema to reflect changes * Remove unused test for kerberos sidecar pod override reconciliation * Refactor test for removed kerberosInitContainer settings to improve error handling and clarity * Drop Kerberos init container rejection from chart values schema * Add documentation for Kerberos worker sidecars as Kubernetes native sidecars * Refactor documentation for Kerberos worker sidecars to clarify container configuration and requirements --------- Co-authored-by: Przemysław Mirowski <17602603+Miretpl@users.noreply.github.com> --- .../pod-template-file.kubernetes-helm-yaml | 181 ++++++-------- chart/kustomize-overlays/kerberos/README.rst | 3 +- chart/newsfragments/72555.significant.rst | 10 + .../templates/workers/worker-deployment.yaml | 86 ++----- .../airflow_aux/test_container_lifecycle.py | 8 +- .../airflow_aux/test_pod_template_file.py | 221 +++++++----------- .../helm_tests/airflow_core/test_worker.py | 92 +------- .../airflow_core/test_worker_sets.py | 211 +++-------------- .../helm_tests/security/test_kerberos.py | 100 +++++++- .../security/test_security_context.py | 25 +- chart/values.schema.json | 170 +------------- chart/values.yaml | 54 +---- 12 files changed, 322 insertions(+), 839 deletions(-) create mode 100644 chart/newsfragments/72555.significant.rst diff --git a/chart/files/pod-template-file.kubernetes-helm-yaml b/chart/files/pod-template-file.kubernetes-helm-yaml index c157a415c2c..4121fee94e2 100644 --- a/chart/files/pod-template-file.kubernetes-helm-yaml +++ b/chart/files/pod-template-file.kubernetes-helm-yaml @@ -24,8 +24,6 @@ {{- $securityContext := include "airflowPodSecurityContext" (list .Values.workers.kubernetes .Values) }} {{- $containerSecurityContextKerberosSidecar := include "containerSecurityContext" (list .Values.workers.kubernetes.kerberosSidecar .Values) }} {{- $containerLifecycleHooksKerberosSidecar := or .Values.workers.kubernetes.kerberosSidecar.containerLifecycleHooks .Values.containerLifecycleHooks }} -{{- $containerSecurityContextKerberosInitContainer := include "containerSecurityContext" (list .Values.workers.kubernetes.kerberosInitContainer .Values) }} -{{- $containerLifecycleHooksKerberosInitContainer := or .Values.workers.kubernetes.kerberosInitContainer.containerLifecycleHooks .Values.containerLifecycleHooks }} {{- $containerSecurityContext := include "containerSecurityContext" (list .Values.workers.kubernetes .Values) }} {{- $containerLifecycleHooks := or .Values.workers.kubernetes.containerLifecycleHooks .Values.containerLifecycleHooks }} {{- $safeToEvict := dict "cluster-autoscaler.kubernetes.io/safe-to-evict" (.Values.workers.kubernetes.safeToEvict | toString) }} @@ -44,7 +42,7 @@ metadata: {{- end }} annotations: {{- tpl (toYaml $podAnnotations) . | nindent 4 }} - {{- if .Values.workers.kubernetes.kerberosInitContainer.enabled }} + {{- if .Values.workers.kubernetes.kerberosSidecar.enabled }} checksum/kerberos-keytab: {{ include (print $.Template.BasePath "/secrets/kerberos-keytab-secret.yaml") . | sha256sum }} {{- end }} spec: @@ -55,120 +53,11 @@ spec: {{- if .Values.workers.kubernetes.extraInitContainers }} {{- tpl (toYaml .Values.workers.kubernetes.extraInitContainers) . | nindent 4 }} {{- end }} - {{- if .Values.workers.kubernetes.kerberosInitContainer.enabled }} - - name: kerberos-init - image: {{ template "airflow_image" . }} - imagePullPolicy: {{ .Values.images.airflow.pullPolicy }} - securityContext: {{ $containerSecurityContextKerberosInitContainer | nindent 8 }} - {{- if $containerLifecycleHooksKerberosInitContainer }} - lifecycle: {{- tpl (toYaml $containerLifecycleHooksKerberosInitContainer) . | nindent 8 }} - {{- end }} - args: ["kerberos", "-o"] - resources: {{- toYaml .Values.workers.kubernetes.kerberosInitContainer.resources | nindent 8 }} - volumeMounts: - - name: logs - mountPath: {{ template "airflow_logs" . }} - {{- if .Values.logs.persistence.subPath }} - subPath: {{ .Values.logs.persistence.subPath }} - {{- end }} - {{- include "airflow_config_mount" . | nindent 8 }} - - name: config - mountPath: {{ .Values.kerberos.configPath | quote }} - subPath: krb5.conf - readOnly: true - - name: kerberos-keytab - subPath: "kerberos.keytab" - mountPath: {{ .Values.kerberos.keytabPath | quote }} - readOnly: true - - name: kerberos-ccache - mountPath: {{ .Values.kerberos.ccacheMountPath | quote }} - readOnly: false - {{- if .Values.volumeMounts }} - {{- tpl (toYaml .Values.volumeMounts) . | nindent 8 }} - {{- end }} - {{- if .Values.workers.kubernetes.extraVolumeMounts }} - {{- tpl (toYaml .Values.workers.kubernetes.extraVolumeMounts) . | nindent 8 }} - {{- end }} - {{- if or .Values.apiServer.apiServerConfig .Values.apiServer.apiServerConfigConfigMapName }} - {{- include "airflow_api_server_config_mount" . | nindent 8 }} - {{- end }} - envFrom: {{- include "custom_airflow_environment_from" . | default "\n []" | indent 6 }} - env: - - name: KRB5_CONFIG - value: {{ .Values.kerberos.configPath | quote }} - - name: KRB5CCNAME - value: {{ include "kerberos_ccache_path" . | quote }} - {{- include "custom_airflow_environment" . | indent 6 }} - {{- include "standard_airflow_environment" . | indent 6 }} - {{- end }} - containers: - - envFrom: {{- include "custom_airflow_environment_from" . | default "\n []" | indent 6 }} - env: - - name: AIRFLOW__CORE__EXECUTOR - value: {{ .Values.executor | quote }} - # Deliver pod-termination signals only to the task supervisor (dumb-init's - # direct child) instead of broadcasting them to the whole process group. - # On graceful pod shutdown the supervisor's warm-shutdown handler then lets - # the running task finish -- the same mechanism Celery workers use -- rather - # than the task subprocess being killed directly by dumb-init's group-wide - # SIGTERM (which could also reach the subprocess before it installs its own - # signal handler). Hard kills (heartbeat loss / overtime / the post-grace - # SIGKILL) are unaffected. - - name: DUMB_INIT_SETSID - value: "0" - {{- if or .Values.workers.kubernetes.kerberosSidecar.enabled .Values.workers.kubernetes.kerberosInitContainer.enabled }} - - name: KRB5_CONFIG - value: {{ .Values.kerberos.configPath | quote }} - - name: KRB5CCNAME - value: {{ include "kerberos_ccache_path" . | quote }} - {{- end }} - {{- include "standard_airflow_environment" . | indent 6}} - {{- include "custom_airflow_environment" . | indent 6 }} - {{- include "container_extra_envs" (list . .Values.workers.kubernetes.env) | indent 6 }} - image: {{ template "pod_template_image" . }} - imagePullPolicy: {{ .Values.images.pod_template.pullPolicy }} - securityContext: {{ $containerSecurityContext | nindent 8 }} - {{- if $containerLifecycleHooks }} - lifecycle: {{- tpl (toYaml $containerLifecycleHooks) . | nindent 8 }} - {{- end }} - name: base - {{- if .Values.workers.kubernetes.command }} - command: {{ tpl (toYaml .Values.workers.kubernetes.command) . | nindent 8 }} - {{- end }} - resources: {{- toYaml .Values.workers.kubernetes.resources | nindent 8 }} - volumeMounts: - - mountPath: {{ template "airflow_logs" . }} - name: logs - {{- if .Values.logs.persistence.subPath }} - subPath: {{ .Values.logs.persistence.subPath }} - {{- end }} - {{- include "airflow_config_mount" . | nindent 8 }} - {{- if or .Values.dags.gitSync.enabled .Values.dags.persistence.enabled }} - {{- include "airflow_dags_mount" . | nindent 8 }} - {{- end }} - {{- if .Values.volumeMounts }} - {{- tpl (toYaml .Values.volumeMounts) . | nindent 8 }} - {{- end }} - {{- if .Values.workers.kubernetes.extraVolumeMounts }} - {{- tpl (toYaml .Values.workers.kubernetes.extraVolumeMounts) . | nindent 8 }} - {{- end }} - {{- if .Values.kerberos.enabled }} - - name: kerberos-keytab - subPath: "kerberos.keytab" - mountPath: {{ .Values.kerberos.keytabPath | quote }} - readOnly: true - - name: config - mountPath: {{ .Values.kerberos.configPath | quote }} - subPath: krb5.conf - readOnly: true - - name: kerberos-ccache - mountPath: {{ .Values.kerberos.ccacheMountPath | quote }} - readOnly: {{ .Values.workers.kubernetes.readonlyKerberosCache }} - {{- end }} {{- if .Values.workers.kubernetes.kerberosSidecar.enabled }} - name: worker-kerberos image: {{ template "airflow_image" . }} imagePullPolicy: {{ .Values.images.airflow.pullPolicy }} + restartPolicy: Always securityContext: {{ $containerSecurityContextKerberosSidecar | nindent 8 }} {{- if $containerLifecycleHooksKerberosSidecar }} lifecycle: {{- tpl (toYaml $containerLifecycleHooksKerberosSidecar) . | nindent 8 }} @@ -220,6 +109,70 @@ spec: {{- include "custom_airflow_environment" . | indent 6 }} {{- include "standard_airflow_environment" . | indent 6 }} {{- end }} + containers: + - envFrom: {{- include "custom_airflow_environment_from" . | default "\n []" | indent 6 }} + env: + - name: AIRFLOW__CORE__EXECUTOR + value: {{ .Values.executor | quote }} + # Deliver pod-termination signals only to the task supervisor (dumb-init's + # direct child) instead of broadcasting them to the whole process group. + # On graceful pod shutdown the supervisor's warm-shutdown handler then lets + # the running task finish -- the same mechanism Celery workers use -- rather + # than the task subprocess being killed directly by dumb-init's group-wide + # SIGTERM (which could also reach the subprocess before it installs its own + # signal handler). Hard kills (heartbeat loss / overtime / the post-grace + # SIGKILL) are unaffected. + - name: DUMB_INIT_SETSID + value: "0" + {{- if .Values.workers.kubernetes.kerberosSidecar.enabled }} + - name: KRB5_CONFIG + value: {{ .Values.kerberos.configPath | quote }} + - name: KRB5CCNAME + value: {{ include "kerberos_ccache_path" . | quote }} + {{- end }} + {{- include "standard_airflow_environment" . | indent 6}} + {{- include "custom_airflow_environment" . | indent 6 }} + {{- include "container_extra_envs" (list . .Values.workers.kubernetes.env) | indent 6 }} + image: {{ template "pod_template_image" . }} + imagePullPolicy: {{ .Values.images.pod_template.pullPolicy }} + securityContext: {{ $containerSecurityContext | nindent 8 }} + {{- if $containerLifecycleHooks }} + lifecycle: {{- tpl (toYaml $containerLifecycleHooks) . | nindent 8 }} + {{- end }} + name: base + {{- if .Values.workers.kubernetes.command }} + command: {{ tpl (toYaml .Values.workers.kubernetes.command) . | nindent 8 }} + {{- end }} + resources: {{- toYaml .Values.workers.kubernetes.resources | nindent 8 }} + volumeMounts: + - mountPath: {{ template "airflow_logs" . }} + name: logs + {{- if .Values.logs.persistence.subPath }} + subPath: {{ .Values.logs.persistence.subPath }} + {{- end }} + {{- include "airflow_config_mount" . | nindent 8 }} + {{- if or .Values.dags.gitSync.enabled .Values.dags.persistence.enabled }} + {{- include "airflow_dags_mount" . | nindent 8 }} + {{- end }} + {{- if .Values.volumeMounts }} + {{- tpl (toYaml .Values.volumeMounts) . | nindent 8 }} + {{- end }} + {{- if .Values.workers.kubernetes.extraVolumeMounts }} + {{- tpl (toYaml .Values.workers.kubernetes.extraVolumeMounts) . | nindent 8 }} + {{- end }} + {{- if .Values.kerberos.enabled }} + - name: kerberos-keytab + subPath: "kerberos.keytab" + mountPath: {{ .Values.kerberos.keytabPath | quote }} + readOnly: true + - name: config + mountPath: {{ .Values.kerberos.configPath | quote }} + subPath: krb5.conf + readOnly: true + - name: kerberos-ccache + mountPath: {{ .Values.kerberos.ccacheMountPath | quote }} + readOnly: {{ .Values.workers.kubernetes.readonlyKerberosCache }} + {{- end }} {{- if .Values.workers.kubernetes.extraContainers }} {{- tpl (toYaml .Values.workers.kubernetes.extraContainers) . | nindent 4 }} {{- end }} @@ -268,7 +221,7 @@ spec: - configMap: name: {{ include "airflow_config" . }} name: config - {{- if and (or .Values.apiServer.apiServerConfig .Values.apiServer.apiServerConfigConfigMapName) (or .Values.workers.kubernetes.kerberosInitContainer.enabled .Values.workers.kubernetes.kerberosSidecar.enabled) }} + {{- if and (or .Values.apiServer.apiServerConfig .Values.apiServer.apiServerConfigConfigMapName) .Values.workers.kubernetes.kerberosSidecar.enabled }} - name: api-server-config configMap: name: {{ template "airflow_api_server_config_configmap_name" . }} diff --git a/chart/kustomize-overlays/kerberos/README.rst b/chart/kustomize-overlays/kerberos/README.rst index 1b604668fa4..0a3cf09635d 100644 --- a/chart/kustomize-overlays/kerberos/README.rst +++ b/chart/kustomize-overlays/kerberos/README.rst @@ -82,8 +82,7 @@ during the local and CI smoke test. Wiring the keytab into the chart's sidecar ------------------------------------------ -The chart's kerberos sidecar (``workers.celery.kerberosInitContainer``, -``workers.celery.kerberosSidecar``, ``workers.kubernetes.kerberosInitContainer``, +The chart's kerberos sidecar (``workers.celery.kerberosSidecar`` and ``workers.kubernetes.kerberosSidecar``) always mounts a keytab Secret whose name is fixed by the chart to ``-kerberos-keytab`` (rendered by the ``kerberos_keytab_secret`` helper from the release fullname). There is no diff --git a/chart/newsfragments/72555.significant.rst b/chart/newsfragments/72555.significant.rst new file mode 100644 index 00000000000..bc3f9e3fbe8 --- /dev/null +++ b/chart/newsfragments/72555.significant.rst @@ -0,0 +1,10 @@ +Kerberos worker sidecars now run as Kubernetes native sidecars + +* The ``worker-kerberos`` container moved from ``spec.containers`` to ``spec.initContainers`` with + ``restartPolicy: Always``; its status is now in ``status.initContainerStatuses``. +* The existing ``klist -s`` startup probe is enabled by default. When enabled, it must now succeed + before the worker or task container starts. +* ``workers.celery.kerberosInitContainer`` and ``workers.kubernetes.kerberosInitContainer``, including + Celery worker-set overrides, have been removed. Helm still accepts these values but the chart + ignores them. Use ``kerberosSidecar`` in the corresponding worker section or set instead. +* The native sidecar requires the ``SidecarContainers`` feature gate on Kubernetes 1.30 through 1.32. diff --git a/chart/templates/workers/worker-deployment.yaml b/chart/templates/workers/worker-deployment.yaml index 51c38b157a7..8f0d2eee5db 100644 --- a/chart/templates/workers/worker-deployment.yaml +++ b/chart/templates/workers/worker-deployment.yaml @@ -46,8 +46,6 @@ {{- $containerSecurityContextWaitForMigrations := include "containerSecurityContext" (list .Values.workers.celery.waitForMigrations .Values) }} {{- $containerSecurityContextLogGroomerSidecar := include "containerSecurityContext" (list .Values.workers.celery.logGroomerSidecar .Values) }} {{- $containerSecurityContextKerberosSidecar := include "containerSecurityContext" (list .Values.workers.celery.kerberosSidecar .Values) }} -{{- $containerSecurityContextKerberosInitContainer := include "containerSecurityContext" (list .Values.workers.celery.kerberosInitContainer .Values) }} -{{- $containerLifecycleHooksKerberosInitContainer := or .Values.workers.celery.kerberosInitContainer.containerLifecycleHooks .Values.containerLifecycleHooks }} {{- $containerLifecycleHooks := or .Values.workers.celery.containerLifecycleHooks .Values.containerLifecycleHooks }} {{- $containerLifecycleHooksLogGroomerSidecar := or .Values.workers.celery.logGroomerSidecar.containerLifecycleHooks .Values.containerLifecycleHooks }} {{- $containerLifecycleHooksKerberosSidecar := or .Values.workers.celery.kerberosSidecar.containerLifecycleHooks .Values.containerLifecycleHooks }} @@ -184,18 +182,28 @@ spec: subPath: {{ .Values.logs.persistence.subPath }} {{- end }} {{- end }} - {{- if .Values.workers.celery.kerberosInitContainer.enabled }} - - name: kerberos-init + {{- if .Values.workers.celery.kerberosSidecar.enabled }} + - name: worker-kerberos image: {{ template "airflow_image" . }} - {{- if $containerSecurityContextKerberosInitContainer }} - securityContext: {{ $containerSecurityContextKerberosInitContainer | nindent 12 }} - {{- end }} - {{- if $containerLifecycleHooksKerberosInitContainer }} - lifecycle: {{- tpl (toYaml $containerLifecycleHooksKerberosInitContainer) . | nindent 12 }} - {{- end }} imagePullPolicy: {{ .Values.images.airflow.pullPolicy }} - args: ["kerberos", "-o"] - resources: {{- toYaml .Values.workers.celery.kerberosInitContainer.resources | nindent 12 }} + restartPolicy: Always + {{- if $containerSecurityContextKerberosSidecar }} + securityContext: {{ $containerSecurityContextKerberosSidecar | nindent 12 }} + {{- end }} + {{- if $containerLifecycleHooksKerberosSidecar }} + lifecycle: {{- tpl (toYaml $containerLifecycleHooksKerberosSidecar) . | nindent 12 }} + {{- end }} + args: ["kerberos"] + resources: {{- toYaml .Values.workers.celery.kerberosSidecar.resources | nindent 12 }} + {{- if .Values.workers.celery.kerberosSidecar.startupProbe.enabled }} + startupProbe: + exec: + command: ["klist", "-s"] + timeoutSeconds: {{ .Values.workers.celery.kerberosSidecar.startupProbe.timeoutSeconds }} + initialDelaySeconds: {{ .Values.workers.celery.kerberosSidecar.startupProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.workers.celery.kerberosSidecar.startupProbe.periodSeconds }} + failureThreshold: {{ .Values.workers.celery.kerberosSidecar.startupProbe.failureThreshold }} + {{- end }} volumeMounts: - name: logs mountPath: {{ template "airflow_logs" . }} @@ -409,60 +417,6 @@ spec: {{- tpl (toYaml .Values.workers.celery.extraVolumeMounts) . | nindent 12 }} {{- end }} {{- end }} - {{- if .Values.workers.celery.kerberosSidecar.enabled }} - - name: worker-kerberos - image: {{ template "airflow_image" . }} - imagePullPolicy: {{ .Values.images.airflow.pullPolicy }} - {{- if $containerSecurityContextKerberosSidecar }} - securityContext: {{ $containerSecurityContextKerberosSidecar | nindent 12 }} - {{- end }} - {{- if $containerLifecycleHooksKerberosSidecar }} - lifecycle: {{- tpl (toYaml $containerLifecycleHooksKerberosSidecar) . | nindent 12 }} - {{- end }} - args: ["kerberos"] - resources: {{- toYaml .Values.workers.celery.kerberosSidecar.resources | nindent 12 }} - {{- if .Values.workers.celery.kerberosSidecar.startupProbe.enabled }} - startupProbe: - exec: - command: ["klist", "-s"] - timeoutSeconds: {{ .Values.workers.celery.kerberosSidecar.startupProbe.timeoutSeconds }} - initialDelaySeconds: {{ .Values.workers.celery.kerberosSidecar.startupProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.workers.celery.kerberosSidecar.startupProbe.periodSeconds }} - failureThreshold: {{ .Values.workers.celery.kerberosSidecar.startupProbe.failureThreshold }} - {{- end }} - volumeMounts: - - name: logs - mountPath: {{ template "airflow_logs" . }} - {{- if .Values.logs.persistence.subPath }} - subPath: {{ .Values.logs.persistence.subPath }} - {{- end }} - {{- include "airflow_config_mount" . | nindent 12 }} - - name: config - mountPath: {{ .Values.kerberos.configPath | quote }} - subPath: krb5.conf - readOnly: true - - name: kerberos-keytab - subPath: "kerberos.keytab" - mountPath: {{ .Values.kerberos.keytabPath | quote }} - readOnly: true - - name: kerberos-ccache - mountPath: {{ .Values.kerberos.ccacheMountPath | quote }} - readOnly: false - {{- if .Values.volumeMounts }} - {{- tpl (toYaml .Values.volumeMounts) . | nindent 12 }} - {{- end }} - {{- if .Values.workers.celery.extraVolumeMounts }} - {{- tpl (toYaml .Values.workers.celery.extraVolumeMounts) . | nindent 12 }} - {{- end }} - envFrom: {{- include "custom_airflow_environment_from" . | default "\n []" | indent 10 }} - env: - - name: KRB5_CONFIG - value: {{ .Values.kerberos.configPath | quote }} - - name: KRB5CCNAME - value: {{ include "kerberos_ccache_path" . | quote }} - {{- include "custom_airflow_environment" . | indent 10 }} - {{- include "standard_airflow_environment" . | indent 10 }} - {{- end }} {{- if .Values.workers.celery.extraContainers }} {{- tpl (toYaml .Values.workers.celery.extraContainers) . | nindent 8 }} {{- end }} diff --git a/chart/tests/helm_tests/airflow_aux/test_container_lifecycle.py b/chart/tests/helm_tests/airflow_aux/test_container_lifecycle.py index 36ab47f602b..37c5f03a01a 100644 --- a/chart/tests/helm_tests/airflow_aux/test_container_lifecycle.py +++ b/chart/tests/helm_tests/airflow_aux/test_container_lifecycle.py @@ -165,7 +165,7 @@ class TestContainerLifecycleHooks: == LIFECYCLE_PARSED ) - # Test container lifecycle hooks for worker-kerberos main container + # Test container lifecycle hooks for worker-kerberos native sidecar @pytest.mark.parametrize("hook_type", ["preStop", "postStart"]) def test_worker_kerberos_container_setting(self, hook_type): docs = render_chart( @@ -183,9 +183,9 @@ class TestContainerLifecycleHooks: show_only=["templates/workers/worker-deployment.yaml"], ) - assert jmespath.search("spec.template.spec.containers[2].lifecycle", docs[0]) == { - hook_type: LIFECYCLE_PARSED - } + assert jmespath.search( + "spec.template.spec.initContainers[?name=='worker-kerberos'] | [0].lifecycle", docs[0] + ) == {hook_type: LIFECYCLE_PARSED} # Test container lifecycle hooks for log-groomer-sidecar main container @pytest.mark.parametrize("hook_type", ["preStop", "postStart"]) diff --git a/chart/tests/helm_tests/airflow_aux/test_pod_template_file.py b/chart/tests/helm_tests/airflow_aux/test_pod_template_file.py index fb400231355..3079dd1cf68 100644 --- a/chart/tests/helm_tests/airflow_aux/test_pod_template_file.py +++ b/chart/tests/helm_tests/airflow_aux/test_pod_template_file.py @@ -1293,6 +1293,37 @@ class TestPodTemplateFile: assert jmespath.search("spec.runtimeClassName", docs[0]) == "nvidia" + def test_kerberos_sidecar_is_native_sidecar(self): + docs = render_chart( + values={"workers": {"kubernetes": {"kerberosSidecar": {"enabled": True}}}}, + show_only=["templates/pod-template-file.yaml"], + chart_dir=self.temp_chart_dir, + ) + sidecar = jmespath.search("spec.initContainers[?name=='worker-kerberos'] | [0]", docs[0]) + assert sidecar is not None + assert sidecar["restartPolicy"] == "Always" + + def test_kerberos_sidecar_disabled(self): + docs = render_chart( + values={"workers": {"kubernetes": {"kerberosSidecar": {"enabled": False}}}}, + show_only=["templates/pod-template-file.yaml"], + chart_dir=self.temp_chart_dir, + ) + assert jmespath.search("spec.initContainers[].name", docs[0]) is None + assert jmespath.search('metadata.annotations."checksum/kerberos-keytab"', docs[0]) is None + + def test_kerberos_sidecar_enabled(self): + docs = render_chart( + values={"workers": {"kubernetes": {"kerberosSidecar": {"enabled": True}}}}, + show_only=["templates/pod-template-file.yaml"], + chart_dir=self.temp_chart_dir, + ) + assert jmespath.search("spec.initContainers[].name", docs[0]) == ["worker-kerberos"] + assert jmespath.search('metadata.annotations."checksum/kerberos-keytab"', docs[0]) is not None + sidecar = jmespath.search("spec.initContainers[?name=='worker-kerberos'] | [0]", docs[0]) + assert sidecar["args"] == ["kerberos"] + assert sidecar["restartPolicy"] == "Always" + def test_airflow_local_settings_kerberos_sidecar(self): docs = render_chart( values={ @@ -1302,14 +1333,17 @@ class TestPodTemplateFile: show_only=["templates/pod-template-file.yaml"], chart_dir=self.temp_chart_dir, ) - assert jmespath.search("spec.containers[1].name", docs[0]) == "worker-kerberos" + assert ( + jmespath.search("spec.initContainers[?name=='worker-kerberos'] | [0].name", docs[0]) + == "worker-kerberos" + ) assert { "name": "config", "mountPath": "/opt/airflow/config/airflow_local_settings.py", "subPath": "airflow_local_settings.py", "readOnly": True, - } in jmespath.search("spec.containers[1].volumeMounts", docs[0]) + } in jmespath.search("spec.initContainers[?name=='worker-kerberos'] | [0].volumeMounts", docs[0]) def test_kerberos_sidecar_resources(self): docs = render_chart( @@ -1329,7 +1363,7 @@ class TestPodTemplateFile: chart_dir=self.temp_chart_dir, ) - assert jmespath.search("spec.containers[?name=='worker-kerberos'] | [0].resources", docs[0]) == { + assert jmespath.search("spec.initContainers[?name=='worker-kerberos'] | [0].resources", docs[0]) == { "requests": { "cpu": "1m", "memory": "2Mi", @@ -1356,7 +1390,7 @@ class TestPodTemplateFile: chart_dir=self.temp_chart_dir, ) - assert jmespath.search("spec.containers[1].lifecycle", docs[0]) == { + assert jmespath.search("spec.initContainers[?name=='worker-kerberos'] | [0].lifecycle", docs[0]) == { hook_type: {"exec": {"command": ["echo", "test-release"]}} } @@ -1376,9 +1410,9 @@ class TestPodTemplateFile: chart_dir=self.temp_chart_dir, ) - assert jmespath.search("spec.containers[1].securityContext", docs[0]) == { - "allowPrivilegeEscalation": False - } + assert jmespath.search( + "spec.initContainers[?name=='worker-kerberos'] | [0].securityContext", docs[0] + ) == {"allowPrivilegeEscalation": False} @pytest.mark.parametrize( ("override", "expected"), @@ -1408,9 +1442,8 @@ class TestPodTemplateFile: "failureThreshold": 14, }, ), - ({"enabled": False}, None), ], - ids=["default", "custom", "disabled"], + ids=["default", "custom"], ) def test_kerberos_sidecar_startup_probe(self, override, expected): docs = render_chart( @@ -1421,11 +1454,26 @@ class TestPodTemplateFile: chart_dir=self.temp_chart_dir, ) - assert ( - jmespath.search("spec.containers[?name=='worker-kerberos'] | [0].startupProbe", docs[0]) - == expected + sidecar = jmespath.search("spec.initContainers[?name=='worker-kerberos'] | [0]", docs[0]) + assert sidecar is not None + assert sidecar["restartPolicy"] == "Always" + assert sidecar["startupProbe"] == expected + + def test_kerberos_sidecar_startup_probe_disabled(self): + docs = render_chart( + values={ + "workers": { + "kubernetes": {"kerberosSidecar": {"enabled": True, "startupProbe": {"enabled": False}}} + } + }, + show_only=["templates/pod-template-file.yaml"], + chart_dir=self.temp_chart_dir, ) + sidecar = jmespath.search("spec.initContainers[?name=='worker-kerberos'] | [0]", docs[0]) + assert sidecar is not None + assert "startupProbe" not in sidecar + @pytest.mark.parametrize( "override", [ @@ -1448,41 +1496,6 @@ class TestPodTemplateFile: chart_dir=self.temp_chart_dir, ) - def test_kerberos_init_container_default(self): - docs = render_chart( - show_only=["templates/pod-template-file.yaml"], - chart_dir=self.temp_chart_dir, - ) - - assert jmespath.search("spec.initContainers[?name=='kerberos-init']", docs[0]) is None - - def test_kerberos_init_container_enable(self): - docs = render_chart( - values={ - "workers": {"kubernetes": {"kerberosInitContainer": {"enabled": True}}}, - }, - show_only=["templates/pod-template-file.yaml"], - chart_dir=self.temp_chart_dir, - ) - - assert jmespath.search("spec.initContainers[?name=='kerberos-init']", docs[0]) is not None - - def test_kerberos_init_container_name_and_args_default(self): - docs = render_chart( - values={ - "workers": { - "kubernetes": {"kerberosInitContainer": {"enabled": True}}, - }, - }, - show_only=["templates/pod-template-file.yaml"], - chart_dir=self.temp_chart_dir, - ) - - initContainers = jmespath.search("spec.initContainers[0]", docs[0]) - - assert initContainers["name"] == "kerberos-init" - assert initContainers["args"] == ["kerberos", "-o"] - @pytest.mark.parametrize("readonly_cache", [False, True]) def test_kerberos_readonly_cache(self, readonly_cache: bool): docs = render_chart( @@ -1547,126 +1560,54 @@ class TestPodTemplateFile: assert jmespath.search("spec.containers[0].command", docs[0]) is None - @pytest.mark.parametrize( - ("workers_values", "kerberos_init_container", "expected_config_name"), - [ - ({"kubernetes": {"kerberosSidecar": {"enabled": True}}}, False, "api-server-config"), - ({"kubernetes": {"kerberosInitContainer": {"enabled": True}}}, True, "api-server-config"), - ], - ) - def test_api_server_config_for_kerberos( - self, workers_values, kerberos_init_container, expected_config_name - ): - values = { - "workers": workers_values, - "apiServer": {"apiServerConfigConfigMapName": "config"}, - } - docs = render_chart( - values=values, - show_only=["templates/pod-template-file.yaml"], - chart_dir=self.temp_chart_dir, - ) - - kerberos_container = "spec.containers[1].volumeMounts[*].name" - if kerberos_init_container: - kerberos_container = "spec.initContainers[0].volumeMounts[*].name" - - volume_mounts_names = jmespath.search(kerberos_container, docs[0]) - print(volume_mounts_names) - assert expected_config_name in volume_mounts_names - assert expected_config_name in jmespath.search("spec.volumes[*].name", docs[0]) - - @pytest.mark.parametrize( - "workers_values", - [ - {"kubernetes": {"kerberosSidecar": {"enabled": True}}}, - {"kubernetes": {"kerberosInitContainer": {"enabled": True}}}, - ], - ) - def test_base_contains_kerberos_env(self, workers_values): + def test_api_server_config_for_kerberos(self): docs = render_chart( values={ - "workers": workers_values, + "workers": {"kubernetes": {"kerberosSidecar": {"enabled": True}}}, + "apiServer": {"apiServerConfigConfigMapName": "config"}, }, show_only=["templates/pod-template-file.yaml"], chart_dir=self.temp_chart_dir, ) - scheduler_env = jmespath.search("spec.containers[0].env[*].name", docs[0]) - assert set(["KRB5_CONFIG", "KRB5CCNAME"]).issubset(scheduler_env) + assert "api-server-config" in jmespath.search("spec.volumes[*].name", docs[0]) + assert "api-server-config" in jmespath.search( + "spec.initContainers[?name=='worker-kerberos'] | [0].volumeMounts[*].name", docs[0] + ) - def test_kerberos_init_container_resources(self): + def test_api_server_config_not_mounted_when_kerberos_sidecar_disabled(self): docs = render_chart( values={ - "workers": { - "kubernetes": { - "kerberosInitContainer": { - "enabled": True, - "resources": { - "requests": {"cpu": "1m", "memory": "2Mi"}, - "limits": {"cpu": "3m", "memory": "4Mi"}, - }, - } - } - }, + "workers": {"kubernetes": {"kerberosSidecar": {"enabled": False}}}, + "apiServer": {"apiServerConfigConfigMapName": "config"}, }, show_only=["templates/pod-template-file.yaml"], chart_dir=self.temp_chart_dir, ) - assert jmespath.search("spec.initContainers[?name=='kerberos-init'] | [0].resources", docs[0]) == { - "requests": { - "cpu": "1m", - "memory": "2Mi", - }, - "limits": { - "cpu": "3m", - "memory": "4Mi", - }, - } + assert "api-server-config" not in jmespath.search("spec.volumes[*].name", docs[0]) - def test_kerberos_init_container_security_context(self): + def test_base_contains_kerberos_env(self): docs = render_chart( - values={ - "workers": { - "kubernetes": { - "kerberosInitContainer": { - "enabled": True, - "securityContexts": {"container": {"runAsUser": 2000}}, - } - } - } - }, + values={"workers": {"kubernetes": {"kerberosSidecar": {"enabled": True}}}}, show_only=["templates/pod-template-file.yaml"], chart_dir=self.temp_chart_dir, ) - assert jmespath.search( - "spec.initContainers[?name=='kerberos-init'] | [0].securityContext", docs[0] - ) == {"runAsUser": 2000} + env_names = jmespath.search("spec.containers[0].env[*].name", docs[0]) + assert "KRB5_CONFIG" in env_names + assert "KRB5CCNAME" in env_names - def test_kerberos_init_container_lifecycle_hooks(self): + def test_base_has_no_kerberos_env_when_sidecar_disabled(self): docs = render_chart( - name="test-release", - values={ - "workers": { - "kubernetes": { - "kerberosInitContainer": { - "enabled": True, - "containerLifecycleHooks": { - "postStart": {"exec": {"command": ["echo", "{{ .Release.Name }}"]}} - }, - } - } - }, - }, + values={"workers": {"kubernetes": {"kerberosSidecar": {"enabled": False}}}}, show_only=["templates/pod-template-file.yaml"], chart_dir=self.temp_chart_dir, ) - assert jmespath.search("spec.initContainers[?name=='kerberos-init'] | [0].lifecycle", docs[0]) == { - "postStart": {"exec": {"command": ["echo", "test-release"]}} - } + env_names = jmespath.search("spec.containers[0].env[*].name", docs[0]) + assert "KRB5_CONFIG" not in env_names + assert "KRB5CCNAME" not in env_names def test_service_account_name_default(self): docs = render_chart( diff --git a/chart/tests/helm_tests/airflow_core/test_worker.py b/chart/tests/helm_tests/airflow_core/test_worker.py index 567bd4b44ee..79d195a6a1a 100644 --- a/chart/tests/helm_tests/airflow_core/test_worker.py +++ b/chart/tests/helm_tests/airflow_core/test_worker.py @@ -822,98 +822,10 @@ class TestWorker: "mountPath": "/opt/airflow/config/airflow_local_settings.py", "subPath": "airflow_local_settings.py", "readOnly": True, - } in jmespath.search("spec.template.spec.containers[2].volumeMounts", docs[0]) - - def test_kerberos_init_container_default(self): - docs = render_chart(show_only=["templates/workers/worker-deployment.yaml"]) - assert ( - jmespath.search("spec.template.spec.initContainers[?name=='kerberos-init'] | [0]", docs[0]) - is None + } in jmespath.search( + "spec.template.spec.initContainers[?name=='worker-kerberos'] | [0].volumeMounts", docs[0] ) - def test_kerberos_init_container_enable(self): - docs = render_chart( - values={ - "workers": {"celery": {"kerberosInitContainer": {"enabled": True}}}, - }, - show_only=["templates/workers/worker-deployment.yaml"], - ) - - assert ( - jmespath.search("spec.template.spec.initContainers[?name=='kerberos-init'] | [0]", docs[0]) - is not None - ) - - def test_kerberos_init_container_name_and_args_default(self): - docs = render_chart( - values={ - "workers": { - "celery": {"kerberosInitContainer": {"enabled": True}}, - }, - }, - show_only=["templates/workers/worker-deployment.yaml"], - ) - - initContainers = jmespath.search( - "spec.template.spec.initContainers[?name=='kerberos-init'] | [0]", docs[0] - ) - - assert initContainers["name"] == "kerberos-init" - assert initContainers["args"] == ["kerberos", "-o"] - - def test_kerberos_init_container_resources(self): - docs = render_chart( - values={ - "workers": { - "celery": { - "kerberosInitContainer": { - "enabled": True, - "resources": { - "requests": {"cpu": "1m", "memory": "2Mi"}, - "limits": {"cpu": "3m", "memory": "4Mi"}, - }, - } - } - }, - }, - show_only=["templates/workers/worker-deployment.yaml"], - ) - - assert jmespath.search( - "spec.template.spec.initContainers[?name=='kerberos-init'] | [0].resources", docs[0] - ) == { - "requests": { - "cpu": "1m", - "memory": "2Mi", - }, - "limits": { - "cpu": "3m", - "memory": "4Mi", - }, - } - - def test_kerberos_init_container_lifecycle_hooks(self): - docs = render_chart( - name="test-release", - values={ - "workers": { - "celery": { - "kerberosInitContainer": { - "enabled": True, - "containerLifecycleHooks": { - "postStart": {"exec": {"command": ["echo", "{{ .Release.Name }}"]}} - }, - } - } - } - }, - show_only=["templates/workers/worker-deployment.yaml"], - ) - - assert jmespath.search( - "spec.template.spec.initContainers[?name=='kerberos-init'] | [0].lifecycle", docs[0] - ) == {"postStart": {"exec": {"command": ["echo", "test-release"]}}} - @pytest.mark.parametrize("readonly_cache", [False, True]) def test_kerberos_readonly_cache(self, readonly_cache: bool): docs = render_chart( diff --git a/chart/tests/helm_tests/airflow_core/test_worker_sets.py b/chart/tests/helm_tests/airflow_core/test_worker_sets.py index 040b0919240..26c66a64b13 100644 --- a/chart/tests/helm_tests/airflow_core/test_worker_sets.py +++ b/chart/tests/helm_tests/airflow_core/test_worker_sets.py @@ -547,169 +547,6 @@ class TestWorkerSets: assert jmespath.search("spec.volumeClaimTemplates[0].metadata.annotations", docs[0]) == {"foo": "bar"} - def test_overwrite_kerberos_init_container_enabled(self): - docs = render_chart( - values={ - "workers": { - "celery": { - "enableDefault": False, - "sets": [{"name": "test", "kerberosInitContainer": {"enabled": True}}], - }, - } - }, - show_only=["templates/workers/worker-deployment.yaml"], - ) - - assert ( - jmespath.search("spec.template.spec.initContainers[?name=='kerberos-init']", docs[0]) is not None - ) - - def test_overwrite_kerberos_init_container_disable(self): - docs = render_chart( - values={ - "workers": { - "celery": { - "enableDefault": False, - "kerberosInitContainer": {"enabled": True}, - "sets": [{"name": "test", "kerberosInitContainer": {"enabled": False}}], - } - } - }, - show_only=["templates/workers/worker-deployment.yaml"], - ) - - assert ( - jmespath.search("spec.template.spec.initContainers[?name=='kerberos-init'] | [0]", docs[0]) - is None - ) - - def test_overwrite_kerberos_init_container_resources(self): - docs = render_chart( - values={ - "workers": { - "celery": { - "enableDefault": False, - "sets": [ - { - "name": "test", - "kerberosInitContainer": { - "enabled": True, - "resources": { - "limits": {"cpu": "3m", "memory": "4Mi"}, - }, - }, - } - ], - } - } - }, - show_only=["templates/workers/worker-deployment.yaml"], - ) - - assert jmespath.search( - "spec.template.spec.initContainers[?name=='kerberos-init'] | [0].resources", docs[0] - ) == { - "limits": {"cpu": "3m", "memory": "4Mi"}, - } - - @pytest.mark.parametrize( - "workers_values", - [ - { - "celery": { - "enableDefault": False, - "sets": [ - { - "name": "test", - "kerberosInitContainer": { - "enabled": True, - "securityContexts": {"container": {"runAsUser": 10}}, - }, - } - ], - } - }, - { - "celery": { - "kerberosInitContainer": { - "securityContexts": { - "container": {"allowPrivilegeEscalation": False}, - } - }, - "enableDefault": False, - "sets": [ - { - "name": "test", - "kerberosInitContainer": { - "enabled": True, - "securityContexts": {"container": {"runAsUser": 10}}, - }, - } - ], - }, - }, - ], - ) - def test_overwrite_kerberos_init_container_security_context(self, workers_values): - docs = render_chart( - values={"workers": workers_values}, - show_only=["templates/workers/worker-deployment.yaml"], - ) - - assert jmespath.search( - "spec.template.spec.initContainers[?name=='kerberos-init'] | [0].securityContext", docs[0] - ) == {"runAsUser": 10} - - @pytest.mark.parametrize( - "workers_values", - [ - { - "celery": { - "enableDefault": False, - "sets": [ - { - "name": "test", - "kerberosInitContainer": { - "enabled": True, - "containerLifecycleHooks": { - "postStart": {"exec": {"command": ["echo", "{{ .Release.Name }}"]}}, - }, - }, - } - ], - } - }, - { - "celery": { - "kerberosInitContainer": { - "containerLifecycleHooks": {"preStop": {"exec": {"command": ["echo", "test"]}}} - }, - "enableDefault": False, - "sets": [ - { - "name": "test", - "kerberosInitContainer": { - "enabled": True, - "containerLifecycleHooks": { - "postStart": {"exec": {"command": ["echo", "{{ .Release.Name }}"]}}, - }, - }, - } - ], - }, - }, - ], - ) - def test_overwrite_kerberos_init_container_lifecycle_hooks(self, workers_values): - docs = render_chart( - values={"workers": workers_values}, - show_only=["templates/workers/worker-deployment.yaml"], - ) - - assert jmespath.search( - "spec.template.spec.initContainers[?name=='kerberos-init'] | [0].lifecycle", docs[0] - ) == {"postStart": {"exec": {"command": ["echo", "release-name"]}}} - @pytest.mark.parametrize( "workers_values", [ @@ -1568,7 +1405,11 @@ class TestWorkerSets: show_only=["templates/workers/worker-deployment.yaml"], ) - assert jmespath.search("spec.template.spec.containers[?name=='worker-kerberos']", docs[0]) is not None + sidecar = jmespath.search( + "spec.template.spec.initContainers[?name=='worker-kerberos'] | [0]", docs[0] + ) + assert sidecar is not None + assert sidecar["restartPolicy"] == "Always" def test_overwrite_kerberos_sidecar_disable(self): docs = render_chart( @@ -1585,7 +1426,8 @@ class TestWorkerSets: ) assert ( - jmespath.search("spec.template.spec.containers[?name=='worker-kerberos'] | [0]", docs[0]) is None + jmespath.search("spec.template.spec.initContainers[?name=='worker-kerberos'] | [0]", docs[0]) + is None ) @pytest.mark.parametrize( @@ -1602,7 +1444,6 @@ class TestWorkerSets: "failureThreshold": 6, }, ), - ({"enabled": True}, {"enabled": False}, None), ( { "timeoutSeconds": 1, @@ -1620,7 +1461,7 @@ class TestWorkerSets: }, ), ], - ids=["enabled", "disabled", "custom"], + ids=["enabled", "custom"], ) def test_overwrite_kerberos_sidecar_startup_probe( self, startup_probe, worker_set_startup_probe, expected @@ -1643,12 +1484,32 @@ class TestWorkerSets: show_only=["templates/workers/worker-deployment.yaml"], ) - assert ( - jmespath.search( - "spec.template.spec.containers[?name=='worker-kerberos'] | [0].startupProbe", docs[0] - ) - == expected + sidecar = jmespath.search( + "spec.template.spec.initContainers[?name=='worker-kerberos'] | [0]", docs[0] ) + assert sidecar is not None + assert sidecar["restartPolicy"] == "Always" + assert sidecar["startupProbe"] == expected + + def test_overwrite_kerberos_sidecar_startup_probe_disabled(self): + docs = render_chart( + values={ + "workers": { + "celery": { + "enableDefault": False, + "kerberosSidecar": {"enabled": True, "startupProbe": {"enabled": True}}, + "sets": [{"name": "test", "kerberosSidecar": {"startupProbe": {"enabled": False}}}], + } + } + }, + show_only=["templates/workers/worker-deployment.yaml"], + ) + + sidecar = jmespath.search( + "spec.template.spec.initContainers[?name=='worker-kerberos'] | [0]", docs[0] + ) + assert sidecar is not None + assert "startupProbe" not in sidecar @pytest.mark.parametrize( "values", @@ -1699,7 +1560,7 @@ class TestWorkerSets: ) assert jmespath.search( - "spec.template.spec.containers[?name=='worker-kerberos'] | [0].resources", docs[0] + "spec.template.spec.initContainers[?name=='worker-kerberos'] | [0].resources", docs[0] ) == { "limits": {"cpu": "3m", "memory": "4Mi"}, } @@ -1753,7 +1614,7 @@ class TestWorkerSets: ) assert jmespath.search( - "spec.template.spec.containers[?name=='worker-kerberos'] | [0].securityContext", docs[0] + "spec.template.spec.initContainers[?name=='worker-kerberos'] | [0].securityContext", docs[0] ) == {"runAsUser": 10} @pytest.mark.parametrize( @@ -1803,7 +1664,7 @@ class TestWorkerSets: ) assert jmespath.search( - "spec.template.spec.containers[?name=='worker-kerberos'] | [0].lifecycle", docs[0] + "spec.template.spec.initContainers[?name=='worker-kerberos'] | [0].lifecycle", docs[0] ) == {"postStart": {"exec": {"command": ["echo", "release-name"]}}} @pytest.mark.parametrize( diff --git a/chart/tests/helm_tests/security/test_kerberos.py b/chart/tests/helm_tests/security/test_kerberos.py index f8c1a79fbb2..49b8104d640 100644 --- a/chart/tests/helm_tests/security/test_kerberos.py +++ b/chart/tests/helm_tests/security/test_kerberos.py @@ -60,6 +60,70 @@ class TestKerberos: "spec.template.spec.containers[0].env", docs[0] ) + def test_kerberos_sidecar_is_native_sidecar(self): + docs = render_chart( + values={ + "executor": "CeleryExecutor", + "workers": {"celery": {"kerberosSidecar": {"enabled": True}}}, + }, + show_only=["templates/workers/worker-deployment.yaml"], + ) + sidecar = jmespath.search( + "spec.template.spec.initContainers[?name=='worker-kerberos'] | [0]", docs[0] + ) + assert sidecar is not None + assert sidecar["restartPolicy"] == "Always" + + def test_kerberos_sidecar_disabled(self): + docs = render_chart( + values={ + "executor": "CeleryExecutor", + "workers": { + "celery": { + "persistence": {"enabled": True, "fixPermissions": True}, + "kerberosSidecar": {"enabled": False}, + "extraInitContainers": [{"name": "custom-init", "image": "custom-image"}], + } + }, + "dags": {"gitSync": {"enabled": True}}, + }, + show_only=["templates/workers/worker-deployment.yaml"], + ) + assert jmespath.search("spec.template.spec.initContainers[].name", docs[0]) == [ + "volume-permissions", + "wait-for-airflow-migrations", + "git-sync-init", + "custom-init", + ] + + def test_kerberos_sidecar_enabled(self): + docs = render_chart( + values={ + "executor": "CeleryExecutor", + "workers": { + "celery": { + "persistence": {"enabled": True, "fixPermissions": True}, + "kerberosSidecar": {"enabled": True}, + "extraInitContainers": [{"name": "custom-init", "image": "custom-image"}], + } + }, + "dags": {"gitSync": {"enabled": True}}, + }, + show_only=["templates/workers/worker-deployment.yaml"], + ) + assert jmespath.search("spec.template.spec.initContainers[].name", docs[0]) == [ + "volume-permissions", + "worker-kerberos", + "wait-for-airflow-migrations", + "git-sync-init", + "custom-init", + ] + sidecar = jmespath.search( + "spec.template.spec.initContainers[?name=='worker-kerberos'] | [0]", docs[0] + ) + assert sidecar["args"] == ["kerberos"] + assert sidecar["restartPolicy"] == "Always" + def test_kerberos_sidecar_resources(self): docs = render_chart( values={ @@ -76,9 +140,9 @@ class TestKerberos: show_only=["templates/workers/worker-deployment.yaml"], ) - assert jmespath.search("spec.template.spec.containers[2].resources", docs[0]) == { - "requests": {"cpu": "200m", "memory": "200Mi"} - } + assert jmespath.search( + "spec.template.spec.initContainers[?name=='worker-kerberos'] | [0].resources", docs[0] + ) == {"requests": {"cpu": "200m", "memory": "200Mi"}} def test_keberos_sidecar_resources_are_not_added_by_default(self): docs = render_chart( @@ -183,9 +247,8 @@ class TestKerberos: "failureThreshold": 14, }, ), - ({"enabled": False}, None), ], - ids=["default", "custom", "disabled"], + ids=["default", "custom"], ) def test_kerberos_sidecar_startup_probe(self, override, expected): docs = render_chart( @@ -196,12 +259,29 @@ class TestKerberos: show_only=["templates/workers/worker-deployment.yaml"], ) - assert ( - jmespath.search( - "spec.template.spec.containers[?name=='worker-kerberos'] | [0].startupProbe", docs[0] - ) - == expected + sidecar = jmespath.search( + "spec.template.spec.initContainers[?name=='worker-kerberos'] | [0]", docs[0] ) + assert sidecar is not None + assert sidecar["restartPolicy"] == "Always" + assert sidecar["startupProbe"] == expected + + def test_kerberos_sidecar_startup_probe_disabled(self): + docs = render_chart( + values={ + "executor": "CeleryExecutor", + "workers": { + "celery": {"kerberosSidecar": {"enabled": True, "startupProbe": {"enabled": False}}} + }, + }, + show_only=["templates/workers/worker-deployment.yaml"], + ) + + sidecar = jmespath.search( + "spec.template.spec.initContainers[?name=='worker-kerberos'] | [0]", docs[0] + ) + assert sidecar is not None + assert "startupProbe" not in sidecar @pytest.mark.parametrize( "override", diff --git a/chart/tests/helm_tests/security/test_security_context.py b/chart/tests/helm_tests/security/test_security_context.py index 3a1be1371e1..a6d28434742 100644 --- a/chart/tests/helm_tests/security/test_security_context.py +++ b/chart/tests/helm_tests/security/test_security_context.py @@ -294,7 +294,7 @@ class TestSecurityContext: assert ctx_value == jmespath.search("spec.template.spec.containers[1].securityContext", docs[0]) - # Test securityContexts for worker-kerberos main container + # Test securityContexts for worker-kerberos native sidecar def test_worker_kerberos_container_security_context(self): docs = render_chart( values={ @@ -310,28 +310,9 @@ class TestSecurityContext: show_only=["templates/workers/worker-deployment.yaml"], ) - assert jmespath.search("spec.template.spec.containers[2].securityContext", docs[0]) == { - "allowPrivilegeEscalation": False - } - - def test_worker_kerberos_init_container_security_contexts(self): - docs = render_chart( - values={ - "workers": { - "celery": { - "kerberosInitContainer": { - "enabled": True, - "securityContexts": {"container": {"runAsUser": 2000}}, - } - } - } - }, - show_only=["templates/workers/worker-deployment.yaml"], - ) - assert jmespath.search( - "spec.template.spec.initContainers[?name=='kerberos-init'] | [0].securityContext", docs[0] - ) == {"runAsUser": 2000} + "spec.template.spec.initContainers[?name=='worker-kerberos'] | [0].securityContext", docs[0] + ) == {"allowPrivilegeEscalation": False} def test_wait_for_migrations_init_container_setting(self): ctx_value = {"allowPrivilegeEscalation": False} diff --git a/chart/values.schema.json b/chart/values.schema.json index 9e5b1782b1e..0831719dec1 100644 --- a/chart/values.schema.json +++ b/chart/values.schema.json @@ -2123,7 +2123,7 @@ "default": false }, "startupProbe": { - "description": "Startup probe for the Kerberos worker sidecar (runs `klist -s`).", + "description": "Wait for a valid Kerberos ticket before starting the Airflow Celery worker (runs `klist -s`). Disabling this probe allows them to start before a ticket is available.", "type": "object", "additionalProperties": false, "properties": { @@ -2231,89 +2231,6 @@ } } }, - "kerberosInitContainer": { - "description": "Kerberos init container for Airflow Celery workers.", - "type": "object", - "additionalProperties": false, - "properties": { - "enabled": { - "description": "Enable Kerberos init container.", - "type": "boolean", - "default": false - }, - "resources": { - "description": "Resources on kerberos init container.", - "type": "object", - "default": {}, - "examples": [ - { - "limits": { - "cpu": "100m", - "memory": "128Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - } - ], - "$ref": "#/definitions/io.k8s.api.core.v1.ResourceRequirements" - }, - "containerLifecycleHooks": { - "description": "Container Lifecycle Hooks definition for the kerberos init container. If not set, the values from `workers.kerberosInitContainer.containerLifecycleHooks` will be used.", - "type": "object", - "$ref": "#/definitions/io.k8s.api.core.v1.Lifecycle", - "default": {}, - "x-docsSection": "Kubernetes", - "examples": [ - { - "postStart": { - "exec": { - "command": [ - "/bin/sh", - "-c", - "echo postStart handler > /usr/share/message" - ] - } - }, - "preStop": { - "exec": { - "command": [ - "/bin/sh", - "-c", - "echo preStop handler > /usr/share/message" - ] - } - } - } - ] - }, - "securityContexts": { - "description": "Security context definition for the kerberos init container. If not set, the values from `workers.kerberosInitContainer.securityContexts` will be used.", - "type": "object", - "x-docsSection": "Kubernetes", - "properties": { - "container": { - "description": "Container security context definition for the kerberos init container.", - "type": "object", - "$ref": "#/definitions/io.k8s.api.core.v1.SecurityContext", - "default": {}, - "x-docsSection": "Kubernetes", - "examples": [ - { - "allowPrivilegeEscalation": false, - "capabilities": { - "drop": [ - "ALL" - ] - } - } - ] - } - } - } - } - }, "resources": { "description": "Resource configuration for Airflow Celery workers.", "type": "object", @@ -2915,7 +2832,7 @@ "default": false }, "startupProbe": { - "description": "Startup probe for the Kerberos worker sidecar (runs `klist -s`).", + "description": "Wait for a valid Kerberos ticket before starting pod-template-file base container (runs `klist -s`). Disabling this probe allows them to start before a ticket is available.", "type": "object", "additionalProperties": false, "properties": { @@ -3023,89 +2940,6 @@ } } }, - "kerberosInitContainer": { - "description": "Kerberos init container for pods created with pod-template-file.", - "type": "object", - "additionalProperties": false, - "properties": { - "enabled": { - "description": "Enable kerberos init container.", - "type": "boolean", - "default": false - }, - "resources": { - "description": "Resources on kerberos init container.", - "type": "object", - "default": {}, - "examples": [ - { - "limits": { - "cpu": "100m", - "memory": "128Mi" - }, - "requests": { - "cpu": "100m", - "memory": "128Mi" - } - } - ], - "$ref": "#/definitions/io.k8s.api.core.v1.ResourceRequirements" - }, - "containerLifecycleHooks": { - "description": "Container Lifecycle Hooks definition for the kerberos init container. If not set, the values from `workers.kerberosInitContainer.containerLifecycleHooks` will be used.", - "type": "object", - "$ref": "#/definitions/io.k8s.api.core.v1.Lifecycle", - "default": {}, - "x-docsSection": "Kubernetes", - "examples": [ - { - "postStart": { - "exec": { - "command": [ - "/bin/sh", - "-c", - "echo postStart handler > /usr/share/message" - ] - } - }, - "preStop": { - "exec": { - "command": [ - "/bin/sh", - "-c", - "echo preStop handler > /usr/share/message" - ] - } - } - } - ] - }, - "securityContexts": { - "description": "Security context definition for the kerberos init container. If not set, the values from `workers.kerberosInitContainer.securityContexts` will be used.", - "type": "object", - "x-docsSection": "Kubernetes", - "properties": { - "container": { - "description": "Container security context definition for the kerberos init container.", - "type": "object", - "$ref": "#/definitions/io.k8s.api.core.v1.SecurityContext", - "default": {}, - "x-docsSection": "Kubernetes", - "examples": [ - { - "allowPrivilegeEscalation": false, - "capabilities": { - "drop": [ - "ALL" - ] - } - } - ] - } - } - } - } - }, "readonlyKerberosCache": { "description": "Should the kubernetes kerberos cache be readonly for the workers.", "type": "boolean", diff --git a/chart/values.yaml b/chart/values.yaml index 6f301412cdf..dad421634d8 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -844,8 +844,9 @@ workers: # Container level lifecycle hooks containerLifecycleHooks: {} - # Startup probe for the kerberos sidecar: `klist -s` succeeds once the credential - # cache holds a valid, unexpired ticket. Disable for custom images without `klist`. + # Wait for a valid Kerberos ticket before starting the Airflow Celery worker. + # Disabling this probe allows them to start before a ticket is available. + # Custom images must provide `klist` to use this probe. startupProbe: enabled: true timeoutSeconds: 5 @@ -853,28 +854,6 @@ workers: periodSeconds: 10 failureThreshold: 6 - # Kerberos init container configuration for Airflow Celery workers - # If not set, the values from `workers.kerberosInitContainer` section will be used. - kerberosInitContainer: - # Enable kerberos init container - # If `workers.kerberosInitContainer.enabled` is set to True, this flag has no effect - enabled: false - - resources: {} - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - - # Detailed default security context for kerberos init container - securityContexts: - container: {} - - # Container level lifecycle hooks - containerLifecycleHooks: {} - # Should the kerberos-ccache mount be readonly for Celery Workers readonlyKerberosCache: true @@ -1103,8 +1082,9 @@ workers: # Container level lifecycle hooks containerLifecycleHooks: {} - # Startup probe for the kerberos sidecar: `klist -s` succeeds once the credential - # cache holds a valid, unexpired ticket. Disable for custom images without `klist`. + # Wait for a valid Kerberos ticket before starting pod-template-file base container. + # Disabling this probe allows them to start before a ticket is available. + # Custom images must provide `klist` to use this probe. startupProbe: enabled: true timeoutSeconds: 5 @@ -1112,28 +1092,6 @@ workers: periodSeconds: 10 failureThreshold: 6 - # Kerberos init container configuration for pods created with pod-template-file - # If not set, the values from `workers.kerberosInitContainer` section will be used. - kerberosInitContainer: - # Enable kerberos init container - # If `workers.kerberosInitContainer.enabled` is set to True, this flag has no effect - enabled: false - - resources: {} - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - - # Detailed default security context for kerberos init container - securityContexts: - container: {} - - # Container level lifecycle hooks - containerLifecycleHooks: {} - # Should the kerberos-ccache mount be readonly for Kubernetes Workers readonlyKerberosCache: true