zenml-nightly 0.62.0.dev20240729__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.
Files changed (240) hide show
  1. README.md +2 -2
  2. RELEASE_NOTES.md +120 -0
  3. zenml/VERSION +1 -1
  4. zenml/__init__.py +0 -4
  5. zenml/actions/pipeline_run/pipeline_run_action.py +19 -17
  6. zenml/analytics/enums.py +4 -6
  7. zenml/cli/__init__.py +28 -76
  8. zenml/cli/base.py +2 -2
  9. zenml/cli/pipeline.py +54 -61
  10. zenml/cli/stack.py +6 -8
  11. zenml/cli/web_login.py +8 -0
  12. zenml/client.py +232 -103
  13. zenml/config/build_configuration.py +43 -17
  14. zenml/config/compiler.py +14 -22
  15. zenml/config/docker_settings.py +80 -57
  16. zenml/config/pipeline_run_configuration.py +3 -0
  17. zenml/config/server_config.py +3 -0
  18. zenml/config/source.py +60 -1
  19. zenml/constants.py +11 -2
  20. zenml/entrypoints/base_entrypoint_configuration.py +53 -8
  21. zenml/enums.py +4 -1
  22. zenml/environment.py +25 -9
  23. zenml/image_builders/base_image_builder.py +1 -1
  24. zenml/image_builders/build_context.py +25 -72
  25. zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +13 -4
  26. zenml/integrations/azure/__init__.py +4 -0
  27. zenml/integrations/azure/flavors/__init__.py +11 -0
  28. zenml/integrations/azure/flavors/azureml_orchestrator_flavor.py +263 -0
  29. zenml/{_hub → integrations/azure/orchestrators}/__init__.py +7 -2
  30. zenml/integrations/azure/orchestrators/azureml_orchestrator.py +544 -0
  31. zenml/integrations/azure/orchestrators/azureml_orchestrator_entrypoint_config.py +86 -0
  32. zenml/integrations/azure/step_operators/azureml_step_operator.py +3 -0
  33. zenml/integrations/databricks/flavors/databricks_orchestrator_flavor.py +20 -2
  34. zenml/integrations/databricks/orchestrators/databricks_orchestrator.py +19 -13
  35. zenml/integrations/gcp/orchestrators/vertex_orchestrator.py +7 -2
  36. zenml/integrations/gcp/service_connectors/gcp_service_connector.py +123 -6
  37. zenml/integrations/kaniko/image_builders/kaniko_image_builder.py +1 -1
  38. zenml/integrations/mlflow/__init__.py +1 -1
  39. zenml/integrations/mlflow/experiment_trackers/mlflow_experiment_tracker.py +3 -1
  40. zenml/integrations/mlflow/flavors/mlflow_experiment_tracker_flavor.py +3 -0
  41. zenml/logger.py +13 -0
  42. zenml/models/__init__.py +26 -22
  43. zenml/models/v2/base/filter.py +32 -0
  44. zenml/models/v2/core/pipeline.py +73 -89
  45. zenml/models/v2/core/pipeline_build.py +15 -11
  46. zenml/models/v2/core/pipeline_deployment.py +72 -24
  47. zenml/models/v2/core/pipeline_run.py +65 -1
  48. zenml/models/v2/core/run_template.py +393 -0
  49. zenml/models/v2/core/server_settings.py +12 -0
  50. zenml/models/v2/core/user.py +0 -21
  51. zenml/models/v2/misc/server_models.py +7 -1
  52. zenml/models/v2/misc/stack_deployment.py +5 -0
  53. zenml/models/v2/misc/user_auth.py +0 -7
  54. zenml/new/pipelines/build_utils.py +220 -89
  55. zenml/new/pipelines/code_archive.py +157 -0
  56. zenml/new/pipelines/pipeline.py +46 -78
  57. zenml/new/pipelines/run_utils.py +79 -1
  58. zenml/post_execution/pipeline.py +1 -4
  59. zenml/service_connectors/service_connector_utils.py +18 -2
  60. zenml/stack_deployments/aws_stack_deployment.py +32 -8
  61. zenml/stack_deployments/azure_stack_deployment.py +122 -10
  62. zenml/stack_deployments/gcp_stack_deployment.py +36 -7
  63. zenml/stack_deployments/stack_deployment.py +23 -7
  64. zenml/steps/base_step.py +3 -0
  65. zenml/steps/utils.py +0 -4
  66. zenml/utils/archivable.py +149 -0
  67. zenml/utils/code_utils.py +244 -0
  68. zenml/utils/notebook_utils.py +122 -0
  69. zenml/utils/package_utils.py +39 -0
  70. zenml/utils/pipeline_docker_image_builder.py +3 -96
  71. zenml/utils/source_utils.py +109 -1
  72. zenml/zen_server/dashboard/assets/{404-B_YdvmwS.js → 404-CRAA_Lew.js} +1 -1
  73. zenml/zen_server/dashboard/assets/@radix-BXWm7HOa.js +85 -0
  74. zenml/zen_server/dashboard/assets/{@react-router-CO-OsFwI.js → @react-router-l3lMcXA2.js} +1 -1
  75. zenml/zen_server/dashboard/assets/{@reactflow-l_1hUr1S.js → @reactflow-CeVxyqYT.js} +2 -2
  76. zenml/zen_server/dashboard/assets/{@tanstack-DYiOyJUL.js → @tanstack-FmcYZMuX.js} +4 -4
  77. zenml/zen_server/dashboard/assets/AlertDialogDropdownItem-ErO9aOgK.js +1 -0
  78. zenml/zen_server/dashboard/assets/{AwarenessChannel-CFg5iX4Z.js → AwarenessChannel-CLXo5rKM.js} +1 -1
  79. zenml/zen_server/dashboard/assets/{CodeSnippet-Dvkx_82E.js → CodeSnippet-D0VLxT2A.js} +2 -2
  80. zenml/zen_server/dashboard/assets/CollapsibleCard-BaUPiVg0.js +1 -0
  81. zenml/zen_server/dashboard/assets/{Commands-DoN1xrEq.js → Commands-JrcZK-3j.js} +1 -1
  82. zenml/zen_server/dashboard/assets/CopyButton-Dbo52T1K.js +2 -0
  83. zenml/zen_server/dashboard/assets/{CsvVizualization-Ck-nZ43m.js → CsvVizualization-D3kAypDj.js} +3 -3
  84. zenml/zen_server/dashboard/assets/DisplayDate-DizbSeT-.js +1 -0
  85. zenml/zen_server/dashboard/assets/EditSecretDialog-Bd7mFLS4.js +1 -0
  86. zenml/zen_server/dashboard/assets/{EmptyState-BMLnFVlB.js → EmptyState-BHblM39I.js} +1 -1
  87. zenml/zen_server/dashboard/assets/{Error-kLtljEOM.js → Error-C6LeJSER.js} +1 -1
  88. zenml/zen_server/dashboard/assets/{ExecutionStatus-DguLLgTK.js → ExecutionStatus-jH4OrWBq.js} +1 -1
  89. zenml/zen_server/dashboard/assets/{Helpbox-BXUMP21n.js → Helpbox-aAB2XP-z.js} +1 -1
  90. zenml/zen_server/dashboard/assets/{Infobox-DSt0O-dm.js → Infobox-BQ0aty32.js} +1 -1
  91. zenml/zen_server/dashboard/assets/{InlineAvatar-xsrsIGE-.js → InlineAvatar-DpTLgM3Q.js} +1 -1
  92. zenml/zen_server/dashboard/assets/Lock-CNyJvf2r.js +1 -0
  93. zenml/zen_server/dashboard/assets/{MarkdownVisualization-xp3hhULl.js → MarkdownVisualization-Bajxn0HY.js} +1 -1
  94. zenml/zen_server/dashboard/assets/NumberBox-BmKE0qnO.js +1 -0
  95. zenml/zen_server/dashboard/assets/{PasswordChecker-DUveqlva.js → PasswordChecker-yGGoJSB-.js} +1 -1
  96. zenml/zen_server/dashboard/assets/ProviderRadio-BBqkIuTd.js +1 -0
  97. zenml/zen_server/dashboard/assets/RadioItem-xLhXoiFV.js +1 -0
  98. zenml/zen_server/dashboard/assets/SearchField-C9R0mdaX.js +1 -0
  99. zenml/zen_server/dashboard/assets/{SetPassword-BXGTWiwj.js → SetPassword-52sNxNiO.js} +1 -1
  100. zenml/zen_server/dashboard/assets/{SuccessStep-DZC60t0x.js → SuccessStep-DlkItqYG.js} +1 -1
  101. zenml/zen_server/dashboard/assets/Tick-uxv80Q6a.js +1 -0
  102. zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-DGvwFWO1.js → UpdatePasswordSchemas-oN4G3sKz.js} +1 -1
  103. zenml/zen_server/dashboard/assets/{aws-BgKTfTfx.js → aws-0_3UsPif.js} +1 -1
  104. zenml/zen_server/dashboard/assets/{check-circle-i56092KI.js → check-circle-1_I207rW.js} +1 -1
  105. zenml/zen_server/dashboard/assets/chevron-down-BpaF8JqM.js +1 -0
  106. zenml/zen_server/dashboard/assets/{chevron-right-double-CZBOf6JM.js → chevron-right-double-Dk8e2L99.js} +1 -1
  107. zenml/zen_server/dashboard/assets/{cloud-only-C_yFCAkP.js → cloud-only-BkUuI0lZ.js} +1 -1
  108. zenml/zen_server/dashboard/assets/components-Br2ezRib.js +1 -0
  109. zenml/zen_server/dashboard/assets/{copy-BXNk6BjL.js → copy-f3XGPPxt.js} +1 -1
  110. zenml/zen_server/dashboard/assets/{database-1xWSgZfO.js → database-cXYNX9tt.js} +1 -1
  111. zenml/zen_server/dashboard/assets/{docker-CQMVm_4d.js → docker-8uj__HHK.js} +1 -1
  112. zenml/zen_server/dashboard/assets/dots-horizontal-sKQlWEni.js +1 -0
  113. zenml/zen_server/dashboard/assets/edit-C0MVvPD2.js +1 -0
  114. zenml/zen_server/dashboard/assets/{file-text-CqD_iu6l.js → file-text-B9JibxTs.js} +1 -1
  115. zenml/zen_server/dashboard/assets/{help-bu_DgLKI.js → help-FuHlZwn0.js} +1 -1
  116. zenml/zen_server/dashboard/assets/{index-rK_Wuy2W.js → index-Bd1xgUQG.js} +1 -1
  117. zenml/zen_server/dashboard/assets/index-DaGknux4.css +1 -0
  118. zenml/zen_server/dashboard/assets/{index-BczVOqUf.js → index-DhIZtpxB.js} +5 -5
  119. zenml/zen_server/dashboard/assets/index.esm-DT4uyn2i.js +1 -0
  120. zenml/zen_server/dashboard/assets/layout-D6oiSbfd.js +1 -0
  121. zenml/zen_server/dashboard/assets/{login-mutation-CrHrndTI.js → login-mutation-13A_JSVA.js} +1 -1
  122. zenml/zen_server/dashboard/assets/{logs-D8k8BVFf.js → logs-CgeE2vZP.js} +1 -1
  123. zenml/zen_server/dashboard/assets/{not-found-DYa4pC-C.js → not-found-B0Mmb90p.js} +1 -1
  124. zenml/zen_server/dashboard/assets/package-DdkziX79.js +1 -0
  125. zenml/zen_server/dashboard/assets/page-7-v2OBm-.js +1 -0
  126. zenml/zen_server/dashboard/assets/{page-MFQyIJd3.js → page-B3ozwdD1.js} +1 -1
  127. zenml/zen_server/dashboard/assets/{page-BkuQDIf-.js → page-BGwA9B1M.js} +1 -1
  128. zenml/zen_server/dashboard/assets/{page-1iL8aMqs.js → page-BkjAUyTA.js} +1 -1
  129. zenml/zen_server/dashboard/assets/page-BnacgBiy.js +1 -0
  130. zenml/zen_server/dashboard/assets/page-BxF_KMQ3.js +2 -0
  131. zenml/zen_server/dashboard/assets/page-C4POHC0K.js +1 -0
  132. zenml/zen_server/dashboard/assets/page-C9kudd44.js +9 -0
  133. zenml/zen_server/dashboard/assets/page-CA1j3GpJ.js +1 -0
  134. zenml/zen_server/dashboard/assets/page-CCY6yfmu.js +1 -0
  135. zenml/zen_server/dashboard/assets/page-CgTe7Bme.js +1 -0
  136. zenml/zen_server/dashboard/assets/{page-8a4UMKXZ.js → page-Cgn-6v2Y.js} +1 -1
  137. zenml/zen_server/dashboard/assets/page-CxQmQqDw.js +1 -0
  138. zenml/zen_server/dashboard/assets/page-D2Goey3H.js +1 -0
  139. zenml/zen_server/dashboard/assets/page-DLpOnf7u.js +1 -0
  140. zenml/zen_server/dashboard/assets/{page-BhgCDInH.js → page-DSTQnBk-.js} +1 -1
  141. zenml/zen_server/dashboard/assets/{page-1h_sD1jz.js → page-DTysUGOy.js} +1 -1
  142. zenml/zen_server/dashboard/assets/{page-2grKx_MY.js → page-D_EXUFJb.js} +1 -1
  143. zenml/zen_server/dashboard/assets/page-Db15QzsM.js +1 -0
  144. zenml/zen_server/dashboard/assets/{page-BDns21Iz.js → page-DugsjcQ_.js} +1 -1
  145. zenml/zen_server/dashboard/assets/{page-C6-UGEbH.js → page-OFKSPyN7.js} +1 -1
  146. zenml/zen_server/dashboard/assets/{page-BkeAAYwp.js → page-RnG-qhv9.js} +1 -1
  147. zenml/zen_server/dashboard/assets/{page-CCNRIt_f.js → page-T2BtjwPl.js} +1 -1
  148. zenml/zen_server/dashboard/assets/page-TXe1Eo3Z.js +1 -0
  149. zenml/zen_server/dashboard/assets/{page-BnaevhnB.js → page-YiF_fNbe.js} +1 -1
  150. zenml/zen_server/dashboard/assets/{page-uA5prJGY.js → page-hQaiQXfg.js} +1 -1
  151. zenml/zen_server/dashboard/assets/persist-3-5nOJ6m.js +1 -0
  152. zenml/zen_server/dashboard/assets/{play-circle-CNtZKDnW.js → play-circle-XSkLR12B.js} +1 -1
  153. zenml/zen_server/dashboard/assets/plus-FB9-lEq_.js +1 -0
  154. zenml/zen_server/dashboard/assets/refresh-COb6KYDi.js +1 -0
  155. zenml/zen_server/dashboard/assets/sharedSchema-BoYx_B_L.js +14 -0
  156. zenml/zen_server/dashboard/assets/{stack-detail-query-Cficsl6d.js → stack-detail-query-B-US_-wa.js} +1 -1
  157. zenml/zen_server/dashboard/assets/{terminal-By9cErXc.js → terminal-grtjrIEJ.js} +1 -1
  158. zenml/zen_server/dashboard/assets/trash-Cd5CSFqA.js +1 -0
  159. zenml/zen_server/dashboard/assets/{update-server-settings-mutation-7d8xi1tS.js → update-server-settings-mutation-B8GB_ubU.js} +1 -1
  160. zenml/zen_server/dashboard/assets/{url-D7mAQGUM.js → url-hcMJkz8p.js} +1 -1
  161. zenml/zen_server/dashboard/assets/{zod-BhoGpZ63.js → zod-CnykDKJj.js} +1 -1
  162. zenml/zen_server/dashboard/index.html +7 -7
  163. zenml/zen_server/dashboard_legacy/asset-manifest.json +4 -4
  164. zenml/zen_server/dashboard_legacy/index.html +1 -1
  165. zenml/zen_server/dashboard_legacy/{precache-manifest.12246c7548e71e2c4438e496360de80c.js → precache-manifest.9c473c96a43298343a7ce1256183123b.js} +4 -4
  166. zenml/zen_server/dashboard_legacy/service-worker.js +1 -1
  167. zenml/zen_server/dashboard_legacy/static/js/{main.3b27024b.chunk.js → main.463c90b9.chunk.js} +2 -2
  168. zenml/zen_server/dashboard_legacy/static/js/{main.3b27024b.chunk.js.map → main.463c90b9.chunk.js.map} +1 -1
  169. zenml/zen_server/deploy/helm/Chart.yaml +1 -1
  170. zenml/zen_server/deploy/helm/README.md +2 -2
  171. zenml/zen_server/rbac/models.py +1 -0
  172. zenml/zen_server/rbac/utils.py +4 -0
  173. zenml/zen_server/routers/pipeline_builds_endpoints.py +2 -66
  174. zenml/zen_server/routers/pipeline_deployments_endpoints.py +2 -53
  175. zenml/zen_server/routers/pipelines_endpoints.py +1 -74
  176. zenml/zen_server/routers/run_templates_endpoints.py +212 -0
  177. zenml/zen_server/routers/stack_deployment_endpoints.py +6 -0
  178. zenml/zen_server/routers/users_endpoints.py +0 -7
  179. zenml/zen_server/routers/workspaces_endpoints.py +79 -0
  180. zenml/zen_server/{pipeline_deployment → template_execution}/runner_entrypoint_configuration.py +1 -8
  181. zenml/zen_server/{pipeline_deployment → template_execution}/utils.py +214 -92
  182. zenml/zen_server/utils.py +77 -2
  183. zenml/zen_server/zen_server_api.py +54 -2
  184. zenml/zen_stores/base_zen_store.py +7 -1
  185. zenml/zen_stores/migrations/versions/0.63.0_release.py +23 -0
  186. zenml/zen_stores/migrations/versions/0.64.0_release.py +23 -0
  187. zenml/zen_stores/migrations/versions/026d4577b6a0_add_code_path.py +39 -0
  188. zenml/zen_stores/migrations/versions/3dcc5d20e82f_add_last_user_activity.py +51 -0
  189. zenml/zen_stores/migrations/versions/7d1919bb1ef0_add_run_templates.py +100 -0
  190. zenml/zen_stores/migrations/versions/909550c7c4da_remove_user_hub_token.py +36 -0
  191. zenml/zen_stores/migrations/versions/b59aa68fdb1f_simplify_pipelines.py +139 -0
  192. zenml/zen_stores/rest_zen_store.py +112 -39
  193. zenml/zen_stores/schemas/__init__.py +2 -0
  194. zenml/zen_stores/schemas/pipeline_build_schemas.py +3 -3
  195. zenml/zen_stores/schemas/pipeline_deployment_schemas.py +32 -2
  196. zenml/zen_stores/schemas/pipeline_run_schemas.py +29 -3
  197. zenml/zen_stores/schemas/pipeline_schemas.py +29 -30
  198. zenml/zen_stores/schemas/run_template_schemas.py +264 -0
  199. zenml/zen_stores/schemas/server_settings_schemas.py +2 -0
  200. zenml/zen_stores/schemas/step_run_schemas.py +11 -4
  201. zenml/zen_stores/schemas/user_schemas.py +0 -2
  202. zenml/zen_stores/sql_zen_store.py +389 -151
  203. zenml/zen_stores/template_utils.py +261 -0
  204. zenml/zen_stores/zen_store_interface.py +93 -20
  205. {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/METADATA +3 -3
  206. {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/RECORD +211 -184
  207. zenml/_hub/client.py +0 -289
  208. zenml/_hub/constants.py +0 -21
  209. zenml/_hub/utils.py +0 -79
  210. zenml/cli/hub.py +0 -1116
  211. zenml/models/v2/core/pipeline_namespace.py +0 -113
  212. zenml/models/v2/misc/hub_plugin_models.py +0 -79
  213. zenml/new/pipelines/deserialization_utils.py +0 -292
  214. zenml/zen_server/dashboard/assets/@radix-CFOkMR_E.js +0 -85
  215. zenml/zen_server/dashboard/assets/CollapsibleCard-opiuBHHc.js +0 -1
  216. zenml/zen_server/dashboard/assets/CopyButton-Cr7xYEPb.js +0 -2
  217. zenml/zen_server/dashboard/assets/DisplayDate-DYgIjlDF.js +0 -1
  218. zenml/zen_server/dashboard/assets/Pagination-C6X-mifw.js +0 -1
  219. zenml/zen_server/dashboard/assets/index-EpMIKgrI.css +0 -1
  220. zenml/zen_server/dashboard/assets/index.esm-Corw4lXQ.js +0 -1
  221. zenml/zen_server/dashboard/assets/package-B3fWP-Dh.js +0 -1
  222. zenml/zen_server/dashboard/assets/page-5NCOHOsy.js +0 -1
  223. zenml/zen_server/dashboard/assets/page-B6h3iaHJ.js +0 -1
  224. zenml/zen_server/dashboard/assets/page-Bi-wtWiO.js +0 -5
  225. zenml/zen_server/dashboard/assets/page-Bq0YxkLV.js +0 -1
  226. zenml/zen_server/dashboard/assets/page-Bs2F4eoD.js +0 -2
  227. zenml/zen_server/dashboard/assets/page-CHNxpz3n.js +0 -1
  228. zenml/zen_server/dashboard/assets/page-DgorQFqi.js +0 -1
  229. zenml/zen_server/dashboard/assets/page-K8ebxVIs.js +0 -1
  230. zenml/zen_server/dashboard/assets/page-TgCF0P_U.js +0 -1
  231. zenml/zen_server/dashboard/assets/page-ZnCEe-eK.js +0 -9
  232. zenml/zen_server/dashboard/assets/persist-D7HJNBWx.js +0 -1
  233. zenml/zen_server/dashboard/assets/plus-C8WOyCzt.js +0 -1
  234. zenml/zen_server/dashboard/assets/secrets-video-OBJ6irhH.svg +0 -21
  235. zenml/zen_server/dashboard/assets/stacks-video-7gfxpAq4.svg +0 -21
  236. /zenml/zen_server/{pipeline_deployment → template_execution}/__init__.py +0 -0
  237. /zenml/zen_server/{pipeline_deployment → template_execution}/workload_manager_interface.py +0 -0
  238. {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/LICENSE +0 -0
  239. {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/WHEEL +0 -0
  240. {zenml_nightly-0.62.0.dev20240729.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(
@@ -20,6 +20,7 @@ from zenml.integrations.databricks import DATABRICKS_ORCHESTRATOR_FLAVOR
20
20
  from zenml.logger import get_logger
21
21
  from zenml.orchestrators import BaseOrchestratorConfig
22
22
  from zenml.orchestrators.base_orchestrator import BaseOrchestratorFlavor
23
+ from zenml.utils.enum_utils import StrEnum
23
24
  from zenml.utils.secret_utils import SecretField
24
25
 
25
26
  if TYPE_CHECKING:
@@ -27,10 +28,17 @@ if TYPE_CHECKING:
27
28
  DatabricksOrchestrator,
28
29
  )
29
30
 
30
-
31
31
  logger = get_logger(__name__)
32
32
 
33
33
 
34
+ class DatabricksAvailabilityType(StrEnum):
35
+ """Databricks availability type."""
36
+
37
+ ON_DEMAND = "ON_DEMAND"
38
+ SPOT = "SPOT"
39
+ SPOT_WITH_FALLBACK = "SPOT_WITH_FALLBACK"
40
+
41
+
34
42
  class DatabricksOrchestratorSettings(BaseSettings):
35
43
  """Databricks orchestrator base settings.
36
44
 
@@ -53,11 +61,12 @@ class DatabricksOrchestratorSettings(BaseSettings):
53
61
  node_type_id: Optional[str] = None
54
62
  policy_id: Optional[str] = None
55
63
  autotermination_minutes: Optional[int] = None
56
- autoscale: Tuple[int, int] = (2, 3)
64
+ autoscale: Tuple[int, int] = (0, 1)
57
65
  single_user_name: Optional[str] = None
58
66
  spark_conf: Optional[Dict[str, str]] = None
59
67
  spark_env_vars: Optional[Dict[str, str]] = None
60
68
  schedule_timezone: Optional[str] = None
69
+ availability_type: Optional[DatabricksAvailabilityType] = None
61
70
 
62
71
 
63
72
  class DatabricksOrchestratorConfig(
@@ -84,6 +93,15 @@ class DatabricksOrchestratorConfig(
84
93
  """
85
94
  return False
86
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
+
87
105
 
88
106
  class DatabricksOrchestratorFlavor(BaseOrchestratorFlavor):
89
107
  """Databricks orchestrator flavor."""