zenml-nightly 0.82.0.dev20250513__py3-none-any.whl → 0.82.1.dev20250517__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 (119) hide show
  1. zenml/VERSION +1 -1
  2. zenml/config/build_configuration.py +9 -0
  3. zenml/config/docker_settings.py +97 -16
  4. zenml/constants.py +1 -0
  5. zenml/integrations/kubernetes/flavors/kubernetes_orchestrator_flavor.py +12 -1
  6. zenml/integrations/kubernetes/orchestrators/kube_utils.py +24 -1
  7. zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py +3 -0
  8. zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator_entrypoint.py +21 -5
  9. zenml/integrations/kubernetes/orchestrators/manifest_utils.py +13 -0
  10. zenml/orchestrators/step_launcher.py +4 -0
  11. zenml/orchestrators/step_run_utils.py +30 -1
  12. zenml/pipelines/build_utils.py +8 -0
  13. zenml/utils/pipeline_docker_image_builder.py +133 -7
  14. zenml/utils/tag_utils.py +3 -3
  15. zenml/zen_server/dashboard/assets/{404-D4aYbspS.js → 404-_AtuLtaX.js} +1 -1
  16. zenml/zen_server/dashboard/assets/{AlertDialogDropdownItem-CRZjthRL.js → AlertDialogDropdownItem-BG7-Ki1L.js} +1 -1
  17. zenml/zen_server/dashboard/assets/{CodeSnippet-Di-loaZu.js → CodeSnippet-CvI6D0wx.js} +1 -1
  18. zenml/zen_server/dashboard/assets/{CollapsibleCard-Dr5zb5T5.js → CollapsibleCard-D20FtrzC.js} +1 -1
  19. zenml/zen_server/dashboard/assets/{Commands-CD9Y7FOE.js → Commands-DGbAvMDk.js} +1 -1
  20. zenml/zen_server/dashboard/assets/{ComponentBadge-BgGnzcLu.js → ComponentBadge-DKw7Gndh.js} +1 -1
  21. zenml/zen_server/dashboard/assets/{ComponentIcon-B_7Y_oV-.js → ComponentIcon-ils7uNAk.js} +1 -1
  22. zenml/zen_server/dashboard/assets/{CsvVizualization-CDrjL6bW.js → CsvVizualization-DVN541XF.js} +1 -1
  23. zenml/zen_server/dashboard/assets/{DeleteAlertDialog-9RTDnEx3.js → DeleteAlertDialog-DVvXt-S6.js} +1 -1
  24. zenml/zen_server/dashboard/assets/{DialogItem-0fWTH7ki.js → DialogItem-BHWf3sIB.js} +1 -1
  25. zenml/zen_server/dashboard/assets/{Error-vBjUYjb-.js → Error-BWaXP0VK.js} +1 -1
  26. zenml/zen_server/dashboard/assets/{ExecutionStatus-B-ysY113.js → ExecutionStatus-XrvT2r65.js} +1 -1
  27. zenml/zen_server/dashboard/assets/{Helpbox-Dt3q7NGd.js → Helpbox-iE1xLmiZ.js} +1 -1
  28. zenml/zen_server/dashboard/assets/{Infobox-Bai0JtFs.js → Infobox-D9k5TFH4.js} +1 -1
  29. zenml/zen_server/dashboard/assets/{InlineAvatar-Cpj0lVRQ.js → InlineAvatar-Cfz4WSLK.js} +1 -1
  30. zenml/zen_server/dashboard/assets/{NestedCollapsible-DbhKdWx-.js → NestedCollapsible-Dor-bi98.js} +1 -1
  31. zenml/zen_server/dashboard/assets/{Partials-BNBtCjuT.js → Partials-DQJFw1yW.js} +1 -1
  32. zenml/zen_server/dashboard/assets/{ProBadge-3vtouJQf.js → ProBadge-Cp4hb1YT.js} +1 -1
  33. zenml/zen_server/dashboard/assets/{ProCta-BoTLdAdV.js → ProCta-EYoV9CvK.js} +1 -1
  34. zenml/zen_server/dashboard/assets/{ProviderIcon-kkODO9tx.js → ProviderIcon-DLo7t1lo.js} +1 -1
  35. zenml/zen_server/dashboard/assets/{ProviderRadio-26EB5tY-.js → ProviderRadio-BVDA-fAr.js} +1 -1
  36. zenml/zen_server/dashboard/assets/{RunSelector-j0C_TPVp.js → RunSelector-BLuBYNJt.js} +1 -1
  37. zenml/zen_server/dashboard/assets/{RunsBody-DexNouV4.js → RunsBody-W4WHf-sq.js} +1 -1
  38. zenml/zen_server/dashboard/assets/{SearchField-DMCywawn.js → SearchField-D-h6jXyg.js} +1 -1
  39. zenml/zen_server/dashboard/assets/{SecretTooltip-LLGP7AIC.js → SecretTooltip-CePCL8kd.js} +1 -1
  40. zenml/zen_server/dashboard/assets/{SetPassword-CBHW-Su8.js → SetPassword-DR-EiLI5.js} +1 -1
  41. zenml/zen_server/dashboard/assets/{StackList-S-9I1R4n.js → StackList-CgmN5H-i.js} +1 -1
  42. zenml/zen_server/dashboard/assets/{Tabs-C2ufJX4t.js → Tabs-DxQ8PDOD.js} +1 -1
  43. zenml/zen_server/dashboard/assets/{Tick-ay3Bgtro.js → Tick-CEsT3HPR.js} +1 -1
  44. zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-p3RX0P-Q.js → UpdatePasswordSchemas-DbFEaezI.js} +1 -1
  45. zenml/zen_server/dashboard/assets/{UsageReason-C8sD2bOU.js → UsageReason-DjI5qMje.js} +1 -1
  46. zenml/zen_server/dashboard/assets/{Wizard-DIllc07n.js → Wizard-CMI6Ksgz.js} +1 -1
  47. zenml/zen_server/dashboard/assets/{WizardFooter-dNDpuero.js → WizardFooter-CFBHFZas.js} +1 -1
  48. zenml/zen_server/dashboard/assets/{all-pipeline-runs-query-BUT1PiTp.js → all-pipeline-runs-query-BGASHYtF.js} +1 -1
  49. zenml/zen_server/dashboard/assets/{configuration-form-DBZSb0FV.js → configuration-form-BtI2Y4eX.js} +1 -1
  50. zenml/zen_server/dashboard/assets/{create-stack-BXLyjE58.js → create-stack-BJ6x5rzj.js} +1 -1
  51. zenml/zen_server/dashboard/assets/{delete-run-DzcYxUnd.js → delete-run-DlSLEl5T.js} +1 -1
  52. zenml/zen_server/dashboard/assets/{flavor-select-BTEwByv6.js → flavor-select-BnPxvQDN.js} +1 -1
  53. zenml/zen_server/dashboard/assets/{form-schemas-BJN_25Ua.js → form-schemas-CbvoEUHr.js} +1 -1
  54. zenml/zen_server/dashboard/assets/{index-wYtmKLnQ.js → index-CFESYpe4.js} +1 -1
  55. zenml/zen_server/dashboard/assets/{index-D9-ukAem.js → index-CmLcvK2z.js} +1 -1
  56. zenml/zen_server/dashboard/assets/{index-8uhUi12k.js → index-CrhdX_qG.js} +1 -1
  57. zenml/zen_server/dashboard/assets/index-CzX3ZYlI.css +1 -0
  58. zenml/zen_server/dashboard/assets/{index-BygJE88j.js → index-D2iSHVZq.js} +7 -7
  59. zenml/zen_server/dashboard/assets/{login-mutation-DywLqguW.js → login-mutation-CXc-Klim.js} +1 -1
  60. zenml/zen_server/dashboard/assets/{not-found-BOoklIG4.js → not-found-olRU3fnu.js} +1 -1
  61. zenml/zen_server/dashboard/assets/{page-ByXQN8A-.js → page-7keIM1V3.js} +1 -1
  62. zenml/zen_server/dashboard/assets/{page-4nVakAEl.js → page-B31neFwG.js} +1 -1
  63. zenml/zen_server/dashboard/assets/{page-xa1EAPcI.js → page-B3zo4KYS.js} +1 -1
  64. zenml/zen_server/dashboard/assets/{page-DfTsntQI.js → page-BN3MHq1a.js} +1 -1
  65. zenml/zen_server/dashboard/assets/{page-BIYqUwTI.js → page-BNgVExjN.js} +1 -1
  66. zenml/zen_server/dashboard/assets/{page-kzSGEVUs.js → page-BPtvu74G.js} +1 -1
  67. zenml/zen_server/dashboard/assets/{page-uDGvpa5g.js → page-BTIuG0ki.js} +1 -1
  68. zenml/zen_server/dashboard/assets/{page--hSXKqyG.js → page-BcQzleH6.js} +1 -1
  69. zenml/zen_server/dashboard/assets/{page-B3DscprQ.js → page-C05Jw4M2.js} +1 -1
  70. zenml/zen_server/dashboard/assets/{page-BE8jdyaM.js → page-C28a7K8h.js} +1 -1
  71. zenml/zen_server/dashboard/assets/{page-B1Uq1S1Q.js → page-C9WLk0X-.js} +1 -1
  72. zenml/zen_server/dashboard/assets/{page-RV8wpZ_4.js → page-CINMx64X.js} +1 -1
  73. zenml/zen_server/dashboard/assets/{page-8AzB83Py.js → page-CSwZxZMQ.js} +1 -1
  74. zenml/zen_server/dashboard/assets/{page-D2TtSfnb.js → page-CYrJbk7P.js} +1 -1
  75. zenml/zen_server/dashboard/assets/{page-EXvws1ss.js → page-Ce0cqLo3.js} +1 -1
  76. zenml/zen_server/dashboard/assets/{page-2BsNK_sZ.js → page-CgNsEkw-.js} +1 -1
  77. zenml/zen_server/dashboard/assets/{page-cOEvFs_l.js → page-Ct2FUYuR.js} +1 -1
  78. zenml/zen_server/dashboard/assets/{page-DmteSSj6.js → page-D8G2B3Bu.js} +1 -1
  79. zenml/zen_server/dashboard/assets/page-DL8a4_lg.js +3 -0
  80. zenml/zen_server/dashboard/assets/{page-Ccnk_5ji.js → page-DMhYn1cF.js} +1 -1
  81. zenml/zen_server/dashboard/assets/{page-CPLlVRXx.js → page-Dd_Yq-Uf.js} +1 -1
  82. zenml/zen_server/dashboard/assets/{page-K9yjbnd4.js → page-DfSvqT8g.js} +1 -1
  83. zenml/zen_server/dashboard/assets/{page-B7Aj2XbV.js → page-Dt6ANUTx.js} +1 -1
  84. zenml/zen_server/dashboard/assets/{page-DHnkTzqs.js → page-DtvTleaT.js} +1 -1
  85. zenml/zen_server/dashboard/assets/{page-uN0n_DE2.js → page-DwfGTiVs.js} +1 -1
  86. zenml/zen_server/dashboard/assets/{page-Ci_n_x3a.js → page-JgomSTDc.js} +1 -1
  87. zenml/zen_server/dashboard/assets/{page-DkY7AIhj.js → page-L84ig6HB.js} +1 -1
  88. zenml/zen_server/dashboard/assets/{page-bUVHi7DE.js → page-Mabsn4QJ.js} +1 -1
  89. zenml/zen_server/dashboard/assets/{page-DP2Ed-2m.js → page-P04L5cm9.js} +1 -1
  90. zenml/zen_server/dashboard/assets/{page-D-ry_oLs.js → page-PfhAnvq4.js} +1 -1
  91. zenml/zen_server/dashboard/assets/{page-CI4a9CXs.js → page-WdRrlNt_.js} +1 -1
  92. zenml/zen_server/dashboard/assets/{page-D_5R6E7b.js → page-ZfTtFicG.js} +1 -1
  93. zenml/zen_server/dashboard/assets/{page-Dt3wHeWX.js → page-cqJDDDeK.js} +1 -1
  94. zenml/zen_server/dashboard/assets/{page-B0r9dBEU.js → page-k-Wxh9L_.js} +1 -1
  95. zenml/zen_server/dashboard/assets/page-y-zV4n0c.js +1 -0
  96. zenml/zen_server/dashboard/assets/{persist-SSTTV5q0.js → persist-D87V82eO.js} +1 -1
  97. zenml/zen_server/dashboard/assets/{persist-CEeDYHW5.js → persist-UUym702q.js} +1 -1
  98. zenml/zen_server/dashboard/assets/{service-BRoAA1Kq.js → service-BQ9KIhls.js} +1 -1
  99. zenml/zen_server/dashboard/assets/{sharedSchema-BACERDat.js → sharedSchema-Bse2agAf.js} +1 -1
  100. zenml/zen_server/dashboard/assets/{stack-detail-query-ihiJr6nq.js → stack-detail-query-BAcZJrN3.js} +1 -1
  101. zenml/zen_server/dashboard/assets/{update-server-settings-mutation-CK6COmw9.js → update-server-settings-mutation-DwMM1LJz.js} +1 -1
  102. zenml/zen_server/dashboard/index.html +3 -3
  103. zenml/zen_server/feature_gate/endpoint_utils.py +9 -12
  104. zenml/zen_server/feature_gate/feature_gate_interface.py +4 -6
  105. zenml/zen_server/feature_gate/zenml_cloud_feature_gate.py +8 -9
  106. zenml/zen_server/rbac/endpoint_utils.py +1 -1
  107. zenml/zen_server/rbac/rbac_sql_zen_store.py +2 -2
  108. zenml/zen_server/routers/run_templates_endpoints.py +10 -1
  109. zenml/zen_server/template_execution/utils.py +12 -1
  110. zenml/zen_stores/migrations/versions/0.82.1_release.py +23 -0
  111. zenml/zen_stores/sql_zen_store.py +3 -6
  112. {zenml_nightly-0.82.0.dev20250513.dist-info → zenml_nightly-0.82.1.dev20250517.dist-info}/METADATA +2 -2
  113. {zenml_nightly-0.82.0.dev20250513.dist-info → zenml_nightly-0.82.1.dev20250517.dist-info}/RECORD +116 -115
  114. zenml/zen_server/dashboard/assets/index-DmTFrHJm.css +0 -1
  115. zenml/zen_server/dashboard/assets/page-BTzhAVEn.js +0 -1
  116. zenml/zen_server/dashboard/assets/page-CSQOpvvK.js +0 -3
  117. {zenml_nightly-0.82.0.dev20250513.dist-info → zenml_nightly-0.82.1.dev20250517.dist-info}/LICENSE +0 -0
  118. {zenml_nightly-0.82.0.dev20250513.dist-info → zenml_nightly-0.82.1.dev20250517.dist-info}/WHEEL +0 -0
  119. {zenml_nightly-0.82.0.dev20250513.dist-info → zenml_nightly-0.82.1.dev20250517.dist-info}/entry_points.txt +0 -0
