zenml-nightly 0.70.0.dev20241126__py3-none-any.whl → 0.70.0.dev20241128__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 (38) hide show
  1. zenml/VERSION +1 -1
  2. zenml/artifact_stores/base_artifact_store.py +2 -2
  3. zenml/artifacts/utils.py +1 -1
  4. zenml/cli/__init__.py +3 -0
  5. zenml/cli/login.py +26 -0
  6. zenml/integrations/__init__.py +1 -0
  7. zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +14 -6
  8. zenml/integrations/constants.py +1 -0
  9. zenml/integrations/kubernetes/orchestrators/kube_utils.py +46 -2
  10. zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py +13 -2
  11. zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator_entrypoint.py +3 -1
  12. zenml/integrations/kubernetes/orchestrators/manifest_utils.py +3 -2
  13. zenml/integrations/kubernetes/step_operators/kubernetes_step_operator.py +3 -1
  14. zenml/integrations/modal/__init__.py +46 -0
  15. zenml/integrations/modal/flavors/__init__.py +26 -0
  16. zenml/integrations/modal/flavors/modal_step_operator_flavor.py +125 -0
  17. zenml/integrations/modal/step_operators/__init__.py +22 -0
  18. zenml/integrations/modal/step_operators/modal_step_operator.py +242 -0
  19. zenml/io/filesystem.py +2 -2
  20. zenml/io/local_filesystem.py +3 -3
  21. zenml/model/model.py +0 -82
  22. zenml/orchestrators/step_run_utils.py +8 -3
  23. zenml/orchestrators/step_runner.py +1 -1
  24. zenml/orchestrators/utils.py +24 -2
  25. zenml/steps/entrypoint_function_utils.py +3 -1
  26. zenml/zen_server/cloud_utils.py +3 -1
  27. zenml/zen_server/rbac/endpoint_utils.py +6 -4
  28. zenml/zen_server/rbac/models.py +3 -2
  29. zenml/zen_server/rbac/utils.py +4 -7
  30. zenml/zen_server/routers/users_endpoints.py +35 -37
  31. zenml/zen_server/routers/workspaces_endpoints.py +25 -36
  32. zenml/zen_stores/sql_zen_store.py +13 -0
  33. {zenml_nightly-0.70.0.dev20241126.dist-info → zenml_nightly-0.70.0.dev20241128.dist-info}/METADATA +1 -1
  34. {zenml_nightly-0.70.0.dev20241126.dist-info → zenml_nightly-0.70.0.dev20241128.dist-info}/RECORD +37 -33
  35. zenml/utils/cloud_utils.py +0 -40
  36. {zenml_nightly-0.70.0.dev20241126.dist-info → zenml_nightly-0.70.0.dev20241128.dist-info}/LICENSE +0 -0
  37. {zenml_nightly-0.70.0.dev20241126.dist-info → zenml_nightly-0.70.0.dev20241128.dist-info}/WHEEL +0 -0
  38. {zenml_nightly-0.70.0.dev20241126.dist-info → zenml_nightly-0.70.0.dev20241128.dist-info}/entry_points.txt +0 -0
zenml/VERSION CHANGED
@@ -1 +1 @@
1
- 0.70.0.dev20241126
1
+ 0.70.0.dev20241128
@@ -267,11 +267,11 @@ class BaseArtifactStore(StackComponent):
267
267
 
268
268
  # --- User interface ---
269
269
  @abstractmethod
