zenml-nightly 0.68.1.dev20241111__py3-none-any.whl → 0.70.0.dev20241114__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 (202) hide show
  1. README.md +1 -1
  2. RELEASE_NOTES.md +77 -0
  3. zenml/VERSION +1 -1
  4. zenml/__init__.py +2 -0
  5. zenml/artifacts/utils.py +5 -1
  6. zenml/cli/base.py +1 -1
  7. zenml/client.py +9 -2
  8. zenml/config/server_config.py +17 -1
  9. zenml/constants.py +1 -7
  10. zenml/enums.py +8 -0
  11. zenml/exceptions.py +4 -0
  12. zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +1 -14
  13. zenml/integrations/lightning/orchestrators/lightning_orchestrator.py +29 -9
  14. zenml/model/model.py +16 -62
  15. zenml/model/utils.py +5 -0
  16. zenml/models/v2/base/filter.py +121 -8
  17. zenml/models/v2/core/artifact_version.py +23 -0
  18. zenml/models/v2/core/model_version.py +23 -0
  19. zenml/models/v2/core/pipeline_run.py +22 -1
  20. zenml/models/v2/core/step_run.py +22 -0
  21. zenml/orchestrators/base_orchestrator.py +12 -1
  22. zenml/orchestrators/step_launcher.py +2 -1
  23. zenml/orchestrators/utils.py +45 -26
  24. zenml/stack_deployments/aws_stack_deployment.py +23 -6
  25. zenml/stack_deployments/azure_stack_deployment.py +28 -5
  26. zenml/stack_deployments/gcp_stack_deployment.py +25 -8
  27. zenml/stack_deployments/stack_deployment.py +3 -5
  28. zenml/steps/utils.py +5 -0
  29. zenml/utils/metadata_utils.py +335 -0
  30. zenml/zen_server/auth.py +221 -3
  31. zenml/zen_server/cache.py +208 -0
  32. zenml/zen_server/dashboard/assets/{404-DT4QRUqN.js → 404-NVXKFp-x.js} +1 -1
  33. zenml/zen_server/dashboard/assets/{@radix-DP6vWzyx.js → @radix-DeK6qiuw.js} +1 -1
  34. zenml/zen_server/dashboard/assets/{@react-router-BMhZulnd.js → @react-router-B3Z5rLr2.js} +1 -1
  35. zenml/zen_server/dashboard/assets/{@reactflow-8U9qNlMR.js → @reactflow-CK0KJUen.js} +2 -2
  36. zenml/zen_server/dashboard/assets/{@tanstack-BUCbhJyH.js → @tanstack-DT5WLu9C.js} +1 -1
  37. zenml/zen_server/dashboard/assets/AlertDialogDropdownItem-DezXKmDf.js +1 -0
  38. zenml/zen_server/dashboard/assets/{CodeSnippet-CqybNv0k.js → CodeSnippet-JzR8CEtw.js} +2 -2
  39. zenml/zen_server/dashboard/assets/{CollapsibleCard-0r_8G2Lj.js → CollapsibleCard-DQW_ktMO.js} +1 -1
  40. zenml/zen_server/dashboard/assets/{Commands-BDjgBQKi.js → Commands-DL2kwkRd.js} +1 -1
  41. zenml/zen_server/dashboard/assets/ComponentBadge-D_g62Wv8.js +1 -0
  42. zenml/zen_server/dashboard/assets/{CopyButton-C745BrKi.js → CopyButton-LNcWaa14.js} +1 -1
  43. zenml/zen_server/dashboard/assets/{CsvVizualization-PpAq0CeZ.js → CsvVizualization-DknpE5ej.js} +5 -5
  44. zenml/zen_server/dashboard/assets/{DialogItem-DcVCZEew.js → DialogItem-Bxf8FuAT.js} +1 -1
  45. zenml/zen_server/dashboard/assets/{DisplayDate-BeXgUG_C.js → DisplayDate-CDMUcQHS.js} +1 -1
  46. zenml/zen_server/dashboard/assets/{EmptyState-DeK7H4pr.js → EmptyState-BzdlCwp3.js} +1 -1
  47. zenml/zen_server/dashboard/assets/{Error-BMlzibXj.js → Error-DYflYyps.js} +1 -1
  48. zenml/zen_server/dashboard/assets/ExecutionStatus-C7zyIQKZ.js +1 -0
  49. zenml/zen_server/dashboard/assets/{Helpbox-BLf40fLV.js → Helpbox-oYSGpLqd.js} +1 -1
  50. zenml/zen_server/dashboard/assets/{Infobox-BwisKifi.js → Infobox-Cx4xGoXR.js} +1 -1
  51. zenml/zen_server/dashboard/assets/{InlineAvatar-jEgodSgX.js → InlineAvatar-DiGOWNKF.js} +1 -1
  52. zenml/zen_server/dashboard/assets/{Lock-3lLt1ih0.js → Lock-CYYy18Mm.js} +1 -1
  53. zenml/zen_server/dashboard/assets/{MarkdownVisualization-8O9kTr-2.js → MarkdownVisualization-ylXaAxev.js} +1 -1
  54. zenml/zen_server/dashboard/assets/NestedCollapsible-DYbgyKxK.js +1 -0
  55. zenml/zen_server/dashboard/assets/{NumberBox-T9eELfLZ.js → NumberBox-Dtp3J6g5.js} +1 -1
  56. zenml/zen_server/dashboard/assets/Partials-03iZf8-N.js +1 -0
  57. zenml/zen_server/dashboard/assets/{PasswordChecker-CW0kqY0W.js → PasswordChecker-B0nadgh6.js} +1 -1
  58. zenml/zen_server/dashboard/assets/ProBadge-D_EB8HNo.js +1 -0
  59. zenml/zen_server/dashboard/assets/ProCta-DqNS4v3x.js +1 -0
  60. zenml/zen_server/dashboard/assets/ProviderIcon-Bki2aw8w.js +1 -0
  61. zenml/zen_server/dashboard/assets/{ProviderRadio-BROY1700.js → ProviderRadio-8f43sPD4.js} +1 -1
  62. zenml/zen_server/dashboard/assets/RunSelector-DkPiIiNr.js +1 -0
  63. zenml/zen_server/dashboard/assets/RunsBody-07YEO7qI.js +1 -0
  64. zenml/zen_server/dashboard/assets/SearchField-lp1KgU4e.js +1 -0
  65. zenml/zen_server/dashboard/assets/{SecretTooltip-C_qByGWB.js → SecretTooltip-CgnbyeOx.js} +1 -1
  66. zenml/zen_server/dashboard/assets/{SetPassword-7pRB00El.js → SetPassword-CpP418A2.js} +1 -1
  67. zenml/zen_server/dashboard/assets/StackList-WvuKQusZ.js +1 -0
  68. zenml/zen_server/dashboard/assets/Tabs-BktHkCJJ.js +1 -0
  69. zenml/zen_server/dashboard/assets/Tick-BlMoIlJT.js +1 -0
  70. zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-DckMEkFf.js → UpdatePasswordSchemas-Sc0A0pP-.js} +1 -1
  71. zenml/zen_server/dashboard/assets/{UsageReason-DVceN14P.js → UsageReason-YYduL4fj.js} +1 -1
  72. zenml/zen_server/dashboard/assets/{WizardFooter-CW0Cvd70.js → WizardFooter-dgmizSJC.js} +1 -1
  73. zenml/zen_server/dashboard/assets/all-pipeline-runs-query-D-c2G6lV.js +1 -0
  74. zenml/zen_server/dashboard/assets/check-DloQpStc.js +1 -0
  75. zenml/zen_server/dashboard/assets/{check-circle-Dwxliy1Z.js → check-circle-jNbX5-sR.js} +1 -1
  76. zenml/zen_server/dashboard/assets/{chevron-down-8wLBS5pQ.js → chevron-down-6JyMkfjR.js} +1 -1
  77. zenml/zen_server/dashboard/assets/{chevron-right-double-DoD8iXWM.js → chevron-right-double-D7ojK9Co.js} +1 -1
  78. zenml/zen_server/dashboard/assets/{code-browser-CZUQs3Wa.js → code-browser-CUFUIHfp.js} +1 -1
  79. zenml/zen_server/dashboard/assets/{copy-CaSMXwiU.js → copy-C8XQA2Ug.js} +1 -1
  80. zenml/zen_server/dashboard/assets/create-stack-DM_JPgef.js +1 -0
  81. zenml/zen_server/dashboard/assets/delete-run-CJdh1P_h.js +1 -0
  82. zenml/zen_server/dashboard/assets/{docker-BFNgg-z3.js → docker-BdA9vrnW.js} +1 -1
  83. zenml/zen_server/dashboard/assets/{dots-horizontal-DK5Duzx4.js → dots-horizontal-otGBOSDJ.js} +1 -1
  84. zenml/zen_server/dashboard/assets/{form-schemas-1AyOCx90.js → form-schemas-K6FYKjwa.js} +1 -1
  85. zenml/zen_server/dashboard/assets/{gcp-7M2Yf3ZK.js → gcp-CFtm4BA7.js} +1 -1
  86. zenml/zen_server/dashboard/assets/{help-Dam461dC.js → help-Cc9bBIJH.js} +1 -1
  87. zenml/zen_server/dashboard/assets/index-B1mVPYxf.js +1 -0
  88. zenml/zen_server/dashboard/assets/index-BAkC7FXi.js +1 -0
  89. zenml/zen_server/dashboard/assets/{index-QQb7wQEC.js → index-CCOPpudF.js} +8 -8
  90. zenml/zen_server/dashboard/assets/index-CEV4Cvaf.js +1 -0
  91. zenml/zen_server/dashboard/assets/index-DlGvJQPn.css +1 -0
  92. zenml/zen_server/dashboard/assets/{index-BVJ8n2-j.js → index-Uu49AX48.js} +1 -1
  93. zenml/zen_server/dashboard/assets/{index.esm-cuVep_NJ.js → index.esm-Dy6Z9Ung.js} +1 -1
  94. zenml/zen_server/dashboard/assets/{kubernetes--g7r02Zu.js → kubernetes-B2wmAJ1d.js} +1 -1
  95. zenml/zen_server/dashboard/assets/{layout-DCSYN7-C.js → layout-BtHBmE4w.js} +1 -1
  96. zenml/zen_server/dashboard/assets/{link-external-CBEk6kEG.js → link-external-b9AXw_sW.js} +1 -1
  97. zenml/zen_server/dashboard/assets/{login-mutation-DTcAFP1l.js → login-mutation-hf-lK87O.js} +1 -1
  98. zenml/zen_server/dashboard/assets/{logs-D5bdJGur.js → logs-WMSM52RF.js} +1 -1
  99. zenml/zen_server/dashboard/assets/{not-found-Cc-JkRH2.js → not-found-BGirLjU-.js} +1 -1
  100. zenml/zen_server/dashboard/assets/{package-Cs35Szwh.js → package-C6uypY4h.js} +1 -1
  101. zenml/zen_server/dashboard/assets/page-0JE_-Ec1.js +1 -0
  102. zenml/zen_server/dashboard/assets/{page-DH_Z7iW1.js → page-6m6yHHlE.js} +1 -1
  103. zenml/zen_server/dashboard/assets/page-BDigxVpo.js +1 -0
  104. zenml/zen_server/dashboard/assets/page-BR68V0V1.js +1 -0
  105. zenml/zen_server/dashboard/assets/page-BRLpxOt0.js +1 -0
  106. zenml/zen_server/dashboard/assets/{page-BQQKaabe.js → page-BU7huvKw.js} +3 -3
  107. zenml/zen_server/dashboard/assets/page-BvqLv2Ky.js +1 -0
  108. zenml/zen_server/dashboard/assets/page-C00YAkaB.js +1 -0
  109. zenml/zen_server/dashboard/assets/{page-N4qoPHKb.js → page-CD-DcWoy.js} +1 -1
  110. zenml/zen_server/dashboard/assets/page-COXXJj1k.js +1 -0
  111. zenml/zen_server/dashboard/assets/page-CbpvrsDL.js +1 -0
  112. zenml/zen_server/dashboard/assets/page-CdMWnQak.js +1 -0
  113. zenml/zen_server/dashboard/assets/{page-ClUVkl-O.js → page-CjGdWY13.js} +1 -1
  114. zenml/zen_server/dashboard/assets/page-CwxrFarU.js +1 -0
  115. zenml/zen_server/dashboard/assets/{page-DLixvR-7.js → page-D01JhjQB.js} +1 -1
  116. zenml/zen_server/dashboard/assets/page-D6uU2ax4.js +1 -0
  117. zenml/zen_server/dashboard/assets/page-D7S3aCbF.js +1 -0
  118. zenml/zen_server/dashboard/assets/{page-9yplj5JT.js → page-DLC-bNBP.js} +1 -1
  119. zenml/zen_server/dashboard/assets/page-DXSTpqRD.js +1 -0
  120. zenml/zen_server/dashboard/assets/{page-DzpVUZ8f.js → page-DakHVWXF.js} +1 -1
  121. zenml/zen_server/dashboard/assets/{page-DIOXwhiD.js → page-Df-Fw0aq.js} +1 -1
  122. zenml/zen_server/dashboard/assets/{page-B-y2XKIc.js → page-DfbXf_8s.js} +1 -1
  123. zenml/zen_server/dashboard/assets/page-DjRJCGb3.js +1 -0
  124. zenml/zen_server/dashboard/assets/{page-C0N5q3l7.js → page-Djikxq_S.js} +1 -1
  125. zenml/zen_server/dashboard/assets/page-Dnovpa0i.js +3 -0
  126. zenml/zen_server/dashboard/assets/page-Dot3LPmL.js +1 -0
  127. zenml/zen_server/dashboard/assets/page-Vcxara9U.js +1 -0
  128. zenml/zen_server/dashboard/assets/page-Xynx4btY.js +14 -0
  129. zenml/zen_server/dashboard/assets/page-YpKAqVSa.js +1 -0
  130. zenml/zen_server/dashboard/assets/page-yYC9OI-E.js +1 -0
  131. zenml/zen_server/dashboard/assets/{persist-DNb5cdrU.js → persist-Coz7ZWvz.js} +1 -1
  132. zenml/zen_server/dashboard/assets/{persist-CP0JmYZ4.js → persist-GjC8PZoC.js} +1 -1
  133. zenml/zen_server/dashboard/assets/{plus-C9IxgN2M.js → plus-tf1V2hTJ.js} +1 -1
  134. zenml/zen_server/dashboard/assets/{refresh-BVu22P_C.js → refresh-BjOeWlEq.js} +1 -1
  135. zenml/zen_server/dashboard/assets/{rocket-CONEmRmB.js → rocket-DjT2cDvG.js} +1 -1
  136. zenml/zen_server/dashboard/assets/sharedSchema-CQb14VSr.js +14 -0
  137. zenml/zen_server/dashboard/assets/stack-detail-query-OPEW-cDJ.js +1 -0
  138. zenml/zen_server/dashboard/assets/{tick-circle-CM1ZScbQ.js → tick-circle-BEX_Tp4v.js} +1 -1
  139. zenml/zen_server/dashboard/assets/{trash-DkJHMOg7.js → trash-arLUMWMS.js} +1 -1
  140. zenml/zen_server/dashboard/assets/{update-server-settings-mutation-DsU8cNVl.js → update-server-settings-mutation-LwuQfHYn.js} +1 -1
  141. zenml/zen_server/dashboard/assets/upgrade-form-CwRHBuXB.webp +0 -0
  142. zenml/zen_server/dashboard/assets/url-CkvKAnwF.js +1 -0
  143. zenml/zen_server/dashboard/assets/{zod-D89GC_vc.js → zod-BwEbpOxH.js} +1 -1
  144. zenml/zen_server/dashboard/index.html +7 -7
  145. zenml/zen_server/deploy/helm/Chart.yaml +1 -1
  146. zenml/zen_server/deploy/helm/README.md +2 -2
  147. zenml/zen_server/exceptions.py +2 -0
  148. zenml/zen_server/jwt.py +30 -13
  149. zenml/zen_server/routers/auth_endpoints.py +134 -102
  150. zenml/zen_server/routers/logs_endpoints.py +66 -0
  151. zenml/zen_server/template_execution/utils.py +14 -16
  152. zenml/zen_server/utils.py +27 -0
  153. zenml/zen_server/zen_server_api.py +6 -1
  154. zenml/zen_stores/migrations/versions/0.70.0_release.py +23 -0
  155. zenml/zen_stores/migrations/versions/904464ea4041_add_pipeline_model_run_unique_constraints.py +192 -0
  156. zenml/zen_stores/rest_zen_store.py +13 -10
  157. zenml/zen_stores/schemas/model_schemas.py +25 -1
  158. zenml/zen_stores/schemas/pipeline_run_schemas.py +5 -0
  159. zenml/zen_stores/schemas/pipeline_schemas.py +8 -2
  160. zenml/zen_stores/sql_zen_store.py +215 -121
  161. {zenml_nightly-0.68.1.dev20241111.dist-info → zenml_nightly-0.70.0.dev20241114.dist-info}/METADATA +2 -2
  162. {zenml_nightly-0.68.1.dev20241111.dist-info → zenml_nightly-0.70.0.dev20241114.dist-info}/RECORD +165 -151
  163. zenml/zen_server/dashboard/assets/AlertDialogDropdownItem-C6N2rGrB.js +0 -1
  164. zenml/zen_server/dashboard/assets/ComponentBadge-DUiEYJHu.js +0 -1
  165. zenml/zen_server/dashboard/assets/ComponentFallbackDialog-BFoH5K4V.js +0 -1
  166. zenml/zen_server/dashboard/assets/ComponentIcon-CAIoUis2.js +0 -1
  167. zenml/zen_server/dashboard/assets/Partials-YPBB3V4q.js +0 -1
  168. zenml/zen_server/dashboard/assets/ProviderIcon-Bb3Xha5A.js +0 -1
  169. zenml/zen_server/dashboard/assets/RunSelector-DCiL3M1c.js +0 -1
  170. zenml/zen_server/dashboard/assets/SearchField-DfUiGFVd.js +0 -1
  171. zenml/zen_server/dashboard/assets/Tick-CykQFPj2.js +0 -1
  172. zenml/zen_server/dashboard/assets/cloud-only-B-s_HMDm.js +0 -1
  173. zenml/zen_server/dashboard/assets/codespaces-BitYDX9d.gif +0 -0
  174. zenml/zen_server/dashboard/assets/create-stack-CEmaPZ4c.js +0 -1
  175. zenml/zen_server/dashboard/assets/delete-run-D-LKbGyz.js +0 -1
  176. zenml/zen_server/dashboard/assets/index-Bpmj40BI.js +0 -1
  177. zenml/zen_server/dashboard/assets/index-CbU4Ln_E.css +0 -1
  178. zenml/zen_server/dashboard/assets/index-DKPhqP2B.js +0 -1
  179. zenml/zen_server/dashboard/assets/page-BBpOxVcY.js +0 -1
  180. zenml/zen_server/dashboard/assets/page-BRInM1Lg.js +0 -1
  181. zenml/zen_server/dashboard/assets/page-BjjlMk7s.js +0 -1
  182. zenml/zen_server/dashboard/assets/page-Bvd7YH2A.js +0 -1
  183. zenml/zen_server/dashboard/assets/page-CT3Nep8W.js +0 -1
  184. zenml/zen_server/dashboard/assets/page-C_f47pBf.js +0 -1
  185. zenml/zen_server/dashboard/assets/page-Cmv8C_yM.js +0 -3
  186. zenml/zen_server/dashboard/assets/page-CyN2bdWG.js +0 -1
  187. zenml/zen_server/dashboard/assets/page-CzzXH4fs.js +0 -1
  188. zenml/zen_server/dashboard/assets/page-DTlGjgnG.js +0 -1
  189. zenml/zen_server/dashboard/assets/page-Dbpl86h0.js +0 -1
  190. zenml/zen_server/dashboard/assets/page-Ddgy6kDS.js +0 -1
  191. zenml/zen_server/dashboard/assets/page-DtCAfBLy.js +0 -9
  192. zenml/zen_server/dashboard/assets/page-Dx16z7nA.js +0 -1
  193. zenml/zen_server/dashboard/assets/page-McUyYbo1.js +0 -1
  194. zenml/zen_server/dashboard/assets/page-T1P3RyAR.js +0 -1
  195. zenml/zen_server/dashboard/assets/page-bKaULTGG.js +0 -1
  196. zenml/zen_server/dashboard/assets/page-sbXUJy9t.js +0 -1
  197. zenml/zen_server/dashboard/assets/sharedSchema-TMLu-nYQ.js +0 -14
  198. zenml/zen_server/dashboard/assets/stack-detail-query-xmYxSsUY.js +0 -1
  199. zenml/zen_server/dashboard/assets/url-D5le3J4q.js +0 -1
  200. {zenml_nightly-0.68.1.dev20241111.dist-info → zenml_nightly-0.70.0.dev20241114.dist-info}/LICENSE +0 -0
  201. {zenml_nightly-0.68.1.dev20241111.dist-info → zenml_nightly-0.70.0.dev20241114.dist-info}/WHEEL +0 -0
  202. {zenml_nightly-0.68.1.dev20241111.dist-info → zenml_nightly-0.70.0.dev20241114.dist-info}/entry_points.txt +0 -0