zenml/VERSION CHANGED
@@ -1 +1 @@
1
- 0.82.0.dev20250513
1
+ 0.82.1.dev20250517
@@ -135,6 +135,9 @@ class BuildConfiguration(BaseModel):
135
135
  Returns:
136
136
  Whether files should be included in the image.
137
137
  """
138
+ if self.settings.local_project_install_command:
139
+ return True
140
+
138
141
  if self.should_download_files(code_repository=code_repository):
139
142
  return False
140
143
 
@@ -153,6 +156,9 @@ class BuildConfiguration(BaseModel):
153
156
  Returns:
154
157
  Whether files should be downloaded in the image.
155
158
  """
159
+ if self.settings.local_project_install_command:
160
+ return False
161
+
156
162
  if self.should_download_files_from_code_repository(
157
163
  code_repository=code_repository
158
164
  ):
@@ -176,6 +182,9 @@ class BuildConfiguration(BaseModel):
176
182
  Returns:
177
183
  Whether files should be downloaded from the code repository.
178
184
  """
185
+ if self.settings.local_project_install_command:
186
+ return False
187
+
179
188
  if (
180
189
  code_repository
181
190
  and self.settings.allow_download_from_code_repository
@@ -91,12 +91,21 @@ class DockerSettings(BaseSettings):
91
91
  --------------------------------
92
92
  Depending on the configuration of this object, requirements will be
93
93
  installed in the following order (each step optional):
94
- - The packages installed in your local python environment
94
+ - The packages installed in your local python environment (extracted using
95
+ `pip freeze`)
95
96
  - The packages required by the stack unless this is disabled by setting
96
- `install_stack_requirements=False`.
97
+ `install_stack_requirements=False`
97
98
  - The packages specified via the `required_integrations`
99
+ - The packages defined inside a pyproject.toml file given by the
100
+ `pyproject_path` attribute.
98
101
  - The packages specified via the `requirements` attribute
99
102
 
103
+ If neither `replicate_local_python_environment`, `pyproject_path` or
104
+ `requirements` are specified, ZenML will try to automatically find a
105
+ requirements.txt or pyproject.toml file in your current source root
106
+ and installs packages from the first one it finds. You can disable this
107
+ behavior by setting `disable_automatic_requirements_detection=True`.
108
+
100
109
  Attributes:
101
110
  parent_image: Full name of the Docker image that should be
102
111
  used as the parent for the image that will be built. Defaults to
@@ -137,10 +146,29 @@ class DockerSettings(BaseSettings):
137
146
  packages.
138
147
  python_package_installer_args: Arguments to pass to the python package
139
148
  installer.
140
- replicate_local_python_environment: If not `None`, ZenML will use the
141
- specified method to generate a requirements file that replicates
142
- the packages installed in the currently running python environment.
143
- This requirements file will then be installed in the Docker image.
149
+ disable_automatic_requirements_detection: If set to True, ZenML will
150
+ not automatically detect requirements.txt files or pyproject.toml
151
+ files in your source root.
152
+ replicate_local_python_environment: If set to True, ZenML will run
153
+ `pip freeze` to gather the requirements of the local Python
154
+ environment and then install them in the Docker image.
155
+ pyproject_path: Path to a pyproject.toml file. If given, the
156
+ dependencies will be exported to a requirements.txt
157
+ formatted file using the `pyproject_export_command` and then
158
+ installed inside the Docker image.
159
+ pyproject_export_command: Command to export the dependencies inside a
160
+ pyproject.toml file to a requirements.txt formatted file. If not
161
+ given and ZenML needs to export the requirements anyway, `uv export`
162
+ and `poetry export` will be tried to see if one of them works. This
163
+ command can contain a `{directory}` placeholder which will be
164
+ replaced with the directory in which the pyproject.toml file is
165
+ stored.
166
+ **Note**: This command will be run before any code files are copied
167
+ into the image. It is therefore not possible to install a local
168
+ project using this command. This command should exclude any local
169
+ projects, and you can specify a `local_project_install_command`
170
+ instead which will be run after the code files are copied into the
171
+ image.
144
172
  requirements: Path to a requirements file or a list of required pip
145
173
  packages. During the image build, these requirements will be
146
174
  installed using pip. If you need to use a different tool to
@@ -149,13 +177,16 @@ class DockerSettings(BaseSettings):
149
177
  required_integrations: List of ZenML integrations that should be
150
178
  installed. All requirements for the specified integrations will
151
179
  be installed inside the Docker image.
152
- required_hub_plugins: DEPRECATED/UNUSED.
153
180
  install_stack_requirements: If `True`, ZenML will automatically detect
154
181
  if components of your active stack are part of a ZenML integration
155
182
  and install the corresponding requirements and apt packages.
156
183
  If you set this to `False` or use custom components in your stack,
157
184
  you need to make sure these get installed by specifying them in
158
185
  the `requirements` and `apt_packages` attributes.
186
+ local_project_install_command: Command to install a local project in
187
+ the Docker image. This is run after the code files are copied into
188
+ the image, and it is therefore only possible when code is included
189
+ in the image, not downloaded at runtime.
159
190
  apt_packages: APT packages to install inside the Docker image.
160
191
  environment: Dictionary of environment variables to set inside the
161
192
  Docker image.
@@ -170,14 +201,6 @@ class DockerSettings(BaseSettings):
170
201
  from a code repository if possible.
171
202
  allow_download_from_artifact_store: If `True`, code can be downloaded
172
203
  from the artifact store.
173
- build_options: DEPRECATED, use parent_image_build_config.build_options
174
- instead.
175
- dockerignore: DEPRECATED, use build_config.dockerignore instead.
176
- copy_files: DEPRECATED/UNUSED.
177
- copy_global_config: DEPRECATED/UNUSED.
178
- source_files: DEPRECATED. Use allow_including_files_in_images,
179
- allow_download_from_code_repository and
180
- allow_download_from_artifact_store instead.
181
204
  """
