zenml-nightly 0.62.0.dev20240726__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.
- zenml/VERSION +1 -1
- zenml/config/pipeline_spec.py +2 -2
- zenml/config/step_configurations.py +3 -3
- zenml/integrations/gcp/orchestrators/vertex_orchestrator.py +44 -28
- zenml/integrations/kubernetes/__init__.py +3 -2
- zenml/integrations/kubernetes/flavors/__init__.py +8 -0
- zenml/integrations/kubernetes/flavors/kubernetes_step_operator_flavor.py +166 -0
- zenml/integrations/kubernetes/step_operators/__init__.py +22 -0
- zenml/integrations/kubernetes/step_operators/kubernetes_step_operator.py +235 -0
- zenml/models/v2/core/code_repository.py +2 -2
- {zenml_nightly-0.62.0.dev20240726.dist-info → zenml_nightly-0.62.0.dev20240727.dist-info}/METADATA +1 -1
- {zenml_nightly-0.62.0.dev20240726.dist-info → zenml_nightly-0.62.0.dev20240727.dist-info}/RECORD +15 -12
- {zenml_nightly-0.62.0.dev20240726.dist-info → zenml_nightly-0.62.0.dev20240727.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.62.0.dev20240726.dist-info → zenml_nightly-0.62.0.dev20240727.dist-info}/WHEEL +0 -0
- {zenml_nightly-0.62.0.dev20240726.dist-info → zenml_nightly-0.62.0.dev20240727.dist-info}/entry_points.txt +0 -0
zenml/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.62.0.
|
1
|
+
0.62.0.dev20240727
|
zenml/config/pipeline_spec.py
CHANGED
@@ -16,7 +16,7 @@
|
|
16
16
|
import json
|
17
17
|
from typing import Any, Dict, List, Optional
|
18
18
|
|
19
|
-
from zenml.config.source import Source
|
19
|
+
from zenml.config.source import Source, SourceWithValidator
|
20
20
|
from zenml.config.step_configurations import StepSpec
|
21
21
|
from zenml.config.strict_base_model import StrictBaseModel
|
22
22
|
from zenml.utils.json_utils import pydantic_encoder
|
@@ -34,7 +34,7 @@ class PipelineSpec(StrictBaseModel):
|
|
34
34
|
# - 0.4: New Pipeline class, the upstream steps and
|
35
35
|
# inputs in the step specs refer to the pipeline parameter names
|
36
36
|
version: str = "0.4"
|
37
|
-
source: Optional[
|
37
|
+
source: Optional[SourceWithValidator] = None
|
38
38
|
parameters: Dict[str, Any] = {}
|
39
39
|
steps: List[StepSpec]
|
40
40
|
|
@@ -55,10 +55,10 @@ logger = get_logger(__name__)
|
|
55
55
|
class PartialArtifactConfiguration(StrictBaseModel):
|
56
56
|
"""Class representing a partial input/output artifact configuration."""
|
57
57
|
|
58
|
-
materializer_source: Optional[Tuple[
|
58
|
+
materializer_source: Optional[Tuple[SourceWithValidator, ...]] = None
|
59
59
|
# TODO: This could be moved to the `PipelineDeployment` as it's the same
|
60
60
|
# for all steps/outputs
|
61
|
-
default_materializer_source: Optional[
|
61
|
+
default_materializer_source: Optional[SourceWithValidator] = None
|
62
62
|
|
63
63
|
@model_validator(mode="before")
|
64
64
|
@classmethod
|
@@ -109,7 +109,7 @@ class PartialArtifactConfiguration(StrictBaseModel):
|
|
109
109
|
class ArtifactConfiguration(PartialArtifactConfiguration):
|
110
110
|
"""Class representing a complete input/output artifact configuration."""
|
111
111
|
|
112
|
-
materializer_source: Tuple[
|
112
|
+
materializer_source: Tuple[SourceWithValidator, ...]
|
113
113
|
|
114
114
|
@field_validator("materializer_source", mode="before")
|
115
115
|
@classmethod
|
@@ -363,7 +363,6 @@ class VertexOrchestrator(ContainerizedOrchestrator, GoogleCredentialsMixin):
|
|
363
363
|
pipeline_func
|
364
364
|
"""
|
365
365
|
step_name_to_dynamic_component: Dict[str, Any] = {}
|
366
|
-
node_selector_constraint: Optional[Tuple[str, str]] = None
|
367
366
|
|
368
367
|
for step_name, step in deployment.step_configurations.items():
|
369
368
|
image = self.get_image(
|
@@ -410,23 +409,17 @@ class VertexOrchestrator(ContainerizedOrchestrator, GoogleCredentialsMixin):
|
|
410
409
|
"Volume mounts are set but not supported in "
|
411
410
|
"Vertex with Kubeflow Pipelines 2.x. Ignoring..."
|
412
411
|
)
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
)
|
425
|
-
elif step_settings.node_selector_constraint:
|
426
|
-
node_selector_constraint = (
|
427
|
-
GKE_ACCELERATOR_NODE_SELECTOR_CONSTRAINT_LABEL,
|
428
|
-
step_settings.node_selector_constraint[1],
|
429
|
-
)
|
412
|
+
for key in pod_settings.node_selectors:
|
413
|
+
if (
|
414
|
+
key
|
415
|
+
!= GKE_ACCELERATOR_NODE_SELECTOR_CONSTRAINT_LABEL
|
416
|
+
):
|
417
|
+
logger.warning(
|
418
|
+
"Vertex only allows the %s node selector, "
|
419
|
+
"ignoring the node selector %s.",
|
420
|
+
GKE_ACCELERATOR_NODE_SELECTOR_CONSTRAINT_LABEL,
|
421
|
+
key,
|
422
|
+
)
|
430
423
|
|
431
424
|
step_name_to_dynamic_component[step_name] = dynamic_component
|
432
425
|
|
@@ -460,10 +453,33 @@ class VertexOrchestrator(ContainerizedOrchestrator, GoogleCredentialsMixin):
|
|
460
453
|
)
|
461
454
|
.after(*upstream_step_components)
|
462
455
|
)
|
456
|
+
|
457
|
+
step_settings = cast(
|
458
|
+
VertexOrchestratorSettings, self.get_settings(step)
|
459
|
+
)
|
460
|
+
pod_settings = step_settings.pod_settings
|
461
|
+
|
462
|
+
node_selector_constraint: Optional[Tuple[str, str]] = None
|
463
|
+
if pod_settings and (
|
464
|
+
GKE_ACCELERATOR_NODE_SELECTOR_CONSTRAINT_LABEL
|
465
|
+
in pod_settings.node_selectors.keys()
|
466
|
+
):
|
467
|
+
node_selector_constraint = (
|
468
|
+
GKE_ACCELERATOR_NODE_SELECTOR_CONSTRAINT_LABEL,
|
469
|
+
pod_settings.node_selectors[
|
470
|
+
GKE_ACCELERATOR_NODE_SELECTOR_CONSTRAINT_LABEL
|
471
|
+
],
|
472
|
+
)
|
473
|
+
elif step_settings.node_selector_constraint:
|
474
|
+
node_selector_constraint = (
|
475
|
+
GKE_ACCELERATOR_NODE_SELECTOR_CONSTRAINT_LABEL,
|
476
|
+
step_settings.node_selector_constraint[1],
|
477
|
+
)
|
478
|
+
|
463
479
|
self._configure_container_resources(
|
464
|
-
task,
|
465
|
-
step.config.resource_settings,
|
466
|
-
node_selector_constraint,
|
480
|
+
dynamic_component=task,
|
481
|
+
resource_settings=step.config.resource_settings,
|
482
|
+
node_selector_constraint=node_selector_constraint,
|
467
483
|
)
|
468
484
|
|
469
485
|
return dynamic_pipeline
|
@@ -731,20 +747,20 @@ class VertexOrchestrator(ContainerizedOrchestrator, GoogleCredentialsMixin):
|
|
731
747
|
)
|
732
748
|
|
733
749
|
if node_selector_constraint:
|
734
|
-
|
750
|
+
_, value = node_selector_constraint
|
735
751
|
if gpu_limit is not None and gpu_limit > 0:
|
736
752
|
dynamic_component = (
|
737
753
|
dynamic_component.set_accelerator_type(value)
|
738
754
|
.set_accelerator_limit(gpu_limit)
|
739
755
|
.set_gpu_limit(gpu_limit)
|
740
756
|
)
|
741
|
-
|
742
|
-
constraint_label
|
743
|
-
== GKE_ACCELERATOR_NODE_SELECTOR_CONSTRAINT_LABEL
|
744
|
-
and gpu_limit == 0
|
745
|
-
):
|
757
|
+
else:
|
746
758
|
logger.warning(
|
747
|
-
"
|
759
|
+
"Accelerator type %s specified, but the GPU limit is not "
|
760
|
+
"set or set to 0. The accelerator type will be ignored. "
|
761
|
+
"To fix this warning, either remove the specified "
|
762
|
+
"accelerator type or set the `gpu_count` using the "
|
763
|
+
"ResourceSettings (https://docs.zenml.io/how-to/training-with-gpus#specify-resource-requirements-for-steps)."
|
748
764
|
)
|
749
765
|
|
750
766
|
return dynamic_component
|
@@ -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
|
@@ -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)
|
@@ -18,7 +18,7 @@ from uuid import UUID
|
|
18
18
|
|
19
19
|
from pydantic import Field
|
20
20
|
|
21
|
-
from zenml.config.source import Source
|
21
|
+
from zenml.config.source import Source, SourceWithValidator
|
22
22
|
from zenml.constants import STR_FIELD_MAX_LENGTH, TEXT_FIELD_MAX_LENGTH
|
23
23
|
from zenml.models.v2.base.base import BaseUpdate
|
24
24
|
from zenml.models.v2.base.scoped import (
|
@@ -71,7 +71,7 @@ class CodeRepositoryUpdate(BaseUpdate):
|
|
71
71
|
description="Configuration for the code repository.",
|
72
72
|
default=None,
|
73
73
|
)
|
74
|
-
source: Optional[
|
74
|
+
source: Optional[SourceWithValidator] = Field(
|
75
75
|
description="The code repository source.", default=None
|
76
76
|
)
|
77
77
|
logo_url: Optional[str] = Field(
|
{zenml_nightly-0.62.0.dev20240726.dist-info → zenml_nightly-0.62.0.dev20240727.dist-info}/RECORD
RENAMED
@@ -6,7 +6,7 @@ RELEASE_NOTES.md,sha256=rF05H0U7U4lCYWomDqKhWdaWtOfCFSOf10LNBoFnl3Y,349994
|
|
6
6
|
ROADMAP.md,sha256=hiLSmr16BH8Dfx7SaQM4JcXCGCVl6mFZPFAwJeDTrJU,407
|
7
7
|
SECURITY.md,sha256=9DepA8y03yvCZLHEfcXLTDH4lUyKHquAdukBsccNN7c,682
|
8
8
|
zenml/README.md,sha256=827dekbOWAs1BpW7VF1a4d7EbwPbjwccX-2zdXBENZo,1777
|
9
|
-
zenml/VERSION,sha256=
|
9
|
+
zenml/VERSION,sha256=MMNIds-xBnzHeYmjMTYM_s5WfrIGWKYS5QNBbYJrXeU,19
|
10
10
|
zenml/__init__.py,sha256=6zRqB0FeBWX5E4Np9k9jzJgKaEHKvLFbtmOZQYrGpD8,2453
|
11
11
|
zenml/_hub/__init__.py,sha256=6qDzpQAAZa__Aiiz0mC1qM-9dw9_jk_v_aXeJknxbDE,644
|
12
12
|
zenml/_hub/client.py,sha256=Um2df1JO7-BmNm65efHSPpV5e0GITuoQJmod_wkdvbw,9136
|
@@ -84,7 +84,7 @@ zenml/config/docker_settings.py,sha256=3RxpPH53gX8_JPHvEUUlX-r-Q2E7DAiNNLYg5NLZD
|
|
84
84
|
zenml/config/global_config.py,sha256=i1CY0d4T2C0iVqLGVSgTJ90vLQsCZyCGRodaZ73XTi8,29069
|
85
85
|
zenml/config/pipeline_configurations.py,sha256=lqIShOr4Mha57dH-NOhSpemlTd3YafbXyR3FlQ6EVF0,2963
|
86
86
|
zenml/config/pipeline_run_configuration.py,sha256=oEKmk55ARJU1JIaAy6agDnyAIwirXM5I-PksVAILtM8,1955
|
87
|
-
zenml/config/pipeline_spec.py,sha256
|
87
|
+
zenml/config/pipeline_spec.py,sha256=uWpiIfznJvXAiKs1yMIUDT1h1tYEFNO-RDVTYcIv9CE,2821
|
88
88
|
zenml/config/resource_settings.py,sha256=bl3xahx--XS9p1CsTDSuvkZX9Oxb-Zj85UpefR8WrYs,3899
|
89
89
|
zenml/config/retry_config.py,sha256=4UH1xqw0G6fSEbXSNKfmiFEkwadxQef9BGMe3JBm6NI,929
|
90
90
|
zenml/config/schedule.py,sha256=QdezLjIONnEAVPrWFekbetNvkclx_5eJyhza0Prl5Y0,4944
|
@@ -93,7 +93,7 @@ zenml/config/secrets_store_config.py,sha256=y05zqyQhr_DGrs3IfBGa_FRoZ043hSYRT5wz
|
|
93
93
|
zenml/config/server_config.py,sha256=0kM2xyikvXxLaNTKFHHU3xG-niHAjoI3n6uOgH3ntkw,23339
|
94
94
|
zenml/config/settings_resolver.py,sha256=PR9BRm_x1dy7nVKa9UqpeFdck8IEATSW6aWT8FKd-DI,4278
|
95
95
|
zenml/config/source.py,sha256=rm3kbtO4L_Yqcr01qrzid3Cquh_iqKkvIBTmdijqAAA,6808
|
96
|
-
zenml/config/step_configurations.py,sha256=
|
96
|
+
zenml/config/step_configurations.py,sha256=TrPckVEgO7kyu9wL8PtL9eMIK_2TbbYvSBdE3ezUNTA,9689
|
97
97
|
zenml/config/step_run_info.py,sha256=KiVRSTtKmZ1GbvseDTap2imr7XwMHD3jSFVpyLNEK1I,1888
|
98
98
|
zenml/config/store_config.py,sha256=Cla5p5dTB6nNlo8_OZDs9hod5hspi64vxwtZj882XgU,3559
|
99
99
|
zenml/config/strict_base_model.py,sha256=iHnO9qOmLUP_eiy9IjRr3JjIs1l1I_CsRQ76EyAneYU,860
|
@@ -268,7 +268,7 @@ zenml/integrations/gcp/google_credentials_mixin.py,sha256=bPy3JYCCcyuTmPiVFqbY81
|
|
268
268
|
zenml/integrations/gcp/image_builders/__init__.py,sha256=2IvTL6U2YpUoxGQXeXew-6WFoL5hHIxkqr4DaA5Ez9w,786
|
269
269
|
zenml/integrations/gcp/image_builders/gcp_image_builder.py,sha256=dNpMJa1TITUOHSn5nj1gWTFwVNmvWz321A_JoTMOqCM,9114
|
270
270
|
zenml/integrations/gcp/orchestrators/__init__.py,sha256=6xLFJKZKQk73fHPF-XdpbQO87zjQNGTsNHjJjLfG_Kg,805
|
271
|
-
zenml/integrations/gcp/orchestrators/vertex_orchestrator.py,sha256=
|
271
|
+
zenml/integrations/gcp/orchestrators/vertex_orchestrator.py,sha256=SDXZqKS7RHaBCqWZIf_1CVqtT4hhs9dfyT7nx4meKq4,30189
|
272
272
|
zenml/integrations/gcp/service_connectors/__init__.py,sha256=fdydawaor8KAtMYvRZieiTuA1i5QATxXXgI-yV1lsn8,788
|
273
273
|
zenml/integrations/gcp/service_connectors/gcp_service_connector.py,sha256=ouP62k-zm3vBf6_XoLopSnjfp0rEzu19yeax53cRMk0,89990
|
274
274
|
zenml/integrations/gcp/step_operators/__init__.py,sha256=iPkob2LtPIQ-OHszhbNz_ojhoovL6SprmTx37It4EJ8,808
|
@@ -329,9 +329,10 @@ zenml/integrations/kubeflow/flavors/kubeflow_orchestrator_flavor.py,sha256=XRt1J
|
|
329
329
|
zenml/integrations/kubeflow/orchestrators/__init__.py,sha256=J879DBt9WbpojBTdOXy7CO6F5OuTMntBeZ8aUY2EGO8,821
|
330
330
|
zenml/integrations/kubeflow/orchestrators/kubeflow_orchestrator.py,sha256=AaAW5HqUeUDzqg_0Hk9SN_KFzEb0bLmD_woERIvRhDc,39165
|
331
331
|
zenml/integrations/kubeflow/orchestrators/local_deployment_utils.py,sha256=qszoOdvBpgIp40XkncphXAr9dRKnyZzGiz2mJ56bYmw,15448
|
332
|
-
zenml/integrations/kubernetes/__init__.py,sha256=
|
333
|
-
zenml/integrations/kubernetes/flavors/__init__.py,sha256=
|
332
|
+
zenml/integrations/kubernetes/__init__.py,sha256=UzU5CaogX6ud5ChCK8JSZ06eoW18eIudbgntgPijYSc,1853
|
333
|
+
zenml/integrations/kubernetes/flavors/__init__.py,sha256=a5gU45qCj3FkLwl_uVjlIkL_2F5DHk-w1gdcZrvVjBI,1266
|
334
334
|
zenml/integrations/kubernetes/flavors/kubernetes_orchestrator_flavor.py,sha256=8SHSyXg0vPxTHsJhQ_MJpSLkDAAw-YvMNUQcvIn5ogo,7689
|
335
|
+
zenml/integrations/kubernetes/flavors/kubernetes_step_operator_flavor.py,sha256=ILN-H4cl7z3i4ltb4UBs55wbtIo871b4ib28pYkQoyQ,5605
|
335
336
|
zenml/integrations/kubernetes/orchestrators/__init__.py,sha256=TJID3OTieZBox36WpQpzD0jdVRA_aZVcs_bNtfXS8ik,811
|
336
337
|
zenml/integrations/kubernetes/orchestrators/kube_utils.py,sha256=o5yfxHt3hPGc6ki5Pzr_FRcUk-KNUh2PTxRUlVnUVCI,10639
|
337
338
|
zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py,sha256=vuyHmaNwiVCPL7uN68HWa8gNmwNc0GH65NC48BfJgA8,20303
|
@@ -342,6 +343,8 @@ zenml/integrations/kubernetes/pod_settings.py,sha256=NMp4aHKRG29mh1Nq5uvV78Hzj1c
|
|
342
343
|
zenml/integrations/kubernetes/serialization_utils.py,sha256=cPSe4szdBLzDnUZT9nQc2CCA8h84aj5oTA8vsUE36ig,7000
|
343
344
|
zenml/integrations/kubernetes/service_connectors/__init__.py,sha256=Uf6zlHIapYrRDl3xOPWQ2jA7jt85SXx1U7DmSxzxTvQ,818
|
344
345
|
zenml/integrations/kubernetes/service_connectors/kubernetes_service_connector.py,sha256=IbVWwKEXG7PZay1lj3Q1VNAlap4qGxjhE4yIW9_0tIc,19525
|
346
|
+
zenml/integrations/kubernetes/step_operators/__init__.py,sha256=40utDPYAezxHsFgO0UUIT_6XpCDzDapje6OH951XsTs,806
|
347
|
+
zenml/integrations/kubernetes/step_operators/kubernetes_step_operator.py,sha256=sEz8IZkzo0qarnPCr8zarBPHR0T0i6HL8r9nSNHQBxI,8310
|
345
348
|
zenml/integrations/label_studio/__init__.py,sha256=tXmK0Wu_bFgtL7CqPPubSK99PaBZSyAu90aghHlXAek,1520
|
346
349
|
zenml/integrations/label_studio/annotators/__init__.py,sha256=YtOtSfS1_NBoLoXIygEerElBP1-B98UU0HOAEfzdRY0,821
|
347
350
|
zenml/integrations/label_studio/annotators/label_studio_annotator.py,sha256=VkuW4zsZhHz8__P9WTTLRTF-FOmoYB-_cFqBdu-PUyA,30498
|
@@ -585,7 +588,7 @@ zenml/models/v2/core/artifact.py,sha256=3MXZLTzTAOSMwdOmM-WhVERBx55ajhEd3xu2P-ru
|
|
585
588
|
zenml/models/v2/core/artifact_version.py,sha256=kmMmYWnN6DO6jPWb-Ct89d0p9Vj4c32_7MG6dIqToPs,18256
|
586
589
|
zenml/models/v2/core/artifact_visualization.py,sha256=milAHTpfuYd8Dx_oB4hcPYJL-TMoiU0FwUI0UhB3F-E,3115
|
587
590
|
zenml/models/v2/core/code_reference.py,sha256=9xy3Gzychq16lLUs01va3gGfwdu-AMrFgys_ua89uFY,3557
|
588
|
-
zenml/models/v2/core/code_repository.py,sha256=
|
591
|
+
zenml/models/v2/core/code_repository.py,sha256=B3Ox9E_Zr9L1pd1CG_sO1h5dELsK-rDxjDoLfNmIgAs,5959
|
589
592
|
zenml/models/v2/core/component.py,sha256=7isCreRBVolPxMp1VrkoNsyOA4LdSY5zMaXXVHbY_xo,13392
|
590
593
|
zenml/models/v2/core/device.py,sha256=Vlz4-6VwglKL3F-k7DObH-2LeHc8oRqKbYxSImHvDJk,13929
|
591
594
|
zenml/models/v2/core/event_source.py,sha256=kOexFvxj3iqsQGiGHPe549v9cSTo2GfsPCK05eDiNCw,6899
|
@@ -1337,8 +1340,8 @@ zenml/zen_stores/secrets_stores/service_connector_secrets_store.py,sha256=kPYX-Z
|
|
1337
1340
|
zenml/zen_stores/secrets_stores/sql_secrets_store.py,sha256=Bq1djrUP9saoD7vECjS7-TlA_7sjJGgw1talri4spjU,8656
|
1338
1341
|
zenml/zen_stores/sql_zen_store.py,sha256=j1D_vqLzg8lK2wT-a8moDOXJfTVGAa_oA0bb-RHWEVo,381780
|
1339
1342
|
zenml/zen_stores/zen_store_interface.py,sha256=ZcgPtlAMdO5die42Hwd6l8glxHYsxFjzuCBewIu5Hrs,91984
|
1340
|
-
zenml_nightly-0.62.0.
|
1341
|
-
zenml_nightly-0.62.0.
|
1342
|
-
zenml_nightly-0.62.0.
|
1343
|
-
zenml_nightly-0.62.0.
|
1344
|
-
zenml_nightly-0.62.0.
|
1343
|
+
zenml_nightly-0.62.0.dev20240727.dist-info/LICENSE,sha256=wbnfEnXnafPbqwANHkV6LUsPKOtdpsd-SNw37rogLtc,11359
|
1344
|
+
zenml_nightly-0.62.0.dev20240727.dist-info/METADATA,sha256=hq-MK42ldYlEHXVW87J6rPHtcrcKqJ1c2fDo3eMbKPE,21024
|
1345
|
+
zenml_nightly-0.62.0.dev20240727.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
1346
|
+
zenml_nightly-0.62.0.dev20240727.dist-info/entry_points.txt,sha256=QK3ETQE0YswAM2mWypNMOv8TLtr7EjnqAFq1br_jEFE,43
|
1347
|
+
zenml_nightly-0.62.0.dev20240727.dist-info/RECORD,,
|
{zenml_nightly-0.62.0.dev20240726.dist-info → zenml_nightly-0.62.0.dev20240727.dist-info}/LICENSE
RENAMED
File without changes
|
{zenml_nightly-0.62.0.dev20240726.dist-info → zenml_nightly-0.62.0.dev20240727.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|