apache-airflow-providers-cncf-kubernetes 7.0.0__tar.gz → 7.1.0__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 (39) hide show
  1. {apache-airflow-providers-cncf-kubernetes-7.0.0/apache_airflow_providers_cncf_kubernetes.egg-info → apache-airflow-providers-cncf-kubernetes-7.1.0}/PKG-INFO +37 -7
  2. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/README.rst +33 -3
  3. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/airflow/providers/cncf/kubernetes/__init__.py +1 -1
  4. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/airflow/providers/cncf/kubernetes/backcompat/backwards_compat_converters.py +10 -10
  5. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/airflow/providers/cncf/kubernetes/get_provider_info.py +3 -1
  6. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/airflow/providers/cncf/kubernetes/hooks/kubernetes.py +14 -12
  7. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/airflow/providers/cncf/kubernetes/operators/pod.py +22 -12
  8. apache-airflow-providers-cncf-kubernetes-7.1.0/airflow/providers/cncf/kubernetes/operators/resource.py +103 -0
  9. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/airflow/providers/cncf/kubernetes/operators/spark_kubernetes.py +1 -1
  10. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/airflow/providers/cncf/kubernetes/python_kubernetes_script.py +2 -2
  11. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/airflow/providers/cncf/kubernetes/sensors/spark_kubernetes.py +1 -1
  12. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/airflow/providers/cncf/kubernetes/triggers/pod.py +1 -1
  13. apache-airflow-providers-cncf-kubernetes-7.1.0/airflow/providers/cncf/kubernetes/utils/delete_from.py +157 -0
  14. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/airflow/providers/cncf/kubernetes/utils/pod_manager.py +35 -21
  15. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/airflow/providers/cncf/kubernetes/utils/xcom_sidecar.py +2 -2
  16. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0/apache_airflow_providers_cncf_kubernetes.egg-info}/PKG-INFO +37 -7
  17. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/apache_airflow_providers_cncf_kubernetes.egg-info/SOURCES.txt +2 -0
  18. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/pyproject.toml +6 -5
  19. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/setup.cfg +3 -3
  20. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/setup.py +1 -1
  21. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/LICENSE +0 -0
  22. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/MANIFEST.in +0 -0
  23. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/NOTICE +0 -0
  24. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/airflow/providers/cncf/kubernetes/backcompat/__init__.py +0 -0
  25. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/airflow/providers/cncf/kubernetes/decorators/__init__.py +0 -0
  26. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/airflow/providers/cncf/kubernetes/decorators/kubernetes.py +0 -0
  27. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/airflow/providers/cncf/kubernetes/hooks/__init__.py +0 -0
  28. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/airflow/providers/cncf/kubernetes/operators/__init__.py +0 -0
  29. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/airflow/providers/cncf/kubernetes/operators/kubernetes_pod.py +0 -0
  30. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/airflow/providers/cncf/kubernetes/python_kubernetes_script.jinja2 +0 -0
  31. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/airflow/providers/cncf/kubernetes/sensors/__init__.py +0 -0
  32. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/airflow/providers/cncf/kubernetes/triggers/__init__.py +0 -0
  33. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/airflow/providers/cncf/kubernetes/triggers/kubernetes_pod.py +0 -0
  34. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/airflow/providers/cncf/kubernetes/utils/__init__.py +0 -0
  35. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/apache_airflow_providers_cncf_kubernetes.egg-info/dependency_links.txt +0 -0
  36. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/apache_airflow_providers_cncf_kubernetes.egg-info/entry_points.txt +0 -0
  37. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/apache_airflow_providers_cncf_kubernetes.egg-info/not-zip-safe +0 -0
  38. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/apache_airflow_providers_cncf_kubernetes.egg-info/requires.txt +0 -0
  39. {apache-airflow-providers-cncf-kubernetes-7.0.0 → apache-airflow-providers-cncf-kubernetes-7.1.0}/apache_airflow_providers_cncf_kubernetes.egg-info/top_level.txt +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: apache-airflow-providers-cncf-kubernetes
3
- Version: 7.0.0
3
+ Version: 7.1.0
4
4
  Summary: Provider for Apache Airflow. Implements apache-airflow-providers-cncf-kubernetes package
5
5
  Home-page: https://airflow.apache.org/
6
6
  Download-URL: https://archive.apache.org/dist/airflow/providers
7
7
  Author: Apache Software Foundation
8
8
  Author-email: dev@airflow.apache.org
9
9
  License: Apache License 2.0
