zenml-nightly 0.61.0.dev20240712__py3-none-any.whl → 0.62.0.dev20240727__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 (161) hide show
  1. README.md +2 -2
  2. RELEASE_NOTES.md +40 -0
  3. zenml/VERSION +1 -1
  4. zenml/__init__.py +2 -0
  5. zenml/cli/stack.py +114 -248
  6. zenml/cli/stack_components.py +5 -3
  7. zenml/config/pipeline_spec.py +2 -2
  8. zenml/config/step_configurations.py +3 -3
  9. zenml/constants.py +3 -0
  10. zenml/enums.py +16 -0
  11. zenml/integrations/__init__.py +1 -0
  12. zenml/integrations/azure/__init__.py +2 -2
  13. zenml/integrations/constants.py +1 -0
  14. zenml/integrations/databricks/__init__.py +52 -0
  15. zenml/integrations/databricks/flavors/__init__.py +30 -0
  16. zenml/integrations/databricks/flavors/databricks_model_deployer_flavor.py +118 -0
  17. zenml/integrations/databricks/flavors/databricks_orchestrator_flavor.py +147 -0
  18. zenml/integrations/databricks/model_deployers/__init__.py +20 -0
  19. zenml/integrations/databricks/model_deployers/databricks_model_deployer.py +249 -0
  20. zenml/integrations/databricks/orchestrators/__init__.py +20 -0
  21. zenml/integrations/databricks/orchestrators/databricks_orchestrator.py +497 -0
  22. zenml/integrations/databricks/orchestrators/databricks_orchestrator_entrypoint_config.py +97 -0
  23. zenml/integrations/databricks/services/__init__.py +19 -0
  24. zenml/integrations/databricks/services/databricks_deployment.py +407 -0
  25. zenml/integrations/databricks/utils/__init__.py +14 -0
  26. zenml/integrations/databricks/utils/databricks_utils.py +87 -0
  27. zenml/integrations/gcp/orchestrators/vertex_orchestrator.py +44 -28
  28. zenml/integrations/great_expectations/data_validators/ge_data_validator.py +12 -8
  29. zenml/integrations/huggingface/materializers/huggingface_datasets_materializer.py +88 -3
  30. zenml/integrations/huggingface/steps/accelerate_runner.py +1 -7
  31. zenml/integrations/kubernetes/__init__.py +3 -2
  32. zenml/integrations/kubernetes/flavors/__init__.py +8 -0
  33. zenml/integrations/kubernetes/flavors/kubernetes_step_operator_flavor.py +166 -0
  34. zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py +1 -13
  35. zenml/integrations/kubernetes/orchestrators/manifest_utils.py +22 -4
  36. zenml/integrations/kubernetes/pod_settings.py +4 -0
  37. zenml/integrations/kubernetes/step_operators/__init__.py +22 -0
  38. zenml/integrations/kubernetes/step_operators/kubernetes_step_operator.py +235 -0
  39. zenml/integrations/lightgbm/__init__.py +1 -0
  40. zenml/integrations/mlflow/__init__.py +1 -1
  41. zenml/integrations/mlflow/model_registries/mlflow_model_registry.py +6 -2
  42. zenml/integrations/mlflow/services/mlflow_deployment.py +1 -1
  43. zenml/integrations/skypilot_azure/__init__.py +1 -3
  44. zenml/integrations/skypilot_lambda/__init__.py +1 -1
  45. zenml/logging/step_logging.py +34 -35
  46. zenml/materializers/built_in_materializer.py +1 -1
  47. zenml/materializers/cloudpickle_materializer.py +1 -1
  48. zenml/model/model.py +1 -1
  49. zenml/models/v2/core/code_repository.py +2 -2
  50. zenml/models/v2/core/component.py +29 -0
  51. zenml/models/v2/core/server_settings.py +0 -20
  52. zenml/models/v2/misc/full_stack.py +32 -0
  53. zenml/models/v2/misc/stack_deployment.py +5 -0
  54. zenml/new/pipelines/run_utils.py +1 -1
  55. zenml/orchestrators/__init__.py +4 -0
  56. zenml/orchestrators/step_launcher.py +1 -0
  57. zenml/orchestrators/wheeled_orchestrator.py +147 -0
  58. zenml/service_connectors/service_connector_utils.py +408 -0
  59. zenml/stack_deployments/azure_stack_deployment.py +179 -0
  60. zenml/stack_deployments/gcp_stack_deployment.py +13 -4
  61. zenml/stack_deployments/stack_deployment.py +10 -0
  62. zenml/stack_deployments/utils.py +4 -0
  63. zenml/steps/base_step.py +7 -5
  64. zenml/utils/function_utils.py +1 -1
  65. zenml/utils/pipeline_docker_image_builder.py +8 -0
  66. zenml/utils/source_utils.py +4 -1
  67. zenml/zen_server/dashboard/assets/{404-DpJaNHKF.js → 404-B_YdvmwS.js} +1 -1
  68. zenml/zen_server/dashboard/assets/{@reactflow-DJfzkHO1.js → @reactflow-l_1hUr1S.js} +1 -1
  69. zenml/zen_server/dashboard/assets/{AwarenessChannel-BYDLT2xC.js → AwarenessChannel-CFg5iX4Z.js} +1 -1
  70. zenml/zen_server/dashboard/assets/{CodeSnippet-BkOuRmyq.js → CodeSnippet-Dvkx_82E.js} +1 -1
  71. zenml/zen_server/dashboard/assets/CollapsibleCard-opiuBHHc.js +1 -0
  72. zenml/zen_server/dashboard/assets/{Commands-ZvWR1BRs.js → Commands-DoN1xrEq.js} +1 -1
  73. zenml/zen_server/dashboard/assets/{CopyButton-DVwLkafa.js → CopyButton-Cr7xYEPb.js} +1 -1
  74. zenml/zen_server/dashboard/assets/{CsvVizualization-C2IiqX4I.js → CsvVizualization-Ck-nZ43m.js} +3 -3
  75. zenml/zen_server/dashboard/assets/{Error-CqX0VqW_.js → Error-kLtljEOM.js} +1 -1
  76. zenml/zen_server/dashboard/assets/{ExecutionStatus-BoLUXR9t.js → ExecutionStatus-DguLLgTK.js} +1 -1
  77. zenml/zen_server/dashboard/assets/{Helpbox-LFydyVwh.js → Helpbox-BXUMP21n.js} +1 -1
  78. zenml/zen_server/dashboard/assets/{Infobox-DnENC0sh.js → Infobox-DSt0O-dm.js} +1 -1
  79. zenml/zen_server/dashboard/assets/{InlineAvatar-CbJtYr0t.js → InlineAvatar-xsrsIGE-.js} +1 -1
  80. zenml/zen_server/dashboard/assets/Pagination-C6X-mifw.js +1 -0
  81. zenml/zen_server/dashboard/assets/{SetPassword-BYBdbQDo.js → SetPassword-BXGTWiwj.js} +1 -1
  82. zenml/zen_server/dashboard/assets/{SuccessStep-Nx743hll.js → SuccessStep-DZC60t0x.js} +1 -1
  83. zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-DF9gSzE0.js → UpdatePasswordSchemas-DGvwFWO1.js} +1 -1
  84. zenml/zen_server/dashboard/assets/{chevron-right-double-BiEMg7rd.js → chevron-right-double-CZBOf6JM.js} +1 -1
  85. zenml/zen_server/dashboard/assets/cloud-only-C_yFCAkP.js +1 -0
  86. zenml/zen_server/dashboard/assets/index-BczVOqUf.js +55 -0
  87. zenml/zen_server/dashboard/assets/index-EpMIKgrI.css +1 -0
  88. zenml/zen_server/dashboard/assets/{login-mutation-BUnVASxp.js → login-mutation-CrHrndTI.js} +1 -1
  89. zenml/zen_server/dashboard/assets/logs-D8k8BVFf.js +1 -0
  90. zenml/zen_server/dashboard/assets/{not-found-B4VnX8gK.js → not-found-DYa4pC-C.js} +1 -1
  91. zenml/zen_server/dashboard/assets/{package-CsUhPmou.js → package-B3fWP-Dh.js} +1 -1
  92. zenml/zen_server/dashboard/assets/page-1h_sD1jz.js +1 -0
  93. zenml/zen_server/dashboard/assets/{page-Sxn82W-5.js → page-1iL8aMqs.js} +1 -1
  94. zenml/zen_server/dashboard/assets/{page-DMOYZppS.js → page-2grKx_MY.js} +1 -1
  95. zenml/zen_server/dashboard/assets/page-5NCOHOsy.js +1 -0
  96. zenml/zen_server/dashboard/assets/{page-JyfeDUfu.js → page-8a4UMKXZ.js} +1 -1
  97. zenml/zen_server/dashboard/assets/{page-Bx6o0ARS.js → page-B6h3iaHJ.js} +1 -1
  98. zenml/zen_server/dashboard/assets/page-BDns21Iz.js +1 -0
  99. zenml/zen_server/dashboard/assets/{page-3efNCDeb.js → page-BhgCDInH.js} +2 -2
  100. zenml/zen_server/dashboard/assets/{page-DKlIdAe5.js → page-Bi-wtWiO.js} +2 -2
  101. zenml/zen_server/dashboard/assets/{page-7zTHbhhI.js → page-BkeAAYwp.js} +1 -1
  102. zenml/zen_server/dashboard/assets/{page-CRTJ0UuR.js → page-BkuQDIf-.js} +1 -1
  103. zenml/zen_server/dashboard/assets/page-BnaevhnB.js +1 -0
  104. zenml/zen_server/dashboard/assets/{page-BEs6jK71.js → page-Bq0YxkLV.js} +1 -1
  105. zenml/zen_server/dashboard/assets/page-Bs2F4eoD.js +2 -0
  106. zenml/zen_server/dashboard/assets/{page-CUZIGO-3.js → page-C6-UGEbH.js} +1 -1
  107. zenml/zen_server/dashboard/assets/{page-Xu8JEjSU.js → page-CCNRIt_f.js} +1 -1
  108. zenml/zen_server/dashboard/assets/{page-DvCvroOM.js → page-CHNxpz3n.js} +1 -1
  109. zenml/zen_server/dashboard/assets/{page-BpSqIf4B.js → page-DgorQFqi.js} +1 -1
  110. zenml/zen_server/dashboard/assets/page-K8ebxVIs.js +1 -0
  111. zenml/zen_server/dashboard/assets/{page-Cx67M0QT.js → page-MFQyIJd3.js} +1 -1
  112. zenml/zen_server/dashboard/assets/page-TgCF0P_U.js +1 -0
  113. zenml/zen_server/dashboard/assets/page-ZnCEe-eK.js +9 -0
  114. zenml/zen_server/dashboard/assets/{page-Dc_7KMQE.js → page-uA5prJGY.js} +1 -1
  115. zenml/zen_server/dashboard/assets/persist-D7HJNBWx.js +1 -0
  116. zenml/zen_server/dashboard/assets/plus-C8WOyCzt.js +1 -0
  117. zenml/zen_server/dashboard/assets/stack-detail-query-Cficsl6d.js +1 -0
  118. zenml/zen_server/dashboard/assets/update-server-settings-mutation-7d8xi1tS.js +1 -0
  119. zenml/zen_server/dashboard/assets/{url-DuQMeqYA.js → url-D7mAQGUM.js} +1 -1
  120. zenml/zen_server/dashboard/index.html +4 -4
  121. zenml/zen_server/dashboard_legacy/asset-manifest.json +4 -4
  122. zenml/zen_server/dashboard_legacy/index.html +1 -1
  123. zenml/zen_server/dashboard_legacy/{precache-manifest.c8c57fb0d2132b1d3c2119e776b7dfb3.js → precache-manifest.12246c7548e71e2c4438e496360de80c.js} +4 -4
  124. zenml/zen_server/dashboard_legacy/service-worker.js +1 -1
  125. zenml/zen_server/dashboard_legacy/static/js/main.3b27024b.chunk.js +2 -0
  126. zenml/zen_server/dashboard_legacy/static/js/{main.382439a7.chunk.js.map → main.3b27024b.chunk.js.map} +1 -1
  127. zenml/zen_server/deploy/helm/Chart.yaml +1 -1
  128. zenml/zen_server/deploy/helm/README.md +2 -2
  129. zenml/zen_server/rbac/utils.py +10 -2
  130. zenml/zen_server/routers/devices_endpoints.py +4 -1
  131. zenml/zen_server/routers/server_endpoints.py +29 -2
  132. zenml/zen_server/routers/service_connectors_endpoints.py +57 -0
  133. zenml/zen_server/routers/steps_endpoints.py +2 -1
  134. zenml/zen_stores/migrations/versions/0.62.0_release.py +23 -0
  135. zenml/zen_stores/migrations/versions/b4fca5241eea_migrate_onboarding_state.py +167 -0
  136. zenml/zen_stores/rest_zen_store.py +4 -0
  137. zenml/zen_stores/schemas/component_schemas.py +14 -0
  138. zenml/zen_stores/schemas/server_settings_schemas.py +23 -11
  139. zenml/zen_stores/sql_zen_store.py +151 -1
  140. {zenml_nightly-0.61.0.dev20240712.dist-info → zenml_nightly-0.62.0.dev20240727.dist-info}/METADATA +5 -5
  141. {zenml_nightly-0.61.0.dev20240712.dist-info → zenml_nightly-0.62.0.dev20240727.dist-info}/RECORD +144 -121
  142. zenml/zen_server/dashboard/assets/Pagination-DEbVUupy.js +0 -1
  143. zenml/zen_server/dashboard/assets/chevron-down-D_ZlKMqH.js +0 -1
  144. zenml/zen_server/dashboard/assets/cloud-only-DVbIeckv.js +0 -1
  145. zenml/zen_server/dashboard/assets/index-C_CrU4vI.js +0 -1
  146. zenml/zen_server/dashboard/assets/index-DK1ynKjA.js +0 -55
  147. zenml/zen_server/dashboard/assets/index-inApY3KQ.css +0 -1
  148. zenml/zen_server/dashboard/assets/page-C43QGHTt.js +0 -9
  149. zenml/zen_server/dashboard/assets/page-CR0OG7ss.js +0 -1
  150. zenml/zen_server/dashboard/assets/page-CaopxiU1.js +0 -1
  151. zenml/zen_server/dashboard/assets/page-D7Z399xy.js +0 -1
  152. zenml/zen_server/dashboard/assets/page-D93kd7Xj.js +0 -1
  153. zenml/zen_server/dashboard/assets/page-DMsSn3dv.js +0 -2
  154. zenml/zen_server/dashboard/assets/page-Hus2pr9T.js +0 -1
  155. zenml/zen_server/dashboard/assets/page-TKXERe16.js +0 -1
  156. zenml/zen_server/dashboard/assets/plus-DOeLmm7C.js +0 -1
  157. zenml/zen_server/dashboard/assets/update-server-settings-mutation-CR8e3Sir.js +0 -1
  158. zenml/zen_server/dashboard_legacy/static/js/main.382439a7.chunk.js +0 -2
  159. {zenml_nightly-0.61.0.dev20240712.dist-info → zenml_nightly-0.62.0.dev20240727.dist-info}/LICENSE +0 -0
  160. {zenml_nightly-0.61.0.dev20240712.dist-info → zenml_nightly-0.62.0.dev20240727.dist-info}/WHEEL +0 -0
  161. {zenml_nightly-0.61.0.dev20240712.dist-info → zenml_nightly-0.62.0.dev20240727.dist-info}/entry_points.txt +0 -0
