mlrun 1.6.4rc2__py3-none-any.whl → 1.7.0rc20__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.

Potentially problematic release.


This version of mlrun might be problematic. Click here for more details.

Files changed (291) hide show
  1. mlrun/__init__.py +11 -1
  2. mlrun/__main__.py +26 -112
  3. mlrun/alerts/__init__.py +15 -0
  4. mlrun/alerts/alert.py +144 -0
  5. mlrun/api/schemas/__init__.py +5 -4
  6. mlrun/artifacts/__init__.py +8 -3
  7. mlrun/artifacts/base.py +46 -257
  8. mlrun/artifacts/dataset.py +11 -192
  9. mlrun/artifacts/manager.py +47 -48
  10. mlrun/artifacts/model.py +31 -159
  11. mlrun/artifacts/plots.py +23 -380
  12. mlrun/common/constants.py +69 -0
  13. mlrun/common/db/sql_session.py +2 -3
  14. mlrun/common/formatters/__init__.py +19 -0
  15. mlrun/common/formatters/artifact.py +21 -0
  16. mlrun/common/formatters/base.py +78 -0
  17. mlrun/common/formatters/function.py +41 -0
  18. mlrun/common/formatters/pipeline.py +53 -0
  19. mlrun/common/formatters/project.py +51 -0
  20. mlrun/common/helpers.py +1 -2
  21. mlrun/common/model_monitoring/helpers.py +9 -5
  22. mlrun/{runtimes → common/runtimes}/constants.py +37 -9
  23. mlrun/common/schemas/__init__.py +24 -4
  24. mlrun/common/schemas/alert.py +203 -0
  25. mlrun/common/schemas/api_gateway.py +148 -0
  26. mlrun/common/schemas/artifact.py +18 -8
  27. mlrun/common/schemas/auth.py +11 -5
  28. mlrun/common/schemas/background_task.py +1 -1
  29. mlrun/common/schemas/client_spec.py +4 -1
  30. mlrun/common/schemas/feature_store.py +16 -16
  31. mlrun/common/schemas/frontend_spec.py +8 -7
  32. mlrun/common/schemas/function.py +5 -1
  33. mlrun/common/schemas/hub.py +11 -18
  34. mlrun/common/schemas/memory_reports.py +2 -2
  35. mlrun/common/schemas/model_monitoring/__init__.py +18 -3
  36. mlrun/common/schemas/model_monitoring/constants.py +83 -26
  37. mlrun/common/schemas/model_monitoring/grafana.py +13 -9
  38. mlrun/common/schemas/model_monitoring/model_endpoints.py +99 -16
  39. mlrun/common/schemas/notification.py +4 -4
  40. mlrun/common/schemas/object.py +2 -2
  41. mlrun/{runtimes/mpijob/v1alpha1.py → common/schemas/pagination.py} +10 -13
  42. mlrun/common/schemas/pipeline.py +1 -10
  43. mlrun/common/schemas/project.py +24 -23
  44. mlrun/common/schemas/runtime_resource.py +8 -12
  45. mlrun/common/schemas/schedule.py +3 -3
  46. mlrun/common/schemas/tag.py +1 -2
  47. mlrun/common/schemas/workflow.py +2 -2
  48. mlrun/common/types.py +7 -1
  49. mlrun/config.py +54 -17
  50. mlrun/data_types/to_pandas.py +10 -12
  51. mlrun/datastore/__init__.py +5 -8
  52. mlrun/datastore/alibaba_oss.py +130 -0
  53. mlrun/datastore/azure_blob.py +17 -5
  54. mlrun/datastore/base.py +62 -39
  55. mlrun/datastore/datastore.py +28 -9
  56. mlrun/datastore/datastore_profile.py +146 -20
  57. mlrun/datastore/filestore.py +0 -1
  58. mlrun/datastore/google_cloud_storage.py +6 -2
  59. mlrun/datastore/hdfs.py +56 -0
  60. mlrun/datastore/inmem.py +2 -2
  61. mlrun/datastore/redis.py +6 -2
  62. mlrun/datastore/s3.py +9 -0
  63. mlrun/datastore/snowflake_utils.py +43 -0
  64. mlrun/datastore/sources.py +201 -96
  65. mlrun/datastore/spark_utils.py +1 -2
  66. mlrun/datastore/store_resources.py +7 -7
  67. mlrun/datastore/targets.py +358 -104
  68. mlrun/datastore/utils.py +72 -58
  69. mlrun/datastore/v3io.py +5 -1
  70. mlrun/db/base.py +185 -35
  71. mlrun/db/factory.py +1 -1
  72. mlrun/db/httpdb.py +614 -179
  73. mlrun/db/nopdb.py +210 -26
  74. mlrun/errors.py +12 -1
  75. mlrun/execution.py +41 -24
  76. mlrun/feature_store/__init__.py +0 -2
  77. mlrun/feature_store/api.py +40 -72
  78. mlrun/feature_store/common.py +1 -1
  79. mlrun/feature_store/feature_set.py +76 -55
  80. mlrun/feature_store/feature_vector.py +28 -30
  81. mlrun/feature_store/ingestion.py +7 -6
  82. mlrun/feature_store/retrieval/base.py +16 -11
  83. mlrun/feature_store/retrieval/conversion.py +11 -13
  84. mlrun/feature_store/retrieval/dask_merger.py +2 -0
  85. mlrun/feature_store/retrieval/job.py +9 -3
  86. mlrun/feature_store/retrieval/local_merger.py +2 -0
  87. mlrun/feature_store/retrieval/spark_merger.py +34 -24
  88. mlrun/feature_store/steps.py +37 -34
  89. mlrun/features.py +9 -20
  90. mlrun/frameworks/_common/artifacts_library.py +9 -9
  91. mlrun/frameworks/_common/mlrun_interface.py +5 -5
  92. mlrun/frameworks/_common/model_handler.py +48 -48
  93. mlrun/frameworks/_common/plan.py +2 -3
  94. mlrun/frameworks/_common/producer.py +3 -4
  95. mlrun/frameworks/_common/utils.py +5 -5
  96. mlrun/frameworks/_dl_common/loggers/logger.py +6 -7
  97. mlrun/frameworks/_dl_common/loggers/mlrun_logger.py +9 -9
  98. mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +23 -47
  99. mlrun/frameworks/_ml_common/artifacts_library.py +1 -2
  100. mlrun/frameworks/_ml_common/loggers/logger.py +3 -4
  101. mlrun/frameworks/_ml_common/loggers/mlrun_logger.py +4 -5
  102. mlrun/frameworks/_ml_common/model_handler.py +24 -24
  103. mlrun/frameworks/_ml_common/pkl_model_server.py +2 -2
  104. mlrun/frameworks/_ml_common/plan.py +1 -1
  105. mlrun/frameworks/_ml_common/plans/calibration_curve_plan.py +2 -3
  106. mlrun/frameworks/_ml_common/plans/confusion_matrix_plan.py +2 -3
  107. mlrun/frameworks/_ml_common/plans/dataset_plan.py +3 -3
  108. mlrun/frameworks/_ml_common/plans/feature_importance_plan.py +3 -3
  109. mlrun/frameworks/_ml_common/plans/roc_curve_plan.py +4 -4
  110. mlrun/frameworks/_ml_common/utils.py +4 -4
  111. mlrun/frameworks/auto_mlrun/auto_mlrun.py +9 -9
  112. mlrun/frameworks/huggingface/model_server.py +4 -4
  113. mlrun/frameworks/lgbm/__init__.py +33 -33
  114. mlrun/frameworks/lgbm/callbacks/callback.py +2 -4
  115. mlrun/frameworks/lgbm/callbacks/logging_callback.py +4 -5
  116. mlrun/frameworks/lgbm/callbacks/mlrun_logging_callback.py +4 -5
  117. mlrun/frameworks/lgbm/mlrun_interfaces/booster_mlrun_interface.py +1 -3
  118. mlrun/frameworks/lgbm/mlrun_interfaces/mlrun_interface.py +6 -6
  119. mlrun/frameworks/lgbm/model_handler.py +10 -10
  120. mlrun/frameworks/lgbm/model_server.py +6 -6
  121. mlrun/frameworks/lgbm/utils.py +5 -5
  122. mlrun/frameworks/onnx/dataset.py +8 -8
  123. mlrun/frameworks/onnx/mlrun_interface.py +3 -3
  124. mlrun/frameworks/onnx/model_handler.py +6 -6
  125. mlrun/frameworks/onnx/model_server.py +7 -7
  126. mlrun/frameworks/parallel_coordinates.py +4 -3
  127. mlrun/frameworks/pytorch/__init__.py +18 -18
  128. mlrun/frameworks/pytorch/callbacks/callback.py +4 -5
  129. mlrun/frameworks/pytorch/callbacks/logging_callback.py +17 -17
  130. mlrun/frameworks/pytorch/callbacks/mlrun_logging_callback.py +11 -11
  131. mlrun/frameworks/pytorch/callbacks/tensorboard_logging_callback.py +23 -29
  132. mlrun/frameworks/pytorch/callbacks_handler.py +38 -38
  133. mlrun/frameworks/pytorch/mlrun_interface.py +20 -20
  134. mlrun/frameworks/pytorch/model_handler.py +17 -17
  135. mlrun/frameworks/pytorch/model_server.py +7 -7
  136. mlrun/frameworks/sklearn/__init__.py +13 -13
  137. mlrun/frameworks/sklearn/estimator.py +4 -4
  138. mlrun/frameworks/sklearn/metrics_library.py +14 -14
  139. mlrun/frameworks/sklearn/mlrun_interface.py +3 -6
  140. mlrun/frameworks/sklearn/model_handler.py +2 -2
  141. mlrun/frameworks/tf_keras/__init__.py +10 -7
  142. mlrun/frameworks/tf_keras/callbacks/logging_callback.py +15 -15
  143. mlrun/frameworks/tf_keras/callbacks/mlrun_logging_callback.py +11 -11
  144. mlrun/frameworks/tf_keras/callbacks/tensorboard_logging_callback.py +19 -23
  145. mlrun/frameworks/tf_keras/mlrun_interface.py +9 -11
  146. mlrun/frameworks/tf_keras/model_handler.py +14 -14
  147. mlrun/frameworks/tf_keras/model_server.py +6 -6
  148. mlrun/frameworks/xgboost/__init__.py +13 -13
  149. mlrun/frameworks/xgboost/model_handler.py +6 -6
  150. mlrun/k8s_utils.py +14 -16
  151. mlrun/launcher/__init__.py +1 -1
  152. mlrun/launcher/base.py +16 -15
  153. mlrun/launcher/client.py +8 -6
  154. mlrun/launcher/factory.py +1 -1
  155. mlrun/launcher/local.py +17 -11
  156. mlrun/launcher/remote.py +16 -10
  157. mlrun/lists.py +7 -6
  158. mlrun/model.py +238 -73
  159. mlrun/model_monitoring/__init__.py +1 -1
  160. mlrun/model_monitoring/api.py +138 -315
  161. mlrun/model_monitoring/application.py +5 -296
  162. mlrun/model_monitoring/applications/__init__.py +24 -0
  163. mlrun/model_monitoring/applications/_application_steps.py +157 -0
  164. mlrun/model_monitoring/applications/base.py +282 -0
  165. mlrun/model_monitoring/applications/context.py +214 -0
  166. mlrun/model_monitoring/applications/evidently_base.py +211 -0
  167. mlrun/model_monitoring/applications/histogram_data_drift.py +349 -0
  168. mlrun/model_monitoring/applications/results.py +99 -0
  169. mlrun/model_monitoring/controller.py +104 -84
  170. mlrun/model_monitoring/controller_handler.py +13 -5
  171. mlrun/model_monitoring/db/__init__.py +18 -0
  172. mlrun/model_monitoring/{stores → db/stores}/__init__.py +43 -36
  173. mlrun/model_monitoring/db/stores/base/__init__.py +15 -0
  174. mlrun/model_monitoring/{stores/model_endpoint_store.py → db/stores/base/store.py} +64 -40
  175. mlrun/model_monitoring/db/stores/sqldb/__init__.py +13 -0
  176. mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +71 -0
  177. mlrun/model_monitoring/{stores → db/stores/sqldb}/models/base.py +109 -5
  178. mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +88 -0
  179. mlrun/model_monitoring/{stores/models/mysql.py → db/stores/sqldb/models/sqlite.py} +19 -13
  180. mlrun/model_monitoring/db/stores/sqldb/sql_store.py +684 -0
  181. mlrun/model_monitoring/db/stores/v3io_kv/__init__.py +13 -0
  182. mlrun/model_monitoring/{stores/kv_model_endpoint_store.py → db/stores/v3io_kv/kv_store.py} +310 -165
  183. mlrun/model_monitoring/db/tsdb/__init__.py +100 -0
  184. mlrun/model_monitoring/db/tsdb/base.py +329 -0
  185. mlrun/model_monitoring/db/tsdb/helpers.py +30 -0
  186. mlrun/model_monitoring/db/tsdb/tdengine/__init__.py +15 -0
  187. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +240 -0
  188. mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +45 -0
  189. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +397 -0
  190. mlrun/model_monitoring/db/tsdb/v3io/__init__.py +15 -0
  191. mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +117 -0
  192. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +630 -0
  193. mlrun/model_monitoring/evidently_application.py +6 -118
  194. mlrun/model_monitoring/features_drift_table.py +134 -106
  195. mlrun/model_monitoring/helpers.py +127 -28
  196. mlrun/model_monitoring/metrics/__init__.py +13 -0
  197. mlrun/model_monitoring/metrics/histogram_distance.py +127 -0
  198. mlrun/model_monitoring/model_endpoint.py +3 -2
  199. mlrun/model_monitoring/prometheus.py +1 -4
  200. mlrun/model_monitoring/stream_processing.py +62 -231
  201. mlrun/model_monitoring/tracking_policy.py +9 -2
  202. mlrun/model_monitoring/writer.py +152 -124
  203. mlrun/package/__init__.py +6 -6
  204. mlrun/package/context_handler.py +5 -5
  205. mlrun/package/packager.py +7 -7
  206. mlrun/package/packagers/default_packager.py +6 -6
  207. mlrun/package/packagers/numpy_packagers.py +15 -15
  208. mlrun/package/packagers/pandas_packagers.py +5 -5
  209. mlrun/package/packagers/python_standard_library_packagers.py +10 -10
  210. mlrun/package/packagers_manager.py +19 -23
  211. mlrun/package/utils/_formatter.py +6 -6
  212. mlrun/package/utils/_pickler.py +2 -2
  213. mlrun/package/utils/_supported_format.py +4 -4
  214. mlrun/package/utils/log_hint_utils.py +2 -2
  215. mlrun/package/utils/type_hint_utils.py +4 -9
  216. mlrun/platforms/__init__.py +11 -10
  217. mlrun/platforms/iguazio.py +24 -203
  218. mlrun/projects/operations.py +35 -21
  219. mlrun/projects/pipelines.py +68 -99
  220. mlrun/projects/project.py +830 -266
  221. mlrun/render.py +3 -11
  222. mlrun/run.py +162 -166
  223. mlrun/runtimes/__init__.py +62 -7
  224. mlrun/runtimes/base.py +39 -32
  225. mlrun/runtimes/daskjob.py +8 -8
  226. mlrun/runtimes/databricks_job/databricks_cancel_task.py +1 -1
  227. mlrun/runtimes/databricks_job/databricks_runtime.py +7 -7
  228. mlrun/runtimes/databricks_job/databricks_wrapper.py +1 -1
  229. mlrun/runtimes/funcdoc.py +0 -28
  230. mlrun/runtimes/function_reference.py +1 -1
  231. mlrun/runtimes/kubejob.py +28 -122
  232. mlrun/runtimes/local.py +6 -3
  233. mlrun/runtimes/mpijob/__init__.py +0 -20
  234. mlrun/runtimes/mpijob/abstract.py +9 -10
  235. mlrun/runtimes/mpijob/v1.py +1 -1
  236. mlrun/{model_monitoring/stores/models/sqlite.py → runtimes/nuclio/__init__.py} +7 -9
  237. mlrun/runtimes/nuclio/api_gateway.py +709 -0
  238. mlrun/runtimes/nuclio/application/__init__.py +15 -0
  239. mlrun/runtimes/nuclio/application/application.py +523 -0
  240. mlrun/runtimes/nuclio/application/reverse_proxy.go +95 -0
  241. mlrun/runtimes/{function.py → nuclio/function.py} +112 -73
  242. mlrun/runtimes/{nuclio.py → nuclio/nuclio.py} +6 -6
  243. mlrun/runtimes/{serving.py → nuclio/serving.py} +45 -51
  244. mlrun/runtimes/pod.py +286 -88
  245. mlrun/runtimes/remotesparkjob.py +2 -2
  246. mlrun/runtimes/sparkjob/spark3job.py +51 -34
  247. mlrun/runtimes/utils.py +7 -75
  248. mlrun/secrets.py +9 -5
  249. mlrun/serving/remote.py +2 -7
  250. mlrun/serving/routers.py +13 -10
  251. mlrun/serving/server.py +22 -26
  252. mlrun/serving/states.py +99 -25
  253. mlrun/serving/utils.py +3 -3
  254. mlrun/serving/v1_serving.py +6 -7
  255. mlrun/serving/v2_serving.py +59 -20
  256. mlrun/track/tracker.py +2 -1
  257. mlrun/track/tracker_manager.py +3 -3
  258. mlrun/track/trackers/mlflow_tracker.py +1 -2
  259. mlrun/utils/async_http.py +5 -7
  260. mlrun/utils/azure_vault.py +1 -1
  261. mlrun/utils/clones.py +1 -2
  262. mlrun/utils/condition_evaluator.py +3 -3
  263. mlrun/utils/db.py +3 -3
  264. mlrun/utils/helpers.py +183 -197
  265. mlrun/utils/http.py +2 -5
  266. mlrun/utils/logger.py +76 -14
  267. mlrun/utils/notifications/notification/__init__.py +17 -12
  268. mlrun/utils/notifications/notification/base.py +14 -2
  269. mlrun/utils/notifications/notification/console.py +2 -0
  270. mlrun/utils/notifications/notification/git.py +3 -1
  271. mlrun/utils/notifications/notification/ipython.py +3 -1
  272. mlrun/utils/notifications/notification/slack.py +101 -21
  273. mlrun/utils/notifications/notification/webhook.py +11 -1
  274. mlrun/utils/notifications/notification_pusher.py +155 -30
  275. mlrun/utils/retryer.py +208 -0
  276. mlrun/utils/singleton.py +1 -1
  277. mlrun/utils/v3io_clients.py +2 -4
  278. mlrun/utils/version/version.json +2 -2
  279. mlrun/utils/version/version.py +2 -6
  280. {mlrun-1.6.4rc2.dist-info → mlrun-1.7.0rc20.dist-info}/METADATA +31 -19
  281. mlrun-1.7.0rc20.dist-info/RECORD +353 -0
  282. mlrun/kfpops.py +0 -868
  283. mlrun/model_monitoring/batch.py +0 -1095
  284. mlrun/model_monitoring/stores/models/__init__.py +0 -27
  285. mlrun/model_monitoring/stores/sql_model_endpoint_store.py +0 -384
  286. mlrun/platforms/other.py +0 -306
  287. mlrun-1.6.4rc2.dist-info/RECORD +0 -314
  288. {mlrun-1.6.4rc2.dist-info → mlrun-1.7.0rc20.dist-info}/LICENSE +0 -0
  289. {mlrun-1.6.4rc2.dist-info → mlrun-1.7.0rc20.dist-info}/WHEEL +0 -0
  290. {mlrun-1.6.4rc2.dist-info → mlrun-1.7.0rc20.dist-info}/entry_points.txt +0 -0
  291. {mlrun-1.6.4rc2.dist-info → mlrun-1.7.0rc20.dist-info}/top_level.txt +0 -0