10
- Project-URL: Documentation, https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/7.0.0/
10
+ Project-URL: Documentation, https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/7.1.0/
11
11
  Project-URL: Bug Tracker, https://github.com/apache/airflow/issues
12
12
  Project-URL: Source Code, https://github.com/apache/airflow
13
13
  Project-URL: Slack Chat, https://s.apache.org/airflow-slack
@@ -21,12 +21,12 @@ Classifier: Intended Audience :: System Administrators
21
21
  Classifier: Framework :: Apache Airflow
22
22
  Classifier: Framework :: Apache Airflow :: Provider
23
23
  Classifier: License :: OSI Approved :: Apache Software License
24
- Classifier: Programming Language :: Python :: 3.7
25
24
  Classifier: Programming Language :: Python :: 3.8
26
25
  Classifier: Programming Language :: Python :: 3.9
27
26
  Classifier: Programming Language :: Python :: 3.10
27
+ Classifier: Programming Language :: Python :: 3.11
28
28
  Classifier: Topic :: System :: Monitoring
29
- Requires-Python: ~=3.7
29
+ Requires-Python: ~=3.8
30
30
  Description-Content-Type: text/x-rst
31
31
  License-File: LICENSE
32
32
  License-File: NOTICE
@@ -52,7 +52,7 @@ License-File: NOTICE
52
52
 
53
53
  Package ``apache-airflow-providers-cncf-kubernetes``
54
54
 
55
- Release: ``7.0.0``
55
+ Release: ``7.1.0``
56
56
 
57
57
 
58
58
  `Kubernetes <https://kubernetes.io/>`__
@@ -65,7 +65,7 @@ This is a provider package for ``cncf.kubernetes`` provider. All classes for thi
65
65
  are in ``airflow.providers.cncf.kubernetes`` python package.
66
66
 
67
67
  You can find package information and changelog for the provider
68
- in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/7.0.0/>`_.
68
+ in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/7.1.0/>`_.
69
69
 
70
70
 
71
71
  Installation