@@ -1,4 +1,4 @@
1
- # Copyright (c) ZenML GmbH 2021. All Rights Reserved.
1
+ # Copyright (c) ZenML GmbH 2024. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -16,12 +16,21 @@
16
16
  import os
17
17
  from collections import defaultdict
18
18
  from tempfile import TemporaryDirectory, mkdtemp
19
- from typing import TYPE_CHECKING, Any, ClassVar, Dict, Tuple, Type, Union
19
+ from typing import (
20
+ TYPE_CHECKING,
21
+ Any,
22
+ ClassVar,
23
+ Dict,
24
+ Optional,
25
+ Tuple,
26
+ Type,
27
+ Union,
28
+ )
20
29
 
21
30
  from datasets import Dataset, load_from_disk
22
31
  from datasets.dataset_dict import DatasetDict
23
32
 
24
- from zenml.enums import ArtifactType
33
+ from zenml.enums import ArtifactType, VisualizationType
25
34
  from zenml.io import fileio
26
35
  from zenml.materializers.base_materializer import BaseMaterializer
27
36
  from zenml.materializers.pandas_materializer import PandasMaterializer
@@ -33,6 +42,31 @@ if TYPE_CHECKING:
33
42
  DEFAULT_DATASET_DIR = "hf_datasets"
