zenml-nightly 0.70.0.dev20241122__py3-none-any.whl → 0.70.0.dev20241201__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.
- zenml/VERSION +1 -1
- zenml/artifact_stores/base_artifact_store.py +2 -2
- zenml/artifacts/artifact_config.py +7 -1
- zenml/artifacts/utils.py +56 -31
- zenml/cli/__init__.py +18 -0
- zenml/cli/base.py +4 -4
- zenml/cli/login.py +26 -0
- zenml/cli/pipeline.py +80 -0
- zenml/cli/server.py +1 -1
- zenml/cli/service_connectors.py +3 -3
- zenml/cli/stack.py +0 -3
- zenml/cli/stack_components.py +0 -1
- zenml/cli/utils.py +0 -5
- zenml/client.py +8 -18
- zenml/config/compiler.py +12 -3
- zenml/config/pipeline_configurations.py +20 -0
- zenml/config/pipeline_run_configuration.py +1 -0
- zenml/config/step_configurations.py +21 -0
- zenml/constants.py +1 -0
- zenml/enums.py +1 -0
- zenml/image_builders/local_image_builder.py +13 -3
- zenml/integrations/__init__.py +1 -0
- zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +14 -6
- zenml/integrations/constants.py +1 -0
- zenml/integrations/feast/__init__.py +1 -1
- zenml/integrations/feast/feature_stores/feast_feature_store.py +13 -9
- zenml/integrations/kubernetes/orchestrators/kube_utils.py +54 -9
- zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py +65 -3
- zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator_entrypoint.py +14 -2
- zenml/integrations/kubernetes/orchestrators/manifest_utils.py +6 -5
- zenml/integrations/kubernetes/service_connectors/kubernetes_service_connector.py +2 -1
- zenml/integrations/kubernetes/step_operators/kubernetes_step_operator.py +3 -1
- zenml/integrations/modal/__init__.py +46 -0
- zenml/integrations/modal/flavors/__init__.py +26 -0
- zenml/integrations/modal/flavors/modal_step_operator_flavor.py +125 -0
- zenml/integrations/modal/step_operators/__init__.py +22 -0
- zenml/integrations/modal/step_operators/modal_step_operator.py +242 -0
- zenml/io/filesystem.py +2 -2
- zenml/io/local_filesystem.py +3 -3
- zenml/materializers/built_in_materializer.py +18 -1
- zenml/materializers/structured_string_materializer.py +8 -3
- zenml/model/model.py +11 -85
- zenml/model/utils.py +18 -16
- zenml/models/__init__.py +12 -1
- zenml/models/v2/core/artifact_version.py +6 -3
- zenml/models/v2/core/component.py +0 -22
- zenml/models/v2/core/model_version.py +6 -3
- zenml/models/v2/core/pipeline_run.py +19 -3
- zenml/models/v2/core/run_metadata.py +30 -9
- zenml/models/v2/core/service_connector.py +4 -0
- zenml/models/v2/core/step_run.py +6 -4
- zenml/models/v2/misc/run_metadata.py +38 -0
- zenml/models/v2/misc/server_models.py +23 -0
- zenml/orchestrators/input_utils.py +19 -6
- zenml/orchestrators/publish_utils.py +12 -5
- zenml/orchestrators/step_launcher.py +7 -3
- zenml/orchestrators/step_run_utils.py +26 -9
- zenml/orchestrators/step_runner.py +40 -3
- zenml/orchestrators/utils.py +24 -23
- zenml/pipelines/pipeline_decorator.py +4 -0
- zenml/pipelines/pipeline_definition.py +26 -8
- zenml/pipelines/run_utils.py +9 -5
- zenml/steps/base_step.py +11 -1
- zenml/steps/entrypoint_function_utils.py +7 -3
- zenml/steps/step_decorator.py +4 -0
- zenml/steps/utils.py +23 -7
- zenml/types.py +4 -0
- zenml/utils/metadata_utils.py +186 -153
- zenml/utils/string_utils.py +41 -16
- zenml/utils/visualization_utils.py +4 -1
- zenml/zen_server/cloud_utils.py +3 -1
- zenml/zen_server/deploy/helm/templates/_environment.tpl +117 -0
- zenml/zen_server/deploy/helm/templates/server-db-job.yaml +3 -14
- zenml/zen_server/deploy/helm/templates/server-deployment.yaml +16 -4
- zenml/zen_server/deploy/helm/templates/server-secret.yaml +2 -17
- zenml/zen_server/rbac/endpoint_utils.py +6 -4
- zenml/zen_server/rbac/models.py +3 -2
- zenml/zen_server/rbac/utils.py +4 -7
- zenml/zen_server/routers/server_endpoints.py +47 -0
- zenml/zen_server/routers/users_endpoints.py +35 -37
- zenml/zen_server/routers/workspaces_endpoints.py +44 -55
- zenml/zen_server/template_execution/utils.py +1 -0
- zenml/zen_server/zen_server_api.py +45 -6
- zenml/zen_stores/migrations/utils.py +40 -24
- zenml/zen_stores/migrations/versions/b73bc71f1106_remove_component_spec_path.py +36 -0
- zenml/zen_stores/migrations/versions/cc269488e5a9_separate_run_metadata.py +135 -0
- zenml/zen_stores/migrations/versions/ec6307720f92_simplify_model_version_links.py +7 -6
- zenml/zen_stores/rest_zen_store.py +38 -1
- zenml/zen_stores/schemas/__init__.py +5 -1
- zenml/zen_stores/schemas/artifact_schemas.py +12 -11
- zenml/zen_stores/schemas/component_schemas.py +0 -3
- zenml/zen_stores/schemas/model_schemas.py +13 -11
- zenml/zen_stores/schemas/pipeline_run_schemas.py +44 -16
- zenml/zen_stores/schemas/run_metadata_schemas.py +66 -31
- zenml/zen_stores/schemas/step_run_schemas.py +32 -12
- zenml/zen_stores/schemas/utils.py +47 -3
- zenml/zen_stores/sql_zen_store.py +130 -34
- {zenml_nightly-0.70.0.dev20241122.dist-info → zenml_nightly-0.70.0.dev20241201.dist-info}/METADATA +1 -1
- {zenml_nightly-0.70.0.dev20241122.dist-info → zenml_nightly-0.70.0.dev20241201.dist-info}/RECORD +102 -95
- zenml/utils/cloud_utils.py +0 -40
- {zenml_nightly-0.70.0.dev20241122.dist-info → zenml_nightly-0.70.0.dev20241201.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.70.0.dev20241122.dist-info → zenml_nightly-0.70.0.dev20241201.dist-info}/WHEEL +0 -0
- {zenml_nightly-0.70.0.dev20241122.dist-info → zenml_nightly-0.70.0.dev20241201.dist-info}/entry_points.txt +0 -0
zenml/constants.py
CHANGED
zenml/enums.py
CHANGED
@@ -68,15 +68,25 @@ class LocalImageBuilder(BaseImageBuilder):
|
|
68
68
|
)
|
69
69
|
|
70
70
|
if not docker_utils.check_docker():
|
71
|
+
# For 3., this is not supported by the python docker library
|
72
|
+
# https://github.com/docker/docker-py/issues/3146
|
71
73
|
raise RuntimeError(
|
72
|
-
"Unable to connect to the Docker daemon. There are
|
74
|
+
"Unable to connect to the Docker daemon. There are three "
|
73
75
|
"common causes for this:\n"
|
74
76
|
"1) The Docker daemon isn't running.\n"
|
75
77
|
"2) The Docker client isn't configured correctly. The client "
|
76
78
|
"loads its configuration from the following file: "
|
77
79
|
"$HOME/.docker/config.json. If your configuration file is in a "
|
78
|
-
"different location, set
|
79
|
-
"
|
80
|
+
"different location, set the `DOCKER_CONFIG` environment "
|
81
|
+
"variable to the directory that contains your `config.json` "
|
82
|
+
"file.\n"
|
83
|
+
"3) If your Docker CLI is working fine but you ran into this "
|
84
|
+
"issue, you might be using a non-default Docker context which "
|
85
|
+
"is not supported by the Docker python library. To verify "
|
86
|
+
"this, run `docker context ls` and check which context has a "
|
87
|
+
"`*` next to it. If this is not the `default` context, copy "
|
88
|
+
"the `DOCKER ENDPOINT` value of that context and set the "
|
89
|
+
"`DOCKER_HOST` environment variable to that value."
|
80
90
|
)
|
81
91
|
|
82
92
|
def build(
|
zenml/integrations/__init__.py
CHANGED
@@ -48,6 +48,7 @@ from zenml.integrations.lightgbm import LightGBMIntegration # noqa
|
|
48
48
|
|
49
49
|
# from zenml.integrations.llama_index import LlamaIndexIntegration # noqa
|
50
50
|
from zenml.integrations.mlflow import MlflowIntegration # noqa
|
51
|
+
from zenml.integrations.modal import ModalIntegration # noqa
|
51
52
|
from zenml.integrations.neptune import NeptuneIntegration # noqa
|
52
53
|
from zenml.integrations.neural_prophet import NeuralProphetIntegration # noqa
|
53
54
|
from zenml.integrations.numpy import NumpyIntegration # noqa
|
@@ -305,8 +305,21 @@ class SagemakerOrchestrator(ContainerizedOrchestrator):
|
|
305
305
|
# Retrieve Executor arguments provided in the Step settings.
|
306
306
|
if use_training_step:
|
307
307
|
args_for_step_executor = step_settings.estimator_args or {}
|
308
|
+
args_for_step_executor.setdefault(
|
309
|
+
"volume_size", step_settings.volume_size_in_gb
|
310
|
+
)
|
311
|
+
args_for_step_executor.setdefault(
|
312
|
+
"max_run", step_settings.max_runtime_in_seconds
|
313
|
+
)
|
308
314
|
else:
|
309
315
|
args_for_step_executor = step_settings.processor_args or {}
|
316
|
+
args_for_step_executor.setdefault(
|
317
|
+
"volume_size_in_gb", step_settings.volume_size_in_gb
|
318
|
+
)
|
319
|
+
args_for_step_executor.setdefault(
|
320
|
+
"max_runtime_in_seconds",
|
321
|
+
step_settings.max_runtime_in_seconds,
|
322
|
+
)
|
310
323
|
|
311
324
|
# Set default values from configured orchestrator Component to
|
312
325
|
# arguments to be used when they are not present in processor_args.
|
@@ -314,12 +327,7 @@ class SagemakerOrchestrator(ContainerizedOrchestrator):
|
|
314
327
|
"role",
|
315
328
|
step_settings.execution_role or self.config.execution_role,
|
316
329
|
)
|
317
|
-
|
318
|
-
"volume_size_in_gb", step_settings.volume_size_in_gb
|
319
|
-
)
|
320
|
-
args_for_step_executor.setdefault(
|
321
|
-
"max_runtime_in_seconds", step_settings.max_runtime_in_seconds
|
322
|
-
)
|
330
|
+
|
323
331
|
tags = step_settings.tags
|
324
332
|
args_for_step_executor.setdefault(
|
325
333
|
"tags",
|
zenml/integrations/constants.py
CHANGED
@@ -31,7 +31,7 @@ class FeastIntegration(Integration):
|
|
31
31
|
|
32
32
|
NAME = FEAST
|
33
33
|
# click is added to keep the feast click version in sync with ZenML's click
|
34
|
-
REQUIREMENTS = ["feast", "click>=8.0.1,<8.1.4"]
|
34
|
+
REQUIREMENTS = ["feast>=0.12.0", "click>=8.0.1,<8.1.4"]
|
35
35
|
REQUIREMENTS_IGNORED_ON_UNINSTALL = ["click", "pandas"]
|
36
36
|
|
37
37
|
@classmethod
|
@@ -16,7 +16,7 @@
|
|
16
16
|
from typing import Any, Dict, List, Union, cast
|
17
17
|
|
18
18
|
import pandas as pd
|
19
|
-
from feast import FeatureStore # type: ignore
|
19
|
+
from feast import FeatureService, FeatureStore # type: ignore
|
20
20
|
from feast.infra.registry.base_registry import BaseRegistry # type: ignore
|
21
21
|
|
22
22
|
from zenml.feature_stores.base_feature_store import BaseFeatureStore
|
@@ -43,14 +43,14 @@ class FeastFeatureStore(BaseFeatureStore):
|
|
43
43
|
def get_historical_features(
|
44
44
|
self,
|
45
45
|
entity_df: Union[pd.DataFrame, str],
|
46
|
-
features: List[str],
|
46
|
+
features: Union[List[str], FeatureService],
|
47
47
|
full_feature_names: bool = False,
|
48
48
|
) -> pd.DataFrame:
|
49
49
|
"""Returns the historical features for training or batch scoring.
|
50
50
|
|
51
51
|
Args:
|
52
52
|
entity_df: The entity DataFrame or entity name.
|
53
|
-
features: The features to retrieve.
|
53
|
+
features: The features to retrieve or a FeatureService.
|
54
54
|
full_feature_names: Whether to return the full feature names.
|
55
55
|
|
56
56
|
Raise:
|
@@ -70,14 +70,14 @@ class FeastFeatureStore(BaseFeatureStore):
|
|
70
70
|
def get_online_features(
|
71
71
|
self,
|
72
72
|
entity_rows: List[Dict[str, Any]],
|
73
|
-
features: List[str],
|
73
|
+
features: Union[List[str], FeatureService],
|
74
74
|
full_feature_names: bool = False,
|
75
75
|
) -> Dict[str, Any]:
|
76
76
|
"""Returns the latest online feature data.
|
77
77
|
|
78
78
|
Args:
|
79
79
|
entity_rows: The entity rows to retrieve.
|
80
|
-
features: The features to retrieve.
|
80
|
+
features: The features to retrieve or a FeatureService.
|
81
81
|
full_feature_names: Whether to return the full feature names.
|
82
82
|
|
83
83
|
Raise:
|
@@ -118,17 +118,21 @@ class FeastFeatureStore(BaseFeatureStore):
|
|
118
118
|
fs = FeatureStore(repo_path=self.config.feast_repo)
|
119
119
|
return [ds.name for ds in fs.list_entities()]
|
120
120
|
|
121
|
-
def get_feature_services(self) -> List[
|
122
|
-
"""Returns the feature
|
121
|
+
def get_feature_services(self) -> List[FeatureService]:
|
122
|
+
"""Returns the feature services.
|
123
123
|
|
124
124
|
Raise:
|
125
125
|
ConnectionError: If the online component (Redis) is not available.
|
126
126
|
|
127
127
|
Returns:
|
128
|
-
The feature
|
128
|
+
The feature services.
|
129
129
|
"""
|
130
130
|
fs = FeatureStore(repo_path=self.config.feast_repo)
|
131
|
-
|
131
|
+
feature_services: List[FeatureService] = list(
|
132
|
+
fs.list_feature_services()
|
133
|
+
)
|
134
|
+
|
135
|
+
return feature_services
|
132
136
|
|
133
137
|
def get_feature_views(self) -> List[str]:
|
134
138
|
"""Returns the feature view names.
|
@@ -42,8 +42,8 @@ from kubernetes import config as k8s_config
|
|
42
42
|
from kubernetes.client.rest import ApiException
|
43
43
|
|
44
44
|
from zenml.integrations.kubernetes.orchestrators.manifest_utils import (
|
45
|
-
build_cluster_role_binding_manifest_for_service_account,
|
46
45
|
build_namespace_manifest,
|
46
|
+
build_role_binding_manifest_for_service_account,
|
47
47
|
build_service_account_manifest,
|
48
48
|
)
|
49
49
|
from zenml.logger import get_logger
|
@@ -94,18 +94,62 @@ def load_kube_config(
|
|
94
94
|
k8s_config.load_kube_config(context=context)
|
95
95
|
|
96
96
|
|
97
|
-
def
|
97
|
+
def calculate_max_pod_name_length_for_namespace(namespace: str) -> int:
|
98
|
+
"""Calculate the max pod length for a certain namespace.
|
99
|
+
|
100
|
+
Args:
|
101
|
+
namespace: The namespace in which the pod will be created.
|
102
|
+
|
103
|
+
Returns:
|
104
|
+
The maximum pod name length.
|
105
|
+
"""
|
106
|
+
# Kubernetes allows Pod names to have 253 characters. However, when
|
107
|
+
# creating a pod they try to create a log file which is called
|
108
|
+
# <NAMESPACE>_<POD_NAME>_<UUID>, which adds additional characters and
|
109
|
+
# runs into filesystem limitations for filename lengths (255). We therefore
|
110
|
+
# subtract the length of a UUID (36), the two underscores and the
|
111
|
+
# namespace length from the max filename length.
|
112
|
+
return 255 - 38 - len(namespace)
|
113
|
+
|
114
|
+
|
115
|
+
def sanitize_pod_name(pod_name: str, namespace: str) -> str:
|
98
116
|
"""Sanitize pod names so they conform to Kubernetes pod naming convention.
|
99
117
|
|
100
118
|
Args:
|
101
119
|
pod_name: Arbitrary input pod name.
|
120
|
+
namespace: Namespace in which the Pod will be created.
|
102
121
|
|
103
122
|
Returns:
|
104
123
|
Sanitized pod name.
|
105
124
|
"""
|
125
|
+
# https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-subdomain-names
|
106
126
|
pod_name = re.sub(r"[^a-z0-9-]", "-", pod_name.lower())
|
107
127
|
pod_name = re.sub(r"^[-]+", "", pod_name)
|
108
|
-
|
128
|
+
pod_name = re.sub(r"[-]+$", "", pod_name)
|
129
|
+
pod_name = re.sub(r"[-]+", "-", pod_name)
|
130
|
+
|
131
|
+
allowed_length = calculate_max_pod_name_length_for_namespace(
|
132
|
+
namespace=namespace
|
133
|
+
)
|
134
|
+
return pod_name[:allowed_length]
|
135
|
+
|
136
|
+
|
137
|
+
def sanitize_label(label: str) -> str:
|
138
|
+
"""Sanitize a label for a Kubernetes resource.
|
139
|
+
|
140
|
+
Args:
|
141
|
+
label: The label to sanitize.
|
142
|
+
|
143
|
+
Returns:
|
144
|
+
The sanitized label.
|
145
|
+
"""
|
146
|
+
# https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#rfc-1035-label-names
|
147
|
+
label = re.sub(r"[^a-z0-9-]", "-", label.lower())
|
148
|
+
label = re.sub(r"^[-]+", "", label)
|
149
|
+
label = re.sub(r"[-]+$", "", label)
|
150
|
+
label = re.sub(r"[-]+", "-", label)
|
151
|
+
|
152
|
+
return label[:63]
|
109
153
|
|
110
154
|
|
111
155
|
def pod_is_not_pending(pod: k8s_client.V1Pod) -> bool:
|
@@ -288,7 +332,7 @@ def create_edit_service_account(
|
|
288
332
|
rbac_api: k8s_client.RbacAuthorizationV1Api,
|
289
333
|
service_account_name: str,
|
290
334
|
namespace: str,
|
291
|
-
|
335
|
+
role_binding_name: str = "zenml-edit",
|
292
336
|
) -> None:
|
293
337
|
"""Create a new Kubernetes service account with "edit" rights.
|
294
338
|
|
@@ -297,16 +341,17 @@ def create_edit_service_account(
|
|
297
341
|
rbac_api: Client of Rbac Authorization V1 API of Kubernetes API.
|
298
342
|
service_account_name: Name of the service account.
|
299
343
|
namespace: Kubernetes namespace. Defaults to "default".
|
300
|
-
|
301
|
-
Defaults to "zenml-edit".
|
344
|
+
role_binding_name: Name of the role binding. Defaults to "zenml-edit".
|
302
345
|
"""
|
303
|
-
|
304
|
-
name=
|
346
|
+
rb_manifest = build_role_binding_manifest_for_service_account(
|
347
|
+
name=role_binding_name,
|
305
348
|
role_name="edit",
|
306
349
|
service_account_name=service_account_name,
|
307
350
|
namespace=namespace,
|
308
351
|
)
|
309
|
-
_if_not_exists(rbac_api.
|
352
|
+
_if_not_exists(rbac_api.create_namespaced_role_binding)(
|
353
|
+
namespace=namespace, body=rb_manifest
|
354
|
+
)
|
310
355
|
|
311
356
|
sa_manifest = build_service_account_manifest(
|
312
357
|
name=service_account_name, namespace=namespace
|
@@ -59,6 +59,7 @@ from zenml.integrations.kubernetes.orchestrators.manifest_utils import (
|
|
59
59
|
build_cron_job_manifest,
|
60
60
|
build_pod_manifest,
|
61
61
|
)
|
62
|
+
from zenml.integrations.kubernetes.pod_settings import KubernetesPodSettings
|
62
63
|
from zenml.logger import get_logger
|
63
64
|
from zenml.orchestrators import ContainerizedOrchestrator
|
64
65
|
from zenml.orchestrators.utils import get_orchestrator_run_name
|
@@ -328,6 +329,45 @@ class KubernetesOrchestrator(ContainerizedOrchestrator):
|
|
328
329
|
custom_validation_function=_validate_local_requirements,
|
329
330
|
)
|
330
331
|
|
332
|
+
@classmethod
|
333
|
+
def apply_default_resource_requests(
|
334
|
+
cls,
|
335
|
+
memory: str,
|
336
|
+
cpu: Optional[str] = None,
|
337
|
+
pod_settings: Optional[KubernetesPodSettings] = None,
|
338
|
+
) -> KubernetesPodSettings:
|
339
|
+
"""Applies default resource requests to a pod settings object.
|
340
|
+
|
341
|
+
Args:
|
342
|
+
memory: The memory resource request.
|
343
|
+
cpu: The CPU resource request.
|
344
|
+
pod_settings: The pod settings to update. A new one will be created
|
345
|
+
if not provided.
|
346
|
+
|
347
|
+
Returns:
|
348
|
+
The new or updated pod settings.
|
349
|
+
"""
|
350
|
+
resources = {
|
351
|
+
"requests": {"memory": memory},
|
352
|
+
}
|
353
|
+
if cpu:
|
354
|
+
resources["requests"]["cpu"] = cpu
|
355
|
+
if not pod_settings:
|
356
|
+
pod_settings = KubernetesPodSettings(resources=resources)
|
357
|
+
elif not pod_settings.resources:
|
358
|
+
# We can't update the pod settings in place (because it's a frozen
|
359
|
+
# pydantic model), so we have to create a new one.
|
360
|
+
pod_settings = KubernetesPodSettings(
|
361
|
+
**pod_settings.model_dump(exclude_unset=True),
|
362
|
+
resources=resources,
|
363
|
+
)
|
364
|
+
else:
|
365
|
+
set_requests = pod_settings.resources.get("requests", {})
|
366
|
+
resources["requests"].update(set_requests)
|
367
|
+
pod_settings.resources["requests"] = resources["requests"]
|
368
|
+
|
369
|
+
return pod_settings
|
370
|
+
|
331
371
|
def prepare_or_run_pipeline(
|
332
372
|
self,
|
333
373
|
deployment: "PipelineDeploymentResponse",
|
@@ -355,8 +395,19 @@ class KubernetesOrchestrator(ContainerizedOrchestrator):
|
|
355
395
|
)
|
356
396
|
|
357
397
|
pipeline_name = deployment.pipeline_configuration.name
|
358
|
-
|
359
|
-
|
398
|
+
|
399
|
+
# We already make sure the orchestrator run name has the correct length
|
400
|
+
# to make sure we don't cut off the randomized suffix later when
|
401
|
+
# sanitizing the pod name. This avoids any pod naming collisions.
|
402
|
+
max_length = kube_utils.calculate_max_pod_name_length_for_namespace(
|
403
|
+
namespace=self.config.kubernetes_namespace
|
404
|
+
)
|
405
|
+
orchestrator_run_name = get_orchestrator_run_name(
|
406
|
+
pipeline_name, max_length=max_length
|
407
|
+
)
|
408
|
+
pod_name = kube_utils.sanitize_pod_name(
|
409
|
+
orchestrator_run_name, namespace=self.config.kubernetes_namespace
|
410
|
+
)
|
360
411
|
|
361
412
|
assert stack.container_registry
|
362
413
|
|
@@ -422,6 +473,17 @@ class KubernetesOrchestrator(ContainerizedOrchestrator):
|
|
422
473
|
)
|
423
474
|
return
|
424
475
|
|
476
|
+
# We set some default minimum resource requests for the orchestrator pod
|
477
|
+
# here if the user has not specified any, because the orchestrator pod
|
478
|
+
# takes up some memory resources itself and, if not specified, the pod
|
479
|
+
# will be scheduled on any node regardless of available memory and risk
|
480
|
+
# negatively impacting or even crashing the node due to memory pressure.
|
481
|
+
orchestrator_pod_settings = self.apply_default_resource_requests(
|
482
|
+
memory="400Mi",
|
483
|
+
cpu="100m",
|
484
|
+
pod_settings=settings.orchestrator_pod_settings,
|
485
|
+
)
|
486
|
+
|
425
487
|
# Create and run the orchestrator pod.
|
426
488
|
pod_manifest = build_pod_manifest(
|
427
489
|
run_name=orchestrator_run_name,
|
@@ -431,7 +493,7 @@ class KubernetesOrchestrator(ContainerizedOrchestrator):
|
|
431
493
|
command=command,
|
432
494
|
args=args,
|
433
495
|
privileged=False,
|
434
|
-
pod_settings=
|
496
|
+
pod_settings=orchestrator_pod_settings,
|
435
497
|
service_account_name=service_account_name,
|
436
498
|
env=environment,
|
437
499
|
mount_local_stores=self.config.is_local,
|
@@ -90,7 +90,9 @@ def main() -> None:
|
|
90
90
|
"""
|
91
91
|
# Define Kubernetes pod name.
|
92
92
|
pod_name = f"{orchestrator_run_id}-{step_name}"
|
93
|
-
pod_name = kube_utils.sanitize_pod_name(
|
93
|
+
pod_name = kube_utils.sanitize_pod_name(
|
94
|
+
pod_name, namespace=args.kubernetes_namespace
|
95
|
+
)
|
94
96
|
|
95
97
|
image = KubernetesOrchestrator.get_image(
|
96
98
|
deployment=deployment_config, step_name=step_name
|
@@ -116,6 +118,16 @@ def main() -> None:
|
|
116
118
|
env = get_config_environment_vars()
|
117
119
|
env[ENV_ZENML_KUBERNETES_RUN_ID] = orchestrator_run_id
|
118
120
|
|
121
|
+
# We set some default minimum memory resource requests for the step pod
|
122
|
+
# here if the user has not specified any, because the step pod takes up
|
123
|
+
# some memory resources itself and, if not specified, the pod will be
|
124
|
+
# scheduled on any node regardless of available memory and risk
|
125
|
+
# negatively impacting or even crashing the node due to memory pressure.
|
126
|
+
pod_settings = KubernetesOrchestrator.apply_default_resource_requests(
|
127
|
+
memory="400Mi",
|
128
|
+
pod_settings=settings.pod_settings,
|
129
|
+
)
|
130
|
+
|
119
131
|
# Define Kubernetes pod manifest.
|
120
132
|
pod_manifest = build_pod_manifest(
|
121
133
|
pod_name=pod_name,
|
@@ -126,7 +138,7 @@ def main() -> None:
|
|
126
138
|
args=step_args,
|
127
139
|
env=env,
|
128
140
|
privileged=settings.privileged,
|
129
|
-
pod_settings=
|
141
|
+
pod_settings=pod_settings,
|
130
142
|
service_account_name=settings.step_pod_service_account_name
|
131
143
|
or settings.service_account_name,
|
132
144
|
mount_local_stores=mount_local_stores,
|
@@ -25,6 +25,7 @@ from zenml.constants import ENV_ZENML_ENABLE_REPO_INIT_WARNINGS
|
|
25
25
|
from zenml.integrations.airflow.orchestrators.dag_generator import (
|
26
26
|
ENV_ZENML_LOCAL_STORES_PATH,
|
27
27
|
)
|
28
|
+
from zenml.integrations.kubernetes.orchestrators import kube_utils
|
28
29
|
from zenml.integrations.kubernetes.pod_settings import KubernetesPodSettings
|
29
30
|
|
30
31
|
|
@@ -167,8 +168,8 @@ def build_pod_manifest(
|
|
167
168
|
# Add run_name and pipeline_name to the labels
|
168
169
|
labels.update(
|
169
170
|
{
|
170
|
-
"run": run_name,
|
171
|
-
"pipeline": pipeline_name,
|
171
|
+
"run": kube_utils.sanitize_label(run_name),
|
172
|
+
"pipeline": kube_utils.sanitize_label(pipeline_name),
|
172
173
|
}
|
173
174
|
)
|
174
175
|
|
@@ -304,13 +305,13 @@ def build_cron_job_manifest(
|
|
304
305
|
return job_manifest
|
305
306
|
|
306
307
|
|
307
|
-
def
|
308
|
+
def build_role_binding_manifest_for_service_account(
|
308
309
|
name: str,
|
309
310
|
role_name: str,
|
310
311
|
service_account_name: str,
|
311
312
|
namespace: str = "default",
|
312
313
|
) -> Dict[str, Any]:
|
313
|
-
"""Build a manifest for a
|
314
|
+
"""Build a manifest for a role binding of a service account.
|
314
315
|
|
315
316
|
Args:
|
316
317
|
name: Name of the cluster role binding.
|
@@ -323,7 +324,7 @@ def build_cluster_role_binding_manifest_for_service_account(
|
|
323
324
|
"""
|
324
325
|
return {
|
325
326
|
"apiVersion": "rbac.authorization.k8s.io/v1",
|
326
|
-
"kind": "
|
327
|
+
"kind": "RoleBinding",
|
327
328
|
"metadata": {"name": name},
|
328
329
|
"subjects": [
|
329
330
|
{
|
@@ -26,6 +26,7 @@ from typing import Any, List, Optional
|
|
26
26
|
from kubernetes import client as k8s_client
|
27
27
|
from kubernetes import config as k8s_config
|
28
28
|
from pydantic import Field
|
29
|
+
from urllib3.exceptions import HTTPError
|
29
30
|
|
30
31
|
from zenml.constants import KUBERNETES_CLUSTER_RESOURCE_TYPE
|
31
32
|
from zenml.exceptions import AuthorizationException
|
@@ -572,7 +573,7 @@ class KubernetesServiceConnector(ServiceConnector):
|
|
572
573
|
auth_settings=["BearerToken"],
|
573
574
|
response_type="VersionInfo",
|
574
575
|
)
|
575
|
-
except k8s_client.ApiException as err:
|
576
|
+
except (k8s_client.ApiException, HTTPError) as err:
|
576
577
|
raise AuthorizationException(
|
577
578
|
f"failed to verify Kubernetes cluster access: {err}"
|
578
579
|
) from err
|
@@ -197,7 +197,9 @@ class KubernetesStepOperator(BaseStepOperator):
|
|
197
197
|
)
|
198
198
|
|
199
199
|
pod_name = f"{info.run_name}_{info.pipeline_step_name}"
|
200
|
-
pod_name = kube_utils.sanitize_pod_name(
|
200
|
+
pod_name = kube_utils.sanitize_pod_name(
|
201
|
+
pod_name, namespace=self.config.kubernetes_namespace
|
202
|
+
)
|
201
203
|
|
202
204
|
command = entrypoint_command[:3]
|
203
205
|
args = entrypoint_command[3:]
|
@@ -0,0 +1,46 @@
|
|
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
|
+
"""Modal integration for cloud-native step execution.
|
15
|
+
|
16
|
+
The Modal integration sub-module provides a step operator flavor that allows
|
17
|
+
executing steps on Modal's cloud infrastructure.
|
18
|
+
"""
|
19
|
+
from typing import List, Type
|
20
|
+
|
21
|
+
from zenml.integrations.constants import MODAL
|
22
|
+
from zenml.integrations.integration import Integration
|
23
|
+
from zenml.stack import Flavor
|
24
|
+
|
25
|
+
MODAL_STEP_OPERATOR_FLAVOR = "modal"
|
26
|
+
|
27
|
+
|
28
|
+
class ModalIntegration(Integration):
|
29
|
+
"""Definition of Modal integration for ZenML."""
|
30
|
+
|
31
|
+
NAME = MODAL
|
32
|
+
REQUIREMENTS = ["modal>=0.64.49,<1"]
|
33
|
+
|
34
|
+
@classmethod
|
35
|
+
def flavors(cls) -> List[Type[Flavor]]:
|
36
|
+
"""Declare the stack component flavors for the Modal integration.
|
37
|
+
|
38
|
+
Returns:
|
39
|
+
List of new stack component flavors.
|
40
|
+
"""
|
41
|
+
from zenml.integrations.modal.flavors import ModalStepOperatorFlavor
|
42
|
+
|
43
|
+
return [ModalStepOperatorFlavor]
|
44
|
+
|
45
|
+
|
46
|
+
ModalIntegration.check_installation()
|
@@ -0,0 +1,26 @@
|
|
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
|
+
"""Modal integration flavors."""
|
15
|
+
|
16
|
+
from zenml.integrations.modal.flavors.modal_step_operator_flavor import (
|
17
|
+
ModalStepOperatorConfig,
|
18
|
+
ModalStepOperatorFlavor,
|
19
|
+
ModalStepOperatorSettings,
|
20
|
+
)
|
21
|
+
|
22
|
+
__all__ = [
|
23
|
+
"ModalStepOperatorConfig",
|
24
|
+
"ModalStepOperatorFlavor",
|
25
|
+
"ModalStepOperatorSettings",
|
26
|
+
]
|