@@ -75,7 +75,7 @@ You can install this package on top of an existing Airflow 2 installation (see `
75
75
  for the minimum Airflow version supported) via
76
76
  ``pip install apache-airflow-providers-cncf-kubernetes``
77
77
 
78
- The package supports the following python versions: 3.7,3.8,3.9,3.10
78
+ The package supports the following python versions: 3.8,3.9,3.10,3.11
79
79
 
80
80
  Requirements
81
81
  ------------
@@ -116,7 +116,37 @@ PIP package Version required
116
116
  Changelog
117
117
  ---------
118
118
 
119
+ 7.1.0
120
+ .....
121
+
122
+ .. note::
123
+ This release dropped support for Python 3.7
124
+
125
+
126
+ Features
127
+ ~~~~~~~~
128
+ * ``KubernetesResourceOperator - KubernetesDeleteResourceOperator & KubernetesCreateResourceOperator (#29930)``
129
+ * ``add a return when the event is yielded in a loop to stop the execution (#31985)``
130
+ * ``Add possibility to disable logging the pod template in a case when task fails (#31595)``
131
+
132
+
133
+ Bug Fixes
134
+ ~~~~~~~~~
135
+
136
+ * ``Remove return statement after yield from triggers class (#31703)``
137
+ * ``Fix Fargate logging for AWS system tests (#31622)``
119
138
 
139
+ Misc
140
+ ~~~~
141
+
142
+ * ``Remove Python 3.7 support (#30963)``
143
+
144
+ .. Below changes are excluded from the changelog. Move them to
145
+ appropriate section above if needed. Do not delete the lines(!):
146
+ * ``Add D400 pydocstyle check (#31742)``
147
+ * ``Add discoverability for triggers in provider.yaml (#31576)``
148
+ * ``Add D400 pydocstyle check - Providers (#31427)``
149
+ * ``Add note about dropping Python 3.7 for providers (#32015)``
120
150
 
121
151
  7.0.0
122
152
  .....
@@ -19,7 +19,7 @@
19
19
 
20
20
  Package ``apache-airflow-providers-cncf-kubernetes``
21
21
 
22
- Release: ``7.0.0``
22
+ Release: ``7.1.0``
23
23
 
24
24
 
25
25
  `Kubernetes <https://kubernetes.io/>`__
@@ -32,7 +32,7 @@ This is a provider package for ``cncf.kubernetes`` provider. All classes for thi
32
32
  are in ``airflow.providers.cncf.kubernetes`` python package.
33
33
 
34
34
  You can find package information and changelog for the provider
35
- in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/7.0.0/>`_.
35
+ in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/7.1.0/>`_.
36
36
 
37
37
 
38
38
  Installation
@@ -42,7 +42,7 @@ You can install this package on top of an existing Airflow 2 installation (see `
42
42
  for the minimum Airflow version supported) via
43
43
  ``pip install apache-airflow-providers-cncf-kubernetes``
44
44
 
45
- The package supports the following python versions: 3.7,3.8,3.9,3.10
45
+ The package supports the following python versions: 3.8,3.9,3.10,3.11
46
46
 
47
47
  Requirements
48
48
  ------------
@@ -83,7 +83,37 @@ PIP package Version required
83
83
  Changelog
84
84
  ---------
85
85
 
86
+ 7.1.0
87
+ .....
88
+
89
+ .. note::
90
+ This release dropped support for Python 3.7
91
+
92
+
93
+ Features
94
+ ~~~~~~~~
95
+ * ``KubernetesResourceOperator - KubernetesDeleteResourceOperator & KubernetesCreateResourceOperator (#29930)``
96
+ * ``add a return when the event is yielded in a loop to stop the execution (#31985)``
97
+ * ``Add possibility to disable logging the pod template in a case when task fails (#31595)``
98
+
99
+
100
+ Bug Fixes
101
+ ~~~~~~~~~
102
+
103
+ * ``Remove return statement after yield from triggers class (#31703)``
104
+ * ``Fix Fargate logging for AWS system tests (#31622)``
86
105
 
106
+ Misc
107
+ ~~~~
108
+
109
+ * ``Remove Python 3.7 support (#30963)``
110
+
111
+ .. Below changes are excluded from the changelog. Move them to
112
+ appropriate section above if needed. Do not delete the lines(!):
113
+ * ``Add D400 pydocstyle check (#31742)``
114
+ * ``Add discoverability for triggers in provider.yaml (#31576)``
115
+ * ``Add D400 pydocstyle check - Providers (#31427)``
116
+ * ``Add note about dropping Python 3.7 for providers (#32015)``
87
117
 
88
118
  7.0.0
89
119
  .....
@@ -28,7 +28,7 @@ import packaging.version
28
28
 
29
29
  __all__ = ["__version__"]
30
30
 
31
- __version__ = "7.0.0"
31
+ __version__ = "7.1.0"
32
32
 
33
33
  try:
34
34
  from airflow import __version__ as airflow_version
@@ -14,7 +14,7 @@
14
14
  # KIND, either express or implied. See the License for the
15
15
  # specific language governing permissions and limitations
16
16
  # under the License.
17
- """Executes task in a Kubernetes POD"""
17
+ """Executes task in a Kubernetes POD."""
18
18
  from __future__ import annotations
19
19
 
20
20
  from kubernetes.client import ApiClient, models as k8s
@@ -43,7 +43,7 @@ def _convert_from_dict(obj, new_class):
43
43
 
44
44
 
45
45
  def convert_volume(volume) -> k8s.V1Volume:
46
- """Converts an airflow Volume object into a k8s.V1Volume
46
+ """Converts an airflow Volume object into a k8s.V1Volume.
47
47
 
48
48
  :param volume:
49
49
  """
@@ -51,7 +51,7 @@ def convert_volume(volume) -> k8s.V1Volume:
51
51
 
52
52
 
53
53
  def convert_volume_mount(volume_mount) -> k8s.V1VolumeMount:
54
- """Converts an airflow VolumeMount object into a k8s.V1VolumeMount
54
+ """Converts an airflow VolumeMount object into a k8s.V1VolumeMount.
55
55
 
56
56
  :param volume_mount:
57
57
  """
@@ -59,7 +59,7 @@ def convert_volume_mount(volume_mount) -> k8s.V1VolumeMount:
59
59
 
60
60
 
61
61
  def convert_port(port) -> k8s.V1ContainerPort:
62
- """Converts an airflow Port object into a k8s.V1ContainerPort
62
+ """Converts an airflow Port object into a k8s.V1ContainerPort.
63
63
 
64
64
  :param port:
65
65
  """
@@ -67,7 +67,7 @@ def convert_port(port) -> k8s.V1ContainerPort:
67
67
 
68
68
 
69
69
  def convert_env_vars(env_vars) -> list[k8s.V1EnvVar]:
70
- """Converts a dictionary into a list of env_vars
70
+ """Converts a dictionary into a list of env_vars.
71
71
 
72
72
  :param env_vars:
73
73
  """
@@ -83,7 +83,7 @@ def convert_env_vars(env_vars) -> list[k8s.V1EnvVar]:
83
83
 
84
84
 
85
85
  def convert_pod_runtime_info_env(pod_runtime_info_envs) -> k8s.V1EnvVar:
86
- """Converts a PodRuntimeInfoEnv into an k8s.V1EnvVar
86
+ """Converts a PodRuntimeInfoEnv into an k8s.V1EnvVar.
87
87
 
88
88
  :param pod_runtime_info_envs:
89
89
  """
@@ -91,7 +91,7 @@ def convert_pod_runtime_info_env(pod_runtime_info_envs) -> k8s.V1EnvVar:
91
91
 
92
92
 
93
93
  def convert_image_pull_secrets(image_pull_secrets) -> list[k8s.V1LocalObjectReference]:
94
- """Converts a PodRuntimeInfoEnv into an k8s.V1EnvVar
94
+ """Converts a PodRuntimeInfoEnv into an k8s.V1EnvVar.
95
95
 
96
96
  :param image_pull_secrets:
97
97
  """
@@ -103,7 +103,7 @@ def convert_image_pull_secrets(image_pull_secrets) -> list[k8s.V1LocalObjectRefe
103
103
 
104
104
 
105
105
  def convert_configmap(configmaps) -> k8s.V1EnvFromSource:
106
- """Converts a str into an k8s.V1EnvFromSource
106
+ """Converts a str into an k8s.V1EnvFromSource.
107
107
 
108
108
  :param configmaps:
109
109
  """
@@ -111,10 +111,10 @@ def convert_configmap(configmaps) -> k8s.V1EnvFromSource:
111
111
 
112
112
 
113
113
  def convert_affinity(affinity) -> k8s.V1Affinity:
114
- """Converts a dict into an k8s.V1Affinity"""
114
+ """Converts a dict into an k8s.V1Affinity."""
115
115
  return _convert_from_dict(affinity, k8s.V1Affinity)
116
116
 
117
117
 
118
118
  def convert_toleration(toleration) -> k8s.V1Toleration:
119
- """Converts a dict into an k8s.V1Toleration"""
119
+ """Converts a dict into an k8s.V1Toleration."""
120
120
  return _convert_from_dict(toleration, k8s.V1Toleration)
@@ -29,6 +29,7 @@ def get_provider_info():
29
29
  "description": "`Kubernetes <https://kubernetes.io/>`__\n",
30
30
  "suspended": False,
31
31
  "versions": [
32
+ "7.1.0",
32
33
  "7.0.0",
33
34
  "6.1.0",
34
35
  "6.0.0",
@@ -93,6 +94,7 @@ def get_provider_info():
93
94
  "airflow.providers.cncf.kubernetes.operators.kubernetes_pod",
94
95
  "airflow.providers.cncf.kubernetes.operators.pod",
95
96
  "airflow.providers.cncf.kubernetes.operators.spark_kubernetes",
97
+ "airflow.providers.cncf.kubernetes.operators.resource",
96
98
  ],
97
99
  }
98
100
  ],