34
43
 
35
44
 
45
+ def extract_repo_name(checksum_str: str) -> Optional[str]:
46
+ """Extracts the repo name from the checksum string.
47
+
48
+ An example of a checksum_str is:
49
+ "hf://datasets/nyu-mll/glue@bcdcba79d07bc864c1c254ccfcedcce55bcc9a8c/mrpc/train-00000-of-00001.parquet"
50
+ and the expected output is "nyu-mll/glue".
51
+
52
+ Args:
53
+ checksum_str: The checksum_str to extract the repo name from.
54
+
55
+ Returns:
56
+ str: The extracted repo name.
57
+ """
58
+ dataset = None
59
+ try:
60
+ parts = checksum_str.split("/")
61
+ if len(parts) >= 4:
62
+ # Case: nyu-mll/glue
63
+ dataset = f"{parts[3]}/{parts[4].split('@')[0]}"
64
+ except Exception: # pylint: disable=broad-except
65
+ pass
66
+
67
+ return dataset
68
+
69
+
36
70
  class HFDatasetMaterializer(BaseMaterializer):
37
71
  """Materializer to read data to and from huggingface datasets."""
38
72
 
@@ -103,3 +137,54 @@ class HFDatasetMaterializer(BaseMaterializer):
103
137
  metadata[key][dataset_name] = value
