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.
- README.md +2 -2
- RELEASE_NOTES.md +120 -0
- zenml/VERSION +1 -1
- zenml/__init__.py +0 -4
- zenml/actions/pipeline_run/pipeline_run_action.py +19 -17
- zenml/analytics/enums.py +4 -6
- zenml/cli/__init__.py +28 -76
- zenml/cli/base.py +2 -2
- zenml/cli/pipeline.py +54 -61
- zenml/cli/stack.py +6 -8
- zenml/cli/web_login.py +8 -0
- zenml/client.py +232 -103
- zenml/config/build_configuration.py +43 -17
- zenml/config/compiler.py +14 -22
- zenml/config/docker_settings.py +80 -57
- zenml/config/pipeline_run_configuration.py +3 -0
- zenml/config/server_config.py +3 -0
- zenml/config/source.py +60 -1
- zenml/constants.py +11 -2
- zenml/entrypoints/base_entrypoint_configuration.py +53 -8
- zenml/enums.py +4 -1
- zenml/environment.py +25 -9
- zenml/image_builders/base_image_builder.py +1 -1
- zenml/image_builders/build_context.py +25 -72
- zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +13 -4
- zenml/integrations/azure/__init__.py +4 -0
- zenml/integrations/azure/flavors/__init__.py +11 -0
- zenml/integrations/azure/flavors/azureml_orchestrator_flavor.py +263 -0
- zenml/{_hub → integrations/azure/orchestrators}/__init__.py +7 -2
- zenml/integrations/azure/orchestrators/azureml_orchestrator.py +544 -0
- zenml/integrations/azure/orchestrators/azureml_orchestrator_entrypoint_config.py +86 -0
- zenml/integrations/azure/step_operators/azureml_step_operator.py +3 -0
- zenml/integrations/databricks/flavors/databricks_orchestrator_flavor.py +20 -2
- zenml/integrations/databricks/orchestrators/databricks_orchestrator.py +19 -13
- zenml/integrations/gcp/orchestrators/vertex_orchestrator.py +7 -2
- zenml/integrations/gcp/service_connectors/gcp_service_connector.py +123 -6
- zenml/integrations/kaniko/image_builders/kaniko_image_builder.py +1 -1
- zenml/integrations/mlflow/__init__.py +1 -1
- zenml/integrations/mlflow/experiment_trackers/mlflow_experiment_tracker.py +3 -1
- zenml/integrations/mlflow/flavors/mlflow_experiment_tracker_flavor.py +3 -0
- zenml/logger.py +13 -0
- zenml/models/__init__.py +26 -22
- zenml/models/v2/base/filter.py +32 -0
- zenml/models/v2/core/pipeline.py +73 -89
- zenml/models/v2/core/pipeline_build.py +15 -11
- zenml/models/v2/core/pipeline_deployment.py +72 -24
- zenml/models/v2/core/pipeline_run.py +65 -1
- zenml/models/v2/core/run_template.py +393 -0
- zenml/models/v2/core/server_settings.py +12 -0
- zenml/models/v2/core/user.py +0 -21
- zenml/models/v2/misc/server_models.py +7 -1
- zenml/models/v2/misc/stack_deployment.py +5 -0
- zenml/models/v2/misc/user_auth.py +0 -7
- zenml/new/pipelines/build_utils.py +220 -89
- zenml/new/pipelines/code_archive.py +157 -0
- zenml/new/pipelines/pipeline.py +46 -78
- zenml/new/pipelines/run_utils.py +79 -1
- zenml/post_execution/pipeline.py +1 -4
- zenml/service_connectors/service_connector_utils.py +18 -2
- zenml/stack_deployments/aws_stack_deployment.py +32 -8
- zenml/stack_deployments/azure_stack_deployment.py +122 -10
- zenml/stack_deployments/gcp_stack_deployment.py +36 -7
- zenml/stack_deployments/stack_deployment.py +23 -7
- zenml/steps/base_step.py +3 -0
- zenml/steps/utils.py +0 -4
- zenml/utils/archivable.py +149 -0
- zenml/utils/code_utils.py +244 -0
- zenml/utils/notebook_utils.py +122 -0
- zenml/utils/package_utils.py +39 -0
- zenml/utils/pipeline_docker_image_builder.py +3 -96
- zenml/utils/source_utils.py +109 -1
- zenml/zen_server/dashboard/assets/{404-B_YdvmwS.js → 404-CRAA_Lew.js} +1 -1
- zenml/zen_server/dashboard/assets/@radix-BXWm7HOa.js +85 -0
- zenml/zen_server/dashboard/assets/{@react-router-CO-OsFwI.js → @react-router-l3lMcXA2.js} +1 -1
- zenml/zen_server/dashboard/assets/{@reactflow-l_1hUr1S.js → @reactflow-CeVxyqYT.js} +2 -2
- zenml/zen_server/dashboard/assets/{@tanstack-DYiOyJUL.js → @tanstack-FmcYZMuX.js} +4 -4
- zenml/zen_server/dashboard/assets/AlertDialogDropdownItem-ErO9aOgK.js +1 -0
- zenml/zen_server/dashboard/assets/{AwarenessChannel-CFg5iX4Z.js → AwarenessChannel-CLXo5rKM.js} +1 -1
- zenml/zen_server/dashboard/assets/{CodeSnippet-Dvkx_82E.js → CodeSnippet-D0VLxT2A.js} +2 -2
- zenml/zen_server/dashboard/assets/CollapsibleCard-BaUPiVg0.js +1 -0
- zenml/zen_server/dashboard/assets/{Commands-DoN1xrEq.js → Commands-JrcZK-3j.js} +1 -1
- zenml/zen_server/dashboard/assets/CopyButton-Dbo52T1K.js +2 -0
- zenml/zen_server/dashboard/assets/{CsvVizualization-Ck-nZ43m.js → CsvVizualization-D3kAypDj.js} +3 -3
- zenml/zen_server/dashboard/assets/DisplayDate-DizbSeT-.js +1 -0
- zenml/zen_server/dashboard/assets/EditSecretDialog-Bd7mFLS4.js +1 -0
- zenml/zen_server/dashboard/assets/{EmptyState-BMLnFVlB.js → EmptyState-BHblM39I.js} +1 -1
- zenml/zen_server/dashboard/assets/{Error-kLtljEOM.js → Error-C6LeJSER.js} +1 -1
- zenml/zen_server/dashboard/assets/{ExecutionStatus-DguLLgTK.js → ExecutionStatus-jH4OrWBq.js} +1 -1
- zenml/zen_server/dashboard/assets/{Helpbox-BXUMP21n.js → Helpbox-aAB2XP-z.js} +1 -1
- zenml/zen_server/dashboard/assets/{Infobox-DSt0O-dm.js → Infobox-BQ0aty32.js} +1 -1
- zenml/zen_server/dashboard/assets/{InlineAvatar-xsrsIGE-.js → InlineAvatar-DpTLgM3Q.js} +1 -1
- zenml/zen_server/dashboard/assets/Lock-CNyJvf2r.js +1 -0
- zenml/zen_server/dashboard/assets/{MarkdownVisualization-xp3hhULl.js → MarkdownVisualization-Bajxn0HY.js} +1 -1
- zenml/zen_server/dashboard/assets/NumberBox-BmKE0qnO.js +1 -0
- zenml/zen_server/dashboard/assets/{PasswordChecker-DUveqlva.js → PasswordChecker-yGGoJSB-.js} +1 -1
- zenml/zen_server/dashboard/assets/ProviderRadio-BBqkIuTd.js +1 -0
- zenml/zen_server/dashboard/assets/RadioItem-xLhXoiFV.js +1 -0
- zenml/zen_server/dashboard/assets/SearchField-C9R0mdaX.js +1 -0
- zenml/zen_server/dashboard/assets/{SetPassword-BXGTWiwj.js → SetPassword-52sNxNiO.js} +1 -1
- zenml/zen_server/dashboard/assets/{SuccessStep-DZC60t0x.js → SuccessStep-DlkItqYG.js} +1 -1
- zenml/zen_server/dashboard/assets/Tick-uxv80Q6a.js +1 -0
- zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-DGvwFWO1.js → UpdatePasswordSchemas-oN4G3sKz.js} +1 -1
- zenml/zen_server/dashboard/assets/{aws-BgKTfTfx.js → aws-0_3UsPif.js} +1 -1
- zenml/zen_server/dashboard/assets/{check-circle-i56092KI.js → check-circle-1_I207rW.js} +1 -1
- zenml/zen_server/dashboard/assets/chevron-down-BpaF8JqM.js +1 -0
- zenml/zen_server/dashboard/assets/{chevron-right-double-CZBOf6JM.js → chevron-right-double-Dk8e2L99.js} +1 -1
- zenml/zen_server/dashboard/assets/{cloud-only-C_yFCAkP.js → cloud-only-BkUuI0lZ.js} +1 -1
- zenml/zen_server/dashboard/assets/components-Br2ezRib.js +1 -0
- zenml/zen_server/dashboard/assets/{copy-BXNk6BjL.js → copy-f3XGPPxt.js} +1 -1
- zenml/zen_server/dashboard/assets/{database-1xWSgZfO.js → database-cXYNX9tt.js} +1 -1
- zenml/zen_server/dashboard/assets/{docker-CQMVm_4d.js → docker-8uj__HHK.js} +1 -1
- zenml/zen_server/dashboard/assets/dots-horizontal-sKQlWEni.js +1 -0
- zenml/zen_server/dashboard/assets/edit-C0MVvPD2.js +1 -0
- zenml/zen_server/dashboard/assets/{file-text-CqD_iu6l.js → file-text-B9JibxTs.js} +1 -1
- zenml/zen_server/dashboard/assets/{help-bu_DgLKI.js → help-FuHlZwn0.js} +1 -1
- zenml/zen_server/dashboard/assets/{index-rK_Wuy2W.js → index-Bd1xgUQG.js} +1 -1
- zenml/zen_server/dashboard/assets/index-DaGknux4.css +1 -0
- zenml/zen_server/dashboard/assets/{index-BczVOqUf.js → index-DhIZtpxB.js} +5 -5
- zenml/zen_server/dashboard/assets/index.esm-DT4uyn2i.js +1 -0
- zenml/zen_server/dashboard/assets/layout-D6oiSbfd.js +1 -0
- zenml/zen_server/dashboard/assets/{login-mutation-CrHrndTI.js → login-mutation-13A_JSVA.js} +1 -1
- zenml/zen_server/dashboard/assets/{logs-D8k8BVFf.js → logs-CgeE2vZP.js} +1 -1
- zenml/zen_server/dashboard/assets/{not-found-DYa4pC-C.js → not-found-B0Mmb90p.js} +1 -1
- zenml/zen_server/dashboard/assets/package-DdkziX79.js +1 -0
- zenml/zen_server/dashboard/assets/page-7-v2OBm-.js +1 -0
- zenml/zen_server/dashboard/assets/{page-MFQyIJd3.js → page-B3ozwdD1.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-BkuQDIf-.js → page-BGwA9B1M.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-1iL8aMqs.js → page-BkjAUyTA.js} +1 -1
- zenml/zen_server/dashboard/assets/page-BnacgBiy.js +1 -0
- zenml/zen_server/dashboard/assets/page-BxF_KMQ3.js +2 -0
- zenml/zen_server/dashboard/assets/page-C4POHC0K.js +1 -0
- zenml/zen_server/dashboard/assets/page-C9kudd44.js +9 -0
- zenml/zen_server/dashboard/assets/page-CA1j3GpJ.js +1 -0
- zenml/zen_server/dashboard/assets/page-CCY6yfmu.js +1 -0
- zenml/zen_server/dashboard/assets/page-CgTe7Bme.js +1 -0
- zenml/zen_server/dashboard/assets/{page-8a4UMKXZ.js → page-Cgn-6v2Y.js} +1 -1
- zenml/zen_server/dashboard/assets/page-CxQmQqDw.js +1 -0
- zenml/zen_server/dashboard/assets/page-D2Goey3H.js +1 -0
- zenml/zen_server/dashboard/assets/page-DLpOnf7u.js +1 -0
- zenml/zen_server/dashboard/assets/{page-BhgCDInH.js → page-DSTQnBk-.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-1h_sD1jz.js → page-DTysUGOy.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-2grKx_MY.js → page-D_EXUFJb.js} +1 -1
- zenml/zen_server/dashboard/assets/page-Db15QzsM.js +1 -0
- zenml/zen_server/dashboard/assets/{page-BDns21Iz.js → page-DugsjcQ_.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-C6-UGEbH.js → page-OFKSPyN7.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-BkeAAYwp.js → page-RnG-qhv9.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-CCNRIt_f.js → page-T2BtjwPl.js} +1 -1
- zenml/zen_server/dashboard/assets/page-TXe1Eo3Z.js +1 -0
- zenml/zen_server/dashboard/assets/{page-BnaevhnB.js → page-YiF_fNbe.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-uA5prJGY.js → page-hQaiQXfg.js} +1 -1
- zenml/zen_server/dashboard/assets/persist-3-5nOJ6m.js +1 -0
- zenml/zen_server/dashboard/assets/{play-circle-CNtZKDnW.js → play-circle-XSkLR12B.js} +1 -1
- zenml/zen_server/dashboard/assets/plus-FB9-lEq_.js +1 -0
- zenml/zen_server/dashboard/assets/refresh-COb6KYDi.js +1 -0
- zenml/zen_server/dashboard/assets/sharedSchema-BoYx_B_L.js +14 -0
- zenml/zen_server/dashboard/assets/{stack-detail-query-Cficsl6d.js → stack-detail-query-B-US_-wa.js} +1 -1
- zenml/zen_server/dashboard/assets/{terminal-By9cErXc.js → terminal-grtjrIEJ.js} +1 -1
- zenml/zen_server/dashboard/assets/trash-Cd5CSFqA.js +1 -0
- zenml/zen_server/dashboard/assets/{update-server-settings-mutation-7d8xi1tS.js → update-server-settings-mutation-B8GB_ubU.js} +1 -1
- zenml/zen_server/dashboard/assets/{url-D7mAQGUM.js → url-hcMJkz8p.js} +1 -1
- zenml/zen_server/dashboard/assets/{zod-BhoGpZ63.js → zod-CnykDKJj.js} +1 -1
- zenml/zen_server/dashboard/index.html +7 -7
- zenml/zen_server/dashboard_legacy/asset-manifest.json +4 -4
- zenml/zen_server/dashboard_legacy/index.html +1 -1
- zenml/zen_server/dashboard_legacy/{precache-manifest.12246c7548e71e2c4438e496360de80c.js → precache-manifest.9c473c96a43298343a7ce1256183123b.js} +4 -4
- zenml/zen_server/dashboard_legacy/service-worker.js +1 -1
- zenml/zen_server/dashboard_legacy/static/js/{main.3b27024b.chunk.js → main.463c90b9.chunk.js} +2 -2
- zenml/zen_server/dashboard_legacy/static/js/{main.3b27024b.chunk.js.map → main.463c90b9.chunk.js.map} +1 -1
- zenml/zen_server/deploy/helm/Chart.yaml +1 -1
- zenml/zen_server/deploy/helm/README.md +2 -2
- zenml/zen_server/rbac/models.py +1 -0
- zenml/zen_server/rbac/utils.py +4 -0
- zenml/zen_server/routers/pipeline_builds_endpoints.py +2 -66
- zenml/zen_server/routers/pipeline_deployments_endpoints.py +2 -53
- zenml/zen_server/routers/pipelines_endpoints.py +1 -74
- zenml/zen_server/routers/run_templates_endpoints.py +212 -0
- zenml/zen_server/routers/stack_deployment_endpoints.py +6 -0
- zenml/zen_server/routers/users_endpoints.py +0 -7
- zenml/zen_server/routers/workspaces_endpoints.py +79 -0
- zenml/zen_server/{pipeline_deployment → template_execution}/runner_entrypoint_configuration.py +1 -8
- zenml/zen_server/{pipeline_deployment → template_execution}/utils.py +214 -92
- zenml/zen_server/utils.py +77 -2
- zenml/zen_server/zen_server_api.py +54 -2
- zenml/zen_stores/base_zen_store.py +7 -1
- zenml/zen_stores/migrations/versions/0.63.0_release.py +23 -0
- zenml/zen_stores/migrations/versions/0.64.0_release.py +23 -0
- zenml/zen_stores/migrations/versions/026d4577b6a0_add_code_path.py +39 -0
- zenml/zen_stores/migrations/versions/3dcc5d20e82f_add_last_user_activity.py +51 -0
- zenml/zen_stores/migrations/versions/7d1919bb1ef0_add_run_templates.py +100 -0
- zenml/zen_stores/migrations/versions/909550c7c4da_remove_user_hub_token.py +36 -0
- zenml/zen_stores/migrations/versions/b59aa68fdb1f_simplify_pipelines.py +139 -0
- zenml/zen_stores/rest_zen_store.py +112 -39
- zenml/zen_stores/schemas/__init__.py +2 -0
- zenml/zen_stores/schemas/pipeline_build_schemas.py +3 -3
- zenml/zen_stores/schemas/pipeline_deployment_schemas.py +32 -2
- zenml/zen_stores/schemas/pipeline_run_schemas.py +29 -3
- zenml/zen_stores/schemas/pipeline_schemas.py +29 -30
- zenml/zen_stores/schemas/run_template_schemas.py +264 -0
- zenml/zen_stores/schemas/server_settings_schemas.py +2 -0
- zenml/zen_stores/schemas/step_run_schemas.py +11 -4
- zenml/zen_stores/schemas/user_schemas.py +0 -2
- zenml/zen_stores/sql_zen_store.py +389 -151
- zenml/zen_stores/template_utils.py +261 -0
- zenml/zen_stores/zen_store_interface.py +93 -20
- {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/METADATA +3 -3
- {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/RECORD +211 -184
- zenml/_hub/client.py +0 -289
- zenml/_hub/constants.py +0 -21
- zenml/_hub/utils.py +0 -79
- zenml/cli/hub.py +0 -1116
- zenml/models/v2/core/pipeline_namespace.py +0 -113
- zenml/models/v2/misc/hub_plugin_models.py +0 -79
- zenml/new/pipelines/deserialization_utils.py +0 -292
- zenml/zen_server/dashboard/assets/@radix-CFOkMR_E.js +0 -85
- zenml/zen_server/dashboard/assets/CollapsibleCard-opiuBHHc.js +0 -1
- zenml/zen_server/dashboard/assets/CopyButton-Cr7xYEPb.js +0 -2
- zenml/zen_server/dashboard/assets/DisplayDate-DYgIjlDF.js +0 -1
- zenml/zen_server/dashboard/assets/Pagination-C6X-mifw.js +0 -1
- zenml/zen_server/dashboard/assets/index-EpMIKgrI.css +0 -1
- zenml/zen_server/dashboard/assets/index.esm-Corw4lXQ.js +0 -1
- zenml/zen_server/dashboard/assets/package-B3fWP-Dh.js +0 -1
- zenml/zen_server/dashboard/assets/page-5NCOHOsy.js +0 -1
- zenml/zen_server/dashboard/assets/page-B6h3iaHJ.js +0 -1
- zenml/zen_server/dashboard/assets/page-Bi-wtWiO.js +0 -5
- zenml/zen_server/dashboard/assets/page-Bq0YxkLV.js +0 -1
- zenml/zen_server/dashboard/assets/page-Bs2F4eoD.js +0 -2
- zenml/zen_server/dashboard/assets/page-CHNxpz3n.js +0 -1
- zenml/zen_server/dashboard/assets/page-DgorQFqi.js +0 -1
- zenml/zen_server/dashboard/assets/page-K8ebxVIs.js +0 -1
- zenml/zen_server/dashboard/assets/page-TgCF0P_U.js +0 -1
- zenml/zen_server/dashboard/assets/page-ZnCEe-eK.js +0 -9
- zenml/zen_server/dashboard/assets/persist-D7HJNBWx.js +0 -1
- zenml/zen_server/dashboard/assets/plus-C8WOyCzt.js +0 -1
- zenml/zen_server/dashboard/assets/secrets-video-OBJ6irhH.svg +0 -21
- zenml/zen_server/dashboard/assets/stacks-video-7gfxpAq4.svg +0 -21
- /zenml/zen_server/{pipeline_deployment → template_execution}/__init__.py +0 -0
- /zenml/zen_server/{pipeline_deployment → template_execution}/workload_manager_interface.py +0 -0
- {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/WHEEL +0 -0
- {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/entry_points.txt +0 -0
@@ -15,7 +15,6 @@
|
|
15
15
|
|
16
16
|
import hashlib
|
17
17
|
import platform
|
18
|
-
from pathlib import Path
|
19
18
|
from typing import (
|
20
19
|
TYPE_CHECKING,
|
21
20
|
Dict,
|
@@ -36,12 +35,10 @@ from zenml.models import (
|
|
36
35
|
PipelineBuildRequest,
|
37
36
|
PipelineBuildResponse,
|
38
37
|
PipelineDeploymentBase,
|
39
|
-
|
38
|
+
StackResponse,
|
40
39
|
)
|
41
40
|
from zenml.stack import Stack
|
42
|
-
from zenml.utils import
|
43
|
-
source_utils,
|
44
|
-
)
|
41
|
+
from zenml.utils import source_utils
|
45
42
|
from zenml.utils.pipeline_docker_image_builder import (
|
46
43
|
PipelineDockerImageBuilder,
|
47
44
|
)
|
@@ -53,64 +50,104 @@ if TYPE_CHECKING:
|
|
53
50
|
logger = get_logger(__name__)
|
54
51
|
|
55
52
|
|
56
|
-
def
|
53
|
+
def build_required(deployment: "PipelineDeploymentBase") -> bool:
|
54
|
+
"""Checks whether a build is required for the deployment and active stack.
|
55
|
+
|
56
|
+
Args:
|
57
|
+
deployment: The deployment for which to check.
|
58
|
+
|
59
|
+
Returns:
|
60
|
+
If a build is required.
|
61
|
+
"""
|
62
|
+
stack = Client().active_stack
|
63
|
+
return bool(stack.get_docker_builds(deployment=deployment))
|
64
|
+
|
65
|
+
|
66
|
+
def requires_included_code(
|
57
67
|
deployment: "PipelineDeploymentBase",
|
58
|
-
pipeline_id: Optional[UUID] = None,
|
59
68
|
code_repository: Optional["BaseCodeRepository"] = None,
|
60
|
-
) ->
|
61
|
-
"""
|
69
|
+
) -> bool:
|
70
|
+
"""Checks whether the deployment requires included code.
|
62
71
|
|
63
72
|
Args:
|
64
|
-
deployment:
|
65
|
-
|
66
|
-
|
73
|
+
deployment: The deployment.
|
74
|
+
code_repository: If provided, this code repository can be used to
|
75
|
+
download the code inside the container images.
|
67
76
|
|
68
77
|
Returns:
|
69
|
-
|
78
|
+
If the deployment requires code included in the container images.
|
70
79
|
"""
|
71
|
-
|
80
|
+
for step in deployment.step_configurations.values():
|
81
|
+
docker_settings = step.config.docker_settings
|
72
82
|
|
73
|
-
|
74
|
-
|
75
|
-
code_repository.get_local_context(source_root)
|
76
|
-
if code_repository
|
77
|
-
else None
|
78
|
-
)
|
79
|
-
if local_repo_context and not local_repo_context.is_dirty:
|
80
|
-
subdirectory = (
|
81
|
-
Path(source_root).resolve().relative_to(local_repo_context.root)
|
82
|
-
)
|
83
|
+
if docker_settings.allow_download_from_artifact_store:
|
84
|
+
return False
|
83
85
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
code_repository=local_repo_context.code_repository_id,
|
88
|
-
)
|
86
|
+
if docker_settings.allow_download_from_code_repository:
|
87
|
+
if code_repository:
|
88
|
+
continue
|
89
89
|
|
90
|
-
|
91
|
-
|
92
|
-
workspace=Client().active_workspace.id,
|
93
|
-
stack=Client().active_stack.id,
|
94
|
-
pipeline=pipeline_id,
|
95
|
-
code_reference=code_reference,
|
96
|
-
**deployment.model_dump(),
|
97
|
-
)
|
98
|
-
return (
|
99
|
-
Client().zen_store.create_deployment(deployment=deployment_request).id
|
100
|
-
)
|
90
|
+
if docker_settings.allow_including_files_in_images:
|
91
|
+
return True
|
101
92
|
|
93
|
+
return False
|
102
94
|
|
103
|
-
|
104
|
-
|
95
|
+
|
96
|
+
def requires_download_from_code_repository(
|
97
|
+
deployment: "PipelineDeploymentBase",
|
98
|
+
) -> bool:
|
99
|
+
"""Checks whether the deployment needs to download code from a repository.
|
105
100
|
|
106
101
|
Args:
|
107
|
-
deployment: The deployment
|
102
|
+
deployment: The deployment.
|
108
103
|
|
109
104
|
Returns:
|
110
|
-
If a
|
105
|
+
If the deployment needs to download code from a code repository.
|
111
106
|
"""
|
112
|
-
|
113
|
-
|
107
|
+
for step in deployment.step_configurations.values():
|
108
|
+
docker_settings = step.config.docker_settings
|
109
|
+
|
110
|
+
if docker_settings.allow_download_from_artifact_store:
|
111
|
+
return False
|
112
|
+
|
113
|
+
if docker_settings.allow_including_files_in_images:
|
114
|
+
return False
|
115
|
+
|
116
|
+
if docker_settings.allow_download_from_code_repository:
|
117
|
+
# The other two options are false, which means download from a
|
118
|
+
# code repo is required.
|
119
|
+
return True
|
120
|
+
|
121
|
+
return False
|
122
|
+
|
123
|
+
|
124
|
+
def code_download_possible(
|
125
|
+
deployment: "PipelineDeploymentBase",
|
126
|
+
code_repository: Optional["BaseCodeRepository"] = None,
|
127
|
+
) -> bool:
|
128
|
+
"""Checks whether code download is possible for the deployment.
|
129
|
+
|
130
|
+
Args:
|
131
|
+
deployment: The deployment.
|
132
|
+
code_repository: If provided, this code repository can be used to
|
133
|
+
download the code inside the container images.
|
134
|
+
|
135
|
+
Returns:
|
136
|
+
Whether code download is possible for the deployment.
|
137
|
+
"""
|
138
|
+
for step in deployment.step_configurations.values():
|
139
|
+
if step.config.docker_settings.allow_download_from_artifact_store:
|
140
|
+
continue
|
141
|
+
|
142
|
+
if (
|
143
|
+
step.config.docker_settings.allow_download_from_code_repository
|
144
|
+
and code_repository
|
145
|
+
):
|
146
|
+
continue
|
147
|
+
|
148
|
+
return False
|
149
|
+
|
150
|
+
return True
|
114
151
|
|
115
152
|
|
116
153
|
def reuse_or_create_pipeline_build(
|
@@ -131,8 +168,8 @@ def reuse_or_create_pipeline_build(
|
|
131
168
|
build: Optional existing build. If given, the build will be fetched
|
132
169
|
(or registered) in the database. If not given, a new build will
|
133
170
|
be created.
|
134
|
-
code_repository: If provided, this code repository
|
135
|
-
download inside the
|
171
|
+
code_repository: If provided, this code repository can be used to
|
172
|
+
download code inside the container images.
|
136
173
|
|
137
174
|
Returns:
|
138
175
|
The build response.
|
@@ -140,8 +177,10 @@ def reuse_or_create_pipeline_build(
|
|
140
177
|
if not build:
|
141
178
|
if (
|
142
179
|
allow_build_reuse
|
143
|
-
and
|
144
|
-
and not
|
180
|
+
and not deployment.should_prevent_build_reuse
|
181
|
+
and not requires_included_code(
|
182
|
+
deployment=deployment, code_repository=code_repository
|
183
|
+
)
|
145
184
|
and build_required(deployment=deployment)
|
146
185
|
):
|
147
186
|
existing_build = find_existing_build(
|
@@ -157,17 +196,13 @@ def reuse_or_create_pipeline_build(
|
|
157
196
|
return existing_build
|
158
197
|
else:
|
159
198
|
logger.info(
|
160
|
-
"Unable to find a build to reuse.
|
161
|
-
"
|
162
|
-
"following conditions are met:\n"
|
199
|
+
"Unable to find a build to reuse. A previous build can be "
|
200
|
+
"reused when the following conditions are met:\n"
|
163
201
|
" * The existing build was created for the same stack, "
|
164
202
|
"ZenML version and Python version\n"
|
165
203
|
" * The stack contains a container registry\n"
|
166
204
|
" * The Docker settings of the pipeline and all its steps "
|
167
|
-
"are the same as for the existing build
|
168
|
-
" * The build does not include code. This will only be "
|
169
|
-
"the case if the existing build was created with a clean "
|
170
|
-
"code repository."
|
205
|
+
"are the same as for the existing build."
|
171
206
|
)
|
172
207
|
|
173
208
|
return create_pipeline_build(
|
@@ -199,7 +234,7 @@ def reuse_or_create_pipeline_build(
|
|
199
234
|
|
200
235
|
def find_existing_build(
|
201
236
|
deployment: "PipelineDeploymentBase",
|
202
|
-
code_repository: "BaseCodeRepository",
|
237
|
+
code_repository: Optional["BaseCodeRepository"] = None,
|
203
238
|
) -> Optional["PipelineBuildResponse"]:
|
204
239
|
"""Find an existing build for a deployment.
|
205
240
|
|
@@ -269,6 +304,7 @@ def create_pipeline_build(
|
|
269
304
|
settings were specified.
|
270
305
|
"""
|
271
306
|
client = Client()
|
307
|
+
stack_model = Client().active_stack_model
|
272
308
|
stack = client.active_stack
|
273
309
|
required_builds = stack.get_docker_builds(deployment=deployment)
|
274
310
|
|
@@ -328,6 +364,11 @@ def create_pipeline_build(
|
|
328
364
|
download_files = build_config.should_download_files(
|
329
365
|
code_repository=code_repository,
|
330
366
|
)
|
367
|
+
pass_code_repo = (
|
368
|
+
build_config.should_download_files_from_code_repository(
|
369
|
+
code_repository=code_repository
|
370
|
+
)
|
371
|
+
)
|
331
372
|
|
332
373
|
(
|
333
374
|
image_name_or_digest,
|
@@ -341,7 +382,7 @@ def create_pipeline_build(
|
|
341
382
|
download_files=download_files,
|
342
383
|
entrypoint=build_config.entrypoint,
|
343
384
|
extra_files=build_config.extra_files,
|
344
|
-
code_repository=code_repository,
|
385
|
+
code_repository=code_repository if pass_code_repo else None,
|
345
386
|
)
|
346
387
|
contains_code = include_files
|
347
388
|
|
@@ -362,16 +403,11 @@ def create_pipeline_build(
|
|
362
403
|
build_checksum = compute_build_checksum(
|
363
404
|
required_builds, stack=stack, code_repository=code_repository
|
364
405
|
)
|
365
|
-
|
366
|
-
deployment=deployment,
|
367
|
-
pipeline_id=pipeline_id,
|
368
|
-
code_repository=code_repository,
|
369
|
-
)
|
370
|
-
|
406
|
+
stack_checksum = compute_stack_checksum(stack=stack_model)
|
371
407
|
build_request = PipelineBuildRequest(
|
372
408
|
user=client.active_user.id,
|
373
409
|
workspace=client.active_workspace.id,
|
374
|
-
stack=
|
410
|
+
stack=stack_model.id,
|
375
411
|
pipeline=pipeline_id,
|
376
412
|
is_local=is_local,
|
377
413
|
contains_code=contains_code,
|
@@ -379,7 +415,7 @@ def create_pipeline_build(
|
|
379
415
|
zenml_version=zenml.__version__,
|
380
416
|
python_version=platform.python_version(),
|
381
417
|
checksum=build_checksum,
|
382
|
-
|
418
|
+
stack_checksum=stack_checksum,
|
383
419
|
)
|
384
420
|
return client.zen_store.create_build(build_request)
|
385
421
|
|
@@ -442,30 +478,30 @@ def verify_local_repository_context(
|
|
442
478
|
deployment, or None if code download is not possible.
|
443
479
|
"""
|
444
480
|
if build_required(deployment=deployment):
|
445
|
-
if deployment
|
481
|
+
if requires_download_from_code_repository(deployment=deployment):
|
446
482
|
if not local_repo_context:
|
447
483
|
raise RuntimeError(
|
448
484
|
"The `DockerSettings` of the pipeline or one of its "
|
449
|
-
"steps specify that code should be
|
450
|
-
"
|
451
|
-
"code repository active at your current source
|
452
|
-
f"`{source_utils.get_source_root()}`."
|
485
|
+
"steps specify that code should be downloaded from a "
|
486
|
+
"code repository, but "
|
487
|
+
"there is no code repository active at your current source "
|
488
|
+
f"root `{source_utils.get_source_root()}`."
|
453
489
|
)
|
454
490
|
elif local_repo_context.is_dirty:
|
455
491
|
raise RuntimeError(
|
456
492
|
"The `DockerSettings` of the pipeline or one of its "
|
457
|
-
"steps specify that code should be
|
458
|
-
"
|
459
|
-
"repository active at your current source root "
|
493
|
+
"steps specify that code should be downloaded from a "
|
494
|
+
"code repository, but "
|
495
|
+
"the code repository active at your current source root "
|
460
496
|
f"`{source_utils.get_source_root()}` has uncommitted "
|
461
497
|
"changes."
|
462
498
|
)
|
463
499
|
elif local_repo_context.has_local_changes:
|
464
500
|
raise RuntimeError(
|
465
501
|
"The `DockerSettings` of the pipeline or one of its "
|
466
|
-
"steps specify that code should be
|
467
|
-
"
|
468
|
-
"repository active at your current source root "
|
502
|
+
"steps specify that code should be downloaded from a "
|
503
|
+
"code repository, but "
|
504
|
+
"the code repository active at your current source root "
|
469
505
|
f"`{source_utils.get_source_root()}` has unpushed "
|
470
506
|
"changes."
|
471
507
|
)
|
@@ -473,13 +509,13 @@ def verify_local_repository_context(
|
|
473
509
|
if local_repo_context:
|
474
510
|
if local_repo_context.is_dirty:
|
475
511
|
logger.warning(
|
476
|
-
"Unable to use code repository to download code for this
|
477
|
-
"as there are uncommitted changes."
|
512
|
+
"Unable to use code repository to download code for this "
|
513
|
+
"run as there are uncommitted changes."
|
478
514
|
)
|
479
515
|
elif local_repo_context.has_local_changes:
|
480
516
|
logger.warning(
|
481
|
-
"Unable to use code repository to download code for this
|
482
|
-
"as there are unpushed changes."
|
517
|
+
"Unable to use code repository to download code for this "
|
518
|
+
"run as there are unpushed changes."
|
483
519
|
)
|
484
520
|
|
485
521
|
code_repository = None
|
@@ -528,13 +564,41 @@ def verify_custom_build(
|
|
528
564
|
"might differ from the local code in your client environment."
|
529
565
|
)
|
530
566
|
|
531
|
-
if build.requires_code_download
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
567
|
+
if build.requires_code_download:
|
568
|
+
if requires_included_code(
|
569
|
+
deployment=deployment, code_repository=code_repository
|
570
|
+
):
|
571
|
+
raise RuntimeError(
|
572
|
+
"The `DockerSettings` of the pipeline or one of its "
|
573
|
+
"steps specify that code should be included in the Docker "
|
574
|
+
"image, but the build you "
|
575
|
+
"specified requires code download. Either update your "
|
576
|
+
"`DockerSettings` or specify a different build and try "
|
577
|
+
"again."
|
578
|
+
)
|
579
|
+
|
580
|
+
if (
|
581
|
+
requires_download_from_code_repository(deployment=deployment)
|
582
|
+
and not code_repository
|
583
|
+
):
|
584
|
+
raise RuntimeError(
|
585
|
+
"The `DockerSettings` of the pipeline or one of its "
|
586
|
+
"steps specify that code should be downloaded from a "
|
587
|
+
"code repository but "
|
588
|
+
"there is no code repository active at your current source "
|
589
|
+
f"root `{source_utils.get_source_root()}`."
|
590
|
+
)
|
591
|
+
|
592
|
+
if not code_download_possible(
|
593
|
+
deployment=deployment, code_repository=code_repository
|
594
|
+
):
|
595
|
+
raise RuntimeError(
|
596
|
+
"The `DockerSettings` of the pipeline or one of its "
|
597
|
+
"steps specify that code can not be downloaded from the "
|
598
|
+
"artifact store, but the build you specified requires code "
|
599
|
+
"download. Either update your `DockerSettings` or specify a "
|
600
|
+
"different build and try again."
|
601
|
+
)
|
538
602
|
|
539
603
|
if build.checksum:
|
540
604
|
build_checksum = compute_build_checksum(
|
@@ -585,3 +649,70 @@ def verify_custom_build(
|
|
585
649
|
"your local machine or the image tags have been "
|
586
650
|
"overwritten since the original build happened."
|
587
651
|
)
|
652
|
+
|
653
|
+
|
654
|
+
def compute_stack_checksum(stack: StackResponse) -> str:
|
655
|
+
"""Compute a stack checksum.
|
656
|
+
|
657
|
+
Args:
|
658
|
+
stack: The stack for which to compute the checksum.
|
659
|
+
|
660
|
+
Returns:
|
661
|
+
The checksum.
|
662
|
+
"""
|
663
|
+
hash_ = hashlib.md5() # nosec
|
664
|
+
|
665
|
+
# This checksum is used to see if the stack has been updated since a build
|
666
|
+
# was created for it. We create this checksum not with specific requirements
|
667
|
+
# as these might change with new ZenML releases, but they don't actually
|
668
|
+
# invalidate those Docker images.
|
669
|
+
required_integrations = sorted(
|
670
|
+
{
|
671
|
+
component.integration
|
672
|
+
for components in stack.components.values()
|
673
|
+
for component in components
|
674
|
+
if component.integration and component.integration != "built-in"
|
675
|
+
}
|
676
|
+
)
|
677
|
+
for integration in required_integrations:
|
678
|
+
hash_.update(integration.encode())
|
679
|
+
|
680
|
+
return hash_.hexdigest()
|
681
|
+
|
682
|
+
|
683
|
+
def should_upload_code(
|
684
|
+
deployment: PipelineDeploymentBase,
|
685
|
+
build: Optional[PipelineBuildResponse],
|
686
|
+
code_reference: Optional[CodeReferenceRequest],
|
687
|
+
) -> bool:
|
688
|
+
"""Checks whether the current code should be uploaded for the deployment.
|
689
|
+
|
690
|
+
Args:
|
691
|
+
deployment: The deployment.
|
692
|
+
build: The build for the deployment.
|
693
|
+
code_reference: The code reference for the deployment.
|
694
|
+
|
695
|
+
Returns:
|
696
|
+
Whether the current code should be uploaded for the deployment.
|
697
|
+
"""
|
698
|
+
if not build:
|
699
|
+
# No build means we don't need to download code into a Docker container
|
700
|
+
# for step execution. In other remote orchestrators that don't use
|
701
|
+
# Docker containers but instead use e.g. Wheels to run, the code should
|
702
|
+
# already be included.
|
703
|
+
return False
|
704
|
+
|
705
|
+
for step in deployment.step_configurations.values():
|
706
|
+
docker_settings = step.config.docker_settings
|
707
|
+
|
708
|
+
if (
|
709
|
+
code_reference
|
710
|
+
and docker_settings.allow_download_from_code_repository
|
711
|
+
):
|
712
|
+
# No upload needed for this step
|
713
|
+
continue
|
714
|
+
|
715
|
+
if docker_settings.allow_download_from_artifact_store:
|
716
|
+
return True
|
717
|
+
|
718
|
+
return False
|
@@ -0,0 +1,157 @@
|
|
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
|
+
"""Code archive."""
|
15
|
+
|
16
|
+
import os
|
17
|
+
from pathlib import Path
|
18
|
+
from typing import IO, TYPE_CHECKING, Dict, Optional
|
19
|
+
|
20
|
+
from zenml.logger import get_logger
|
21
|
+
from zenml.utils import string_utils
|
22
|
+
from zenml.utils.archivable import Archivable
|
23
|
+
|
24
|
+
if TYPE_CHECKING:
|
25
|
+
from git.repo.base import Repo
|
26
|
+
|
27
|
+
|
28
|
+
logger = get_logger(__name__)
|
29
|
+
|
30
|
+
|
31
|
+
class CodeArchive(Archivable):
|
32
|
+
"""Code archive class.
|
33
|
+
|
34
|
+
This class is used to archive user code before uploading it to the artifact
|
35
|
+
store. If the user code is stored in a Git repository, only files not
|
36
|
+
excluded by gitignores will be included in the archive.
|
37
|
+
"""
|
38
|
+
|
39
|
+
def __init__(self, root: str) -> None:
|
40
|
+
"""Initialize the object.
|
41
|
+
|
42
|
+
Args:
|
43
|
+
root: Root directory of the archive.
|
44
|
+
"""
|
45
|
+
super().__init__()
|
46
|
+
self._root = root
|
47
|
+
|
48
|
+
@property
|
49
|
+
def git_repo(self) -> Optional["Repo"]:
|
50
|
+
"""Git repository active at the code archive root.
|
51
|
+
|
52
|
+
Returns:
|
53
|
+
The git repository if available.
|
54
|
+
"""
|
55
|
+
try:
|
56
|
+
# These imports fail when git is not installed on the machine
|
57
|
+
from git.exc import InvalidGitRepositoryError
|
58
|
+
from git.repo.base import Repo
|
59
|
+
except ImportError:
|
60
|
+
return None
|
61
|
+
|
62
|
+
try:
|
63
|
+
git_repo = Repo(path=self._root, search_parent_directories=True)
|
64
|
+
except InvalidGitRepositoryError:
|
65
|
+
return None
|
66
|
+
|
67
|
+
return git_repo
|
68
|
+
|
69
|
+
def _get_all_files(self) -> Dict[str, str]:
|
70
|
+
"""Get all files inside the archive root.
|
71
|
+
|
72
|
+
Returns:
|
73
|
+
All files inside the archive root.
|
74
|
+
"""
|
75
|
+
all_files = {}
|
76
|
+
for root, _, files in os.walk(self._root):
|
77
|
+
for file in files:
|
78
|
+
file_path = os.path.join(root, file)
|
79
|
+
path_in_archive = os.path.relpath(file_path, self._root)
|
80
|
+
all_files[path_in_archive] = file_path
|
81
|
+
|
82
|
+
return all_files
|
83
|
+
|
84
|
+
def get_files(self) -> Dict[str, str]:
|
85
|
+
"""Gets all regular files that should be included in the archive.
|
86
|
+
|
87
|
+
Raises:
|
88
|
+
RuntimeError: If the code archive would not include any files.
|
89
|
+
|
90
|
+
Returns:
|
91
|
+
A dict {path_in_archive: path_on_filesystem} for all regular files
|
92
|
+
in the archive.
|
93
|
+
"""
|
94
|
+
all_files = {}
|
95
|
+
|
96
|
+
if repo := self.git_repo:
|
97
|
+
try:
|
98
|
+
result = repo.git.ls_files(
|
99
|
+
"--cached",
|
100
|
+
"--others",
|
101
|
+
"--modified",
|
102
|
+
"--exclude-standard",
|
103
|
+
self._root,
|
104
|
+
)
|
105
|
+
except Exception as e:
|
106
|
+
logger.warning(
|
107
|
+
"Failed to get non-ignored files from git: %s", str(e)
|
108
|
+
)
|
109
|
+
all_files = self._get_all_files()
|
110
|
+
else:
|
111
|
+
for file in result.split():
|
112
|
+
file_path = os.path.join(repo.working_dir, file)
|
113
|
+
path_in_archive = os.path.relpath(file_path, self._root)
|
114
|
+
|
115
|
+
if os.path.exists(file_path):
|
116
|
+
all_files[path_in_archive] = file_path
|
117
|
+
else:
|
118
|
+
all_files = self._get_all_files()
|
119
|
+
|
120
|
+
if not all_files:
|
121
|
+
raise RuntimeError(
|
122
|
+
"The code archive to be uploaded does not contain any files. "
|
123
|
+
"This is probably because all files in your source root "
|
124
|
+
f"`{self._root}` are ignored by a .gitignore file."
|
125
|
+
)
|
126
|
+
|
127
|
+
# Explicitly remove .zen directories as we write an updated version
|
128
|
+
# to disk everytime ZenML is called. This updates the mtime of the
|
129
|
+
# file, which invalidates the code upload caching. The values in
|
130
|
+
# the .zen directory are not needed anyway as we set them as
|
131
|
+
# environment variables.
|
132
|
+
all_files = {
|
133
|
+
path_in_archive: file_path
|
134
|
+
for path_in_archive, file_path in sorted(all_files.items())
|
135
|
+
if ".zen" not in Path(path_in_archive).parts[:-1]
|
136
|
+
}
|
137
|
+
|
138
|
+
return all_files
|
139
|
+
|
140
|
+
def write_archive(
|
141
|
+
self, output_file: IO[bytes], use_gzip: bool = True
|
142
|
+
) -> None:
|
143
|
+
"""Writes an archive of the build context to the given file.
|
144
|
+
|
145
|
+
Args:
|
146
|
+
output_file: The file to write the archive to.
|
147
|
+
use_gzip: Whether to use `gzip` to compress the file.
|
148
|
+
"""
|
149
|
+
super().write_archive(output_file=output_file, use_gzip=use_gzip)
|
150
|
+
archive_size = os.path.getsize(output_file.name)
|
151
|
+
if archive_size > 20 * 1024 * 1024:
|
152
|
+
logger.warning(
|
153
|
+
"Code archive size: `%s`. If you believe this is "
|
154
|
+
"unreasonably large, make sure to version your code in git and "
|
155
|
+
"ignore unnecessary files using a `.gitignore` file.",
|
156
|
+
string_utils.get_human_readable_filesize(archive_size),
|
157
|
+
)
|