@@ -111,7 +113,7 @@ def get_provider_info():
111
113
  "triggers": [
112
114
  {
113
115
  "integration-name": "Kubernetes",
114
- "class-names": ["airflow.providers.cncf.kubernetes.triggers.pod.KubernetesPodTrigger"],
116
+ "python-modules": ["airflow.providers.cncf.kubernetes.triggers.pod"],
115
117
  }
116
118
  ],
117
119
  "connection-types": [
@@ -18,6 +18,7 @@ from __future__ import annotations
18
18
 
19
19
  import contextlib
20
20
  import tempfile
21
+ from functools import cached_property
21
22
  from typing import TYPE_CHECKING, Any, Generator
22
23
 
23
24
  from asgiref.sync import sync_to_async
@@ -27,7 +28,6 @@ from kubernetes.config import ConfigException
27
28
  from kubernetes_asyncio import client as async_client, config as async_config
28
29
  from urllib3.exceptions import HTTPError
29
30
 
30
- from airflow.compat.functools import cached_property
31
31
  from airflow.exceptions import AirflowException, AirflowNotFoundException
32
32
  from airflow.hooks.base import BaseHook
33
33
  from airflow.kubernetes.kube_client import _disable_verify_ssl, _enable_tcp_keepalive
@@ -84,7 +84,7 @@ class KubernetesHook(BaseHook, PodOperatorHookProtocol):
84
84
 
85
85
  @staticmethod
86
86
  def get_connection_form_widgets() -> dict[str, Any]:
87
- """Returns connection widgets to add to connection form"""
87
+ """Returns connection widgets to add to connection form."""
88
88
  from flask_appbuilder.fieldwidgets import BS3TextFieldWidget
89
89
  from flask_babel import lazy_gettext
90
90
  from wtforms import BooleanField, StringField
@@ -103,7 +103,7 @@ class KubernetesHook(BaseHook, PodOperatorHookProtocol):
103
103
 
104
104
  @staticmethod
105
105
  def get_ui_field_behaviour() -> dict[str, Any]:
106
- """Returns custom field behaviour"""
106
+ """Returns custom field behaviour."""
107
107
  return {
108
108
  "hidden_fields": ["host", "schema", "login", "password", "port", "extra"],
109
109
  "relabeling": {},
@@ -177,7 +177,7 @@ class KubernetesHook(BaseHook, PodOperatorHookProtocol):
177
177
  return self.conn_extras.get(prefixed_name) or None
178
178
 
179
179
  def get_conn(self) -> client.ApiClient:
180
- """Returns kubernetes api session for use with requests"""
180
+ """Returns kubernetes api session for use with requests."""
181
181
  in_cluster = self._coalesce_param(self.in_cluster, self._get_field("in_cluster"))
182
182
  cluster_context = self._coalesce_param(self.cluster_context, self._get_field("cluster_context"))
183
183
  kubeconfig_path = self._coalesce_param(self.config_file, self._get_field("kube_config_path"))
@@ -253,7 +253,7 @@ class KubernetesHook(BaseHook, PodOperatorHookProtocol):
253
253
 
254
254
  @property
255
255
  def is_in_cluster(self) -> bool:
256
- """Expose whether the hook is configured with ``load_incluster_config`` or not"""
256
+ """Expose whether the hook is configured with ``load_incluster_config`` or not."""
257
257
  if self._is_in_cluster is not None:
258
258
  return self._is_in_cluster
259
259
  self.api_client # so we can determine if we are in_cluster or not
@@ -263,7 +263,7 @@ class KubernetesHook(BaseHook, PodOperatorHookProtocol):
263
263
 
264
264
  @cached_property
265
265
  def api_client(self) -> client.ApiClient:
266
- """Cached Kubernetes API client"""
266
+ """Cached Kubernetes API client."""
267
267
  return self.get_conn()
268
268
 
269
269
  @cached_property
@@ -278,7 +278,8 @@ class KubernetesHook(BaseHook, PodOperatorHookProtocol):
278
278
  self, group: str, version: str, plural: str, body: str | dict, namespace: str | None = None
279
279
  ):
280
280
  """
281
- Creates custom resource definition object in Kubernetes
281
+ Creates custom resource definition object in Kubernetes.
282
+
282
283
  :param group: api group
283
284
  :param version: api version
284
285
  :param plural: api plural
@@ -307,7 +308,7 @@ class KubernetesHook(BaseHook, PodOperatorHookProtocol):
307
308
  self, group: str, version: str, plural: str, name: str, namespace: str | None = None
308
309
  ):
309
310
  """
310
- Get custom resource definition object from Kubernetes
311
+ Get custom resource definition object from Kubernetes.
311
312
 
312
313
  :param group: api group
313
314
  :param version: api version
@@ -329,7 +330,7 @@ class KubernetesHook(BaseHook, PodOperatorHookProtocol):
329
330
  self, group: str, version: str, plural: str, name: str, namespace: str | None = None, **kwargs
330
331
  ):
