zenml-nightly 0.61.0.dev20240713__py3-none-any.whl → 0.62.0.dev20240717__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.
- README.md +1 -1
- RELEASE_NOTES.md +40 -0
- zenml/VERSION +1 -1
- zenml/__init__.py +2 -0
- zenml/cli/stack.py +87 -228
- zenml/cli/stack_components.py +5 -3
- zenml/constants.py +2 -0
- zenml/entrypoints/entrypoint.py +3 -1
- zenml/integrations/__init__.py +1 -0
- zenml/integrations/constants.py +1 -0
- zenml/integrations/databricks/__init__.py +52 -0
- zenml/integrations/databricks/flavors/__init__.py +30 -0
- zenml/integrations/databricks/flavors/databricks_model_deployer_flavor.py +118 -0
- zenml/integrations/databricks/flavors/databricks_orchestrator_flavor.py +147 -0
- zenml/integrations/databricks/model_deployers/__init__.py +20 -0
- zenml/integrations/databricks/model_deployers/databricks_model_deployer.py +249 -0
- zenml/integrations/databricks/orchestrators/__init__.py +20 -0
- zenml/integrations/databricks/orchestrators/databricks_orchestrator.py +498 -0
- zenml/integrations/databricks/orchestrators/databricks_orchestrator_entrypoint_config.py +97 -0
- zenml/integrations/databricks/services/__init__.py +19 -0
- zenml/integrations/databricks/services/databricks_deployment.py +407 -0
- zenml/integrations/databricks/utils/__init__.py +14 -0
- zenml/integrations/databricks/utils/databricks_utils.py +87 -0
- zenml/integrations/great_expectations/data_validators/ge_data_validator.py +12 -8
- zenml/integrations/huggingface/materializers/huggingface_datasets_materializer.py +88 -3
- zenml/integrations/huggingface/steps/accelerate_runner.py +1 -7
- zenml/integrations/kubernetes/orchestrators/manifest_utils.py +7 -0
- zenml/integrations/kubernetes/pod_settings.py +2 -0
- zenml/integrations/lightgbm/__init__.py +1 -0
- zenml/integrations/mlflow/__init__.py +1 -1
- zenml/integrations/mlflow/model_registries/mlflow_model_registry.py +6 -2
- zenml/integrations/mlflow/services/mlflow_deployment.py +1 -1
- zenml/integrations/skypilot_lambda/__init__.py +1 -1
- zenml/materializers/built_in_materializer.py +1 -1
- zenml/materializers/cloudpickle_materializer.py +1 -1
- zenml/model/model.py +1 -1
- zenml/models/v2/misc/full_stack.py +32 -0
- zenml/orchestrators/__init__.py +4 -0
- zenml/orchestrators/wheeled_orchestrator.py +147 -0
- zenml/service_connectors/service_connector_utils.py +349 -0
- zenml/stack_deployments/gcp_stack_deployment.py +2 -4
- zenml/steps/base_step.py +7 -5
- zenml/utils/function_utils.py +1 -1
- zenml/utils/pipeline_docker_image_builder.py +8 -0
- zenml/zen_server/dashboard/assets/{404-DpJaNHKF.js → 404-B_YdvmwS.js} +1 -1
- zenml/zen_server/dashboard/assets/{@reactflow-DJfzkHO1.js → @reactflow-l_1hUr1S.js} +1 -1
- zenml/zen_server/dashboard/assets/{AwarenessChannel-BYDLT2xC.js → AwarenessChannel-CFg5iX4Z.js} +1 -1
- zenml/zen_server/dashboard/assets/{CodeSnippet-BkOuRmyq.js → CodeSnippet-Dvkx_82E.js} +1 -1
- zenml/zen_server/dashboard/assets/CollapsibleCard-opiuBHHc.js +1 -0
- zenml/zen_server/dashboard/assets/{Commands-ZvWR1BRs.js → Commands-DoN1xrEq.js} +1 -1
- zenml/zen_server/dashboard/assets/{CopyButton-DVwLkafa.js → CopyButton-Cr7xYEPb.js} +1 -1
- zenml/zen_server/dashboard/assets/{CsvVizualization-C2IiqX4I.js → CsvVizualization-Ck-nZ43m.js} +3 -3
- zenml/zen_server/dashboard/assets/{Error-CqX0VqW_.js → Error-kLtljEOM.js} +1 -1
- zenml/zen_server/dashboard/assets/{ExecutionStatus-BoLUXR9t.js → ExecutionStatus-DguLLgTK.js} +1 -1
- zenml/zen_server/dashboard/assets/{Helpbox-LFydyVwh.js → Helpbox-BXUMP21n.js} +1 -1
- zenml/zen_server/dashboard/assets/{Infobox-DnENC0sh.js → Infobox-DSt0O-dm.js} +1 -1
- zenml/zen_server/dashboard/assets/{InlineAvatar-CbJtYr0t.js → InlineAvatar-xsrsIGE-.js} +1 -1
- zenml/zen_server/dashboard/assets/Pagination-C6X-mifw.js +1 -0
- zenml/zen_server/dashboard/assets/{SetPassword-BYBdbQDo.js → SetPassword-BXGTWiwj.js} +1 -1
- zenml/zen_server/dashboard/assets/{SuccessStep-Nx743hll.js → SuccessStep-DZC60t0x.js} +1 -1
- zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-DF9gSzE0.js → UpdatePasswordSchemas-DGvwFWO1.js} +1 -1
- zenml/zen_server/dashboard/assets/{chevron-right-double-BiEMg7rd.js → chevron-right-double-CZBOf6JM.js} +1 -1
- zenml/zen_server/dashboard/assets/cloud-only-C_yFCAkP.js +1 -0
- zenml/zen_server/dashboard/assets/index-BczVOqUf.js +55 -0
- zenml/zen_server/dashboard/assets/index-EpMIKgrI.css +1 -0
- zenml/zen_server/dashboard/assets/{login-mutation-BUnVASxp.js → login-mutation-CrHrndTI.js} +1 -1
- zenml/zen_server/dashboard/assets/logs-D8k8BVFf.js +1 -0
- zenml/zen_server/dashboard/assets/{not-found-B4VnX8gK.js → not-found-DYa4pC-C.js} +1 -1
- zenml/zen_server/dashboard/assets/{package-CsUhPmou.js → package-B3fWP-Dh.js} +1 -1
- zenml/zen_server/dashboard/assets/page-1h_sD1jz.js +1 -0
- zenml/zen_server/dashboard/assets/{page-Sxn82W-5.js → page-1iL8aMqs.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-DMOYZppS.js → page-2grKx_MY.js} +1 -1
- zenml/zen_server/dashboard/assets/page-5NCOHOsy.js +1 -0
- zenml/zen_server/dashboard/assets/{page-JyfeDUfu.js → page-8a4UMKXZ.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-Bx6o0ARS.js → page-B6h3iaHJ.js} +1 -1
- zenml/zen_server/dashboard/assets/page-BDns21Iz.js +1 -0
- zenml/zen_server/dashboard/assets/{page-3efNCDeb.js → page-BhgCDInH.js} +2 -2
- zenml/zen_server/dashboard/assets/{page-DKlIdAe5.js → page-Bi-wtWiO.js} +2 -2
- zenml/zen_server/dashboard/assets/{page-7zTHbhhI.js → page-BkeAAYwp.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-CRTJ0UuR.js → page-BkuQDIf-.js} +1 -1
- zenml/zen_server/dashboard/assets/page-BnaevhnB.js +1 -0
- zenml/zen_server/dashboard/assets/{page-BEs6jK71.js → page-Bq0YxkLV.js} +1 -1
- zenml/zen_server/dashboard/assets/page-Bs2F4eoD.js +2 -0
- zenml/zen_server/dashboard/assets/{page-CUZIGO-3.js → page-C6-UGEbH.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-Xu8JEjSU.js → page-CCNRIt_f.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-DvCvroOM.js → page-CHNxpz3n.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-BpSqIf4B.js → page-DgorQFqi.js} +1 -1
- zenml/zen_server/dashboard/assets/page-K8ebxVIs.js +1 -0
- zenml/zen_server/dashboard/assets/{page-Cx67M0QT.js → page-MFQyIJd3.js} +1 -1
- zenml/zen_server/dashboard/assets/page-TgCF0P_U.js +1 -0
- zenml/zen_server/dashboard/assets/page-ZnCEe-eK.js +9 -0
- zenml/zen_server/dashboard/assets/{page-Dc_7KMQE.js → page-uA5prJGY.js} +1 -1
- zenml/zen_server/dashboard/assets/persist-D7HJNBWx.js +1 -0
- zenml/zen_server/dashboard/assets/plus-C8WOyCzt.js +1 -0
- zenml/zen_server/dashboard/assets/stack-detail-query-Cficsl6d.js +1 -0
- zenml/zen_server/dashboard/assets/update-server-settings-mutation-7d8xi1tS.js +1 -0
- zenml/zen_server/dashboard/assets/{url-DuQMeqYA.js → url-D7mAQGUM.js} +1 -1
- zenml/zen_server/dashboard/index.html +4 -4
- zenml/zen_server/dashboard_legacy/asset-manifest.json +4 -4
- zenml/zen_server/dashboard_legacy/index.html +1 -1
- zenml/zen_server/dashboard_legacy/{precache-manifest.c8c57fb0d2132b1d3c2119e776b7dfb3.js → precache-manifest.12246c7548e71e2c4438e496360de80c.js} +4 -4
- zenml/zen_server/dashboard_legacy/service-worker.js +1 -1
- zenml/zen_server/dashboard_legacy/static/js/main.3b27024b.chunk.js +2 -0
- zenml/zen_server/dashboard_legacy/static/js/{main.382439a7.chunk.js.map → main.3b27024b.chunk.js.map} +1 -1
- zenml/zen_server/deploy/helm/Chart.yaml +1 -1
- zenml/zen_server/deploy/helm/README.md +2 -2
- zenml/zen_server/routers/service_connectors_endpoints.py +57 -0
- zenml/zen_stores/migrations/versions/0.62.0_release.py +23 -0
- zenml/zen_stores/rest_zen_store.py +4 -0
- {zenml_nightly-0.61.0.dev20240713.dist-info → zenml_nightly-0.62.0.dev20240717.dist-info}/METADATA +2 -2
- {zenml_nightly-0.61.0.dev20240713.dist-info → zenml_nightly-0.62.0.dev20240717.dist-info}/RECORD +114 -96
- zenml/zen_server/dashboard/assets/Pagination-DEbVUupy.js +0 -1
- zenml/zen_server/dashboard/assets/chevron-down-D_ZlKMqH.js +0 -1
- zenml/zen_server/dashboard/assets/cloud-only-DVbIeckv.js +0 -1
- zenml/zen_server/dashboard/assets/index-C_CrU4vI.js +0 -1
- zenml/zen_server/dashboard/assets/index-DK1ynKjA.js +0 -55
- zenml/zen_server/dashboard/assets/index-inApY3KQ.css +0 -1
- zenml/zen_server/dashboard/assets/page-C43QGHTt.js +0 -9
- zenml/zen_server/dashboard/assets/page-CR0OG7ss.js +0 -1
- zenml/zen_server/dashboard/assets/page-CaopxiU1.js +0 -1
- zenml/zen_server/dashboard/assets/page-D7Z399xy.js +0 -1
- zenml/zen_server/dashboard/assets/page-D93kd7Xj.js +0 -1
- zenml/zen_server/dashboard/assets/page-DMsSn3dv.js +0 -2
- zenml/zen_server/dashboard/assets/page-Hus2pr9T.js +0 -1
- zenml/zen_server/dashboard/assets/page-TKXERe16.js +0 -1
- zenml/zen_server/dashboard/assets/plus-DOeLmm7C.js +0 -1
- zenml/zen_server/dashboard/assets/update-server-settings-mutation-CR8e3Sir.js +0 -1
- zenml/zen_server/dashboard_legacy/static/js/main.382439a7.chunk.js +0 -2
- {zenml_nightly-0.61.0.dev20240713.dist-info → zenml_nightly-0.62.0.dev20240717.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.61.0.dev20240713.dist-info → zenml_nightly-0.62.0.dev20240717.dist-info}/WHEEL +0 -0
- {zenml_nightly-0.61.0.dev20240713.dist-info → zenml_nightly-0.62.0.dev20240717.dist-info}/entry_points.txt +0 -0
@@ -33,6 +33,7 @@ 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.
|
36
37
|
"""
|
37
38
|
|
38
39
|
node_selectors: Dict[str, str] = {}
|
@@ -43,6 +44,7 @@ class KubernetesPodSettings(BaseSettings):
|
|
43
44
|
volumes: List[Dict[str, Any]] = []
|
44
45
|
volume_mounts: List[Dict[str, Any]] = []
|
45
46
|
host_ipc: bool = False
|
47
|
+
image_pull_secrets: List[str] = []
|
46
48
|
|
47
49
|
@field_validator("volumes", mode="before")
|
48
50
|
@classmethod
|
@@ -371,7 +371,7 @@ class MLFlowModelRegistry(BaseModelRegistry):
|
|
371
371
|
self.get_model(name=name)
|
372
372
|
except KeyError:
|
373
373
|
logger.info(
|
374
|
-
f"No registered model with name {name} found. Creating a new"
|
374
|
+
f"No registered model with name {name} found. Creating a new "
|
375
375
|
"registered model."
|
376
376
|
)
|
377
377
|
self.register_model(
|
@@ -615,7 +615,11 @@ class MLFlowModelRegistry(BaseModelRegistry):
|
|
615
615
|
for mlflow_model_version in mlflow_model_versions:
|
616
616
|
# check if given MlFlow model version matches the given request
|
617
617
|
# before casting it
|
618
|
-
if
|
618
|
+
if (
|
619
|
+
stage
|
620
|
+
and not ModelVersionStage(mlflow_model_version.current_stage)
|
621
|
+
== stage
|
622
|
+
):
|
619
623
|
continue
|
620
624
|
if created_after and not (
|
621
625
|
mlflow_model_version.creation_timestamp
|
@@ -260,7 +260,7 @@ class MLFlowDeploymentService(LocalDaemonService, BaseDeploymentService):
|
|
260
260
|
)
|
261
261
|
|
262
262
|
if self.endpoint.prediction_url is not None:
|
263
|
-
if type(request)
|
263
|
+
if type(request) is pd.DataFrame:
|
264
264
|
response = requests.post( # nosec
|
265
265
|
self.endpoint.prediction_url,
|
266
266
|
json={"instances": request.to_dict("records")},
|
@@ -31,7 +31,7 @@ class SkypilotLambdaIntegration(Integration):
|
|
31
31
|
"""Definition of Skypilot Lambda Integration for ZenML."""
|
32
32
|
|
33
33
|
NAME = SKYPILOT_LAMBDA
|
34
|
-
REQUIREMENTS = ["skypilot[lambda]
|
34
|
+
REQUIREMENTS = ["skypilot[lambda]~=0.6.0"]
|
35
35
|
|
36
36
|
@classmethod
|
37
37
|
def flavors(cls) -> List[Type[Flavor]]:
|
@@ -80,7 +80,7 @@ class BuiltInMaterializer(BaseMaterializer):
|
|
80
80
|
The data read.
|
81
81
|
"""
|
82
82
|
contents = yaml_utils.read_json(self.data_path)
|
83
|
-
if type(contents)
|
83
|
+
if type(contents) is not data_type:
|
84
84
|
# TODO [ENG-142]: Raise error or try to coerce
|
85
85
|
logger.debug(
|
86
86
|
f"Contents {contents} was type {type(contents)} but expected "
|
@@ -94,7 +94,7 @@ class CloudpickleMaterializer(BaseMaterializer):
|
|
94
94
|
"""
|
95
95
|
# Log a warning if this materializer was not explicitly specified for
|
96
96
|
# the given data type.
|
97
|
-
if type(self)
|
97
|
+
if type(self) is CloudpickleMaterializer:
|
98
98
|
logger.warning(
|
99
99
|
f"No materializer is registered for type `{type(data)}`, so "
|
100
100
|
"the default Pickle materializer was used. Pickle is not "
|
zenml/model/model.py
CHANGED
@@ -518,7 +518,7 @@ class Model(BaseModel):
|
|
518
518
|
and not suppress_class_validation_warnings
|
519
519
|
):
|
520
520
|
logger.info(
|
521
|
-
f"
|
521
|
+
f"Version `{version}` matches one of the possible "
|
522
522
|
"`ModelStages` and will be fetched using stage."
|
523
523
|
)
|
524
524
|
if str(version).isnumeric() and not suppress_class_validation_warnings:
|
@@ -21,6 +21,7 @@ from pydantic import BaseModel, Field, model_validator
|
|
21
21
|
from zenml.constants import STR_FIELD_MAX_LENGTH
|
22
22
|
from zenml.enums import StackComponentType
|
23
23
|
from zenml.models.v2.base.base import BaseRequest
|
24
|
+
from zenml.models.v2.core.component import ComponentResponse
|
24
25
|
|
25
26
|
|
26
27
|
class ServiceConnectorInfo(BaseModel):
|
@@ -95,3 +96,34 @@ class FullStackRequest(BaseRequest):
|
|
95
96
|
"the position in the list of service connectors."
|
96
97
|
)
|
97
98
|
return self
|
99
|
+
|
100
|
+
|
101
|
+
class ResourcesInfo(BaseModel):
|
102
|
+
"""Information about the resources needed for CLI and UI."""
|
103
|
+
|
104
|
+
flavor: str
|
105
|
+
flavor_display_name: str
|
106
|
+
required_configuration: Dict[str, str] = {}
|
107
|
+
use_resource_value_as_fixed_config: bool = False
|
108
|
+
|
109
|
+
accessible_by_service_connector: List[str]
|
110
|
+
connected_through_service_connector: List[ComponentResponse]
|
111
|
+
|
112
|
+
@model_validator(mode="after")
|
113
|
+
def _validate_resource_info(self) -> "ResourcesInfo":
|
114
|
+
if (
|
115
|
+
self.use_resource_value_as_fixed_config
|
116
|
+
and len(self.required_configuration) > 1
|
117
|
+
):
|
118
|
+
raise ValueError(
|
119
|
+
"Cannot use resource value as fixed config if more than one required configuration key is provided."
|
120
|
+
)
|
121
|
+
return self
|
122
|
+
|
123
|
+
|
124
|
+
class ServiceConnectorResourcesInfo(BaseModel):
|
125
|
+
"""Information about the service connector resources needed for CLI and UI."""
|
126
|
+
|
127
|
+
connector_type: str
|
128
|
+
|
129
|
+
components_resources_info: Dict[StackComponentType, List[ResourcesInfo]]
|
zenml/orchestrators/__init__.py
CHANGED
@@ -30,6 +30,9 @@ from zenml.orchestrators.base_orchestrator import (
|
|
30
30
|
from zenml.orchestrators.containerized_orchestrator import (
|
31
31
|
ContainerizedOrchestrator,
|
32
32
|
)
|
33
|
+
from zenml.orchestrators.wheeled_orchestrator import (
|
34
|
+
WheeledOrchestrator,
|
35
|
+
)
|
33
36
|
from zenml.orchestrators.local.local_orchestrator import (
|
34
37
|
LocalOrchestrator,
|
35
38
|
LocalOrchestratorFlavor,
|
@@ -44,6 +47,7 @@ __all__ = [
|
|
44
47
|
"BaseOrchestratorConfig",
|
45
48
|
"BaseOrchestratorFlavor",
|
46
49
|
"ContainerizedOrchestrator",
|
50
|
+
"WheeledOrchestrator",
|
47
51
|
"LocalOrchestrator",
|
48
52
|
"LocalOrchestratorFlavor",
|
49
53
|
"LocalDockerOrchestrator",
|
@@ -0,0 +1,147 @@
|
|
1
|
+
# Copyright (c) ZenML GmbH 2021. 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
|
+
"""Wheeled orchestrator class."""
|
15
|
+
|
16
|
+
import os
|
17
|
+
import re
|
18
|
+
import subprocess
|
19
|
+
import tempfile
|
20
|
+
from abc import ABC
|
21
|
+
|
22
|
+
from zenml import __version__
|
23
|
+
from zenml.io import fileio
|
24
|
+
from zenml.logger import get_logger
|
25
|
+
from zenml.orchestrators import BaseOrchestrator
|
26
|
+
from zenml.utils.io_utils import copy_dir
|
27
|
+
from zenml.utils.source_utils import get_source_root
|
28
|
+
|
29
|
+
logger = get_logger(__name__)
|
30
|
+
|
31
|
+
DEFAULT_PACKAGE_NAME = "zenmlproject"
|
32
|
+
|
33
|
+
|
34
|
+
class WheeledOrchestrator(BaseOrchestrator, ABC):
|
35
|
+
"""Base class for wheeled orchestrators."""
|
36
|
+
|
37
|
+
package_name = DEFAULT_PACKAGE_NAME
|
38
|
+
package_version = __version__
|
39
|
+
|
40
|
+
def copy_repository_to_temp_dir_and_add_setup_py(self) -> str:
|
41
|
+
"""Copy the repository to a temporary directory and add a setup.py file.
|
42
|
+
|
43
|
+
Returns:
|
44
|
+
Path to the temporary directory containing the copied repository.
|
45
|
+
"""
|
46
|
+
repo_path = get_source_root()
|
47
|
+
|
48
|
+
self.package_name = f"{DEFAULT_PACKAGE_NAME}_{self.sanitize_name(os.path.basename(repo_path))}"
|
49
|
+
|
50
|
+
# Create a temporary folder
|
51
|
+
temp_dir = tempfile.mkdtemp(prefix="zenml-temp-")
|
52
|
+
|
53
|
+
# Create a folder within the temporary directory
|
54
|
+
temp_repo_path = os.path.join(temp_dir, self.package_name)
|
55
|
+
fileio.mkdir(temp_repo_path)
|
56
|
+
|
57
|
+
# Copy the repository to the temporary directory
|
58
|
+
copy_dir(repo_path, temp_repo_path)
|
59
|
+
|
60
|
+
# Create init file in the copied directory
|
61
|
+
init_file_path = os.path.join(temp_repo_path, "__init__.py")
|
62
|
+
with fileio.open(init_file_path, "w") as f:
|
63
|
+
f.write("")
|
64
|
+
|
65
|
+
# Create a setup.py file
|
66
|
+
setup_py_content = f"""
|
67
|
+
from setuptools import setup, find_packages
|
68
|
+
|
69
|
+
setup(
|
70
|
+
name="{self.package_name}",
|
71
|
+
version="{self.package_version}",
|
72
|
+
packages=find_packages(),
|
73
|
+
)
|
74
|
+
"""
|
75
|
+
setup_py_path = os.path.join(temp_dir, "setup.py")
|
76
|
+
with fileio.open(setup_py_path, "w") as f:
|
77
|
+
f.write(setup_py_content)
|
78
|
+
|
79
|
+
return temp_dir
|
80
|
+
|
81
|
+
def create_wheel(self, temp_dir: str) -> str:
|
82
|
+
"""Create a wheel for the package in the given temporary directory.
|
83
|
+
|
84
|
+
Args:
|
85
|
+
temp_dir (str): Path to the temporary directory containing the package.
|
86
|
+
|
87
|
+
Raises:
|
88
|
+
RuntimeError: If the wheel file could not be created.
|
89
|
+
|
90
|
+
Returns:
|
91
|
+
str: Path to the created wheel file.
|
92
|
+
"""
|
93
|
+
# Change to the temporary directory
|
94
|
+
original_dir = os.getcwd()
|
95
|
+
os.chdir(temp_dir)
|
96
|
+
|
97
|
+
try:
|
98
|
+
# Run the `pip wheel` command to create the wheel
|
99
|
+
result = subprocess.run(
|
100
|
+
["pip", "wheel", "."], check=True, capture_output=True
|
101
|
+
)
|
102
|
+
logger.debug(f"Wheel creation stdout: {result.stdout.decode()}")
|
103
|
+
logger.debug(f"Wheel creation stderr: {result.stderr.decode()}")
|
104
|
+
|
105
|
+
# Find the created wheel file
|
106
|
+
wheel_file = next(
|
107
|
+
(
|
108
|
+
file
|
109
|
+
for file in os.listdir(temp_dir)
|
110
|
+
if file.endswith(".whl")
|
111
|
+
),
|
112
|
+
None,
|
113
|
+
)
|
114
|
+
|
115
|
+
if wheel_file is None:
|
116
|
+
raise RuntimeError("Failed to create wheel file.")
|
117
|
+
|
118
|
+
wheel_path = os.path.join(temp_dir, wheel_file)
|
119
|
+
|
120
|
+
# Verify the wheel file is a valid zip file
|
121
|
+
import zipfile
|
122
|
+
|
123
|
+
if not zipfile.is_zipfile(wheel_path):
|
124
|
+
raise RuntimeError(
|
125
|
+
f"The file {wheel_path} is not a valid zip file."
|
126
|
+
)
|
127
|
+
|
128
|
+
return wheel_path
|
129
|
+
finally:
|
130
|
+
# Change back to the original directory
|
131
|
+
os.chdir(original_dir)
|
132
|
+
|
133
|
+
def sanitize_name(self, name: str) -> str:
|
134
|
+
"""Sanitize the value to be used in a cluster name.
|
135
|
+
|
136
|
+
Args:
|
137
|
+
name: Arbitrary input cluster name.
|
138
|
+
|
139
|
+
Returns:
|
140
|
+
Sanitized cluster name.
|
141
|
+
"""
|
142
|
+
name = re.sub(
|
143
|
+
r"[^a-z0-9-]", "-", name.lower()
|
144
|
+
) # replaces any character that is not a lowercase letter, digit, or hyphen with a hyphen
|
145
|
+
name = re.sub(r"^[-]+", "", name) # trim leading hyphens
|
146
|
+
name = re.sub(r"[-]+$", "", name) # trim trailing hyphens
|
147
|
+
return name
|
@@ -0,0 +1,349 @@
|
|
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
|
+
"""Utility methods for Service Connectors."""
|
15
|
+
|
16
|
+
from typing import Dict, List, Union
|
17
|
+
from uuid import UUID
|
18
|
+
|
19
|
+
from zenml.client import Client
|
20
|
+
from zenml.enums import StackComponentType
|
21
|
+
from zenml.models.v2.core.service_connector import ServiceConnectorRequest
|
22
|
+
from zenml.models.v2.misc.full_stack import (
|
23
|
+
ResourcesInfo,
|
24
|
+
ServiceConnectorInfo,
|
25
|
+
ServiceConnectorResourcesInfo,
|
26
|
+
)
|
27
|
+
from zenml.utils.pagination_utils import depaginate
|
28
|
+
|
29
|
+
|
30
|
+
def _prepare_resource_info(
|
31
|
+
connector_details: Union[UUID, ServiceConnectorInfo],
|
32
|
+
resource_ids: List[str],
|
33
|
+
stack_component_type: StackComponentType,
|
34
|
+
flavor: str,
|
35
|
+
required_configuration: Dict[str, str],
|
36
|
+
flavor_display_name: str,
|
37
|
+
use_resource_value_as_fixed_config: bool = False,
|
38
|
+
) -> ResourcesInfo:
|
39
|
+
existing_components = []
|
40
|
+
if isinstance(connector_details, UUID):
|
41
|
+
existing_components = depaginate(
|
42
|
+
Client().list_stack_components,
|
43
|
+
type=stack_component_type.value,
|
44
|
+
connector_id=connector_details,
|
45
|
+
flavor=flavor,
|
46
|
+
)
|
47
|
+
return ResourcesInfo(
|
48
|
+
flavor=flavor,
|
49
|
+
required_configuration=required_configuration,
|
50
|
+
flavor_display_name=flavor_display_name,
|
51
|
+
use_resource_value_as_fixed_config=use_resource_value_as_fixed_config,
|
52
|
+
accessible_by_service_connector=resource_ids,
|
53
|
+
connected_through_service_connector=existing_components,
|
54
|
+
)
|
55
|
+
|
56
|
+
|
57
|
+
def _raise_specific_cloud_exception_if_needed(
|
58
|
+
cloud_provider: str,
|
59
|
+
artifact_stores: List[ResourcesInfo],
|
60
|
+
orchestrators: List[ResourcesInfo],
|
61
|
+
container_registries: List[ResourcesInfo],
|
62
|
+
) -> None:
|
63
|
+
AWS_DOCS = (
|
64
|
+
"https://docs.zenml.io/how-to/auth-management/aws-service-connector"
|
65
|
+
)
|
66
|
+
GCP_DOCS = (
|
67
|
+
"https://docs.zenml.io/how-to/auth-management/gcp-service-connector"
|
68
|
+
)
|
69
|
+
|
70
|
+
if not artifact_stores:
|
71
|
+
error_msg = (
|
72
|
+
"We were unable to find any {obj_name} available "
|
73
|
+
"to configured service connector. Please, verify "
|
74
|
+
"that needed permission are granted for the "
|
75
|
+
"service connector.\nDocumentation for the "
|
76
|
+
"{obj_name} configuration can be found at "
|
77
|
+
"{docs}"
|
78
|
+
)
|
79
|
+
if cloud_provider == "aws":
|
80
|
+
raise ValueError(
|
81
|
+
error_msg.format(
|
82
|
+
obj_name="S3 Bucket", docs=f"{AWS_DOCS}#s3-bucket"
|
83
|
+
)
|
84
|
+
)
|
85
|
+
elif cloud_provider == "gcp":
|
86
|
+
raise ValueError(
|
87
|
+
error_msg.format(
|
88
|
+
obj_name="GCS Bucket", docs=f"{GCP_DOCS}#gcs-bucket"
|
89
|
+
)
|
90
|
+
)
|
91
|
+
elif cloud_provider == "azure":
|
92
|
+
pass
|
93
|
+
if not orchestrators:
|
94
|
+
error_msg = (
|
95
|
+
"We were unable to find any orchestrator engines "
|
96
|
+
"available to the service connector. Please, verify "
|
97
|
+
"that needed permission are granted for the "
|
98
|
+
"service connector.\nDocumentation for the Generic "
|
99
|
+
"{cloud_name} resource configuration can be found at "
|
100
|
+
"{gen_docs}\n Documentation for the {k8s_name} resource "
|
101
|
+
"configuration can be found at {k8s_docs}"
|
102
|
+
)
|
103
|
+
if cloud_provider == "aws":
|
104
|
+
raise ValueError(
|
105
|
+
error_msg.format(
|
106
|
+
cloud_name="AWS",
|
107
|
+
gen_docs=f"{AWS_DOCS}#generic-aws-resource",
|
108
|
+
k8s_name="Kubernetes",
|
109
|
+
k8s_docs=f"{AWS_DOCS}#eks-kubernetes-cluster",
|
110
|
+
)
|
111
|
+
)
|
112
|
+
|
113
|
+
elif cloud_provider == "gcp":
|
114
|
+
raise ValueError(
|
115
|
+
error_msg.format(
|
116
|
+
cloud_name="GCP",
|
117
|
+
gen_docs=f"{GCP_DOCS}#generic-gcp-resource",
|
118
|
+
k8s_name="GKE Kubernetes",
|
119
|
+
k8s_docs=f"{GCP_DOCS}#gke-kubernetes-cluster",
|
120
|
+
)
|
121
|
+
)
|
122
|
+
elif cloud_provider == "azure":
|
123
|
+
pass
|
124
|
+
if not container_registries:
|
125
|
+
error_msg = (
|
126
|
+
"We were unable to find any container registries "
|
127
|
+
"available to the service connector. Please, verify "
|
128
|
+
"that needed permission are granted for the "
|
129
|
+
"service connector.\nDocumentation for the {registry_name} "
|
130
|
+
"container registry resource configuration can "
|
131
|
+
"be found at {docs_link}"
|
132
|
+
)
|
133
|
+
if cloud_provider == "aws":
|
134
|
+
raise ValueError(
|
135
|
+
error_msg.format(
|
136
|
+
registry_name="ECR",
|
137
|
+
docs_link=f"{AWS_DOCS}#ecr-container-registry",
|
138
|
+
)
|
139
|
+
)
|
140
|
+
elif cloud_provider == "gcp":
|
141
|
+
raise ValueError(
|
142
|
+
error_msg.format(
|
143
|
+
registry_name="GCR",
|
144
|
+
docs_link=f"{GCP_DOCS}#gcr-container-registry",
|
145
|
+
)
|
146
|
+
)
|
147
|
+
elif cloud_provider == "azure":
|
148
|
+
pass
|
149
|
+
|
150
|
+
|
151
|
+
def get_resources_options_from_resource_model_for_full_stack(
|
152
|
+
connector_details: Union[UUID, ServiceConnectorInfo],
|
153
|
+
) -> ServiceConnectorResourcesInfo:
|
154
|
+
"""Get the resource options from the resource model for the full stack.
|
155
|
+
|
156
|
+
Args:
|
157
|
+
connector_details: The service connector details (UUID or Info).
|
158
|
+
|
159
|
+
Returns:
|
160
|
+
All available service connector resource options.
|
161
|
+
"""
|
162
|
+
client = Client()
|
163
|
+
zen_store = client.zen_store
|
164
|
+
|
165
|
+
can_generate_long_tokens = False
|
166
|
+
if isinstance(connector_details, UUID):
|
167
|
+
resource_model = zen_store.verify_service_connector(
|
168
|
+
connector_details,
|
169
|
+
list_resources=True,
|
170
|
+
)
|
171
|
+
can_generate_long_tokens = not zen_store.get_service_connector(
|
172
|
+
connector_details
|
173
|
+
).configuration.get("generate_temporary_tokens", True)
|
174
|
+
else:
|
175
|
+
resource_model = zen_store.verify_service_connector_config(
|
176
|
+
service_connector=ServiceConnectorRequest(
|
177
|
+
user=client.active_user.id,
|
178
|
+
workspace=client.active_workspace.id,
|
179
|
+
name="fake",
|
180
|
+
connector_type=connector_details.type,
|
181
|
+
auth_method=connector_details.auth_method,
|
182
|
+
configuration=connector_details.configuration,
|
183
|
+
secrets={},
|
184
|
+
labels={},
|
185
|
+
),
|
186
|
+
list_resources=True,
|
187
|
+
)
|
188
|
+
can_generate_long_tokens = True
|
189
|
+
|
190
|
+
resources = resource_model.resources
|
191
|
+
|
192
|
+
if isinstance(
|
193
|
+
resource_model.connector_type,
|
194
|
+
str,
|
195
|
+
):
|
196
|
+
connector_type = resource_model.connector_type
|
197
|
+
else:
|
198
|
+
connector_type = resource_model.connector_type.connector_type
|
199
|
+
|
200
|
+
artifact_stores: List[ResourcesInfo] = []
|
201
|
+
orchestrators: List[ResourcesInfo] = []
|
202
|
+
container_registries: List[ResourcesInfo] = []
|
203
|
+
|
204
|
+
if connector_type == "aws":
|
205
|
+
for each in resources:
|
206
|
+
if each.resource_ids:
|
207
|
+
if each.resource_type == "s3-bucket":
|
208
|
+
artifact_stores.append(
|
209
|
+
_prepare_resource_info(
|
210
|
+
connector_details=connector_details,
|
211
|
+
resource_ids=each.resource_ids,
|
212
|
+
stack_component_type=StackComponentType.ARTIFACT_STORE,
|
213
|
+
flavor="s3",
|
214
|
+
required_configuration={"path": "Path"},
|
215
|
+
use_resource_value_as_fixed_config=True,
|
216
|
+
flavor_display_name="S3 Bucket",
|
217
|
+
)
|
218
|
+
)
|
219
|
+
if each.resource_type == "aws-generic":
|
220
|
+
orchestrators.append(
|
221
|
+
_prepare_resource_info(
|
222
|
+
connector_details=connector_details,
|
223
|
+
resource_ids=each.resource_ids,
|
224
|
+
stack_component_type=StackComponentType.ORCHESTRATOR,
|
225
|
+
flavor="sagemaker",
|
226
|
+
required_configuration={
|
227
|
+
"execution_role": "execution role ARN"
|
228
|
+
},
|
229
|
+
flavor_display_name="AWS Sagemaker",
|
230
|
+
)
|
231
|
+
)
|
232
|
+
if can_generate_long_tokens:
|
233
|
+
orchestrators.append(
|
234
|
+
_prepare_resource_info(
|
235
|
+
connector_details=connector_details,
|
236
|
+
resource_ids=each.resource_ids,
|
237
|
+
stack_component_type=StackComponentType.ORCHESTRATOR,
|
238
|
+
flavor="vm_aws",
|
239
|
+
required_configuration={"region": "region"},
|
240
|
+
use_resource_value_as_fixed_config=True,
|
241
|
+
flavor_display_name="Skypilot (EC2)",
|
242
|
+
)
|
243
|
+
)
|
244
|
+
|
245
|
+
if each.resource_type == "kubernetes-cluster":
|
246
|
+
orchestrators.append(
|
247
|
+
_prepare_resource_info(
|
248
|
+
connector_details=connector_details,
|
249
|
+
resource_ids=each.resource_ids,
|
250
|
+
stack_component_type=StackComponentType.ORCHESTRATOR,
|
251
|
+
flavor="kubernetes",
|
252
|
+
required_configuration={},
|
253
|
+
flavor_display_name="Kubernetes",
|
254
|
+
)
|
255
|
+
)
|
256
|
+
if each.resource_type == "docker-registry":
|
257
|
+
container_registries.append(
|
258
|
+
_prepare_resource_info(
|
259
|
+
connector_details=connector_details,
|
260
|
+
resource_ids=each.resource_ids,
|
261
|
+
stack_component_type=StackComponentType.CONTAINER_REGISTRY,
|
262
|
+
flavor="aws",
|
263
|
+
required_configuration={"uri": "URI"},
|
264
|
+
use_resource_value_as_fixed_config=True,
|
265
|
+
flavor_display_name="ECR",
|
266
|
+
)
|
267
|
+
)
|
268
|
+
|
269
|
+
elif connector_type == "gcp":
|
270
|
+
for each in resources:
|
271
|
+
if each.resource_ids:
|
272
|
+
if each.resource_type == "gcs-bucket":
|
273
|
+
artifact_stores.append(
|
274
|
+
_prepare_resource_info(
|
275
|
+
connector_details=connector_details,
|
276
|
+
resource_ids=each.resource_ids,
|
277
|
+
stack_component_type=StackComponentType.ARTIFACT_STORE,
|
278
|
+
flavor="gcp",
|
279
|
+
required_configuration={},
|
280
|
+
flavor_display_name="GCS Bucket",
|
281
|
+
)
|
282
|
+
)
|
283
|
+
if each.resource_type == "gcp-generic":
|
284
|
+
orchestrators.append(
|
285
|
+
_prepare_resource_info(
|
286
|
+
connector_details=connector_details,
|
287
|
+
resource_ids=each.resource_ids,
|
288
|
+
stack_component_type=StackComponentType.ORCHESTRATOR,
|
289
|
+
flavor="vertex",
|
290
|
+
required_configuration={"location": "region name"},
|
291
|
+
flavor_display_name="Vertex AI",
|
292
|
+
)
|
293
|
+
)
|
294
|
+
if can_generate_long_tokens:
|
295
|
+
orchestrators.append(
|
296
|
+
_prepare_resource_info(
|
297
|
+
connector_details=connector_details,
|
298
|
+
resource_ids=each.resource_ids,
|
299
|
+
stack_component_type=StackComponentType.ORCHESTRATOR,
|
300
|
+
flavor="vm_gcp",
|
301
|
+
required_configuration={
|
302
|
+
"region": "region name"
|
303
|
+
},
|
304
|
+
flavor_display_name="Skypilot (Compute)",
|
305
|
+
)
|
306
|
+
)
|
307
|
+
|
308
|
+
if each.resource_type == "kubernetes-cluster":
|
309
|
+
orchestrators.append(
|
310
|
+
_prepare_resource_info(
|
311
|
+
connector_details=connector_details,
|
312
|
+
resource_ids=each.resource_ids,
|
313
|
+
stack_component_type=StackComponentType.ORCHESTRATOR,
|
314
|
+
flavor="kubernetes",
|
315
|
+
required_configuration={},
|
316
|
+
flavor_display_name="Kubernetes",
|
317
|
+
)
|
318
|
+
)
|
319
|
+
if each.resource_type == "docker-registry":
|
320
|
+
container_registries.append(
|
321
|
+
_prepare_resource_info(
|
322
|
+
connector_details=connector_details,
|
323
|
+
resource_ids=each.resource_ids,
|
324
|
+
stack_component_type=StackComponentType.CONTAINER_REGISTRY,
|
325
|
+
flavor="gcp",
|
326
|
+
required_configuration={"uri": "URI"},
|
327
|
+
use_resource_value_as_fixed_config=True,
|
328
|
+
flavor_display_name="GCR",
|
329
|
+
)
|
330
|
+
)
|
331
|
+
|
332
|
+
elif connector_type == "azure":
|
333
|
+
pass
|
334
|
+
|
335
|
+
_raise_specific_cloud_exception_if_needed(
|
336
|
+
cloud_provider=connector_type,
|
337
|
+
artifact_stores=artifact_stores,
|
338
|
+
orchestrators=orchestrators,
|
339
|
+
container_registries=container_registries,
|
340
|
+
)
|
341
|
+
|
342
|
+
return ServiceConnectorResourcesInfo(
|
343
|
+
connector_type=connector_type,
|
344
|
+
components_resources_info={
|
345
|
+
StackComponentType.ARTIFACT_STORE: artifact_stores,
|
346
|
+
StackComponentType.ORCHESTRATOR: orchestrators,
|
347
|
+
StackComponentType.CONTAINER_REGISTRY: container_registries,
|
348
|
+
},
|
349
|
+
)
|