apache-airflow-providers-cncf-kubernetes 8.4.0rc1__tar.gz → 8.4.1rc1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of apache-airflow-providers-cncf-kubernetes might be problematic. Click here for more details.

Files changed (59) hide show
  1. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/PKG-INFO +6 -6
  2. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/README.rst +3 -3
  3. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/__init__.py +1 -1
  4. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/executors/kubernetes_executor_utils.py +10 -0
  5. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/executors/local_kubernetes_executor.py +5 -0
  6. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/get_provider_info.py +2 -1
  7. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/hooks/kubernetes.py +40 -1
  8. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/operators/job.py +53 -2
  9. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/triggers/job.py +54 -1
  10. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/pyproject.toml +3 -3
  11. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/LICENSE +0 -0
  12. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/backcompat/__init__.py +0 -0
  13. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/backcompat/backwards_compat_converters.py +0 -0
  14. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/callbacks.py +0 -0
  15. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/cli/__init__.py +0 -0
  16. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/cli/kubernetes_command.py +0 -0
  17. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/decorators/__init__.py +0 -0
  18. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/decorators/kubernetes.py +0 -0
  19. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/executors/__init__.py +0 -0
  20. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/executors/kubernetes_executor.py +0 -0
  21. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/executors/kubernetes_executor_types.py +0 -0
  22. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/hooks/__init__.py +0 -0
  23. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/k8s_model.py +0 -0
  24. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/kube_client.py +0 -0
  25. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/kube_config.py +0 -0
  26. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/kubernetes_executor_templates/__init__.py +0 -0
  27. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/kubernetes_executor_templates/basic_template.yaml +0 -0
  28. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/kubernetes_helper_functions.py +0 -0
  29. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/operators/__init__.py +0 -0
  30. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/operators/custom_object_launcher.py +0 -0
  31. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/operators/kubernetes_pod.py +0 -0
  32. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/operators/pod.py +0 -0
  33. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/operators/resource.py +0 -0
  34. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/operators/spark_kubernetes.py +0 -0
  35. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/pod_generator.py +0 -0
  36. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/pod_generator_deprecated.py +0 -0
  37. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/pod_launcher_deprecated.py +0 -0
  38. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/pod_template_file_examples/__init__.py +0 -0
  39. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/pod_template_file_examples/dags_in_image_template.yaml +0 -0
  40. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/pod_template_file_examples/dags_in_volume_template.yaml +0 -0
  41. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/pod_template_file_examples/git_sync_template.yaml +0 -0
  42. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/python_kubernetes_script.jinja2 +0 -0
  43. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/python_kubernetes_script.py +0 -0
  44. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/resource_convert/__init__.py +0 -0
  45. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/resource_convert/configmap.py +0 -0
  46. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/resource_convert/env_variable.py +0 -0
  47. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/resource_convert/secret.py +0 -0
  48. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/secret.py +0 -0
  49. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/sensors/__init__.py +0 -0
  50. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/sensors/spark_kubernetes.py +0 -0
  51. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/template_rendering.py +0 -0
  52. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/triggers/__init__.py +0 -0
  53. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/triggers/kubernetes_pod.py +0 -0
  54. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/triggers/pod.py +0 -0
  55. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/utils/__init__.py +0 -0
  56. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/utils/delete_from.py +0 -0
  57. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/utils/k8s_resource_iterator.py +0 -0
  58. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/utils/pod_manager.py +0 -0
  59. {apache_airflow_providers_cncf_kubernetes-8.4.0rc1 → apache_airflow_providers_cncf_kubernetes-8.4.1rc1}/airflow/providers/cncf/kubernetes/utils/xcom_sidecar.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: apache-airflow-providers-cncf-kubernetes
3
- Version: 8.4.0rc1
3
+ Version: 8.4.1rc1
4
4
  Summary: Provider package apache-airflow-providers-cncf-kubernetes for Apache Airflow