104
138
  return dict(metadata)
105
139
  raise ValueError(f"Unsupported type {type(ds)}")
140
+
141
+ def save_visualizations(
142
+ self, ds: Union[Dataset, DatasetDict]
143
+ ) -> Dict[str, VisualizationType]:
144
+ """Save visualizations for the dataset.
145
+
146
+ Args:
147
+ ds: The Dataset or DatasetDict to visualize.
148
+
149
+ Returns:
150
+ A dictionary mapping visualization paths to their types.
151
+
152
+ Raises:
153
+ ValueError: If the given object is not a `Dataset` or `DatasetDict`.
154
+ """
155
+ visualizations = {}
156
+
157
+ if isinstance(ds, Dataset):
158
+ datasets = {"default": ds}
159
+ elif isinstance(ds, DatasetDict):
160
+ datasets = ds
161
+ else:
162
+ raise ValueError(f"Unsupported type {type(ds)}")
163
+
164
+ for name, dataset in datasets.items():
165
+ # Generate a unique identifier for the dataset
166
+ if dataset.info.download_checksums:
167
+ dataset_id = extract_repo_name(
168
+ [x for x in dataset.info.download_checksums.keys()][0]
169
+ )
170
+ if dataset_id:
171
+ # Create the iframe HTML
172
+ html = f"""
173
+ <iframe
174
+ src="https://huggingface.co/datasets/{dataset_id}/embed/viewer"
175
+ frameborder="0"
176
+ width="100%"
177
+ height="560px"
178
+ ></iframe>
179
+ """
180
+
181
+ # Save the HTML to a file
182
+ visualization_path = os.path.join(
183
+ self.uri, f"{name}_viewer.html"
184
+ )
185
+ with fileio.open(visualization_path, "w") as f:
186
+ f.write(html)
187
+
188
+ visualizations[visualization_path] = VisualizationType.HTML
189
+
190
+ return visualizations
@@ -111,15 +111,9 @@ def run_with_accelerate(
111
111
  if isinstance(v, bool):
112
112
  if v:
113
113
  commands.append(f"--{k}")
114
- elif isinstance(v, str):
115
- commands += [f"--{k}", '"{v}"']
116
114
  elif type(v) in (list, tuple, set):
117
115
  for each in v:
118
- commands.append(f"--{k}")
119
- if isinstance(each, str):
120
- commands.append(f'"{each}"')
121
- else:
122
- commands.append(f"{each}")
116
+ commands += [f"--{k}", f"{each}"]
123
117
  else:
124
118
  commands += [f"--{k}", f"{v}"]
125
119
 
@@ -24,6 +24,7 @@ from zenml.integrations.integration import Integration
24
24
  from zenml.stack import Flavor
25
25
 
26
26
  KUBERNETES_ORCHESTRATOR_FLAVOR = "kubernetes"
27
+ KUBERNETES_STEP_OPERATOR_FLAVOR = "kubernetes"
27
28
 
28
29
 
29
30
  class KubernetesIntegration(Integration):
@@ -42,10 +43,10 @@ class KubernetesIntegration(Integration):
42
43
  List of new stack component flavors.
43
44
  """
44
45
  from zenml.integrations.kubernetes.flavors import (
45
- KubernetesOrchestratorFlavor,
46
+ KubernetesOrchestratorFlavor, KubernetesStepOperatorFlavor
46
47
  )
47
48
 
48
- return [KubernetesOrchestratorFlavor]
49
+ return [KubernetesOrchestratorFlavor, KubernetesStepOperatorFlavor]
49
50
 
50
51
 
51
52
  KubernetesIntegration.check_installation()
@@ -18,9 +18,17 @@ from zenml.integrations.kubernetes.flavors.kubernetes_orchestrator_flavor import
18
18
  KubernetesOrchestratorFlavor,
19
19
  KubernetesOrchestratorSettings,
20
20
  )
21
+ from zenml.integrations.kubernetes.flavors.kubernetes_step_operator_flavor import (
22
+ KubernetesStepOperatorConfig,
23
+ KubernetesStepOperatorFlavor,
24
+ KubernetesStepOperatorSettings,
25
+ )
21
26
 
22
27
  __all__ = [
23
28
  "KubernetesOrchestratorFlavor",
24
29
  "KubernetesOrchestratorConfig",
25
30
  "KubernetesOrchestratorSettings",
31
+ "KubernetesStepOperatorConfig",
32
+ "KubernetesStepOperatorFlavor",
33
+ "KubernetesStepOperatorSettings",
26
34
  ]
@@ -0,0 +1,166 @@
1
+ # Copyright (c) ZenML GmbH 2024. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at:
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12
+ # or implied. See the License for the specific language governing
13
+ # permissions and limitations under the License.
14
+ """Kubernetes step operator flavor."""
15
+
16
+ from typing import TYPE_CHECKING, Optional, Type
17
+
18
+ from zenml.config.base_settings import BaseSettings
19
+ from zenml.constants import KUBERNETES_CLUSTER_RESOURCE_TYPE
20
+ from zenml.integrations.kubernetes import KUBERNETES_STEP_OPERATOR_FLAVOR
21
+ from zenml.integrations.kubernetes.pod_settings import KubernetesPodSettings
22
+ from zenml.models import ServiceConnectorRequirements
23
+ from zenml.step_operators import BaseStepOperatorConfig, BaseStepOperatorFlavor
24
+
25
+ if TYPE_CHECKING:
26
+ from zenml.integrations.kubernetes.step_operators import (
27
+ KubernetesStepOperator,
28
+ )
29
+
30
+
31
+ class KubernetesStepOperatorSettings(BaseSettings):
32
+ """Settings for the Kubernetes step operator.
33
+
34
+ Attributes:
35
+ pod_settings: Pod settings to apply to pods executing the steps.
36
+ service_account_name: Name of the service account to use for the pod.
37
+ privileged: If the container should be run in privileged mode.
38
+ """
39
+
40
+ pod_settings: Optional[KubernetesPodSettings] = None
41
+ service_account_name: Optional[str] = None
42
+ privileged: bool = False
43
+
44
+
45
+ class KubernetesStepOperatorConfig(
46
+ BaseStepOperatorConfig, KubernetesStepOperatorSettings
47
+ ):
48
+ """Configuration for the Kubernetes step operator.
49
+
50
+ Attributes:
51
+ kubernetes_namespace: Name of the Kubernetes namespace to be used.
52
+ incluster: If `True`, the step operator will run the pipeline inside the
53
+ same cluster in which the orchestrator is running. For this to work,
54
+ the pod running the orchestrator needs permissions to create new
55
+ pods. If set, the `kubernetes_context` config option is ignored. If
56
+ the stack component is linked to a Kubernetes service connector,
57
+ this field is ignored.
58
+ kubernetes_context: Name of a Kubernetes context to run pipelines in.
59
+ If the stack component is linked to a Kubernetes service connector,
60
+ this field is ignored. Otherwise, it is mandatory.
61
+ """
62
+
63
+ kubernetes_namespace: str = "zenml"
64
+ incluster: bool = False
65
+ kubernetes_context: Optional[str] = None
66
+
67
+ @property
68
+ def is_remote(self) -> bool:
69
+ """Checks if this stack component is running remotely.
70
+
71
+ This designation is used to determine if the stack component can be
72
+ used with a local ZenML database or if it requires a remote ZenML
73
+ server.
74
+
75
+ Returns:
76
+ True if this config is for a remote component, False otherwise.
77
+ """
78
+ return True
79
+
80
+ @property
81
+ def is_local(self) -> bool:
82
+ """Checks if this stack component is running locally.
83
+
84
+ Returns:
85
+ True if this config is for a local component, False otherwise.
86
+ """
87
+ return False
88
+
89
+
90
+ class KubernetesStepOperatorFlavor(BaseStepOperatorFlavor):
91
+ """Kubernetes step operator flavor."""
92
+
93
+ @property
94
+ def name(self) -> str:
95
+ """Name of the flavor.
96
+
97
+ Returns:
98
+ The name of the flavor.
99
+ """
100
+ return KUBERNETES_STEP_OPERATOR_FLAVOR
101
+
102
+ @property
103
+ def service_connector_requirements(
104
+ self,
105
+ ) -> Optional[ServiceConnectorRequirements]:
106
+ """Service connector resource requirements for service connectors.
107
+
108
+ Specifies resource requirements that are used to filter the available
109
+ service connector types that are compatible with this flavor.
110
+
111
+ Returns:
112
+ Requirements for compatible service connectors, if a service
113
+ connector is required for this flavor.
114
+ """
115
+ return ServiceConnectorRequirements(
116
+ resource_type=KUBERNETES_CLUSTER_RESOURCE_TYPE,
117
+ )
118
+
119
+ @property
120
+ def docs_url(self) -> Optional[str]:
121
+ """A url to point at docs explaining this flavor.
122
+
123
+ Returns:
124
+ A flavor docs url.
125
+ """
126
+ return self.generate_default_docs_url()
127
+
128
+ @property
129
+ def sdk_docs_url(self) -> Optional[str]:
130
+ """A url to point at SDK docs explaining this flavor.
131
+
132
+ Returns:
133
+ A flavor SDK docs url.
134
+ """
135
+ return self.generate_default_sdk_docs_url()
136
+
137
+ @property
138
+ def logo_url(self) -> str:
139
+ """A url to represent the flavor in the dashboard.
140
+
141
+ Returns:
142
+ The flavor logo.
143
+ """
144
+ return "https://public-flavor-logos.s3.eu-central-1.amazonaws.com/step_operator/kubernetes.png"
145
+
146
+ @property
147
+ def config_class(self) -> Type[KubernetesStepOperatorConfig]:
148
+ """Returns `KubernetesStepOperatorConfig` config class.
149
+
150
+ Returns:
151
+ The config class.
152
+ """
153
+ return KubernetesStepOperatorConfig
154
+
155
+ @property
156
+ def implementation_class(self) -> Type["KubernetesStepOperator"]:
157
+ """Implementation class for this flavor.
158
+
159
+ Returns:
160
+ The implementation class.
161
+ """
162
+ from zenml.integrations.kubernetes.step_operators import (
163
+ KubernetesStepOperator,
164
+ )
165
+
166
+ return KubernetesStepOperator
@@ -38,7 +38,6 @@ from kubernetes import config as k8s_config
38
38
 
39
39
  from zenml.config.base_settings import BaseSettings
40
40
  from zenml.enums import StackComponentType
41
- from zenml.environment import Environment
42
41
  from zenml.integrations.kubernetes.flavors.kubernetes_orchestrator_flavor import (
43
42
  KubernetesOrchestratorConfig,
44
43
  KubernetesOrchestratorSettings,
@@ -335,19 +334,8 @@ class KubernetesOrchestrator(ContainerizedOrchestrator):
335
334
  environment.
336
335
 
337
336
  Raises:
338
- RuntimeError: If trying to run from a Jupyter notebook.
337
+ RuntimeError: If the Kubernetes orchestrator is not configured.
339
338
  """
340
- # First check whether the code is running in a notebook.
341
- if Environment.in_notebook():
342
- raise RuntimeError(
343
- "The Kubernetes orchestrator cannot run pipelines in a notebook "
344
- "environment. The reason is that it is non-trivial to create "
345
- "a Docker image of a notebook. Please consider refactoring "
346
- "your notebook cells into separate scripts in a Python module "
347
- "and run the code outside of a notebook when using this "
348
- "orchestrator."
349
- )
350
-
351
339
  for step_name, step in deployment.step_configurations.items():
352
340
  if self.requires_resources_in_orchestration_environment(step):
353
341
  logger.warning(
@@ -139,24 +139,42 @@ def build_pod_manifest(
139
139
  ],
140
140
  security_context=security_context,
141
141
  )
142
+ image_pull_secrets = []
143
+ if pod_settings:
144
+ image_pull_secrets = [
145
+ k8s_client.V1LocalObjectReference(name=name)
146
+ for name in pod_settings.image_pull_secrets
147
+ ]
142
148
 
143
149
  pod_spec = k8s_client.V1PodSpec(
144
150
  containers=[container_spec],
145
151
  restart_policy="Never",
152
+ image_pull_secrets=image_pull_secrets,
146
153
  )
147
154
 
148
155
  if service_account_name is not None:
149
156
  pod_spec.service_account_name = service_account_name
150
157
 
158
+ labels = {}
159
+
151
160
  if pod_settings:
152
161
  add_pod_settings(pod_spec, pod_settings)
153
162
 
154
- pod_metadata = k8s_client.V1ObjectMeta(
155
- name=pod_name,
156
- labels={
163
+ # Add pod_settings.labels to the labels
164
+ if pod_settings.labels:
165
+ labels.update(pod_settings.labels)
166
+
167
+ # Add run_name and pipeline_name to the labels
168
+ labels.update(
169
+ {
157
170
  "run": run_name,
158
171
  "pipeline": pipeline_name,
159
- },
172
+ }
173
+ )
174
+
175
+ pod_metadata = k8s_client.V1ObjectMeta(
176
+ name=pod_name,
177
+ labels=labels,
160
178
  )
161
179
 
162
180
  if pod_settings and pod_settings.annotations:
@@ -33,6 +33,8 @@ class KubernetesPodSettings(BaseSettings):
33
33
  volumes: Volumes to mount in the pod.
34
34
  volume_mounts: Volume mounts to apply to the pod containers.
35
35
  host_ipc: Whether to enable host IPC for the pod.
36
+ image_pull_secrets: Image pull secrets to use for the pod.
37
+ labels: Labels to apply to the pod.
36
38
  """
37
39
 
38
40
  node_selectors: Dict[str, str] = {}
@@ -43,6 +45,8 @@ class KubernetesPodSettings(BaseSettings):
43
45
  volumes: List[Dict[str, Any]] = []
44
46
  volume_mounts: List[Dict[str, Any]] = []
45
47
  host_ipc: bool = False
48
+ image_pull_secrets: List[str] = []
49
+ labels: Dict[str, str] = {}
46
50
 
47
51
  @field_validator("volumes", mode="before")
48
52
  @classmethod
@@ -0,0 +1,22 @@
1
+ # Copyright (c) ZenML GmbH 2024. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at:
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12
+ # or implied. See the License for the specific language governing
13
+ # permissions and limitations under the License.
14
+ """Kubernetes step operator."""
15
+
16
+ from zenml.integrations.kubernetes.step_operators.kubernetes_step_operator import (
17
+ KubernetesStepOperator,
18
+ )
19
+
20
+ __all__ = [
21
+ "KubernetesStepOperator",
22
+ ]
@@ -0,0 +1,235 @@
1
+ # Copyright (c) ZenML GmbH 2024. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at:
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12
+ # or implied. See the License for the specific language governing
13
+ # permissions and limitations under the License.
14
+ """Kubernetes step operator implementation."""
15
+
16
+ from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Type, cast
17
+
18
+ from kubernetes import client as k8s_client
19
+
20
+ from zenml.config.base_settings import BaseSettings
21
+ from zenml.config.build_configuration import BuildConfiguration
22
+ from zenml.enums import StackComponentType
23
+ from zenml.integrations.kubernetes.flavors import (
24
+ KubernetesStepOperatorConfig,
25
+ KubernetesStepOperatorSettings,
26
+ )
27
+ from zenml.integrations.kubernetes.orchestrators import kube_utils
28
+ from zenml.integrations.kubernetes.orchestrators.manifest_utils import (
29
+ build_pod_manifest,
30
+ )
31
+ from zenml.logger import get_logger
32
+ from zenml.stack import Stack, StackValidator
33
+ from zenml.step_operators import BaseStepOperator
34
+
35
+ if TYPE_CHECKING:
36
+ from zenml.config.base_settings import BaseSettings
37
+ from zenml.config.step_run_info import StepRunInfo
38
+ from zenml.models import PipelineDeploymentBase
39
+
40
+ logger = get_logger(__name__)
41
+
42
+ KUBERNETES_STEP_OPERATOR_DOCKER_IMAGE_KEY = "kubernetes_step_operator"
43
+
44
+
45
+ class KubernetesStepOperator(BaseStepOperator):
46
+ """Step operator to run on Kubernetes."""
47
+
48
+ _k8s_client: Optional[k8s_client.ApiClient] = None
49
+
50
+ @property
51
+ def config(self) -> KubernetesStepOperatorConfig:
52
+ """Returns the `KubernetesStepOperatorConfig` config.
53
+
54
+ Returns:
55
+ The configuration.
56
+ """
57
+ return cast(KubernetesStepOperatorConfig, self._config)
58
+
59
+ @property
60
+ def settings_class(self) -> Optional[Type["BaseSettings"]]:
61
+ """Settings class for the Kubernetes step operator.
62
+
63
+ Returns:
64
+ The settings class.
65
+ """
66
+ return KubernetesStepOperatorSettings
67
+
68
+ @property
69
+ def validator(self) -> Optional[StackValidator]:
70
+ """Validates the stack.
71
+
72
+ Returns:
73
+ A validator that checks that the stack contains a remote container
74
+ registry and a remote artifact store.
75
+ """
76
+
77
+ def _validate_remote_components(stack: "Stack") -> Tuple[bool, str]:
78
+ if stack.artifact_store.config.is_local:
79
+ return False, (
80
+ "The Kubernetes step operator runs code remotely and "
81
+ "needs to write files into the artifact store, but the "
82
+ f"artifact store `{stack.artifact_store.name}` of the "
83
+ "active stack is local. Please ensure that your stack "
84
+ "contains a remote artifact store when using the Vertex "
85
+ "step operator."
86
+ )
87
+
88
+ container_registry = stack.container_registry
89
+ assert container_registry is not None
90
+
91
+ if container_registry.config.is_local:
92
+ return False, (
93
+ "The Kubernetes step operator runs code remotely and "
94
+ "needs to push/pull Docker images, but the "
95
+ f"container registry `{container_registry.name}` of the "
96
+ "active stack is local. Please ensure that your stack "
97
+ "contains a remote container registry when using the "
98
+ "Kubernetes step operator."
99
+ )
100
+
101
+ return True, ""
102
+
103
+ return StackValidator(
104
+ required_components={
105
+ StackComponentType.CONTAINER_REGISTRY,
106
+ StackComponentType.IMAGE_BUILDER,
107
+ },
108
+ custom_validation_function=_validate_remote_components,
109
+ )
110
+
111
+ def get_docker_builds(
112
+ self, deployment: "PipelineDeploymentBase"
113
+ ) -> List["BuildConfiguration"]:
114
+ """Gets the Docker builds required for the component.
115
+
116
+ Args:
117
+ deployment: The pipeline deployment for which to get the builds.
118
+
119
+ Returns:
120
+ The required Docker builds.
121
+ """
122
+ builds = []
123
+ for step_name, step in deployment.step_configurations.items():
124
+ if step.config.step_operator == self.name:
125
+ build = BuildConfiguration(
126
+ key=KUBERNETES_STEP_OPERATOR_DOCKER_IMAGE_KEY,
127
+ settings=step.config.docker_settings,
128
+ step_name=step_name,
129
+ )
130
+ builds.append(build)
131
+
132
+ return builds
133
+
134
+ def get_kube_client(self) -> k8s_client.ApiClient:
135
+ """Get the Kubernetes API client.
136
+
137
+ Returns:
138
+ The Kubernetes API client.
139
+
140
+ Raises:
141
+ RuntimeError: If the service connector returns an unexpected client.
142
+ """
143
+ if self.config.incluster:
144
+ kube_utils.load_kube_config(incluster=True)
145
+ self._k8s_client = k8s_client.ApiClient()
146
+ return self._k8s_client
147
+
148
+ # Refresh the client also if the connector has expired
149
+ if self._k8s_client and not self.connector_has_expired():
150
+ return self._k8s_client
151
+
152
+ connector = self.get_connector()
153
+ if connector:
154
+ client = connector.connect()
155
+ if not isinstance(client, k8s_client.ApiClient):
156
+ raise RuntimeError(
157
+ f"Expected a k8s_client.ApiClient while trying to use the "
158
+ f"linked connector, but got {type(client)}."
159
+ )
160
+ self._k8s_client = client
161
+ else:
162
+ kube_utils.load_kube_config(
163
+ context=self.config.kubernetes_context,
164
+ )
165
+ self._k8s_client = k8s_client.ApiClient()
166
+
167
+ return self._k8s_client
168
+
169
+ @property
170
+ def _k8s_core_api(self) -> k8s_client.CoreV1Api:
171
+ """Getter for the Kubernetes Core API client.
172
+
173
+ Returns:
174
+ The Kubernetes Core API client.
175
+ """
176
+ return k8s_client.CoreV1Api(self.get_kube_client())
177
+
178
+ def launch(
179
+ self,
180
+ info: "StepRunInfo",
181
+ entrypoint_command: List[str],
182
+ environment: Dict[str, str],
183
+ ) -> None:
184
+ """Launches a step on Kubernetes.
185
+
186
+ Args:
187
+ info: Information about the step run.
188
+ entrypoint_command: Command that executes the step.
189
+ environment: Environment variables to set in the step operator
190
+ environment.
191
+ """
192
+ settings = cast(
193
+ KubernetesStepOperatorSettings, self.get_settings(info)
194
+ )
195
+ image_name = info.get_image(
196
+ key=KUBERNETES_STEP_OPERATOR_DOCKER_IMAGE_KEY
197
+ )
198
+
199
+ pod_name = f"{info.run_name}_{info.pipeline_step_name}"
200
+ pod_name = kube_utils.sanitize_pod_name(pod_name)
201
+
202
+ command = entrypoint_command[:3]
203
+ args = entrypoint_command[3:]
204
+
205
+ # Create and run the orchestrator pod.
206
+ pod_manifest = build_pod_manifest(
207
+ run_name=info.run_name,
208
+ pod_name=pod_name,
209
+ pipeline_name=info.pipeline.name,
210
+ image_name=image_name,
211
+ command=command,
212
+ args=args,
213
+ privileged=settings.privileged,
214
+ service_account_name=settings.service_account_name,
215
+ pod_settings=settings.pod_settings,
216
+ env=environment,
217
+ mount_local_stores=False,
218
+ )
219
+
220
+ self._k8s_core_api.create_namespaced_pod(
221
+ namespace=self.config.kubernetes_namespace,
222
+ body=pod_manifest,
223
+ )
224
+
225
+ logger.info(
226
+ "Waiting for pod of step `%s` to start...", info.pipeline_step_name
227
+ )
228
+ kube_utils.wait_pod(
229
+ kube_client_fn=self.get_kube_client,
230
+ pod_name=pod_name,
231
+ namespace=self.config.kubernetes_namespace,
232
+ exit_condition_lambda=kube_utils.pod_is_done,
233
+ stream_logs=True,
234
+ )
235
+ logger.info("Pod of step `%s` completed.", info.pipeline_step_name)