270
- def open(self, name: PathType, mode: str = "r") -> Any:
270
+ def open(self, path: PathType, mode: str = "r") -> Any:
271
271
  """Open a file at the given path.
272
272
 
273
273
  Args:
274
- name: The path of the file to open.
274
+ path: The path of the file to open.
275
275
  mode: The mode to open the file.
276
276
 
277
277
  Returns:
zenml/artifacts/utils.py CHANGED
@@ -575,7 +575,7 @@ def download_artifact_files_from_response(
575
575
  )
576
576
  file_path = str(Path(artifact.uri) / file_str)
577
577
  with artifact_store.open(
578
- name=file_path, mode="rb"
578
+ file_path, mode="rb"
579
579
  ) as store_file:
580
580
  # Use a loop to read and write chunks of the file
581
581
  # instead of reading the entire file into memory
zenml/cli/__init__.py CHANGED
@@ -2242,6 +2242,9 @@ export ZENML_STORE_URL=https://...
2242
2242
  export ZENML_STORE_API_KEY=<API_KEY>
2243
2243
  ```
2244
2244
 
2245
+ You don't need to run `zenml login` after setting these two environment
2246
+ variables and can start interacting with your server right away.
2247
+
2245
2248
  To see all the service accounts you've created and their API keys, use the
2246
2249
  following commands:
2247
2250
 
zenml/cli/login.py CHANGED
@@ -14,6 +14,7 @@
14
14
  """CLI for managing ZenML server deployments."""
15
15
 
16
16
  import ipaddress
17
+ import os
17
18
  import re
18
19
  import sys
19
20
  import time
@@ -413,6 +414,27 @@ def connect_to_pro_server(
413
414
  cli_utils.declare(f"Connected to ZenML Pro server: {server.name}.")
414
415
 
415
416
 
417
+ def _fail_if_authentication_environment_variables_set() -> None:
418
+ """Fail if any of the authentication environment variables are set."""
419
+ environment_variables = [
420
+ "ZENML_STORE_URL",
421
+ "ZENML_STORE_API_KEY",
422
+ "ZENML_STORE_USERNAME",
423
+ "ZENML_STORE_PASSWORD",
424
+ ]
425
+
426
+ if any(env_var in os.environ for env_var in environment_variables):
427
+ cli_utils.error(
428
+ "You're running to login/logout while having one of the "
429
+ f"{environment_variables} environment variables set. "
430
+ "If you want to use those environment variables to authenticate "
431
+ "to your ZenML server, there is no need to login/logout, you can "
432
+ "start interacting with your server right away. If you want to use "
433
+ "the `zenml login` command for authentication, please unset these "
434
+ "environment variables first."
435
+ )
436
+
437
+
416
438
  @cli.command(
417
439
  "login",
418
440
  help=(
@@ -670,6 +692,8 @@ def login(
670
692
  dashboard on a public domain. Primarily used for accessing the
671
693
  dashboard in Colab.
672
694
  """
695
+ _fail_if_authentication_environment_variables_set()
696
+
673
697
  if local:
674
698
  if api_key:
675
699
  cli_utils.error(
@@ -849,6 +873,8 @@ def logout(
849
873
  """
850
874
  from zenml.login.credentials_store import get_credentials_store
851
875
 
876
+ _fail_if_authentication_environment_variables_set()
877
+
852
878
  credentials_store = get_credentials_store()
853
879
  gc = GlobalConfiguration()
854
880
  store_cfg = gc.store_configuration
@@ -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
- args_for_step_executor.setdefault(
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",
@@ -42,6 +42,7 @@ LANGCHAIN = "langchain"
42
42
  LIGHTGBM = "lightgbm"
43
43
  # LLAMA_INDEX = "llama_index"
44
44
  MLFLOW = "mlflow"
45
+ MODAL = "modal"
45
46
  NEPTUNE = "neptune"
46
47
  NEURAL_PROPHET = "neural_prophet"
47
48
  NUMPY = "numpy"
@@ -94,18 +94,62 @@ def load_kube_config(
94
94
  k8s_config.load_kube_config(context=context)
95
95
 
96
96
 
97
- def sanitize_pod_name(pod_name: str) -> str:
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
- return re.sub(r"[-]+", "-", pod_name)
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:
@@ -395,8 +395,19 @@ class KubernetesOrchestrator(ContainerizedOrchestrator):
395
395
  )
396
396
 
397
397
  pipeline_name = deployment.pipeline_configuration.name
398
- orchestrator_run_name = get_orchestrator_run_name(pipeline_name)
399
- pod_name = kube_utils.sanitize_pod_name(orchestrator_run_name)
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
+ )
400
411
 
401
412
  assert stack.container_registry
402
413
 
@@ -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(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
@@ -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
 
@@ -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(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
+ ]
@@ -0,0 +1,125 @@
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 step operator flavor."""
15
+
16
+ from typing import TYPE_CHECKING, Optional, Type
17
+
18
+ from zenml.config.base_settings import BaseSettings
19
+ from zenml.integrations.modal import MODAL_STEP_OPERATOR_FLAVOR
20
+ from zenml.step_operators import BaseStepOperatorConfig, BaseStepOperatorFlavor
21
+
22
+ if TYPE_CHECKING:
23
+ from zenml.integrations.modal.step_operators import ModalStepOperator
24
+
25
+
26
+ class ModalStepOperatorSettings(BaseSettings):
27
+ """Settings for the Modal step operator.
28
+
29
+ Specifying the region and cloud provider is only available for Enterprise
30
+ and Team plan customers.
31
+
32
+ Certain combinations of settings are not available. It is suggested to err
33
+ on the side of looser settings rather than more restrictive ones to avoid
34
+ pipeline execution failures. In the case of failures, however, Modal
35
+ provides detailed error messages that can help identify what is
36
+ incompatible. See more in the Modal docs at https://modal.com/docs/guide/region-selection.
37
+
38
+ Attributes:
39
+ gpu: The type of GPU to use for the step execution.
40
+ region: The region to use for the step execution.
41
+ cloud: The cloud provider to use for the step execution.
42
+ """
43
+
44
+ gpu: Optional[str] = None
45
+ region: Optional[str] = None
46
+ cloud: Optional[str] = None
47
+
48
+
49
+ class ModalStepOperatorConfig(
50
+ BaseStepOperatorConfig, ModalStepOperatorSettings
51
+ ):
52
+ """Configuration for the Modal step operator."""
53
+
54
+ @property
55
+ def is_remote(self) -> bool:
56
+ """Checks if this stack component is running remotely.
57
+
58
+ This designation is used to determine if the stack component can be
59
+ used with a local ZenML database or if it requires a remote ZenML
60
+ server.
61
+
62
+ Returns:
63
+ True if this config is for a remote component, False otherwise.
64
+ """
65
+ return True
66
+
67
+
68
+ class ModalStepOperatorFlavor(BaseStepOperatorFlavor):
69
+ """Modal step operator flavor."""
70
+
71
+ @property
72
+ def name(self) -> str:
73
+ """Name of the flavor.
74
+
75
+ Returns:
76
+ The name of the flavor.
77
+ """
78
+ return MODAL_STEP_OPERATOR_FLAVOR
79
+
80
+ @property
81
+ def docs_url(self) -> Optional[str]:
82
+ """A url to point at docs explaining this flavor.
83
+
84
+ Returns:
85
+ A flavor docs url.
86
+ """
87
+ return self.generate_default_docs_url()
88
+
89
+ @property
90
+ def sdk_docs_url(self) -> Optional[str]:
91
+ """A url to point at SDK docs explaining this flavor.
92
+
93
+ Returns:
94
+ A flavor SDK docs url.
95
+ """
96
+ return self.generate_default_sdk_docs_url()
97
+
98
+ @property
99
+ def logo_url(self) -> str:
100
+ """A url to represent the flavor in the dashboard.
101
+
102
+ Returns:
103
+ The flavor logo.
104
+ """
105
+ return "https://public-flavor-logos.s3.eu-central-1.amazonaws.com/step_operator/modal.png"
106
+
107
+ @property
108
+ def config_class(self) -> Type[ModalStepOperatorConfig]:
109
+ """Returns `ModalStepOperatorConfig` config class.
110
+
111
+ Returns:
112
+ The config class.
113
+ """
114
+ return ModalStepOperatorConfig
115
+
116
+ @property
117
+ def implementation_class(self) -> Type["ModalStepOperator"]:
118
+ """Implementation class for this flavor.
119
+
120
+ Returns:
121
+ The implementation class.
122
+ """
123
+ from zenml.integrations.modal.step_operators import ModalStepOperator
124
+
125
+ return ModalStepOperator
@@ -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
+ """Modal step operator."""
15
+
16
+ from zenml.integrations.modal.step_operators.modal_step_operator import (
17
+ ModalStepOperator,
18
+ )
19
+
20
+ __all__ = [
21
+ "ModalStepOperator",
22
+ ]