5
5
  Keywords: airflow-provider,cncf.kubernetes,airflow,integration
6
6
  Author-email: Apache Software Foundation <dev@airflow.apache.org>
@@ -29,8 +29,8 @@ Requires-Dist: google-re2>=1.0
29
29
  Requires-Dist: kubernetes>=29.0.0,<=30.1.0
30
30
  Requires-Dist: kubernetes_asyncio>=29.0.0,<=30.1.0
31
31
  Project-URL: Bug Tracker, https://github.com/apache/airflow/issues
32
- Project-URL: Changelog, https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/8.4.0/changelog.html
33
- Project-URL: Documentation, https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/8.4.0
32
+ Project-URL: Changelog, https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/8.4.1/changelog.html
33
+ Project-URL: Documentation, https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/8.4.1
34
34
  Project-URL: Slack Chat, https://s.apache.org/airflow-slack
35
35
  Project-URL: Source Code, https://github.com/apache/airflow
36
36
  Project-URL: Twitter, https://twitter.com/ApacheAirflow
@@ -80,7 +80,7 @@ Project-URL: YouTube, https://www.youtube.com/channel/UCSXwxpWZQ7XZ1WL3wqevChA/
80
80
 
81
81
  Package ``apache-airflow-providers-cncf-kubernetes``
82
82
 
83
- Release: ``8.4.0.rc1``
83
+ Release: ``8.4.1.rc1``
84
84
 
85
85
 
86
86
  `Kubernetes <https://kubernetes.io/>`__
@@ -93,7 +93,7 @@ This is a provider package for ``cncf.kubernetes`` provider. All classes for thi
93
93
  are in ``airflow.providers.cncf.kubernetes`` python package.
94
94
 
95
95
  You can find package information and changelog for the provider
96
- in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/8.4.0/>`_.
96
+ in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/8.4.1/>`_.
97
97
 
98
98
  Installation
99
99
  ------------
@@ -120,4 +120,4 @@ PIP package Version required
120
120
  ====================== =====================
121
121
 
122
122
  The changelog for the provider package can be found in the
123
- `changelog <https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/8.4.0/changelog.html>`_.
123
+ `changelog <https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/8.4.1/changelog.html>`_.
@@ -42,7 +42,7 @@
42
42
 
43
43
  Package ``apache-airflow-providers-cncf-kubernetes``
44
44
 
45
- Release: ``8.4.0.rc1``
45
+ Release: ``8.4.1.rc1``
46
46
 
47
47
 
48
48
  `Kubernetes <https://kubernetes.io/>`__
@@ -55,7 +55,7 @@ This is a provider package for ``cncf.kubernetes`` provider. All classes for thi
55
55
  are in ``airflow.providers.cncf.kubernetes`` python package.
56
56
 
57
57
  You can find package information and changelog for the provider
58
- in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/8.4.0/>`_.
58
+ in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/8.4.1/>`_.
59
59
 
60
60
  Installation
61
61
  ------------
@@ -82,4 +82,4 @@ PIP package Version required
82
82
  ====================== =====================
83
83
 
84
84
  The changelog for the provider package can be found in the
85
- `changelog <https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/8.4.0/changelog.html>`_.
85
+ `changelog <https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/8.4.1/changelog.html>`_.
@@ -29,7 +29,7 @@ from airflow import __version__ as airflow_version
29
29
 
30
30
  __all__ = ["__version__"]
31
31
 
32
- __version__ = "8.4.0"
32
+ __version__ = "8.4.1"
33
33
 