331
332
  """
332
- Delete custom resource definition object from Kubernetes
333
+ Delete custom resource definition object from Kubernetes.
333
334
 
334
335
  :param group: api group
335
336
  :param version: api version
@@ -348,7 +349,7 @@ class KubernetesHook(BaseHook, PodOperatorHookProtocol):
348
349
  )
349
350
 
350
351
  def get_namespace(self) -> str | None:
351
- """Returns the namespace that defined in the connection"""
352
+ """Returns the namespace that defined in the connection."""
352
353
  if self.conn_id:
353
354
  return self._get_field("namespace")
354
355
  return None
@@ -412,7 +413,8 @@ class KubernetesHook(BaseHook, PodOperatorHookProtocol):
412
413
  **kwargs,
413
414
  ):
414
415
  """
415
- Retrieves a list of Kind pod which belong default kubernetes namespace
416
+ Retrieves a list of Kind pod which belong default kubernetes namespace.
417
+
416
418
  :param label_selector: A selector to restrict the list of returned objects by their labels
417
419
  :param namespace: kubernetes namespace
418
420
  :param watch: Watch for changes to the described resources and return them as a stream
@@ -449,7 +451,7 @@ class AsyncKubernetesHook(KubernetesHook):
449
451
  self._extras: dict | None = None
