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.
- airflow/providers/cncf/kubernetes/LICENSE +0 -52
- airflow/providers/cncf/kubernetes/__init__.py +1 -1
- airflow/providers/cncf/kubernetes/backcompat/backwards_compat_converters.py +2 -3
- airflow/providers/cncf/kubernetes/callbacks.py +90 -8
- airflow/providers/cncf/kubernetes/cli/kubernetes_command.py +3 -4
- airflow/providers/cncf/kubernetes/decorators/kubernetes.py +10 -5
- airflow/providers/cncf/kubernetes/exceptions.py +29 -0
- airflow/providers/cncf/kubernetes/executors/kubernetes_executor.py +36 -113
- airflow/providers/cncf/kubernetes/executors/kubernetes_executor_utils.py +27 -15
- airflow/providers/cncf/kubernetes/get_provider_info.py +14 -21
- airflow/providers/cncf/kubernetes/hooks/kubernetes.py +20 -10
- airflow/providers/cncf/kubernetes/kube_config.py +0 -4
- airflow/providers/cncf/kubernetes/kubernetes_helper_functions.py +1 -1
- airflow/providers/cncf/kubernetes/operators/custom_object_launcher.py +3 -3
- airflow/providers/cncf/kubernetes/operators/job.py +4 -4
- airflow/providers/cncf/kubernetes/operators/kueue.py +2 -2
- airflow/providers/cncf/kubernetes/operators/pod.py +102 -44
- airflow/providers/cncf/kubernetes/operators/resource.py +1 -1
- airflow/providers/cncf/kubernetes/operators/spark_kubernetes.py +23 -19
- airflow/providers/cncf/kubernetes/pod_generator.py +51 -21
- airflow/providers/cncf/kubernetes/resource_convert/env_variable.py +1 -2
- airflow/providers/cncf/kubernetes/secret.py +1 -2
- airflow/providers/cncf/kubernetes/sensors/spark_kubernetes.py +1 -2
- airflow/providers/cncf/kubernetes/template_rendering.py +10 -2
- airflow/providers/cncf/kubernetes/utils/k8s_resource_iterator.py +1 -2
- airflow/providers/cncf/kubernetes/utils/pod_manager.py +12 -11
- {apache_airflow_providers_cncf_kubernetes-10.1.0rc2.dist-info → apache_airflow_providers_cncf_kubernetes-10.2.0.dist-info}/METADATA +10 -27
- {apache_airflow_providers_cncf_kubernetes-10.1.0rc2.dist-info → apache_airflow_providers_cncf_kubernetes-10.2.0.dist-info}/RECORD +30 -30
- airflow/providers/cncf/kubernetes/pod_generator_deprecated.py +0 -309
- {apache_airflow_providers_cncf_kubernetes-10.1.0rc2.dist-info → apache_airflow_providers_cncf_kubernetes-10.2.0.dist-info}/WHEEL +0 -0
- {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
|
|
File without changes
|