34
34
  if packaging.version.parse(packaging.version.parse(airflow_version).base_version) < packaging.version.parse(
35
35
  "2.8.0"
@@ -220,6 +220,16 @@ class KubernetesJobWatcher(multiprocessing.Process, LoggingMixin):
220
220
  # However, need to free the executor slot from the current executor.
221
221
  self.log.info("Event: pod %s adopted, annotations: %s", pod_name, annotations_string)
222
222
  self.watcher_queue.put((pod_name, namespace, ADOPTED, annotations, resource_version))
223
+ elif hasattr(pod.status, "reason") and pod.status.reason == "ProviderFailed":
224
+ # Most likely this happens due to Kubernetes setup (virtual kubelet, virtual nodes, etc.)
225
+ self.log.error(
226
+ "Event: %s failed to start with reason ProviderFailed, annotations: %s",
227
+ pod_name,
228
+ annotations_string,
229
+ )
230
+ self.watcher_queue.put(
231
+ (pod_name, namespace, TaskInstanceState.FAILED, annotations, resource_version)
232
+ )
223
233
  elif status == "Pending":
224
234
  # deletion_timestamp is set by kube server when a graceful deletion is requested.
225
235
  # since kube server have received request to delete pod set TI state failed
@@ -103,6 +103,11 @@ class LocalKubernetesExecutor(LoggingMixin):
103
103
  """Number of new tasks this executor instance can accept."""
104
104
  return self.local_executor.slots_available
105
105
 
106
+ @property
107
+ def slots_occupied(self):
108
+ """Number of tasks this executor instance is currently managing."""
109
+ return len(self.running) + len(self.queued_tasks)
110
+
106
111
  def queue_command(
107
112
  self,
108
113
  task_instance: TaskInstance,
@@ -28,8 +28,9 @@ def get_provider_info():
28
28
  "name": "Kubernetes",
29
29
  "description": "`Kubernetes <https://kubernetes.io/>`__\n",
30
30
  "state": "ready",
31
- "source-date-epoch": 1723969891,
31
+ "source-date-epoch": 1724520459,
32
32
  "versions": [
33
+ "8.4.1",
33
34
  "8.4.0",
34
35
  "8.3.4",
35
36
  "8.3.3",
@@ -37,7 +37,11 @@ from airflow.hooks.base import BaseHook
37
37
  from airflow.models import Connection
38
38
  from airflow.providers.cncf.kubernetes.kube_client import _disable_verify_ssl, _enable_tcp_keepalive
39
39
  from airflow.providers.cncf.kubernetes.kubernetes_helper_functions import should_retry_creation
40
- from airflow.providers.cncf.kubernetes.utils.pod_manager import PodOperatorHookProtocol
40
+ from airflow.providers.cncf.kubernetes.utils.pod_manager import (
41
+ PodOperatorHookProtocol,
42
+ container_is_completed,
43
+ container_is_running,
44
+ )
41
45
  from airflow.utils import yaml
42
46
 
43
47
  if TYPE_CHECKING:
@@ -682,6 +686,7 @@ class AsyncKubernetesHook(KubernetesHook):
682
686
 
683
687
  if self.config_dict:
684
688
  self.log.debug(LOADING_KUBE_CONFIG_FILE_RESOURCE.format("config dictionary"))
689
+ self._is_in_cluster = False
685
690
  await async_config.load_kube_config_from_dict(self.config_dict)
686
691
  return async_client.ApiClient()
687
692
 
@@ -830,3 +835,37 @@ class AsyncKubernetesHook(KubernetesHook):
830
835
  return job
831
836
  self.log.info("The job '%s' is incomplete. Sleeping for %i sec.", name, poll_interval)
832
837
  await asyncio.sleep(poll_interval)
838
+
839
+ async def wait_until_container_complete(
840
+ self, name: str, namespace: str, container_name: str, poll_interval: float = 10
841
+ ) -> None:
842
+ """
843
+ Wait for the given container in the given pod to be completed.
844
+
845
+ :param name: Name of Pod to fetch.
846
+ :param namespace: Namespace of the Pod.
847
+ :param container_name: name of the container within the pod to monitor
848
+ """
849
+ while True:
850
+ pod = await self.get_pod(name=name, namespace=namespace)
851
+ if container_is_completed(pod=pod, container_name=container_name):
852
+ break
853
+ self.log.info("Waiting for container '%s' state to be completed", container_name)
854
+ await asyncio.sleep(poll_interval)
855
+
856
+ async def wait_until_container_started(
857
+ self, name: str, namespace: str, container_name: str, poll_interval: float = 10
858
+ ) -> None:
859
+ """
860
+ Wait for the given container in the given pod to be started.
861
+
862
+ :param name: Name of Pod to fetch.
863
+ :param namespace: Namespace of the Pod.
864
+ :param container_name: name of the container within the pod to monitor
865
+ """
866
+ while True:
867
+ pod = await self.get_pod(name=name, namespace=namespace)
868
+ if container_is_running(pod=pod, container_name=container_name):
869
+ break
870
+ self.log.info("Waiting for container '%s' state to be running", container_name)
871
+ await asyncio.sleep(poll_interval)
@@ -19,6 +19,7 @@
19
19
  from __future__ import annotations
20
20
 
21
21
  import copy
22
+ import json
22
23
  import logging
23
24
  import os
24
25
  from functools import cached_property
@@ -39,6 +40,7 @@ from airflow.providers.cncf.kubernetes.kubernetes_helper_functions import (
39
40
  from airflow.providers.cncf.kubernetes.operators.pod import KubernetesPodOperator
40
41
  from airflow.providers.cncf.kubernetes.pod_generator import PodGenerator, merge_objects
41
42
  from airflow.providers.cncf.kubernetes.triggers.job import KubernetesJobTrigger
43
+ from airflow.providers.cncf.kubernetes.utils.pod_manager import EMPTY_XCOM_RESULT, PodNotFoundException
42
44
  from airflow.utils import yaml
43
45
  from airflow.utils.context import Context
44
46
 
@@ -135,7 +137,7 @@ class KubernetesJobOperator(KubernetesPodOperator):
135
137
  return hook
136
138
 
137
139
  @cached_property
138
- def client(self) -> BatchV1Api:
140
+ def job_client(self) -> BatchV1Api:
139
141
  return self.hook.batch_v1_client
140
142
 
141
143
  def create_job(self, job_request_obj: k8s.V1Job) -> k8s.V1Job:
@@ -150,6 +152,11 @@ class KubernetesJobOperator(KubernetesPodOperator):
150
152
  "Deferrable mode is available only with parameter `wait_until_job_complete=True`. "
151
153
  "Please, set it up."
152
154
  )
155
+ if (self.get_logs or self.do_xcom_push) and not self.wait_until_job_complete:
156
+ self.log.warning(
157
+ "Getting Logs and pushing to XCom are available only with parameter `wait_until_job_complete=True`. "
158
+ "Please, set it up."
159
+ )
153
160
  self.job_request_obj = self.build_job_request_obj(context)
154
161
  self.job = self.create_job( # must set `self.job` for `on_kill`
155
162
  job_request_obj=self.job_request_obj
@@ -159,16 +166,34 @@ class KubernetesJobOperator(KubernetesPodOperator):
159
166
  ti.xcom_push(key="job_name", value=self.job.metadata.name)
160
167
  ti.xcom_push(key="job_namespace", value=self.job.metadata.namespace)
161
168
 
169
+ if self.pod is None:
170
+ self.pod = self.get_or_create_pod( # must set `self.pod` for `on_kill`
171
+ pod_request_obj=self.pod_request_obj,
172
+ context=context,
173
+ )
174
+
162
175
  if self.wait_until_job_complete and self.deferrable:
163
176
  self.execute_deferrable()
164
177
  return
165
178
 
166
179
  if self.wait_until_job_complete:
180
+ if self.do_xcom_push:
181
+ self.pod_manager.await_container_completion(
182
+ pod=self.pod, container_name=self.base_container_name
183
+ )
184
+ self.pod_manager.await_xcom_sidecar_container_start(pod=self.pod)
185
+ xcom_result = self.extract_xcom(pod=self.pod)
167
186
  self.job = self.hook.wait_until_job_complete(
168
187
  job_name=self.job.metadata.name,
169
188
  namespace=self.job.metadata.namespace,
170
189
  job_poll_interval=self.job_poll_interval,
171
190
  )
191
+ if self.get_logs:
192
+ self.pod_manager.fetch_requested_container_logs(
193
+ pod=self.pod,
194
+ containers=self.container_logs,
195
+ follow_logs=True,
196
+ )
172
197
 
173
198
  ti.xcom_push(key="job", value=self.job.to_dict())
174
199
  if self.wait_until_job_complete:
@@ -176,17 +201,24 @@ class KubernetesJobOperator(KubernetesPodOperator):
176
201
  raise AirflowException(
177
202
  f"Kubernetes job '{self.job.metadata.name}' is failed with error '{error_message}'"
178
203
  )
204
+ if self.do_xcom_push:
205
+ return xcom_result
179
206
 
180
207
  def execute_deferrable(self):
181
208
  self.defer(
182
209
  trigger=KubernetesJobTrigger(
183
210
  job_name=self.job.metadata.name, # type: ignore[union-attr]
184
211
  job_namespace=self.job.metadata.namespace, # type: ignore[union-attr]
212
+ pod_name=self.pod.metadata.name, # type: ignore[union-attr]
213
+ pod_namespace=self.pod.metadata.namespace, # type: ignore[union-attr]
214
+ base_container_name=self.base_container_name,
185
215
  kubernetes_conn_id=self.kubernetes_conn_id,
186
216
  cluster_context=self.cluster_context,
187
217
  config_file=self.config_file,
188
218
  in_cluster=self.in_cluster,
189
219
  poll_interval=self.job_poll_interval,
220
+ get_logs=self.get_logs,
221
+ do_xcom_push=self.do_xcom_push,
190
222
  ),
191
223
  method_name="execute_complete",
192
224
  )
@@ -197,6 +229,22 @@ class KubernetesJobOperator(KubernetesPodOperator):
197
229
  if event["status"] == "error":
198
230
  raise AirflowException(event["message"])
199
231
 
232
+ if self.get_logs:
233
+ pod_name = event["pod_name"]
234
+ pod_namespace = event["pod_namespace"]
235
+ self.pod = self.hook.get_pod(pod_name, pod_namespace)
236
+ if not self.pod:
237
+ raise PodNotFoundException("Could not find pod after resuming from deferral")
238
+ self._write_logs(self.pod)
239
+
240
+ if self.do_xcom_push:
241
+ xcom_result = event["xcom_result"]
242
+ if isinstance(xcom_result, str) and xcom_result.rstrip() == EMPTY_XCOM_RESULT:
243
+ self.log.info("xcom result file is empty.")
244
+ return None
245
+ self.log.info("xcom result: \n%s", xcom_result)
246
+ return json.loads(xcom_result)
247
+
200
248
  @staticmethod
201
249
  def deserialize_job_template_file(path: str) -> k8s.V1Job:
202
250
  """
@@ -229,7 +277,9 @@ class KubernetesJobOperator(KubernetesPodOperator):
229
277
  }
230
278
  if self.termination_grace_period is not None:
231
279
  kwargs.update(grace_period_seconds=self.termination_grace_period)
232
- self.client.delete_namespaced_job(**kwargs)
280
+ self.job_client.delete_namespaced_job(**kwargs)
281
+ if self.pod:
282
+ super().on_kill()
233
283
 
234
284
  def build_job_request_obj(self, context: Context | None = None) -> k8s.V1Job:
235
285
  """
@@ -254,6 +304,7 @@ class KubernetesJobOperator(KubernetesPodOperator):
254
304
  metadata=pod_template.metadata,
255
305
  spec=pod_template.spec,
256
306
  )
307
+ self.pod_request_obj = pod_template
257
308
 
258
309
  job = k8s.V1Job(
259
310
  api_version="batch/v1",
@@ -16,10 +16,13 @@
16
16
  # under the License.
17
17
  from __future__ import annotations
18
18
 
19
+ import asyncio
19
20
  from functools import cached_property
20
21
  from typing import TYPE_CHECKING, Any, AsyncIterator
21
22
 
22
- from airflow.providers.cncf.kubernetes.hooks.kubernetes import AsyncKubernetesHook
23
+ from airflow.providers.cncf.kubernetes.hooks.kubernetes import AsyncKubernetesHook, KubernetesHook
24
+ from airflow.providers.cncf.kubernetes.utils.pod_manager import PodManager
25
+ from airflow.providers.cncf.kubernetes.utils.xcom_sidecar import PodDefaults
23
26
  from airflow.triggers.base import BaseTrigger, TriggerEvent
24
27
 
25
28
  if TYPE_CHECKING:
@@ -32,32 +35,49 @@ class KubernetesJobTrigger(BaseTrigger):
32
35
 
33
36
  :param job_name: The name of the job.
34
37
  :param job_namespace: The namespace of the job.
38
+ :param pod_name: The name of the Pod.
39
+ :param pod_namespace: The namespace of the Pod.
40
+ :param base_container_name: The name of the base container in the pod.
35
41
  :param kubernetes_conn_id: The :ref:`kubernetes connection id <howto/connection:kubernetes>`
36
42
  for the Kubernetes cluster.
37
43
  :param cluster_context: Context that points to kubernetes cluster.
38
44
  :param config_file: Path to kubeconfig file.
39
45
  :param poll_interval: Polling period in seconds to check for the status.
40
46
  :param in_cluster: run kubernetes client with in_cluster configuration.
47
+ :param get_logs: get the stdout of the base container as logs of the tasks.
48
+ :param do_xcom_push: If True, the content of the file
49
+ /airflow/xcom/return.json in the container will also be pushed to an
50
+ XCom when the container completes.
41
51
  """
42
52
 
43
53
  def __init__(
44
54
  self,
45
55
  job_name: str,
46
56
  job_namespace: str,
57
+ pod_name: str,
58
+ pod_namespace: str,
59
+ base_container_name: str,
47
60
  kubernetes_conn_id: str | None = None,
48
61
  poll_interval: float = 10.0,
49
62
  cluster_context: str | None = None,
50
63
  config_file: str | None = None,
51
64
  in_cluster: bool | None = None,
65
+ get_logs: bool = True,
66
+ do_xcom_push: bool = False,
52
67
  ):
53
68
  super().__init__()
54
69
  self.job_name = job_name
55
70
  self.job_namespace = job_namespace
71
+ self.pod_name = pod_name
72
+ self.pod_namespace = pod_namespace
73
+ self.base_container_name = base_container_name
56
74
  self.kubernetes_conn_id = kubernetes_conn_id
57
75
  self.poll_interval = poll_interval
58
76
  self.cluster_context = cluster_context
59
77
  self.config_file = config_file
60
78
  self.in_cluster = in_cluster
79
+ self.get_logs = get_logs
80
+ self.do_xcom_push = do_xcom_push
61
81
 
62
82
  def serialize(self) -> tuple[str, dict[str, Any]]:
63
83
  """Serialize KubernetesCreateJobTrigger arguments and classpath."""
@@ -66,16 +86,36 @@ class KubernetesJobTrigger(BaseTrigger):
66
86
  {
67
87
  "job_name": self.job_name,
68
88
  "job_namespace": self.job_namespace,
89
+ "pod_name": self.pod_name,
90
+ "pod_namespace": self.pod_namespace,
91
+ "base_container_name": self.base_container_name,
69
92
  "kubernetes_conn_id": self.kubernetes_conn_id,
70
93
  "poll_interval": self.poll_interval,
71
94
  "cluster_context": self.cluster_context,
72
95
  "config_file": self.config_file,
73
96
  "in_cluster": self.in_cluster,
97
+ "get_logs": self.get_logs,
98
+ "do_xcom_push": self.do_xcom_push,
74
99
  },
75
100
  )
76
101
 
77
102
  async def run(self) -> AsyncIterator[TriggerEvent]: # type: ignore[override]
78
103
  """Get current job status and yield a TriggerEvent."""
104
+ if self.get_logs or self.do_xcom_push:
105
+ pod = await self.hook.get_pod(name=self.pod_name, namespace=self.pod_namespace)
106
+ if self.do_xcom_push:
107
+ await self.hook.wait_until_container_complete(
108
+ name=self.pod_name, namespace=self.pod_namespace, container_name=self.base_container_name
109
+ )
110
+ self.log.info("Checking if xcom sidecar container is started.")
111
+ await self.hook.wait_until_container_started(
112
+ name=self.pod_name,
113
+ namespace=self.pod_namespace,
114
+ container_name=PodDefaults.SIDECAR_CONTAINER_NAME,
115
+ )
116
+ self.log.info("Extracting result from xcom sidecar container.")
117
+ loop = asyncio.get_running_loop()
118
+ xcom_result = await loop.run_in_executor(None, self.pod_manager.extract_xcom, pod)
79
119
  job: V1Job = await self.hook.wait_until_job_complete(name=self.job_name, namespace=self.job_namespace)
80
120
  job_dict = job.to_dict()
81
121
  error_message = self.hook.is_job_failed(job=job)
@@ -83,11 +123,14 @@ class KubernetesJobTrigger(BaseTrigger):
83
123
  {
84
124
  "name": job.metadata.name,
85
125
  "namespace": job.metadata.namespace,
126
+ "pod_name": pod.metadata.name if self.get_logs else None,
127
+ "pod_namespace": pod.metadata.namespace if self.get_logs else None,
86
128
  "status": "error" if error_message else "success",
87
129
  "message": f"Job failed with error: {error_message}"
88
130
  if error_message
89
131
  else "Job completed successfully",
90
132
  "job": job_dict,
133
+ "xcom_result": xcom_result if self.do_xcom_push else None,
91
134
  }
92
135
  )
93
136
 
@@ -99,3 +142,13 @@ class KubernetesJobTrigger(BaseTrigger):
99
142
  config_file=self.config_file,
100
143
  cluster_context=self.cluster_context,
101
144
  )
145
+
146
+ @cached_property
147
+ def pod_manager(self) -> PodManager:
148
+ sync_hook = KubernetesHook(
149
+ conn_id=self.kubernetes_conn_id,
150
+ in_cluster=self.in_cluster,
151
+ config_file=self.config_file,
152
+ cluster_context=self.cluster_context,
153
+ )
154
+ return PodManager(kube_client=sync_hook.core_v1_client)
@@ -28,7 +28,7 @@ build-backend = "flit_core.buildapi"
28
28
 
29
29
  [project]
30
30
  name = "apache-airflow-providers-cncf-kubernetes"
31
- version = "8.4.0.rc1"
31
+ version = "8.4.1.rc1"
32
32
  description = "Provider package apache-airflow-providers-cncf-kubernetes for Apache Airflow"
33
33
  readme = "README.rst"
34
34
  authors = [
@@ -66,8 +66,8 @@ dependencies = [
66
66
  ]
67
67
 
68
68
  [project.urls]
69
- "Documentation" = "https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/8.4.0"
70
- "Changelog" = "https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/8.4.0/changelog.html"
69
+ "Documentation" = "https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/8.4.1"
70
+ "Changelog" = "https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/8.4.1/changelog.html"
71
71
  "Bug Tracker" = "https://github.com/apache/airflow/issues"
72
72
  "Source Code" = "https://github.com/apache/airflow"
73
73
  "Slack Chat" = "https://s.apache.org/airflow-slack"