apache-airflow-providers-cncf-kubernetes 10.1.0rc2__py3-none-any.whl → 10.2.0__py3-none-any.whl

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 (31) hide show
  1. airflow/providers/cncf/kubernetes/LICENSE +0 -52
  2. airflow/providers/cncf/kubernetes/__init__.py +1 -1
  3. airflow/providers/cncf/kubernetes/backcompat/backwards_compat_converters.py +2 -3
  4. airflow/providers/cncf/kubernetes/callbacks.py +90 -8
  5. airflow/providers/cncf/kubernetes/cli/kubernetes_command.py +3 -4
  6. airflow/providers/cncf/kubernetes/decorators/kubernetes.py +10 -5
  7. airflow/providers/cncf/kubernetes/exceptions.py +29 -0
  8. airflow/providers/cncf/kubernetes/executors/kubernetes_executor.py +36 -113
  9. airflow/providers/cncf/kubernetes/executors/kubernetes_executor_utils.py +27 -15
  10. airflow/providers/cncf/kubernetes/get_provider_info.py +14 -21
  11. airflow/providers/cncf/kubernetes/hooks/kubernetes.py +20 -10
  12. airflow/providers/cncf/kubernetes/kube_config.py +0 -4
  13. airflow/providers/cncf/kubernetes/kubernetes_helper_functions.py +1 -1
  14. airflow/providers/cncf/kubernetes/operators/custom_object_launcher.py +3 -3
  15. airflow/providers/cncf/kubernetes/operators/job.py +4 -4
  16. airflow/providers/cncf/kubernetes/operators/kueue.py +2 -2
  17. airflow/providers/cncf/kubernetes/operators/pod.py +102 -44
  18. airflow/providers/cncf/kubernetes/operators/resource.py +1 -1
  19. airflow/providers/cncf/kubernetes/operators/spark_kubernetes.py +23 -19
  20. airflow/providers/cncf/kubernetes/pod_generator.py +51 -21
  21. airflow/providers/cncf/kubernetes/resource_convert/env_variable.py +1 -2
  22. airflow/providers/cncf/kubernetes/secret.py +1 -2
  23. airflow/providers/cncf/kubernetes/sensors/spark_kubernetes.py +1 -2
  24. airflow/providers/cncf/kubernetes/template_rendering.py +10 -2
  25. airflow/providers/cncf/kubernetes/utils/k8s_resource_iterator.py +1 -2
  26. airflow/providers/cncf/kubernetes/utils/pod_manager.py +12 -11
  27. {apache_airflow_providers_cncf_kubernetes-10.1.0rc2.dist-info → apache_airflow_providers_cncf_kubernetes-10.2.0.dist-info}/METADATA +10 -27
  28. {apache_airflow_providers_cncf_kubernetes-10.1.0rc2.dist-info → apache_airflow_providers_cncf_kubernetes-10.2.0.dist-info}/RECORD +30 -30
  29. airflow/providers/cncf/kubernetes/pod_generator_deprecated.py +0 -309
  30. {apache_airflow_providers_cncf_kubernetes-10.1.0rc2.dist-info → apache_airflow_providers_cncf_kubernetes-10.2.0.dist-info}/WHEEL +0 -0
  31. {apache_airflow_providers_cncf_kubernetes-10.1.0rc2.dist-info → apache_airflow_providers_cncf_kubernetes-10.2.0.dist-info}/entry_points.txt +0 -0