182
205
 
183
206
  parent_image: Optional[str] = None
@@ -191,14 +214,18 @@ class DockerSettings(BaseSettings):
191
214
  PythonPackageInstaller.PIP
192
215
  )
193
216
  python_package_installer_args: Dict[str, Any] = {}
217
+ disable_automatic_requirements_detection: bool = True
194
218
  replicate_local_python_environment: Optional[
195
- Union[List[str], PythonEnvironmentExportMethod]
219
+ Union[List[str], PythonEnvironmentExportMethod, bool]
196
220
  ] = Field(default=None, union_mode="left_to_right")
221
+ pyproject_path: Optional[str] = None
222
+ pyproject_export_command: Optional[List[str]] = None
197
223
  requirements: Union[None, str, List[str]] = Field(
198
224
  default=None, union_mode="left_to_right"
199
225
  )
200
226
  required_integrations: List[str] = []
201
227
  install_stack_requirements: bool = True
228
+ local_project_install_command: Optional[str] = None
202
229
  apt_packages: List[str] = []
203
230
  environment: Dict[str, Any] = {}
204
231
  user: Optional[str] = None
@@ -221,6 +248,8 @@ class DockerSettings(BaseSettings):
221
248
  "copy_global_config",
222
249
  "source_files",
223
250
  "required_hub_plugins",