mlrun/runtimes/pod.py CHANGED
@@ -15,12 +15,14 @@ import copy
15
15
  import inspect
16
16
  import os
17
17
  import re
18
+ import time
18
19
  import typing
19
20
  from enum import Enum
20
21
 
21
22
  import dotenv
22
- import kfp.dsl
23
23
  import kubernetes.client as k8s_client
24
+ import mlrun_pipelines.mounts
25
+ from mlrun_pipelines.mixins import KfpAdapterMixin
24
26
 
25
27
  import mlrun.errors
26
28
  import mlrun.utils.regex
@@ -40,7 +42,6 @@ from ..k8s_utils import (
40
42
  from ..utils import logger, update_in
41
43
  from .base import BaseRuntime, FunctionSpec, spec_fields
42
44
  from .utils import (
43
- apply_kfp,
44
45
  get_gpu_from_resource_requirement,
45
46
  get_item_name,
46
47
  set_named_item,
@@ -105,6 +106,50 @@ class KubeResourceSpec(FunctionSpec):
105
106
  "security_context",
106
107
  "state_thresholds",
107
108
  ]
109
+ _default_fields_to_strip = FunctionSpec._default_fields_to_strip + [
110
+ "volumes",
111
+ "volume_mounts",
112
+ "resources",
113
+ "replicas",
114
+ "image_pull_policy",
115
+ "service_account",
116
+ "image_pull_secret",
117
+ "node_name",
118
+ "node_selector",
119
+ "affinity",
120
+ "priority_class_name",
121
+ "tolerations",
122
+ "preemption_mode",
123
+ "security_context",
124
+ ]
125
+ _k8s_fields_to_serialize = [
126
+ "volumes",
127
+ "volume_mounts",
128
+ "resources",
129
+ "env",
130
+ "image_pull_policy",
131
+ "service_account",
132
+ "image_pull_secret",
133
+ "node_name",
134
+ "node_selector",
135
+ "affinity",
136
+ "tolerations",
137
+ "security_context",
138
+ ]
139
+ _fields_to_serialize = FunctionSpec._fields_to_serialize + _k8s_fields_to_serialize
140
+ _fields_to_enrich = FunctionSpec._fields_to_enrich + [
141
+ "env", # Removing sensitive data from env
142
+ ]
143
+ _fields_to_skip_validation = FunctionSpec._fields_to_skip_validation + [
144
+ # TODO: affinity, tolerations and node_selector are skipped due to preemption mode transitions.
145
+ # Preemption mode 'none' depends on the previous mode while the default mode may enrich these values.
146
+ # When we allow 'None' values for these attributes we get their true values and they will undo the default
147
+ # enrichment when creating the runtime from dict.
148
+ # The enrichment should move to the server side and then this can be removed.
149
+ "affinity",
150
+ "tolerations",
151
+ "node_selector",
152
+ ]
108
153
 
109
154
  def __init__(
110
155
  self,
@@ -222,7 +267,7 @@ class KubeResourceSpec(FunctionSpec):
222
267
  self._affinity = transform_attribute_to_k8s_class_instance("affinity", affinity)
223
268
 
224
269
  @property
225
- def tolerations(self) -> typing.List[k8s_client.V1Toleration]:
270
+ def tolerations(self) -> list[k8s_client.V1Toleration]:
226
271
  return self._tolerations
227
272
 
228
273
  @tolerations.setter
@@ -264,15 +309,42 @@ class KubeResourceSpec(FunctionSpec):
264
309
  def termination_grace_period_seconds(self) -> typing.Optional[int]:
265
310
  return self._termination_grace_period_seconds
266
311
 
267
- def to_dict(self, fields=None, exclude=None):
268
- exclude = exclude or []
269
- _exclude = ["affinity", "tolerations", "security_context"]
270
- struct = super().to_dict(fields, exclude=list(set(exclude + _exclude)))
271
- api = k8s_client.ApiClient()
272
- for field in _exclude:
273
- if field not in exclude:
274
- struct[field] = api.sanitize_for_serialization(getattr(self, field))
275
- return struct
312
+ def _serialize_field(
313
+ self, struct: dict, field_name: str = None, strip: bool = False
314
+ ) -> typing.Any:
315
+ """
316
+ Serialize a field to a dict, list, or primitive type.
317
+ If field_name is in _k8s_fields_to_serialize, we will apply k8s serialization
318
+ """
319
+ k8s_api = k8s_client.ApiClient()
320
+ if field_name in self._k8s_fields_to_serialize:
321
+ return k8s_api.sanitize_for_serialization(getattr(self, field_name))
322
+ return super()._serialize_field(struct, field_name, strip)
323
+
324
+ def _enrich_field(
325
+ self, struct: dict, field_name: str = None, strip: bool = False
326
+ ) -> typing.Any:
327
+ k8s_api = k8s_client.ApiClient()
328
+ if strip:
329
+ if field_name == "env":
330
+ # We first try to pull from struct because the field might have been already serialized and if not,
331
+ # we pull from self
332
+ envs = struct.get(field_name, None) or getattr(self, field_name, None)
333
+ if envs:
334
+ serialized_envs = k8s_api.sanitize_for_serialization(envs)
335
+ for env in serialized_envs:
336
+ if env["name"].startswith("V3IO_"):
337
+ env["value"] = ""
338
+ return serialized_envs
339
+ return super()._enrich_field(struct=struct, field_name=field_name, strip=strip)
340
+
341
+ def _apply_enrichment_before_to_dict_completion(
342
+ self, struct: dict, strip: bool = False
343
+ ):
344
+ if strip:
345
+ # Reset this, since mounts and env variables were cleared.
346
+ struct["disable_auto_mount"] = False
347
+ return super()._apply_enrichment_before_to_dict_completion(struct, strip)
276
348
 
277
349
  def update_vols_and_mounts(
278
350
  self, volumes, volume_mounts, volume_mounts_field_name="_volume_mounts"
@@ -455,7 +527,7 @@ class KubeResourceSpec(FunctionSpec):
455
527
  return {}
456
528
  return resources
457
529
 
458
- def _merge_node_selector(self, node_selector: typing.Dict[str, str]):
530
+ def _merge_node_selector(self, node_selector: dict[str, str]):
459
531
  if not node_selector:
460
532
  return
461
533
 
@@ -464,7 +536,7 @@ class KubeResourceSpec(FunctionSpec):
464
536
 
465
537
  def _merge_tolerations(
466
538
  self,
467
- tolerations: typing.List[k8s_client.V1Toleration],
539
+ tolerations: list[k8s_client.V1Toleration],
468
540
  tolerations_field_name: str,
469
541
  ):
470
542
  if not tolerations:
@@ -649,7 +721,7 @@ class KubeResourceSpec(FunctionSpec):
649
721
 
650
722
  def _merge_node_selector_term_to_node_affinity(
651
723
  self,
652
- node_selector_terms: typing.List[k8s_client.V1NodeSelectorTerm],
724
+ node_selector_terms: list[k8s_client.V1NodeSelectorTerm],
653
725
  affinity_field_name: str,
654
726
  ):
655
727
  if not node_selector_terms:
@@ -694,7 +766,7 @@ class KubeResourceSpec(FunctionSpec):
694
766
 
695
767
  def _prune_affinity_node_selector_requirement(
696
768
  self,
697
- node_selector_requirements: typing.List[k8s_client.V1NodeSelectorRequirement],
769
+ node_selector_requirements: list[k8s_client.V1NodeSelectorRequirement],
698
770
  affinity_field_name: str = "affinity",
699
771
  ):
700
772
  """
@@ -749,20 +821,18 @@ class KubeResourceSpec(FunctionSpec):
749
821
 
750
822
  @staticmethod
751
823
  def _prune_node_selector_requirements_from_node_selector_terms(
752
- node_selector_terms: typing.List[k8s_client.V1NodeSelectorTerm],
753
- node_selector_requirements_to_prune: typing.List[
754
- k8s_client.V1NodeSelectorRequirement
755
- ],
756
- ) -> typing.List[k8s_client.V1NodeSelectorTerm]:
824
+ node_selector_terms: list[k8s_client.V1NodeSelectorTerm],
825
+ node_selector_requirements_to_prune: list[k8s_client.V1NodeSelectorRequirement],
826
+ ) -> list[k8s_client.V1NodeSelectorTerm]:
757
827
  """
758
828
  Goes over each expression in all the terms provided and removes the expressions if it matches
759
829
  one of the requirements provided to remove
760
830
 
761
831
  :return: New list of terms without the provided node selector requirements
762
832
  """
763
- new_node_selector_terms: typing.List[k8s_client.V1NodeSelectorTerm] = []
833
+ new_node_selector_terms: list[k8s_client.V1NodeSelectorTerm] = []
764
834
  for term in node_selector_terms:
765
- new_node_selector_requirements: typing.List[
835
+ new_node_selector_requirements: list[
766
836
  k8s_client.V1NodeSelectorRequirement
767
837
  ] = []
768
838
  for node_selector_requirement in term.match_expressions:
@@ -791,7 +861,7 @@ class KubeResourceSpec(FunctionSpec):
791
861
 
792
862
  def _prune_tolerations(
793
863
  self,
794
- tolerations: typing.List[k8s_client.V1Toleration],
864
+ tolerations: list[k8s_client.V1Toleration],
795
865
  tolerations_field_name: str = "tolerations",
796
866
  ):
797
867
  """
@@ -820,7 +890,7 @@ class KubeResourceSpec(FunctionSpec):
820
890
 
821
891
  def _prune_node_selector(
822
892
  self,
823
- node_selector: typing.Dict[str, str],
893
+ node_selector: dict[str, str],
824
894
  node_selector_field_name: str,
825
895
  ):
826
896
  """
@@ -865,12 +935,12 @@ class AutoMountType(str, Enum):
865
935
  @classmethod
866
936
  def all_mount_modifiers(cls):
867
937
  return [
868
- mlrun.v3io_cred.__name__,
869
- mlrun.mount_v3io.__name__,
870
- mlrun.platforms.other.mount_pvc.__name__,
871
- mlrun.auto_mount.__name__,
872
- mlrun.platforms.mount_s3.__name__,
873
- mlrun.platforms.set_env_variables.__name__,
938
+ mlrun_pipelines.mounts.v3io_cred.__name__,
939
+ mlrun_pipelines.mounts.mount_v3io.__name__,
940
+ mlrun_pipelines.mounts.mount_pvc.__name__,
941
+ mlrun_pipelines.mounts.auto_mount.__name__,
942
+ mlrun_pipelines.mounts.mount_s3.__name__,
943
+ mlrun_pipelines.mounts.set_env_variables.__name__,
874
944
  ]
875
945
 
876
946
  @classmethod
@@ -887,27 +957,27 @@ class AutoMountType(str, Enum):
887
957
  def _get_auto_modifier():
888
958
  # If we're running on Iguazio - use v3io_cred
889
959
  if mlconf.igz_version != "":
890
- return mlrun.v3io_cred
960
+ return mlrun_pipelines.mounts.v3io_cred
891
961
  # Else, either pvc mount if it's configured or do nothing otherwise
892
962
  pvc_configured = (
893
963
  "MLRUN_PVC_MOUNT" in os.environ
894
964
  or "pvc_name" in mlconf.get_storage_auto_mount_params()
895
965
  )
896
- return mlrun.platforms.other.mount_pvc if pvc_configured else None
966
+ return mlrun_pipelines.mounts.mount_pvc if pvc_configured else None
897
967
 
898
968
  def get_modifier(self):
899
969
  return {
900
970
  AutoMountType.none: None,
901
- AutoMountType.v3io_credentials: mlrun.v3io_cred,
902
- AutoMountType.v3io_fuse: mlrun.mount_v3io,
903
- AutoMountType.pvc: mlrun.platforms.other.mount_pvc,
971
+ AutoMountType.v3io_credentials: mlrun_pipelines.mounts.v3io_cred,
972
+ AutoMountType.v3io_fuse: mlrun_pipelines.mounts.mount_v3io,
973
+ AutoMountType.pvc: mlrun_pipelines.mounts.mount_pvc,
904
974
  AutoMountType.auto: self._get_auto_modifier(),
905
- AutoMountType.s3: mlrun.platforms.mount_s3,
906
- AutoMountType.env: mlrun.platforms.set_env_variables,
975
+ AutoMountType.s3: mlrun_pipelines.mounts.mount_s3,
976
+ AutoMountType.env: mlrun_pipelines.mounts.set_env_variables,
907
977
  }[self]
908
978
 
909
979
 
910
- class KubeResource(BaseRuntime):
980
+ class KubeResource(BaseRuntime, KfpAdapterMixin):
911
981
  """
912
982
  A parent class for runtimes that generate k8s resources when executing.
913
983
  """
@@ -916,7 +986,7 @@ class KubeResource(BaseRuntime):
916
986
  _is_nested = True
917
987
 
918
988
  def __init__(self, spec=None, metadata=None):
919
- super().__init__(metadata, spec)
989
+ super().__init__(metadata=metadata, spec=spec)
920
990
  self.verbose = False
921
991
 
922
992
  @property
@@ -927,48 +997,6 @@ class KubeResource(BaseRuntime):
927
997
  def spec(self, spec):
928
998
  self._spec = self._verify_dict(spec, "spec", KubeResourceSpec)
929
999
 
930
- def to_dict(self, fields=None, exclude=None, strip=False):
931
- struct = super().to_dict(fields, exclude, strip=strip)
932
- api = k8s_client.ApiClient()
933
- struct = api.sanitize_for_serialization(struct)
934
- if strip:
935
- spec = struct["spec"]
936
- for attr in [
937
- "volumes",
938
- "volume_mounts",
939
- "driver_volume_mounts",
940
- "executor_volume_mounts",
941
- ]:
942
- if attr in spec:
943
- del spec[attr]
944
- if "env" in spec and spec["env"]:
945
- for ev in spec["env"]:
946
- if ev["name"].startswith("V3IO_"):
947
- ev["value"] = ""
948
- # Reset this, since mounts and env variables were cleared.
949
- spec["disable_auto_mount"] = False
950
- return struct
951
-
952
- def apply(self, modify):
953
- """
954
- Apply a modifier to the runtime which is used to change the runtimes k8s object's spec.
955
- Modifiers can be either KFP modifiers or MLRun modifiers (which are compatible with KFP). All modifiers accept
956
- a `kfp.dsl.ContainerOp` object, apply some changes on its spec and return it so modifiers can be chained
957
- one after the other.
958
-
959
- :param modify: a modifier runnable object
960
- :return: the runtime (self) after the modifications
961
- """
962
-
963
- # Kubeflow pipeline have a hook to add the component to the DAG on ContainerOp init
964
- # we remove the hook to suppress kubeflow op registration and return it after the apply()
965
- old_op_handler = kfp.dsl._container_op._register_op_handler
966
- kfp.dsl._container_op._register_op_handler = lambda x: self.metadata.name
967
- cop = kfp.dsl.ContainerOp("name", "image")
968
- kfp.dsl._container_op._register_op_handler = old_op_handler
969
-
970
- return apply_kfp(modify, cop, self)
971
-
972
1000
  def set_env_from_secret(self, name, secret=None, secret_key=None):
973
1001
  """set pod environment var from secret"""
974
1002
  secret_key = secret_key or name
@@ -1010,6 +1038,32 @@ class KubeResource(BaseRuntime):
1010
1038
  return True
1011
1039
  return False
1012
1040
 
1041
+ def enrich_runtime_spec(
1042
+ self,
1043
+ project_node_selector: dict[str, str],
1044
+ ):
1045
+ """
1046
+ Enriches the runtime spec with the project-level node selector.
1047
+
1048
+ This method merges the project-level node selector with the existing function node_selector.
1049
+ The merge logic used here combines the two dictionaries, giving precedence to
1050
+ the keys in the runtime node_selector. If there are conflicting keys between the
1051
+ two dictionaries, the values from self.spec.node_selector will overwrite the
1052
+ values from project_node_selector.
1053
+
1054
+ Example:
1055
+ Suppose self.spec.node_selector = {"type": "gpu", "zone": "us-east-1"}
1056
+ and project_node_selector = {"type": "cpu", "environment": "production"}.
1057
+ After the merge, the resulting node_selector will be:
1058
+ {"type": "gpu", "zone": "us-east-1", "environment": "production"}
1059
+
1060
+ Note:
1061
+ - The merge uses the ** operator, also known as the "unpacking" operator in Python,
1062
+ combining key-value pairs from each dictionary. Later dictionaries take precedence
1063
+ when there are conflicting keys.
1064
+ """
1065
+ self.spec.node_selector = {**project_node_selector, **self.spec.node_selector}
1066
+
1013
1067
  def _set_env(self, name, value=None, value_from=None):
1014
1068
  new_var = k8s_client.V1EnvVar(name=name, value=value, value_from=value_from)
1015
1069
 
@@ -1065,7 +1119,7 @@ class KubeResource(BaseRuntime):
1065
1119
 
1066
1120
  def set_state_thresholds(
1067
1121
  self,
1068
- state_thresholds: typing.Dict[str, str],
1122
+ state_thresholds: dict[str, str],
1069
1123
  patch: bool = True,
1070
1124
  ):
1071
1125
  """
@@ -1126,9 +1180,9 @@ class KubeResource(BaseRuntime):
1126
1180
  def with_node_selection(
1127
1181
  self,
1128
1182
  node_name: typing.Optional[str] = None,
1129
- node_selector: typing.Optional[typing.Dict[str, str]] = None,
1183
+ node_selector: typing.Optional[dict[str, str]] = None,
1130
1184
  affinity: typing.Optional[k8s_client.V1Affinity] = None,
1131
- tolerations: typing.Optional[typing.List[k8s_client.V1Toleration]] = None,
1185
+ tolerations: typing.Optional[list[k8s_client.V1Toleration]] = None,
1132
1186
  ):
1133
1187
  """
1134
1188
  Enables to control on which k8s node the job will run
@@ -1204,9 +1258,9 @@ class KubeResource(BaseRuntime):
1204
1258
  from kubernetes import client as k8s_client
1205
1259
 
1206
1260
  security_context = k8s_client.V1SecurityContext(
1207
- run_as_user=1000,
1208
- run_as_group=3000,
1209
- )
1261
+ run_as_user=1000,
1262
+ run_as_group=3000,
1263
+ )
1210
1264
  function.with_security_context(security_context)
1211
1265
 
1212
1266
  More info:
@@ -1265,6 +1319,150 @@ class KubeResource(BaseRuntime):
1265
1319
 
1266
1320
  self.spec.validate_service_account(allowed_service_accounts)
1267
1321
 
1322
+ def _configure_mlrun_build_with_source(
1323
+ self, source, workdir=None, handler=None, pull_at_runtime=True, target_dir=None
1324
+ ):
1325
+ mlrun.utils.helpers.validate_builder_source(source, pull_at_runtime, workdir)
1326
+
1327
+ self.spec.build.source = source
1328
+ if handler:
1329
+ self.spec.default_handler = handler
1330
+ if workdir:
1331
+ self.spec.workdir = workdir
1332
+ if target_dir:
1333
+ self.spec.build.source_code_target_dir = target_dir
1334
+
1335
+ self.spec.build.load_source_on_run = pull_at_runtime
1336
+ if (
1337
+ self.spec.build.base_image
1338
+ and not self.spec.build.commands
1339
+ and pull_at_runtime
1340
+ and not self.spec.image
1341
+ ):
1342
+ # if we load source from repo and don't need a full build use the base_image as the image
1343
+ self.spec.image = self.spec.build.base_image
1344
+ elif not pull_at_runtime:
1345
+ # clear the image so build will not be skipped
1346
+ self.spec.build.base_image = self.spec.build.base_image or self.spec.image
1347
+ self.spec.image = ""
1348
+
1349
+ def _resolve_build_with_mlrun(self, with_mlrun: typing.Optional[bool] = None):
1350
+ build = self.spec.build
1351
+ if with_mlrun is None:
1352
+ if build.with_mlrun is not None:
1353
+ with_mlrun = build.with_mlrun
1354
+ else:
1355
+ with_mlrun = build.base_image and not (
1356
+ build.base_image.startswith("mlrun/")
1357
+ or "/mlrun/" in build.base_image
1358
+ )
1359
+ if (
1360
+ not build.source
1361
+ and not build.commands
1362
+ and not build.requirements
1363
+ and not build.extra
1364
+ and with_mlrun
1365
+ ):
1366
+ logger.info(
1367
+ "Running build to add mlrun package, set "
1368
+ "with_mlrun=False to skip if its already in the image"
1369
+ )
1370
+ return with_mlrun
1371
+
1372
+ def _build_image(
1373
+ self,
1374
+ builder_env,
1375
+ force_build,
1376
+ mlrun_version_specifier,
1377
+ show_on_failure,
1378
+ skip_deployed,
1379
+ watch,
1380
+ is_kfp,
1381
+ with_mlrun,
1382
+ ):
1383
+ # When we're in pipelines context we must watch otherwise the pipelines pod will exit before the operation
1384
+ # is actually done. (when a pipelines pod exits, the pipeline step marked as done)
1385
+ if is_kfp:
1386
+ watch = True
1387
+
1388
+ db = self._get_db()
1389
+ data = db.remote_builder(
1390
+ self,
1391
+ with_mlrun,
1392
+ mlrun_version_specifier,
1393
+ skip_deployed,
1394
+ builder_env=builder_env,
1395
+ force_build=force_build,
1396
+ )
1397
+ self.status = data["data"].get("status", None)
1398
+ self.spec.image = mlrun.utils.get_in(
1399
+ data, "data.spec.image"
1400
+ ) or mlrun.utils.get_in(data, "data.spec.build.image")
1401
+ self.spec.build.base_image = self.spec.build.base_image or mlrun.utils.get_in(
1402
+ data, "data.spec.build.base_image"
1403
+ )
1404
+ # Get the source target dir in case it was enriched due to loading source
1405
+ self.spec.build.source_code_target_dir = mlrun.utils.get_in(
1406
+ data, "data.spec.build.source_code_target_dir"
1407
+ ) or mlrun.utils.get_in(data, "data.spec.clone_target_dir")
1408
+ ready = data.get("ready", False)
1409
+ if not ready:
1410
+ logger.info(
1411
+ f"Started building image: {data.get('data', {}).get('spec', {}).get('build', {}).get('image')}"
1412
+ )
1413
+ if watch and not ready:
1414
+ state = self._build_watch(
1415
+ watch=watch,
1416
+ show_on_failure=show_on_failure,
1417
+ )
1418
+ ready = state == "ready"
1419
+ self.status.state = state
1420
+
1421
+ if watch and not ready:
1422
+ raise mlrun.errors.MLRunRuntimeError("Deploy failed")
1423
+ return ready
1424
+
1425
+ def _build_watch(
1426
+ self,
1427
+ watch: bool = True,
1428
+ logs: bool = True,
1429
+ show_on_failure: bool = False,
1430
+ ):
1431
+ db = self._get_db()
1432
+ offset = 0
1433
+ try:
1434
+ text, _ = db.get_builder_status(self, 0, logs=logs)
1435
+ except mlrun.db.RunDBError:
1436
+ raise ValueError("function or build process not found")
1437
+
1438
+ def print_log(text):
1439
+ if text and (
1440
+ not show_on_failure
1441
+ or self.status.state == mlrun.common.schemas.FunctionState.error
1442
+ ):
1443
+ print(text, end="")
1444
+
1445
+ print_log(text)
1446
+ offset += len(text)
1447
+ if watch:
1448
+ while self.status.state in [
1449
+ mlrun.common.schemas.FunctionState.pending,
1450
+ mlrun.common.schemas.FunctionState.running,
1451
+ ]:
1452
+ time.sleep(2)
1453
+ if show_on_failure:
1454
+ text = ""
1455
+ db.get_builder_status(self, 0, logs=False)
1456
+ if self.status.state == mlrun.common.schemas.FunctionState.error:
1457
+ # re-read the full log on failure
1458
+ text, _ = db.get_builder_status(self, offset, logs=logs)
1459
+ else:
1460
+ text, _ = db.get_builder_status(self, offset, logs=logs)
1461
+ print_log(text)
1462
+ offset += len(text)
1463
+
1464
+ return self.status.state
1465
+
1268
1466
 
1269
1467
  def _resolve_if_type_sanitized(attribute_name, attribute):
1270
1468
  attribute_config = sanitized_attributes[attribute_name]
@@ -15,11 +15,11 @@ import re
15
15
  from subprocess import run
16
16
 
17
17
  import kubernetes.client
18
+ from mlrun_pipelines.mounts import mount_v3io, mount_v3iod
18
19
 
19
20
  import mlrun.errors
20
21
  from mlrun.config import config
21
22
 
22
- from ..platforms.iguazio import mount_v3io, mount_v3iod
23
23
  from .kubejob import KubejobRuntime
24
24
  from .pod import KubeResourceSpec
25
25
 
@@ -92,7 +92,7 @@ class RemoteSparkSpec(KubeResourceSpec):
92
92
  self.provider = provider
93
93
 
94
94
 
95
- class RemoteSparkProviders(object):
95
+ class RemoteSparkProviders:
96
96
  iguazio = "iguazio"
97
97
 
98
98