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.
- airflow/providers/cncf/kubernetes/__init__.py +18 -23
- airflow/providers/cncf/kubernetes/backcompat/__init__.py +17 -0
- airflow/providers/cncf/kubernetes/backcompat/backwards_compat_converters.py +31 -49
- airflow/providers/cncf/kubernetes/callbacks.py +200 -0
- airflow/providers/cncf/kubernetes/cli/__init__.py +16 -0
- airflow/providers/cncf/kubernetes/cli/kubernetes_command.py +195 -0
- airflow/providers/cncf/kubernetes/decorators/kubernetes.py +163 -0
- airflow/providers/cncf/kubernetes/decorators/kubernetes_cmd.py +118 -0
- airflow/providers/cncf/kubernetes/exceptions.py +37 -0
- airflow/providers/cncf/kubernetes/executors/__init__.py +17 -0
- airflow/providers/cncf/kubernetes/executors/kubernetes_executor.py +831 -0
- airflow/providers/cncf/kubernetes/executors/kubernetes_executor_types.py +91 -0
- airflow/providers/cncf/kubernetes/executors/kubernetes_executor_utils.py +736 -0
- airflow/providers/cncf/kubernetes/executors/local_kubernetes_executor.py +306 -0
- airflow/providers/cncf/kubernetes/get_provider_info.py +249 -50
- airflow/providers/cncf/kubernetes/hooks/kubernetes.py +846 -112
- airflow/providers/cncf/kubernetes/k8s_model.py +62 -0
- airflow/providers/cncf/kubernetes/kube_client.py +156 -0
- airflow/providers/cncf/kubernetes/kube_config.py +125 -0
- airflow/providers/cncf/kubernetes/kubernetes_executor_templates/__init__.py +16 -0
- airflow/providers/cncf/kubernetes/kubernetes_executor_templates/basic_template.yaml +79 -0
- airflow/providers/cncf/kubernetes/kubernetes_helper_functions.py +165 -0
- airflow/providers/cncf/kubernetes/operators/custom_object_launcher.py +368 -0
- airflow/providers/cncf/kubernetes/operators/job.py +646 -0
- airflow/providers/cncf/kubernetes/operators/kueue.py +132 -0
- airflow/providers/cncf/kubernetes/operators/pod.py +1417 -0
- airflow/providers/cncf/kubernetes/operators/resource.py +191 -0
- airflow/providers/cncf/kubernetes/operators/spark_kubernetes.py +336 -35
- airflow/providers/cncf/kubernetes/pod_generator.py +592 -0
- airflow/providers/cncf/kubernetes/pod_template_file_examples/__init__.py +16 -0
- airflow/providers/cncf/kubernetes/pod_template_file_examples/dags_in_image_template.yaml +68 -0
- airflow/providers/cncf/kubernetes/pod_template_file_examples/dags_in_volume_template.yaml +74 -0
- airflow/providers/cncf/kubernetes/pod_template_file_examples/git_sync_template.yaml +95 -0
- airflow/providers/cncf/kubernetes/python_kubernetes_script.jinja2 +51 -0
- airflow/providers/cncf/kubernetes/python_kubernetes_script.py +92 -0
- airflow/providers/cncf/kubernetes/resource_convert/__init__.py +16 -0
- airflow/providers/cncf/kubernetes/resource_convert/configmap.py +52 -0
- airflow/providers/cncf/kubernetes/resource_convert/env_variable.py +39 -0
- airflow/providers/cncf/kubernetes/resource_convert/secret.py +40 -0
- airflow/providers/cncf/kubernetes/secret.py +128 -0
- airflow/providers/cncf/kubernetes/sensors/spark_kubernetes.py +30 -14
- airflow/providers/cncf/kubernetes/template_rendering.py +81 -0
- airflow/providers/cncf/kubernetes/triggers/__init__.py +16 -0
- airflow/providers/cncf/kubernetes/triggers/job.py +176 -0
- airflow/providers/cncf/kubernetes/triggers/pod.py +344 -0
- airflow/providers/cncf/kubernetes/utils/__init__.py +3 -0
- airflow/providers/cncf/kubernetes/utils/container.py +118 -0
- airflow/providers/cncf/kubernetes/utils/delete_from.py +154 -0
- airflow/providers/cncf/kubernetes/utils/k8s_resource_iterator.py +46 -0
- airflow/providers/cncf/kubernetes/utils/pod_manager.py +887 -152
- airflow/providers/cncf/kubernetes/utils/xcom_sidecar.py +25 -16
- airflow/providers/cncf/kubernetes/version_compat.py +38 -0
- apache_airflow_providers_cncf_kubernetes-10.10.0rc1.dist-info/METADATA +125 -0
- apache_airflow_providers_cncf_kubernetes-10.10.0rc1.dist-info/RECORD +62 -0
- {apache_airflow_providers_cncf_kubernetes-3.1.0.dist-info → apache_airflow_providers_cncf_kubernetes-10.10.0rc1.dist-info}/WHEEL +1 -2
- apache_airflow_providers_cncf_kubernetes-10.10.0rc1.dist-info/entry_points.txt +3 -0
- apache_airflow_providers_cncf_kubernetes-10.10.0rc1.dist-info/licenses/NOTICE +5 -0
- airflow/providers/cncf/kubernetes/backcompat/pod.py +0 -119
- airflow/providers/cncf/kubernetes/backcompat/pod_runtime_info_env.py +0 -56
- airflow/providers/cncf/kubernetes/backcompat/volume.py +0 -62
- airflow/providers/cncf/kubernetes/backcompat/volume_mount.py +0 -58
- airflow/providers/cncf/kubernetes/example_dags/example_kubernetes.py +0 -163
- airflow/providers/cncf/kubernetes/example_dags/example_spark_kubernetes.py +0 -66
- airflow/providers/cncf/kubernetes/example_dags/example_spark_kubernetes_spark_pi.yaml +0 -57
- airflow/providers/cncf/kubernetes/operators/kubernetes_pod.py +0 -622
- apache_airflow_providers_cncf_kubernetes-3.1.0.dist-info/METADATA +0 -452
- apache_airflow_providers_cncf_kubernetes-3.1.0.dist-info/NOTICE +0 -6
- apache_airflow_providers_cncf_kubernetes-3.1.0.dist-info/RECORD +0 -29
- apache_airflow_providers_cncf_kubernetes-3.1.0.dist-info/entry_points.txt +0 -3
- apache_airflow_providers_cncf_kubernetes-3.1.0.dist-info/top_level.txt +0 -1
- /airflow/providers/cncf/kubernetes/{example_dags → decorators}/__init__.py +0 -0
- {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})"
|