zenml-nightly 0.63.0.dev20240801__py3-none-any.whl → 0.64.0.dev20240809__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 +2 -2
- RELEASE_NOTES.md +79 -0
- zenml/VERSION +1 -1
- zenml/__init__.py +0 -4
- zenml/analytics/enums.py +0 -6
- zenml/cli/__init__.py +0 -61
- zenml/cli/base.py +1 -1
- zenml/cli/web_login.py +8 -0
- zenml/client.py +0 -4
- zenml/config/build_configuration.py +43 -17
- zenml/config/docker_settings.py +80 -57
- zenml/config/source.py +58 -0
- zenml/constants.py +9 -2
- zenml/entrypoints/base_entrypoint_configuration.py +53 -8
- zenml/enums.py +1 -1
- zenml/environment.py +25 -9
- zenml/image_builders/base_image_builder.py +1 -1
- zenml/image_builders/build_context.py +25 -72
- zenml/integrations/azure/__init__.py +4 -0
- zenml/integrations/azure/flavors/__init__.py +11 -0
- zenml/integrations/azure/flavors/azureml_orchestrator_flavor.py +263 -0
- zenml/{_hub → integrations/azure/orchestrators}/__init__.py +7 -2
- zenml/integrations/azure/orchestrators/azureml_orchestrator.py +544 -0
- zenml/integrations/azure/orchestrators/azureml_orchestrator_entrypoint_config.py +86 -0
- zenml/integrations/azure/step_operators/azureml_step_operator.py +3 -0
- zenml/integrations/databricks/flavors/databricks_orchestrator_flavor.py +9 -0
- zenml/integrations/gcp/orchestrators/vertex_orchestrator.py +7 -2
- zenml/integrations/gcp/service_connectors/gcp_service_connector.py +123 -6
- zenml/integrations/kaniko/image_builders/kaniko_image_builder.py +1 -1
- zenml/integrations/mlflow/__init__.py +1 -1
- zenml/integrations/mlflow/experiment_trackers/mlflow_experiment_tracker.py +3 -1
- zenml/integrations/mlflow/flavors/mlflow_experiment_tracker_flavor.py +3 -0
- zenml/logger.py +13 -0
- zenml/models/__init__.py +0 -12
- zenml/models/v2/core/pipeline_deployment.py +21 -29
- zenml/models/v2/core/pipeline_run.py +13 -0
- zenml/models/v2/core/server_settings.py +12 -0
- zenml/models/v2/core/user.py +0 -21
- zenml/models/v2/misc/server_models.py +7 -1
- zenml/models/v2/misc/user_auth.py +0 -7
- zenml/new/pipelines/build_utils.py +193 -38
- zenml/new/pipelines/code_archive.py +157 -0
- zenml/new/pipelines/pipeline.py +29 -2
- zenml/new/pipelines/run_utils.py +67 -1
- zenml/service_connectors/service_connector_utils.py +14 -0
- zenml/stack_deployments/aws_stack_deployment.py +26 -3
- zenml/stack_deployments/azure_stack_deployment.py +11 -6
- zenml/stack_deployments/gcp_stack_deployment.py +24 -2
- zenml/stack_deployments/stack_deployment.py +17 -2
- zenml/steps/base_step.py +3 -0
- zenml/utils/archivable.py +149 -0
- zenml/utils/code_utils.py +244 -0
- zenml/utils/notebook_utils.py +122 -0
- zenml/utils/pipeline_docker_image_builder.py +3 -96
- zenml/utils/source_utils.py +109 -1
- zenml/zen_server/dashboard/assets/{404-CI13wQp4.js → 404-CRAA_Lew.js} +1 -1
- zenml/zen_server/dashboard/assets/@radix-BXWm7HOa.js +85 -0
- zenml/zen_server/dashboard/assets/{@react-router-CO-OsFwI.js → @react-router-l3lMcXA2.js} +1 -1
- zenml/zen_server/dashboard/assets/{@reactflow-DIYUhKYX.js → @reactflow-CeVxyqYT.js} +2 -2
- zenml/zen_server/dashboard/assets/{@tanstack-k96lU_C-.js → @tanstack-FmcYZMuX.js} +4 -4
- zenml/zen_server/dashboard/assets/AlertDialogDropdownItem-ErO9aOgK.js +1 -0
- zenml/zen_server/dashboard/assets/{AwarenessChannel-BNg5uWgI.js → AwarenessChannel-CLXo5rKM.js} +1 -1
- zenml/zen_server/dashboard/assets/{CodeSnippet-Cyp7f4dM.js → CodeSnippet-D0VLxT2A.js} +1 -1
- zenml/zen_server/dashboard/assets/{CollapsibleCard-Cu_A9W57.js → CollapsibleCard-BaUPiVg0.js} +1 -1
- zenml/zen_server/dashboard/assets/{Commands-DmQwTXjj.js → Commands-JrcZK-3j.js} +1 -1
- zenml/zen_server/dashboard/assets/CopyButton-Dbo52T1K.js +2 -0
- zenml/zen_server/dashboard/assets/{CsvVizualization-BvqItd-O.js → CsvVizualization-D3kAypDj.js} +3 -3
- zenml/zen_server/dashboard/assets/DisplayDate-DizbSeT-.js +1 -0
- zenml/zen_server/dashboard/assets/EditSecretDialog-Bd7mFLS4.js +1 -0
- zenml/zen_server/dashboard/assets/{EmptyState-BMLnFVlB.js → EmptyState-BHblM39I.js} +1 -1
- zenml/zen_server/dashboard/assets/{Error-DbXCTGua.js → Error-C6LeJSER.js} +1 -1
- zenml/zen_server/dashboard/assets/{ExecutionStatus-9zM7eaLh.js → ExecutionStatus-jH4OrWBq.js} +1 -1
- zenml/zen_server/dashboard/assets/{Helpbox-BIiNc-uH.js → Helpbox-aAB2XP-z.js} +1 -1
- zenml/zen_server/dashboard/assets/{Infobox-iv1Nu1A0.js → Infobox-BQ0aty32.js} +1 -1
- zenml/zen_server/dashboard/assets/{InlineAvatar-BvBtO2Dp.js → InlineAvatar-DpTLgM3Q.js} +1 -1
- zenml/zen_server/dashboard/assets/Lock-CNyJvf2r.js +1 -0
- zenml/zen_server/dashboard/assets/{MarkdownVisualization-xp3hhULl.js → MarkdownVisualization-Bajxn0HY.js} +1 -1
- zenml/zen_server/dashboard/assets/NumberBox-BmKE0qnO.js +1 -0
- zenml/zen_server/dashboard/assets/{PasswordChecker-DUveqlva.js → PasswordChecker-yGGoJSB-.js} +1 -1
- zenml/zen_server/dashboard/assets/{ProviderRadio-pSAvrGRS.js → ProviderRadio-BBqkIuTd.js} +1 -1
- zenml/zen_server/dashboard/assets/RadioItem-xLhXoiFV.js +1 -0
- zenml/zen_server/dashboard/assets/SearchField-C9R0mdaX.js +1 -0
- zenml/zen_server/dashboard/assets/{SetPassword-BOxpgh6N.js → SetPassword-52sNxNiO.js} +1 -1
- zenml/zen_server/dashboard/assets/{SuccessStep-CTSKN2lp.js → SuccessStep-DlkItqYG.js} +1 -1
- zenml/zen_server/dashboard/assets/{Tick-Bnr2TpW6.js → Tick-uxv80Q6a.js} +1 -1
- zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-BeCeaRW5.js → UpdatePasswordSchemas-oN4G3sKz.js} +1 -1
- zenml/zen_server/dashboard/assets/{aws-BgKTfTfx.js → aws-0_3UsPif.js} +1 -1
- zenml/zen_server/dashboard/assets/{check-circle-i56092KI.js → check-circle-1_I207rW.js} +1 -1
- zenml/zen_server/dashboard/assets/{chevron-down-D_ZlKMqH.js → chevron-down-BpaF8JqM.js} +1 -1
- zenml/zen_server/dashboard/assets/{chevron-right-double-CZBOf6JM.js → chevron-right-double-Dk8e2L99.js} +1 -1
- zenml/zen_server/dashboard/assets/{cloud-only-qelmY92E.js → cloud-only-BkUuI0lZ.js} +1 -1
- zenml/zen_server/dashboard/assets/components-Br2ezRib.js +1 -0
- zenml/zen_server/dashboard/assets/{copy-BXNk6BjL.js → copy-f3XGPPxt.js} +1 -1
- zenml/zen_server/dashboard/assets/{database-1xWSgZfO.js → database-cXYNX9tt.js} +1 -1
- zenml/zen_server/dashboard/assets/{docker-CQMVm_4d.js → docker-8uj__HHK.js} +1 -1
- zenml/zen_server/dashboard/assets/{dots-horizontal-BObFzD5l.js → dots-horizontal-sKQlWEni.js} +1 -1
- zenml/zen_server/dashboard/assets/edit-C0MVvPD2.js +1 -0
- zenml/zen_server/dashboard/assets/{file-text-CqD_iu6l.js → file-text-B9JibxTs.js} +1 -1
- zenml/zen_server/dashboard/assets/{help-bu_DgLKI.js → help-FuHlZwn0.js} +1 -1
- zenml/zen_server/dashboard/assets/index-Bd1xgUQG.js +1 -0
- zenml/zen_server/dashboard/assets/index-DaGknux4.css +1 -0
- zenml/zen_server/dashboard/assets/{index-KsTz2dHG.js → index-DhIZtpxB.js} +5 -5
- zenml/zen_server/dashboard/assets/{index.esm-CbHNSeVw.js → index.esm-DT4uyn2i.js} +1 -1
- zenml/zen_server/dashboard/assets/layout-D6oiSbfd.js +1 -0
- zenml/zen_server/dashboard/assets/{login-mutation-DRpbESS7.js → login-mutation-13A_JSVA.js} +1 -1
- zenml/zen_server/dashboard/assets/{logs-D8k8BVFf.js → logs-CgeE2vZP.js} +1 -1
- zenml/zen_server/dashboard/assets/{not-found-Dfx9hfkf.js → not-found-B0Mmb90p.js} +1 -1
- zenml/zen_server/dashboard/assets/{package-ClbU3KUi.js → package-DdkziX79.js} +1 -1
- zenml/zen_server/dashboard/assets/page-7-v2OBm-.js +1 -0
- zenml/zen_server/dashboard/assets/{page-f3jBVI5Z.js → page-B3ozwdD1.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-DYBNGxJt.js → page-BGwA9B1M.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-C176KxyB.js → page-BkjAUyTA.js} +1 -1
- zenml/zen_server/dashboard/assets/page-BnacgBiy.js +1 -0
- zenml/zen_server/dashboard/assets/{page-CzucfYPo.js → page-BxF_KMQ3.js} +2 -2
- zenml/zen_server/dashboard/assets/page-C4POHC0K.js +1 -0
- zenml/zen_server/dashboard/assets/page-C9kudd44.js +9 -0
- zenml/zen_server/dashboard/assets/page-CA1j3GpJ.js +1 -0
- zenml/zen_server/dashboard/assets/page-CCY6yfmu.js +1 -0
- zenml/zen_server/dashboard/assets/page-CgTe7Bme.js +1 -0
- zenml/zen_server/dashboard/assets/{page-DtpwnNXq.js → page-Cgn-6v2Y.js} +1 -1
- zenml/zen_server/dashboard/assets/page-CxQmQqDw.js +1 -0
- zenml/zen_server/dashboard/assets/page-D2Goey3H.js +1 -0
- zenml/zen_server/dashboard/assets/page-DLpOnf7u.js +1 -0
- zenml/zen_server/dashboard/assets/{page-DVPxY5fT.js → page-DSTQnBk-.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-BoFtUD9H.js → page-DTysUGOy.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-p2hLJdS2.js → page-D_EXUFJb.js} +1 -1
- zenml/zen_server/dashboard/assets/page-Db15QzsM.js +1 -0
- zenml/zen_server/dashboard/assets/{page-Btu39x7k.js → page-DugsjcQ_.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-CZe9GEBF.js → page-OFKSPyN7.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-CDgZmwxP.js → page-RnG-qhv9.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-Cjn97HMv.js → page-T2BtjwPl.js} +1 -1
- zenml/zen_server/dashboard/assets/page-TXe1Eo3Z.js +1 -0
- zenml/zen_server/dashboard/assets/{page-BxiWdeyg.js → page-YiF_fNbe.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-399pVZHU.js → page-hQaiQXfg.js} +1 -1
- zenml/zen_server/dashboard/assets/persist-3-5nOJ6m.js +1 -0
- zenml/zen_server/dashboard/assets/{play-circle-CNtZKDnW.js → play-circle-XSkLR12B.js} +1 -1
- zenml/zen_server/dashboard/assets/{plus-DOeLmm7C.js → plus-FB9-lEq_.js} +1 -1
- zenml/zen_server/dashboard/assets/refresh-COb6KYDi.js +1 -0
- zenml/zen_server/dashboard/assets/sharedSchema-BoYx_B_L.js +14 -0
- zenml/zen_server/dashboard/assets/{stack-detail-query-Ck7j7BP_.js → stack-detail-query-B-US_-wa.js} +1 -1
- zenml/zen_server/dashboard/assets/{terminal-By9cErXc.js → terminal-grtjrIEJ.js} +1 -1
- zenml/zen_server/dashboard/assets/trash-Cd5CSFqA.js +1 -0
- zenml/zen_server/dashboard/assets/{update-server-settings-mutation-f3ZT7psb.js → update-server-settings-mutation-B8GB_ubU.js} +1 -1
- zenml/zen_server/dashboard/assets/{url-rGEp5Umh.js → url-hcMJkz8p.js} +1 -1
- zenml/zen_server/dashboard/assets/{zod-BtSyGx4C.js → zod-CnykDKJj.js} +1 -1
- zenml/zen_server/dashboard/index.html +7 -7
- 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.2fa6e528a6e7447caaf35dadfe7514bb.js → precache-manifest.9c473c96a43298343a7ce1256183123b.js} +4 -4
- zenml/zen_server/dashboard_legacy/service-worker.js +1 -1
- zenml/zen_server/dashboard_legacy/static/js/{main.4aab7e98.chunk.js → main.463c90b9.chunk.js} +2 -2
- zenml/zen_server/dashboard_legacy/static/js/{main.4aab7e98.chunk.js.map → main.463c90b9.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/stack_deployment_endpoints.py +6 -0
- zenml/zen_server/routers/users_endpoints.py +0 -7
- zenml/zen_server/utils.py +75 -0
- zenml/zen_server/zen_server_api.py +52 -1
- zenml/zen_stores/base_zen_store.py +7 -1
- zenml/zen_stores/migrations/versions/0.64.0_release.py +23 -0
- zenml/zen_stores/migrations/versions/026d4577b6a0_add_code_path.py +39 -0
- zenml/zen_stores/migrations/versions/3dcc5d20e82f_add_last_user_activity.py +51 -0
- zenml/zen_stores/migrations/versions/909550c7c4da_remove_user_hub_token.py +36 -0
- zenml/zen_stores/rest_zen_store.py +5 -3
- zenml/zen_stores/schemas/pipeline_deployment_schemas.py +3 -0
- zenml/zen_stores/schemas/pipeline_run_schemas.py +3 -0
- zenml/zen_stores/schemas/server_settings_schemas.py +2 -0
- zenml/zen_stores/schemas/user_schemas.py +0 -2
- zenml/zen_stores/sql_zen_store.py +25 -1
- {zenml_nightly-0.63.0.dev20240801.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/METADATA +3 -3
- {zenml_nightly-0.63.0.dev20240801.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/RECORD +174 -157
- zenml/_hub/client.py +0 -289
- zenml/_hub/constants.py +0 -21
- zenml/_hub/utils.py +0 -79
- zenml/cli/hub.py +0 -1116
- zenml/models/v2/misc/hub_plugin_models.py +0 -79
- zenml/zen_server/dashboard/assets/@radix-CFOkMR_E.js +0 -85
- zenml/zen_server/dashboard/assets/CopyButton-B3sWVJ4Z.js +0 -2
- zenml/zen_server/dashboard/assets/DisplayDate-DYgIjlDF.js +0 -1
- zenml/zen_server/dashboard/assets/SearchField-CXoBknpt.js +0 -1
- zenml/zen_server/dashboard/assets/components-DWe4cTjS.js +0 -1
- zenml/zen_server/dashboard/assets/index-vfjX_fJV.css +0 -1
- zenml/zen_server/dashboard/assets/page-C6tXXjnK.js +0 -1
- zenml/zen_server/dashboard/assets/page-CP9obrnG.js +0 -1
- zenml/zen_server/dashboard/assets/page-CaTOsNNw.js +0 -1
- zenml/zen_server/dashboard/assets/page-CmXmB_5i.js +0 -1
- zenml/zen_server/dashboard/assets/page-CvGAOfad.js +0 -1
- zenml/zen_server/dashboard/assets/page-D0bbc-qr.js +0 -5
- zenml/zen_server/dashboard/assets/page-DLEtD2ex.js +0 -1
- zenml/zen_server/dashboard/assets/page-DupV0aBd.js +0 -1
- zenml/zen_server/dashboard/assets/page-EweAR81y.js +0 -1
- zenml/zen_server/dashboard/assets/page-w-YaL77M.js +0 -9
- zenml/zen_server/dashboard/assets/persist-BReKApOc.js +0 -14
- zenml/zen_server/dashboard/assets/secrets-video-OBJ6irhH.svg +0 -21
- zenml/zen_server/dashboard/assets/stacks-video-7gfxpAq4.svg +0 -21
- {zenml_nightly-0.63.0.dev20240801.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.63.0.dev20240801.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/WHEEL +0 -0
- {zenml_nightly-0.63.0.dev20240801.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,544 @@
|
|
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
|
+
"""Implementation of the AzureML Orchestrator."""
|
15
|
+
|
16
|
+
import json
|
17
|
+
import os
|
18
|
+
from typing import (
|
19
|
+
TYPE_CHECKING,
|
20
|
+
Any,
|
21
|
+
Dict,
|
22
|
+
List,
|
23
|
+
Optional,
|
24
|
+
Tuple,
|
25
|
+
Type,
|
26
|
+
Union,
|
27
|
+
cast,
|
28
|
+
)
|
29
|
+
|
30
|
+
from azure.ai.ml import Input, MLClient, Output
|
31
|
+
from azure.ai.ml.constants import TimeZone
|
32
|
+
from azure.ai.ml.dsl import pipeline
|
33
|
+
from azure.ai.ml.entities import (
|
34
|
+
CommandComponent,
|
35
|
+
CronTrigger,
|
36
|
+
Environment,
|
37
|
+
JobSchedule,
|
38
|
+
RecurrenceTrigger,
|
39
|
+
)
|
40
|
+
from azure.core.exceptions import (
|
41
|
+
HttpResponseError,
|
42
|
+
ResourceExistsError,
|
43
|
+
ResourceNotFoundError,
|
44
|
+
)
|
45
|
+
from azure.identity import DefaultAzureCredential
|
46
|
+
|
47
|
+
from zenml.config.base_settings import BaseSettings
|
48
|
+
from zenml.config.step_configurations import Step
|
49
|
+
from zenml.enums import StackComponentType
|
50
|
+
from zenml.integrations.azure.flavors.azureml_orchestrator_flavor import (
|
51
|
+
AzureMLComputeTypes,
|
52
|
+
AzureMLOrchestratorConfig,
|
53
|
+
AzureMLOrchestratorSettings,
|
54
|
+
)
|
55
|
+
from zenml.integrations.azure.orchestrators.azureml_orchestrator_entrypoint_config import (
|
56
|
+
AzureMLEntrypointConfiguration,
|
57
|
+
)
|
58
|
+
from zenml.logger import get_logger
|
59
|
+
from zenml.orchestrators import ContainerizedOrchestrator
|
60
|
+
from zenml.orchestrators.utils import get_orchestrator_run_name
|
61
|
+
from zenml.stack import StackValidator
|
62
|
+
from zenml.utils.string_utils import b64_encode
|
63
|
+
|
64
|
+
if TYPE_CHECKING:
|
65
|
+
from azure.ai.ml.entities import AmlCompute, ComputeInstance
|
66
|
+
|
67
|
+
from zenml.models import PipelineDeploymentResponse
|
68
|
+
from zenml.stack import Stack
|
69
|
+
|
70
|
+
logger = get_logger(__name__)
|
71
|
+
|
72
|
+
ENV_ZENML_AZUREML_RUN_ID = "AZUREML_ROOT_RUN_ID"
|
73
|
+
|
74
|
+
|
75
|
+
class AzureMLOrchestrator(ContainerizedOrchestrator):
|
76
|
+
"""Orchestrator responsible for running pipelines on AzureML."""
|
77
|
+
|
78
|
+
@property
|
79
|
+
def config(self) -> AzureMLOrchestratorConfig:
|
80
|
+
"""Returns the `AzureMLOrchestratorConfig` config.
|
81
|
+
|
82
|
+
Returns:
|
83
|
+
The configuration.
|
84
|
+
"""
|
85
|
+
return cast(AzureMLOrchestratorConfig, self._config)
|
86
|
+
|
87
|
+
@property
|
88
|
+
def settings_class(self) -> Optional[Type["BaseSettings"]]:
|
89
|
+
"""Settings class for the AzureML orchestrator.
|
90
|
+
|
91
|
+
Returns:
|
92
|
+
The settings class.
|
93
|
+
"""
|
94
|
+
return AzureMLOrchestratorSettings
|
95
|
+
|
96
|
+
@property
|
97
|
+
def validator(self) -> Optional[StackValidator]:
|
98
|
+
"""Validates the stack.
|
99
|
+
|
100
|
+
In the remote case, checks that the stack contains a container registry,
|
101
|
+
image builder and only remote components.
|
102
|
+
|
103
|
+
Returns:
|
104
|
+
A `StackValidator` instance.
|
105
|
+
"""
|
106
|
+
|
107
|
+
def _validate_remote_components(
|
108
|
+
stack: "Stack",
|
109
|
+
) -> Tuple[bool, str]:
|
110
|
+
for component in stack.components.values():
|
111
|
+
if not component.config.is_local:
|
112
|
+
continue
|
113
|
+
|
114
|
+
return False, (
|
115
|
+
f"The AzureML orchestrator runs pipelines remotely, "
|
116
|
+
f"but the '{component.name}' {component.type.value} is "
|
117
|
+
"a local stack component and will not be available in "
|
118
|
+
"the AzureML step.\nPlease ensure that you always "
|
119
|
+
"use non-local stack components with the AzureML "
|
120
|
+
"orchestrator."
|
121
|
+
)
|
122
|
+
|
123
|
+
return True, ""
|
124
|
+
|
125
|
+
return StackValidator(
|
126
|
+
required_components={
|
127
|
+
StackComponentType.CONTAINER_REGISTRY,
|
128
|
+
StackComponentType.IMAGE_BUILDER,
|
129
|
+
},
|
130
|
+
custom_validation_function=_validate_remote_components,
|
131
|
+
)
|
132
|
+
|
133
|
+
def get_orchestrator_run_id(self) -> str:
|
134
|
+
"""Returns the run id of the active orchestrator run.
|
135
|
+
|
136
|
+
Important: This needs to be a unique ID and return the same value for
|
137
|
+
all steps of a pipeline run.
|
138
|
+
|
139
|
+
Returns:
|
140
|
+
The orchestrator run id.
|
141
|
+
|
142
|
+
Raises:
|
143
|
+
RuntimeError: If the run id cannot be read from the environment.
|
144
|
+
"""
|
145
|
+
try:
|
146
|
+
return os.environ[ENV_ZENML_AZUREML_RUN_ID]
|
147
|
+
except KeyError:
|
148
|
+
raise RuntimeError(
|
149
|
+
"Unable to read run id from environment variable "
|
150
|
+
f"{ENV_ZENML_AZUREML_RUN_ID}."
|
151
|
+
)
|
152
|
+
|
153
|
+
@staticmethod
|
154
|
+
def _create_command_component(
|
155
|
+
step: Step,
|
156
|
+
step_name: str,
|
157
|
+
env_name: str,
|
158
|
+
image: str,
|
159
|
+
command: List[str],
|
160
|
+
arguments: List[str],
|
161
|
+
) -> CommandComponent:
|
162
|
+
"""Creates a CommandComponent to run on AzureML Pipelines.
|
163
|
+
|
164
|
+
Args:
|
165
|
+
step: The step definition in ZenML.
|
166
|
+
step_name: The name of the step.
|
167
|
+
env_name: The name of the environment.
|
168
|
+
image: The image to use in the environment
|
169
|
+
command: The command to execute the entrypoint with.
|
170
|
+
arguments: The arguments to pass into the command.
|
171
|
+
|
172
|
+
Returns:
|
173
|
+
the generated AzureML CommandComponent.
|
174
|
+
"""
|
175
|
+
env = Environment(name=env_name, image=image)
|
176
|
+
|
177
|
+
outputs = {"completed": Output(type="uri_file")}
|
178
|
+
|
179
|
+
inputs = {}
|
180
|
+
if step.spec.upstream_steps:
|
181
|
+
inputs = {
|
182
|
+
f"{upstream_step}": Input(type="uri_file")
|
183
|
+
for upstream_step in step.spec.upstream_steps
|
184
|
+
}
|
185
|
+
|
186
|
+
return CommandComponent(
|
187
|
+
name=step_name,
|
188
|
+
display_name=step_name,
|
189
|
+
description=f"AzureML CommandComponent for {step_name}.",
|
190
|
+
inputs=inputs,
|
191
|
+
outputs=outputs,
|
192
|
+
environment=env,
|
193
|
+
command=" ".join(command + arguments),
|
194
|
+
)
|
195
|
+
|
196
|
+
@staticmethod
|
197
|
+
def _check_settings_and_compute_configuration(
|
198
|
+
parameter: str,
|
199
|
+
settings: AzureMLOrchestratorSettings,
|
200
|
+
compute: Union["ComputeInstance", "AmlCompute"],
|
201
|
+
) -> None:
|
202
|
+
"""Utility function comparing a parameter between settings and compute.
|
203
|
+
|
204
|
+
Args:
|
205
|
+
parameter: the name of the parameter.
|
206
|
+
settings: The AzureML orchestrator settings.
|
207
|
+
compute: The compute instance or cluster from AzureML.
|
208
|
+
"""
|
209
|
+
# Check the compute size
|
210
|
+
compute_value = getattr(compute, parameter)
|
211
|
+
settings_value = getattr(settings, parameter)
|
212
|
+
|
213
|
+
if settings_value is not None and settings_value != compute_value:
|
214
|
+
logger.warning(
|
215
|
+
f"The '{parameter}' defined in the settings '{settings_value}' "
|
216
|
+
"does not match the actual parameter of the instance: "
|
217
|
+
f"'{compute_value}'. Will ignore this setting for now."
|
218
|
+
)
|
219
|
+
|
220
|
+
def _create_or_get_compute(
|
221
|
+
self, client: MLClient, settings: AzureMLOrchestratorSettings
|
222
|
+
) -> Optional[str]:
|
223
|
+
"""Creates or fetches the compute target if defined in the settings.
|
224
|
+
|
225
|
+
Args:
|
226
|
+
client: the AzureML client.
|
227
|
+
settings: the settings for the orchestrator.
|
228
|
+
|
229
|
+
Returns:
|
230
|
+
None, if the orchestrator is using serverless compute or
|
231
|
+
str, the name of the compute target (instance or cluster).
|
232
|
+
|
233
|
+
Raises:
|
234
|
+
RuntimeError: if the fetched compute target is unsupported or the
|
235
|
+
mode defined in the setting does not match the type of the
|
236
|
+
compute target.
|
237
|
+
"""
|
238
|
+
# If the mode is serverless, we can not fetch anything anyhow
|
239
|
+
if settings.mode == AzureMLComputeTypes.SERVERLESS:
|
240
|
+
return None
|
241
|
+
|
242
|
+
# If a name is not provided, generate one based on the orchestrator id
|
243
|
+
compute_name = settings.compute_name or f"compute_{self.id}"
|
244
|
+
# Try to fetch the compute target
|
245
|
+
try:
|
246
|
+
compute = client.compute.get(compute_name)
|
247
|
+
|
248
|
+
logger.info(f"Using existing compute target: '{compute_name}'.")
|
249
|
+
|
250
|
+
# Check if compute size matches with the settings
|
251
|
+
self._check_settings_and_compute_configuration(
|
252
|
+
parameter="size", settings=settings, compute=compute
|
253
|
+
)
|
254
|
+
|
255
|
+
compute_type = compute.type
|
256
|
+
|
257
|
+
# Check the type and matches the settings
|
258
|
+
if compute_type == "computeinstance": # Compute Instance
|
259
|
+
if settings.mode != AzureMLComputeTypes.COMPUTE_INSTANCE:
|
260
|
+
raise RuntimeError(
|
261
|
+
"The mode of operation for the compute target defined"
|
262
|
+
f"in the settings '{settings.mode}' does not match "
|
263
|
+
f"the type of the compute target: `{compute_name}` "
|
264
|
+
"which is a 'compute-instance'. Please make sure that "
|
265
|
+
"the settings are adjusted properly."
|
266
|
+
)
|
267
|
+
|
268
|
+
if compute.state != "Running":
|
269
|
+
raise RuntimeError(
|
270
|
+
f"The compute instance `{compute_name}` is not in a "
|
271
|
+
"running state at the moment. Please make sure that "
|
272
|
+
"the compute target is running, before executing the "
|
273
|
+
"pipeline."
|
274
|
+
)
|
275
|
+
|
276
|
+
# Idle time before shutdown
|
277
|
+
self._check_settings_and_compute_configuration(
|
278
|
+
parameter="idle_time_before_shutdown_minutes",
|
279
|
+
settings=settings,
|
280
|
+
compute=compute,
|
281
|
+
)
|
282
|
+
|
283
|
+
elif compute_type == "amIcompute": # Compute Cluster
|
284
|
+
if settings.mode != AzureMLComputeTypes.COMPUTE_CLUSTER:
|
285
|
+
raise RuntimeError(
|
286
|
+
"The mode of operation for the compute target defined "
|
287
|
+
f"in the settings '{settings.mode}' does not match "
|
288
|
+
f"the type of the compute target: `{compute_name}` "
|
289
|
+
"which is a 'compute-cluster'. Please make sure that "
|
290
|
+
"the settings are adjusted properly."
|
291
|
+
)
|
292
|
+
|
293
|
+
if compute.provisioning_state != "Succeeded":
|
294
|
+
raise RuntimeError(
|
295
|
+
f"The provisioning state '{compute.provisioning_state}'"
|
296
|
+
f"of the compute cluster `{compute_name}` is not "
|
297
|
+
"successful. Please make sure that the compute cluster "
|
298
|
+
"is provisioned properly, before executing the "
|
299
|
+
"pipeline."
|
300
|
+
)
|
301
|
+
|
302
|
+
for parameter in [
|
303
|
+
"idle_time_before_scale_down",
|
304
|
+
"max_instances",
|
305
|
+
"min_instances",
|
306
|
+
"tier",
|
307
|
+
"location",
|
308
|
+
]:
|
309
|
+
# Check all possible configurations
|
310
|
+
self._check_settings_and_compute_configuration(
|
311
|
+
parameter=parameter, settings=settings, compute=compute
|
312
|
+
)
|
313
|
+
else:
|
314
|
+
raise RuntimeError(f"Unsupported compute type: {compute_type}")
|
315
|
+
return compute_name
|
316
|
+
|
317
|
+
# If the compute target does not exist create it
|
318
|
+
except ResourceNotFoundError:
|
319
|
+
logger.info(
|
320
|
+
"Can not find the compute target with name: "
|
321
|
+
f"'{compute_name}':"
|
322
|
+
)
|
323
|
+
|
324
|
+
if settings.mode == AzureMLComputeTypes.COMPUTE_INSTANCE:
|
325
|
+
logger.info(
|
326
|
+
"Creating a new compute instance. This might take a "
|
327
|
+
"few minutes."
|
328
|
+
)
|
329
|
+
|
330
|
+
from azure.ai.ml.entities import ComputeInstance
|
331
|
+
|
332
|
+
compute_instance = ComputeInstance(
|
333
|
+
name=compute_name,
|
334
|
+
size=settings.size,
|
335
|
+
idle_time_before_shutdown_minutes=settings.idle_time_before_shutdown_minutes,
|
336
|
+
)
|
337
|
+
client.begin_create_or_update(compute_instance).result()
|
338
|
+
return compute_name
|
339
|
+
|
340
|
+
elif settings.mode == AzureMLComputeTypes.COMPUTE_CLUSTER:
|
341
|
+
logger.info(
|
342
|
+
"Creating a new compute cluster. This might take a "
|
343
|
+
"few minutes."
|
344
|
+
)
|
345
|
+
|
346
|
+
from azure.ai.ml.entities import AmlCompute
|
347
|
+
|
348
|
+
compute_cluster = AmlCompute(
|
349
|
+
name=compute_name,
|
350
|
+
size=settings.size,
|
351
|
+
location=settings.location,
|
352
|
+
min_instances=settings.min_instances,
|
353
|
+
max_instances=settings.max_instances,
|
354
|
+
idle_time_before_scale_down=settings.idle_time_before_scaledown_down,
|
355
|
+
tier=settings.tier,
|
356
|
+
)
|
357
|
+
client.begin_create_or_update(compute_cluster).result()
|
358
|
+
return compute_name
|
359
|
+
|
360
|
+
return None
|
361
|
+
|
362
|
+
def prepare_or_run_pipeline(
|
363
|
+
self,
|
364
|
+
deployment: "PipelineDeploymentResponse",
|
365
|
+
stack: "Stack",
|
366
|
+
environment: Dict[str, str],
|
367
|
+
) -> None:
|
368
|
+
"""Prepares or runs a pipeline on AzureML.
|
369
|
+
|
370
|
+
Args:
|
371
|
+
deployment: The deployment to prepare or run.
|
372
|
+
stack: The stack to run on.
|
373
|
+
environment: Environment variables to set in the orchestration
|
374
|
+
environment.
|
375
|
+
|
376
|
+
Raises:
|
377
|
+
RuntimeError: If the creation of the schedule fails.
|
378
|
+
"""
|
379
|
+
# Authentication
|
380
|
+
if connector := self.get_connector():
|
381
|
+
credentials = connector.connect()
|
382
|
+
else:
|
383
|
+
credentials = DefaultAzureCredential()
|
384
|
+
|
385
|
+
# Settings
|
386
|
+
settings = cast(
|
387
|
+
AzureMLOrchestratorSettings,
|
388
|
+
self.get_settings(deployment),
|
389
|
+
)
|
390
|
+
|
391
|
+
# Client creation
|
392
|
+
ml_client = MLClient(
|
393
|
+
credential=credentials,
|
394
|
+
subscription_id=self.config.subscription_id,
|
395
|
+
resource_group_name=self.config.resource_group,
|
396
|
+
workspace_name=self.config.workspace,
|
397
|
+
)
|
398
|
+
|
399
|
+
# Create components
|
400
|
+
components = {}
|
401
|
+
for step_name, step in deployment.step_configurations.items():
|
402
|
+
# Get the image for each step
|
403
|
+
image = self.get_image(deployment=deployment, step_name=step_name)
|
404
|
+
|
405
|
+
# Get the command and arguments
|
406
|
+
command = AzureMLEntrypointConfiguration.get_entrypoint_command()
|
407
|
+
arguments = (
|
408
|
+
AzureMLEntrypointConfiguration.get_entrypoint_arguments(
|
409
|
+
step_name=step_name,
|
410
|
+
deployment_id=deployment.id,
|
411
|
+
zenml_env_variables=b64_encode(json.dumps(environment)),
|
412
|
+
)
|
413
|
+
)
|
414
|
+
|
415
|
+
# Generate an AzureML CommandComponent
|
416
|
+
components[step_name] = self._create_command_component(
|
417
|
+
step=step,
|
418
|
+
step_name=step_name,
|
419
|
+
env_name=deployment.pipeline_configuration.name,
|
420
|
+
image=image,
|
421
|
+
command=command,
|
422
|
+
arguments=arguments,
|
423
|
+
)
|
424
|
+
|
425
|
+
# Pipeline definition
|
426
|
+
pipeline_args = dict()
|
427
|
+
run_name = get_orchestrator_run_name(
|
428
|
+
pipeline_name=deployment.pipeline_configuration.name
|
429
|
+
)
|
430
|
+
pipeline_args["name"] = run_name
|
431
|
+
|
432
|
+
if compute_target := self._create_or_get_compute(ml_client, settings):
|
433
|
+
pipeline_args["compute"] = compute_target
|
434
|
+
|
435
|
+
@pipeline(force_rerun=True, **pipeline_args) # type: ignore[call-overload, misc]
|
436
|
+
def azureml_pipeline() -> None:
|
437
|
+
"""Create an AzureML pipeline."""
|
438
|
+
# Here we have to track the inputs and outputs so that we can bind
|
439
|
+
# the components to each other to execute them in a specific order.
|
440
|
+
component_outputs: Dict[str, Any] = {}
|
441
|
+
for component_name, component in components.items():
|
442
|
+
# Inputs
|
443
|
+
component_inputs = {}
|
444
|
+
if component.inputs:
|
445
|
+
component_inputs.update(
|
446
|
+
{i: component_outputs[i] for i in component.inputs}
|
447
|
+
)
|
448
|
+
|
449
|
+
# Job
|
450
|
+
component_job = component(**component_inputs)
|
451
|
+
|
452
|
+
# Outputs
|
453
|
+
if component_job.outputs:
|
454
|
+
component_outputs[component_name] = (
|
455
|
+
component_job.outputs.completed
|
456
|
+
)
|
457
|
+
|
458
|
+
# Create and execute the pipeline job
|
459
|
+
pipeline_job = azureml_pipeline()
|
460
|
+
|
461
|
+
if settings.mode == AzureMLComputeTypes.SERVERLESS:
|
462
|
+
pipeline_job.settings.default_compute = "serverless"
|
463
|
+
|
464
|
+
# Scheduling
|
465
|
+
if schedule := deployment.schedule:
|
466
|
+
try:
|
467
|
+
schedule_trigger: Optional[
|
468
|
+
Union[CronTrigger, RecurrenceTrigger]
|
469
|
+
] = None
|
470
|
+
|
471
|
+
start_time = None
|
472
|
+
if schedule.start_time is not None:
|
473
|
+
start_time = schedule.start_time.isoformat()
|
474
|
+
|
475
|
+
end_time = None
|
476
|
+
if schedule.end_time is not None:
|
477
|
+
end_time = schedule.end_time.isoformat()
|
478
|
+
|
479
|
+
if schedule.cron_expression:
|
480
|
+
# If we are working with a cron expression
|
481
|
+
schedule_trigger = CronTrigger(
|
482
|
+
expression=schedule.cron_expression,
|
483
|
+
start_time=start_time,
|
484
|
+
end_time=end_time,
|
485
|
+
time_zone=TimeZone.UTC,
|
486
|
+
)
|
487
|
+
|
488
|
+
elif schedule.interval_second:
|
489
|
+
# If we are working with intervals
|
490
|
+
interval = schedule.interval_second.total_seconds()
|
491
|
+
|
492
|
+
if interval % 60 != 0:
|
493
|
+
logger.warning(
|
494
|
+
"The ZenML AzureML orchestrator only works with "
|
495
|
+
"time intervals defined over minutes. Will "
|
496
|
+
f"use a schedule over {int(interval // 60)}."
|
497
|
+
)
|
498
|
+
|
499
|
+
if interval < 60:
|
500
|
+
raise RuntimeError(
|
501
|
+
"Can not create a schedule with an interval less "
|
502
|
+
"than 60 secs."
|
503
|
+
)
|
504
|
+
|
505
|
+
frequency = "minute"
|
506
|
+
interval = int(interval // 60)
|
507
|
+
|
508
|
+
schedule_trigger = RecurrenceTrigger(
|
509
|
+
frequency=frequency,
|
510
|
+
interval=interval,
|
511
|
+
start_time=start_time,
|
512
|
+
end_time=end_time,
|
513
|
+
time_zone=TimeZone.UTC,
|
514
|
+
)
|
515
|
+
|
516
|
+
if schedule_trigger:
|
517
|
+
# Create and execute the job schedule
|
518
|
+
job_schedule = JobSchedule(
|
519
|
+
name=run_name,
|
520
|
+
trigger=schedule_trigger,
|
521
|
+
create_job=pipeline_job,
|
522
|
+
)
|
523
|
+
ml_client.schedules.begin_create_or_update(
|
524
|
+
job_schedule
|
525
|
+
).result()
|
526
|
+
logger.info(
|
527
|
+
f"Scheduled pipeline '{run_name}' with recurrence "
|
528
|
+
"or cron expression."
|
529
|
+
)
|
530
|
+
else:
|
531
|
+
raise RuntimeError(
|
532
|
+
"No valid scheduling configuration found for "
|
533
|
+
f"pipeline '{run_name}'."
|
534
|
+
)
|
535
|
+
|
536
|
+
except (HttpResponseError, ResourceExistsError) as e:
|
537
|
+
raise RuntimeError(
|
538
|
+
"Failed to create schedule for the pipeline "
|
539
|
+
f"'{run_name}': {str(e)}"
|
540
|
+
)
|
541
|
+
|
542
|
+
else:
|
543
|
+
ml_client.jobs.create_or_update(pipeline_job)
|
544
|
+
logger.info(f"Pipeline {run_name} has been started.")
|
@@ -0,0 +1,86 @@
|
|
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
|
+
"""Entrypoint configuration for ZenML AzureML pipeline steps."""
|
15
|
+
|
16
|
+
import json
|
17
|
+
import os
|
18
|
+
from typing import Any, List, Set
|
19
|
+
|
20
|
+
from zenml.entrypoints.step_entrypoint_configuration import (
|
21
|
+
StepEntrypointConfiguration,
|
22
|
+
)
|
23
|
+
from zenml.utils.string_utils import b64_decode
|
24
|
+
|
25
|
+
ZENML_ENV_VARIABLES = "zenml_env_variables"
|
26
|
+
AZURE_ML_OUTPUT_COMPLETED = "AZURE_ML_OUTPUT_COMPLETED"
|
27
|
+
|
28
|
+
|
29
|
+
class AzureMLEntrypointConfiguration(StepEntrypointConfiguration):
|
30
|
+
"""Entrypoint configuration for ZenML AzureML pipeline steps."""
|
31
|
+
|
32
|
+
@classmethod
|
33
|
+
def get_entrypoint_options(cls) -> Set[str]:
|
34
|
+
"""Gets all options required for running with this configuration.
|
35
|
+
|
36
|
+
Returns:
|
37
|
+
The superclass options as well as an option for the
|
38
|
+
environmental variables.
|
39
|
+
"""
|
40
|
+
return super().get_entrypoint_options() | {ZENML_ENV_VARIABLES}
|
41
|
+
|
42
|
+
@classmethod
|
43
|
+
def get_entrypoint_arguments(cls, **kwargs: Any) -> List[str]:
|
44
|
+
"""Gets all arguments that the entrypoint command should be called with.
|
45
|
+
|
46
|
+
Args:
|
47
|
+
**kwargs: Kwargs, can include the environmental variables.
|
48
|
+
|
49
|
+
Returns:
|
50
|
+
The superclass arguments as well as arguments for environmental
|
51
|
+
variables.
|
52
|
+
"""
|
53
|
+
return super().get_entrypoint_arguments(**kwargs) + [
|
54
|
+
f"--{ZENML_ENV_VARIABLES}",
|
55
|
+
kwargs[ZENML_ENV_VARIABLES],
|
56
|
+
]
|
57
|
+
|
58
|
+
def _set_env_variables(self) -> None:
|
59
|
+
"""Sets the environmental variables before executing the step."""
|
60
|
+
env_variables = json.loads(
|
61
|
+
b64_decode(self.entrypoint_args[ZENML_ENV_VARIABLES])
|
62
|
+
)
|
63
|
+
os.environ.update(env_variables)
|
64
|
+
|
65
|
+
def run(self) -> None:
|
66
|
+
"""Runs the step."""
|
67
|
+
# Set the environmental variables first
|
68
|
+
self._set_env_variables()
|
69
|
+
|
70
|
+
# Azure automatically changes the working directory, we have to set it
|
71
|
+
# back to /app before running the step.
|
72
|
+
os.chdir("/app")
|
73
|
+
|
74
|
+
# Run the step
|
75
|
+
super().run()
|
76
|
+
|
77
|
+
# Unfortunately, in AzureML's Python SDK v2, there is no native way
|
78
|
+
# to execute steps/components in a specific sequence. In order to
|
79
|
+
# establish the correct order, we are using dummy inputs and
|
80
|
+
# outputs. However, these steps only execute if the inputs and outputs
|
81
|
+
# actually exist. This is why we create a dummy file and write to it and
|
82
|
+
# use it as the output of the steps.
|
83
|
+
if completed := os.environ.get(AZURE_ML_OUTPUT_COMPLETED):
|
84
|
+
os.makedirs(os.path.dirname(completed), exist_ok=True)
|
85
|
+
with open(completed, "w") as f:
|
86
|
+
f.write("Component completed!")
|
@@ -250,6 +250,9 @@ class AzureMLStepOperator(BaseStepOperator):
|
|
250
250
|
"apt_packages",
|
251
251
|
"user",
|
252
252
|
"source_files",
|
253
|
+
"allow_including_files_in_images",
|
254
|
+
"allow_download_from_code_repository",
|
255
|
+
"allow_download_from_artifact_store",
|
253
256
|
]
|
254
257
|
docker_settings = info.config.docker_settings
|
255
258
|
ignored_docker_fields = docker_settings.model_fields_set.intersection(
|
@@ -93,6 +93,15 @@ class DatabricksOrchestratorConfig(
|
|
93
93
|
"""
|
94
94
|
return False
|
95
95
|
|
96
|
+
@property
|
97
|
+
def is_remote(self) -> bool:
|
98
|
+
"""Checks if this stack component is running remotely.
|
99
|
+
|
100
|
+
Returns:
|
101
|
+
True if this config is for a remote component, False otherwise.
|
102
|
+
"""
|
103
|
+
return True
|
104
|
+
|
96
105
|
|
97
106
|
class DatabricksOrchestratorFlavor(BaseOrchestratorFlavor):
|
98
107
|
"""Databricks orchestrator flavor."""
|
@@ -574,6 +574,9 @@ class VertexOrchestrator(ContainerizedOrchestrator, GoogleCredentialsMixin):
|
|
574
574
|
run_name: Orchestrator run name.
|
575
575
|
settings: Pipeline level settings for this orchestrator.
|
576
576
|
schedule: The schedule the pipeline will run on.
|
577
|
+
|
578
|
+
Raises:
|
579
|
+
RuntimeError: If the Vertex Orchestrator fails to provision or any other Runtime errors
|
577
580
|
"""
|
578
581
|
# We have to replace the hyphens in the run name with underscores
|
579
582
|
# and lower case the string, because the Vertex AI Pipelines service
|
@@ -656,13 +659,15 @@ class VertexOrchestrator(ContainerizedOrchestrator, GoogleCredentialsMixin):
|
|
656
659
|
run.wait()
|
657
660
|
|
658
661
|
except google_exceptions.ClientError as e:
|
659
|
-
logger.
|
660
|
-
|
662
|
+
logger.error("Failed to create the Vertex AI Pipelines job: %s", e)
|
663
|
+
raise RuntimeError(
|
664
|
+
f"Failed to create the Vertex AI Pipelines job: {e}"
|
661
665
|
)
|
662
666
|
except RuntimeError as e:
|
663
667
|
logger.error(
|
664
668
|
"The Vertex AI Pipelines job execution has failed: %s", e
|
665
669
|
)
|
670
|
+
raise
|
666
671
|
|
667
672
|
def get_orchestrator_run_id(self) -> str:
|
668
673
|
"""Returns the active orchestrator run id.
|