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,74 @@
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
+ # This file is used for documentation purposes. Example can be found at docs/executor/kubernetes.rst
19
+ #
20
+ # [START template_with_dags_in_volume]
21
+ ---
22
+ apiVersion: v1
23
+ kind: Pod
24
+ metadata:
25
+ name: placeholder-name
26
+ spec:
27
+ containers:
28
+ - env:
29
+ - name: AIRFLOW__CORE__EXECUTOR
30
+ value: LocalExecutor
31
+ # Hard Coded Airflow Envs
32
+ - name: AIRFLOW__CORE__FERNET_KEY
33
+ valueFrom:
34
+ secretKeyRef:
35
+ name: RELEASE-NAME-fernet-key
36
+ key: fernet-key
37
+ - name: AIRFLOW__DATABASE__SQL_ALCHEMY_CONN
38
+ valueFrom:
39
+ secretKeyRef:
40
+ name: RELEASE-NAME-airflow-metadata
41
+ key: connection
42
+ - name: AIRFLOW_CONN_AIRFLOW_DB
43
+ valueFrom:
44
+ secretKeyRef:
45
+ name: RELEASE-NAME-airflow-metadata
46
+ key: connection
47
+ image: dummy_image
48
+ imagePullPolicy: IfNotPresent
49
+ name: base
50
+ volumeMounts:
51
+ - mountPath: "/opt/airflow/logs"
52
+ name: airflow-logs
53
+ - mountPath: /opt/airflow/dags
54
+ name: airflow-dags
55
+ readOnly: true
56
+ - mountPath: /opt/airflow/airflow.cfg
57
+ name: airflow-config
58
+ readOnly: true
59
+ subPath: airflow.cfg
60
+ restartPolicy: Never
61
+ securityContext:
62
+ runAsUser: 50000
63
+ fsGroup: 50000
64
+ serviceAccountName: "RELEASE-NAME-worker-serviceaccount"
65
+ volumes:
66
+ - name: airflow-dags
67
+ persistentVolumeClaim:
68
+ claimName: RELEASE-NAME-dags
69
+ - emptyDir: {}
70
+ name: airflow-logs
71
+ - configMap:
72
+ name: RELEASE-NAME-airflow-config
73
+ name: airflow-config
74
+ # [END template_with_dags_in_volume]
@@ -0,0 +1,95 @@
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
+ # This file is used for documentation purposes. Example can be found at docs/executor/kubernetes.rst
19
+ #
20
+ # [START git_sync_template]
21
+ ---
22
+ apiVersion: v1
23
+ kind: Pod
24
+ metadata:
25
+ name: dummy-name
26
+ spec:
27
+ initContainers:
28
+ - name: git-sync
29
+ image: "registry.k8s.io/git-sync/git-sync:v3.6.3"
30
+ env:
31
+ - name: GIT_SYNC_BRANCH
32
+ value: "v2-2-stable"
33
+ - name: GIT_SYNC_REPO
34
+ value: "https://github.com/apache/airflow.git"
35
+ - name: GIT_SYNC_DEPTH
36
+ value: "1"
37
+ - name: GIT_SYNC_ROOT
38
+ value: "/git"
39
+ - name: GIT_SYNC_DEST
40
+ value: "repo"
41
+ - name: GIT_SYNC_ADD_USER
42
+ value: "true"
43
+ - name: GIT_SYNC_ONE_TIME
44
+ value: "true"
45
+ volumeMounts:
46
+ - name: airflow-dags
47
+ mountPath: /git
48
+ containers:
49
+ - env:
50
+ - name: AIRFLOW__CORE__EXECUTOR
51
+ value: LocalExecutor
52
+ # Hard Coded Airflow Envs
53
+ - name: AIRFLOW__CORE__FERNET_KEY
54
+ valueFrom:
55
+ secretKeyRef:
56
+ name: RELEASE-NAME-fernet-key
57
+ key: fernet-key
58
+ - name: AIRFLOW__DATABASE__SQL_ALCHEMY_CONN
59
+ valueFrom:
60
+ secretKeyRef:
61
+ name: RELEASE-NAME-airflow-metadata
62
+ key: connection
63
+ - name: AIRFLOW_CONN_AIRFLOW_DB
64
+ valueFrom:
65
+ secretKeyRef:
66
+ name: RELEASE-NAME-airflow-metadata
67
+ key: connection
68
+ image: dummy_image
69
+ imagePullPolicy: IfNotPresent
70
+ name: base
71
+ volumeMounts:
72
+ - mountPath: "/opt/airflow/logs"
73
+ name: airflow-logs
74
+ - mountPath: /opt/airflow/dags
75
+ name: airflow-dags
76
+ subPath: repo/airflow/example_dags
77
+ readOnly: false
78
+ - mountPath: /opt/airflow/airflow.cfg
79
+ name: airflow-config
80
+ readOnly: true
81
+ subPath: airflow.cfg
82
+ restartPolicy: Never
83
+ securityContext:
84
+ runAsUser: 50000
85
+ fsGroup: 50000
86
+ serviceAccountName: "RELEASE-NAME-worker-serviceaccount"
87
+ volumes:
88
+ - name: airflow-dags
89
+ emptyDir: {}
90
+ - name: airflow-logs
91
+ emptyDir: {}
92
+ - configMap:
93
+ name: RELEASE-NAME-airflow-config
94
+ name: airflow-config
95
+ # [END git_sync_template]
@@ -0,0 +1,51 @@
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.
18
+ -#}
19
+ from __future__ import annotations
20
+
21
+ import json
22
+ import {{ pickling_library }}
23
+ import sys
24
+
25
+ {# Check whether Airflow is available in the environment.
26
+ # If it is, we'll want to ensure that we integrate any macros that are being provided
27
+ # by plugins prior to unpickling the task context. #}
28
+ if sys.version_info >= (3,6):
29
+ try:
30
+ from airflow.plugins_manager import integrate_macros_plugins
31
+ integrate_macros_plugins()
32
+ except ImportError:
33
+ {# Airflow is not available in this environment, therefore we won't
34
+ # be able to integrate any plugin macros. #}
35
+ pass
36
+
37
+ {% if op_args or op_kwargs %}
38
+ with open(sys.argv[1], "rb") as file:
39
+ arg_dict = {{ pickling_library }}.load(file)
40
+ {% else %}
41
+ arg_dict = {"args": [], "kwargs": {}}
42
+ {% endif %}
43
+
44
+ # Script
45
+ {{ python_callable_source }}
46
+ res = {{ python_callable }}(*arg_dict["args"], **arg_dict["kwargs"])
47
+
48
+ # Write output
49
+ with open(sys.argv[2], "w") as file:
50
+ if res is not None:
51
+ file.write(json.dumps(res))
@@ -0,0 +1,92 @@
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.
18
+ """Utilities for using the kubernetes decorator."""
19
+
20
+ from __future__ import annotations
21
+
22
+ import os
23
+ from collections import deque
24
+
25
+ from jinja2 import Environment, FileSystemLoader, StrictUndefined, select_autoescape
26
+ from jinja2.nativetypes import NativeEnvironment
27
+
28
+
29
+ def _balance_parens(after_decorator):
30
+ num_paren = 1
31
+ after_decorator = deque(after_decorator)
32
+ after_decorator.popleft()
33
+ while num_paren:
34
+ current = after_decorator.popleft()
35
+ if current == "(":
36
+ num_paren += 1
37
+ elif current == ")":
38
+ num_paren -= 1
39
+ return "".join(after_decorator)
40
+
41
+
42
+ def remove_task_decorator(python_source: str, task_decorator_name: str) -> str:
43
+ """
44
+ Remove @task.kubernetes or similar as well as @setup and @teardown.
45
+
46
+ :param python_source: python source code
47
+ :param task_decorator_name: the task decorator name
48
+ """
49
+
50
+ def _remove_task_decorator(py_source, decorator_name):
51
+ if decorator_name not in py_source:
52
+ return python_source
53
+ split = python_source.split(decorator_name)
54
+ before_decorator, after_decorator = split[0], split[1]
55
+ if after_decorator[0] == "(":
56
+ after_decorator = _balance_parens(after_decorator)
57
+ if after_decorator[0] == "\n":
58
+ after_decorator = after_decorator[1:]
59
+ return before_decorator + after_decorator
60
+
61
+ decorators = ["@setup", "@teardown", task_decorator_name]
62
+ for decorator in decorators:
63
+ python_source = _remove_task_decorator(python_source, decorator)
64
+ return python_source
65
+
66
+
67
+ def write_python_script(
68
+ jinja_context: dict,
69
+ filename: str,
70
+ render_template_as_native_obj: bool = False,
71
+ ):
72
+ """
73
+ Render the python script to a file to execute in the virtual environment.
74
+
75
+ :param jinja_context: The jinja context variables to unpack and replace with its placeholders in the
76
+ template file.
77
+ :param filename: The name of the file to dump the rendered script to.
78
+ :param render_template_as_native_obj: If ``True``, rendered Jinja template would be converted
79
+ to a native Python object
80
+ """
81
+ template_loader = FileSystemLoader(searchpath=os.path.dirname(__file__))
82
+ template_env: Environment
83
+ if render_template_as_native_obj:
84
+ template_env = NativeEnvironment(loader=template_loader, undefined=StrictUndefined)
85
+ else:
86
+ template_env = Environment(
87
+ loader=template_loader,
88
+ undefined=StrictUndefined,
89
+ autoescape=select_autoescape(["html", "xml"]),
90
+ )
91
+ template = template_env.get_template("python_kubernetes_script.jinja2")
92
+ template.stream(**jinja_context).dump(filename)
@@ -0,0 +1,16 @@
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.
@@ -0,0 +1,52 @@
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 kubernetes.client import models as k8s
20
+
21
+
22
+ def convert_configmap(configmap_name) -> k8s.V1EnvFromSource:
23
+ """
24
+ Convert a str into an k8s object.
25
+
26
+ :param configmap_name: config map name
27
+ :return:
28
+ """
29
+ return k8s.V1EnvFromSource(config_map_ref=k8s.V1ConfigMapEnvSource(name=configmap_name))
30
+
31
+
32
+ def convert_configmap_to_volume(
33
+ configmap_info: dict[str, str],
34
+ ) -> tuple[list[k8s.V1Volume], list[k8s.V1VolumeMount]]:
35
+ """
36
+ Convert a dictionary of config_map_name and mount_path into k8s volume mount object and k8s volume.
37
+
38
+ :param configmap_info: a dictionary of {config_map_name: mount_path}
39
+ :return:
40
+ """
41
+ volume_mounts = []
42
+ volumes = []
43
+ for config_name, mount_path in configmap_info.items():
44
+ volume_mounts.append(k8s.V1VolumeMount(mount_path=mount_path, name=config_name))
45
+ volumes.append(
46
+ k8s.V1Volume(
47
+ name=config_name,
48
+ config_map=k8s.V1ConfigMapVolumeSource(name=config_name),
49
+ )
50
+ )
51
+
52
+ return volumes, volume_mounts
@@ -0,0 +1,39 @@
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 kubernetes.client import models as k8s
20
+
21
+ from airflow.exceptions import AirflowException
22
+
23
+
24
+ def convert_env_vars(env_vars) -> list[k8s.V1EnvVar]:
25
+ """
26
+ Convert a dictionary of key:value into a list of env_vars.
27
+
28
+ :param env_vars:
29
+ :return:
30
+ """
31
+ if isinstance(env_vars, dict):
32
+ res = []
33
+ for k, v in env_vars.items():
34
+ res.append(k8s.V1EnvVar(name=k, value=v))
35
+ return res
36
+ if isinstance(env_vars, list):
37
+ if all([isinstance(e, k8s.V1EnvVar) for e in env_vars]):
38
+ return env_vars
39
+ raise AirflowException(f"Expected dict or list of V1EnvVar, got {type(env_vars)}")
@@ -0,0 +1,40 @@
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 kubernetes.client import models as k8s
20
+
21
+
22
+ def convert_secret(secret_name: str) -> k8s.V1EnvFromSource:
23
+ """
24
+ Convert a str into an k8s object.
25
+
26
+ :param secret_name:
27
+ :return:
28
+ """
29
+ return k8s.V1EnvFromSource(secret_ref=k8s.V1SecretEnvSource(name=secret_name))
30
+
31
+
32
+ def convert_image_pull_secrets(image_pull_secrets: str) -> list[k8s.V1LocalObjectReference]:
33
+ """
34
+ Convert an image pull secret name into k8s local object reference.
35
+
36
+ :param image_pull_secrets: comma separated string that contains secrets
37
+ :return:
38
+ """
39
+ secrets = image_pull_secrets.split(",")
40
+ return [k8s.V1LocalObjectReference(name=secret) for secret in secrets]
@@ -0,0 +1,128 @@
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
+ """Classes for interacting with Kubernetes API."""
18
+
19
+ from __future__ import annotations
20
+
21
+ import copy
22
+ import uuid
23
+
24
+ from kubernetes.client import models as k8s
25
+
26
+ from airflow.exceptions import AirflowConfigException
27
+ from airflow.providers.cncf.kubernetes.k8s_model import K8SModel
28
+
29
+
30
+ class Secret(K8SModel):
31
+ """Defines Kubernetes Secret Volume."""
32
+
33
+ def __init__(self, deploy_type, deploy_target, secret, key=None, items=None):
34
+ """
35
+ Initialize a Kubernetes Secret Object.
36
+
37
+ Used to track requested secrets from the user.
38
+
39
+ :param deploy_type: The type of secret deploy in Kubernetes, either `env` or
40
+ `volume`
41
+ :param deploy_target: (Optional) The environment variable when
42
+ `deploy_type` `env` or file path when `deploy_type` `volume` where
43
+ expose secret. If `key` is not provided deploy target should be None.
44
+ :param secret: Name of the secrets object in Kubernetes
45
+ :param key: (Optional) Key of the secret within the Kubernetes Secret
46
+ if not provided in `deploy_type` `env` it will mount all secrets in object
47
+ :param items: (Optional) items that can be added to a volume secret for specifying projects of
48
+ secret keys to paths
49
+ https://kubernetes.io/docs/concepts/configuration/secret/#projection-of-secret-keys-to-specific-paths
50
+ """
51
+ if deploy_type not in ("env", "volume"):
52
+ raise AirflowConfigException("deploy_type must be env or volume")
53
+
54
+ self.deploy_type = deploy_type
55
+ self.deploy_target = deploy_target
56
+ self.items = items or []
57
+
58
+ if deploy_target is not None and deploy_type == "env":
59
+ # if deploying to env, capitalize the deploy target
60
+ self.deploy_target = deploy_target.upper()
61
+
62
+ if key is not None and deploy_target is None:
63
+ raise AirflowConfigException("If `key` is set, `deploy_target` should not be None")
64
+
65
+ self.secret = secret
66
+ self.key = key
67
+
68
+ def to_env_secret(self) -> k8s.V1EnvVar:
69
+ """Store es environment secret."""
70
+ return k8s.V1EnvVar(
71
+ name=self.deploy_target,
72
+ value_from=k8s.V1EnvVarSource(
73
+ secret_key_ref=k8s.V1SecretKeySelector(name=self.secret, key=self.key)
74
+ ),
75
+ )
76
+
77
+ def to_env_from_secret(self) -> k8s.V1EnvFromSource:
78
+ """Read from environment to secret."""
79
+ return k8s.V1EnvFromSource(secret_ref=k8s.V1SecretEnvSource(name=self.secret))
80
+
81
+ def to_volume_secret(self) -> tuple[k8s.V1Volume, k8s.V1VolumeMount]:
82
+ """Convert to volume secret."""
83
+ vol_id = f"secretvol{uuid.uuid4()}"
84
+ volume = k8s.V1Volume(name=vol_id, secret=k8s.V1SecretVolumeSource(secret_name=self.secret))
85
+ if self.items:
86
+ volume.secret.items = self.items
87
+ return (volume, k8s.V1VolumeMount(mount_path=self.deploy_target, name=vol_id, read_only=True))
88
+
89
+ def attach_to_pod(self, pod: k8s.V1Pod) -> k8s.V1Pod:
90
+ """Attach to pod."""
91
+ cp_pod = copy.deepcopy(pod)
92
+
93
+ if self.deploy_type == "volume":
94
+ volume, volume_mount = self.to_volume_secret()
95
+ if cp_pod.spec.volumes is None:
96
+ cp_pod.spec.volumes = []
97
+ cp_pod.spec.volumes.append(volume)
98
+ if cp_pod.spec.containers[0].volume_mounts is None:
99
+ cp_pod.spec.containers[0].volume_mounts = []
100
+ cp_pod.spec.containers[0].volume_mounts.append(volume_mount)
101
+
102
+ if self.deploy_type == "env" and self.key is not None:
103
+ env = self.to_env_secret()
104
+ if cp_pod.spec.containers[0].env is None:
105
+ cp_pod.spec.containers[0].env = []
106
+ cp_pod.spec.containers[0].env.append(env)
107
+
108
+ if self.deploy_type == "env" and self.key is None:
109
+ env_from = self.to_env_from_secret()
110
+ if cp_pod.spec.containers[0].env_from is None:
111
+ cp_pod.spec.containers[0].env_from = []
112
+ cp_pod.spec.containers[0].env_from.append(env_from)
113
+
114
+ return cp_pod
115
+
116
+ def __eq__(self, other):
117
+ return (
118
+ self.deploy_type == other.deploy_type
119
+ and self.deploy_target == other.deploy_target
120
+ and self.secret == other.secret
121
+ and self.key == other.key
122
+ )
123
+
124
+ def __hash__(self):
125
+ return hash((self.deploy_type, self.deploy_target, self.secret, self.key))
126
+
127
+ def __repr__(self):
128
+ return f"Secret({self.deploy_type}, {self.deploy_target}, {self.secret}, {self.key})"