251
+ "build_options",
252
+ "dockerignore",
224
253
  )
225
254
 
226
255
  @model_validator(mode="before")
@@ -305,6 +334,58 @@ class DockerSettings(BaseSettings):
305
334
 
306
335
  return self
307
336
 
337
+ @model_validator(mode="after")
338
+ def _validate_code_files_included_if_installing_local_project(
339
+ self,
340
+ ) -> "DockerSettings":
341
+ """Ensures that files are included when installing a local package.
342
+
343
+ Raises:
344
+ ValueError: If files are not included in the Docker image
345
+ when trying to install a local package.
346
+
347
+ Returns:
348
+ The validated settings values.
349
+ """
350
+ if (
351
+ self.local_project_install_command
352
+ and not self.allow_including_files_in_images
353
+ ):
354
+ raise ValueError(
355
+ "Files must be included in the Docker image when trying to "
356
+ "install a local python package. You can do so by setting "
357
+ "the `allow_including_files_in_images` attribute of your "
358
+ "DockerSettings to `True`."
359
+ )
360
+
361
+ return self
362
+
363
+ @model_validator(mode="after")
364
+ def _deprecate_replicate_local_environment_commands(
365
+ self,
366
+ ) -> "DockerSettings":
367
+ """Deprecates some values for `replicate_local_python_environment`.
368
+
369
+ Returns:
370
+ The validated settings values.
371
+ """
372
+ if isinstance(
373
+ self.replicate_local_python_environment,
374
+ (str, list, PythonEnvironmentExportMethod),
375
+ ):
376
+ logger.warning(
377
+ "Specifying a command (`%s`) for "
378
+ "`DockerSettings.replicate_local_python_environment` is "
379
+ "deprecated. If you want to replicate your exact local "
380
+ "environment using `pip freeze`, set "
381
+ "`DockerSettings.replicate_local_python_environment=True`. "
382
+ "If you want to export requirements from a pyproject.toml "
383
+ "file, use `DockerSettings.pyproject_path` and "
384
+ "`DockerSettings.pyproject_export_command` instead."
385
+ )
386
+
387
+ return self
388
+
308
389
  model_config = ConfigDict(
309
390
  # public attributes are immutable
310
391
  frozen=True,
zenml/constants.py CHANGED
@@ -341,6 +341,7 @@ DEFAULT_ZENML_SERVER_REPORT_USER_ACTIVITY_TO_DB_SECONDS = 30
341
341
  DEFAULT_ZENML_SERVER_MAX_REQUEST_BODY_SIZE_IN_BYTES = 256 * 1024 * 1024
342
342
 
343
343
  DEFAULT_REPORTABLE_RESOURCES = ["project", "pipeline", "pipeline_run", "model"]
344
+ RUN_TEMPLATE_TRIGGERS_FEATURE_NAME = "template_run"
344
345
 
345
346
  # API Endpoint paths:
346
347
  ACTIVATE = "/activate"
@@ -15,7 +15,7 @@
15
15
 
16
16
  from typing import TYPE_CHECKING, Optional, Type
17
17
 
18
- from pydantic import PositiveInt
18
+ from pydantic import NonNegativeInt, PositiveInt
19
19
 
20
20
  from zenml.config.base_settings import BaseSettings
21
21
  from zenml.constants import KUBERNETES_CLUSTER_RESOURCE_TYPE
@@ -59,6 +59,14 @@ class KubernetesOrchestratorSettings(BaseSettings):
59
59
  pod_failure_backoff: The backoff factor for pod failure retries and
60
60
  pod startup retries.
61
61
  max_parallelism: Maximum number of steps to run in parallel.
62
+ successful_jobs_history_limit: The number of successful jobs
63
+ to retain. This only applies to jobs created when scheduling a
64
+ pipeline.
65
+ failed_jobs_history_limit: The number of failed jobs to retain.
66
+ This only applies to jobs created when scheduling a pipeline.
67
+ ttl_seconds_after_finished: The amount of seconds to keep finished jobs
68
+ before deleting them. This only applies to jobs created when
69
+ scheduling a pipeline.
62
70
  """
63
71
 
64
72
  synchronous: bool = True
@@ -74,6 +82,9 @@ class KubernetesOrchestratorSettings(BaseSettings):
74
82
  pod_failure_retry_delay: int = 10
75
83
  pod_failure_backoff: float = 1.0
76
84
  max_parallelism: Optional[PositiveInt] = None
85
+ successful_jobs_history_limit: Optional[NonNegativeInt] = None
86
+ failed_jobs_history_limit: Optional[NonNegativeInt] = None
87
+ ttl_seconds_after_finished: Optional[NonNegativeInt] = None
77
88
 
78
89
 
79
90
  class KubernetesOrchestratorConfig(
@@ -34,7 +34,7 @@ Adjusted from https://github.com/tensorflow/tfx/blob/master/tfx/utils/kube_utils
34
34
  import enum
35
35
  import re
36
36
  import time
37
- from typing import Any, Callable, Dict, Optional, TypeVar, cast
37
+ from typing import Any, Callable, Dict, List, Optional, TypeVar, cast
38
38
 
39
39
  from kubernetes import client as k8s_client
40
40
  from kubernetes import config as k8s_config
@@ -554,3 +554,26 @@ def create_and_wait_for_pod_to_start(
554
554
  total_wait += delay
555
555
  time.sleep(delay)
556
556
  delay *= startup_failure_backoff
557
+
558
+
559
+ def get_pod_owner_references(
560
+ core_api: k8s_client.CoreV1Api, pod_name: str, namespace: str
561
+ ) -> List[k8s_client.V1OwnerReference]:
562
+ """Get owner references for a pod.
563
+
564
+ Args:
565
+ core_api: Kubernetes CoreV1Api client.
566
+ pod_name: Name of the pod.
567
+ namespace: Kubernetes namespace.
568
+
569
+ Returns:
570
+ List of owner references.
571
+ """
572
+ pod = get_pod(core_api=core_api, pod_name=pod_name, namespace=namespace)
573
+
574
+ if not pod or not pod.metadata or not pod.metadata.owner_references:
575
+ return []
576
+
577
+ return cast(
578
+ List[k8s_client.V1OwnerReference], pod.metadata.owner_references
579
+ )
@@ -523,6 +523,9 @@ class KubernetesOrchestrator(ContainerizedOrchestrator):
523
523
  pod_settings=orchestrator_pod_settings,
524
524
  env=environment,
525
525
  mount_local_stores=self.config.is_local,
526
+ successful_jobs_history_limit=settings.successful_jobs_history_limit,
527
+ failed_jobs_history_limit=settings.failed_jobs_history_limit,
528
+ ttl_seconds_after_finished=settings.ttl_seconds_after_finished,
526
529
  )
527
530
 
528
531
  self._k8s_batch_api.create_namespaced_cron_job(
@@ -71,7 +71,7 @@ def main() -> None:
71
71
  # Parse / extract args.
72
72
  args = parse_args()
73
73
 
74
- orchestrator_run_id = socket.gethostname()
74
+ orchestrator_pod_name = socket.gethostname()
75
75
 
76
76
  client = Client()
77
77
 
@@ -95,7 +95,22 @@ def main() -> None:
95
95
  core_api = k8s_client.CoreV1Api(kube_client)
96
96
 
97
97
  env = get_config_environment_vars()
98
- env[ENV_ZENML_KUBERNETES_RUN_ID] = orchestrator_run_id
98
+ env[ENV_ZENML_KUBERNETES_RUN_ID] = orchestrator_pod_name
99
+
100
+ try:
101
+ owner_references = kube_utils.get_pod_owner_references(
102
+ core_api=core_api,
103
+ pod_name=orchestrator_pod_name,
104
+ namespace=args.kubernetes_namespace,
105
+ )
106
+ except Exception as e:
107
+ logger.warning(f"Failed to get pod owner references: {str(e)}")
108
+ owner_references = []
109
+ else:
110
+ # Make sure None of the owner references are marked as controllers of
111
+ # the created pod, which messes with the garbage collection logic.
112
+ for owner_reference in owner_references:
113
+ owner_reference.controller = False
99
114
 
100
115
  def run_step_on_kubernetes(step_name: str) -> None:
101
116
  """Run a pipeline step in a separate Kubernetes pod.
@@ -112,7 +127,7 @@ def main() -> None:
112
127
  settings.model_dump() if settings else {}
113
128
  )