@@ -70,11 +70,12 @@ and are aware of any potential costs:
70
70
 
71
71
  - A GCS bucket registered as a [ZenML artifact store](https://docs.zenml.io/stack-components/artifact-stores/gcp).
72
72
  - A Google Artifact Registry registered as a [ZenML container registry](https://docs.zenml.io/stack-components/container-registries/gcp).
73
- - Vertex AI registered as a [ZenML orchestrator](https://docs.zenml.io/stack-components/orchestrators/vertex).
73
+ - Vertex AI registered as a [ZenML orchestrator](https://docs.zenml.io/stack-components/orchestrators/vertex)
74
+ and as a [ZenML step operator](https://docs.zenml.io/stack-components/step-operators/vertex).
74
75
  - GCP Cloud Build registered as a [ZenML image builder](https://docs.zenml.io/stack-components/image-builders/gcp).
75
76
  - A GCP Service Account with the minimum necessary permissions to access the
76
77
  above resources.
77
- - An GCP Service Account access key used to give access to ZenML to connect to
78
+ - A GCP Service Account access key used to give access to ZenML to connect to
78
79
  the above resources through a [ZenML service connector](https://docs.zenml.io/how-to/auth-management/gcp-service-connector).
79
80
 
80
81
  The Deployment Manager deployment will automatically create a GCP Service
@@ -259,14 +260,30 @@ GCP project and to clean up the resources created by the stack by using
259
260
  )
260
261
 
261
262
  if self.deployment_type == STACK_DEPLOYMENT_TERRAFORM:
262
- config = f"""module "zenml_stack" {{
263
+ config = f"""terraform {{
264
+ required_providers {{
265
+ google = {{
266
+ source = "hashicorp/google"
267
+ }}
268
+ zenml = {{
269
+ source = "zenml-io/zenml"
270
+ }}
271
+ }}
272
+ }}
273
+
274
+ provider "google" {{
275
+ region = "{self.location or "europe-west3"}"
276
+ project = your GCP project name
277
+ }}
278
+
279
+ provider "zenml" {{
280
+ server_url = "{self.zenml_server_url}"
281
+ api_token = "{self.zenml_server_api_token}"
282
+ }}
283
+
284
+ module "zenml_stack" {{
263
285
  source = "zenml-io/zenml-stack/gcp"
264
286
 
265
- project_id = "my-gcp-project"
266
- region = "{self.location or "europe-west3"}"
267
- zenml_server_url = "{self.zenml_server_url}"
268
- zenml_api_key = ""
269
- zenml_api_token = "{self.zenml_server_api_token}"
270
287
  zenml_stack_name = "{self.stack_name}"
271
288
  zenml_stack_deployment = "{self.deployment_type}"
272
289
  }}
@@ -219,16 +219,14 @@ class ZenMLCloudStackDeployment(BaseModel):
219
219
  if stack.labels.get("zenml:deployment") != self.deployment_type:
220
220
  continue
221
221
 
222
- artifact_store = stack.components[
223
- StackComponentType.ARTIFACT_STORE
224
- ][0]
222
+ orchestrator = stack.components[StackComponentType.ORCHESTRATOR][0]
225
223
 
226
- if not artifact_store.connector:
224
+ if not orchestrator.connector:
227
225
  continue
228
226
 
229
227
  return DeployedStack(
230
228
  stack=stack,
231
- service_connector=artifact_store.connector,
229
+ service_connector=orchestrator.connector,
232
230
  )
233
231
 
234
232
  return None
zenml/steps/utils.py CHANGED
@@ -442,6 +442,11 @@ def log_step_metadata(
442
442
  from within a step or if no pipeline name or ID is provided and
443
443
  the function is not called from within a step.
444
444
  """
445
+ logger.warning(
446
+ "The `log_step_metadata` function is deprecated and will soon be "
447
+ "removed. Please use `log_metadata` instead."
448
+ )
449
+
445
450
  step_context = None
446
451
  if not step_name:
447
452
  with contextlib.suppress(RuntimeError):
@@ -0,0 +1,335 @@
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
+ """Utility functions to handle metadata for ZenML entities."""
15
+
16
+ import contextlib
17
+ from typing import Dict, Optional, Union, overload
18
+ from uuid import UUID
19
+
20
+ from zenml.client import Client
21
+ from zenml.enums import MetadataResourceTypes
22
+ from zenml.logger import get_logger
23
+ from zenml.metadata.metadata_types import MetadataType
24
+ from zenml.steps.step_context import get_step_context
25
+
26
+ logger = get_logger(__name__)
27
+
28
+
29
+ @overload
30
+ def log_metadata(metadata: Dict[str, MetadataType]) -> None: ...
31
+
32
+
33
+ @overload
34
+ def log_metadata(
35
+ *,
36
+ metadata: Dict[str, MetadataType],
37
+ artifact_version_id: UUID,
38
+ ) -> None: ...
39
+
40
+
41
+ @overload
42
+ def log_metadata(
43
+ *,
44
+ metadata: Dict[str, MetadataType],
45
+ artifact_name: str,
46
+ artifact_version: Optional[str] = None,
47
+ ) -> None: ...
48
+
49
+
50
+ @overload
51
+ def log_metadata(
52
+ *,
53
+ metadata: Dict[str, MetadataType],
54
+ model_version_id: UUID,
55
+ ) -> None: ...
56
+
57
+
58
+ @overload
59
+ def log_metadata(
60
+ *,
61
+ metadata: Dict[str, MetadataType],
62
+ model_name: str,
63
+ model_version: str,
64
+ ) -> None: ...
65
+
66
+
67
+ @overload
68
+ def log_metadata(
69
+ *,
70
+ metadata: Dict[str, MetadataType],
71
+ step_id: UUID,
72
+ ) -> None: ...
73
+
74
+
75
+ @overload
76
+ def log_metadata(
77
+ *,
78
+ metadata: Dict[str, MetadataType],
79
+ run_id_name_or_prefix: Union[UUID, str],
80
+ ) -> None: ...
81
+
82
+
83
+ @overload
84
+ def log_metadata(
85
+ *,
86
+ metadata: Dict[str, MetadataType],
87
+ step_name: str,
88
+ run_id_name_or_prefix: Union[UUID, str],
89
+ ) -> None: ...
90
+
91
+
92
+ def log_metadata(
93
+ metadata: Dict[str, MetadataType],
94
+ # Parameters to manually log metadata for steps and runs
95
+ step_id: Optional[UUID] = None,
96
+ step_name: Optional[str] = None,
97
+ run_id_name_or_prefix: Optional[Union[UUID, str]] = None,
98
+ # Parameters to manually log metadata for artifacts
99
+ artifact_version_id: Optional[UUID] = None,
100
+ artifact_name: Optional[str] = None,
101
+ artifact_version: Optional[str] = None,
102
+ # Parameters to manually log metadata for models
103
+ model_version_id: Optional[UUID] = None,
104
+ model_name: Optional[str] = None,
105
+ model_version: Optional[str] = None,
106
+ ) -> None:
107
+ """Logs metadata for various resource types in a generalized way.
108
+
109
+ Args:
110
+ metadata: The metadata to log.
111
+ step_id: The ID of the step.
112
+ step_name: The name of the step.
113
+ run_id_name_or_prefix: The id, name or prefix of the run
114
+ artifact_version_id: The ID of the artifact version
115
+ artifact_name: The name of the artifact.
116
+ artifact_version: The version of the artifact.
117
+ model_version_id: The ID of the model version.
118
+ model_name: The name of the model.
119
+ model_version: The version of the model
120
+
121
+ Raises:
122
+ ValueError: If no identifiers are provided and the function is not
123
+ called from within a step.
124
+ """
125
+ client = Client()
126
+
127
+ # If a step name is provided, we need a run_id_name_or_prefix and will log
128
+ # metadata for the steps pipeline and model accordingly.
129
+ if step_name is not None and run_id_name_or_prefix is not None:
130
+ run_model = client.get_pipeline_run(
131
+ name_id_or_prefix=run_id_name_or_prefix
132
+ )
133
+ step_model = run_model.steps[step_name]
134
+
135
+ client.create_run_metadata(
136
+ metadata=metadata,
137
+ resource_id=run_model.id,
138
+ resource_type=MetadataResourceTypes.PIPELINE_RUN,
139
+ )
140
+ client.create_run_metadata(
141
+ metadata=metadata,
142
+ resource_id=step_model.id,
143
+ resource_type=MetadataResourceTypes.STEP_RUN,
144
+ )
145
+ if step_model.model_version:
146
+ client.create_run_metadata(
147
+ metadata=metadata,
148
+ resource_id=step_model.model_version.id,
149
+ resource_type=MetadataResourceTypes.MODEL_VERSION,
150
+ )
151
+
152
+ # If a step is identified by id, fetch it directly through the client,
153
+ # follow a similar procedure and log metadata for its pipeline and model
154
+ # as well.
155
+ elif step_id is not None:
156
+ step_model = client.get_run_step(step_run_id=step_id)
157
+ client.create_run_metadata(
158
+ metadata=metadata,
159
+ resource_id=step_model.pipeline_run_id,
160
+ resource_type=MetadataResourceTypes.PIPELINE_RUN,
161
+ )
162
+ client.create_run_metadata(
163
+ metadata=metadata,
164
+ resource_id=step_model.id,
165
+ resource_type=MetadataResourceTypes.STEP_RUN,
166
+ )
167
+ if step_model.model_version:
168
+ client.create_run_metadata(
169
+ metadata=metadata,
170
+ resource_id=step_model.model_version.id,
171
+ resource_type=MetadataResourceTypes.MODEL_VERSION,
172
+ )
173
+
174
+ # If a pipeline run id is identified, we need to log metadata to it and its
175
+ # model as well.
176
+ elif run_id_name_or_prefix is not None:
177
+ run_model = client.get_pipeline_run(
178
+ name_id_or_prefix=run_id_name_or_prefix
179
+ )
180
+ client.create_run_metadata(
181
+ metadata=metadata,
182
+ resource_id=run_model.id,
183
+ resource_type=MetadataResourceTypes.PIPELINE_RUN,
184
+ )
185
+ if run_model.model_version:
186
+ client.create_run_metadata(
187
+ metadata=metadata,
188
+ resource_id=run_model.model_version.id,
189
+ resource_type=MetadataResourceTypes.MODEL_VERSION,
190
+ )
191
+
192
+ # If the user provides a model name and version, we use to model abstraction
193
+ # to fetch the model version and attach the corresponding metadata to it.
194
+ elif model_name is not None and model_version is not None:
195
+ from zenml import Model
196
+
197
+ mv = Model(name=model_name, version=model_version)
198
+ client.create_run_metadata(
199
+ metadata=metadata,
200
+ resource_id=mv.id,
201
+ resource_type=MetadataResourceTypes.MODEL_VERSION,
202
+ )
203
+
204
+ # If the user provides a model version id, we use the client to fetch it and
205
+ # attach the metadata to it.
206
+ elif model_version_id is not None:
207
+ model_version_id = client.get_model_version(
208
+ model_version_name_or_number_or_id=model_version_id
209
+ ).id
210
+ client.create_run_metadata(
211
+ metadata=metadata,
212
+ resource_id=model_version_id,
213
+ resource_type=MetadataResourceTypes.MODEL_VERSION,
214
+ )
215
+
216
+ # If the user provides an artifact name, there are three possibilities. If
217
+ # an artifact version is also provided with the name, we use both to fetch
218
+ # the artifact version and use it to log the metadata. If no version is
219
+ # provided, if the function is called within a step we search the artifacts
220
+ # of the step if not we fetch the latest version and attach the metadata
221
+ # to the latest version.
222
+ elif artifact_name is not None:
223
+ if artifact_version:
224
+ artifact_version_model = client.get_artifact_version(
225
+ name_id_or_prefix=artifact_name, version=artifact_version
226
+ )
227
+ client.create_run_metadata(
228
+ metadata=metadata,
229
+ resource_id=artifact_version_model.id,
230
+ resource_type=MetadataResourceTypes.ARTIFACT_VERSION,
231
+ )
232
+ else:
233
+ step_context = None
234
+ with contextlib.suppress(RuntimeError):
235
+ step_context = get_step_context()
236
+
237
+ if step_context:
238
+ step_context.add_output_metadata(
239
+ metadata=metadata, output_name=artifact_name
240
+ )
241
+ else:
242
+ artifact_version_model = client.get_artifact_version(
243
+ name_id_or_prefix=artifact_name
244
+ )
245
+ client.create_run_metadata(
246
+ metadata=metadata,
247
+ resource_id=artifact_version_model.id,
248
+ resource_type=MetadataResourceTypes.ARTIFACT_VERSION,
249
+ )
250
+
251
+ # If the user directly provides an artifact_version_id, we use the client to
252
+ # fetch is and attach the metadata accordingly.
253
+ elif artifact_version_id is not None:
254
+ artifact_version_model = client.get_artifact_version(
255
+ name_id_or_prefix=artifact_version_id,
256
+ )
257
+ client.create_run_metadata(
258
+ metadata=metadata,
259
+ resource_id=artifact_version_model.id,
260
+ resource_type=MetadataResourceTypes.ARTIFACT_VERSION,
261
+ )
262
+
263
+ # If every additional value is None, that means we are calling it bare bones
264
+ # and this call needs to happen during a step execution. We will use the
265
+ # step context to fetch the step, run and possibly the model version and
266
+ # attach the metadata accordingly.
267
+ elif all(
268
+ v is None
269
+ for v in [
270
+ step_id,
271
+ step_name,
272
+ run_id_name_or_prefix,
273
+ artifact_version_id,
274
+ artifact_name,
275
+ artifact_version,
276
+ model_version_id,
277
+ model_name,
278
+ model_version,
279
+ ]
280
+ ):
281
+ try:
282
+ step_context = get_step_context()
283
+ except RuntimeError:
284
+ raise ValueError(
285
+ "You are calling 'log_metadata()' outside of a step execution. "
286
+ "If you would like to add metadata to a ZenML entity outside "
287
+ "of the step execution, please provide the required "
288
+ "identifiers."
289
+ )
290
+ client.create_run_metadata(
291
+ metadata=metadata,
292
+ resource_id=step_context.pipeline_run.id,
293
+ resource_type=MetadataResourceTypes.PIPELINE_RUN,
294
+ )
295
+ client.create_run_metadata(
296
+ metadata=metadata,
297
+ resource_id=step_context.step_run.id,
298
+ resource_type=MetadataResourceTypes.STEP_RUN,
299
+ )
300
+ if step_context.model_version:
301
+ client.create_run_metadata(
302
+ metadata=metadata,
303
+ resource_id=step_context.model_version.id,
304
+ resource_type=MetadataResourceTypes.MODEL_VERSION,
305
+ )
306
+
307
+ else:
308
+ raise ValueError(
309
+ """
310
+ Unsupported way to call the `log_metadata`. Possible combinations "
311
+ include:
312
+
313
+ # Inside a step
314
+ # Logs the metadata to the step, its run and possibly its model
315
+ log_metadata(metadata={})
316
+
317
+ # Manually logging for a step
318
+ # Logs the metadata to the step, its run and possibly its model
319
+ log_metadata(metadata={}, step_name=..., run_id_name_or_prefix=...)
320
+ log_metadata(metadata={}, step_id=...)
321
+
322
+ # Manually logging for a run
323
+ # Logs the metadata to the run, possibly its model
324
+ log_metadata(metadata={}, run_id_name_or_prefix=...)
325
+
326
+ # Manually logging for a model
327
+ log_metadata(metadata={}, model_name=..., model_version=...)
328
+ log_metadata(metadata={}, model_version_id=...)
329
+
330
+ # Manually logging for an artifact
331
+ log_metadata(metadata={}, artifact_name=...) # inside a step
332
+ log_metadata(metadata={}, artifact_name=..., artifact_version=...)
333
+ log_metadata(metadata={}, artifact_version_id=...)
334
+ """
335
+ )
zenml/zen_server/auth.py CHANGED
@@ -14,13 +14,13 @@
14
14
  """Authentication module for ZenML server."""
15
15
 
16
16
  from contextvars import ContextVar
17
- from datetime import datetime
17
+ from datetime import datetime, timedelta
18
18
  from typing import Callable, Optional, Union
19
19
  from urllib.parse import urlencode
20
20
  from uuid import UUID
21
21
 
22
22
  import requests
23
- from fastapi import Depends
23
+ from fastapi import Depends, Response
24
24
  from fastapi.security import (
25
25
  HTTPBasic,
26
26
  HTTPBasicCredentials,
@@ -37,7 +37,7 @@ from zenml.constants import (
37
37
  LOGIN,
38
38
  VERSION_1,
39
39
  )
40
- from zenml.enums import AuthScheme, OAuthDeviceStatus
40
+ from zenml.enums import AuthScheme, ExecutionStatus, OAuthDeviceStatus
41
41
  from zenml.exceptions import (
42
42
  AuthorizationException,
43
43
  CredentialsNotValid,
@@ -51,11 +51,13 @@ from zenml.models import (
51
51
  ExternalUserModel,
52
52
  OAuthDeviceInternalResponse,
53
53
  OAuthDeviceInternalUpdate,
54
+ OAuthTokenResponse,
54
55
  UserAuthModel,
55
56
  UserRequest,
56
57
  UserResponse,
57
58
  UserUpdate,
58
59
  )
60
+ from zenml.zen_server.cache import cache_result
59
61
  from zenml.zen_server.exceptions import http_exception_from_error
60
62
  from zenml.zen_server.jwt import JWTToken
61
63
  from zenml.zen_server.utils import server_config, zen_store
@@ -329,6 +331,148 @@ def authenticate_credentials(
329
331
  ),
330
332
  )
331
333
 
334
+ if decoded_token.schedule_id:
335
+ # If the token contains a schedule ID, we need to check if the
336
+ # schedule still exists in the database. We use a cached version
337
+ # of the schedule active status to avoid unnecessary database
338
+ # queries.
339
+
340
+ @cache_result(expiry=30)
341
+ def get_schedule_active(schedule_id: UUID) -> Optional[bool]:
342
+ """Get the active status of a schedule.
343
+
344
+ Args:
345
+ schedule_id: The schedule ID.
346
+
347
+ Returns:
348
+ The schedule active status or None if the schedule does not
349
+ exist.
350
+ """
351
+ try:
352
+ schedule = zen_store().get_schedule(
353
+ schedule_id, hydrate=False
354
+ )
355
+ except KeyError:
356
+ return False
357
+
358
+ return schedule.active
359
+
360
+ schedule_active = get_schedule_active(decoded_token.schedule_id)
361
+ if schedule_active is None:
362
+ error = (
363
+ f"Authentication error: error retrieving token schedule "
364
+ f"{decoded_token.schedule_id}"
365
+ )
366
+ logger.error(error)
367
+ raise CredentialsNotValid(error)
368
+
369
+ if not schedule_active:
370
+ error = (
371
+ f"Authentication error: schedule {decoded_token.schedule_id} "
372
+ "is not active"
373
+ )
374
+ logger.error(error)
375
+ raise CredentialsNotValid(error)
376
+
377
+ if decoded_token.pipeline_run_id:
378
+ # If the token contains a pipeline run ID, we need to check if the
379
+ # pipeline run exists in the database and the pipeline run has
380
+ # not concluded. We use a cached version of the pipeline run status
381
+ # to avoid unnecessary database queries.
382
+
383
+ @cache_result(expiry=30)
384
+ def get_pipeline_run_status(
385
+ pipeline_run_id: UUID,
386
+ ) -> Optional[ExecutionStatus]:
387
+ """Get the status of a pipeline run.
388
+
389
+ Args:
390
+ pipeline_run_id: The pipeline run ID.
391
+
392
+ Returns:
393
+ The pipeline run status or None if the pipeline run does not
394
+ exist.
395
+ """
396
+ try:
397
+ pipeline_run = zen_store().get_run(
398
+ pipeline_run_id, hydrate=False
399
+ )
400
+ except KeyError:
401
+ return None
402
+
403
+ return pipeline_run.status
404
+
405
+ pipeline_run_status = get_pipeline_run_status(
406
+ decoded_token.pipeline_run_id
407
+ )
408
+ if pipeline_run_status is None:
409
+ error = (
410
+ f"Authentication error: error retrieving token pipeline run "
411
+ f"{decoded_token.pipeline_run_id}"
412
+ )
413
+ logger.error(error)
414
+ raise CredentialsNotValid(error)
415
+
416
+ if pipeline_run_status in [
417
+ ExecutionStatus.FAILED,
418
+ ExecutionStatus.COMPLETED,
419
+ ]:
420
+ error = (
421
+ f"The execution of pipeline run "
422
+ f"{decoded_token.pipeline_run_id} has already concluded and "
423
+ "API tokens scoped to it are no longer valid."
424
+ )
425
+ logger.error(error)
426
+ raise CredentialsNotValid(error)
427
+
428
+ if decoded_token.step_run_id:
429
+ # If the token contains a step run ID, we need to check if the
430
+ # step run exists in the database and the step run has not concluded.
431
+ # We use a cached version of the step run status to avoid unnecessary
432
+ # database queries.
433
+
434
+ @cache_result(expiry=30)
435
+ def get_step_run_status(
436
+ step_run_id: UUID,
437
+ ) -> Optional[ExecutionStatus]:
438
+ """Get the status of a step run.
439
+
440
+ Args:
441
+ step_run_id: The step run ID.
442
+
443
+ Returns:
444
+ The step run status or None if the step run does not exist.
445
+ """
446
+ try:
447
+ step_run = zen_store().get_run_step(
448
+ step_run_id, hydrate=False
449
+ )
450
+ except KeyError:
451
+ return None
452
+
453
+ return step_run.status
454
+
455
+ step_run_status = get_step_run_status(decoded_token.step_run_id)
456
+ if step_run_status is None:
457
+ error = (
458
+ f"Authentication error: error retrieving token step run "
459
+ f"{decoded_token.step_run_id}"
460
+ )
461
+ logger.error(error)
462
+ raise CredentialsNotValid(error)
463
+
464
+ if step_run_status in [
465
+ ExecutionStatus.FAILED,
466
+ ExecutionStatus.COMPLETED,
467
+ ]:
468
+ error = (
469
+ f"The execution of step run "
470
+ f"{decoded_token.step_run_id} has already concluded and "
471
+ "API tokens scoped to it are no longer valid."
472
+ )
473
+ logger.error(error)
474
+ raise CredentialsNotValid(error)
475
+
332
476
  auth_context = AuthContext(
333
477
  user=user_model,
334
478
  access_token=decoded_token,
@@ -660,6 +804,80 @@ def authenticate_api_key(
660
804
  return AuthContext(user=user_model, api_key=internal_api_key)
661
805
 
662
806
 
807
+ def generate_access_token(
808
+ user_id: UUID,
809
+ response: Optional[Response] = None,
810
+ device: Optional[OAuthDeviceInternalResponse] = None,
811
+ api_key: Optional[APIKeyInternalResponse] = None,
812
+ expires_in: Optional[int] = None,
813
+ schedule_id: Optional[UUID] = None,
814
+ pipeline_run_id: Optional[UUID] = None,
815
+ step_run_id: Optional[UUID] = None,
816
+ ) -> OAuthTokenResponse:
817
+ """Generates an access token for the given user.
818
+
819
+ Args:
820
+ user_id: The ID of the user.
821
+ response: The FastAPI response object.
822
+ device: The device used for authentication.
823
+ api_key: The service account API key used for authentication.
824
+ expires_in: The number of seconds until the token expires.
825
+ schedule_id: The ID of the schedule to scope the token to.
826
+ pipeline_run_id: The ID of the pipeline run to scope the token to.
827
+ step_run_id: The ID of the step run to scope the token to.
828
+
829
+ Returns:
830
+ An authentication response with an access token.
831
+ """
832
+ config = server_config()
833
+
834
+ # If the expiration time is not supplied, the JWT tokens are set to expire
835
+ # according to the values configured in the server config. Device tokens are
836
+ # handled separately from regular user tokens.
837
+ expires: Optional[datetime] = None
838
+ if expires_in:
839
+ expires = datetime.utcnow() + timedelta(seconds=expires_in)
840
+ elif device:
841
+ # If a device was used for authentication, the token will expire
842
+ # at the same time as the device.
843
+ expires = device.expires
844
+ if expires:
845
+ expires_in = max(
846
+ int(expires.timestamp() - datetime.utcnow().timestamp()), 0
847
+ )
848
+ elif config.jwt_token_expire_minutes:
849
+ expires = datetime.utcnow() + timedelta(
850
+ minutes=config.jwt_token_expire_minutes
851
+ )
852
+ expires_in = config.jwt_token_expire_minutes * 60
853
+
854
+ access_token = JWTToken(
855
+ user_id=user_id,
856
+ device_id=device.id if device else None,
857
+ api_key_id=api_key.id if api_key else None,
858
+ schedule_id=schedule_id,
859
+ pipeline_run_id=pipeline_run_id,
860
+ step_run_id=step_run_id,
861
+ ).encode(expires=expires)
862
+
863
+ if not device and response:
864
+ # Also set the access token as an HTTP only cookie in the response
865
+ response.set_cookie(
866
+ key=config.get_auth_cookie_name(),
867
+ value=access_token,
868
+ httponly=True,
869
+ samesite="lax",
870
+ max_age=config.jwt_token_expire_minutes * 60
871
+ if config.jwt_token_expire_minutes
872
+ else None,
873
+ domain=config.auth_cookie_domain,
874
+ )
875
+
876
+ return OAuthTokenResponse(
877
+ access_token=access_token, expires_in=expires_in, token_type="bearer"
878
+ )
879
+
880
+
663
881
  def http_authentication(
664
882
  credentials: HTTPBasicCredentials = Depends(HTTPBasic()),
665
883
  ) -> AuthContext: