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.
Files changed (137) hide show
  1. zenml/VERSION +1 -1
  2. zenml/config/server_config.py +4 -0
  3. zenml/constants.py +10 -0
  4. zenml/image_builders/base_image_builder.py +5 -2
  5. zenml/image_builders/build_context.py +7 -16
  6. zenml/integrations/aws/__init__.py +3 -0
  7. zenml/integrations/aws/flavors/__init__.py +6 -0
  8. zenml/integrations/aws/flavors/aws_image_builder_flavor.py +146 -0
  9. zenml/integrations/aws/image_builders/__init__.py +20 -0
  10. zenml/integrations/aws/image_builders/aws_image_builder.py +307 -0
  11. zenml/integrations/kaniko/image_builders/kaniko_image_builder.py +2 -1
  12. zenml/integrations/kubernetes/flavors/kubernetes_orchestrator_flavor.py +11 -0
  13. zenml/integrations/lightning/flavors/lightning_orchestrator_flavor.py +11 -0
  14. zenml/integrations/skypilot/flavors/skypilot_orchestrator_base_vm_config.py +12 -0
  15. zenml/materializers/built_in_materializer.py +1 -1
  16. zenml/orchestrators/base_orchestrator.py +12 -0
  17. zenml/orchestrators/output_utils.py +5 -1
  18. zenml/orchestrators/utils.py +55 -27
  19. zenml/service_connectors/service_connector_utils.py +3 -9
  20. zenml/stack_deployments/aws_stack_deployment.py +22 -0
  21. zenml/utils/archivable.py +65 -36
  22. zenml/utils/code_utils.py +8 -4
  23. zenml/utils/docker_utils.py +9 -0
  24. zenml/zen_server/auth.py +7 -2
  25. zenml/zen_server/dashboard/assets/{404-NVXKFp-x.js → 404-Cqu3EDCm.js} +1 -1
  26. zenml/zen_server/dashboard/assets/{@reactflow-CK0KJUen.js → @reactflow-D2Y7BWwz.js} +1 -1
  27. zenml/zen_server/dashboard/assets/{AlertDialogDropdownItem-DezXKmDf.js → AlertDialogDropdownItem-BHd71pVS.js} +1 -1
  28. zenml/zen_server/dashboard/assets/{CodeSnippet-JzR8CEtw.js → CodeSnippet-DIonwetW.js} +1 -1
  29. zenml/zen_server/dashboard/assets/{CollapsibleCard-DQW_ktMO.js → CollapsibleCard-CDnC97pB.js} +1 -1
  30. zenml/zen_server/dashboard/assets/{Commands-DL2kwkRd.js → Commands-BVEXKAOj.js} +1 -1
  31. zenml/zen_server/dashboard/assets/{ComponentBadge-D_g62Wv8.js → ComponentBadge-CrRvovox.js} +1 -1
  32. zenml/zen_server/dashboard/assets/{CopyButton-LNcWaa14.js → CopyButton-B6wGAhQv.js} +1 -1
  33. zenml/zen_server/dashboard/assets/{CsvVizualization-DknpE5ej.js → CsvVizualization-CjcT7LMm.js} +5 -5
  34. zenml/zen_server/dashboard/assets/DeleteAlertDialog-D2ELtM2W.js +1 -0
  35. zenml/zen_server/dashboard/assets/{DialogItem-Bxf8FuAT.js → DialogItem-DXIMhBgU.js} +1 -1
  36. zenml/zen_server/dashboard/assets/{Error-DYflYyps.js → Error-B8uUfTpL.js} +1 -1
  37. zenml/zen_server/dashboard/assets/{ExecutionStatus-C7zyIQKZ.js → ExecutionStatus-ibAdY-dG.js} +1 -1
  38. zenml/zen_server/dashboard/assets/{Helpbox-oYSGpLqd.js → Helpbox-BfAfhKHw.js} +1 -1
  39. zenml/zen_server/dashboard/assets/{Infobox-Cx4xGoXR.js → Infobox-M_SMOu96.js} +1 -1
  40. zenml/zen_server/dashboard/assets/{InlineAvatar-DiGOWNKF.js → InlineAvatar-DBA0a0-a.js} +1 -1
  41. zenml/zen_server/dashboard/assets/{NestedCollapsible-DYbgyKxK.js → NestedCollapsible-DpgmEFKw.js} +1 -1
  42. zenml/zen_server/dashboard/assets/{Partials-03iZf8-N.js → Partials-D_ldD9if.js} +1 -1
  43. zenml/zen_server/dashboard/assets/{ProBadge-D_EB8HNo.js → ProBadge-DQbfFotM.js} +1 -1
  44. zenml/zen_server/dashboard/assets/{ProCta-DqNS4v3x.js → ProCta-Bcpb4rcY.js} +1 -1
  45. zenml/zen_server/dashboard/assets/{ProviderIcon-Bki2aw8w.js → ProviderIcon-BZpgPigN.js} +1 -1
  46. zenml/zen_server/dashboard/assets/{ProviderRadio-8f43sPD4.js → ProviderRadio-DWPnMuQ1.js} +1 -1
  47. zenml/zen_server/dashboard/assets/RunSelector-DgRGaAc6.js +1 -0
  48. zenml/zen_server/dashboard/assets/{RunsBody-07YEO7qI.js → RunsBody-KecfSkjY.js} +1 -1
  49. zenml/zen_server/dashboard/assets/{SearchField-lp1KgU4e.js → SearchField-n-ILHnaP.js} +1 -1
  50. zenml/zen_server/dashboard/assets/{SecretTooltip-CgnbyeOx.js → SecretTooltip-B8MrX5yu.js} +1 -1
  51. zenml/zen_server/dashboard/assets/{SetPassword-CpP418A2.js → SetPassword-B_IVq_wg.js} +1 -1
  52. zenml/zen_server/dashboard/assets/StackList-TWPBYnkF.js +1 -0
  53. zenml/zen_server/dashboard/assets/{Tabs-BktHkCJJ.js → Tabs-Rg857zmd.js} +1 -1
  54. zenml/zen_server/dashboard/assets/{Tick-BlMoIlJT.js → Tick-COg4A-xo.js} +1 -1
  55. zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-Sc0A0pP-.js → UpdatePasswordSchemas-C6Aj3hm6.js} +1 -1
  56. zenml/zen_server/dashboard/assets/{UsageReason-YYduL4fj.js → UsageReason-BTLbx7w4.js} +1 -1
  57. zenml/zen_server/dashboard/assets/{WizardFooter-dgmizSJC.js → WizardFooter-BCAj69Vj.js} +1 -1
  58. zenml/zen_server/dashboard/assets/{all-pipeline-runs-query-D-c2G6lV.js → all-pipeline-runs-query-DMXkDrV2.js} +1 -1
  59. zenml/zen_server/dashboard/assets/code-snippets-CqONne41.js +13 -0
  60. zenml/zen_server/dashboard/assets/{create-stack-DM_JPgef.js → create-stack-HfdbhLs4.js} +1 -1
  61. zenml/zen_server/dashboard/assets/dates-3pMLCNrD.js +1 -0
  62. zenml/zen_server/dashboard/assets/delete-run-DZ4hIXff.js +1 -0
  63. zenml/zen_server/dashboard/assets/{form-schemas-K6FYKjwa.js → form-schemas-B0AVEd9b.js} +1 -1
  64. zenml/zen_server/dashboard/assets/{index-BAkC7FXi.js → index-DPqSWjug.js} +1 -1
  65. zenml/zen_server/dashboard/assets/{index-CEV4Cvaf.js → index-DScjfBRb.js} +1 -1
  66. zenml/zen_server/dashboard/assets/index-DXvT1_Um.css +1 -0
  67. zenml/zen_server/dashboard/assets/{index-CCOPpudF.js → index-FO-p0GU7.js} +5 -5
  68. zenml/zen_server/dashboard/assets/{index-B1mVPYxf.js → index-I3bKUGUj.js} +1 -1
  69. zenml/zen_server/dashboard/assets/key-icon-aH-QIa5R.js +1 -0
  70. zenml/zen_server/dashboard/assets/login-command-CkqxPtV3.js +1 -0
  71. zenml/zen_server/dashboard/assets/{login-mutation-hf-lK87O.js → login-mutation-BQeo4wTY.js} +1 -1
  72. zenml/zen_server/dashboard/assets/{not-found-BGirLjU-.js → not-found-gAJ5aDdR.js} +1 -1
  73. zenml/zen_server/dashboard/assets/page-9Y9-gig0.js +1 -0
  74. zenml/zen_server/dashboard/assets/{page-DjRJCGb3.js → page-AUwiQ14W.js} +1 -1
  75. zenml/zen_server/dashboard/assets/page-B6XU7yYT.js +2 -0
  76. zenml/zen_server/dashboard/assets/{page-C00YAkaB.js → page-BKZYc2Zv.js} +1 -1
  77. zenml/zen_server/dashboard/assets/{page-CdMWnQak.js → page-BU9FG4sR.js} +1 -1
  78. zenml/zen_server/dashboard/assets/{page-D7S3aCbF.js → page-B_Apk3xg.js} +1 -1
  79. zenml/zen_server/dashboard/assets/{page-Djikxq_S.js → page-BdowiCbr.js} +1 -1
  80. zenml/zen_server/dashboard/assets/page-Bg8OjTRe.js +1 -0
  81. zenml/zen_server/dashboard/assets/page-BxL4qD4_.js +1 -0
  82. zenml/zen_server/dashboard/assets/{page-DakHVWXF.js → page-CWxT5K5J.js} +1 -1
  83. zenml/zen_server/dashboard/assets/page-CXuQufSe.js +1 -0
  84. zenml/zen_server/dashboard/assets/{page-DLC-bNBP.js → page-CcQr8CPP.js} +1 -1
  85. zenml/zen_server/dashboard/assets/{page-CD-DcWoy.js → page-Ce4Hrjnr.js} +1 -1
  86. zenml/zen_server/dashboard/assets/page-CiYxgZP_.js +1 -0
  87. zenml/zen_server/dashboard/assets/page-Cldq1mpe.js +1 -0
  88. zenml/zen_server/dashboard/assets/{page-BDigxVpo.js → page-D4wdonLm.js} +1 -1
  89. zenml/zen_server/dashboard/assets/{page-D6uU2ax4.js → page-D8ObrbH8.js} +1 -1
  90. zenml/zen_server/dashboard/assets/{page-DXSTpqRD.js → page-DFuAUGt4.js} +1 -1
  91. zenml/zen_server/dashboard/assets/{page-CbpvrsDL.js → page-DGazBpuP.js} +1 -1
  92. zenml/zen_server/dashboard/assets/{page-COXXJj1k.js → page-DO1UcqPX.js} +1 -1
  93. zenml/zen_server/dashboard/assets/page-DRYXdL5o.js +1 -0
  94. zenml/zen_server/dashboard/assets/{page-Df-Fw0aq.js → page-DYEquBC2.js} +1 -1
  95. zenml/zen_server/dashboard/assets/page-Dk32IeZm.js +1 -0
  96. zenml/zen_server/dashboard/assets/{page-yYC9OI-E.js → page-I3nKFGie.js} +1 -1
  97. zenml/zen_server/dashboard/assets/{page-6m6yHHlE.js → page-M0w-n6vn.js} +1 -1
  98. zenml/zen_server/dashboard/assets/{page-Vcxara9U.js → page-R5dx3xGF.js} +1 -1
  99. zenml/zen_server/dashboard/assets/{page-BR68V0V1.js → page-bT5pOvcB.js} +1 -1
  100. zenml/zen_server/dashboard/assets/page-hUqK889I.js +6 -0
  101. zenml/zen_server/dashboard/assets/{page-CjGdWY13.js → page-h_Stveon.js} +1 -1
  102. zenml/zen_server/dashboard/assets/{page-D01JhjQB.js → page-r8XK5vR7.js} +1 -1
  103. zenml/zen_server/dashboard/assets/page-u_-ZXBKb.js +1 -0
  104. zenml/zen_server/dashboard/assets/page-zaMqB_ao.js +1 -0
  105. zenml/zen_server/dashboard/assets/{persist-GjC8PZoC.js → persist-AppN1B0J.js} +1 -1
  106. zenml/zen_server/dashboard/assets/{persist-Coz7ZWvz.js → persist-DAUi_3za.js} +1 -1
  107. zenml/zen_server/dashboard/assets/service-BqqeXLEe.js +2 -0
  108. zenml/zen_server/dashboard/assets/{sharedSchema-CQb14VSr.js → sharedSchema-uXN9FLLk.js} +1 -1
  109. zenml/zen_server/dashboard/assets/{stack-detail-query-OPEW-cDJ.js → stack-detail-query-XfZBiBP2.js} +1 -1
  110. zenml/zen_server/dashboard/assets/{update-server-settings-mutation-LwuQfHYn.js → update-server-settings-mutation-BWmgVJwA.js} +1 -1
  111. zenml/zen_server/dashboard/assets/{url-CkvKAnwF.js → url-BLwMbzES.js} +1 -1
  112. zenml/zen_server/dashboard/index.html +4 -4
  113. zenml/zen_server/routers/auth_endpoints.py +20 -2
  114. zenml/zen_server/routers/steps_endpoints.py +7 -1
  115. zenml/zen_server/template_execution/utils.py +3 -1
  116. zenml/zen_stores/rest_zen_store.py +8 -3
  117. zenml/zen_stores/schemas/pipeline_run_schemas.py +12 -6
  118. zenml/zen_stores/sql_zen_store.py +5 -1
  119. {zenml_nightly-0.70.0.dev20241203.dist-info → zenml_nightly-0.70.0.dev20241205.dist-info}/METADATA +1 -1
  120. {zenml_nightly-0.70.0.dev20241203.dist-info → zenml_nightly-0.70.0.dev20241205.dist-info}/RECORD +123 -112
  121. zenml/zen_server/dashboard/assets/RunSelector-DkPiIiNr.js +0 -1
  122. zenml/zen_server/dashboard/assets/StackList-WvuKQusZ.js +0 -1
  123. zenml/zen_server/dashboard/assets/delete-run-CJdh1P_h.js +0 -1
  124. zenml/zen_server/dashboard/assets/index-DlGvJQPn.css +0 -1
  125. zenml/zen_server/dashboard/assets/page-0JE_-Ec1.js +0 -1
  126. zenml/zen_server/dashboard/assets/page-BRLpxOt0.js +0 -1
  127. zenml/zen_server/dashboard/assets/page-BU7huvKw.js +0 -6
  128. zenml/zen_server/dashboard/assets/page-BvqLv2Ky.js +0 -1
  129. zenml/zen_server/dashboard/assets/page-CwxrFarU.js +0 -1
  130. zenml/zen_server/dashboard/assets/page-DfbXf_8s.js +0 -1
  131. zenml/zen_server/dashboard/assets/page-Dnovpa0i.js +0 -3
  132. zenml/zen_server/dashboard/assets/page-Dot3LPmL.js +0 -1
  133. zenml/zen_server/dashboard/assets/page-Xynx4btY.js +0 -14
  134. zenml/zen_server/dashboard/assets/page-YpKAqVSa.js +0 -1
  135. {zenml_nightly-0.70.0.dev20241203.dist-info → zenml_nightly-0.70.0.dev20241205.dist-info}/LICENSE +0 -0
  136. {zenml_nightly-0.70.0.dev20241203.dist-info → zenml_nightly-0.70.0.dev20241205.dist-info}/WHEEL +0 -0
  137. {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=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")
@@ -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
- # If only a schedule is given, the pipeline run credentials will
147
- # be valid for the entire duration of the schedule.
148
- api_key = credentials_store.get_api_key(url)
149
- if not api_key and not pipeline_run_id and not step_run_id:
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 without an expiration time will be generated "
152
- "and used to run this pipeline on a schedule. This is very "
153
- "insecure because the API token will be valid for the "
154
- "entire lifetime of the schedule and can be used to access "
155
- "your user account if accidentally leaked. When deploying "
156
- "a pipeline on a schedule, it is strongly advised to use a "
157
- "service account API key to authenticate to the ZenML "
158
- "server instead of your regular user account. For more "
159
- "information, see "
160
- "https://docs.zenml.io/how-to/connecting-to-zenml/connect-with-a-service-account"
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
- # The schedule, pipeline run or step run credentials are scoped to
164
- # the schedule, pipeline run or step run and will only be valid for
165
- # the duration of the schedule/pipeline run/step run.
166
- new_api_token = global_config.zen_store.get_api_token(
167
- schedule_id=schedule_id,
168
- pipeline_run_id=pipeline_run_id,
169
- step_run_id=step_run_id,
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
- "https://docs.zenml.io/how-to/infrastructure-deployment/auth-management/aws-service-connector"
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, output_file: IO[bytes], use_gzip: bool = True
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
- use_gzip: Whether to use `gzip` to compress the file.
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 use_gzip:
96
- from gzip import GzipFile
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
- fileobj = output_file
109
-
110
- with tarfile.open(mode="w", fileobj=fileobj) as tf:
111
- for archive_path, file_path in files.items():
112
- if archive_path in extra_files:
113
- continue
114
-
115
- if info := tf.gettarinfo(file_path, arcname=archive_path):
116
- if info.isfile():
117
- with open(file_path, "rb") as f:
118
- tf.addfile(info, f)
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
- tf.addfile(info, None)
121
-
122
- for archive_path, contents in extra_files.items():
123
- info = tarfile.TarInfo(archive_path)
124
- contents_encoded = contents.encode("utf-8")
125
- info.size = len(contents_encoded)
126
- tf.addfile(info, io.BytesIO(contents_encoded))
127
-
128
- if use_gzip:
129
- fileobj.close()
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, output_file: IO[bytes], use_gzip: bool = True
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
- use_gzip: Whether to use `gzip` to compress the file.
163
+ archive_type: The type of archive to create.
162
164
  """
163
- super().write_archive(output_file=output_file, use_gzip=use_gzip)
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(
@@ -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-CCOPpudF.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-CK0KJUen.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};
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};