apache-airflow-providers-cncf-kubernetes 3.1.0__py3-none-any.whl → 10.10.0rc1__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.
Files changed (72) hide show
  1. airflow/providers/cncf/kubernetes/__init__.py +18 -23
  2. airflow/providers/cncf/kubernetes/backcompat/__init__.py +17 -0
  3. airflow/providers/cncf/kubernetes/backcompat/backwards_compat_converters.py +31 -49
  4. airflow/providers/cncf/kubernetes/callbacks.py +200 -0
  5. airflow/providers/cncf/kubernetes/cli/__init__.py +16 -0
  6. airflow/providers/cncf/kubernetes/cli/kubernetes_command.py +195 -0
  7. airflow/providers/cncf/kubernetes/decorators/kubernetes.py +163 -0
  8. airflow/providers/cncf/kubernetes/decorators/kubernetes_cmd.py +118 -0
  9. airflow/providers/cncf/kubernetes/exceptions.py +37 -0
  10. airflow/providers/cncf/kubernetes/executors/__init__.py +17 -0
  11. airflow/providers/cncf/kubernetes/executors/kubernetes_executor.py +831 -0
  12. airflow/providers/cncf/kubernetes/executors/kubernetes_executor_types.py +91 -0
  13. airflow/providers/cncf/kubernetes/executors/kubernetes_executor_utils.py +736 -0
  14. airflow/providers/cncf/kubernetes/executors/local_kubernetes_executor.py +306 -0
  15. airflow/providers/cncf/kubernetes/get_provider_info.py +249 -50
  16. airflow/providers/cncf/kubernetes/hooks/kubernetes.py +846 -112
  17. airflow/providers/cncf/kubernetes/k8s_model.py +62 -0
  18. airflow/providers/cncf/kubernetes/kube_client.py +156 -0
  19. airflow/providers/cncf/kubernetes/kube_config.py +125 -0
  20. airflow/providers/cncf/kubernetes/kubernetes_executor_templates/__init__.py +16 -0
  21. airflow/providers/cncf/kubernetes/kubernetes_executor_templates/basic_template.yaml +79 -0
  22. airflow/providers/cncf/kubernetes/kubernetes_helper_functions.py +165 -0
  23. airflow/providers/cncf/kubernetes/operators/custom_object_launcher.py +368 -0
  24. airflow/providers/cncf/kubernetes/operators/job.py +646 -0
  25. airflow/providers/cncf/kubernetes/operators/kueue.py +132 -0
  26. airflow/providers/cncf/kubernetes/operators/pod.py +1417 -0
  27. airflow/providers/cncf/kubernetes/operators/resource.py +191 -0
  28. airflow/providers/cncf/kubernetes/operators/spark_kubernetes.py +336 -35
  29. airflow/providers/cncf/kubernetes/pod_generator.py +592 -0
  30. airflow/providers/cncf/kubernetes/pod_template_file_examples/__init__.py +16 -0
  31. airflow/providers/cncf/kubernetes/pod_template_file_examples/dags_in_image_template.yaml +68 -0
  32. airflow/providers/cncf/kubernetes/pod_template_file_examples/dags_in_volume_template.yaml +74 -0
  33. airflow/providers/cncf/kubernetes/pod_template_file_examples/git_sync_template.yaml +95 -0
  34. airflow/providers/cncf/kubernetes/python_kubernetes_script.jinja2 +51 -0
  35. airflow/providers/cncf/kubernetes/python_kubernetes_script.py +92 -0
  36. airflow/providers/cncf/kubernetes/resource_convert/__init__.py +16 -0
  37. airflow/providers/cncf/kubernetes/resource_convert/configmap.py +52 -0
  38. airflow/providers/cncf/kubernetes/resource_convert/env_variable.py +39 -0
  39. airflow/providers/cncf/kubernetes/resource_convert/secret.py +40 -0
  40. airflow/providers/cncf/kubernetes/secret.py +128 -0
  41. airflow/providers/cncf/kubernetes/sensors/spark_kubernetes.py +30 -14
  42. airflow/providers/cncf/kubernetes/template_rendering.py +81 -0
  43. airflow/providers/cncf/kubernetes/triggers/__init__.py +16 -0
  44. airflow/providers/cncf/kubernetes/triggers/job.py +176 -0
  45. airflow/providers/cncf/kubernetes/triggers/pod.py +344 -0
  46. airflow/providers/cncf/kubernetes/utils/__init__.py +3 -0
  47. airflow/providers/cncf/kubernetes/utils/container.py +118 -0
  48. airflow/providers/cncf/kubernetes/utils/delete_from.py +154 -0
  49. airflow/providers/cncf/kubernetes/utils/k8s_resource_iterator.py +46 -0
  50. airflow/providers/cncf/kubernetes/utils/pod_manager.py +887 -152
  51. airflow/providers/cncf/kubernetes/utils/xcom_sidecar.py +25 -16
  52. airflow/providers/cncf/kubernetes/version_compat.py +38 -0
  53. apache_airflow_providers_cncf_kubernetes-10.10.0rc1.dist-info/METADATA +125 -0
  54. apache_airflow_providers_cncf_kubernetes-10.10.0rc1.dist-info/RECORD +62 -0
  55. {apache_airflow_providers_cncf_kubernetes-3.1.0.dist-info → apache_airflow_providers_cncf_kubernetes-10.10.0rc1.dist-info}/WHEEL +1 -2
  56. apache_airflow_providers_cncf_kubernetes-10.10.0rc1.dist-info/entry_points.txt +3 -0
  57. apache_airflow_providers_cncf_kubernetes-10.10.0rc1.dist-info/licenses/NOTICE +5 -0
  58. airflow/providers/cncf/kubernetes/backcompat/pod.py +0 -119
  59. airflow/providers/cncf/kubernetes/backcompat/pod_runtime_info_env.py +0 -56
  60. airflow/providers/cncf/kubernetes/backcompat/volume.py +0 -62
  61. airflow/providers/cncf/kubernetes/backcompat/volume_mount.py +0 -58
  62. airflow/providers/cncf/kubernetes/example_dags/example_kubernetes.py +0 -163
  63. airflow/providers/cncf/kubernetes/example_dags/example_spark_kubernetes.py +0 -66
  64. airflow/providers/cncf/kubernetes/example_dags/example_spark_kubernetes_spark_pi.yaml +0 -57
  65. airflow/providers/cncf/kubernetes/operators/kubernetes_pod.py +0 -622
  66. apache_airflow_providers_cncf_kubernetes-3.1.0.dist-info/METADATA +0 -452
  67. apache_airflow_providers_cncf_kubernetes-3.1.0.dist-info/NOTICE +0 -6
  68. apache_airflow_providers_cncf_kubernetes-3.1.0.dist-info/RECORD +0 -29
  69. apache_airflow_providers_cncf_kubernetes-3.1.0.dist-info/entry_points.txt +0 -3
  70. apache_airflow_providers_cncf_kubernetes-3.1.0.dist-info/top_level.txt +0 -1
  71. /airflow/providers/cncf/kubernetes/{example_dags → decorators}/__init__.py +0 -0
  72. {apache_airflow_providers_cncf_kubernetes-3.1.0.dist-info → apache_airflow_providers_cncf_kubernetes-10.10.0rc1.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,163 @@
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
+ from __future__ import annotations
18
+
19
+ import base64
20
+ import os
21
+ import pickle
22
+ from collections.abc import Callable, Sequence
23
+ from shlex import quote
24
+ from tempfile import TemporaryDirectory
25
+ from typing import TYPE_CHECKING
26
+
27
+ import dill
28
+ from kubernetes.client import models as k8s
29
+
30
+ from airflow.providers.cncf.kubernetes.operators.pod import KubernetesPodOperator
31
+ from airflow.providers.cncf.kubernetes.python_kubernetes_script import (
32
+ write_python_script,
33
+ )
34
+ from airflow.providers.common.compat.sdk import (
35
+ DecoratedOperator,
36
+ TaskDecorator,
37
+ task_decorator_factory,
38
+ )
39
+
40
+ if TYPE_CHECKING:
41
+ from airflow.utils.context import Context
42
+
43
+ _PYTHON_SCRIPT_ENV = "__PYTHON_SCRIPT"
44
+ _PYTHON_INPUT_ENV = "__PYTHON_INPUT"
45
+
46
+
47
+ def _generate_decoded_command(env_var: str, file: str) -> str:
48
+ return (
49
+ f'python -c "import base64, os;'
50
+ rf"x = base64.b64decode(os.environ[\"{env_var}\"]);"
51
+ rf'f = open(\"{file}\", \"wb\"); f.write(x); f.close()"'
52
+ )
53
+
54
+
55
+ def _read_file_contents(filename: str) -> str:
56
+ with open(filename, "rb") as script_file:
57
+ return base64.b64encode(script_file.read()).decode("utf-8")
58
+
59
+
60
+ class _KubernetesDecoratedOperator(DecoratedOperator, KubernetesPodOperator):
61
+ custom_operator_name = "@task.kubernetes"
62
+
63
+ # `cmds` and `arguments` are used internally by the operator
64
+ template_fields: Sequence[str] = tuple(
65
+ {"op_args", "op_kwargs", *KubernetesPodOperator.template_fields} - {"cmds", "arguments"}
66
+ )
67
+
68
+ # Since we won't mutate the arguments, we should just do the shallow copy
69
+ # there are some cases we can't deepcopy the objects (e.g protobuf).
70
+ shallow_copy_attrs: Sequence[str] = ("python_callable",)
71
+
72
+ def __init__(self, namespace: str | None = None, use_dill: bool = False, **kwargs) -> None:
73
+ self.use_dill = use_dill
74
+
75
+ # If the name was not provided, we generate operator name from the python_callable
76
+ # we also instruct operator to add a random suffix to avoid collisions by default
77
+ op_name = kwargs.pop("name", f"k8s-airflow-pod-{kwargs['python_callable'].__name__}")
78
+ random_name_suffix = kwargs.pop("random_name_suffix", True)
79
+ super().__init__(
80
+ namespace=namespace,
81
+ name=op_name,
82
+ random_name_suffix=random_name_suffix,
83
+ cmds=["placeholder-command"],
84
+ **kwargs,
85
+ )
86
+
87
+ def _generate_cmds(self) -> list[str]:
88
+ script_filename = "/tmp/script.py"
89
+ input_filename = "/tmp/script.in"
90
+ output_filename = "/airflow/xcom/return.json"
91
+
92
+ write_local_script_file_cmd = (
93
+ f"{_generate_decoded_command(quote(_PYTHON_SCRIPT_ENV), quote(script_filename))}"
94
+ )
95
+ write_local_input_file_cmd = (
96
+ f"{_generate_decoded_command(quote(_PYTHON_INPUT_ENV), quote(input_filename))}"
97
+ )
98
+ make_xcom_dir_cmd = "mkdir -p /airflow/xcom"
99
+ exec_python_cmd = f"python {script_filename} {input_filename} {output_filename}"
100
+ return [
101
+ "bash",
102
+ "-cx",
103
+ (
104
+ f"{write_local_script_file_cmd} && "
105
+ f"{write_local_input_file_cmd} && "
106
+ f"{make_xcom_dir_cmd} && "
107
+ f"{exec_python_cmd}"
108
+ ),
109
+ ]
110
+
111
+ def execute(self, context: Context):
112
+ with TemporaryDirectory(prefix="venv") as tmp_dir:
113
+ pickling_library = dill if self.use_dill else pickle
114
+ script_filename = os.path.join(tmp_dir, "script.py")
115
+ input_filename = os.path.join(tmp_dir, "script.in")
116
+
117
+ with open(input_filename, "wb") as file:
118
+ pickling_library.dump({"args": self.op_args, "kwargs": self.op_kwargs}, file)
119
+
120
+ py_source = self.get_python_source()
121
+ jinja_context = {
122
+ "op_args": self.op_args,
123
+ "op_kwargs": self.op_kwargs,
124
+ "pickling_library": pickling_library.__name__,
125
+ "python_callable": self.python_callable.__name__,
126
+ "python_callable_source": py_source,
127
+ "string_args_global": False,
128
+ }
129
+ write_python_script(jinja_context=jinja_context, filename=script_filename)
130
+
131
+ self.env_vars: list[k8s.V1EnvVar] = [
132
+ *self.env_vars,
133
+ k8s.V1EnvVar(name=_PYTHON_SCRIPT_ENV, value=_read_file_contents(script_filename)),
134
+ k8s.V1EnvVar(name=_PYTHON_INPUT_ENV, value=_read_file_contents(input_filename)),
135
+ ]
136
+
137
+ self.cmds = self._generate_cmds()
138
+ return super().execute(context)
139
+
140
+
141
+ def kubernetes_task(
142
+ python_callable: Callable | None = None,
143
+ multiple_outputs: bool | None = None,
144
+ **kwargs,
145
+ ) -> TaskDecorator:
146
+ """
147
+ Kubernetes operator decorator.
148
+
149
+ This wraps a function to be executed in K8s using KubernetesPodOperator.
150
+ Also accepts any argument that KubernetesPodOperator will via ``kwargs``. Can be
151
+ reused in a single DAG.
152
+
153
+ :param python_callable: Function to decorate
154
+ :param multiple_outputs: if set, function return value will be
155
+ unrolled to multiple XCom values. Dict will unroll to xcom values with
156
+ keys as XCom keys. Defaults to False.
157
+ """
158
+ return task_decorator_factory(
159
+ python_callable=python_callable,
160
+ multiple_outputs=multiple_outputs,
161
+ decorated_operator_class=_KubernetesDecoratedOperator,
162
+ **kwargs,
163
+ )
@@ -0,0 +1,118 @@
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
+ from __future__ import annotations
18
+
19
+ import warnings
20
+ from collections.abc import Callable, Sequence
21
+ from typing import TYPE_CHECKING
22
+
23
+ from airflow.providers.cncf.kubernetes.operators.pod import KubernetesPodOperator
24
+ from airflow.providers.common.compat.sdk import (
25
+ DecoratedOperator,
26
+ TaskDecorator,
27
+ context_merge,
28
+ task_decorator_factory,
29
+ )
30
+ from airflow.utils.operator_helpers import determine_kwargs
31
+
32
+ if TYPE_CHECKING:
33
+ from airflow.utils.context import Context
34
+
35
+
36
+ class _KubernetesCmdDecoratedOperator(DecoratedOperator, KubernetesPodOperator):
37
+ custom_operator_name = "@task.kubernetes_cmd"
38
+
39
+ template_fields: Sequence[str] = KubernetesPodOperator.template_fields
40
+ overwrite_rtif_after_execution: bool = True
41
+
42
+ def __init__(self, *, python_callable: Callable, args_only: bool = False, **kwargs) -> None:
43
+ self.args_only = args_only
44
+
45
+ cmds = kwargs.pop("cmds", None)
46
+ arguments = kwargs.pop("arguments", None)
47
+
48
+ if cmds is not None or arguments is not None:
49
+ warnings.warn(
50
+ f"The `cmds` and `arguments` are unused in {self.custom_operator_name} decorator. "
51
+ "You should return a list of commands or image entrypoint arguments with "
52
+ "args_only=True from the python_callable.",
53
+ UserWarning,
54
+ stacklevel=3,
55
+ )
56
+
57
+ # If the name was not provided, we generate operator name from the python_callable
58
+ # we also instruct operator to add a random suffix to avoid collisions by default
59
+ op_name = kwargs.pop("name", f"k8s-airflow-pod-{python_callable.__name__}")
60
+ random_name_suffix = kwargs.pop("random_name_suffix", True)
61
+
62
+ super().__init__(
63
+ python_callable=python_callable,
64
+ name=op_name,
65
+ random_name_suffix=random_name_suffix,
66
+ cmds=None,
67
+ arguments=None,
68
+ **kwargs,
69
+ )
70
+
71
+ def execute(self, context: Context):
72
+ generated = self._generate_cmds(context)
73
+ if self.args_only:
74
+ self.cmds = []
75
+ self.arguments = generated
76
+ else:
77
+ self.cmds = generated
78
+ self.arguments = []
79
+ context["ti"].render_templates() # type: ignore[attr-defined]
80
+ return super().execute(context)
81
+
82
+ def _generate_cmds(self, context: Context) -> list[str]:
83
+ context_merge(context, self.op_kwargs)
84
+ kwargs = determine_kwargs(self.python_callable, self.op_args, context)
85
+ generated_cmds = self.python_callable(*self.op_args, **kwargs)
86
+ func_name = self.python_callable.__name__
87
+ if not isinstance(generated_cmds, list):
88
+ raise TypeError(
89
+ f"Expected python_callable to return a list of strings, but got {type(generated_cmds)}"
90
+ )
91
+ if not all(isinstance(cmd, str) for cmd in generated_cmds):
92
+ raise TypeError(f"Expected {func_name} to return a list of strings, but got {generated_cmds}")
93
+ if not generated_cmds:
94
+ raise ValueError(f"The {func_name} returned an empty list of commands")
95
+
96
+ return generated_cmds
97
+
98
+
99
+ def kubernetes_cmd_task(
100
+ python_callable: Callable | None = None,
101
+ **kwargs,
102
+ ) -> TaskDecorator:
103
+ """
104
+ Kubernetes cmd operator decorator.
105
+
106
+ This wraps a function which should return command to be executed
107
+ in K8s using KubernetesPodOperator. The function should return a list of strings.
108
+ If args_only is set to True, the function should return a list of arguments for
109
+ container default command. Also accepts any argument that KubernetesPodOperator
110
+ will via ``kwargs``. Can be reused in a single DAG.
111
+
112
+ :param python_callable: Function to decorate
113
+ """
114
+ return task_decorator_factory(
115
+ python_callable=python_callable,
116
+ decorated_operator_class=_KubernetesCmdDecoratedOperator,
117
+ **kwargs,
118
+ )
@@ -0,0 +1,37 @@
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
+ from __future__ import annotations
18
+
19
+ from airflow.exceptions import (
20
+ AirflowException,
21
+ )
22
+
23
+
24
+ class PodMutationHookException(AirflowException):
25
+ """Raised when exception happens during Pod Mutation Hook execution."""
26
+
27
+
28
+ class PodReconciliationError(AirflowException):
29
+ """Raised when an error is encountered while trying to merge pod configs."""
30
+
31
+
32
+ class KubernetesApiError(AirflowException):
33
+ """Raised when an error is encountered while trying access Kubernetes API."""
34
+
35
+
36
+ class KubernetesApiPermissionError(AirflowException):
37
+ """Raised when an error is encountered while trying access Kubernetes API."""
@@ -0,0 +1,17 @@
1
+ #
2
+ # Licensed to the Apache Software Foundation (ASF) under one
3
+ # or more contributor license agreements. See the NOTICE file
4
+ # distributed with this work for additional information
5
+ # regarding copyright ownership. The ASF licenses this file
6
+ # to you under the Apache License, Version 2.0 (the
7
+ # "License"); you may not use this file except in compliance
8
+ # with the License. You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing,
13
+ # software distributed under the License is distributed on an
14
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ # KIND, either express or implied. See the License for the
16
+ # specific language governing permissions and limitations
17
+ # under the License.