450
452
 
451
453
  async def _load_config(self):
452
- """Returns Kubernetes API session for use with requests"""
454
+ """Returns Kubernetes API session for use with requests."""
453
455
  in_cluster = self._coalesce_param(self.in_cluster, await self._get_field("in_cluster"))
454
456
  cluster_context = self._coalesce_param(self.cluster_context, await self._get_field("cluster_context"))
455
457
  kubeconfig_path = self._coalesce_param(self.config_file, await self._get_field("kube_config_path"))
@@ -14,7 +14,7 @@
14
14
  # KIND, either express or implied. See the License for the
15
15
  # specific language governing permissions and limitations
16
16
  # under the License.
17
- """Executes task in a Kubernetes POD"""
17
+ """Executes task in a Kubernetes POD."""
18
18
 
19
19
  from __future__ import annotations
20
20
 
@@ -25,13 +25,13 @@ import secrets
25
25
  import string
26
26
  from collections.abc import Container
27
27
  from contextlib import AbstractContextManager
28
+ from functools import cached_property
28
29
  from typing import TYPE_CHECKING, Any, Sequence
29
30
 
30
31
  from kubernetes.client import CoreV1Api, models as k8s
31
32
  from slugify import slugify
32
33
  from urllib3.exceptions import HTTPError
33
34
 
34
- from airflow.compat.functools import cached_property
35
35
  from airflow.exceptions import AirflowException, AirflowSkipException
36
36
  from airflow.kubernetes import pod_generator
37
37
  from airflow.kubernetes.pod_generator import PodGenerator
@@ -85,7 +85,7 @@ def _rand_str(num):
85
85
 
86
86
 
87
87
  def _add_pod_suffix(*, pod_name, rand_len=8, max_len=253):
88
- """Add random string to pod name while staying under max len
88
+ """Add random string to pod name while staying under max len.
89
89
 
90
90
  TODO: when min airflow version >= 2.5, delete this function and import from kubernetes_helper_functions.
91
91
 
@@ -135,7 +135,7 @@ class PodReattachFailure(AirflowException):
135
135
 
136
136
  class KubernetesPodOperator(BaseOperator):
137
137
  """
138
- Execute a task in a Kubernetes Pod
138
+ Execute a task in a Kubernetes Pod.
139
139
 
140
140
  .. seealso::
141
141
  For more information on how to use this operator, take a look at the guide:
@@ -225,6 +225,7 @@ class KubernetesPodOperator(BaseOperator):
225
225
  container name to use.
226
226
  :param deferrable: Run operator in the deferrable mode.
227
227
  :param poll_interval: Polling period in seconds to check for the status. Used only in deferrable mode.
228
+ :param log_pod_spec_on_failure: Log the pod's specification if a failure occurs
228
229
  """
229
230
 
230
231
  # This field can be overloaded at the instance level via base_container_name
@@ -301,6 +302,7 @@ class KubernetesPodOperator(BaseOperator):
301
302
  base_container_name: str | None = None,
302
303
  deferrable: bool = False,
303
304
  poll_interval: float = 2,
305
+ log_pod_spec_on_failure: bool = True,
304
306
  **kwargs,
305
307
  ) -> None:
306
308
  # TODO: remove in provider 6.0.0 release. This is a mitigate step to advise users to switch to the
@@ -381,6 +383,7 @@ class KubernetesPodOperator(BaseOperator):
381
383
  self.deferrable = deferrable
382
384
  self.poll_interval = poll_interval
383
385
  self.remote_pod: k8s.V1Pod | None = None
386
+ self.log_pod_spec_on_failure = log_pod_spec_on_failure
384
387
  self._config_dict: dict | None = None # TODO: remove it when removing convert_config_file_to_dict
385
388
 
386
389
  @cached_property
@@ -423,7 +426,7 @@ class KubernetesPodOperator(BaseOperator):
423
426
  @staticmethod
424
427
  def _get_ti_pod_labels(context: Context | None = None, include_try_number: bool = True) -> dict[str, str]:
425
428
  """
426
- Generate labels for the pod to track the pod in case of Operator crash
429
+ Generate labels for the pod to track the pod in case of Operator crash.
427
430
 
428
431
  :param context: task context provided by airflow DAG
429
432
  :return: dict
@@ -515,7 +518,7 @@ class KubernetesPodOperator(BaseOperator):
515
518
  raise
516
519
 
517
520
  def extract_xcom(self, pod: k8s.V1Pod):
518
- """Retrieves xcom value and kills xcom sidecar container"""
521
+ """Retrieves xcom value and kills xcom sidecar container."""
519
522
  result = self.pod_manager.extract_xcom(pod)
520
523
  if isinstance(result, str) and result.rstrip() == "__airflow_xcom_result_empty__":
521
524
  self.log.info("xcom result file is empty.")
@@ -525,7 +528,7 @@ class KubernetesPodOperator(BaseOperator):
525
528
  return json.loads(result)
526
529
 
527
530
  def execute(self, context: Context):
528
- """Based on the deferrable parameter runs the pod asynchronously or synchronously"""
531
+ """Based on the deferrable parameter runs the pod asynchronously or synchronously."""
529
532
  if self.deferrable:
530
533
  self.execute_async(context)
531
534
  else:
@@ -676,7 +679,6 @@ class KubernetesPodOperator(BaseOperator):
676
679
  self.process_pod_deletion(remote_pod, reraise=False)
677
680
 
678
681
  error_message = get_container_termination_message(remote_pod, self.base_container_name)
679
- error_message = "\n" + error_message if error_message else ""
680
682
  if self.skip_on_exit_code is not None:
681
683
  container_statuses = (
682
684
  remote_pod.status.container_statuses if remote_pod and remote_pod.status else None
@@ -697,14 +699,22 @@ class KubernetesPodOperator(BaseOperator):
697
699
  f"{self.skip_on_exit_code}. Skipping."
698
700
  )
699
701
  raise AirflowException(
700
- f"Pod {pod and pod.metadata.name} returned a failure:\n{error_message}\n"
701
- f"remote_pod: {remote_pod}"
702
+ "\n".join(
703
+ filter(
704
+ None,
705
+ [
706
+ f"Pod {pod and pod.metadata.name} returned a failure.",
707
+ error_message if isinstance(error_message, str) else None,
708
+ f"remote_pod: {remote_pod}" if self.log_pod_spec_on_failure else None,
709
+ ],
710
+ )
711
+ )
702
712
  )
703
713
  else:
704
714
  self.process_pod_deletion(remote_pod, reraise=False)
705
715
 
706
716
  def _read_pod_events(self, pod, *, reraise=True):
707
- """Will fetch and emit events from pod"""
717
+ """Will fetch and emit events from pod."""
708
718
  with _optionally_suppress(reraise=reraise):
709
719
  for event in self.pod_manager.read_pod_events(pod).items:
710
720
  self.log.error("Pod Event: %s - %s", event.reason, event.message)
@@ -735,7 +745,7 @@ class KubernetesPodOperator(BaseOperator):
735
745
  return None
736
746
 
737
747
  def patch_already_checked(self, pod: k8s.V1Pod, *, reraise=True):
738
- """Add an "already checked" annotation to ensure we don't reattach on retries"""
748
+ """Add an "already checked" annotation to ensure we don't reattach on retries."""
739
749
  with _optionally_suppress(reraise=reraise):
740
750
  self.client.patch_namespaced_pod(
741
751
  name=pod.metadata.name,
@@ -0,0 +1,103 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The ASF licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+ """Manage a Kubernetes Resource."""
18
+
19
+ from __future__ import annotations
20
+
21
+ from functools import cached_property
22
+
23
+ import yaml
24
+ from kubernetes.client import ApiClient
25
+ from kubernetes.utils import create_from_yaml
26
+
27
+ from airflow.models import BaseOperator
28
+ from airflow.providers.cncf.kubernetes.hooks.kubernetes import KubernetesHook
29
+ from airflow.providers.cncf.kubernetes.utils.delete_from import delete_from_yaml
30
+
31
+ __all__ = ["KubernetesCreateResourceOperator", "KubernetesDeleteResourceOperator"]
32
+
33
+
34
+ class KubernetesResourceBaseOperator(BaseOperator):
35
+ """
36
+ Abstract base class for all Kubernetes Resource operators.
37
+
38
+ :param yaml_conf: string. Contains the kubernetes resources to Create or Delete
39
+ :param namespace: string. Contains the namespace to create all resources inside.
40
+ The namespace must preexist otherwise the resource creation will fail.
41
+ If the API object in the yaml file already contains a namespace definition then
42
+ this parameter has no effect.
43
+ :param kubernetes_conn_id: The :ref:`kubernetes connection id <howto/connection:kubernetes>`
44
+ for the Kubernetes cluster.
45
+ :param in_cluster: run kubernetes client with in_cluster configuration.
46
+ :param cluster_context: context that points to kubernetes cluster.
47
+ Ignored when in_cluster is True. If None, current-context is used.
48
+ :param config_file: The path to the Kubernetes config file. (templated)
49
+ If not specified, default value is ``~/.kube/config``
50
+ """
51
+
52
+ template_fields = ("yaml_conf",)
53
+ template_fields_renderers = {"yaml_conf": "yaml"}
54
+
55
+ def __init__(
56
+ self,
57
+ *,
58
+ yaml_conf: str,
59
+ namespace: str | None = None,
60
+ kubernetes_conn_id: str | None = KubernetesHook.default_conn_name,
61
+ **kwargs,
62
+ ) -> None:
63
+ super().__init__(**kwargs)
64
+ self._namespace = namespace
65
+ self.kubernetes_conn_id = kubernetes_conn_id
66
+ self.yaml_conf = yaml_conf
67
+
68
+ @cached_property
69
+ def client(self) -> ApiClient:
70
+ return self.hook.api_client
71
+
72
+ @cached_property
73
+ def hook(self) -> KubernetesHook:
74
+ hook = KubernetesHook(conn_id=self.kubernetes_conn_id)
75
+ return hook
76
+
77
+ def get_namespace(self) -> str:
78
+ if self._namespace:
79
+ return self._namespace
80
+ else:
81
+ return self.hook.get_namespace() or "default"
82
+
83
+
84
+ class KubernetesCreateResourceOperator(KubernetesResourceBaseOperator):
85
+ """Create a resource in a kubernetes."""
86
+
87
+ def execute(self, context) -> None:
88
+ create_from_yaml(
89
+ k8s_client=self.client,
90
+ yaml_objects=yaml.safe_load_all(self.yaml_conf),
91
+ namespace=self.get_namespace(),
92
+ )
93
+
94
+
95
+ class KubernetesDeleteResourceOperator(KubernetesResourceBaseOperator):
96
+ """Delete a resource in a kubernetes."""
97
+
98
+ def execute(self, context) -> None:
99
+ delete_from_yaml(
100
+ k8s_client=self.client,
101
+ yaml_objects=yaml.safe_load_all(self.yaml_conf),
102
+ namespace=self.get_namespace(),
103
+ )
@@ -30,7 +30,7 @@ if TYPE_CHECKING:
30
30
 
31
31
  class SparkKubernetesOperator(BaseOperator):
32
32
  """
33
- Creates sparkApplication object in kubernetes cluster:
33
+ Creates sparkApplication object in kubernetes cluster.
34
34
 
35
35
  .. seealso::
36
36
  For more detail about Spark Application Object have a look at the reference:
@@ -15,7 +15,7 @@
15
15
  # KIND, either express or implied. See the License for the
16
16
  # specific language governing permissions and limitations
17
17
  # under the License.
18
- """Utilities for using the kubernetes decorator"""
18
+ """Utilities for using the kubernetes decorator."""
19
19
  from __future__ import annotations
20
20
 
21
21
  import os
@@ -39,7 +39,7 @@ def _balance_parens(after_decorator):
39
39
 
40
40
  def remove_task_decorator(python_source: str, task_decorator_name: str) -> str:
41
41
  """
42
- Removes @task.kubernetes or similar as well as @setup and @teardown
42
+ Removes @task.kubernetes or similar as well as @setup and @teardown.
43
43
 
44
44
  :param python_source: python source code
45
45
  :param task_decorator_name: the task decorator name
@@ -31,7 +31,7 @@ if TYPE_CHECKING:
31
31
 
32
32
  class SparkKubernetesSensor(BaseSensorOperator):
33
33
  """
34
- Checks sparkApplication object in kubernetes cluster:
34
+ Checks sparkApplication object in kubernetes cluster.
35
35
 
36
36
  .. seealso::
37
37
  For more detail about Spark Application Object have a look at the reference:
@@ -116,7 +116,7 @@ class KubernetesPodTrigger(BaseTrigger):
116
116
  )
117
117
 
118
118
  async def run(self) -> AsyncIterator[TriggerEvent]: # type: ignore[override]
119
- """Gets current pod status and yields a TriggerEvent"""
119
+ """Gets current pod status and yields a TriggerEvent."""
120
120
  hook = self._get_async_hook()
121
121
  self.log.info("Checking pod %r in namespace %r.", self.pod_name, self.pod_namespace)
122
122
  while True: