mlrun 1.7.0rc4__py3-none-any.whl → 1.7.2__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 (235) hide show
  1. mlrun/__init__.py +11 -1
  2. mlrun/__main__.py +39 -121
  3. mlrun/{datastore/helpers.py → alerts/__init__.py} +2 -5
  4. mlrun/alerts/alert.py +248 -0
  5. mlrun/api/schemas/__init__.py +4 -3
  6. mlrun/artifacts/__init__.py +8 -3
  7. mlrun/artifacts/base.py +39 -254
  8. mlrun/artifacts/dataset.py +9 -190
  9. mlrun/artifacts/manager.py +73 -46
  10. mlrun/artifacts/model.py +30 -158
  11. mlrun/artifacts/plots.py +23 -380
  12. mlrun/common/constants.py +73 -1
  13. mlrun/common/db/sql_session.py +3 -2
  14. mlrun/common/formatters/__init__.py +21 -0
  15. mlrun/common/formatters/artifact.py +46 -0
  16. mlrun/common/formatters/base.py +113 -0
  17. mlrun/common/formatters/feature_set.py +44 -0
  18. mlrun/common/formatters/function.py +46 -0
  19. mlrun/common/formatters/pipeline.py +53 -0
  20. mlrun/common/formatters/project.py +51 -0
  21. mlrun/common/formatters/run.py +29 -0
  22. mlrun/common/helpers.py +11 -1
  23. mlrun/{runtimes → common/runtimes}/constants.py +32 -4
  24. mlrun/common/schemas/__init__.py +31 -4
  25. mlrun/common/schemas/alert.py +202 -0
  26. mlrun/common/schemas/api_gateway.py +196 -0
  27. mlrun/common/schemas/artifact.py +28 -1
  28. mlrun/common/schemas/auth.py +13 -2
  29. mlrun/common/schemas/client_spec.py +2 -1
  30. mlrun/common/schemas/common.py +7 -4
  31. mlrun/common/schemas/constants.py +3 -0
  32. mlrun/common/schemas/feature_store.py +58 -28
  33. mlrun/common/schemas/frontend_spec.py +8 -0
  34. mlrun/common/schemas/function.py +11 -0
  35. mlrun/common/schemas/hub.py +7 -9
  36. mlrun/common/schemas/model_monitoring/__init__.py +21 -4
  37. mlrun/common/schemas/model_monitoring/constants.py +136 -42
  38. mlrun/common/schemas/model_monitoring/grafana.py +9 -5
  39. mlrun/common/schemas/model_monitoring/model_endpoints.py +89 -41
  40. mlrun/common/schemas/notification.py +69 -12
  41. mlrun/{runtimes/mpijob/v1alpha1.py → common/schemas/pagination.py} +10 -13
  42. mlrun/common/schemas/pipeline.py +7 -0
  43. mlrun/common/schemas/project.py +67 -16
  44. mlrun/common/schemas/runs.py +17 -0
  45. mlrun/common/schemas/schedule.py +1 -1
  46. mlrun/common/schemas/workflow.py +10 -2
  47. mlrun/common/types.py +14 -1
  48. mlrun/config.py +233 -58
  49. mlrun/data_types/data_types.py +11 -1
  50. mlrun/data_types/spark.py +5 -4
  51. mlrun/data_types/to_pandas.py +75 -34
  52. mlrun/datastore/__init__.py +8 -10
  53. mlrun/datastore/alibaba_oss.py +131 -0
  54. mlrun/datastore/azure_blob.py +131 -43
  55. mlrun/datastore/base.py +107 -47
  56. mlrun/datastore/datastore.py +17 -7
  57. mlrun/datastore/datastore_profile.py +91 -7
  58. mlrun/datastore/dbfs_store.py +3 -7
  59. mlrun/datastore/filestore.py +1 -3
  60. mlrun/datastore/google_cloud_storage.py +92 -32
  61. mlrun/datastore/hdfs.py +5 -0
  62. mlrun/datastore/inmem.py +6 -3
  63. mlrun/datastore/redis.py +3 -2
  64. mlrun/datastore/s3.py +30 -12
  65. mlrun/datastore/snowflake_utils.py +45 -0
  66. mlrun/datastore/sources.py +274 -59
  67. mlrun/datastore/spark_utils.py +30 -0
  68. mlrun/datastore/store_resources.py +9 -7
  69. mlrun/datastore/storeytargets.py +151 -0
  70. mlrun/datastore/targets.py +387 -119
  71. mlrun/datastore/utils.py +68 -5
  72. mlrun/datastore/v3io.py +28 -50
  73. mlrun/db/auth_utils.py +152 -0
  74. mlrun/db/base.py +245 -20
  75. mlrun/db/factory.py +1 -4
  76. mlrun/db/httpdb.py +909 -231
  77. mlrun/db/nopdb.py +279 -14
  78. mlrun/errors.py +35 -5
  79. mlrun/execution.py +111 -38
  80. mlrun/feature_store/__init__.py +0 -2
  81. mlrun/feature_store/api.py +46 -53
  82. mlrun/feature_store/common.py +6 -11
  83. mlrun/feature_store/feature_set.py +48 -23
  84. mlrun/feature_store/feature_vector.py +13 -2
  85. mlrun/feature_store/ingestion.py +7 -6
  86. mlrun/feature_store/retrieval/base.py +9 -4
  87. mlrun/feature_store/retrieval/dask_merger.py +2 -0
  88. mlrun/feature_store/retrieval/job.py +13 -4
  89. mlrun/feature_store/retrieval/local_merger.py +2 -0
  90. mlrun/feature_store/retrieval/spark_merger.py +24 -32
  91. mlrun/feature_store/steps.py +38 -19
  92. mlrun/features.py +6 -14
  93. mlrun/frameworks/_common/plan.py +3 -3
  94. mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +7 -12
  95. mlrun/frameworks/_ml_common/plan.py +1 -1
  96. mlrun/frameworks/auto_mlrun/auto_mlrun.py +2 -2
  97. mlrun/frameworks/lgbm/__init__.py +1 -1
  98. mlrun/frameworks/lgbm/callbacks/callback.py +2 -4
  99. mlrun/frameworks/lgbm/model_handler.py +1 -1
  100. mlrun/frameworks/parallel_coordinates.py +4 -4
  101. mlrun/frameworks/pytorch/__init__.py +2 -2
  102. mlrun/frameworks/sklearn/__init__.py +1 -1
  103. mlrun/frameworks/sklearn/mlrun_interface.py +13 -3
  104. mlrun/frameworks/tf_keras/__init__.py +5 -2
  105. mlrun/frameworks/tf_keras/callbacks/logging_callback.py +1 -1
  106. mlrun/frameworks/tf_keras/mlrun_interface.py +2 -2
  107. mlrun/frameworks/xgboost/__init__.py +1 -1
  108. mlrun/k8s_utils.py +57 -12
  109. mlrun/launcher/__init__.py +1 -1
  110. mlrun/launcher/base.py +6 -5
  111. mlrun/launcher/client.py +13 -11
  112. mlrun/launcher/factory.py +1 -1
  113. mlrun/launcher/local.py +15 -5
  114. mlrun/launcher/remote.py +10 -3
  115. mlrun/lists.py +6 -2
  116. mlrun/model.py +297 -48
  117. mlrun/model_monitoring/__init__.py +1 -1
  118. mlrun/model_monitoring/api.py +152 -357
  119. mlrun/model_monitoring/applications/__init__.py +10 -0
  120. mlrun/model_monitoring/applications/_application_steps.py +190 -0
  121. mlrun/model_monitoring/applications/base.py +108 -0
  122. mlrun/model_monitoring/applications/context.py +341 -0
  123. mlrun/model_monitoring/{evidently_application.py → applications/evidently_base.py} +27 -22
  124. mlrun/model_monitoring/applications/histogram_data_drift.py +227 -91
  125. mlrun/model_monitoring/applications/results.py +99 -0
  126. mlrun/model_monitoring/controller.py +130 -303
  127. mlrun/model_monitoring/{stores/models/sqlite.py → db/__init__.py} +5 -10
  128. mlrun/model_monitoring/db/stores/__init__.py +136 -0
  129. mlrun/model_monitoring/db/stores/base/__init__.py +15 -0
  130. mlrun/model_monitoring/db/stores/base/store.py +213 -0
  131. mlrun/model_monitoring/db/stores/sqldb/__init__.py +13 -0
  132. mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +71 -0
  133. mlrun/model_monitoring/db/stores/sqldb/models/base.py +190 -0
  134. mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +103 -0
  135. mlrun/model_monitoring/{stores/models/mysql.py → db/stores/sqldb/models/sqlite.py} +19 -13
  136. mlrun/model_monitoring/db/stores/sqldb/sql_store.py +659 -0
  137. mlrun/model_monitoring/db/stores/v3io_kv/__init__.py +13 -0
  138. mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +726 -0
  139. mlrun/model_monitoring/db/tsdb/__init__.py +105 -0
  140. mlrun/model_monitoring/db/tsdb/base.py +448 -0
  141. mlrun/model_monitoring/db/tsdb/helpers.py +30 -0
  142. mlrun/model_monitoring/db/tsdb/tdengine/__init__.py +15 -0
  143. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +298 -0
  144. mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +42 -0
  145. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +522 -0
  146. mlrun/model_monitoring/db/tsdb/v3io/__init__.py +15 -0
  147. mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +158 -0
  148. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +849 -0
  149. mlrun/model_monitoring/features_drift_table.py +34 -22
  150. mlrun/model_monitoring/helpers.py +177 -39
  151. mlrun/model_monitoring/model_endpoint.py +3 -2
  152. mlrun/model_monitoring/stream_processing.py +165 -398
  153. mlrun/model_monitoring/tracking_policy.py +7 -1
  154. mlrun/model_monitoring/writer.py +161 -125
  155. mlrun/package/packagers/default_packager.py +2 -2
  156. mlrun/package/packagers_manager.py +1 -0
  157. mlrun/package/utils/_formatter.py +2 -2
  158. mlrun/platforms/__init__.py +11 -10
  159. mlrun/platforms/iguazio.py +67 -228
  160. mlrun/projects/__init__.py +6 -1
  161. mlrun/projects/operations.py +47 -20
  162. mlrun/projects/pipelines.py +396 -249
  163. mlrun/projects/project.py +1176 -406
  164. mlrun/render.py +28 -22
  165. mlrun/run.py +208 -181
  166. mlrun/runtimes/__init__.py +76 -11
  167. mlrun/runtimes/base.py +54 -24
  168. mlrun/runtimes/daskjob.py +9 -2
  169. mlrun/runtimes/databricks_job/databricks_runtime.py +1 -0
  170. mlrun/runtimes/databricks_job/databricks_wrapper.py +1 -1
  171. mlrun/runtimes/funcdoc.py +1 -29
  172. mlrun/runtimes/kubejob.py +34 -128
  173. mlrun/runtimes/local.py +39 -10
  174. mlrun/runtimes/mpijob/__init__.py +0 -20
  175. mlrun/runtimes/mpijob/abstract.py +8 -8
  176. mlrun/runtimes/mpijob/v1.py +1 -1
  177. mlrun/runtimes/nuclio/__init__.py +1 -0
  178. mlrun/runtimes/nuclio/api_gateway.py +769 -0
  179. mlrun/runtimes/nuclio/application/__init__.py +15 -0
  180. mlrun/runtimes/nuclio/application/application.py +758 -0
  181. mlrun/runtimes/nuclio/application/reverse_proxy.go +95 -0
  182. mlrun/runtimes/nuclio/function.py +188 -68
  183. mlrun/runtimes/nuclio/serving.py +57 -60
  184. mlrun/runtimes/pod.py +191 -58
  185. mlrun/runtimes/remotesparkjob.py +11 -8
  186. mlrun/runtimes/sparkjob/spark3job.py +17 -18
  187. mlrun/runtimes/utils.py +40 -73
  188. mlrun/secrets.py +6 -2
  189. mlrun/serving/__init__.py +8 -1
  190. mlrun/serving/remote.py +2 -3
  191. mlrun/serving/routers.py +89 -64
  192. mlrun/serving/server.py +54 -26
  193. mlrun/serving/states.py +187 -56
  194. mlrun/serving/utils.py +19 -11
  195. mlrun/serving/v2_serving.py +136 -63
  196. mlrun/track/tracker.py +2 -1
  197. mlrun/track/trackers/mlflow_tracker.py +5 -0
  198. mlrun/utils/async_http.py +26 -6
  199. mlrun/utils/db.py +18 -0
  200. mlrun/utils/helpers.py +375 -105
  201. mlrun/utils/http.py +2 -2
  202. mlrun/utils/logger.py +75 -9
  203. mlrun/utils/notifications/notification/__init__.py +14 -10
  204. mlrun/utils/notifications/notification/base.py +48 -0
  205. mlrun/utils/notifications/notification/console.py +2 -0
  206. mlrun/utils/notifications/notification/git.py +24 -1
  207. mlrun/utils/notifications/notification/ipython.py +2 -0
  208. mlrun/utils/notifications/notification/slack.py +96 -21
  209. mlrun/utils/notifications/notification/webhook.py +63 -2
  210. mlrun/utils/notifications/notification_pusher.py +146 -16
  211. mlrun/utils/regex.py +9 -0
  212. mlrun/utils/retryer.py +3 -2
  213. mlrun/utils/v3io_clients.py +2 -3
  214. mlrun/utils/version/version.json +2 -2
  215. mlrun-1.7.2.dist-info/METADATA +390 -0
  216. mlrun-1.7.2.dist-info/RECORD +351 -0
  217. {mlrun-1.7.0rc4.dist-info → mlrun-1.7.2.dist-info}/WHEEL +1 -1
  218. mlrun/feature_store/retrieval/conversion.py +0 -271
  219. mlrun/kfpops.py +0 -868
  220. mlrun/model_monitoring/application.py +0 -310
  221. mlrun/model_monitoring/batch.py +0 -974
  222. mlrun/model_monitoring/controller_handler.py +0 -37
  223. mlrun/model_monitoring/prometheus.py +0 -216
  224. mlrun/model_monitoring/stores/__init__.py +0 -111
  225. mlrun/model_monitoring/stores/kv_model_endpoint_store.py +0 -574
  226. mlrun/model_monitoring/stores/model_endpoint_store.py +0 -145
  227. mlrun/model_monitoring/stores/models/__init__.py +0 -27
  228. mlrun/model_monitoring/stores/models/base.py +0 -84
  229. mlrun/model_monitoring/stores/sql_model_endpoint_store.py +0 -382
  230. mlrun/platforms/other.py +0 -305
  231. mlrun-1.7.0rc4.dist-info/METADATA +0 -269
  232. mlrun-1.7.0rc4.dist-info/RECORD +0 -321
  233. {mlrun-1.7.0rc4.dist-info → mlrun-1.7.2.dist-info}/LICENSE +0 -0
  234. {mlrun-1.7.0rc4.dist-info → mlrun-1.7.2.dist-info}/entry_points.txt +0 -0
  235. {mlrun-1.7.0rc4.dist-info → mlrun-1.7.2.dist-info}/top_level.txt +0 -0
mlrun/db/httpdb.py CHANGED
@@ -11,28 +11,37 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+
14
15
  import enum
15
16
  import http
16
17
  import re
17
- import tempfile
18
18
  import time
19
19
  import traceback
20
20
  import typing
21
21
  import warnings
22
+ from copy import deepcopy
22
23
  from datetime import datetime, timedelta
23
24
  from os import path, remove
24
25
  from typing import Optional, Union
25
26
  from urllib.parse import urlparse
26
27
 
27
- import kfp
28
28
  import requests
29
29
  import semver
30
+ from mlrun_pipelines.utils import compile_pipeline
30
31
 
31
32
  import mlrun
33
+ import mlrun.common.formatters
34
+ import mlrun.common.runtimes
32
35
  import mlrun.common.schemas
36
+ import mlrun.common.types
33
37
  import mlrun.model_monitoring.model_endpoint
34
38
  import mlrun.platforms
35
39
  import mlrun.projects
40
+ import mlrun.runtimes.nuclio.api_gateway
41
+ import mlrun.runtimes.nuclio.function
42
+ import mlrun.utils
43
+ from mlrun.alerts.alert import AlertConfig
44
+ from mlrun.db.auth_utils import OAuthClientIDTokenProvider, StaticTokenProvider
36
45
  from mlrun.errors import MLRunInvalidArgumentError, err_to_str
37
46
 
38
47
  from ..artifacts import Artifact
@@ -45,7 +54,6 @@ from ..utils import (
45
54
  datetime_to_iso,
46
55
  dict_to_json,
47
56
  logger,
48
- new_pipe_metadata,
49
57
  normalize_name,
50
58
  version,
51
59
  )
@@ -133,17 +141,28 @@ class HTTPRunDB(RunDBInterface):
133
141
  endpoint += f":{parsed_url.port}"
134
142
  base_url = f"{parsed_url.scheme}://{endpoint}{parsed_url.path}"
135
143
 
144
+ self.base_url = base_url
136
145
  username = parsed_url.username or config.httpdb.user
137
146
  password = parsed_url.password or config.httpdb.password
147
+ self.token_provider = None
148
+
149
+ if config.auth_with_client_id.enabled:
150
+ self.token_provider = OAuthClientIDTokenProvider(
151
+ token_endpoint=mlrun.get_secret_or_env("MLRUN_AUTH_TOKEN_ENDPOINT"),
152
+ client_id=mlrun.get_secret_or_env("MLRUN_AUTH_CLIENT_ID"),
153
+ client_secret=mlrun.get_secret_or_env("MLRUN_AUTH_CLIENT_SECRET"),
154
+ timeout=config.auth_with_client_id.request_timeout,
155
+ )
156
+ else:
157
+ username, password, token = mlrun.platforms.add_or_refresh_credentials(
158
+ parsed_url.hostname, username, password, config.httpdb.token
159
+ )
138
160
 
139
- username, password, token = mlrun.platforms.add_or_refresh_credentials(
140
- parsed_url.hostname, username, password, config.httpdb.token
141
- )
161
+ if token:
162
+ self.token_provider = StaticTokenProvider(token)
142
163
 
143
- self.base_url = base_url
144
164
  self.user = username
145
165
  self.password = password
146
- self.token = token
147
166
 
148
167
  def __repr__(self):
149
168
  cls = self.__class__.__name__
@@ -179,7 +198,7 @@ class HTTPRunDB(RunDBInterface):
179
198
  headers=None,
180
199
  timeout=45,
181
200
  version=None,
182
- ):
201
+ ) -> requests.Response:
183
202
  """Perform a direct REST API call on the :py:mod:`mlrun` API server.
184
203
 
185
204
  Caution:
@@ -197,7 +216,7 @@ class HTTPRunDB(RunDBInterface):
197
216
  :param version: API version to use, None (the default) will mean to use the default value from config,
198
217
  for un-versioned api set an empty string.
199
218
 
200
- :return: Python HTTP response object
219
+ :returns: `requests.Response` HTTP response object
201
220
  """
202
221
  url = self.get_base_api_url(path, version)
203
222
  kw = {
@@ -213,17 +232,19 @@ class HTTPRunDB(RunDBInterface):
213
232
 
214
233
  if self.user:
215
234
  kw["auth"] = (self.user, self.password)
216
- elif self.token:
217
- # Iguazio auth doesn't support passing token through bearer, so use cookie instead
218
- if mlrun.platforms.iguazio.is_iguazio_session(self.token):
219
- session_cookie = f'j:{{"sid": "{self.token}"}}'
220
- cookies = {
221
- "session": session_cookie,
222
- }
223
- kw["cookies"] = cookies
224
- else:
225
- if "Authorization" not in kw.setdefault("headers", {}):
226
- kw["headers"].update({"Authorization": "Bearer " + self.token})
235
+ elif self.token_provider:
236
+ token = self.token_provider.get_token()
237
+ if token:
238
+ # Iguazio auth doesn't support passing token through bearer, so use cookie instead
239
+ if self.token_provider.is_iguazio_session():
240
+ session_cookie = f'j:{{"sid": "{token}"}}'
241
+ cookies = {
242
+ "session": session_cookie,
243
+ }
244
+ kw["cookies"] = cookies
245
+ else:
246
+ if "Authorization" not in kw.setdefault("headers", {}):
247
+ kw["headers"].update({"Authorization": "Bearer " + token})
227
248
 
228
249
  if mlrun.common.schemas.HeaderNames.client_version not in kw.setdefault(
229
250
  "headers", {}
@@ -278,6 +299,68 @@ class HTTPRunDB(RunDBInterface):
278
299
 
279
300
  return response
280
301
 
302
+ def paginated_api_call(
303
+ self,
304
+ method,
305
+ path,
306
+ error=None,
307
+ params=None,
308
+ body=None,
309
+ json=None,
310
+ headers=None,
311
+ timeout=45,
312
+ version=None,
313
+ ) -> typing.Generator[requests.Response, None, None]:
314
+ """
315
+ Calls the api with pagination, yielding each page of the response
316
+ """
317
+
318
+ def _api_call(_params):
319
+ return self.api_call(
320
+ method=method,
321
+ path=path,
322
+ error=error,
323
+ params=_params,
324
+ body=body,
325
+ json=json,
326
+ headers=headers,
327
+ timeout=timeout,
328
+ version=version,
329
+ )
330
+
331
+ first_page_params = deepcopy(params) or {}
332
+ first_page_params["page"] = 1
333
+ first_page_params["page-size"] = config.httpdb.pagination.default_page_size
334
+ response = _api_call(first_page_params)
335
+ page_token = response.json().get("pagination", {}).get("page-token")
336
+ if not page_token:
337
+ yield response
338
+ return
339
+
340
+ params_with_page_token = deepcopy(params) or {}
341
+ params_with_page_token["page-token"] = page_token
342
+ while page_token:
343
+ yield response
344
+ try:
345
+ response = _api_call(params_with_page_token)
346
+ except mlrun.errors.MLRunNotFoundError:
347
+ # pagination token expired
348
+ break
349
+
350
+ page_token = response.json().get("pagination", {}).get("page-token", None)
351
+
352
+ @staticmethod
353
+ def process_paginated_responses(
354
+ responses: typing.Generator[requests.Response, None, None], key: str = "data"
355
+ ) -> list[typing.Any]:
356
+ """
357
+ Processes the paginated responses and returns the combined data
358
+ """
359
+ data = []
360
+ for response in responses:
361
+ data.extend(response.json().get(key, []))
362
+ return data
363
+
281
364
  def _init_session(self, retry_on_post: bool = False):
282
365
  return mlrun.utils.HTTPSessionWithRetry(
283
366
  retry_on_exception=config.httpdb.retry_api_call_on_exception
@@ -310,7 +393,7 @@ class HTTPRunDB(RunDBInterface):
310
393
 
311
394
  For example::
312
395
 
313
- config.dbpath = config.dbpath or 'http://mlrun-api:8080'
396
+ config.dbpath = config.dbpath or "http://mlrun-api:8080"
314
397
  db = get_run_db().connect()
315
398
  """
316
399
  # hack to allow unit tests to instantiate HTTPRunDB without a real server behind
@@ -442,14 +525,18 @@ class HTTPRunDB(RunDBInterface):
442
525
  server_cfg.get("external_platform_tracking")
443
526
  or config.external_platform_tracking
444
527
  )
445
- config.model_endpoint_monitoring.store_type = (
446
- server_cfg.get("model_endpoint_monitoring_store_type")
447
- or config.model_endpoint_monitoring.store_type
448
- )
449
528
  config.model_endpoint_monitoring.endpoint_store_connection = (
450
529
  server_cfg.get("model_endpoint_monitoring_endpoint_store_connection")
451
530
  or config.model_endpoint_monitoring.endpoint_store_connection
452
531
  )
532
+ config.model_endpoint_monitoring.tsdb_connection = (
533
+ server_cfg.get("model_monitoring_tsdb_connection")
534
+ or config.model_endpoint_monitoring.tsdb_connection
535
+ )
536
+ config.model_endpoint_monitoring.stream_connection = (
537
+ server_cfg.get("stream_connection")
538
+ or config.model_endpoint_monitoring.stream_connection
539
+ )
453
540
  config.packagers = server_cfg.get("packagers") or config.packagers
454
541
  server_data_prefixes = server_cfg.get("feature_store_data_prefixes") or {}
455
542
  for prefix in ["default", "nosql", "redisnosql"]:
@@ -462,6 +549,7 @@ class HTTPRunDB(RunDBInterface):
462
549
  server_cfg.get("feature_store_default_targets")
463
550
  or config.feature_store.default_targets
464
551
  )
552
+ config.alerts.mode = server_cfg.get("alerts_mode") or config.alerts.mode
465
553
 
466
554
  except Exception as exc:
467
555
  logger.warning(
@@ -508,7 +596,7 @@ class HTTPRunDB(RunDBInterface):
508
596
  if offset < 0:
509
597
  raise MLRunInvalidArgumentError("Offset cannot be negative")
510
598
  if size is None:
511
- size = int(config.httpdb.logs.pull_logs_default_size_limit)
599
+ size = int(mlrun.mlconf.httpdb.logs.pull_logs_default_size_limit)
512
600
  elif size == -1:
513
601
  logger.warning(
514
602
  "Retrieving all logs. This may be inefficient and can result in a large log."
@@ -554,33 +642,35 @@ class HTTPRunDB(RunDBInterface):
554
642
 
555
643
  state, text = self.get_log(uid, project, offset=offset)
556
644
  if text:
557
- print(text.decode(errors=config.httpdb.logs.decode.errors))
645
+ print(text.decode(errors=mlrun.mlconf.httpdb.logs.decode.errors))
558
646
  nil_resp = 0
559
647
  while True:
560
648
  offset += len(text)
561
649
  # if we get 3 nil responses in a row, increase the sleep time to 10 seconds
562
650
  # TODO: refactor this to use a conditional backoff mechanism
563
651
  if nil_resp < 3:
564
- time.sleep(int(config.httpdb.logs.pull_logs_default_interval))
652
+ time.sleep(int(mlrun.mlconf.httpdb.logs.pull_logs_default_interval))
565
653
  else:
566
654
  time.sleep(
567
- int(config.httpdb.logs.pull_logs_backoff_no_logs_default_interval)
655
+ int(
656
+ mlrun.mlconf.httpdb.logs.pull_logs_backoff_no_logs_default_interval
657
+ )
568
658
  )
569
659
  state, text = self.get_log(uid, project, offset=offset)
570
660
  if text:
571
661
  nil_resp = 0
572
662
  print(
573
- text.decode(errors=config.httpdb.logs.decode.errors),
663
+ text.decode(errors=mlrun.mlconf.httpdb.logs.decode.errors),
574
664
  end="",
575
665
  )
576
666
  else:
577
667
  nil_resp += 1
578
668
 
579
669
  if watch and state in [
580
- mlrun.runtimes.constants.RunStates.pending,
581
- mlrun.runtimes.constants.RunStates.running,
582
- mlrun.runtimes.constants.RunStates.created,
583
- mlrun.runtimes.constants.RunStates.aborting,
670
+ mlrun.common.runtimes.constants.RunStates.pending,
671
+ mlrun.common.runtimes.constants.RunStates.running,
672
+ mlrun.common.runtimes.constants.RunStates.created,
673
+ mlrun.common.runtimes.constants.RunStates.aborting,
584
674
  ]:
585
675
  continue
586
676
  else:
@@ -636,16 +726,26 @@ class HTTPRunDB(RunDBInterface):
636
726
  )
637
727
  return None
638
728
 
639
- def read_run(self, uid, project="", iter=0):
729
+ def read_run(
730
+ self,
731
+ uid,
732
+ project="",
733
+ iter=0,
734
+ format_: mlrun.common.formatters.RunFormat = mlrun.common.formatters.RunFormat.full,
735
+ ):
640
736
  """Read the details of a stored run from the DB.
641
737
 
642
- :param uid: The run's unique ID.
643
- :param project: Project name.
644
- :param iter: Iteration within a specific execution.
738
+ :param uid: The run's unique ID.
739
+ :param project: Project name.
740
+ :param iter: Iteration within a specific execution.
741
+ :param format_: The format in which to return the run details.
645
742
  """
646
743
 
647
744
  path = self._path_of("runs", project, uid)
648
- params = {"iter": iter}
745
+ params = {
746
+ "iter": iter,
747
+ "format": format_.value,
748
+ }
649
749
  error = f"get run {project}/{uid}"
650
750
  resp = self.api_call("GET", path, error, params=params)
651
751
  return resp.json()["data"]
@@ -669,7 +769,10 @@ class HTTPRunDB(RunDBInterface):
669
769
  uid: Optional[Union[str, list[str]]] = None,
670
770
  project: Optional[str] = None,
671
771
  labels: Optional[Union[str, list[str]]] = None,
672
- state: Optional[str] = None,
772
+ state: Optional[
773
+ mlrun.common.runtimes.constants.RunStates
774
+ ] = None, # Backward compatibility
775
+ states: typing.Optional[list[mlrun.common.runtimes.constants.RunStates]] = None,
673
776
  sort: bool = True,
674
777
  last: int = 0,
675
778
  iter: bool = False,
@@ -694,9 +797,11 @@ class HTTPRunDB(RunDBInterface):
694
797
 
695
798
  Example::
696
799
 
697
- runs = db.list_runs(name='download', project='iris', labels=['owner=admin', 'kind=job'])
800
+ runs = db.list_runs(
801
+ name="download", project="iris", labels=["owner=admin", "kind=job"]
802
+ )
698
803
  # If running in Jupyter, can use the .show() function to display the results
699
- db.list_runs(name='', project=project_name).show()
804
+ db.list_runs(name="", project=project_name).show()
700
805
 
701
806
 
702
807
  :param name: Name of the run to retrieve.
@@ -705,7 +810,8 @@ class HTTPRunDB(RunDBInterface):
705
810
  :param labels: A list of labels to filter by. Label filters work by either filtering a specific value
706
811
  of a label (i.e. list("key=value")) or by looking for the existence of a given
707
812
  key (i.e. "key").
708
- :param state: List only runs whose state is specified.
813
+ :param state: Deprecated - List only runs whose state is specified (will be removed in 1.9.0)
814
+ :param states: List only runs whose state is one of the provided states.
709
815
  :param sort: Whether to sort the result according to their start time. Otherwise, results will be
710
816
  returned by their internal order in the DB (order will not be guaranteed).
711
817
  :param last: Deprecated - currently not used (will be removed in 1.8.0).
@@ -741,11 +847,19 @@ class HTTPRunDB(RunDBInterface):
741
847
  FutureWarning,
742
848
  )
743
849
 
850
+ if state:
851
+ # TODO: Remove this in 1.9.0
852
+ warnings.warn(
853
+ "'state' is deprecated and will be removed in 1.9.0. Use 'states' instead.",
854
+ FutureWarning,
855
+ )
856
+
744
857
  if (
745
858
  not name
746
859
  and not uid
747
860
  and not labels
748
861
  and not state
862
+ and not states
749
863
  and not last
750
864
  and not start_time_from
751
865
  and not start_time_to
@@ -757,14 +871,16 @@ class HTTPRunDB(RunDBInterface):
757
871
  ):
758
872
  # default to last week on no filter
759
873
  start_time_from = datetime.now() - timedelta(days=7)
760
- partition_by = mlrun.common.schemas.RunPartitionByField.name
874
+ partition_by = mlrun.common.schemas.RunPartitionByField.project_and_name
761
875
  partition_sort_by = mlrun.common.schemas.SortField.updated
762
876
 
763
877
  params = {
764
878
  "name": name,
765
879
  "uid": uid,
766
880
  "label": labels or [],
767
- "state": state,
881
+ "state": mlrun.utils.helpers.as_list(state)
882
+ if state is not None
883
+ else states or None,
768
884
  "sort": bool2str(sort),
769
885
  "iter": bool2str(iter),
770
886
  "start_time_from": datetime_to_iso(start_time_from),
@@ -787,15 +903,15 @@ class HTTPRunDB(RunDBInterface):
787
903
  )
788
904
  error = "list runs"
789
905
  _path = self._path_of("runs", project)
790
- resp = self.api_call("GET", _path, error, params=params)
791
- return RunList(resp.json()["runs"])
906
+ responses = self.paginated_api_call("GET", _path, error, params=params)
907
+ return RunList(self.process_paginated_responses(responses, "runs"))
792
908
 
793
909
  def del_runs(self, name=None, project=None, labels=None, state=None, days_ago=0):
794
910
  """Delete a group of runs identified by the parameters of the function.
795
911
 
796
912
  Example::
797
913
 
798
- db.del_runs(state='completed')
914
+ db.del_runs(state="completed")
799
915
 
800
916
  :param name: Name of the task which the runs belong to.
801
917
  :param project: Project to which the runs belong.
@@ -848,7 +964,7 @@ class HTTPRunDB(RunDBInterface):
848
964
 
849
965
  # we do this because previously the 'uid' name was used for the 'tree' parameter
850
966
  tree = tree or uid
851
-
967
+ project = project or mlrun.mlconf.default_project
852
968
  endpoint_path = f"projects/{project}/artifacts/{key}"
853
969
 
854
970
  error = f"store artifact {project}/{key}"
@@ -874,6 +990,7 @@ class HTTPRunDB(RunDBInterface):
874
990
  project="",
875
991
  tree=None,
876
992
  uid=None,
993
+ format_: mlrun.common.formatters.ArtifactFormat = mlrun.common.formatters.ArtifactFormat.full,
877
994
  ):
878
995
  """Read an artifact, identified by its key, tag, tree and iteration.
879
996
 
@@ -883,25 +1000,37 @@ class HTTPRunDB(RunDBInterface):
883
1000
  :param project: Project that the artifact belongs to.
884
1001
  :param tree: The tree which generated this artifact.
885
1002
  :param uid: A unique ID for this specific version of the artifact (the uid that was generated in the backend)
1003
+ :param format_: The format in which to return the artifact. Default is 'full'.
886
1004
  """
887
1005
 
888
- project = project or config.default_project
1006
+ project = project or mlrun.mlconf.default_project
889
1007
  tag = tag or "latest"
890
1008
  endpoint_path = f"projects/{project}/artifacts/{key}"
891
1009
  error = f"read artifact {project}/{key}"
892
- # explicitly set artifacts format to 'full' since old servers may default to 'legacy'
893
1010
  params = {
894
- "format": mlrun.common.schemas.ArtifactsFormat.full.value,
1011
+ "format": format_,
895
1012
  "tag": tag,
896
1013
  "tree": tree,
897
- "uid": uid,
1014
+ "object-uid": uid,
898
1015
  }
899
- if iter:
1016
+ if iter is not None:
900
1017
  params["iter"] = str(iter)
901
1018
  resp = self.api_call("GET", endpoint_path, error, params=params, version="v2")
902
1019
  return resp.json()
903
1020
 
904
- def del_artifact(self, key, tag=None, project="", tree=None, uid=None):
1021
+ def del_artifact(
1022
+ self,
1023
+ key,
1024
+ tag=None,
1025
+ project="",
1026
+ tree=None,
1027
+ uid=None,
1028
+ deletion_strategy: mlrun.common.schemas.artifact.ArtifactsDeletionStrategies = (
1029
+ mlrun.common.schemas.artifact.ArtifactsDeletionStrategies.metadata_only
1030
+ ),
1031
+ secrets: dict = None,
1032
+ iter=None,
1033
+ ):
905
1034
  """Delete an artifact.
906
1035
 
907
1036
  :param key: Identifying key of the artifact.
@@ -909,17 +1038,28 @@ class HTTPRunDB(RunDBInterface):
909
1038
  :param project: Project that the artifact belongs to.
910
1039
  :param tree: The tree which generated this artifact.
911
1040
  :param uid: A unique ID for this specific version of the artifact (the uid that was generated in the backend)
1041
+ :param deletion_strategy: The artifact deletion strategy types.
1042
+ :param secrets: Credentials needed to access the artifact data.
912
1043
  """
913
-
1044
+ project = project or mlrun.mlconf.default_project
914
1045
  endpoint_path = f"projects/{project}/artifacts/{key}"
915
1046
  params = {
916
1047
  "key": key,
917
1048
  "tag": tag,
918
1049
  "tree": tree,
919
- "uid": uid,
1050
+ "object-uid": uid,
1051
+ "iter": iter,
1052
+ "deletion_strategy": deletion_strategy,
920
1053
  }
921
1054
  error = f"del artifact {project}/{key}"
922
- self.api_call("DELETE", endpoint_path, error, params=params, version="v2")
1055
+ self.api_call(
1056
+ "DELETE",
1057
+ endpoint_path,
1058
+ error,
1059
+ params=params,
1060
+ version="v2",
1061
+ body=dict_to_json(secrets),
1062
+ )
923
1063
 
924
1064
  def list_artifacts(
925
1065
  self,
@@ -927,24 +1067,31 @@ class HTTPRunDB(RunDBInterface):
927
1067
  project=None,
928
1068
  tag=None,
929
1069
  labels: Optional[Union[dict[str, str], list[str]]] = None,
930
- since=None,
931
- until=None,
1070
+ since: Optional[datetime] = None,
1071
+ until: Optional[datetime] = None,
932
1072
  iter: int = None,
933
1073
  best_iteration: bool = False,
934
1074
  kind: str = None,
935
1075
  category: Union[str, mlrun.common.schemas.ArtifactCategories] = None,
936
1076
  tree: str = None,
1077
+ producer_uri: str = None,
1078
+ format_: Optional[
1079
+ mlrun.common.formatters.ArtifactFormat
1080
+ ] = mlrun.common.formatters.ArtifactFormat.full,
1081
+ limit: int = None,
937
1082
  ) -> ArtifactList:
938
1083
  """List artifacts filtered by various parameters.
939
1084
 
940
1085
  Examples::
941
1086
 
942
1087
  # Show latest version of all artifacts in project
943
- latest_artifacts = db.list_artifacts('', tag='latest', project='iris')
1088
+ latest_artifacts = db.list_artifacts("", tag="latest", project="iris")
944
1089
  # check different artifact versions for a specific artifact
945
- result_versions = db.list_artifacts('results', tag='*', project='iris')
1090
+ result_versions = db.list_artifacts("results", tag="*", project="iris")
946
1091
  # Show artifacts with label filters - both uploaded and of binary type
947
- result_labels = db.list_artifacts('results', tag='*', project='iris', labels=['uploaded', 'type=binary'])
1092
+ result_labels = db.list_artifacts(
1093
+ "results", tag="*", project="iris", labels=["uploaded", "type=binary"]
1094
+ )
948
1095
 
949
1096
  :param name: Name of artifacts to retrieve. Name with '~' prefix is used as a like query, and is not
950
1097
  case-sensitive. This means that querying for ``~name`` may return artifacts named
@@ -953,16 +1100,21 @@ class HTTPRunDB(RunDBInterface):
953
1100
  :param tag: Return artifacts assigned this tag.
954
1101
  :param labels: Return artifacts that have these labels. Labels can either be a dictionary {"label": "value"} or
955
1102
  a list of "label=value" (match label key and value) or "label" (match just label key) strings.
956
- :param since: Not in use in :py:class:`HTTPRunDB`.
957
- :param until: Not in use in :py:class:`HTTPRunDB`.
1103
+ :param since: Return artifacts updated after this date (as datetime object).
1104
+ :param until: Return artifacts updated before this date (as datetime object).
958
1105
  :param iter: Return artifacts from a specific iteration (where ``iter=0`` means the root iteration). If
959
1106
  ``None`` (default) return artifacts from all iterations.
960
1107
  :param best_iteration: Returns the artifact which belongs to the best iteration of a given run, in the case of
961
1108
  artifacts generated from a hyper-param run. If only a single iteration exists, will return the artifact
962
1109
  from that iteration. If using ``best_iter``, the ``iter`` parameter must not be used.
963
- :param kind: Return artifacts of the requested kind.
964
- :param category: Return artifacts of the requested category.
965
- :param tree: Return artifacts of the requested tree.
1110
+ :param kind: Return artifacts of the requested kind.
1111
+ :param category: Return artifacts of the requested category.
1112
+ :param tree: Return artifacts of the requested tree.
1113
+ :param producer_uri: Return artifacts produced by the requested producer URI. Producer URI usually
1114
+ points to a run and is used to filter artifacts by the run that produced them when the artifact producer id
1115
+ is a workflow id (artifact was created as part of a workflow).
1116
+ :param format_: The format in which to return the artifacts. Default is 'full'.
1117
+ :param limit: Maximum number of artifacts to return.
966
1118
  """
967
1119
 
968
1120
  project = project or config.default_project
@@ -980,7 +1132,11 @@ class HTTPRunDB(RunDBInterface):
980
1132
  "kind": kind,
981
1133
  "category": category,
982
1134
  "tree": tree,
983
- "format": mlrun.common.schemas.ArtifactsFormat.full.value,
1135
+ "format": format_,
1136
+ "producer_uri": producer_uri,
1137
+ "limit": limit,
1138
+ "since": datetime_to_iso(since),
1139
+ "until": datetime_to_iso(until),
984
1140
  }
985
1141
  error = "list artifacts"
986
1142
  endpoint_path = f"projects/{project}/artifacts"
@@ -1070,15 +1226,44 @@ class HTTPRunDB(RunDBInterface):
1070
1226
  project = project or config.default_project
1071
1227
  path = f"projects/{project}/functions/{name}"
1072
1228
  error_message = f"Failed deleting function {project}/{name}"
1073
- self.api_call("DELETE", path, error_message)
1229
+ response = self.api_call("DELETE", path, error_message, version="v2")
1230
+ if response.status_code == http.HTTPStatus.ACCEPTED:
1231
+ logger.info(
1232
+ "Function is being deleted", project_name=project, function_name=name
1233
+ )
1234
+ background_task = mlrun.common.schemas.BackgroundTask(**response.json())
1235
+ background_task = self._wait_for_background_task_to_reach_terminal_state(
1236
+ background_task.metadata.name, project=project
1237
+ )
1238
+ if (
1239
+ background_task.status.state
1240
+ == mlrun.common.schemas.BackgroundTaskState.succeeded
1241
+ ):
1242
+ logger.info(
1243
+ "Function deleted", project_name=project, function_name=name
1244
+ )
1245
+ elif (
1246
+ background_task.status.state
1247
+ == mlrun.common.schemas.BackgroundTaskState.failed
1248
+ ):
1249
+ logger.info(
1250
+ "Function deletion failed",
1251
+ reason=background_task.status.error,
1252
+ project_name=project,
1253
+ function_name=name,
1254
+ )
1074
1255
 
1075
- def list_functions(self, name=None, project=None, tag=None, labels=None):
1256
+ def list_functions(
1257
+ self, name=None, project=None, tag=None, labels=None, since=None, until=None
1258
+ ):
1076
1259
  """Retrieve a list of functions, filtered by specific criteria.
1077
1260
 
1078
1261
  :param name: Return only functions with a specific name.
1079
1262
  :param project: Return functions belonging to this project. If not specified, the default project is used.
1080
- :param tag: Return function versions with specific tags.
1263
+ :param tag: Return function versions with specific tags. To return only tagged functions, set tag to ``"*"``.
1081
1264
  :param labels: Return functions that have specific labels assigned to them.
1265
+ :param since: Return functions updated after this date (as datetime object).
1266
+ :param until: Return functions updated before this date (as datetime object).
1082
1267
  :returns: List of function objects (as dictionary).
1083
1268
  """
1084
1269
  project = project or config.default_project
@@ -1086,11 +1271,13 @@ class HTTPRunDB(RunDBInterface):
1086
1271
  "name": name,
1087
1272
  "tag": tag,
1088
1273
  "label": labels or [],
1274
+ "since": datetime_to_iso(since),
1275
+ "until": datetime_to_iso(until),
1089
1276
  }
1090
1277
  error = "list functions"
1091
1278
  path = f"projects/{project}/functions"
1092
- resp = self.api_call("GET", path, error, params=params)
1093
- return resp.json()["funcs"]
1279
+ responses = self.paginated_api_call("GET", path, error, params=params)
1280
+ return self.process_paginated_responses(responses, "funcs")
1094
1281
 
1095
1282
  def list_runtime_resources(
1096
1283
  self,
@@ -1180,25 +1367,19 @@ class HTTPRunDB(RunDBInterface):
1180
1367
  period didn't pass.
1181
1368
  :param grace_period: Grace period given to the runtime resource before they are actually removed, counted from
1182
1369
  the moment they moved to terminal state
1183
- (defaults to mlrun.config.config.runtime_resources_deletion_grace_period).
1370
+ (defaults to mlrun.mlconf.runtime_resources_deletion_grace_period).
1184
1371
 
1185
1372
  :returns: :py:class:`~mlrun.common.schemas.GroupedByProjectRuntimeResourcesOutput` listing the runtime resources
1186
1373
  that were removed.
1187
1374
  """
1188
- if grace_period is None:
1189
- grace_period = config.runtime_resources_deletion_grace_period
1190
- logger.info(
1191
- "Using default grace period for runtime resources deletion",
1192
- grace_period=grace_period,
1193
- )
1194
-
1195
1375
  params = {
1196
1376
  "label-selector": label_selector,
1197
1377
  "kind": kind,
1198
1378
  "object-id": object_id,
1199
1379
  "force": force,
1200
- "grace-period": grace_period,
1201
1380
  }
1381
+ if grace_period is not None:
1382
+ params["grace-period"] = grace_period
1202
1383
  error = "Failed deleting runtime resources"
1203
1384
  project_path = project if project else "*"
1204
1385
  response = self.api_call(
@@ -1236,7 +1417,9 @@ class HTTPRunDB(RunDBInterface):
1236
1417
  name="run_func_on_tuesdays",
1237
1418
  kind="job",
1238
1419
  scheduled_object=get_data_func,
1239
- cron_trigger=schemas.ScheduleCronTrigger(day_of_week='tue', hour=15, minute=30),
1420
+ cron_trigger=schemas.ScheduleCronTrigger(
1421
+ day_of_week="tue", hour=15, minute=30
1422
+ ),
1240
1423
  )
1241
1424
  db.create_schedule(project_name, schedule)
1242
1425
  """
@@ -1339,21 +1522,7 @@ class HTTPRunDB(RunDBInterface):
1339
1522
  :param builder_env: Kaniko builder pod env vars dict (for config/credentials)
1340
1523
  :param force_build: Force building the image, even when no changes were made
1341
1524
  """
1342
- is_s3_source = func.spec.build.source and func.spec.build.source.startswith(
1343
- "s3://"
1344
- )
1345
- is_ecr_image = mlrun.utils.is_ecr_url(config.httpdb.builder.docker_registry)
1346
- if not func.spec.build.load_source_on_run and is_s3_source and is_ecr_image:
1347
- logger.warning(
1348
- "Building a function image to ECR and loading an S3 source to the image may require conflicting access "
1349
- "keys. Only the permissions granted to the platform's configured secret will take affect "
1350
- "(see mlrun.config.config.httpdb.builder.docker_registry_secret). "
1351
- "In case the permissions are limited to ECR scope, you may use pull_at_runtime=True instead",
1352
- source=func.spec.build.source,
1353
- load_source_on_run=func.spec.build.load_source_on_run,
1354
- default_docker_registry=config.httpdb.builder.docker_registry,
1355
- )
1356
-
1525
+ self.warn_on_s3_and_ecr_permissions_conflict(func)
1357
1526
  try:
1358
1527
  req = {
1359
1528
  "function": func.to_dict(),
@@ -1372,10 +1541,94 @@ class HTTPRunDB(RunDBInterface):
1372
1541
 
1373
1542
  if not resp.ok:
1374
1543
  logger.error(f"bad resp!!\n{resp.text}")
1375
- raise ValueError("bad function run response")
1544
+ raise ValueError("bad submit build response")
1545
+
1546
+ return resp.json()
1547
+
1548
+ def deploy_nuclio_function(
1549
+ self,
1550
+ func: mlrun.runtimes.RemoteRuntime,
1551
+ builder_env: Optional[dict] = None,
1552
+ ):
1553
+ """
1554
+ Deploy a Nuclio function.
1555
+
1556
+ :param func: Function to build.
1557
+ :param builder_env: Kaniko builder pod env vars dict (for config/credentials)
1558
+ """
1559
+ func.metadata.project = func.metadata.project or config.default_project
1560
+ self.warn_on_s3_and_ecr_permissions_conflict(func)
1561
+ try:
1562
+ req = {
1563
+ "function": func.to_dict(),
1564
+ }
1565
+ if builder_env:
1566
+ req["builder_env"] = builder_env
1567
+ _path = (
1568
+ f"projects/{func.metadata.project}/nuclio/{func.metadata.name}/deploy"
1569
+ )
1570
+ resp = self.api_call("POST", _path, json=req)
1571
+ except OSError as err:
1572
+ logger.error(f"error submitting nuclio deploy task: {err_to_str(err)}")
1573
+ raise OSError(f"error: cannot submit deploy, {err_to_str(err)}")
1574
+
1575
+ if not resp.ok:
1576
+ logger.error(f"deploy nuclio - bad response:\n{resp.text}")
1577
+ raise ValueError("bad nuclio deploy response")
1376
1578
 
1377
1579
  return resp.json()
1378
1580
 
1581
+ def get_nuclio_deploy_status(
1582
+ self,
1583
+ func: mlrun.runtimes.RemoteRuntime,
1584
+ last_log_timestamp: float = 0.0,
1585
+ verbose: bool = False,
1586
+ ):
1587
+ """Retrieve the status of a deploy operation currently in progress.
1588
+
1589
+ :param func: Function object that is being built.
1590
+ :param last_log_timestamp: Last timestamp of logs that were already retrieved. Function will return only logs
1591
+ later than this parameter.
1592
+ :param verbose: Add verbose logs into the output.
1593
+
1594
+ :returns: The following parameters:
1595
+
1596
+ - Text of builder logs.
1597
+ - Timestamp of last log retrieved, to be used in subsequent calls to this function.
1598
+ """
1599
+
1600
+ try:
1601
+ normalized_name = normalize_name(func.metadata.name)
1602
+ params = {
1603
+ "name": normalized_name,
1604
+ "project": func.metadata.project,
1605
+ "tag": func.metadata.tag,
1606
+ "last_log_timestamp": str(last_log_timestamp),
1607
+ "verbose": bool2str(verbose),
1608
+ }
1609
+ _path = f"projects/{func.metadata.project}/nuclio/{normalized_name}/deploy"
1610
+ resp = self.api_call("GET", _path, params=params)
1611
+ except OSError as err:
1612
+ logger.error(f"error getting deploy status: {err_to_str(err)}")
1613
+ raise OSError(f"error: cannot get deploy status, {err_to_str(err)}")
1614
+
1615
+ if not resp.ok:
1616
+ logger.warning(f"failed resp, {resp.text}")
1617
+ raise RunDBError("bad function build response")
1618
+
1619
+ if resp.headers:
1620
+ last_log_timestamp = float(
1621
+ resp.headers.get("x-mlrun-last-timestamp", "0.0")
1622
+ )
1623
+ mlrun.runtimes.nuclio.function.enrich_nuclio_function_from_headers(
1624
+ func, resp.headers
1625
+ )
1626
+
1627
+ text = ""
1628
+ if resp.content:
1629
+ text = resp.content.decode()
1630
+ return text, last_log_timestamp
1631
+
1379
1632
  def get_builder_status(
1380
1633
  self,
1381
1634
  func: BaseRuntime,
@@ -1425,21 +1678,18 @@ class HTTPRunDB(RunDBInterface):
1425
1678
  last_log_timestamp = float(
1426
1679
  resp.headers.get("x-mlrun-last-timestamp", "0.0")
1427
1680
  )
1428
- if func.kind in mlrun.runtimes.RuntimeKinds.nuclio_runtimes():
1429
- func.status.address = resp.headers.get("x-mlrun-address", "")
1430
- func.status.nuclio_name = resp.headers.get("x-mlrun-name", "")
1431
- func.status.internal_invocation_urls = resp.headers.get(
1432
- "x-mlrun-internal-invocation-urls", ""
1433
- ).split(",")
1434
- func.status.external_invocation_urls = resp.headers.get(
1435
- "x-mlrun-external-invocation-urls", ""
1436
- ).split(",")
1437
- func.status.container_image = resp.headers.get(
1438
- "x-mlrun-container-image", ""
1681
+ if func.kind in mlrun.runtimes.RuntimeKinds.pure_nuclio_deployed_runtimes():
1682
+ mlrun.runtimes.nuclio.function.enrich_nuclio_function_from_headers(
1683
+ func, resp.headers
1439
1684
  )
1440
- else:
1441
- func.status.build_pod = resp.headers.get("builder_pod", "")
1442
- func.spec.image = resp.headers.get("function_image", "")
1685
+
1686
+ builder_pod = resp.headers.get("builder_pod", "")
1687
+ if builder_pod:
1688
+ func.status.build_pod = builder_pod
1689
+
1690
+ function_image = resp.headers.get("function_image", "")
1691
+ if function_image:
1692
+ func.spec.image = function_image
1443
1693
 
1444
1694
  text = ""
1445
1695
  if resp.content:
@@ -1502,7 +1752,7 @@ class HTTPRunDB(RunDBInterface):
1502
1752
  Retrieve updated information on project background tasks being executed.
1503
1753
  If no filter is provided, will return background tasks from the last week.
1504
1754
 
1505
- :param project: Project name (defaults to mlrun.config.config.default_project).
1755
+ :param project: Project name (defaults to mlrun.mlconf.default_project).
1506
1756
  :param state: List only background tasks whose state is specified.
1507
1757
  :param created_from: Filter by background task created time in ``[created_from, created_to]``.
1508
1758
  :param created_to: Filter by background task created time in ``[created_from, created_to]``.
@@ -1615,32 +1865,31 @@ class HTTPRunDB(RunDBInterface):
1615
1865
  artifact_path=None,
1616
1866
  ops=None,
1617
1867
  cleanup_ttl=None,
1868
+ timeout=60,
1618
1869
  ):
1619
1870
  """Submit a KFP pipeline for execution.
1620
1871
 
1621
- :param project: The project of the pipeline
1622
- :param pipeline: Pipeline function or path to .yaml/.zip pipeline file.
1623
- :param arguments: A dictionary of arguments to pass to the pipeline.
1624
- :param experiment: A name to assign for the specific experiment.
1625
- :param run: A name for this specific run.
1626
- :param namespace: Kubernetes namespace to execute the pipeline in.
1627
- :param artifact_path: A path to artifacts used by this pipeline.
1628
- :param ops: Transformers to apply on all ops in the pipeline.
1629
- :param cleanup_ttl: pipeline cleanup ttl in secs (time to wait after workflow completion, at which point the
1630
- workflow and all its resources are deleted)
1872
+ :param project: The project of the pipeline
1873
+ :param pipeline: Pipeline function or path to .yaml/.zip pipeline file.
1874
+ :param arguments: A dictionary of arguments to pass to the pipeline.
1875
+ :param experiment: A name to assign for the specific experiment.
1876
+ :param run: A name for this specific run.
1877
+ :param namespace: Kubernetes namespace to execute the pipeline in.
1878
+ :param artifact_path: A path to artifacts used by this pipeline.
1879
+ :param ops: Transformers to apply on all ops in the pipeline.
1880
+ :param cleanup_ttl: Pipeline cleanup ttl in secs (time to wait after workflow completion, at which point the
1881
+ workflow and all its resources are deleted)
1882
+ :param timeout: Timeout for the API call.
1631
1883
  """
1632
1884
 
1633
1885
  if isinstance(pipeline, str):
1634
1886
  pipe_file = pipeline
1635
1887
  else:
1636
- pipe_file = tempfile.NamedTemporaryFile(suffix=".yaml", delete=False).name
1637
- conf = new_pipe_metadata(
1888
+ pipe_file = compile_pipeline(
1638
1889
  artifact_path=artifact_path,
1639
1890
  cleanup_ttl=cleanup_ttl,
1640
- op_transformers=ops,
1641
- )
1642
- kfp.compiler.Compiler().compile(
1643
- pipeline, pipe_file, type_check=False, pipeline_conf=conf
1891
+ ops=ops,
1892
+ pipeline=pipeline,
1644
1893
  )
1645
1894
 
1646
1895
  if pipe_file.endswith(".yaml"):
@@ -1669,7 +1918,7 @@ class HTTPRunDB(RunDBInterface):
1669
1918
  "POST",
1670
1919
  f"projects/{project}/pipelines",
1671
1920
  params=params,
1672
- timeout=20,
1921
+ timeout=timeout,
1673
1922
  body=data,
1674
1923
  headers=headers,
1675
1924
  )
@@ -1695,8 +1944,8 @@ class HTTPRunDB(RunDBInterface):
1695
1944
  page_token: str = "",
1696
1945
  filter_: str = "",
1697
1946
  format_: Union[
1698
- str, mlrun.common.schemas.PipelinesFormat
1699
- ] = mlrun.common.schemas.PipelinesFormat.metadata_only,
1947
+ str, mlrun.common.formatters.PipelineFormat
1948
+ ] = mlrun.common.formatters.PipelineFormat.metadata_only,
1700
1949
  page_size: int = None,
1701
1950
  ) -> mlrun.common.schemas.PipelinesOutput:
1702
1951
  """Retrieve a list of KFP pipelines. This function can be invoked to get all pipelines from all projects,
@@ -1742,8 +1991,8 @@ class HTTPRunDB(RunDBInterface):
1742
1991
  namespace: str = None,
1743
1992
  timeout: int = 30,
1744
1993
  format_: Union[
1745
- str, mlrun.common.schemas.PipelinesFormat
1746
- ] = mlrun.common.schemas.PipelinesFormat.summary,
1994
+ str, mlrun.common.formatters.PipelineFormat
1995
+ ] = mlrun.common.formatters.PipelineFormat.summary,
1747
1996
  project: str = None,
1748
1997
  ):
1749
1998
  """Retrieve details of a specific pipeline using its run ID (as provided when the pipeline was executed)."""
@@ -1867,6 +2116,41 @@ class HTTPRunDB(RunDBInterface):
1867
2116
  resp = self.api_call("GET", path, error_message, params=params)
1868
2117
  return resp.json()["features"]
1869
2118
 
2119
+ def list_features_v2(
2120
+ self,
2121
+ project: str,
2122
+ name: str = None,
2123
+ tag: str = None,
2124
+ entities: list[str] = None,
2125
+ labels: list[str] = None,
2126
+ ) -> dict[str, list[dict]]:
2127
+ """List feature-sets which contain specific features. This function may return multiple versions of the same
2128
+ feature-set if a specific tag is not requested. Note that the various filters of this function actually
2129
+ refer to the feature-set object containing the features, not to the features themselves.
2130
+
2131
+ :param project: Project which contains these features.
2132
+ :param name: Name of the feature to look for. The name is used in a like query, and is not case-sensitive. For
2133
+ example, looking for ``feat`` will return features which are named ``MyFeature`` as well as ``defeat``.
2134
+ :param tag: Return feature-sets which contain the features looked for, and are tagged with the specific tag.
2135
+ :param entities: Return only feature-sets which contain an entity whose name is contained in this list.
2136
+ :param labels: Return only feature-sets which are labeled as requested.
2137
+ :returns: A list of features, and a list of their corresponding feature sets.
2138
+ """
2139
+
2140
+ project = project or config.default_project
2141
+ params = {
2142
+ "name": name,
2143
+ "tag": tag,
2144
+ "entity": entities or [],
2145
+ "label": labels or [],
2146
+ }
2147
+
2148
+ path = f"projects/{project}/features"
2149
+
2150
+ error_message = f"Failed listing features, project: {project}, query: {params}"
2151
+ resp = self.api_call("GET", path, error_message, params=params, version="v2")
2152
+ return resp.json()
2153
+
1870
2154
  def list_entities(
1871
2155
  self,
1872
2156
  project: str,
@@ -1892,6 +2176,31 @@ class HTTPRunDB(RunDBInterface):
1892
2176
  resp = self.api_call("GET", path, error_message, params=params)
1893
2177
  return resp.json()["entities"]
1894
2178
 
2179
+ def list_entities_v2(
2180
+ self,
2181
+ project: str,
2182
+ name: str = None,
2183
+ tag: str = None,
2184
+ labels: list[str] = None,
2185
+ ) -> dict[str, list[dict]]:
2186
+ """Retrieve a list of entities and their mapping to the containing feature-sets. This function is similar
2187
+ to the :py:func:`~list_features_v2` function, and uses the same logic. However, the entities are matched
2188
+ against the name rather than the features.
2189
+ """
2190
+
2191
+ project = project or config.default_project
2192
+ params = {
2193
+ "name": name,
2194
+ "tag": tag,
2195
+ "label": labels or [],
2196
+ }
2197
+
2198
+ path = f"projects/{project}/entities"
2199
+
2200
+ error_message = f"Failed listing entities, project: {project}, query: {params}"
2201
+ resp = self.api_call("GET", path, error_message, params=params, version="v2")
2202
+ return resp.json()
2203
+
1895
2204
  @staticmethod
1896
2205
  def _generate_partition_by_params(
1897
2206
  partition_by_cls,
@@ -1928,6 +2237,9 @@ class HTTPRunDB(RunDBInterface):
1928
2237
  partition_order: Union[
1929
2238
  mlrun.common.schemas.OrderType, str
1930
2239
  ] = mlrun.common.schemas.OrderType.desc,
2240
+ format_: Union[
2241
+ str, mlrun.common.formatters.FeatureSetFormat
2242
+ ] = mlrun.common.formatters.FeatureSetFormat.full,
1931
2243
  ) -> list[FeatureSet]:
1932
2244
  """Retrieve a list of feature-sets matching the criteria provided.
1933
2245
 
@@ -1945,6 +2257,9 @@ class HTTPRunDB(RunDBInterface):
1945
2257
  :param partition_sort_by: What field to sort the results by, within each partition defined by `partition_by`.
1946
2258
  Currently the only allowed value are `created` and `updated`.
1947
2259
  :param partition_order: Order of sorting within partitions - `asc` or `desc`. Default is `desc`.
2260
+ :param format_: Format of the results. Possible values are:
2261
+ - ``minimal`` - Return minimal feature set objects, not including stats and preview for each feature set.
2262
+ - ``full`` - Return full feature set objects.
1948
2263
  :returns: List of matching :py:class:`~mlrun.feature_store.FeatureSet` objects.
1949
2264
  """
1950
2265
 
@@ -1957,6 +2272,7 @@ class HTTPRunDB(RunDBInterface):
1957
2272
  "entity": entities or [],
1958
2273
  "feature": features or [],
1959
2274
  "label": labels or [],
2275
+ "format": format_,
1960
2276
  }
1961
2277
  if partition_by:
1962
2278
  params.update(
@@ -2043,7 +2359,7 @@ class HTTPRunDB(RunDBInterface):
2043
2359
  not a full object.
2044
2360
  Example::
2045
2361
 
2046
- feature_set_update = {"status": {"processed" : True}}
2362
+ feature_set_update = {"status": {"processed": True}}
2047
2363
 
2048
2364
  Will apply the field ``status.processed`` to the existing object.
2049
2365
  :param project: Project which contains the modified object.
@@ -2385,8 +2701,8 @@ class HTTPRunDB(RunDBInterface):
2385
2701
  self,
2386
2702
  owner: str = None,
2387
2703
  format_: Union[
2388
- str, mlrun.common.schemas.ProjectsFormat
2389
- ] = mlrun.common.schemas.ProjectsFormat.name_only,
2704
+ str, mlrun.common.formatters.ProjectFormat
2705
+ ] = mlrun.common.formatters.ProjectFormat.name_only,
2390
2706
  labels: list[str] = None,
2391
2707
  state: Union[str, mlrun.common.schemas.ProjectState] = None,
2392
2708
  ) -> list[Union[mlrun.projects.MlrunProject, str]]:
@@ -2412,7 +2728,7 @@ class HTTPRunDB(RunDBInterface):
2412
2728
 
2413
2729
  error_message = f"Failed listing projects, query: {params}"
2414
2730
  response = self.api_call("GET", "projects", error_message, params=params)
2415
- if format_ == mlrun.common.schemas.ProjectsFormat.name_only:
2731
+ if format_ == mlrun.common.formatters.ProjectFormat.name_only:
2416
2732
  # projects is just a list of strings
2417
2733
  return response.json()["projects"]
2418
2734
 
@@ -2440,7 +2756,7 @@ class HTTPRunDB(RunDBInterface):
2440
2756
  deletion_strategy: Union[
2441
2757
  str, mlrun.common.schemas.DeletionStrategy
2442
2758
  ] = mlrun.common.schemas.DeletionStrategy.default(),
2443
- ):
2759
+ ) -> None:
2444
2760
  """Delete a project.
2445
2761
 
2446
2762
  :param name: Name of the project to delete.
@@ -2459,7 +2775,7 @@ class HTTPRunDB(RunDBInterface):
2459
2775
  "DELETE", f"projects/{name}", error_message, headers=headers, version="v2"
2460
2776
  )
2461
2777
  if response.status_code == http.HTTPStatus.ACCEPTED:
2462
- logger.info("Project is being deleted", project_name=name)
2778
+ logger.info("Waiting for project to be deleted", project_name=name)
2463
2779
  background_task = mlrun.common.schemas.BackgroundTask(**response.json())
2464
2780
  background_task = self._wait_for_background_task_to_reach_terminal_state(
2465
2781
  background_task.metadata.name
@@ -2469,10 +2785,17 @@ class HTTPRunDB(RunDBInterface):
2469
2785
  == mlrun.common.schemas.BackgroundTaskState.succeeded
2470
2786
  ):
2471
2787
  logger.info("Project deleted", project_name=name)
2472
- return
2788
+ elif (
2789
+ background_task.status.state
2790
+ == mlrun.common.schemas.BackgroundTaskState.failed
2791
+ ):
2792
+ logger.error(
2793
+ "Project deletion failed",
2794
+ project_name=name,
2795
+ error=background_task.status.error,
2796
+ )
2473
2797
  elif response.status_code == http.HTTPStatus.NO_CONTENT:
2474
2798
  logger.info("Project deleted", project_name=name)
2475
- return
2476
2799
 
2477
2800
  def store_project(
2478
2801
  self,
@@ -2617,11 +2940,11 @@ class HTTPRunDB(RunDBInterface):
2617
2940
  :param secrets: A set of secret values to store.
2618
2941
  Example::
2619
2942
 
2620
- secrets = {'password': 'myPassw0rd', 'aws_key': '111222333'}
2943
+ secrets = {"password": "myPassw0rd", "aws_key": "111222333"}
2621
2944
  db.create_project_secrets(
2622
2945
  "project1",
2623
2946
  provider=mlrun.common.schemas.SecretProviderName.kubernetes,
2624
- secrets=secrets
2947
+ secrets=secrets,
2625
2948
  )
2626
2949
  """
2627
2950
  path = f"projects/{project}/secrets"
@@ -2917,14 +3240,12 @@ class HTTPRunDB(RunDBInterface):
2917
3240
  :param labels: A list of labels to filter by. Label filters work by either filtering a specific value of a
2918
3241
  label (i.e. list("key=value")) or by looking for the existence of a given key (i.e. "key")
2919
3242
  :param metrics: A list of metrics to return for each endpoint, read more in 'TimeMetric'
2920
- :param start: The start time of the metrics. Can be represented by a string containing an RFC 3339
2921
- time, a Unix timestamp in milliseconds, a relative time (`'now'` or
2922
- `'now-[0-9]+[mhd]'`, where `m` = minutes, `h` = hours, and `'d'` =
2923
- days), or 0 for the earliest time.
2924
- :param end: The end time of the metrics. Can be represented by a string containing an RFC 3339
2925
- time, a Unix timestamp in milliseconds, a relative time (`'now'` or
2926
- `'now-[0-9]+[mhd]'`, where `m` = minutes, `h` = hours, and `'d'` =
2927
- days), or 0 for the earliest time.
3243
+ :param start: The start time of the metrics. Can be represented by a string containing an RFC 3339 time, a
3244
+ Unix timestamp in milliseconds, a relative time (`'now'` or `'now-[0-9]+[mhd]'`, where
3245
+ `m` = minutes, `h` = hours, `'d'` = days, and `'s'` = seconds), or 0 for the earliest time.
3246
+ :param end: The end time of the metrics. Can be represented by a string containing an RFC 3339 time, a
3247
+ Unix timestamp in milliseconds, a relative time (`'now'` or `'now-[0-9]+[mhd]'`, where
3248
+ `m` = minutes, `h` = hours, `'d'` = days, and `'s'` = seconds), or 0 for the earliest time.
2928
3249
  :param top_level: if true will return only routers and endpoint that are NOT children of any router
2929
3250
  :param uids: if passed will return a list `ModelEndpoint` object with uid in uids
2930
3251
  """
@@ -2973,13 +3294,13 @@ class HTTPRunDB(RunDBInterface):
2973
3294
  :param project: The name of the project
2974
3295
  :param endpoint_id: The unique id of the model endpoint.
2975
3296
  :param start: The start time of the metrics. Can be represented by a string containing an
2976
- RFC 3339 time, a Unix timestamp in milliseconds, a relative time (`'now'` or
2977
- `'now-[0-9]+[mhd]'`, where `m` = minutes, `h` = hours, and `'d'` = days), or
2978
- 0 for the earliest time.
3297
+ RFC 3339 time, a Unix timestamp in milliseconds, a relative time
3298
+ (`'now'` or `'now-[0-9]+[mhd]'`, where `m` = minutes, `h` = hours,
3299
+ `'d'` = days, and `'s'` = seconds), or 0 for the earliest time.
2979
3300
  :param end: The end time of the metrics. Can be represented by a string containing an
2980
- RFC 3339 time, a Unix timestamp in milliseconds, a relative time (`'now'` or
2981
- `'now-[0-9]+[mhd]'`, where `m` = minutes, `h` = hours, and `'d'` = days), or
2982
- 0 for the earliest time.
3301
+ RFC 3339 time, a Unix timestamp in milliseconds, a relative time
3302
+ (`'now'` or `'now-[0-9]+[mhd]'`, where `m` = minutes, `h` = hours,
3303
+ `'d'` = days, and `'s'` = seconds), or 0 for the earliest time.
2983
3304
  :param metrics: A list of metrics to return for the model endpoint. There are pre-defined
2984
3305
  metrics for model endpoints such as predictions_per_second and
2985
3306
  latency_avg_5m but also custom metrics defined by the user. Please note that
@@ -2988,7 +3309,7 @@ class HTTPRunDB(RunDBInterface):
2988
3309
  :param feature_analysis: When True, the base feature statistics and current feature statistics will
2989
3310
  be added to the output of the resulting object.
2990
3311
 
2991
- :return: A `ModelEndpoint` object.
3312
+ :returns: A `ModelEndpoint` object.
2992
3313
  """
2993
3314
 
2994
3315
  path = f"projects/{project}/model-endpoints/{endpoint_id}"
@@ -3049,41 +3370,12 @@ class HTTPRunDB(RunDBInterface):
3049
3370
  params=attributes,
3050
3371
  )
3051
3372
 
3052
- def deploy_monitoring_batch_job(
3053
- self,
3054
- project: str = "",
3055
- default_batch_image: str = "mlrun/mlrun",
3056
- with_schedule: bool = False,
3057
- ):
3058
- """
3059
- Submit model monitoring batch job. By default, submit only the batch job as ML function without scheduling.
3060
- To submit a scheduled job as well, please set with_schedule = True.
3061
-
3062
- :param project: Project name.
3063
- :param default_batch_image: The default image of the model monitoring batch job. By default, the image
3064
- is mlrun/mlrun.
3065
- :param with_schedule: If true, submit the model monitoring scheduled job as well.
3066
-
3067
-
3068
- :returns: model monitoring batch job as a dictionary. You can easily convert the returned function into a
3069
- runtime object by calling ~mlrun.new_function.
3070
- """
3071
-
3072
- params = {
3073
- "default_batch_image": default_batch_image,
3074
- "with_schedule": with_schedule,
3075
- }
3076
- path = f"projects/{project}/jobs/batch-monitoring"
3077
-
3078
- resp = self.api_call(method="POST", path=path, params=params)
3079
- return resp.json()["func"]
3080
-
3081
3373
  def update_model_monitoring_controller(
3082
3374
  self,
3083
3375
  project: str,
3084
3376
  base_period: int = 10,
3085
3377
  image: str = "mlrun/mlrun",
3086
- ):
3378
+ ) -> None:
3087
3379
  """
3088
3380
  Redeploy model monitoring application controller function.
3089
3381
 
@@ -3093,20 +3385,24 @@ class HTTPRunDB(RunDBInterface):
3093
3385
  :param image: The image of the model monitoring controller function.
3094
3386
  By default, the image is mlrun/mlrun.
3095
3387
  """
3096
-
3097
- params = {
3098
- "image": image,
3099
- "base_period": base_period,
3100
- }
3101
- path = f"projects/{project}/model-monitoring/model-monitoring-controller"
3102
- self.api_call(method="POST", path=path, params=params)
3388
+ self.api_call(
3389
+ method=mlrun.common.types.HTTPMethod.PATCH,
3390
+ path=f"projects/{project}/model-monitoring/model-monitoring-controller",
3391
+ params={
3392
+ "base_period": base_period,
3393
+ "image": image,
3394
+ },
3395
+ )
3103
3396
 
3104
3397
  def enable_model_monitoring(
3105
3398
  self,
3106
3399
  project: str,
3107
3400
  base_period: int = 10,
3108
3401
  image: str = "mlrun/mlrun",
3109
- ):
3402
+ deploy_histogram_data_drift_app: bool = True,
3403
+ rebuild_images: bool = False,
3404
+ fetch_credentials_from_sys_config: bool = False,
3405
+ ) -> None:
3110
3406
  """
3111
3407
  Deploy model monitoring application controller, writer and stream functions.
3112
3408
  While the main goal of the controller function is to handle the monitoring processing and triggering
@@ -3115,21 +3411,169 @@ class HTTPRunDB(RunDBInterface):
3115
3411
  The stream function goal is to monitor the log of the data stream. It is triggered when a new log entry
3116
3412
  is detected. It processes the new events into statistics that are then written to statistics databases.
3117
3413
 
3414
+ :param project: Project name.
3415
+ :param base_period: The time period in minutes in which the model monitoring controller
3416
+ function triggers. By default, the base period is 10 minutes.
3417
+ :param image: The image of the model monitoring controller, writer & monitoring
3418
+ stream functions, which are real time nuclio functions.
3419
+ By default, the image is mlrun/mlrun.
3420
+ :param deploy_histogram_data_drift_app: If true, deploy the default histogram-based data drift application.
3421
+ :param rebuild_images: If true, force rebuild of model monitoring infrastructure images.
3422
+ :param fetch_credentials_from_sys_config: If true, fetch the credentials from the system configuration.
3118
3423
 
3119
- :param project: Project name.
3120
- :param base_period: The time period in minutes in which the model monitoring controller function
3121
- triggers. By default, the base period is 10 minutes.
3122
- :param image: The image of the model monitoring controller, writer & monitoring
3123
- stream functions, which are real time nuclio functions.
3124
- By default, the image is mlrun/mlrun.
3125
3424
  """
3425
+ self.api_call(
3426
+ method=mlrun.common.types.HTTPMethod.POST,
3427
+ path=f"projects/{project}/model-monitoring/enable-model-monitoring",
3428
+ params={
3429
+ "base_period": base_period,
3430
+ "image": image,
3431
+ "deploy_histogram_data_drift_app": deploy_histogram_data_drift_app,
3432
+ "rebuild_images": rebuild_images,
3433
+ "fetch_credentials_from_sys_config": fetch_credentials_from_sys_config,
3434
+ },
3435
+ )
3126
3436
 
3127
- params = {
3128
- "base_period": base_period,
3129
- "image": image,
3130
- }
3131
- path = f"projects/{project}/model-monitoring/enable-model-monitoring"
3132
- self.api_call(method="POST", path=path, params=params)
3437
+ def disable_model_monitoring(
3438
+ self,
3439
+ project: str,
3440
+ delete_resources: bool = True,
3441
+ delete_stream_function: bool = False,
3442
+ delete_histogram_data_drift_app: bool = True,
3443
+ delete_user_applications: bool = False,
3444
+ user_application_list: list[str] = None,
3445
+ ) -> bool:
3446
+ """
3447
+ Disable model monitoring application controller, writer, stream, histogram data drift application
3448
+ and the user's applications functions, according to the given params.
3449
+
3450
+ :param project: Project name.
3451
+ :param delete_resources: If True, it would delete the model monitoring controller & writer
3452
+ functions. Default True
3453
+ :param delete_stream_function: If True, it would delete model monitoring stream function,
3454
+ need to use wisely because if you're deleting this function
3455
+ this can cause data loss in case you will want to
3456
+ enable the model monitoring capability to the project.
3457
+ Default False.
3458
+ :param delete_histogram_data_drift_app: If True, it would delete the default histogram-based data drift
3459
+ application. Default False.
3460
+ :param delete_user_applications: If True, it would delete the user's model monitoring
3461
+ application according to user_application_list, Default False.
3462
+ :param user_application_list: List of the user's model monitoring application to disable.
3463
+ Default all the applications.
3464
+ Note: you have to set delete_user_applications to True
3465
+ in order to delete the desired application.
3466
+
3467
+ :returns: True if the deletion was successful, False otherwise.
3468
+ """
3469
+ response = self.api_call(
3470
+ method=mlrun.common.types.HTTPMethod.DELETE,
3471
+ path=f"projects/{project}/model-monitoring/disable-model-monitoring",
3472
+ params={
3473
+ "delete_resources": delete_resources,
3474
+ "delete_stream_function": delete_stream_function,
3475
+ "delete_histogram_data_drift_app": delete_histogram_data_drift_app,
3476
+ "delete_user_applications": delete_user_applications,
3477
+ "user_application_list": user_application_list,
3478
+ },
3479
+ )
3480
+ deletion_failed = False
3481
+ if response.status_code == http.HTTPStatus.ACCEPTED:
3482
+ if delete_resources:
3483
+ logger.info(
3484
+ "Model Monitoring is being disabled",
3485
+ project_name=project,
3486
+ )
3487
+ if delete_user_applications:
3488
+ logger.info("User applications are being deleted", project_name=project)
3489
+ background_tasks = mlrun.common.schemas.BackgroundTaskList(
3490
+ **response.json()
3491
+ ).background_tasks
3492
+ for task in background_tasks:
3493
+ task = self._wait_for_background_task_to_reach_terminal_state(
3494
+ task.metadata.name, project=project
3495
+ )
3496
+ if (
3497
+ task.status.state
3498
+ == mlrun.common.schemas.BackgroundTaskState.succeeded
3499
+ ):
3500
+ continue
3501
+ elif (
3502
+ task.status.state == mlrun.common.schemas.BackgroundTaskState.failed
3503
+ ):
3504
+ deletion_failed = True
3505
+ return not deletion_failed
3506
+
3507
+ def delete_model_monitoring_function(
3508
+ self, project: str, functions: list[str]
3509
+ ) -> bool:
3510
+ """
3511
+ Delete a model monitoring application.
3512
+
3513
+ :param functions: List of the model monitoring function to delete.
3514
+ :param project: Project name.
3515
+
3516
+ :returns: True if the deletion was successful, False otherwise.
3517
+ """
3518
+ response = self.api_call(
3519
+ method=mlrun.common.types.HTTPMethod.DELETE,
3520
+ path=f"projects/{project}/model-monitoring/functions",
3521
+ params={"functions": functions},
3522
+ )
3523
+ deletion_failed = False
3524
+ if response.status_code == http.HTTPStatus.ACCEPTED:
3525
+ logger.info("User applications are being deleted", project_name=project)
3526
+ background_tasks = mlrun.common.schemas.BackgroundTaskList(
3527
+ **response.json()
3528
+ ).background_tasks
3529
+ for task in background_tasks:
3530
+ task = self._wait_for_background_task_to_reach_terminal_state(
3531
+ task.metadata.name, project=project
3532
+ )
3533
+ if (
3534
+ task.status.state
3535
+ == mlrun.common.schemas.BackgroundTaskState.succeeded
3536
+ ):
3537
+ continue
3538
+ elif (
3539
+ task.status.state == mlrun.common.schemas.BackgroundTaskState.failed
3540
+ ):
3541
+ deletion_failed = True
3542
+ return not deletion_failed
3543
+
3544
+ def deploy_histogram_data_drift_app(
3545
+ self, project: str, image: str = "mlrun/mlrun"
3546
+ ) -> None:
3547
+ """
3548
+ Deploy the histogram data drift application.
3549
+
3550
+ :param project: Project name.
3551
+ :param image: The image on which the application will run.
3552
+ """
3553
+ self.api_call(
3554
+ method=mlrun.common.types.HTTPMethod.POST,
3555
+ path=f"projects/{project}/model-monitoring/deploy-histogram-data-drift-app",
3556
+ params={"image": image},
3557
+ )
3558
+
3559
+ def set_model_monitoring_credentials(
3560
+ self,
3561
+ project: str,
3562
+ credentials: dict[str, str],
3563
+ replace_creds: bool,
3564
+ ) -> None:
3565
+ """
3566
+ Set the credentials for the model monitoring application.
3567
+
3568
+ :param project: Project name.
3569
+ :param credentials: Credentials to set.
3570
+ :param replace_creds: If True, will override the existing credentials.
3571
+ """
3572
+ self.api_call(
3573
+ method=mlrun.common.types.HTTPMethod.POST,
3574
+ path=f"projects/{project}/model-monitoring/set-model-monitoring-credentials",
3575
+ params={**credentials, "replace_creds": replace_creds},
3576
+ )
3133
3577
 
3134
3578
  def create_hub_source(
3135
3579
  self, source: Union[dict, mlrun.common.schemas.IndexedHubSource]
@@ -3160,8 +3604,10 @@ class HTTPRunDB(RunDBInterface):
3160
3604
  metadata=mlrun.common.schemas.HubObjectMetadata(
3161
3605
  name="priv", description="a private source"
3162
3606
  ),
3163
- spec=mlrun.common.schemas.HubSourceSpec(path="/local/path/to/source", channel="development")
3164
- )
3607
+ spec=mlrun.common.schemas.HubSourceSpec(
3608
+ path="/local/path/to/source", channel="development"
3609
+ ),
3610
+ ),
3165
3611
  )
3166
3612
  db.create_hub_source(private_source)
3167
3613
 
@@ -3175,9 +3621,9 @@ class HTTPRunDB(RunDBInterface):
3175
3621
  spec=mlrun.common.schemas.HubSourceSpec(
3176
3622
  path="/local/path/to/source/2",
3177
3623
  channel="development",
3178
- credentials={...}
3179
- )
3180
- )
3624
+ credentials={...},
3625
+ ),
3626
+ ),
3181
3627
  )
3182
3628
  db.create_hub_source(another_source)
3183
3629
 
@@ -3341,7 +3787,7 @@ class HTTPRunDB(RunDBInterface):
3341
3787
  :param version: Get a specific version of the item. Default is ``None``.
3342
3788
  :param tag: Get a specific version of the item identified by tag. Default is ``latest``.
3343
3789
 
3344
- :return: http response with the asset in the content attribute
3790
+ :returns: http response with the asset in the content attribute
3345
3791
  """
3346
3792
  path = f"hub/sources/{source_name}/items/{item_name}/assets/{asset_name}"
3347
3793
  params = {
@@ -3369,24 +3815,81 @@ class HTTPRunDB(RunDBInterface):
3369
3815
  body=dict_to_json(authorization_verification_input.dict()),
3370
3816
  )
3371
3817
 
3372
- def list_api_gateways(self, project=None):
3818
+ def list_api_gateways(self, project=None) -> mlrun.common.schemas.APIGatewaysOutput:
3373
3819
  """
3374
3820
  Returns a list of Nuclio api gateways
3375
- :param project: optional str parameter to filter by project, if not passed, default Nuclio's value is taken
3376
3821
 
3377
- :return: json with the list of Nuclio Api Gateways
3378
- (json example is here
3379
- https://github.com/nuclio/nuclio/blob/development/docs/reference/api/README.md#listing-all-api-gateways)
3822
+ :param project: optional str parameter to filter by project, if not passed, default project value is taken
3823
+
3824
+ :returns: :py:class:`~mlrun.common.schemas.APIGateways`.
3380
3825
  """
3381
3826
  project = project or config.default_project
3382
3827
  error = "list api gateways"
3383
- endpoint_path = f"projects/{project}/nuclio/api-gateways"
3384
- resp = self.api_call("GET", endpoint_path, error)
3385
- return resp.json()
3828
+ endpoint_path = f"projects/{project}/api-gateways"
3829
+ response = self.api_call("GET", endpoint_path, error)
3830
+ return mlrun.common.schemas.APIGatewaysOutput(**response.json())
3831
+
3832
+ def get_api_gateway(self, name, project=None) -> mlrun.common.schemas.APIGateway:
3833
+ """
3834
+ Returns an API gateway
3835
+
3836
+ :param name: API gateway name
3837
+ :param project: optional str parameter to filter by project, if not passed, default project value is taken
3838
+
3839
+ :returns: :py:class:`~mlrun.common.schemas.APIGateway`.
3840
+ """
3841
+ project = project or config.default_project
3842
+ error = "get api gateway"
3843
+ endpoint_path = f"projects/{project}/api-gateways/{name}"
3844
+ response = self.api_call("GET", endpoint_path, error)
3845
+ return mlrun.common.schemas.APIGateway(**response.json())
3846
+
3847
+ def delete_api_gateway(self, name, project=None):
3848
+ """
3849
+ Deletes an API gateway
3850
+
3851
+ :param name: API gateway name
3852
+ :param project: Project name
3853
+ """
3854
+ project = project or config.default_project
3855
+ error = "delete api gateway"
3856
+ endpoint_path = f"projects/{project}/api-gateways/{name}"
3857
+ self.api_call("DELETE", endpoint_path, error)
3858
+
3859
+ def store_api_gateway(
3860
+ self,
3861
+ api_gateway: Union[
3862
+ mlrun.common.schemas.APIGateway,
3863
+ mlrun.runtimes.nuclio.api_gateway.APIGateway,
3864
+ ],
3865
+ project: Optional[str] = None,
3866
+ ) -> mlrun.common.schemas.APIGateway:
3867
+ """
3868
+ Stores an API Gateway.
3869
+
3870
+ :param api_gateway: :py:class:`~mlrun.runtimes.nuclio.APIGateway`
3871
+ or :py:class:`~mlrun.common.schemas.APIGateway`: API Gateway entity.
3872
+ :param project: project name. Mandatory if api_gateway is mlrun.common.schemas.APIGateway.
3873
+
3874
+ :returns: :py:class:`~mlrun.common.schemas.APIGateway`.
3875
+ """
3876
+
3877
+ if isinstance(api_gateway, mlrun.runtimes.nuclio.api_gateway.APIGateway):
3878
+ api_gateway = api_gateway.to_scheme()
3879
+ endpoint_path = f"projects/{project}/api-gateways/{api_gateway.metadata.name}"
3880
+ error = "store api gateways"
3881
+ response = self.api_call(
3882
+ "PUT",
3883
+ endpoint_path,
3884
+ error,
3885
+ json=api_gateway.dict(exclude_none=True),
3886
+ )
3887
+ return mlrun.common.schemas.APIGateway(**response.json())
3386
3888
 
3387
3889
  def trigger_migrations(self) -> Optional[mlrun.common.schemas.BackgroundTask]:
3388
3890
  """Trigger migrations (will do nothing if no migrations are needed) and wait for them to finish if actually
3389
3891
  triggered
3892
+
3390
3893
  :returns: :py:class:`~mlrun.common.schemas.BackgroundTask`.
3391
3894
  """
3392
3895
  response = self.api_call(
@@ -3409,6 +3912,7 @@ class HTTPRunDB(RunDBInterface):
3409
3912
  ):
3410
3913
  """
3411
3914
  Set notifications on a run. This will override any existing notifications on the run.
3915
+
3412
3916
  :param project: Project containing the run.
3413
3917
  :param run_uid: UID of the run.
3414
3918
  :param notifications: List of notifications to set on the run. Default is an empty list.
@@ -3434,6 +3938,7 @@ class HTTPRunDB(RunDBInterface):
3434
3938
  ):
3435
3939
  """
3436
3940
  Set notifications on a schedule. This will override any existing notifications on the schedule.
3941
+
3437
3942
  :param project: Project containing the schedule.
3438
3943
  :param schedule_name: Name of the schedule.
3439
3944
  :param notifications: List of notifications to set on the schedule. Default is an empty list.
@@ -3465,6 +3970,16 @@ class HTTPRunDB(RunDBInterface):
3465
3970
  """
3466
3971
  pass
3467
3972
 
3973
+ def store_alert_notifications(
3974
+ self,
3975
+ session,
3976
+ notification_objects: list[mlrun.model.Notification],
3977
+ alert_id: str,
3978
+ project: str,
3979
+ mask_params: bool = True,
3980
+ ):
3981
+ pass
3982
+
3468
3983
  def submit_workflow(
3469
3984
  self,
3470
3985
  project: str,
@@ -3572,15 +4087,16 @@ class HTTPRunDB(RunDBInterface):
3572
4087
  ) -> str:
3573
4088
  """
3574
4089
  Loading a project remotely from the given source.
4090
+
3575
4091
  :param name: project name
3576
4092
  :param url: git or tar.gz or .zip sources archive path e.g.:
3577
- git://github.com/mlrun/demo-xgb-project.git
3578
- http://mysite/archived-project.zip
3579
- The git project should include the project yaml file.
4093
+ git://github.com/mlrun/demo-xgb-project.git
4094
+ http://mysite/archived-project.zip
4095
+ The git project should include the project yaml file.
3580
4096
  :param secrets: Secrets to store in project in order to load it from the provided url. For more
3581
- information see :py:func:`mlrun.load_project` function.
4097
+ information see :py:func:`mlrun.load_project` function.
3582
4098
  :param save_secrets: Whether to store secrets in the loaded project. Setting to False will cause waiting
3583
- for the process completion.
4099
+ for the process completion.
3584
4100
 
3585
4101
  :returns: The terminal state of load project process.
3586
4102
  """
@@ -3656,6 +4172,168 @@ class HTTPRunDB(RunDBInterface):
3656
4172
 
3657
4173
  self.api_call(method="PUT", path=_path, json=profile.dict())
3658
4174
 
4175
+ @staticmethod
4176
+ def warn_on_s3_and_ecr_permissions_conflict(func):
4177
+ is_s3_source = func.spec.build.source and func.spec.build.source.startswith(
4178
+ "s3://"
4179
+ )
4180
+ is_ecr_image = mlrun.utils.is_ecr_url(config.httpdb.builder.docker_registry)
4181
+ if not func.spec.build.load_source_on_run and is_s3_source and is_ecr_image:
4182
+ logger.warning(
4183
+ "Building a function image to ECR and loading an S3 source to the image may require conflicting access "
4184
+ "keys. Only the permissions granted to the platform's configured secret will take affect "
4185
+ "(see mlrun.mlconf.httpdb.builder.docker_registry_secret). "
4186
+ "In case the permissions are limited to ECR scope, you may use pull_at_runtime=True instead",
4187
+ source=func.spec.build.source,
4188
+ load_source_on_run=func.spec.build.load_source_on_run,
4189
+ default_docker_registry=config.httpdb.builder.docker_registry,
4190
+ )
4191
+
4192
+ def generate_event(
4193
+ self, name: str, event_data: Union[dict, mlrun.common.schemas.Event], project=""
4194
+ ):
4195
+ """
4196
+ Generate an event.
4197
+
4198
+ :param name: The name of the event.
4199
+ :param event_data: The data of the event.
4200
+ :param project: The project that the event belongs to.
4201
+ """
4202
+ if mlrun.mlconf.alerts.mode == mlrun.common.schemas.alert.AlertsModes.disabled:
4203
+ logger.warning("Alerts are disabled, event will not be generated")
4204
+
4205
+ project = project or config.default_project
4206
+ endpoint_path = f"projects/{project}/events/{name}"
4207
+ error_message = f"post event {project}/events/{name}"
4208
+ if isinstance(event_data, mlrun.common.schemas.Event):
4209
+ event_data = event_data.dict()
4210
+ self.api_call(
4211
+ "POST", endpoint_path, error_message, body=dict_to_json(event_data)
4212
+ )
4213
+
4214
+ def store_alert_config(
4215
+ self,
4216
+ alert_name: str,
4217
+ alert_data: Union[dict, AlertConfig],
4218
+ project="",
4219
+ ) -> AlertConfig:
4220
+ """
4221
+ Create/modify an alert.
4222
+
4223
+ :param alert_name: The name of the alert.
4224
+ :param alert_data: The data of the alert.
4225
+ :param project: The project that the alert belongs to.
4226
+ :returns: The created/modified alert.
4227
+ """
4228
+ if not alert_data:
4229
+ raise mlrun.errors.MLRunInvalidArgumentError("Alert data must be provided")
4230
+
4231
+ if mlrun.mlconf.alerts.mode == mlrun.common.schemas.alert.AlertsModes.disabled:
4232
+ logger.warning(
4233
+ "Alerts are disabled, alert will still be stored but will not be triggered"
4234
+ )
4235
+
4236
+ project = project or config.default_project
4237
+ endpoint_path = f"projects/{project}/alerts/{alert_name}"
4238
+ error_message = f"put alert {project}/alerts/{alert_name}"
4239
+ alert_instance = (
4240
+ alert_data
4241
+ if isinstance(alert_data, AlertConfig)
4242
+ else AlertConfig.from_dict(alert_data)
4243
+ )
4244
+ # Validation is necessary here because users can directly invoke this function
4245
+ # through `mlrun.get_run_db().store_alert_config()`.
4246
+ alert_instance.validate_required_fields()
4247
+
4248
+ alert_data = alert_instance.to_dict()
4249
+ body = _as_json(alert_data)
4250
+ response = self.api_call("PUT", endpoint_path, error_message, body=body)
4251
+ return AlertConfig.from_dict(response.json())
4252
+
4253
+ def get_alert_config(self, alert_name: str, project="") -> AlertConfig:
4254
+ """
4255
+ Retrieve an alert.
4256
+
4257
+ :param alert_name: The name of the alert to retrieve.
4258
+ :param project: The project that the alert belongs to.
4259
+
4260
+ :returns: The alert object.
4261
+ """
4262
+ project = project or config.default_project
4263
+ endpoint_path = f"projects/{project}/alerts/{alert_name}"
4264
+ error_message = f"get alert {project}/alerts/{alert_name}"
4265
+ response = self.api_call("GET", endpoint_path, error_message)
4266
+ return AlertConfig.from_dict(response.json())
4267
+
4268
+ def list_alerts_configs(self, project="") -> list[AlertConfig]:
4269
+ """
4270
+ Retrieve list of alerts of a project.
4271
+
4272
+ :param project: The project name.
4273
+
4274
+ :returns: All the alerts objects of the project.
4275
+ """
4276
+ project = project or config.default_project
4277
+ endpoint_path = f"projects/{project}/alerts"
4278
+ error_message = f"get alerts {project}/alerts"
4279
+ response = self.api_call("GET", endpoint_path, error_message).json()
4280
+ results = []
4281
+ for item in response:
4282
+ results.append(AlertConfig(**item))
4283
+ return results
4284
+
4285
+ def delete_alert_config(self, alert_name: str, project=""):
4286
+ """
4287
+ Delete an alert.
4288
+ :param alert_name: The name of the alert to delete.
4289
+ :param project: The project that the alert belongs to.
4290
+ """
4291
+ project = project or config.default_project
4292
+ endpoint_path = f"projects/{project}/alerts/{alert_name}"
4293
+ error_message = f"delete alert {project}/alerts/{alert_name}"
4294
+ self.api_call("DELETE", endpoint_path, error_message)
4295
+
4296
+ def reset_alert_config(self, alert_name: str, project=""):
4297
+ """
4298
+ Reset an alert.
4299
+
4300
+ :param alert_name: The name of the alert to reset.
4301
+ :param project: The project that the alert belongs to.
4302
+ """
4303
+ project = project or config.default_project
4304
+ endpoint_path = f"projects/{project}/alerts/{alert_name}/reset"
4305
+ error_message = f"post alert {project}/alerts/{alert_name}/reset"
4306
+ self.api_call("POST", endpoint_path, error_message)
4307
+
4308
+ def get_alert_template(
4309
+ self, template_name: str
4310
+ ) -> mlrun.common.schemas.AlertTemplate:
4311
+ """
4312
+ Retrieve a specific alert template.
4313
+
4314
+ :param template_name: The name of the template to retrieve.
4315
+
4316
+ :returns: The template object.
4317
+ """
4318
+ endpoint_path = f"alert-templates/{template_name}"
4319
+ error_message = f"get template alert-templates/{template_name}"
4320
+ response = self.api_call("GET", endpoint_path, error_message)
4321
+ return mlrun.common.schemas.AlertTemplate(**response.json())
4322
+
4323
+ def list_alert_templates(self) -> list[mlrun.common.schemas.AlertTemplate]:
4324
+ """
4325
+ Retrieve list of all alert templates.
4326
+
4327
+ :returns: All the alert template objects in the database.
4328
+ """
4329
+ endpoint_path = "alert-templates"
4330
+ error_message = "get templates /alert-templates"
4331
+ response = self.api_call("GET", endpoint_path, error_message).json()
4332
+ results = []
4333
+ for item in response:
4334
+ results.append(mlrun.common.schemas.AlertTemplate(**item))
4335
+ return results
4336
+
3659
4337
 
3660
4338
  def _as_json(obj):
3661
4339
  fn = getattr(obj, "to_json", None)