@@ -1,309 +0,0 @@
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
- """
18
- Backwards compatibility for Pod generation.
19
-
20
- This module provides an interface between the previous Pod
21
- API and outputs a kubernetes.client.models.V1Pod.
22
- The advantage being that the full Kubernetes API
23
- is supported and no serialization need be written.
24
- """
25
-
26
- from __future__ import annotations
27
-
28
- import copy
29
- import uuid
30
-
31
- import re2
32
- from kubernetes.client import models as k8s
33
-
34
- MAX_POD_ID_LEN = 253
35
-
36
- MAX_LABEL_LEN = 63
37
-
38
-
39
- class PodDefaults:
40
- """Static defaults for Pods."""
41
-
42
- XCOM_MOUNT_PATH = "/airflow/xcom"
43
- SIDECAR_CONTAINER_NAME = "airflow-xcom-sidecar"
44
- XCOM_CMD = 'trap "exit 0" INT; while true; do sleep 30; done;'
45
- VOLUME_MOUNT = k8s.V1VolumeMount(name="xcom", mount_path=XCOM_MOUNT_PATH)
46
- VOLUME = k8s.V1Volume(name="xcom", empty_dir=k8s.V1EmptyDirVolumeSource())
47
- SIDECAR_CONTAINER = k8s.V1Container(
48
- name=SIDECAR_CONTAINER_NAME,
49
- command=["sh", "-c", XCOM_CMD],
50
- image="alpine",
51
- volume_mounts=[VOLUME_MOUNT],
52
- resources=k8s.V1ResourceRequirements(
53
- requests={
54
- "cpu": "1m",
55
- }
56
- ),
57
- )
58
-
59
-
60
- def make_safe_label_value(string):
61
- """
62
- Normalize a provided label to be of valid length and characters.
63
-
64
- Valid label values must be 63 characters or less and must be empty or begin and
65
- end with an alphanumeric character ([a-z0-9A-Z]) with dashes (-), underscores (_),
66
- dots (.), and alphanumerics between.
67
-
68
- If the label value is greater than 63 chars once made safe, or differs in any
69
- way from the original value sent to this function, then we need to truncate to
70
- 53 chars, and append it with a unique hash.
71
- """
72
- from airflow.utils.hashlib_wrapper import md5
73
-
74
- safe_label = re2.sub(r"^[^a-z0-9A-Z]*|[^a-zA-Z0-9_\-\.]|[^a-z0-9A-Z]*$", "", string)
75
-
76
- if len(safe_label) > MAX_LABEL_LEN or string != safe_label:
77
- safe_hash = md5(string.encode()).hexdigest()[:9]
78
- safe_label = safe_label[: MAX_LABEL_LEN - len(safe_hash) - 1] + "-" + safe_hash
79
-
80
- return safe_label
81
-
82
-
83
- class PodGenerator:
84
- """
85
- Contains Kubernetes Airflow Worker configuration logic.
86
-
87
- Represents a kubernetes pod and manages execution of a single pod.
88
- Any configuration that is container specific gets applied to
89
- the first container in the list of containers.
90
-
91
- :param image: The docker image
92
- :param name: name in the metadata section (not the container name)
93
- :param namespace: pod namespace
94
- :param volume_mounts: list of kubernetes volumes mounts
95
- :param envs: A dict containing the environment variables
96
- :param cmds: The command to be run on the first container
97
- :param args: The arguments to be run on the pod
98
- :param labels: labels for the pod metadata
99
- :param node_selectors: node selectors for the pod
100
- :param ports: list of ports. Applies to the first container.
101
- :param volumes: Volumes to be attached to the first container
102
- :param image_pull_policy: Specify a policy to cache or always pull an image
103
- :param restart_policy: The restart policy of the pod
104
- :param image_pull_secrets: Any image pull secrets to be given to the pod.
105
- If more than one secret is required, provide a comma separated list:
106
- secret_a,secret_b
107
- :param init_containers: A list of init containers
108
- :param service_account_name: Identity for processes that run in a Pod
109
- :param resources: Resource requirements for the first containers
110
- :param annotations: annotations for the pod
111
- :param affinity: A dict containing a group of affinity scheduling rules
112
- :param hostnetwork: If True enable host networking on the pod
113
- :param tolerations: A list of kubernetes tolerations
114
- :param security_context: A dict containing the security context for the pod
115
- :param configmaps: Any configmap refs to read ``configmaps`` for environments from.
116
- If more than one configmap is required, provide a comma separated list
117
- configmap_a,configmap_b
118
- :param dnspolicy: Specify a dnspolicy for the pod
119
- :param schedulername: Specify a schedulername for the pod
120
- :param pod: The fully specified pod. Mutually exclusive with `path_or_string`
121
- :param extract_xcom: Whether to bring up a container for xcom
122
- :param priority_class_name: priority class name for the launched Pod
123
- """
124
-
125
- def __init__(
126
- self,
127
- image: str | None = None,
128
- name: str | None = None,
129
- namespace: str | None = None,
130
- volume_mounts: list[k8s.V1VolumeMount | dict] | None = None,
131
- envs: dict[str, str] | None = None,
132
- cmds: list[str] | None = None,
133
- args: list[str] | None = None,
134
- labels: dict[str, str] | None = None,
135
- node_selectors: dict[str, str] | None = None,
136
- ports: list[k8s.V1ContainerPort | dict] | None = None,
137
- volumes: list[k8s.V1Volume | dict] | None = None,
138
- image_pull_policy: str | None = None,
139
- restart_policy: str | None = None,
140
- image_pull_secrets: str | None = None,
141
- init_containers: list[k8s.V1Container] | None = None,
142
- service_account_name: str | None = None,
143
- resources: k8s.V1ResourceRequirements | dict | None = None,
144
- annotations: dict[str, str] | None = None,
145
- affinity: dict | None = None,
146
- hostnetwork: bool = False,
147
- tolerations: list | None = None,
148
- security_context: k8s.V1PodSecurityContext | dict | None = None,
149
- configmaps: list[str] | None = None,
150
- dnspolicy: str | None = None,
151
- schedulername: str | None = None,
152
- extract_xcom: bool = False,
153
- priority_class_name: str | None = None,
154
- ):
155
- self.pod = k8s.V1Pod()
156
- self.pod.api_version = "v1"
157
- self.pod.kind = "Pod"
158
-
159
- # Pod Metadata
160
- self.metadata = k8s.V1ObjectMeta()
161
- self.metadata.labels = labels
162
- self.metadata.name = name
163
- self.metadata.namespace = namespace
164
- self.metadata.annotations = annotations
165
-
166
- # Pod Container
167
- self.container = k8s.V1Container(name="base")
168
- self.container.image = image
169
- self.container.env = []
170
-
171
- if envs:
172
- if isinstance(envs, dict):
173
- for key, val in envs.items():
174
- self.container.env.append(k8s.V1EnvVar(name=key, value=val))
175
- elif isinstance(envs, list):
176
- self.container.env.extend(envs)
177
-
178
- configmaps = configmaps or []
179
- self.container.env_from = []
180
- for configmap in configmaps:
181
- self.container.env_from.append(
182
- k8s.V1EnvFromSource(config_map_ref=k8s.V1ConfigMapEnvSource(name=configmap))
183
- )
184
-
185
- self.container.command = cmds or []
186
- self.container.args = args or []
187
- if image_pull_policy:
188
- self.container.image_pull_policy = image_pull_policy
189
- self.container.ports = ports or []
190
- self.container.resources = resources
191
- self.container.volume_mounts = volume_mounts or []
192
-
193
- # Pod Spec
194
- self.spec = k8s.V1PodSpec(containers=[])
195
- self.spec.security_context = security_context
196
- self.spec.tolerations = tolerations
197
- if dnspolicy:
198
- self.spec.dns_policy = dnspolicy
199
- self.spec.scheduler_name = schedulername
200
- self.spec.host_network = hostnetwork
201
- self.spec.affinity = affinity
202
- self.spec.service_account_name = service_account_name
203
- self.spec.init_containers = init_containers
204
- self.spec.volumes = volumes or []
205
- self.spec.node_selector = node_selectors
206
- if restart_policy:
207
- self.spec.restart_policy = restart_policy
208
- self.spec.priority_class_name = priority_class_name
209
-
210
- self.spec.image_pull_secrets = []
211
-
212
- if image_pull_secrets:
213
- for image_pull_secret in image_pull_secrets.split(","):
214
- self.spec.image_pull_secrets.append(k8s.V1LocalObjectReference(name=image_pull_secret))
215
-
216
- # Attach sidecar
217
- self.extract_xcom = extract_xcom
218
-
219
- def gen_pod(self) -> k8s.V1Pod:
220
- """Generate pod."""
221
- result = None
222
-
223
- if result is None:
224
- result = self.pod
225
- result.spec = self.spec
226
- result.metadata = self.metadata
227
- result.spec.containers = [self.container]
228
-
229
- result.metadata.name = self.make_unique_pod_id(result.metadata.name)
230
-
231
- if self.extract_xcom:
232
- result = self.add_sidecar(result)
233
-
234
- return result
235
-
236
- @staticmethod
237
- def add_sidecar(pod: k8s.V1Pod) -> k8s.V1Pod:
238
- """Add sidecar."""
239
- pod_cp = copy.deepcopy(pod)
240
- pod_cp.spec.volumes = pod.spec.volumes or []
241
- pod_cp.spec.volumes.insert(0, PodDefaults.VOLUME)
242
- pod_cp.spec.containers[0].volume_mounts = pod_cp.spec.containers[0].volume_mounts or []
243
- pod_cp.spec.containers[0].volume_mounts.insert(0, PodDefaults.VOLUME_MOUNT)
244
- pod_cp.spec.containers.append(PodDefaults.SIDECAR_CONTAINER)
245
-
246
- return pod_cp
247
-
248
- @staticmethod
249
- def from_obj(obj) -> k8s.V1Pod | None:
250
- """Convert to pod from obj."""
251
- if obj is None:
252
- return None
253
-
254
- if isinstance(obj, PodGenerator):
255
- return obj.gen_pod()
256
-
257
- if not isinstance(obj, dict):
258
- raise TypeError(
259
- "Cannot convert a non-dictionary or non-PodGenerator "
260
- "object into a KubernetesExecutorConfig"
261
- )
262
-
263
- # We do not want to extract constant here from ExecutorLoader because it is just
264
- # A name in dictionary rather than executor selection mechanism and it causes cyclic import
265
- namespaced = obj.get("KubernetesExecutor", {})
266
-
267
- if not namespaced:
268
- return None
269
-
270
- resources = namespaced.get("resources")
271
-
272
- if resources is None:
273
- requests = {
274
- "cpu": namespaced.get("request_cpu"),
275
- "memory": namespaced.get("request_memory"),
276
- "ephemeral-storage": namespaced.get("ephemeral-storage"),
277
- }
278
- limits = {
279
- "cpu": namespaced.get("limit_cpu"),
280
- "memory": namespaced.get("limit_memory"),
281
- "ephemeral-storage": namespaced.get("ephemeral-storage"),
282
- }
283
- all_resources = list(requests.values()) + list(limits.values())
284
- if all(r is None for r in all_resources):
285
- resources = None
286
- else:
287
- resources = k8s.V1ResourceRequirements(requests=requests, limits=limits)
288
- namespaced["resources"] = resources
289
- return PodGenerator(**namespaced).gen_pod()
290
-
291
- @staticmethod
292
- def make_unique_pod_id(dag_id):
293
- r"""
294
- Generate a unique Pod name.
295
-
296
- Kubernetes pod names must be <= 253 chars and must pass the following regex for
297
- validation
298
- ``^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$``
299
-
300
- :param dag_id: a dag_id with only alphanumeric characters
301
- :return: ``str`` valid Pod name of appropriate length
302
- """
303
- if not dag_id:
304
- return None
305
-
306
- safe_uuid = uuid.uuid4().hex
307
- safe_pod_id = dag_id[: MAX_POD_ID_LEN - len(safe_uuid) - 1] + "-" + safe_uuid
308
-
309
- return safe_pod_id