zenml-nightly 0.58.2.dev20240626__py3-none-any.whl → 0.62.0.dev20240726__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 (266) hide show
  1. README.md +31 -10
  2. RELEASE_NOTES.md +280 -0
  3. zenml/VERSION +1 -1
  4. zenml/__init__.py +2 -0
  5. zenml/analytics/enums.py +3 -0
  6. zenml/cli/__init__.py +28 -0
  7. zenml/cli/artifact.py +1 -2
  8. zenml/cli/integration.py +9 -8
  9. zenml/cli/server.py +6 -0
  10. zenml/cli/stack.py +812 -39
  11. zenml/cli/stack_components.py +9 -0
  12. zenml/cli/text_utils.py +35 -1
  13. zenml/cli/utils.py +127 -10
  14. zenml/client.py +23 -14
  15. zenml/config/docker_settings.py +8 -5
  16. zenml/constants.py +13 -1
  17. zenml/container_registries/base_container_registry.py +1 -0
  18. zenml/enums.py +23 -0
  19. zenml/event_hub/event_hub.py +5 -8
  20. zenml/integrations/__init__.py +1 -0
  21. zenml/integrations/aws/__init__.py +1 -0
  22. zenml/integrations/azure/__init__.py +3 -2
  23. zenml/integrations/constants.py +1 -0
  24. zenml/integrations/databricks/__init__.py +52 -0
  25. zenml/integrations/databricks/flavors/__init__.py +30 -0
  26. zenml/integrations/databricks/flavors/databricks_model_deployer_flavor.py +118 -0
  27. zenml/integrations/databricks/flavors/databricks_orchestrator_flavor.py +147 -0
  28. zenml/integrations/databricks/model_deployers/__init__.py +20 -0
  29. zenml/integrations/databricks/model_deployers/databricks_model_deployer.py +249 -0
  30. zenml/integrations/databricks/orchestrators/__init__.py +20 -0
  31. zenml/integrations/databricks/orchestrators/databricks_orchestrator.py +497 -0
  32. zenml/integrations/databricks/orchestrators/databricks_orchestrator_entrypoint_config.py +97 -0
  33. zenml/integrations/databricks/services/__init__.py +19 -0
  34. zenml/integrations/databricks/services/databricks_deployment.py +407 -0
  35. zenml/integrations/databricks/utils/__init__.py +14 -0
  36. zenml/integrations/databricks/utils/databricks_utils.py +87 -0
  37. zenml/integrations/deepchecks/__init__.py +1 -0
  38. zenml/integrations/discord/__init__.py +1 -0
  39. zenml/integrations/evidently/__init__.py +1 -0
  40. zenml/integrations/facets/__init__.py +1 -0
  41. zenml/integrations/feast/__init__.py +1 -0
  42. zenml/integrations/gcp/__init__.py +3 -1
  43. zenml/integrations/gcp/google_credentials_mixin.py +1 -1
  44. zenml/integrations/gcp/service_connectors/gcp_service_connector.py +320 -64
  45. zenml/integrations/great_expectations/data_validators/ge_data_validator.py +12 -8
  46. zenml/integrations/huggingface/__init__.py +1 -0
  47. zenml/integrations/huggingface/materializers/huggingface_datasets_materializer.py +88 -3
  48. zenml/integrations/huggingface/steps/accelerate_runner.py +1 -7
  49. zenml/integrations/integration.py +24 -0
  50. zenml/integrations/kubeflow/__init__.py +3 -0
  51. zenml/integrations/kubeflow/flavors/kubeflow_orchestrator_flavor.py +1 -1
  52. zenml/integrations/kubeflow/orchestrators/kubeflow_orchestrator.py +0 -1
  53. zenml/integrations/kubernetes/__init__.py +3 -1
  54. zenml/integrations/kubernetes/orchestrators/kube_utils.py +4 -1
  55. zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py +1 -13
  56. zenml/integrations/kubernetes/orchestrators/manifest_utils.py +22 -4
  57. zenml/integrations/kubernetes/pod_settings.py +4 -0
  58. zenml/integrations/label_studio/annotators/label_studio_annotator.py +1 -0
  59. zenml/integrations/langchain/__init__.py +1 -0
  60. zenml/integrations/lightgbm/__init__.py +1 -0
  61. zenml/integrations/mlflow/__init__.py +4 -2
  62. zenml/integrations/mlflow/model_registries/mlflow_model_registry.py +6 -2
  63. zenml/integrations/mlflow/services/mlflow_deployment.py +1 -1
  64. zenml/integrations/neural_prophet/__init__.py +1 -0
  65. zenml/integrations/polars/__init__.py +1 -0
  66. zenml/integrations/prodigy/__init__.py +1 -0
  67. zenml/integrations/pycaret/__init__.py +6 -0
  68. zenml/integrations/registry.py +37 -0
  69. zenml/integrations/s3/artifact_stores/s3_artifact_store.py +17 -6
  70. zenml/integrations/seldon/__init__.py +1 -0
  71. zenml/integrations/seldon/model_deployers/seldon_model_deployer.py +1 -0
  72. zenml/integrations/skypilot/flavors/skypilot_orchestrator_base_vm_config.py +2 -2
  73. zenml/integrations/skypilot/orchestrators/skypilot_base_vm_orchestrator.py +1 -1
  74. zenml/integrations/skypilot/orchestrators/skypilot_orchestrator_entrypoint.py +2 -2
  75. zenml/integrations/skypilot_aws/__init__.py +2 -1
  76. zenml/integrations/skypilot_azure/__init__.py +1 -3
  77. zenml/integrations/skypilot_gcp/__init__.py +1 -1
  78. zenml/integrations/skypilot_lambda/__init__.py +1 -1
  79. zenml/integrations/skypilot_lambda/flavors/skypilot_orchestrator_lambda_vm_flavor.py +1 -1
  80. zenml/integrations/slack/__init__.py +1 -0
  81. zenml/integrations/tekton/__init__.py +1 -0
  82. zenml/integrations/tensorboard/__init__.py +0 -1
  83. zenml/integrations/tensorflow/__init__.py +18 -6
  84. zenml/integrations/wandb/__init__.py +1 -0
  85. zenml/logging/step_logging.py +34 -35
  86. zenml/materializers/built_in_materializer.py +1 -1
  87. zenml/materializers/cloudpickle_materializer.py +1 -1
  88. zenml/model/model.py +1 -1
  89. zenml/models/__init__.py +11 -0
  90. zenml/models/v2/core/component.py +47 -0
  91. zenml/models/v2/core/model.py +1 -2
  92. zenml/models/v2/core/server_settings.py +0 -20
  93. zenml/models/v2/core/service_connector.py +17 -0
  94. zenml/models/v2/core/stack.py +31 -0
  95. zenml/models/v2/misc/full_stack.py +129 -0
  96. zenml/models/v2/misc/stack_deployment.py +91 -0
  97. zenml/new/pipelines/pipeline.py +1 -1
  98. zenml/new/pipelines/run_utils.py +1 -1
  99. zenml/orchestrators/__init__.py +4 -0
  100. zenml/orchestrators/input_utils.py +3 -6
  101. zenml/orchestrators/step_launcher.py +1 -0
  102. zenml/orchestrators/wheeled_orchestrator.py +147 -0
  103. zenml/service_connectors/service_connector_utils.py +408 -0
  104. zenml/stack/stack.py +3 -6
  105. zenml/stack_deployments/__init__.py +14 -0
  106. zenml/stack_deployments/aws_stack_deployment.py +254 -0
  107. zenml/stack_deployments/azure_stack_deployment.py +179 -0
  108. zenml/stack_deployments/gcp_stack_deployment.py +269 -0
  109. zenml/stack_deployments/stack_deployment.py +218 -0
  110. zenml/stack_deployments/utils.py +48 -0
  111. zenml/steps/base_step.py +7 -5
  112. zenml/utils/function_utils.py +2 -2
  113. zenml/utils/pagination_utils.py +7 -5
  114. zenml/utils/pipeline_docker_image_builder.py +105 -68
  115. zenml/utils/pydantic_utils.py +6 -5
  116. zenml/utils/source_utils.py +4 -1
  117. zenml/zen_server/cloud_utils.py +18 -3
  118. zenml/zen_server/dashboard/assets/{404-CDPQCl4D.js → 404-B_YdvmwS.js} +1 -1
  119. zenml/zen_server/dashboard/assets/@radix-CFOkMR_E.js +85 -0
  120. zenml/zen_server/dashboard/assets/{@react-router-DYovave8.js → @react-router-CO-OsFwI.js} +2 -2
  121. zenml/zen_server/dashboard/assets/{@reactflow-CHBapDaj.js → @reactflow-l_1hUr1S.js} +2 -2
  122. zenml/zen_server/dashboard/assets/@tanstack-DYiOyJUL.js +22 -0
  123. zenml/zen_server/dashboard/assets/AwarenessChannel-CFg5iX4Z.js +1 -0
  124. zenml/zen_server/dashboard/assets/{CodeSnippet-BidtnWOi.js → CodeSnippet-Dvkx_82E.js} +2 -2
  125. zenml/zen_server/dashboard/assets/CollapsibleCard-opiuBHHc.js +1 -0
  126. zenml/zen_server/dashboard/assets/Commands-DoN1xrEq.js +1 -0
  127. zenml/zen_server/dashboard/assets/CopyButton-Cr7xYEPb.js +2 -0
  128. zenml/zen_server/dashboard/assets/{CsvVizualization-BOuez-fG.js → CsvVizualization-Ck-nZ43m.js} +7 -7
  129. zenml/zen_server/dashboard/assets/DisplayDate-DYgIjlDF.js +1 -0
  130. zenml/zen_server/dashboard/assets/EmptyState-BMLnFVlB.js +1 -0
  131. zenml/zen_server/dashboard/assets/Error-kLtljEOM.js +1 -0
  132. zenml/zen_server/dashboard/assets/ExecutionStatus-DguLLgTK.js +1 -0
  133. zenml/zen_server/dashboard/assets/Helpbox-BXUMP21n.js +1 -0
  134. zenml/zen_server/dashboard/assets/Infobox-DSt0O-dm.js +1 -0
  135. zenml/zen_server/dashboard/assets/InlineAvatar-xsrsIGE-.js +1 -0
  136. zenml/zen_server/dashboard/assets/{MarkdownVisualization-DsB2QZiK.js → MarkdownVisualization-xp3hhULl.js} +2 -2
  137. zenml/zen_server/dashboard/assets/Pagination-C6X-mifw.js +1 -0
  138. zenml/zen_server/dashboard/assets/PasswordChecker-DUveqlva.js +1 -0
  139. zenml/zen_server/dashboard/assets/SetPassword-BXGTWiwj.js +1 -0
  140. zenml/zen_server/dashboard/assets/SuccessStep-DZC60t0x.js +1 -0
  141. zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-DnM-c11H.js → UpdatePasswordSchemas-DGvwFWO1.js} +1 -1
  142. zenml/zen_server/dashboard/assets/{aws-t0gKCj_R.js → aws-BgKTfTfx.js} +1 -1
  143. zenml/zen_server/dashboard/assets/{check-circle-BVvhm5dy.js → check-circle-i56092KI.js} +1 -1
  144. zenml/zen_server/dashboard/assets/{chevron-right-double-CJ50E9Gr.js → chevron-right-double-CZBOf6JM.js} +1 -1
  145. zenml/zen_server/dashboard/assets/cloud-only-C_yFCAkP.js +1 -0
  146. zenml/zen_server/dashboard/assets/{copy-BRhQz3j-.js → copy-BXNk6BjL.js} +1 -1
  147. zenml/zen_server/dashboard/assets/{database-CRRnyFWh.js → database-1xWSgZfO.js} +1 -1
  148. zenml/zen_server/dashboard/assets/{docker-BAonhm6G.js → docker-CQMVm_4d.js} +1 -1
  149. zenml/zen_server/dashboard/assets/{file-text-CbVERUON.js → file-text-CqD_iu6l.js} +1 -1
  150. zenml/zen_server/dashboard/assets/{help-B8rqCvqn.js → help-bu_DgLKI.js} +1 -1
  151. zenml/zen_server/dashboard/assets/index-BczVOqUf.js +55 -0
  152. zenml/zen_server/dashboard/assets/index-EpMIKgrI.css +1 -0
  153. zenml/zen_server/dashboard/assets/index-rK_Wuy2W.js +1 -0
  154. zenml/zen_server/dashboard/assets/index.esm-Corw4lXQ.js +1 -0
  155. zenml/zen_server/dashboard/assets/{login-mutation-wzzl23C6.js → login-mutation-CrHrndTI.js} +1 -1
  156. zenml/zen_server/dashboard/assets/logs-D8k8BVFf.js +1 -0
  157. zenml/zen_server/dashboard/assets/not-found-DYa4pC-C.js +1 -0
  158. zenml/zen_server/dashboard/assets/package-B3fWP-Dh.js +1 -0
  159. zenml/zen_server/dashboard/assets/page-1h_sD1jz.js +1 -0
  160. zenml/zen_server/dashboard/assets/{page-yN4rZ-ZS.js → page-1iL8aMqs.js} +1 -1
  161. zenml/zen_server/dashboard/assets/{page-Bi5AI0S7.js → page-2grKx_MY.js} +1 -1
  162. zenml/zen_server/dashboard/assets/page-5NCOHOsy.js +1 -0
  163. zenml/zen_server/dashboard/assets/page-8a4UMKXZ.js +1 -0
  164. zenml/zen_server/dashboard/assets/{page-AQKopn_4.js → page-B6h3iaHJ.js} +1 -1
  165. zenml/zen_server/dashboard/assets/page-BDns21Iz.js +1 -0
  166. zenml/zen_server/dashboard/assets/{page-BmkSiYeQ.js → page-BhgCDInH.js} +2 -2
  167. zenml/zen_server/dashboard/assets/{page-BzVZGExK.js → page-Bi-wtWiO.js} +2 -2
  168. zenml/zen_server/dashboard/assets/page-BkeAAYwp.js +1 -0
  169. zenml/zen_server/dashboard/assets/page-BkuQDIf-.js +1 -0
  170. zenml/zen_server/dashboard/assets/page-BnaevhnB.js +1 -0
  171. zenml/zen_server/dashboard/assets/page-Bq0YxkLV.js +1 -0
  172. zenml/zen_server/dashboard/assets/page-Bs2F4eoD.js +2 -0
  173. zenml/zen_server/dashboard/assets/page-C6-UGEbH.js +1 -0
  174. zenml/zen_server/dashboard/assets/page-CCNRIt_f.js +1 -0
  175. zenml/zen_server/dashboard/assets/page-CHNxpz3n.js +1 -0
  176. zenml/zen_server/dashboard/assets/page-DgorQFqi.js +1 -0
  177. zenml/zen_server/dashboard/assets/page-K8ebxVIs.js +1 -0
  178. zenml/zen_server/dashboard/assets/{page-CuT1SUik.js → page-MFQyIJd3.js} +1 -1
  179. zenml/zen_server/dashboard/assets/page-TgCF0P_U.js +1 -0
  180. zenml/zen_server/dashboard/assets/page-ZnCEe-eK.js +9 -0
  181. zenml/zen_server/dashboard/assets/{page-BW6Ket3a.js → page-uA5prJGY.js} +1 -1
  182. zenml/zen_server/dashboard/assets/persist-D7HJNBWx.js +1 -0
  183. zenml/zen_server/dashboard/assets/{play-circle-DK5QMJyp.js → play-circle-CNtZKDnW.js} +1 -1
  184. zenml/zen_server/dashboard/assets/plus-C8WOyCzt.js +1 -0
  185. zenml/zen_server/dashboard/assets/stack-detail-query-Cficsl6d.js +1 -0
  186. zenml/zen_server/dashboard/assets/{terminal-B2ovgWuz.js → terminal-By9cErXc.js} +1 -1
  187. zenml/zen_server/dashboard/assets/update-server-settings-mutation-7d8xi1tS.js +1 -0
  188. zenml/zen_server/dashboard/assets/{url-6_xv0WJS.js → url-D7mAQGUM.js} +1 -1
  189. zenml/zen_server/dashboard/assets/{zod-DrZvVLjd.js → zod-BhoGpZ63.js} +1 -1
  190. zenml/zen_server/dashboard/index.html +7 -7
  191. zenml/zen_server/dashboard_legacy/asset-manifest.json +4 -4
  192. zenml/zen_server/dashboard_legacy/index.html +1 -1
  193. zenml/zen_server/dashboard_legacy/{precache-manifest.f4abc5b7cfa7d90c1caf5521918e29a8.js → precache-manifest.12246c7548e71e2c4438e496360de80c.js} +4 -4
  194. zenml/zen_server/dashboard_legacy/service-worker.js +1 -1
  195. zenml/zen_server/dashboard_legacy/static/js/main.3b27024b.chunk.js +2 -0
  196. zenml/zen_server/dashboard_legacy/static/js/{main.ac2f17d0.chunk.js.map → main.3b27024b.chunk.js.map} +1 -1
  197. zenml/zen_server/deploy/helm/Chart.yaml +1 -1
  198. zenml/zen_server/deploy/helm/README.md +2 -2
  199. zenml/zen_server/feature_gate/zenml_cloud_feature_gate.py +11 -5
  200. zenml/zen_server/pipeline_deployment/utils.py +57 -44
  201. zenml/zen_server/rbac/utils.py +10 -2
  202. zenml/zen_server/rbac/zenml_cloud_rbac.py +11 -5
  203. zenml/zen_server/routers/devices_endpoints.py +4 -1
  204. zenml/zen_server/routers/server_endpoints.py +29 -2
  205. zenml/zen_server/routers/service_connectors_endpoints.py +57 -0
  206. zenml/zen_server/routers/stack_deployment_endpoints.py +158 -0
  207. zenml/zen_server/routers/steps_endpoints.py +2 -1
  208. zenml/zen_server/routers/workspaces_endpoints.py +64 -0
  209. zenml/zen_server/zen_server_api.py +2 -0
  210. zenml/zen_stores/migrations/utils.py +1 -1
  211. zenml/zen_stores/migrations/versions/0.60.0_release.py +23 -0
  212. zenml/zen_stores/migrations/versions/0.61.0_release.py +23 -0
  213. zenml/zen_stores/migrations/versions/0.62.0_release.py +23 -0
  214. zenml/zen_stores/migrations/versions/0d707865f404_adding_labels_to_stacks.py +30 -0
  215. zenml/zen_stores/migrations/versions/b4fca5241eea_migrate_onboarding_state.py +167 -0
  216. zenml/zen_stores/rest_zen_store.py +149 -4
  217. zenml/zen_stores/schemas/component_schemas.py +14 -0
  218. zenml/zen_stores/schemas/server_settings_schemas.py +23 -11
  219. zenml/zen_stores/schemas/stack_schemas.py +10 -0
  220. zenml/zen_stores/schemas/step_run_schemas.py +27 -11
  221. zenml/zen_stores/sql_zen_store.py +450 -6
  222. zenml/zen_stores/zen_store_interface.py +80 -0
  223. {zenml_nightly-0.58.2.dev20240626.dist-info → zenml_nightly-0.62.0.dev20240726.dist-info}/METADATA +35 -13
  224. {zenml_nightly-0.58.2.dev20240626.dist-info → zenml_nightly-0.62.0.dev20240726.dist-info}/RECORD +227 -191
  225. zenml/zen_server/dashboard/assets/@radix-C9DBgJhe.js +0 -77
  226. zenml/zen_server/dashboard/assets/@tanstack-CEbkxrhX.js +0 -30
  227. zenml/zen_server/dashboard/assets/AwarenessChannel-nXGpmj_f.js +0 -1
  228. zenml/zen_server/dashboard/assets/Cards-nwsvQLVS.js +0 -1
  229. zenml/zen_server/dashboard/assets/Commands-DuIWKg_Q.js +0 -1
  230. zenml/zen_server/dashboard/assets/CopyButton-B_YSm-Ds.js +0 -2
  231. zenml/zen_server/dashboard/assets/DisplayDate-BdguISQF.js +0 -1
  232. zenml/zen_server/dashboard/assets/EmptyState-BkooiGtL.js +0 -1
  233. zenml/zen_server/dashboard/assets/Error-B6M0dPph.js +0 -1
  234. zenml/zen_server/dashboard/assets/Helpbox-BQoqCm04.js +0 -1
  235. zenml/zen_server/dashboard/assets/Infobox-Ce9mefqU.js +0 -1
  236. zenml/zen_server/dashboard/assets/InlineAvatar-DGf3dVhV.js +0 -1
  237. zenml/zen_server/dashboard/assets/PageHeader-DGaemzjc.js +0 -1
  238. zenml/zen_server/dashboard/assets/Pagination-DVYfBCCc.js +0 -1
  239. zenml/zen_server/dashboard/assets/PasswordChecker-DSLBp7Vl.js +0 -1
  240. zenml/zen_server/dashboard/assets/SetPassword-B5s7DJug.js +0 -1
  241. zenml/zen_server/dashboard/assets/SuccessStep-ZzczaM7g.js +0 -1
  242. zenml/zen_server/dashboard/assets/chevron-down-zcvCWmyP.js +0 -1
  243. zenml/zen_server/dashboard/assets/cloud-only-Ba_ShBR5.js +0 -1
  244. zenml/zen_server/dashboard/assets/index-CWJ3xbIf.css +0 -1
  245. zenml/zen_server/dashboard/assets/index-QORVVTMN.js +0 -55
  246. zenml/zen_server/dashboard/assets/index.esm-F7nqy9zY.js +0 -1
  247. zenml/zen_server/dashboard/assets/not-found-Dh2la7kh.js +0 -1
  248. zenml/zen_server/dashboard/assets/page-B-5jAKoO.js +0 -1
  249. zenml/zen_server/dashboard/assets/page-B-vWk8a6.js +0 -1
  250. zenml/zen_server/dashboard/assets/page-B0BrqfS8.js +0 -1
  251. zenml/zen_server/dashboard/assets/page-BQxVFlUl.js +0 -1
  252. zenml/zen_server/dashboard/assets/page-ByrHy6Ss.js +0 -1
  253. zenml/zen_server/dashboard/assets/page-CPtY4Kv_.js +0 -1
  254. zenml/zen_server/dashboard/assets/page-CmmukLsl.js +0 -1
  255. zenml/zen_server/dashboard/assets/page-D2D-7qyr.js +0 -9
  256. zenml/zen_server/dashboard/assets/page-DAQQyLxT.js +0 -1
  257. zenml/zen_server/dashboard/assets/page-DHkUMl_E.js +0 -1
  258. zenml/zen_server/dashboard/assets/page-DZCbwOEs.js +0 -2
  259. zenml/zen_server/dashboard/assets/page-DdaIt20-.js +0 -1
  260. zenml/zen_server/dashboard/assets/page-LqLs24Ot.js +0 -1
  261. zenml/zen_server/dashboard/assets/page-lebv0c7C.js +0 -1
  262. zenml/zen_server/dashboard/assets/update-server-settings-mutation-0Wgz8pUE.js +0 -1
  263. zenml/zen_server/dashboard_legacy/static/js/main.ac2f17d0.chunk.js +0 -2
  264. {zenml_nightly-0.58.2.dev20240626.dist-info → zenml_nightly-0.62.0.dev20240726.dist-info}/LICENSE +0 -0
  265. {zenml_nightly-0.58.2.dev20240626.dist-info → zenml_nightly-0.62.0.dev20240726.dist-info}/WHEEL +0 -0
  266. {zenml_nightly-0.58.2.dev20240626.dist-info → zenml_nightly-0.62.0.dev20240726.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  apiVersion: v2
2
2
  name: zenml
3
- version: "0.58.2"
3
+ version: "0.62.0"
4
4
  description: Open source MLOps framework for portable production ready ML pipelines
5
5
  keywords:
6
6
  - mlops
@@ -20,8 +20,8 @@ ZenML is an open-source MLOps framework designed to help you create robust, main
20
20
  To install the ZenML chart directly from Amazon ECR, use the following command:
21
21
 
22
22
  ```bash
23
- # example command for version 0.58.2
24
- helm install my-zenml oci://public.ecr.aws/zenml/zenml --version 0.58.2
23
+ # example command for version 0.62.0
24
+ helm install my-zenml oci://public.ecr.aws/zenml/zenml --version 0.62.0
25
25
  ```
26
26
 
27
27
  Note: Ensure you have OCI support enabled in your Helm client and that you are authenticated with Amazon ECR.
@@ -21,7 +21,7 @@ from pydantic import BaseModel, Field
21
21
  from zenml.config.server_config import ServerConfiguration
22
22
  from zenml.exceptions import SubscriptionUpgradeRequiredError
23
23
  from zenml.logger import get_logger
24
- from zenml.zen_server.cloud_utils import ZenMLCloudSession
24
+ from zenml.zen_server.cloud_utils import cloud_connection
25
25
  from zenml.zen_server.feature_gate.feature_gate_interface import (
26
26
  FeatureGateInterface,
27
27
  )
@@ -59,8 +59,12 @@ class RawUsageEvent(BaseModel):
59
59
  )
60
60
 
61
61
 
62
- class ZenMLCloudFeatureGateInterface(FeatureGateInterface, ZenMLCloudSession):
63
- """Feature Gate interface definition."""
62
+ class ZenMLCloudFeatureGateInterface(FeatureGateInterface):
63
+ """ZenML Cloud Feature Gate implementation."""
64
+
65
+ def __init__(self) -> None:
66
+ """Initialize the object."""
67
+ self._connection = cloud_connection()
64
68
 
65
69
  def check_entitlement(self, resource: ResourceType) -> None:
66
70
  """Checks if a user is entitled to create a resource.
@@ -72,7 +76,7 @@ class ZenMLCloudFeatureGateInterface(FeatureGateInterface, ZenMLCloudSession):
72
76
  SubscriptionUpgradeRequiredError: in case a subscription limit is reached
73
77
  """
74
78
  try:
75
- response = self._get(
79
+ response = self._connection.get(
76
80
  endpoint=ENTITLEMENT_ENDPOINT + "/" + resource, params=None
77
81
  )
78
82
  except SubscriptionUpgradeRequiredError:
@@ -110,7 +114,9 @@ class ZenMLCloudFeatureGateInterface(FeatureGateInterface, ZenMLCloudSession):
110
114
  "resource_id": str(resource_id),
111
115
  },
112
116
  ).model_dump()
113
- response = self._post(endpoint=USAGE_EVENT_ENDPOINT, data=data)
117
+ response = self._connection.post(
118
+ endpoint=USAGE_EVENT_ENDPOINT, data=data
119
+ )
114
120
  if response.status_code != 200:
115
121
  logger.error(
116
122
  "Usage report not accepted by upstream backend. "
@@ -17,7 +17,7 @@ from zenml.constants import (
17
17
  ENV_ZENML_ACTIVE_STACK_ID,
18
18
  ENV_ZENML_ACTIVE_WORKSPACE_ID,
19
19
  )
20
- from zenml.enums import StackComponentType, StoreType
20
+ from zenml.enums import ExecutionStatus, StackComponentType, StoreType
21
21
  from zenml.integrations.utils import get_integration_for_module
22
22
  from zenml.models import (
23
23
  CodeReferenceRequest,
@@ -26,6 +26,7 @@ from zenml.models import (
26
26
  PipelineDeploymentRequest,
27
27
  PipelineDeploymentResponse,
28
28
  PipelineRunResponse,
29
+ PipelineRunUpdate,
29
30
  StackResponse,
30
31
  )
31
32
  from zenml.new.pipelines.run_utils import (
@@ -88,8 +89,6 @@ def run_pipeline(
88
89
  ensure_async_orchestrator(deployment=deployment_request, stack=stack)
89
90
 
90
91
  new_deployment = zen_store().create_deployment(deployment_request)
91
- placeholder_run = create_placeholder_run(deployment=new_deployment)
92
- assert placeholder_run
93
92
 
94
93
  if auth_context.access_token:
95
94
  token = auth_context.access_token
@@ -125,52 +124,62 @@ def run_pipeline(
125
124
  deployment_id=new_deployment.id
126
125
  )
127
126
 
128
- def _task() -> None:
129
- pypi_requirements, apt_packages = get_requirements_for_stack(
130
- stack=stack
131
- )
127
+ placeholder_run = create_placeholder_run(deployment=new_deployment)
128
+ assert placeholder_run
132
129
 
133
- if build.python_version:
134
- version_info = version.parse(build.python_version)
135
- python_version = f"{version_info.major}.{version_info.minor}"
136
- else:
137
- python_version = (
138
- f"{sys.version_info.major}.{sys.version_info.minor}"
130
+ def _task() -> None:
131
+ try:
132
+ pypi_requirements, apt_packages = get_requirements_for_stack(
133
+ stack=stack
139
134
  )
140
135
 
141
- dockerfile = generate_dockerfile(
142
- pypi_requirements=pypi_requirements,
143
- apt_packages=apt_packages,
144
- zenml_version=zenml_version,
145
- python_version=python_version,
146
- )
136
+ if build.python_version:
137
+ version_info = version.parse(build.python_version)
138
+ python_version = f"{version_info.major}.{version_info.minor}"
139
+ else:
140
+ python_version = (
141
+ f"{sys.version_info.major}.{sys.version_info.minor}"
142
+ )
143
+
144
+ dockerfile = generate_dockerfile(
145
+ pypi_requirements=pypi_requirements,
146
+ apt_packages=apt_packages,
147
+ zenml_version=zenml_version,
148
+ python_version=python_version,
149
+ )
147
150
 
148
- image_hash = generate_image_hash(dockerfile=dockerfile)
151
+ image_hash = generate_image_hash(dockerfile=dockerfile)
149
152
 
150
- runner_image = workload_manager().build_and_push_image(
151
- workload_id=new_deployment.id,
152
- dockerfile=dockerfile,
153
- image_name=f"{RUNNER_IMAGE_REPOSITORY}:{image_hash}",
154
- sync=True,
155
- )
153
+ runner_image = workload_manager().build_and_push_image(
154
+ workload_id=new_deployment.id,
155
+ dockerfile=dockerfile,
156
+ image_name=f"{RUNNER_IMAGE_REPOSITORY}:{image_hash}",
157
+ sync=True,
158
+ )
156
159
 
157
- workload_manager().log(
158
- workload_id=new_deployment.id,
159
- message="Starting pipeline deployment.",
160
- )
161
- workload_manager().run(
162
- workload_id=new_deployment.id,
163
- image=runner_image,
164
- command=command,
165
- arguments=args,
166
- environment=environment,
167
- timeout_in_seconds=30,
168
- sync=True,
169
- )
170
- workload_manager().log(
171
- workload_id=new_deployment.id,
172
- message="Pipeline deployed successfully.",
173
- )
160
+ workload_manager().log(
161
+ workload_id=new_deployment.id,
162
+ message="Starting pipeline deployment.",
163
+ )
164
+ workload_manager().run(
165
+ workload_id=new_deployment.id,
166
+ image=runner_image,
167
+ command=command,
168
+ arguments=args,
169
+ environment=environment,
170
+ timeout_in_seconds=30,
171
+ sync=True,
172
+ )
173
+ workload_manager().log(
174
+ workload_id=new_deployment.id,
175
+ message="Pipeline deployed successfully.",
176
+ )
177
+ except Exception:
178
+ zen_store().update_run(
179
+ run_id=placeholder_run.id,
180
+ run_update=PipelineRunUpdate(status=ExecutionStatus.FAILED),
181
+ )
182
+ raise
174
183
 
175
184
  if background_tasks:
176
185
  background_tasks.add_task(_task)
@@ -357,8 +366,12 @@ def apply_run_config(
357
366
  step_config = StepConfiguration.model_validate(step_config_dict)
358
367
 
359
368
  if update := run_config.steps.get(invocation_id):
369
+ update_dict = update.model_dump()
370
+ # Get rid of deprecated name to prevent overriding the step name
371
+ # with `None`.
372
+ update_dict.pop("name", None)
360
373
  step_config = pydantic_utils.update_model(
361
- step_config, update=update
374
+ step_config, update=update_dict
362
375
  )
363
376
  steps[invocation_id] = Step(spec=step.spec, config=step_config)
364
377
 
@@ -103,7 +103,9 @@ def dehydrate_response_model(
103
103
  )
104
104
 
105
105
  dehydrated_values = {}
106
- for key, value in dict(model).items():
106
+ # See `get_subresources_for_model(...)` for a detailed explanation why we
107
+ # need to use `model.__iter__()` here
108
+ for key, value in model.__iter__():
107
109
  dehydrated_values[key] = _dehydrate_value(
108
110
  value, permissions=permissions
109
111
  )
@@ -484,7 +486,13 @@ def get_subresources_for_model(
484
486
  """
485
487
  resources = set()
486
488
 
487
- for value in dict(model).values():
489
+ # We don't want to use `model.model_dump()` here as that recursively
490
+ # converts models to dicts, but we want to preserve those classes for
491
+ # the recursive `_get_subresources_for_value` calls.
492
+ # We previously used `dict(model)` here, but that lead to issues with
493
+ # models overwriting `__getattr__`, this `model.__iter__()` has the same
494
+ # results though.
495
+ for _, value in model.__iter__():
488
496
  resources.update(_get_subresources_for_value(value))
489
497
 
490
498
  return resources
@@ -15,7 +15,7 @@
15
15
 
16
16
  from typing import TYPE_CHECKING, Dict, List, Set, Tuple
17
17
 
18
- from zenml.zen_server.cloud_utils import ZenMLCloudSession
18
+ from zenml.zen_server.cloud_utils import cloud_connection
19
19
  from zenml.zen_server.rbac.models import Action, Resource
20
20
  from zenml.zen_server.rbac.rbac_interface import RBACInterface
21
21
  from zenml.zen_server.utils import server_config
@@ -74,9 +74,13 @@ def _convert_from_cloud_resource(cloud_resource: str) -> Resource:
74
74
  return Resource(type=resource_type_and_id)
75
75
 
76
76
 
77
- class ZenMLCloudRBAC(RBACInterface, ZenMLCloudSession):
77
+ class ZenMLCloudRBAC(RBACInterface):
78
78
  """RBAC implementation that uses the ZenML Pro Management Plane as a backend."""
79
79
 
80
+ def __init__(self) -> None:
81
+ """Initialize the object."""
82
+ self._connection = cloud_connection()
83
+
80
84
  def check_permissions(
81
85
  self, user: "UserResponse", resources: Set[Resource], action: Action
82
86
  ) -> Dict[Resource, bool]:
@@ -110,7 +114,9 @@ class ZenMLCloudRBAC(RBACInterface, ZenMLCloudSession):
110
114
  ],
111
115
  "action": str(action),
112
116
  }
113
- response = self._get(endpoint=PERMISSIONS_ENDPOINT, params=params)
117
+ response = self._connection.get(
118
+ endpoint=PERMISSIONS_ENDPOINT, params=params
119
+ )
114
120
  value = response.json()
115
121
 
116
122
  assert isinstance(value, dict)
@@ -147,7 +153,7 @@ class ZenMLCloudRBAC(RBACInterface, ZenMLCloudSession):
147
153
  "resource": _convert_to_cloud_resource(resource),
148
154
  "action": str(action),
149
155
  }
150
- response = self._get(
156
+ response = self._connection.get(
151
157
  endpoint=ALLOWED_RESOURCE_IDS_ENDPOINT, params=params
152
158
  )
153
159
  response_json = response.json()
@@ -177,4 +183,4 @@ class ZenMLCloudRBAC(RBACInterface, ZenMLCloudSession):
177
183
  "resource": _convert_to_cloud_resource(resource),
178
184
  "actions": [str(action) for action in actions],
179
185
  }
180
- self._post(endpoint=RESOURCE_MEMBERSHIP_ENDPOINT, data=data)
186
+ self._connection.post(endpoint=RESOURCE_MEMBERSHIP_ENDPOINT, data=data)
@@ -27,7 +27,7 @@ from zenml.constants import (
27
27
  DEVICES,
28
28
  VERSION_1,
29
29
  )
30
- from zenml.enums import OAuthDeviceStatus
30
+ from zenml.enums import OAuthDeviceStatus, OnboardingStep
31
31
  from zenml.models import (
32
32
  OAuthDeviceFilter,
33
33
  OAuthDeviceInternalUpdate,
@@ -270,6 +270,9 @@ def verify_authorized_device(
270
270
  update=update,
271
271
  )
272
272
 
273
+ store.update_onboarding_state(
274
+ completed_steps={OnboardingStep.DEVICE_VERIFIED}
275
+ )
273
276
  return device_model
274
277
 
275
278
 
@@ -13,12 +13,19 @@
13
13
  # permissions and limitations under the License.
14
14
  """Endpoint definitions for authentication (login)."""
15
15
 
16
- from typing import Optional
16
+ from typing import List, Optional
17
17
 
18
18
  from fastapi import APIRouter, Security
19
19
 
20
20
  import zenml
21
- from zenml.constants import ACTIVATE, API, INFO, SERVER_SETTINGS, VERSION_1
21
+ from zenml.constants import (
22
+ ACTIVATE,
23
+ API,
24
+ INFO,
25
+ ONBOARDING_STATE,
26
+ SERVER_SETTINGS,
27
+ VERSION_1,
28
+ )
22
29
  from zenml.enums import AuthScheme
23
30
  from zenml.exceptions import IllegalOperationError
24
31
  from zenml.models import (
@@ -64,6 +71,26 @@ def server_info() -> ServerModel:
64
71
  return zen_store().get_store_info()
65
72
 
66
73
 
74
+ @router.get(
75
+ ONBOARDING_STATE,
76
+ responses={
77
+ 401: error_response,
78
+ 404: error_response,
79
+ 422: error_response,
80
+ },
81
+ )
82
+ @handle_exceptions
83
+ def get_onboarding_state(
84
+ _: AuthContext = Security(authorize),
85
+ ) -> List[str]:
86
+ """Get the onboarding state of the server.
87
+
88
+ Returns:
89
+ The onboarding state of the server.
90
+ """
91
+ return zen_store().get_onboarding_state()
92
+
93
+
67
94
  # We don't have any concrete value that tells us whether a server is a cloud
68
95
  # tenant, so we use `external_server_id` as the best proxy option.
69
96
  # For cloud tenants, we don't add these endpoints as the server settings don't
@@ -21,6 +21,7 @@ from fastapi import APIRouter, Depends, Security
21
21
  from zenml.constants import (
22
22
  API,
23
23
  SERVICE_CONNECTOR_CLIENT,
24
+ SERVICE_CONNECTOR_FULL_STACK,
24
25
  SERVICE_CONNECTOR_TYPES,
25
26
  SERVICE_CONNECTOR_VERIFY,
26
27
  SERVICE_CONNECTORS,
@@ -35,6 +36,13 @@ from zenml.models import (
35
36
  ServiceConnectorTypeModel,
36
37
  ServiceConnectorUpdate,
37
38
  )
39
+ from zenml.models.v2.misc.full_stack import (
40
+ ServiceConnectorInfo,
41
+ ServiceConnectorResourcesInfo,
42
+ )
43
+ from zenml.service_connectors.service_connector_utils import (
44
+ get_resources_options_from_resource_model_for_full_stack,
45
+ )
38
46
  from zenml.zen_server.auth import AuthContext, authorize
39
47
  from zenml.zen_server.exceptions import error_response
40
48
  from zenml.zen_server.rbac.endpoint_utils import (
@@ -393,3 +401,52 @@ def get_service_connector_type(
393
401
  The requested service connector type.
394
402
  """
395
403
  return zen_store().get_service_connector_type(connector_type)
404
+
405
+
406
+ @router.post(
407
+ SERVICE_CONNECTOR_FULL_STACK,
408
+ responses={401: error_response, 409: error_response, 422: error_response},
409
+ )
410
+ @handle_exceptions
411
+ def get_resources_based_on_service_connector_info(
412
+ connector_info: Optional[ServiceConnectorInfo] = None,
413
+ connector_uuid: Optional[UUID] = None,
414
+ _: AuthContext = Security(authorize),
415
+ ) -> ServiceConnectorResourcesInfo:
416
+ """Gets the list of resources that a service connector can access.
417
+
418
+ Args:
419
+ connector_info: The service connector info.
420
+ connector_uuid: The service connector uuid.
421
+
422
+ Returns:
423
+ The list of resources that the service connector configuration has
424
+ access to and consumable from UI/CLI.
425
+
426
+ Raises:
427
+ ValueError: If both connector_info and connector_uuid are provided.
428
+ ValueError: If neither connector_info nor connector_uuid are provided.
429
+ """
430
+ if connector_info is not None and connector_uuid is not None:
431
+ raise ValueError(
432
+ "Only one of connector_info or connector_uuid must be provided."
433
+ )
434
+ if connector_info is None and connector_uuid is None:
435
+ raise ValueError(
436
+ "Either connector_info or connector_uuid must be provided."
437
+ )
438
+
439
+ if connector_info is not None:
440
+ verify_permission(
441
+ resource_type=ResourceType.SERVICE_CONNECTOR, action=Action.CREATE
442
+ )
443
+ elif connector_uuid is not None:
444
+ verify_permission(
445
+ resource_type=ResourceType.SERVICE_CONNECTOR,
446
+ action=Action.READ,
447
+ resource_id=connector_uuid,
448
+ )
449
+
450
+ return get_resources_options_from_resource_model_for_full_stack(
451
+ connector_details=connector_info or connector_uuid # type: ignore[arg-type]
452
+ )
@@ -0,0 +1,158 @@
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
+ """Endpoint definitions for stack deployments."""
15
+
16
+ import datetime
17
+ from typing import Optional
18
+
19
+ from fastapi import APIRouter, Request, Security
20
+
21
+ from zenml.constants import (
22
+ API,
23
+ CONFIG,
24
+ INFO,
25
+ STACK,
26
+ STACK_DEPLOYMENT,
27
+ STACK_DEPLOYMENT_API_TOKEN_EXPIRATION,
28
+ VERSION_1,
29
+ )
30
+ from zenml.enums import StackDeploymentProvider
31
+ from zenml.models import (
32
+ DeployedStack,
33
+ StackDeploymentConfig,
34
+ StackDeploymentInfo,
35
+ )
36
+ from zenml.stack_deployments.utils import get_stack_deployment_class
37
+ from zenml.zen_server.auth import AuthContext, authorize
38
+ from zenml.zen_server.exceptions import error_response
39
+ from zenml.zen_server.rbac.models import Action, ResourceType
40
+ from zenml.zen_server.rbac.utils import verify_permission
41
+ from zenml.zen_server.utils import (
42
+ handle_exceptions,
43
+ )
44
+
45
+ router = APIRouter(
46
+ prefix=API + VERSION_1 + STACK_DEPLOYMENT,
47
+ tags=["stacks"],
48
+ responses={401: error_response, 403: error_response},
49
+ )
50
+
51
+
52
+ @router.get(
53
+ INFO,
54
+ )
55
+ @handle_exceptions
56
+ def get_stack_deployment_info(
57
+ provider: StackDeploymentProvider,
58
+ _: AuthContext = Security(authorize),
59
+ ) -> StackDeploymentInfo:
60
+ """Get information about a stack deployment provider.
61
+
62
+ Args:
63
+ provider: The stack deployment provider.
64
+
65
+ Returns:
66
+ Information about the stack deployment provider.
67
+ """
68
+ stack_deployment_class = get_stack_deployment_class(provider)
69
+ return stack_deployment_class.get_deployment_info()
70
+
71
+
72
+ @router.get(
73
+ CONFIG,
74
+ )
75
+ @handle_exceptions
76
+ def get_stack_deployment_config(
77
+ request: Request,
78
+ provider: StackDeploymentProvider,
79
+ stack_name: str,
80
+ location: Optional[str] = None,
81
+ auth_context: AuthContext = Security(authorize),
82
+ ) -> StackDeploymentConfig:
83
+ """Return the URL to deploy the ZenML stack to the specified cloud provider.
84
+
85
+ Args:
86
+ request: The FastAPI request object.
87
+ provider: The stack deployment provider.
88
+ stack_name: The name of the stack.
89
+ location: The location where the stack should be deployed.
90
+ auth_context: The authentication context.
91
+
92
+ Returns:
93
+ The cloud provider console URL where the stack will be deployed and
94
+ the configuration for the stack deployment.
95
+ """
96
+ verify_permission(
97
+ resource_type=ResourceType.SERVICE_CONNECTOR, action=Action.CREATE
98
+ )
99
+ verify_permission(
100
+ resource_type=ResourceType.STACK_COMPONENT,
101
+ action=Action.CREATE,
102
+ )
103
+ verify_permission(resource_type=ResourceType.STACK, action=Action.CREATE)
104
+
105
+ stack_deployment_class = get_stack_deployment_class(provider)
106
+ # Get the base server URL used to call this FastAPI endpoint
107
+ url = request.url.replace(path="").replace(query="")
108
+ # Use HTTPS for the URL
109
+ url = url.replace(scheme="https")
110
+
111
+ token = auth_context.access_token
112
+ assert token is not None
113
+
114
+ # A new API token is generated for the stack deployment
115
+ expires = datetime.datetime.utcnow() + datetime.timedelta(
116
+ minutes=STACK_DEPLOYMENT_API_TOKEN_EXPIRATION
117
+ )
118
+ api_token = token.encode(expires=expires)
119
+
120
+ return stack_deployment_class(
121
+ stack_name=stack_name,
122
+ location=location,
123
+ zenml_server_url=str(url),
124
+ zenml_server_api_token=api_token,
125
+ ).get_deployment_config()
126
+
127
+
128
+ @router.get(
129
+ STACK,
130
+ )
131
+ @handle_exceptions
132
+ def get_deployed_stack(
133
+ provider: StackDeploymentProvider,
134
+ stack_name: str,
135
+ location: Optional[str] = None,
136
+ date_start: Optional[datetime.datetime] = None,
137
+ _: AuthContext = Security(authorize),
138
+ ) -> Optional[DeployedStack]:
139
+ """Return a matching ZenML stack that was deployed and registered.
140
+
141
+ Args:
142
+ provider: The stack deployment provider.
143
+ stack_name: The name of the stack.
144
+ location: The location where the stack should be deployed.
145
+ date_start: The date when the deployment started.
146
+
147
+ Returns:
148
+ The ZenML stack that was deployed and registered or None if the stack
149
+ was not found.
150
+ """
151
+ stack_deployment_class = get_stack_deployment_class(provider)
152
+ return stack_deployment_class(
153
+ stack_name=stack_name,
154
+ location=location,
155
+ # These fields are not needed for this operation
156
+ zenml_server_url="",
157
+ zenml_server_api_token="",
158
+ ).get_stack(date_start=date_start)
@@ -117,7 +117,8 @@ def create_run_step(
117
117
  pipeline_run = zen_store().get_run(step.pipeline_run_id)
118
118
  verify_permission_for_model(pipeline_run, action=Action.UPDATE)
119
119
 
120
- return zen_store().create_run_step(step_run=step)
120
+ step_response = zen_store().create_run_step(step_run=step)
121
+ return dehydrate_response_model(step_response)
121
122
 
122
123
 
123
124
  @router.get(
@@ -22,6 +22,7 @@ from zenml.constants import (
22
22
  API,
23
23
  ARTIFACTS,
24
24
  CODE_REPOSITORIES,
25
+ FULL_STACK,
25
26
  GET_OR_CREATE,
26
27
  MODEL_VERSIONS,
27
28
  MODELS,
@@ -51,6 +52,7 @@ from zenml.models import (
51
52
  ComponentFilter,
52
53
  ComponentRequest,
53
54
  ComponentResponse,
55
+ FullStackRequest,
54
56
  ModelRequest,
55
57
  ModelResponse,
56
58
  ModelVersionArtifactRequest,
@@ -349,6 +351,68 @@ def create_stack(
349
351
  )
350
352
 
351
353
 
354
+ @router.post(
355
+ WORKSPACES + "/{workspace_name_or_id}" + FULL_STACK,
356
+ response_model=StackResponse,
357
+ responses={401: error_response, 409: error_response, 422: error_response},
358
+ )
359
+ @handle_exceptions
360
+ def create_full_stack(
361
+ workspace_name_or_id: Union[str, UUID],
362
+ full_stack: FullStackRequest,
363
+ auth_context: AuthContext = Security(authorize),
364
+ ) -> StackResponse:
365
+ """Creates a stack for a particular workspace.
366
+
367
+ Args:
368
+ workspace_name_or_id: Name or ID of the workspace.
369
+ full_stack: Stack to register.
370
+ auth_context: Authentication context.
371
+
372
+ Returns:
373
+ The created stack.
374
+ """
375
+ workspace = zen_store().get_workspace(workspace_name_or_id)
376
+
377
+ is_connector_create_needed = False
378
+ for connector_id_or_info in full_stack.service_connectors:
379
+ if isinstance(connector_id_or_info, UUID):
380
+ service_connector = zen_store().get_service_connector(
381
+ connector_id_or_info, hydrate=False
382
+ )
383
+ verify_permission_for_model(
384
+ model=service_connector, action=Action.READ
385
+ )
386
+ else:
387
+ is_connector_create_needed = True
388
+ if is_connector_create_needed:
389
+ verify_permission(
390
+ resource_type=ResourceType.SERVICE_CONNECTOR, action=Action.CREATE
391
+ )
392
+
393
+ is_component_create_needed = False
394
+ for component_id_or_info in full_stack.components.values():
395
+ if isinstance(component_id_or_info, UUID):
396
+ component = zen_store().get_stack_component(
397
+ component_id_or_info, hydrate=False
398
+ )
399
+ verify_permission_for_model(model=component, action=Action.READ)
400
+ else:
401
+ is_component_create_needed = True
402
+ if is_component_create_needed:
403
+ verify_permission(
404
+ resource_type=ResourceType.STACK_COMPONENT,
405
+ action=Action.CREATE,
406
+ )
407
+
408
+ verify_permission(resource_type=ResourceType.STACK, action=Action.CREATE)
409
+
410
+ full_stack.user = auth_context.user.id
411
+ full_stack.workspace = workspace.id
412
+
413
+ return zen_store().create_full_stack(full_stack)
414
+
415
+
352
416
  @router.get(
353
417
  WORKSPACES + "/{workspace_name_or_id}" + STACK_COMPONENTS,
354
418
  response_model=Page[ComponentResponse],