zenml-nightly 0.70.0.dev20241203__py3-none-any.whl → 0.70.0.dev20241205__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.
- zenml/VERSION +1 -1
- zenml/config/server_config.py +4 -0
- zenml/constants.py +10 -0
- zenml/image_builders/base_image_builder.py +5 -2
- zenml/image_builders/build_context.py +7 -16
- zenml/integrations/aws/__init__.py +3 -0
- zenml/integrations/aws/flavors/__init__.py +6 -0
- zenml/integrations/aws/flavors/aws_image_builder_flavor.py +146 -0
- zenml/integrations/aws/image_builders/__init__.py +20 -0
- zenml/integrations/aws/image_builders/aws_image_builder.py +307 -0
- zenml/integrations/kaniko/image_builders/kaniko_image_builder.py +2 -1
- zenml/integrations/kubernetes/flavors/kubernetes_orchestrator_flavor.py +11 -0
- zenml/integrations/lightning/flavors/lightning_orchestrator_flavor.py +11 -0
- zenml/integrations/skypilot/flavors/skypilot_orchestrator_base_vm_config.py +12 -0
- zenml/materializers/built_in_materializer.py +1 -1
- zenml/orchestrators/base_orchestrator.py +12 -0
- zenml/orchestrators/output_utils.py +5 -1
- zenml/orchestrators/utils.py +55 -27
- zenml/service_connectors/service_connector_utils.py +3 -9
- zenml/stack_deployments/aws_stack_deployment.py +22 -0
- zenml/utils/archivable.py +65 -36
- zenml/utils/code_utils.py +8 -4
- zenml/utils/docker_utils.py +9 -0
- zenml/zen_server/auth.py +7 -2
- zenml/zen_server/dashboard/assets/{404-NVXKFp-x.js → 404-Cqu3EDCm.js} +1 -1
- zenml/zen_server/dashboard/assets/{@reactflow-CK0KJUen.js → @reactflow-D2Y7BWwz.js} +1 -1
- zenml/zen_server/dashboard/assets/{AlertDialogDropdownItem-DezXKmDf.js → AlertDialogDropdownItem-BHd71pVS.js} +1 -1
- zenml/zen_server/dashboard/assets/{CodeSnippet-JzR8CEtw.js → CodeSnippet-DIonwetW.js} +1 -1
- zenml/zen_server/dashboard/assets/{CollapsibleCard-DQW_ktMO.js → CollapsibleCard-CDnC97pB.js} +1 -1
- zenml/zen_server/dashboard/assets/{Commands-DL2kwkRd.js → Commands-BVEXKAOj.js} +1 -1
- zenml/zen_server/dashboard/assets/{ComponentBadge-D_g62Wv8.js → ComponentBadge-CrRvovox.js} +1 -1
- zenml/zen_server/dashboard/assets/{CopyButton-LNcWaa14.js → CopyButton-B6wGAhQv.js} +1 -1
- zenml/zen_server/dashboard/assets/{CsvVizualization-DknpE5ej.js → CsvVizualization-CjcT7LMm.js} +5 -5
- zenml/zen_server/dashboard/assets/DeleteAlertDialog-D2ELtM2W.js +1 -0
- zenml/zen_server/dashboard/assets/{DialogItem-Bxf8FuAT.js → DialogItem-DXIMhBgU.js} +1 -1
- zenml/zen_server/dashboard/assets/{Error-DYflYyps.js → Error-B8uUfTpL.js} +1 -1
- zenml/zen_server/dashboard/assets/{ExecutionStatus-C7zyIQKZ.js → ExecutionStatus-ibAdY-dG.js} +1 -1
- zenml/zen_server/dashboard/assets/{Helpbox-oYSGpLqd.js → Helpbox-BfAfhKHw.js} +1 -1
- zenml/zen_server/dashboard/assets/{Infobox-Cx4xGoXR.js → Infobox-M_SMOu96.js} +1 -1
- zenml/zen_server/dashboard/assets/{InlineAvatar-DiGOWNKF.js → InlineAvatar-DBA0a0-a.js} +1 -1
- zenml/zen_server/dashboard/assets/{NestedCollapsible-DYbgyKxK.js → NestedCollapsible-DpgmEFKw.js} +1 -1
- zenml/zen_server/dashboard/assets/{Partials-03iZf8-N.js → Partials-D_ldD9if.js} +1 -1
- zenml/zen_server/dashboard/assets/{ProBadge-D_EB8HNo.js → ProBadge-DQbfFotM.js} +1 -1
- zenml/zen_server/dashboard/assets/{ProCta-DqNS4v3x.js → ProCta-Bcpb4rcY.js} +1 -1
- zenml/zen_server/dashboard/assets/{ProviderIcon-Bki2aw8w.js → ProviderIcon-BZpgPigN.js} +1 -1
- zenml/zen_server/dashboard/assets/{ProviderRadio-8f43sPD4.js → ProviderRadio-DWPnMuQ1.js} +1 -1
- zenml/zen_server/dashboard/assets/RunSelector-DgRGaAc6.js +1 -0
- zenml/zen_server/dashboard/assets/{RunsBody-07YEO7qI.js → RunsBody-KecfSkjY.js} +1 -1
- zenml/zen_server/dashboard/assets/{SearchField-lp1KgU4e.js → SearchField-n-ILHnaP.js} +1 -1
- zenml/zen_server/dashboard/assets/{SecretTooltip-CgnbyeOx.js → SecretTooltip-B8MrX5yu.js} +1 -1
- zenml/zen_server/dashboard/assets/{SetPassword-CpP418A2.js → SetPassword-B_IVq_wg.js} +1 -1
- zenml/zen_server/dashboard/assets/StackList-TWPBYnkF.js +1 -0
- zenml/zen_server/dashboard/assets/{Tabs-BktHkCJJ.js → Tabs-Rg857zmd.js} +1 -1
- zenml/zen_server/dashboard/assets/{Tick-BlMoIlJT.js → Tick-COg4A-xo.js} +1 -1
- zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-Sc0A0pP-.js → UpdatePasswordSchemas-C6Aj3hm6.js} +1 -1
- zenml/zen_server/dashboard/assets/{UsageReason-YYduL4fj.js → UsageReason-BTLbx7w4.js} +1 -1
- zenml/zen_server/dashboard/assets/{WizardFooter-dgmizSJC.js → WizardFooter-BCAj69Vj.js} +1 -1
- zenml/zen_server/dashboard/assets/{all-pipeline-runs-query-D-c2G6lV.js → all-pipeline-runs-query-DMXkDrV2.js} +1 -1
- zenml/zen_server/dashboard/assets/code-snippets-CqONne41.js +13 -0
- zenml/zen_server/dashboard/assets/{create-stack-DM_JPgef.js → create-stack-HfdbhLs4.js} +1 -1
- zenml/zen_server/dashboard/assets/dates-3pMLCNrD.js +1 -0
- zenml/zen_server/dashboard/assets/delete-run-DZ4hIXff.js +1 -0
- zenml/zen_server/dashboard/assets/{form-schemas-K6FYKjwa.js → form-schemas-B0AVEd9b.js} +1 -1
- zenml/zen_server/dashboard/assets/{index-BAkC7FXi.js → index-DPqSWjug.js} +1 -1
- zenml/zen_server/dashboard/assets/{index-CEV4Cvaf.js → index-DScjfBRb.js} +1 -1
- zenml/zen_server/dashboard/assets/index-DXvT1_Um.css +1 -0
- zenml/zen_server/dashboard/assets/{index-CCOPpudF.js → index-FO-p0GU7.js} +5 -5
- zenml/zen_server/dashboard/assets/{index-B1mVPYxf.js → index-I3bKUGUj.js} +1 -1
- zenml/zen_server/dashboard/assets/key-icon-aH-QIa5R.js +1 -0
- zenml/zen_server/dashboard/assets/login-command-CkqxPtV3.js +1 -0
- zenml/zen_server/dashboard/assets/{login-mutation-hf-lK87O.js → login-mutation-BQeo4wTY.js} +1 -1
- zenml/zen_server/dashboard/assets/{not-found-BGirLjU-.js → not-found-gAJ5aDdR.js} +1 -1
- zenml/zen_server/dashboard/assets/page-9Y9-gig0.js +1 -0
- zenml/zen_server/dashboard/assets/{page-DjRJCGb3.js → page-AUwiQ14W.js} +1 -1
- zenml/zen_server/dashboard/assets/page-B6XU7yYT.js +2 -0
- zenml/zen_server/dashboard/assets/{page-C00YAkaB.js → page-BKZYc2Zv.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-CdMWnQak.js → page-BU9FG4sR.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-D7S3aCbF.js → page-B_Apk3xg.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-Djikxq_S.js → page-BdowiCbr.js} +1 -1
- zenml/zen_server/dashboard/assets/page-Bg8OjTRe.js +1 -0
- zenml/zen_server/dashboard/assets/page-BxL4qD4_.js +1 -0
- zenml/zen_server/dashboard/assets/{page-DakHVWXF.js → page-CWxT5K5J.js} +1 -1
- zenml/zen_server/dashboard/assets/page-CXuQufSe.js +1 -0
- zenml/zen_server/dashboard/assets/{page-DLC-bNBP.js → page-CcQr8CPP.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-CD-DcWoy.js → page-Ce4Hrjnr.js} +1 -1
- zenml/zen_server/dashboard/assets/page-CiYxgZP_.js +1 -0
- zenml/zen_server/dashboard/assets/page-Cldq1mpe.js +1 -0
- zenml/zen_server/dashboard/assets/{page-BDigxVpo.js → page-D4wdonLm.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-D6uU2ax4.js → page-D8ObrbH8.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-DXSTpqRD.js → page-DFuAUGt4.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-CbpvrsDL.js → page-DGazBpuP.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-COXXJj1k.js → page-DO1UcqPX.js} +1 -1
- zenml/zen_server/dashboard/assets/page-DRYXdL5o.js +1 -0
- zenml/zen_server/dashboard/assets/{page-Df-Fw0aq.js → page-DYEquBC2.js} +1 -1
- zenml/zen_server/dashboard/assets/page-Dk32IeZm.js +1 -0
- zenml/zen_server/dashboard/assets/{page-yYC9OI-E.js → page-I3nKFGie.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-6m6yHHlE.js → page-M0w-n6vn.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-Vcxara9U.js → page-R5dx3xGF.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-BR68V0V1.js → page-bT5pOvcB.js} +1 -1
- zenml/zen_server/dashboard/assets/page-hUqK889I.js +6 -0
- zenml/zen_server/dashboard/assets/{page-CjGdWY13.js → page-h_Stveon.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-D01JhjQB.js → page-r8XK5vR7.js} +1 -1
- zenml/zen_server/dashboard/assets/page-u_-ZXBKb.js +1 -0
- zenml/zen_server/dashboard/assets/page-zaMqB_ao.js +1 -0
- zenml/zen_server/dashboard/assets/{persist-GjC8PZoC.js → persist-AppN1B0J.js} +1 -1
- zenml/zen_server/dashboard/assets/{persist-Coz7ZWvz.js → persist-DAUi_3za.js} +1 -1
- zenml/zen_server/dashboard/assets/service-BqqeXLEe.js +2 -0
- zenml/zen_server/dashboard/assets/{sharedSchema-CQb14VSr.js → sharedSchema-uXN9FLLk.js} +1 -1
- zenml/zen_server/dashboard/assets/{stack-detail-query-OPEW-cDJ.js → stack-detail-query-XfZBiBP2.js} +1 -1
- zenml/zen_server/dashboard/assets/{update-server-settings-mutation-LwuQfHYn.js → update-server-settings-mutation-BWmgVJwA.js} +1 -1
- zenml/zen_server/dashboard/assets/{url-CkvKAnwF.js → url-BLwMbzES.js} +1 -1
- zenml/zen_server/dashboard/index.html +4 -4
- zenml/zen_server/routers/auth_endpoints.py +20 -2
- zenml/zen_server/routers/steps_endpoints.py +7 -1
- zenml/zen_server/template_execution/utils.py +3 -1
- zenml/zen_stores/rest_zen_store.py +8 -3
- zenml/zen_stores/schemas/pipeline_run_schemas.py +12 -6
- zenml/zen_stores/sql_zen_store.py +5 -1
- {zenml_nightly-0.70.0.dev20241203.dist-info → zenml_nightly-0.70.0.dev20241205.dist-info}/METADATA +1 -1
- {zenml_nightly-0.70.0.dev20241203.dist-info → zenml_nightly-0.70.0.dev20241205.dist-info}/RECORD +123 -112
- zenml/zen_server/dashboard/assets/RunSelector-DkPiIiNr.js +0 -1
- zenml/zen_server/dashboard/assets/StackList-WvuKQusZ.js +0 -1
- zenml/zen_server/dashboard/assets/delete-run-CJdh1P_h.js +0 -1
- zenml/zen_server/dashboard/assets/index-DlGvJQPn.css +0 -1
- zenml/zen_server/dashboard/assets/page-0JE_-Ec1.js +0 -1
- zenml/zen_server/dashboard/assets/page-BRLpxOt0.js +0 -1
- zenml/zen_server/dashboard/assets/page-BU7huvKw.js +0 -6
- zenml/zen_server/dashboard/assets/page-BvqLv2Ky.js +0 -1
- zenml/zen_server/dashboard/assets/page-CwxrFarU.js +0 -1
- zenml/zen_server/dashboard/assets/page-DfbXf_8s.js +0 -1
- zenml/zen_server/dashboard/assets/page-Dnovpa0i.js +0 -3
- zenml/zen_server/dashboard/assets/page-Dot3LPmL.js +0 -1
- zenml/zen_server/dashboard/assets/page-Xynx4btY.js +0 -14
- zenml/zen_server/dashboard/assets/page-YpKAqVSa.js +0 -1
- {zenml_nightly-0.70.0.dev20241203.dist-info → zenml_nightly-0.70.0.dev20241205.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.70.0.dev20241203.dist-info → zenml_nightly-0.70.0.dev20241205.dist-info}/WHEEL +0 -0
- {zenml_nightly-0.70.0.dev20241203.dist-info → zenml_nightly-0.70.0.dev20241205.dist-info}/entry_points.txt +0 -0
@@ -134,6 +134,17 @@ class KubernetesOrchestratorConfig(
|
|
134
134
|
"""
|
135
135
|
return True
|
136
136
|
|
137
|
+
@property
|
138
|
+
def supports_client_side_caching(self) -> bool:
|
139
|
+
"""Whether the orchestrator supports client side caching.
|
140
|
+
|
141
|
+
Returns:
|
142
|
+
Whether the orchestrator supports client side caching.
|
143
|
+
"""
|
144
|
+
# The Kubernetes orchestrator starts step pods from a pipeline pod.
|
145
|
+
# This is currently not supported when using client-side caching.
|
146
|
+
return False
|
147
|
+
|
137
148
|
|
138
149
|
class KubernetesOrchestratorFlavor(BaseOrchestratorFlavor):
|
139
150
|
"""Kubernetes orchestrator flavor."""
|
@@ -94,6 +94,17 @@ class LightningOrchestratorConfig(
|
|
94
94
|
"""
|
95
95
|
return False
|
96
96
|
|
97
|
+
@property
|
98
|
+
def supports_client_side_caching(self) -> bool:
|
99
|
+
"""Whether the orchestrator supports client side caching.
|
100
|
+
|
101
|
+
Returns:
|
102
|
+
Whether the orchestrator supports client side caching.
|
103
|
+
"""
|
104
|
+
# The Lightning orchestrator starts step studios from a pipeline studio.
|
105
|
+
# This is currently not supported when using client-side caching.
|
106
|
+
return False
|
107
|
+
|
97
108
|
|
98
109
|
class LightningOrchestratorFlavor(BaseOrchestratorFlavor):
|
99
110
|
"""Lightning orchestrator flavor."""
|
@@ -144,3 +144,15 @@ class SkypilotBaseOrchestratorConfig(
|
|
144
144
|
True if this config is for a local component, False otherwise.
|
145
145
|
"""
|
146
146
|
return False
|
147
|
+
|
148
|
+
@property
|
149
|
+
def supports_client_side_caching(self) -> bool:
|
150
|
+
"""Whether the orchestrator supports client side caching.
|
151
|
+
|
152
|
+
Returns:
|
153
|
+
Whether the orchestrator supports client side caching.
|
154
|
+
"""
|
155
|
+
# The Skypilot orchestrator runs the entire pipeline in a single VM, or
|
156
|
+
# starts additional VMs from the root VM. Both of those cases are
|
157
|
+
# currently not supported when using client-side caching.
|
158
|
+
return False
|
@@ -429,7 +429,7 @@ class BuiltInContainerMaterializer(BaseMaterializer):
|
|
429
429
|
# doesn't work for non-serializable types as they
|
430
430
|
# are saved as list of lists in different files
|
431
431
|
if _is_serializable(data):
|
432
|
-
return {self.data_path: VisualizationType.JSON}
|
432
|
+
return {self.data_path.replace("\\", "/"): VisualizationType.JSON}
|
433
433
|
return {}
|
434
434
|
|
435
435
|
def extract_metadata(self, data: Any) -> Dict[str, "MetadataType"]:
|
@@ -84,6 +84,15 @@ class BaseOrchestratorConfig(StackComponentConfig):
|
|
84
84
|
"""
|
85
85
|
return False
|
86
86
|
|
87
|
+
@property
|
88
|
+
def supports_client_side_caching(self) -> bool:
|
89
|
+
"""Whether the orchestrator supports client side caching.
|
90
|
+
|
91
|
+
Returns:
|
92
|
+
Whether the orchestrator supports client side caching.
|
93
|
+
"""
|
94
|
+
return True
|
95
|
+
|
87
96
|
|
88
97
|
class BaseOrchestrator(StackComponent, ABC):
|
89
98
|
"""Base class for all orchestrators.
|
@@ -205,6 +214,7 @@ class BaseOrchestrator(StackComponent, ABC):
|
|
205
214
|
|
206
215
|
if (
|
207
216
|
placeholder_run
|
217
|
+
and self.config.supports_client_side_caching
|
208
218
|
and not deployment.schedule
|
209
219
|
and not prevent_client_side_caching
|
210
220
|
):
|
@@ -232,6 +242,8 @@ class BaseOrchestrator(StackComponent, ABC):
|
|
232
242
|
self._cleanup_run()
|
233
243
|
logger.info("All steps of the pipeline run were cached.")
|
234
244
|
return
|
245
|
+
else:
|
246
|
+
logger.debug("Skipping client-side caching.")
|
235
247
|
|
236
248
|
try:
|
237
249
|
if metadata_iterator := self.prepare_or_run_pipeline(
|
@@ -19,6 +19,7 @@ from uuid import uuid4
|
|
19
19
|
|
20
20
|
from zenml.client import Client
|
21
21
|
from zenml.logger import get_logger
|
22
|
+
from zenml.utils import string_utils
|
22
23
|
|
23
24
|
if TYPE_CHECKING:
|
24
25
|
from zenml.artifact_stores import BaseArtifactStore
|
@@ -75,10 +76,13 @@ def prepare_output_artifact_uris(
|
|
75
76
|
artifact_store = stack.artifact_store
|
76
77
|
output_artifact_uris: Dict[str, str] = {}
|
77
78
|
for output_name in step.config.outputs.keys():
|
79
|
+
substituted_output_name = string_utils.format_name_template(
|
80
|
+
output_name, substitutions=step_run.config.substitutions
|
81
|
+
)
|
78
82
|
artifact_uri = generate_artifact_uri(
|
79
83
|
artifact_store=stack.artifact_store,
|
80
84
|
step_run=step_run,
|
81
|
-
output_name=
|
85
|
+
output_name=substituted_output_name,
|
82
86
|
)
|
83
87
|
if artifact_store.exists(artifact_uri):
|
84
88
|
raise RuntimeError("Artifact already exists")
|
zenml/orchestrators/utils.py
CHANGED
@@ -26,10 +26,12 @@ from zenml.constants import (
|
|
26
26
|
ENV_ZENML_ACTIVE_STACK_ID,
|
27
27
|
ENV_ZENML_ACTIVE_WORKSPACE_ID,
|
28
28
|
ENV_ZENML_DISABLE_CREDENTIALS_DISK_CACHING,
|
29
|
+
ENV_ZENML_PIPELINE_RUN_API_TOKEN_EXPIRATION,
|
29
30
|
ENV_ZENML_SERVER,
|
30
31
|
ENV_ZENML_STORE_PREFIX,
|
32
|
+
ZENML_PIPELINE_RUN_API_TOKEN_EXPIRATION,
|
31
33
|
)
|
32
|
-
from zenml.enums import AuthScheme, StackComponentType, StoreType
|
34
|
+
from zenml.enums import APITokenType, AuthScheme, StackComponentType, StoreType
|
33
35
|
from zenml.logger import get_logger
|
34
36
|
from zenml.stack import StackComponent
|
35
37
|
|
@@ -137,37 +139,63 @@ def get_config_environment_vars(
|
|
137
139
|
url = global_config.store_configuration.url
|
138
140
|
api_token = credentials_store.get_token(url, allow_expired=False)
|
139
141
|
if schedule_id or pipeline_run_id or step_run_id:
|
140
|
-
# When connected to an authenticated ZenML server, if a schedule ID,
|
141
|
-
# pipeline run ID or step run ID is supplied, we need to fetch a new
|
142
|
-
# workload API token scoped to the schedule, pipeline run or step
|
143
|
-
# run.
|
144
142
|
assert isinstance(global_config.zen_store, RestZenStore)
|
145
143
|
|
146
|
-
#
|
147
|
-
#
|
148
|
-
|
149
|
-
if
|
144
|
+
# The user has the option to manually set an expiration for the API
|
145
|
+
# token generated for a pipeline run. In this case, we generate a new
|
146
|
+
# generic API token that will be valid for the indicated duration.
|
147
|
+
if (
|
148
|
+
pipeline_run_id
|
149
|
+
and ZENML_PIPELINE_RUN_API_TOKEN_EXPIRATION != 0
|
150
|
+
):
|
150
151
|
logger.warning(
|
151
|
-
"An API token
|
152
|
-
"
|
153
|
-
"
|
154
|
-
"
|
155
|
-
"
|
156
|
-
"
|
157
|
-
"
|
158
|
-
"
|
159
|
-
"
|
160
|
-
"
|
152
|
+
f"An unscoped API token will be generated for this pipeline "
|
153
|
+
f"run that will expire after "
|
154
|
+
f"{ZENML_PIPELINE_RUN_API_TOKEN_EXPIRATION} "
|
155
|
+
f"seconds instead of being scoped to the pipeline run "
|
156
|
+
f"and not having an expiration time. This is more insecure "
|
157
|
+
f"because the API token will remain valid even after the "
|
158
|
+
f"pipeline run completes its execution. This option has "
|
159
|
+
"been explicitly enabled by setting the "
|
160
|
+
f"{ENV_ZENML_PIPELINE_RUN_API_TOKEN_EXPIRATION} environment "
|
161
|
+
f"variable"
|
162
|
+
)
|
163
|
+
new_api_token = global_config.zen_store.get_api_token(
|
164
|
+
token_type=APITokenType.GENERIC,
|
165
|
+
expires_in=ZENML_PIPELINE_RUN_API_TOKEN_EXPIRATION,
|
161
166
|
)
|
162
167
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
168
|
+
else:
|
169
|
+
# If a schedule ID, pipeline run ID or step run ID is supplied,
|
170
|
+
# we need to fetch a new workload API token scoped to the
|
171
|
+
# schedule, pipeline run or step run.
|
172
|
+
|
173
|
+
# If only a schedule is given, the pipeline run credentials will
|
174
|
+
# be valid for the entire duration of the schedule.
|
175
|
+
api_key = credentials_store.get_api_key(url)
|
176
|
+
if not api_key and not pipeline_run_id and not step_run_id:
|
177
|
+
logger.warning(
|
178
|
+
"An API token without an expiration time will be generated "
|
179
|
+
"and used to run this pipeline on a schedule. This is very "
|
180
|
+
"insecure because the API token will be valid for the "
|
181
|
+
"entire lifetime of the schedule and can be used to access "
|
182
|
+
"your user account if accidentally leaked. When deploying "
|
183
|
+
"a pipeline on a schedule, it is strongly advised to use a "
|
184
|
+
"service account API key to authenticate to the ZenML "
|
185
|
+
"server instead of your regular user account. For more "
|
186
|
+
"information, see "
|
187
|
+
"https://docs.zenml.io/how-to/connecting-to-zenml/connect-with-a-service-account"
|
188
|
+
)
|
189
|
+
|
190
|
+
# The schedule, pipeline run or step run credentials are scoped to
|
191
|
+
# the schedule, pipeline run or step run and will only be valid for
|
192
|
+
# the duration of the schedule/pipeline run/step run.
|
193
|
+
new_api_token = global_config.zen_store.get_api_token(
|
194
|
+
token_type=APITokenType.WORKLOAD,
|
195
|
+
schedule_id=schedule_id,
|
196
|
+
pipeline_run_id=pipeline_run_id,
|
197
|
+
step_run_id=step_run_id,
|
198
|
+
)
|
171
199
|
|
172
200
|
environment_vars[ENV_ZENML_STORE_PREFIX + "API_TOKEN"] = (
|
173
201
|
new_api_token
|
@@ -60,15 +60,9 @@ def _raise_specific_cloud_exception_if_needed(
|
|
60
60
|
orchestrators: List[ResourcesInfo],
|
61
61
|
container_registries: List[ResourcesInfo],
|
62
62
|
) -> None:
|
63
|
-
AWS_DOCS =
|
64
|
-
|
65
|
-
|
66
|
-
GCP_DOCS = (
|
67
|
-
"https://docs.zenml.io/how-to/infrastructure-deployment/auth-management/gcp-service-connector"
|
68
|
-
)
|
69
|
-
AZURE_DOCS = (
|
70
|
-
"https://docs.zenml.io/how-to/infrastructure-deployment/auth-management/azure-service-connector"
|
71
|
-
)
|
63
|
+
AWS_DOCS = "https://docs.zenml.io/how-to/infrastructure-deployment/auth-management/aws-service-connector"
|
64
|
+
GCP_DOCS = "https://docs.zenml.io/how-to/infrastructure-deployment/auth-management/gcp-service-connector"
|
65
|
+
AZURE_DOCS = "https://docs.zenml.io/how-to/infrastructure-deployment/auth-management/azure-service-connector"
|
72
66
|
|
73
67
|
if not artifact_stores:
|
74
68
|
error_msg = (
|
@@ -73,6 +73,7 @@ of any potential costs:
|
|
73
73
|
- An ECR repository registered as a [ZenML container registry](https://docs.zenml.io/stack-components/container-registries/aws).
|
74
74
|
- Sagemaker registered as a [ZenML orchestrator](https://docs.zenml.io/stack-components/orchestrators/sagemaker)
|
75
75
|
as well as a [ZenML step operator](https://docs.zenml.io/stack-components/step-operators/sagemaker).
|
76
|
+
- A CodeBuild project registered as a [ZenML image builder](https://docs.zenml.io/stack-components/image-builder/aws).
|
76
77
|
- An IAM user and IAM role with the minimum necessary permissions to access the
|
77
78
|
above resources.
|
78
79
|
- An AWS access key used to give access to ZenML to connect to the above
|
@@ -158,6 +159,26 @@ console.
|
|
158
159
|
"ecr:PutImage",
|
159
160
|
"ecr:GetAuthorizationToken",
|
160
161
|
],
|
162
|
+
"CloudBuild (Client)": [
|
163
|
+
"codebuild:CreateProject",
|
164
|
+
"codebuild:BatchGetBuilds",
|
165
|
+
],
|
166
|
+
"CloudBuild (Service)": [
|
167
|
+
"s3:GetObject",
|
168
|
+
"s3:GetObjectVersion",
|
169
|
+
"logs:CreateLogGroup",
|
170
|
+
"logs:CreateLogStream",
|
171
|
+
"logs:PutLogEvents",
|
172
|
+
"ecr:BatchGetImage",
|
173
|
+
"ecr:DescribeImages",
|
174
|
+
"ecr:BatchCheckLayerAvailability",
|
175
|
+
"ecr:GetDownloadUrlForLayer",
|
176
|
+
"ecr:InitiateLayerUpload",
|
177
|
+
"ecr:UploadLayerPart",
|
178
|
+
"ecr:CompleteLayerUpload",
|
179
|
+
"ecr:PutImage",
|
180
|
+
"ecr:GetAuthorizationToken",
|
181
|
+
],
|
161
182
|
"SageMaker (Client)": [
|
162
183
|
"sagemaker:CreatePipeline",
|
163
184
|
"sagemaker:StartPipelineExecution",
|
@@ -243,6 +264,7 @@ console.
|
|
243
264
|
param_ResourceName=f"zenml-{random_str(6).lower()}",
|
244
265
|
param_ZenMLServerURL=self.zenml_server_url,
|
245
266
|
param_ZenMLServerAPIToken=self.zenml_server_api_token,
|
267
|
+
param_CodeBuild="true",
|
246
268
|
)
|
247
269
|
# Encode the parameters as URL query parameters
|
248
270
|
query_params = "&".join([f"{k}={v}" for k, v in params.items()])
|
zenml/utils/archivable.py
CHANGED
@@ -15,11 +15,21 @@
|
|
15
15
|
|
16
16
|
import io
|
17
17
|
import tarfile
|
18
|
+
import zipfile
|
18
19
|
from abc import ABC, abstractmethod
|
19
20
|
from pathlib import Path
|
20
|
-
from typing import IO, Any, Dict
|
21
|
+
from typing import IO, Any, Dict, Optional
|
21
22
|
|
22
23
|
from zenml.io import fileio
|
24
|
+
from zenml.utils.enum_utils import StrEnum
|
25
|
+
|
26
|
+
|
27
|
+
class ArchiveType(StrEnum):
|
28
|
+
"""Archive types supported by the ZenML build context."""
|
29
|
+
|
30
|
+
TAR = "tar"
|
31
|
+
TAR_GZ = "tar.gz"
|
32
|
+
ZIP = "zip"
|
23
33
|
|
24
34
|
|
25
35
|
class Archivable(ABC):
|
@@ -81,52 +91,71 @@ class Archivable(ABC):
|
|
81
91
|
self._extra_files[file_destination.as_posix()] = f.read()
|
82
92
|
|
83
93
|
def write_archive(
|
84
|
-
self,
|
94
|
+
self,
|
95
|
+
output_file: IO[bytes],
|
96
|
+
archive_type: ArchiveType = ArchiveType.TAR_GZ,
|
85
97
|
) -> None:
|
86
98
|
"""Writes an archive of the build context to the given file.
|
87
99
|
|
88
100
|
Args:
|
89
101
|
output_file: The file to write the archive to.
|
90
|
-
|
102
|
+
archive_type: The type of archive to create.
|
91
103
|
"""
|
92
104
|
files = self.get_files()
|
93
105
|
extra_files = self.get_extra_files()
|
106
|
+
close_fileobj: Optional[Any] = None
|
107
|
+
fileobj: Any = output_file
|
94
108
|
|
95
|
-
if
|
96
|
-
|
97
|
-
|
98
|
-
# We don't use the builtin gzip functionality of the `tarfile`
|
99
|
-
# library as that one includes the tar filename and creation
|
100
|
-
# timestamp in the archive which causes the hash of the resulting
|
101
|
-
# file to be different each time. We use this hash to avoid
|
102
|
-
# duplicate uploads, which is why we pass empty values for filename
|
103
|
-
# and mtime here.
|
104
|
-
fileobj: Any = GzipFile(
|
105
|
-
filename="", mode="wb", fileobj=output_file, mtime=0.0
|
106
|
-
)
|
109
|
+
if archive_type == ArchiveType.ZIP:
|
110
|
+
fileobj = zipfile.ZipFile(output_file, "w", zipfile.ZIP_DEFLATED)
|
107
111
|
else:
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
112
|
+
if archive_type == ArchiveType.TAR_GZ:
|
113
|
+
from gzip import GzipFile
|
114
|
+
|
115
|
+
# We don't use the builtin gzip functionality of the `tarfile`
|
116
|
+
# library as that one includes the tar filename and creation
|
117
|
+
# timestamp in the archive which causes the hash of the resulting
|
118
|
+
# file to be different each time. We use this hash to avoid
|
119
|
+
# duplicate uploads, which is why we pass empty values for filename
|
120
|
+
# and mtime here.
|
121
|
+
close_fileobj = fileobj = GzipFile(
|
122
|
+
filename="", mode="wb", fileobj=output_file, mtime=0.0
|
123
|
+
)
|
124
|
+
fileobj = tarfile.open(mode="w", fileobj=fileobj)
|
125
|
+
|
126
|
+
try:
|
127
|
+
with fileobj as af:
|
128
|
+
for archive_path, file_path in files.items():
|
129
|
+
if archive_path in extra_files:
|
130
|
+
continue
|
131
|
+
if archive_type == ArchiveType.ZIP:
|
132
|
+
assert isinstance(af, zipfile.ZipFile)
|
133
|
+
af.write(file_path, arcname=archive_path)
|
119
134
|
else:
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
135
|
+
assert isinstance(af, tarfile.TarFile)
|
136
|
+
if info := af.gettarinfo(
|
137
|
+
file_path, arcname=archive_path
|
138
|
+
):
|
139
|
+
if info.isfile():
|
140
|
+
with open(file_path, "rb") as f:
|
141
|
+
af.addfile(info, f)
|
142
|
+
else:
|
143
|
+
af.addfile(info, None)
|
144
|
+
|
145
|
+
for archive_path, contents in extra_files.items():
|
146
|
+
contents_encoded = contents.encode("utf-8")
|
147
|
+
|
148
|
+
if archive_type == ArchiveType.ZIP:
|
149
|
+
assert isinstance(af, zipfile.ZipFile)
|
150
|
+
af.writestr(archive_path, contents_encoded)
|
151
|
+
else:
|
152
|
+
assert isinstance(af, tarfile.TarFile)
|
153
|
+
info = tarfile.TarInfo(archive_path)
|
154
|
+
info.size = len(contents_encoded)
|
155
|
+
af.addfile(info, io.BytesIO(contents_encoded))
|
156
|
+
finally:
|
157
|
+
if close_fileobj:
|
158
|
+
close_fileobj.close()
|
130
159
|
|
131
160
|
output_file.seek(0)
|
132
161
|
|
zenml/utils/code_utils.py
CHANGED
@@ -25,7 +25,7 @@ from zenml.client import Client
|
|
25
25
|
from zenml.io import fileio
|
26
26
|
from zenml.logger import get_logger
|
27
27
|
from zenml.utils import source_utils, string_utils
|
28
|
-
from zenml.utils.archivable import Archivable
|
28
|
+
from zenml.utils.archivable import Archivable, ArchiveType
|
29
29
|
|
30
30
|
if TYPE_CHECKING:
|
31
31
|
from git.repo.base import Repo
|
@@ -152,15 +152,19 @@ class CodeArchive(Archivable):
|
|
152
152
|
return all_files
|
153
153
|
|
154
154
|
def write_archive(
|
155
|
-
self,
|
155
|
+
self,
|
156
|
+
output_file: IO[bytes],
|
157
|
+
archive_type: ArchiveType = ArchiveType.TAR_GZ,
|
156
158
|
) -> None:
|
157
159
|
"""Writes an archive of the build context to the given file.
|
158
160
|
|
159
161
|
Args:
|
160
162
|
output_file: The file to write the archive to.
|
161
|
-
|
163
|
+
archive_type: The type of archive to create.
|
162
164
|
"""
|
163
|
-
super().write_archive(
|
165
|
+
super().write_archive(
|
166
|
+
output_file=output_file, archive_type=archive_type
|
167
|
+
)
|
164
168
|
archive_size = os.path.getsize(output_file.name)
|
165
169
|
if archive_size > 20 * 1024 * 1024:
|
166
170
|
logger.warning(
|
zenml/utils/docker_utils.py
CHANGED
@@ -266,6 +266,14 @@ def push_image(
|
|
266
266
|
logger.info("Finished pushing Docker image.")
|
267
267
|
|
268
268
|
image_name_without_tag, _ = image_name.rsplit(":", maxsplit=1)
|
269
|
+
|
270
|
+
image = docker_client.images.get(image_name)
|
271
|
+
repo_digests: List[str] = image.attrs["RepoDigests"]
|
272
|
+
|
273
|
+
for digest in repo_digests:
|
274
|
+
if digest.startswith(f"{image_name_without_tag}@"):
|
275
|
+
return digest
|
276
|
+
|
269
277
|
for info in reversed(aux_info):
|
270
278
|
try:
|
271
279
|
repo_digest = info["Digest"]
|
@@ -304,6 +312,7 @@ def get_image_digest(image_name: str) -> Optional[str]:
|
|
304
312
|
|
305
313
|
image = docker_client.images.get(image_name)
|
306
314
|
repo_digests = image.attrs["RepoDigests"]
|
315
|
+
|
307
316
|
if len(repo_digests) == 1:
|
308
317
|
return cast(str, repo_digests[0])
|
309
318
|
else:
|
zenml/zen_server/auth.py
CHANGED
@@ -821,7 +821,10 @@ def generate_access_token(
|
|
821
821
|
response: The FastAPI response object.
|
822
822
|
device: The device used for authentication.
|
823
823
|
api_key: The service account API key used for authentication.
|
824
|
-
expires_in: The number of seconds until the token expires.
|
824
|
+
expires_in: The number of seconds until the token expires. If not set,
|
825
|
+
the default value is determined automatically based on the server
|
826
|
+
configuration and type of token. If set to 0, the token will not
|
827
|
+
expire.
|
825
828
|
schedule_id: The ID of the schedule to scope the token to.
|
826
829
|
pipeline_run_id: The ID of the pipeline run to scope the token to.
|
827
830
|
step_run_id: The ID of the step run to scope the token to.
|
@@ -835,7 +838,9 @@ def generate_access_token(
|
|
835
838
|
# according to the values configured in the server config. Device tokens are
|
836
839
|
# handled separately from regular user tokens.
|
837
840
|
expires: Optional[datetime] = None
|
838
|
-
if expires_in:
|
841
|
+
if expires_in == 0:
|
842
|
+
expires_in = None
|
843
|
+
elif expires_in is not None:
|
839
844
|
expires = datetime.utcnow() + timedelta(seconds=expires_in)
|
840
845
|
elif device:
|
841
846
|
# If a device was used for authentication, the token will expire
|
@@ -1 +1 @@
|
|
1
|
-
import{j as e}from"./@radix-DeK6qiuw.js";import{f as s,r as t}from"./index-
|
1
|
+
import{j as e}from"./@radix-DeK6qiuw.js";import{f as s,r as t}from"./index-FO-p0GU7.js";import{E as r}from"./EmptyState-BzdlCwp3.js";import{S as a}from"./help-Cc9bBIJH.js";import{L as o}from"./@react-router-B3Z5rLr2.js";import"./@tanstack-DT5WLu9C.js";import"./@reactflow-D2Y7BWwz.js";function d(){return e.jsx("div",{className:"flex min-h-screen w-full flex-col",children:e.jsx(r,{icon:e.jsx(a,{className:"h-[120px] w-[120px] fill-neutral-300"}),children:e.jsxs("div",{className:"text-center",children:[e.jsx("h1",{className:"mb-2 text-display-xs font-semibold",children:"We can't find the page you are looking for"}),e.jsx("p",{className:"text-lg text-theme-text-secondary",children:"You can try typing a different URL or we can bring you back to your Homepage."}),e.jsx("div",{className:"mt-5 flex justify-center",children:e.jsx(s,{size:"md",asChild:!0,children:e.jsx(o,{className:"w-min self-center whitespace-nowrap",to:t.home,children:e.jsx("span",{className:"px-0.5",children:"Go to Home"})})})})]})})})}export{d as default};
|