114
129
 
115
- if settings.pod_name_prefix and not orchestrator_run_id.startswith(
130
+ if settings.pod_name_prefix and not orchestrator_pod_name.startswith(
116
131
  settings.pod_name_prefix
117
132
  ):
118
133
  max_length = (
@@ -125,7 +140,7 @@ def main() -> None:
125
140
  )
126
141
  pod_name = f"{pod_name_prefix}-{step_name}"
127
142
  else:
128
- pod_name = f"{orchestrator_run_id}-{step_name}"
143
+ pod_name = f"{orchestrator_pod_name}-{step_name}"
129
144
 
130
145
  pod_name = kube_utils.sanitize_pod_name(
131
146
  pod_name, namespace=args.kubernetes_namespace
@@ -179,6 +194,7 @@ def main() -> None:
179
194
  service_account_name=settings.step_pod_service_account_name
180
195
  or settings.service_account_name,
181
196
  mount_local_stores=mount_local_stores,
197
+ owner_references=owner_references,
182
198
  )
183
199
 
184
200
  kube_utils.create_and_wait_for_pod_to_start(
@@ -231,7 +247,7 @@ def main() -> None:
231
247
  else:
232
248
  # For a run triggered by a schedule, we can only use the
233
249
  # orchestrator run ID to find the pipeline run.
234
- list_args = dict(orchestrator_run_id=orchestrator_run_id)
250
+ list_args = dict(orchestrator_run_id=orchestrator_pod_name)
235
251
 
236
252
  pipeline_runs = client.list_pipeline_runs(
237
253
  hydrate=True,
@@ -107,6 +107,7 @@ def build_pod_manifest(
107
107
  service_account_name: Optional[str] = None,
108
108
  env: Optional[Dict[str, str]] = None,
109
109
  mount_local_stores: bool = False,
110
+ owner_references: Optional[List[k8s_client.V1OwnerReference]] = None,
110
111
  ) -> k8s_client.V1Pod:
111
112
  """Build a Kubernetes pod manifest for a ZenML run or step.
112
113
 
@@ -125,6 +126,7 @@ def build_pod_manifest(
125
126
  env: Environment variables to set.
126
127
  mount_local_stores: Whether to mount the local stores path inside the
127
128
  pod.
129
+ owner_references: List of owner references for the pod.
128
130
 
129
131
  Returns:
130
132
  Pod manifest.
@@ -180,6 +182,7 @@ def build_pod_manifest(
180
182
  pod_metadata = k8s_client.V1ObjectMeta(
181
183
  name=pod_name,
182
184
  labels=labels,
185
+ owner_references=owner_references,
183
186
  )
184
187
 
185
188
  if pod_settings and pod_settings.annotations:
@@ -279,6 +282,9 @@ def build_cron_job_manifest(
279
282
  service_account_name: Optional[str] = None,
280
283
  env: Optional[Dict[str, str]] = None,
281
284
  mount_local_stores: bool = False,
285
+ successful_jobs_history_limit: Optional[int] = None,
286
+ failed_jobs_history_limit: Optional[int] = None,
287
+ ttl_seconds_after_finished: Optional[int] = None,
282
288
  ) -> k8s_client.V1CronJob:
283
289
  """Create a manifest for launching a pod as scheduled CRON job.
284
290
 
@@ -298,6 +304,10 @@ def build_cron_job_manifest(
298
304
  env: Environment variables to set.
299
305
  mount_local_stores: Whether to mount the local stores path inside the
300
306
  pod.
307
+ successful_jobs_history_limit: The number of successful jobs to retain.
308
+ failed_jobs_history_limit: The number of failed jobs to retain.
309
+ ttl_seconds_after_finished: The amount of seconds to keep finished jobs
310
+ before deleting them.
301
311
 
302
312
  Returns:
303
313
  CRON job manifest.
@@ -318,6 +328,8 @@ def build_cron_job_manifest(
318
328
 
319
329
  job_spec = k8s_client.V1CronJobSpec(
320
330
  schedule=cron_expression,
331
+ successful_jobs_history_limit=successful_jobs_history_limit,
332
+ failed_jobs_history_limit=failed_jobs_history_limit,
321
333
  job_template=k8s_client.V1JobTemplateSpec(
322
334
  metadata=pod_manifest.metadata,
323
335
  spec=k8s_client.V1JobSpec(
@@ -325,6 +337,7 @@ def build_cron_job_manifest(
325
337
  metadata=pod_manifest.metadata,
326
338
  spec=pod_manifest.spec,
327
339
  ),
340
+ ttl_seconds_after_finished=ttl_seconds_after_finished,
328
341
  ),
329
342
  ),
330
343
  )
@@ -292,6 +292,10 @@ class StepLauncher:
292
292
  artifacts=step_run.outputs,
293
293
  model_version=model_version,
294
294
  )
295
+ step_run_utils.cascade_tags_for_output_artifacts(
296
+ artifacts=step_run.outputs,
297
+ tags=pipeline_run.config.tags,
298
+ )
295
299
 
296
300
  except: # noqa: E722
297
301
  logger.error(f"Pipeline run `{pipeline_run.name}` failed.")
@@ -13,8 +13,9 @@
13
13
  # permissions and limitations under the License.
14
14
  """Utilities for creating step runs."""
15
15
 
16
- from typing import Dict, List, Optional, Set, Tuple
16
+ from typing import Dict, List, Optional, Set, Tuple, Union
17
17
 
18
+ from zenml import Tag, add_tags
18
19
  from zenml.client import Client
19
20
  from zenml.config.step_configurations import Step
20
21
  from zenml.constants import CODE_HASH_PARAMETER_NAME, TEXT_FIELD_MAX_LENGTH
@@ -333,6 +334,11 @@ def create_cached_step_runs(
333
334
  model_version=model_version,
334
335
  )
335
336
 
337
+ cascade_tags_for_output_artifacts(
338
+ artifacts=step_run.outputs,
339
+ tags=pipeline_run.config.tags,
340
+ )
341
+
336
342
  logger.info("Using cached version of step `%s`.", invocation_id)
337
343
  cached_invocations.add(invocation_id)
338
344
 
@@ -382,3 +388,26 @@ def link_output_artifacts_to_model_version(
382
388
  artifact_version=output_artifact,
383
389
  model_version=model_version,
384
390
  )
391
+
392
+
393
+ def cascade_tags_for_output_artifacts(
394
+ artifacts: Dict[str, List[ArtifactVersionResponse]],
395
+ tags: Optional[List[Union[str, Tag]]] = None,
396
+ ) -> None:
397
+ """Tag the outputs of a step run.
398
+
399
+ Args:
400
+ artifacts: The step output artifacts.
401
+ tags: The tags to add to the artifacts.
402
+ """
403
+ if tags is None:
404
+ return
405
+
406
+ cascade_tags = [t for t in tags if isinstance(t, Tag) and t.cascade]
407
+
408
+ for output_artifacts in artifacts.values():
409
+ for output_artifact in output_artifacts:
410
+ add_tags(
411
+ tags=[t.name for t in cascade_tags],
412
+ artifact_version_id=output_artifact.id,
413
+ )
@@ -80,6 +80,11 @@ def requires_included_code(
80
80
  for step in deployment.step_configurations.values():
81
81
  docker_settings = step.config.docker_settings
82
82
 
83
+ if docker_settings.local_project_install_command:
84
+ # When installing a local package, we need to include the code
85
+ # files in the container image.
86
+ return True
87
+
83
88
  if docker_settings.allow_download_from_artifact_store:
84
89
  return False
85
90
 
@@ -136,6 +141,9 @@ def code_download_possible(
136
141
  Whether code download is possible for the deployment.
137
142
  """
138
143
  for step in deployment.step_configurations.values():
144
+ if step.config.docker_settings.local_project_install_command:
145
+ return False
146
+
139
147
  if step.config.docker_settings.allow_download_from_artifact_store:
140
148
  continue
141
149