mlrun 1.7.0rc4__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 (200) hide show
  1. mlrun/__init__.py +11 -1
  2. mlrun/__main__.py +25 -111
  3. mlrun/{datastore/helpers.py → alerts/__init__.py} +2 -5
  4. mlrun/alerts/alert.py +144 -0
  5. mlrun/api/schemas/__init__.py +4 -3
  6. mlrun/artifacts/__init__.py +8 -3
  7. mlrun/artifacts/base.py +38 -254
  8. mlrun/artifacts/dataset.py +9 -190
  9. mlrun/artifacts/manager.py +41 -47
  10. mlrun/artifacts/model.py +30 -158
  11. mlrun/artifacts/plots.py +23 -380
  12. mlrun/common/constants.py +68 -0
  13. mlrun/common/formatters/__init__.py +19 -0
  14. mlrun/{model_monitoring/stores/models/sqlite.py → common/formatters/artifact.py} +6 -8
  15. mlrun/common/formatters/base.py +78 -0
  16. mlrun/common/formatters/function.py +41 -0
  17. mlrun/common/formatters/pipeline.py +53 -0
  18. mlrun/common/formatters/project.py +51 -0
  19. mlrun/{runtimes → common/runtimes}/constants.py +32 -4
  20. mlrun/common/schemas/__init__.py +25 -4
  21. mlrun/common/schemas/alert.py +203 -0
  22. mlrun/common/schemas/api_gateway.py +148 -0
  23. mlrun/common/schemas/artifact.py +15 -5
  24. mlrun/common/schemas/auth.py +8 -2
  25. mlrun/common/schemas/client_spec.py +2 -0
  26. mlrun/common/schemas/frontend_spec.py +1 -0
  27. mlrun/common/schemas/function.py +4 -0
  28. mlrun/common/schemas/hub.py +7 -9
  29. mlrun/common/schemas/model_monitoring/__init__.py +19 -3
  30. mlrun/common/schemas/model_monitoring/constants.py +96 -26
  31. mlrun/common/schemas/model_monitoring/grafana.py +9 -5
  32. mlrun/common/schemas/model_monitoring/model_endpoints.py +86 -2
  33. mlrun/{runtimes/mpijob/v1alpha1.py → common/schemas/pagination.py} +10 -13
  34. mlrun/common/schemas/pipeline.py +0 -9
  35. mlrun/common/schemas/project.py +22 -21
  36. mlrun/common/types.py +7 -1
  37. mlrun/config.py +87 -19
  38. mlrun/data_types/data_types.py +4 -0
  39. mlrun/data_types/to_pandas.py +9 -9
  40. mlrun/datastore/__init__.py +5 -8
  41. mlrun/datastore/alibaba_oss.py +130 -0
  42. mlrun/datastore/azure_blob.py +4 -5
  43. mlrun/datastore/base.py +69 -30
  44. mlrun/datastore/datastore.py +10 -2
  45. mlrun/datastore/datastore_profile.py +90 -6
  46. mlrun/datastore/google_cloud_storage.py +1 -1
  47. mlrun/datastore/hdfs.py +5 -0
  48. mlrun/datastore/inmem.py +2 -2
  49. mlrun/datastore/redis.py +2 -2
  50. mlrun/datastore/s3.py +5 -0
  51. mlrun/datastore/snowflake_utils.py +43 -0
  52. mlrun/datastore/sources.py +172 -44
  53. mlrun/datastore/store_resources.py +7 -7
  54. mlrun/datastore/targets.py +285 -41
  55. mlrun/datastore/utils.py +68 -5
  56. mlrun/datastore/v3io.py +27 -50
  57. mlrun/db/auth_utils.py +152 -0
  58. mlrun/db/base.py +149 -14
  59. mlrun/db/factory.py +1 -1
  60. mlrun/db/httpdb.py +608 -178
  61. mlrun/db/nopdb.py +191 -7
  62. mlrun/errors.py +11 -0
  63. mlrun/execution.py +37 -20
  64. mlrun/feature_store/__init__.py +0 -2
  65. mlrun/feature_store/api.py +21 -52
  66. mlrun/feature_store/feature_set.py +48 -23
  67. mlrun/feature_store/feature_vector.py +2 -1
  68. mlrun/feature_store/ingestion.py +7 -6
  69. mlrun/feature_store/retrieval/base.py +9 -4
  70. mlrun/feature_store/retrieval/conversion.py +9 -9
  71. mlrun/feature_store/retrieval/dask_merger.py +2 -0
  72. mlrun/feature_store/retrieval/job.py +9 -3
  73. mlrun/feature_store/retrieval/local_merger.py +2 -0
  74. mlrun/feature_store/retrieval/spark_merger.py +34 -24
  75. mlrun/feature_store/steps.py +30 -19
  76. mlrun/features.py +4 -13
  77. mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +7 -12
  78. mlrun/frameworks/auto_mlrun/auto_mlrun.py +2 -2
  79. mlrun/frameworks/lgbm/__init__.py +1 -1
  80. mlrun/frameworks/lgbm/callbacks/callback.py +2 -4
  81. mlrun/frameworks/lgbm/model_handler.py +1 -1
  82. mlrun/frameworks/parallel_coordinates.py +2 -1
  83. mlrun/frameworks/pytorch/__init__.py +2 -2
  84. mlrun/frameworks/sklearn/__init__.py +1 -1
  85. mlrun/frameworks/tf_keras/__init__.py +5 -2
  86. mlrun/frameworks/tf_keras/callbacks/logging_callback.py +1 -1
  87. mlrun/frameworks/tf_keras/mlrun_interface.py +2 -2
  88. mlrun/frameworks/xgboost/__init__.py +1 -1
  89. mlrun/k8s_utils.py +10 -11
  90. mlrun/launcher/__init__.py +1 -1
  91. mlrun/launcher/base.py +6 -5
  92. mlrun/launcher/client.py +8 -6
  93. mlrun/launcher/factory.py +1 -1
  94. mlrun/launcher/local.py +9 -3
  95. mlrun/launcher/remote.py +9 -3
  96. mlrun/lists.py +6 -2
  97. mlrun/model.py +58 -19
  98. mlrun/model_monitoring/__init__.py +1 -1
  99. mlrun/model_monitoring/api.py +127 -301
  100. mlrun/model_monitoring/application.py +5 -296
  101. mlrun/model_monitoring/applications/__init__.py +11 -0
  102. mlrun/model_monitoring/applications/_application_steps.py +157 -0
  103. mlrun/model_monitoring/applications/base.py +282 -0
  104. mlrun/model_monitoring/applications/context.py +214 -0
  105. mlrun/model_monitoring/applications/evidently_base.py +211 -0
  106. mlrun/model_monitoring/applications/histogram_data_drift.py +224 -93
  107. mlrun/model_monitoring/applications/results.py +99 -0
  108. mlrun/model_monitoring/controller.py +30 -36
  109. mlrun/model_monitoring/db/__init__.py +18 -0
  110. mlrun/model_monitoring/{stores → db/stores}/__init__.py +43 -36
  111. mlrun/model_monitoring/db/stores/base/__init__.py +15 -0
  112. mlrun/model_monitoring/{stores/model_endpoint_store.py → db/stores/base/store.py} +58 -32
  113. mlrun/model_monitoring/db/stores/sqldb/__init__.py +13 -0
  114. mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +71 -0
  115. mlrun/model_monitoring/{stores → db/stores/sqldb}/models/base.py +109 -5
  116. mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +88 -0
  117. mlrun/model_monitoring/{stores/models/mysql.py → db/stores/sqldb/models/sqlite.py} +19 -13
  118. mlrun/model_monitoring/db/stores/sqldb/sql_store.py +684 -0
  119. mlrun/model_monitoring/db/stores/v3io_kv/__init__.py +13 -0
  120. mlrun/model_monitoring/{stores/kv_model_endpoint_store.py → db/stores/v3io_kv/kv_store.py} +302 -155
  121. mlrun/model_monitoring/db/tsdb/__init__.py +100 -0
  122. mlrun/model_monitoring/db/tsdb/base.py +329 -0
  123. mlrun/model_monitoring/db/tsdb/helpers.py +30 -0
  124. mlrun/model_monitoring/db/tsdb/tdengine/__init__.py +15 -0
  125. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +240 -0
  126. mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +45 -0
  127. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +397 -0
  128. mlrun/model_monitoring/db/tsdb/v3io/__init__.py +15 -0
  129. mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +117 -0
  130. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +630 -0
  131. mlrun/model_monitoring/evidently_application.py +6 -118
  132. mlrun/model_monitoring/features_drift_table.py +34 -22
  133. mlrun/model_monitoring/helpers.py +100 -7
  134. mlrun/model_monitoring/model_endpoint.py +3 -2
  135. mlrun/model_monitoring/stream_processing.py +93 -228
  136. mlrun/model_monitoring/tracking_policy.py +7 -1
  137. mlrun/model_monitoring/writer.py +152 -124
  138. mlrun/package/packagers_manager.py +1 -0
  139. mlrun/package/utils/_formatter.py +2 -2
  140. mlrun/platforms/__init__.py +11 -10
  141. mlrun/platforms/iguazio.py +21 -202
  142. mlrun/projects/operations.py +30 -16
  143. mlrun/projects/pipelines.py +92 -99
  144. mlrun/projects/project.py +757 -268
  145. mlrun/render.py +15 -14
  146. mlrun/run.py +160 -162
  147. mlrun/runtimes/__init__.py +55 -3
  148. mlrun/runtimes/base.py +33 -19
  149. mlrun/runtimes/databricks_job/databricks_wrapper.py +1 -1
  150. mlrun/runtimes/funcdoc.py +0 -28
  151. mlrun/runtimes/kubejob.py +28 -122
  152. mlrun/runtimes/local.py +5 -2
  153. mlrun/runtimes/mpijob/__init__.py +0 -20
  154. mlrun/runtimes/mpijob/abstract.py +8 -8
  155. mlrun/runtimes/mpijob/v1.py +1 -1
  156. mlrun/runtimes/nuclio/__init__.py +1 -0
  157. mlrun/runtimes/nuclio/api_gateway.py +709 -0
  158. mlrun/runtimes/nuclio/application/__init__.py +15 -0
  159. mlrun/runtimes/nuclio/application/application.py +523 -0
  160. mlrun/runtimes/nuclio/application/reverse_proxy.go +95 -0
  161. mlrun/runtimes/nuclio/function.py +98 -58
  162. mlrun/runtimes/nuclio/serving.py +36 -42
  163. mlrun/runtimes/pod.py +196 -45
  164. mlrun/runtimes/remotesparkjob.py +1 -1
  165. mlrun/runtimes/sparkjob/spark3job.py +1 -1
  166. mlrun/runtimes/utils.py +6 -73
  167. mlrun/secrets.py +6 -2
  168. mlrun/serving/remote.py +2 -3
  169. mlrun/serving/routers.py +7 -4
  170. mlrun/serving/server.py +7 -8
  171. mlrun/serving/states.py +73 -43
  172. mlrun/serving/v2_serving.py +8 -7
  173. mlrun/track/tracker.py +2 -1
  174. mlrun/utils/async_http.py +25 -5
  175. mlrun/utils/helpers.py +141 -75
  176. mlrun/utils/http.py +1 -1
  177. mlrun/utils/logger.py +39 -7
  178. mlrun/utils/notifications/notification/__init__.py +14 -9
  179. mlrun/utils/notifications/notification/base.py +12 -0
  180. mlrun/utils/notifications/notification/console.py +2 -0
  181. mlrun/utils/notifications/notification/git.py +3 -1
  182. mlrun/utils/notifications/notification/ipython.py +2 -0
  183. mlrun/utils/notifications/notification/slack.py +101 -21
  184. mlrun/utils/notifications/notification/webhook.py +11 -1
  185. mlrun/utils/notifications/notification_pusher.py +147 -16
  186. mlrun/utils/retryer.py +3 -2
  187. mlrun/utils/v3io_clients.py +0 -1
  188. mlrun/utils/version/version.json +2 -2
  189. {mlrun-1.7.0rc4.dist-info → mlrun-1.7.0rc20.dist-info}/METADATA +33 -18
  190. mlrun-1.7.0rc20.dist-info/RECORD +353 -0
  191. {mlrun-1.7.0rc4.dist-info → mlrun-1.7.0rc20.dist-info}/WHEEL +1 -1
  192. mlrun/kfpops.py +0 -868
  193. mlrun/model_monitoring/batch.py +0 -974
  194. mlrun/model_monitoring/stores/models/__init__.py +0 -27
  195. mlrun/model_monitoring/stores/sql_model_endpoint_store.py +0 -382
  196. mlrun/platforms/other.py +0 -305
  197. mlrun-1.7.0rc4.dist-info/RECORD +0 -321
  198. {mlrun-1.7.0rc4.dist-info → mlrun-1.7.0rc20.dist-info}/LICENSE +0 -0
  199. {mlrun-1.7.0rc4.dist-info → mlrun-1.7.0rc20.dist-info}/entry_points.txt +0 -0
  200. {mlrun-1.7.0rc4.dist-info → mlrun-1.7.0rc20.dist-info}/top_level.txt +0 -0
mlrun/datastore/utils.py CHANGED
@@ -12,9 +12,11 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  #
15
+ import math
15
16
  import tarfile
16
17
  import tempfile
17
18
  import typing
19
+ import warnings
18
20
  from urllib.parse import parse_qs, urlparse
19
21
 
20
22
  import pandas as pd
@@ -23,24 +25,29 @@ import semver
23
25
  import mlrun.datastore
24
26
 
25
27
 
26
- def parse_kafka_url(url: str, bootstrap_servers: list = None) -> tuple[str, list]:
28
+ def parse_kafka_url(
29
+ url: str, brokers: typing.Union[list, str] = None
30
+ ) -> tuple[str, list]:
27
31
  """Generating Kafka topic and adjusting a list of bootstrap servers.
28
32
 
29
33
  :param url: URL path to parse using urllib.parse.urlparse.
30
- :param bootstrap_servers: List of bootstrap servers for the kafka brokers.
34
+ :param brokers: List of kafka brokers.
31
35
 
32
36
  :return: A tuple of:
33
37
  [0] = Kafka topic value
34
38
  [1] = List of bootstrap servers
35
39
  """
36
- bootstrap_servers = bootstrap_servers or []
40
+ brokers = brokers or []
41
+
42
+ if isinstance(brokers, str):
43
+ brokers = brokers.split(",")
37
44
 
38
45
  # Parse the provided URL into six components according to the general structure of a URL
39
46
  url = urlparse(url)
40
47
 
41
48
  # Add the network location to the bootstrap servers list
42
49
  if url.netloc:
43
- bootstrap_servers = [url.netloc] + bootstrap_servers
50
+ brokers = [url.netloc] + brokers
44
51
 
45
52
  # Get the topic value from the parsed url
46
53
  query_dict = parse_qs(url.query)
@@ -49,7 +56,7 @@ def parse_kafka_url(url: str, bootstrap_servers: list = None) -> tuple[str, list
49
56
  else:
50
57
  topic = url.path
51
58
  topic = topic.lstrip("/")
52
- return topic, bootstrap_servers
59
+ return topic, brokers
53
60
 
54
61
 
55
62
  def upload_tarball(source_dir, target, secrets=None):
@@ -159,3 +166,59 @@ def _generate_sql_query_with_time_filter(
159
166
  query = query.filter(getattr(table.c, time_column) <= end_time)
160
167
 
161
168
  return query, parse_dates
169
+
170
+
171
+ def get_kafka_brokers_from_dict(options: dict, pop=False) -> typing.Optional[str]:
172
+ get_or_pop = options.pop if pop else options.get
173
+ kafka_brokers = get_or_pop("kafka_brokers", None)
174
+ if kafka_brokers:
175
+ return kafka_brokers
176
+ kafka_bootstrap_servers = get_or_pop("kafka_bootstrap_servers", None)
177
+ if kafka_bootstrap_servers:
178
+ warnings.warn(
179
+ "The 'kafka_bootstrap_servers' parameter is deprecated and will be removed in "
180
+ "1.9.0. Please pass the 'kafka_brokers' parameter instead.",
181
+ FutureWarning,
182
+ )
183
+ return kafka_bootstrap_servers
184
+
185
+
186
+ def transform_list_filters_to_tuple(additional_filters):
187
+ tuple_filters = []
188
+ if not additional_filters:
189
+ return tuple_filters
190
+ validate_additional_filters(additional_filters)
191
+ for additional_filter in additional_filters:
192
+ tuple_filters.append(tuple(additional_filter))
193
+ return tuple_filters
194
+
195
+
196
+ def validate_additional_filters(additional_filters):
197
+ nan_error_message = "using NaN in additional_filters is not supported"
198
+ if additional_filters in [None, [], ()]:
199
+ return
200
+ for filter_tuple in additional_filters:
201
+ if filter_tuple == () or filter_tuple == []:
202
+ continue
203
+ if not isinstance(filter_tuple, (list, tuple)):
204
+ raise mlrun.errors.MLRunInvalidArgumentError(
205
+ f"mlrun supports additional_filters only as a list of tuples."
206
+ f" Current additional_filters: {additional_filters}"
207
+ )
208
+ if isinstance(filter_tuple[0], (list, tuple)):
209
+ raise mlrun.errors.MLRunInvalidArgumentError(
210
+ f"additional_filters does not support nested list inside filter tuples except in -in- logic."
211
+ f" Current filter_tuple: {filter_tuple}."
212
+ )
213
+ if len(filter_tuple) != 3:
214
+ raise mlrun.errors.MLRunInvalidArgumentError(
215
+ f"illegal filter tuple length, {filter_tuple} in additional filters:"
216
+ f" {additional_filters}"
217
+ )
218
+ col_name, op, value = filter_tuple
219
+ if isinstance(value, float) and math.isnan(value):
220
+ raise mlrun.errors.MLRunInvalidArgumentError(nan_error_message)
221
+ elif isinstance(value, (list, tuple)):
222
+ for sub_value in value:
223
+ if isinstance(sub_value, float) and math.isnan(sub_value):
224
+ raise mlrun.errors.MLRunInvalidArgumentError(nan_error_message)
mlrun/datastore/v3io.py CHANGED
@@ -12,8 +12,6 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- import mmap
16
- import os
17
15
  import time
18
16
  from datetime import datetime
19
17
 
@@ -22,7 +20,6 @@ import v3io
22
20
  from v3io.dataplane.response import HttpResponseError
23
21
 
24
22
  import mlrun
25
- from mlrun.datastore.helpers import ONE_GB, ONE_MB
26
23
 
27
24
  from ..platforms.iguazio import parse_path, split_path
28
25
  from .base import (
@@ -32,6 +29,7 @@ from .base import (
32
29
  )
33
30
 
34
31
  V3IO_LOCAL_ROOT = "v3io"
32
+ V3IO_DEFAULT_UPLOAD_CHUNK_SIZE = 1024 * 1024 * 10
35
33
 
36
34
 
37
35
  class V3ioStore(DataStore):
@@ -98,46 +96,28 @@ class V3ioStore(DataStore):
98
96
  )
99
97
  return self._sanitize_storage_options(res)
100
98
 
101
- def _upload(self, key: str, src_path: str, max_chunk_size: int = ONE_GB):
99
+ def _upload(
100
+ self,
101
+ key: str,
102
+ src_path: str,
103
+ max_chunk_size: int = V3IO_DEFAULT_UPLOAD_CHUNK_SIZE,
104
+ ):
102
105
  """helper function for upload method, allows for controlling max_chunk_size in testing"""
103
106
  container, path = split_path(self._join(key))
104
- file_size = os.path.getsize(src_path) # in bytes
105
- if file_size <= ONE_MB:
106
- with open(src_path, "rb") as source_file:
107
- data = source_file.read()
108
- self._do_object_request(
109
- self.object.put,
110
- container=container,
111
- path=path,
112
- body=data,
113
- append=False,
114
- )
115
- return
116
- # chunk must be a multiple of the ALLOCATIONGRANULARITY
117
- # https://docs.python.org/3/library/mmap.html
118
- if residue := max_chunk_size % mmap.ALLOCATIONGRANULARITY:
119
- # round down to the nearest multiple of ALLOCATIONGRANULARITY
120
- max_chunk_size -= residue
121
-
122
107
  with open(src_path, "rb") as file_obj:
123
- file_offset = 0
124
- while file_offset < file_size:
125
- chunk_size = min(file_size - file_offset, max_chunk_size)
126
- with mmap.mmap(
127
- file_obj.fileno(),
128
- length=chunk_size,
129
- access=mmap.ACCESS_READ,
130
- offset=file_offset,
131
- ) as mmap_obj:
132
- append = file_offset != 0
133
- self._do_object_request(
134
- self.object.put,
135
- container=container,
136
- path=path,
137
- body=mmap_obj,
138
- append=append,
139
- )
140
- file_offset += chunk_size
108
+ append = False
109
+ while True:
110
+ data = memoryview(file_obj.read(max_chunk_size))
111
+ if not data:
112
+ break
113
+ self._do_object_request(
114
+ self.object.put,
115
+ container=container,
116
+ path=path,
117
+ body=data,
118
+ append=append,
119
+ )
120
+ append = True
141
121
 
142
122
  def upload(self, key, src_path):
143
123
  return self._upload(key, src_path)
@@ -152,19 +132,16 @@ class V3ioStore(DataStore):
152
132
  num_bytes=size,
153
133
  ).body
154
134
 
155
- def _put(self, key, data, append=False, max_chunk_size: int = ONE_GB):
135
+ def _put(
136
+ self,
137
+ key,
138
+ data,
139
+ append=False,
140
+ max_chunk_size: int = V3IO_DEFAULT_UPLOAD_CHUNK_SIZE,
141
+ ):
156
142
  """helper function for put method, allows for controlling max_chunk_size in testing"""
157
143
  container, path = split_path(self._join(key))
158
144
  buffer_size = len(data) # in bytes
159
- if buffer_size <= ONE_MB:
160
- self._do_object_request(
161
- self.object.put,
162
- container=container,
163
- path=path,
164
- body=data,
165
- append=append,
166
- )
167
- return
168
145
  buffer_offset = 0
169
146
  try:
170
147
  data = memoryview(data)
mlrun/db/auth_utils.py ADDED
@@ -0,0 +1,152 @@
1
+ # Copyright 2024 Iguazio
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from abc import ABC, abstractmethod
16
+ from datetime import datetime, timedelta
17
+
18
+ import requests
19
+
20
+ import mlrun.errors
21
+ from mlrun.utils import logger
22
+
23
+
24
+ class TokenProvider(ABC):
25
+ @abstractmethod
26
+ def get_token(self):
27
+ pass
28
+
29
+ @abstractmethod
30
+ def is_iguazio_session(self):
31
+ pass
32
+
33
+
34
+ class StaticTokenProvider(TokenProvider):
35
+ def __init__(self, token: str):
36
+ self.token = token
37
+
38
+ def get_token(self):
39
+ return self.token
40
+
41
+ def is_iguazio_session(self):
42
+ return mlrun.platforms.iguazio.is_iguazio_session(self.token)
43
+
44
+
45
+ class OAuthClientIDTokenProvider(TokenProvider):
46
+ def __init__(
47
+ self, token_endpoint: str, client_id: str, client_secret: str, timeout=5
48
+ ):
49
+ if not token_endpoint or not client_id or not client_secret:
50
+ raise mlrun.errors.MLRunValueError(
51
+ "Invalid client_id configuration for authentication. Must provide token endpoint, client-id and secret"
52
+ )
53
+ self.token_endpoint = token_endpoint
54
+ self.client_id = client_id
55
+ self.client_secret = client_secret
56
+ self.timeout = timeout
57
+
58
+ # Since we're only issuing POST requests, which are actually a disguised GET, then it's ok to allow retries
59
+ # on them.
60
+ self._session = mlrun.utils.HTTPSessionWithRetry(
61
+ retry_on_post=True,
62
+ verbose=True,
63
+ )
64
+
65
+ self._cleanup()
66
+ self._refresh_token_if_needed()
67
+
68
+ def get_token(self):
69
+ self._refresh_token_if_needed()
70
+ return self.token
71
+
72
+ def is_iguazio_session(self):
73
+ return False
74
+
75
+ def _cleanup(self):
76
+ self.token = self.token_expiry_time = self.token_refresh_time = None
77
+
78
+ def _refresh_token_if_needed(self):
79
+ now = datetime.now()
80
+ if self.token:
81
+ if self.token_refresh_time and now <= self.token_refresh_time:
82
+ return self.token
83
+
84
+ # We only cleanup if token was really expired - even if we fail in refreshing the token, we can still
85
+ # use the existing one given that it's not expired.
86
+ if now >= self.token_expiry_time:
87
+ self._cleanup()
88
+
89
+ self._issue_token_request()
90
+ return self.token
91
+
92
+ def _issue_token_request(self, raise_on_error=False):
93
+ try:
94
+ headers = {"Content-Type": "application/x-www-form-urlencoded"}
95
+ request_body = {
96
+ "grant_type": "client_credentials",
97
+ "client_id": self.client_id,
98
+ "client_secret": self.client_secret,
99
+ }
100
+ response = self._session.request(
101
+ "POST",
102
+ self.token_endpoint,
103
+ timeout=self.timeout,
104
+ headers=headers,
105
+ data=request_body,
106
+ )
107
+ except requests.RequestException as exc:
108
+ error = f"Retrieving token failed: {mlrun.errors.err_to_str(exc)}"
109
+ if raise_on_error:
110
+ raise mlrun.errors.MLRunRuntimeError(error) from exc
111
+ else:
112
+ logger.warning(error)
113
+ return
114
+
115
+ if not response.ok:
116
+ error = "No error available"
117
+ if response.content:
118
+ try:
119
+ data = response.json()
120
+ error = data.get("error")
121
+ except Exception:
122
+ pass
123
+ logger.warning(
124
+ "Retrieving token failed", status=response.status_code, error=error
125
+ )
126
+ if raise_on_error:
127
+ mlrun.errors.raise_for_status(response)
128
+ return
129
+
130
+ self._parse_response(response.json())
131
+
132
+ def _parse_response(self, data: dict):
133
+ # Response is described in https://datatracker.ietf.org/doc/html/rfc6749#section-4.4.3
134
+ # According to spec, there isn't a refresh token - just the access token and its expiry time (in seconds).
135
+ self.token = data.get("access_token")
136
+ expires_in = data.get("expires_in")
137
+ if not self.token or not expires_in:
138
+ token_str = "****" if self.token else "missing"
139
+ logger.warning(
140
+ "Failed to parse token response", token=token_str, expires_in=expires_in
141
+ )
142
+ return
143
+
144
+ now = datetime.now()
145
+ self.token_expiry_time = now + timedelta(seconds=expires_in)
146
+ self.token_refresh_time = now + timedelta(seconds=expires_in / 2)
147
+ logger.info(
148
+ "Successfully retrieved client-id token",
149
+ expires_in=expires_in,
150
+ expiry=str(self.token_expiry_time),
151
+ refresh=str(self.token_refresh_time),
152
+ )
mlrun/db/base.py CHANGED
@@ -16,8 +16,11 @@ import datetime
16
16
  from abc import ABC, abstractmethod
17
17
  from typing import Optional, Union
18
18
 
19
+ import mlrun.alerts
20
+ import mlrun.common.formatters
21
+ import mlrun.common.runtimes.constants
19
22
  import mlrun.common.schemas
20
- import mlrun.model_monitoring.model_endpoint
23
+ import mlrun.model_monitoring
21
24
 
22
25
 
23
26
  class RunDBError(Exception):
@@ -62,7 +65,10 @@ class RunDBInterface(ABC):
62
65
  uid: Optional[Union[str, list[str]]] = None,
63
66
  project: Optional[str] = None,
64
67
  labels: Optional[Union[str, list[str]]] = None,
65
- state: Optional[str] = None,
68
+ state: Optional[
69
+ mlrun.common.runtimes.constants.RunStates
70
+ ] = None, # Backward compatibility
71
+ states: Optional[list[mlrun.common.runtimes.constants.RunStates]] = None,
66
72
  sort: bool = True,
67
73
  last: int = 0,
68
74
  iter: bool = False,
@@ -117,7 +123,18 @@ class RunDBInterface(ABC):
117
123
  pass
118
124
 
119
125
  @abstractmethod
120
- def del_artifact(self, key, tag="", project="", tree=None, uid=None):
126
+ def del_artifact(
127
+ self,
128
+ key,
129
+ tag="",
130
+ project="",
131
+ tree=None,
132
+ uid=None,
133
+ deletion_strategy: mlrun.common.schemas.artifact.ArtifactsDeletionStrategies = (
134
+ mlrun.common.schemas.artifact.ArtifactsDeletionStrategies.metadata_only
135
+ ),
136
+ secrets: dict = None,
137
+ ):
121
138
  pass
122
139
 
123
140
  @abstractmethod
@@ -251,7 +268,7 @@ class RunDBInterface(ABC):
251
268
  def list_projects(
252
269
  self,
253
270
  owner: str = None,
254
- format_: mlrun.common.schemas.ProjectsFormat = mlrun.common.schemas.ProjectsFormat.name_only,
271
+ format_: mlrun.common.formatters.ProjectFormat = mlrun.common.formatters.ProjectFormat.name_only,
255
272
  labels: list[str] = None,
256
273
  state: mlrun.common.schemas.ProjectState = None,
257
274
  ) -> mlrun.common.schemas.ProjectsOutput:
@@ -427,8 +444,8 @@ class RunDBInterface(ABC):
427
444
  namespace: str = None,
428
445
  timeout: int = 30,
429
446
  format_: Union[
430
- str, mlrun.common.schemas.PipelinesFormat
431
- ] = mlrun.common.schemas.PipelinesFormat.summary,
447
+ str, mlrun.common.formatters.PipelineFormat
448
+ ] = mlrun.common.formatters.PipelineFormat.summary,
432
449
  project: str = None,
433
450
  ):
434
451
  pass
@@ -442,8 +459,8 @@ class RunDBInterface(ABC):
442
459
  page_token: str = "",
443
460
  filter_: str = "",
444
461
  format_: Union[
445
- str, mlrun.common.schemas.PipelinesFormat
446
- ] = mlrun.common.schemas.PipelinesFormat.metadata_only,
462
+ str, mlrun.common.formatters.PipelineFormat
463
+ ] = mlrun.common.formatters.PipelineFormat.metadata_only,
447
464
  page_size: int = None,
448
465
  ) -> mlrun.common.schemas.PipelinesOutput:
449
466
  pass
@@ -509,9 +526,7 @@ class RunDBInterface(ABC):
509
526
  self,
510
527
  project: str,
511
528
  endpoint_id: str,
512
- model_endpoint: Union[
513
- mlrun.model_monitoring.model_endpoint.ModelEndpoint, dict
514
- ],
529
+ model_endpoint: Union[mlrun.model_monitoring.ModelEndpoint, dict],
515
530
  ):
516
531
  pass
517
532
 
@@ -545,7 +560,7 @@ class RunDBInterface(ABC):
545
560
  end: Optional[str] = None,
546
561
  metrics: Optional[list[str]] = None,
547
562
  features: bool = False,
548
- ):
563
+ ) -> mlrun.model_monitoring.ModelEndpoint:
549
564
  pass
550
565
 
551
566
  @abstractmethod
@@ -616,6 +631,86 @@ class RunDBInterface(ABC):
616
631
  ):
617
632
  pass
618
633
 
634
+ @abstractmethod
635
+ def store_api_gateway(
636
+ self,
637
+ api_gateway: mlrun.common.schemas.APIGateway,
638
+ project: str = None,
639
+ ):
640
+ pass
641
+
642
+ @abstractmethod
643
+ def list_api_gateways(self, project=None) -> mlrun.common.schemas.APIGatewaysOutput:
644
+ pass
645
+
646
+ @abstractmethod
647
+ def get_api_gateway(self, name, project=None) -> mlrun.common.schemas.APIGateway:
648
+ pass
649
+
650
+ @abstractmethod
651
+ def delete_api_gateway(self, name, project=None):
652
+ pass
653
+
654
+ @abstractmethod
655
+ def remote_builder(
656
+ self,
657
+ func: "mlrun.runtimes.BaseRuntime",
658
+ with_mlrun: bool,
659
+ mlrun_version_specifier: Optional[str] = None,
660
+ skip_deployed: bool = False,
661
+ builder_env: Optional[dict] = None,
662
+ force_build: bool = False,
663
+ ):
664
+ pass
665
+
666
+ @abstractmethod
667
+ def deploy_nuclio_function(
668
+ self,
669
+ func: "mlrun.runtimes.RemoteRuntime",
670
+ builder_env: Optional[dict] = None,
671
+ ):
672
+ pass
673
+
674
+ @abstractmethod
675
+ def generate_event(
676
+ self, name: str, event_data: Union[dict, mlrun.common.schemas.Event], project=""
677
+ ):
678
+ pass
679
+
680
+ @abstractmethod
681
+ def store_alert_config(
682
+ self,
683
+ alert_name: str,
684
+ alert_data: Union[dict, mlrun.alerts.alert.AlertConfig],
685
+ project="",
686
+ ):
687
+ pass
688
+
689
+ @abstractmethod
690
+ def get_alert_config(self, alert_name: str, project=""):
691
+ pass
692
+
693
+ @abstractmethod
694
+ def list_alerts_configs(self, project=""):
695
+ pass
696
+
697
+ @abstractmethod
698
+ def delete_alert_config(self, alert_name: str, project=""):
699
+ pass
700
+
701
+ @abstractmethod
702
+ def reset_alert_config(self, alert_name: str, project=""):
703
+ pass
704
+
705
+ @abstractmethod
706
+ def get_alert_template(self, template_name: str):
707
+ pass
708
+
709
+ @abstractmethod
710
+ def list_alert_templates(self):
711
+ pass
712
+
713
+ @abstractmethod
619
714
  def get_builder_status(
620
715
  self,
621
716
  func: "mlrun.runtimes.BaseRuntime",
@@ -626,6 +721,16 @@ class RunDBInterface(ABC):
626
721
  ):
627
722
  pass
628
723
 
724
+ @abstractmethod
725
+ def get_nuclio_deploy_status(
726
+ self,
727
+ func: "mlrun.runtimes.RemoteRuntime",
728
+ last_log_timestamp: float = 0.0,
729
+ verbose: bool = False,
730
+ ):
731
+ pass
732
+
733
+ @abstractmethod
629
734
  def set_run_notifications(
630
735
  self,
631
736
  project: str,
@@ -634,6 +739,7 @@ class RunDBInterface(ABC):
634
739
  ):
635
740
  pass
636
741
 
742
+ @abstractmethod
637
743
  def store_run_notifications(
638
744
  self,
639
745
  notification_objects: list[mlrun.model.Notification],
@@ -643,40 +749,60 @@ class RunDBInterface(ABC):
643
749
  ):
644
750
  pass
645
751
 
752
+ @abstractmethod
646
753
  def get_log_size(self, uid, project=""):
647
754
  pass
648
755
 
756
+ @abstractmethod
757
+ def store_alert_notifications(
758
+ self,
759
+ session,
760
+ notification_objects: list[mlrun.model.Notification],
761
+ alert_id: str,
762
+ project: str,
763
+ mask_params: bool = True,
764
+ ):
765
+ pass
766
+
767
+ @abstractmethod
649
768
  def watch_log(self, uid, project="", watch=True, offset=0):
650
769
  pass
651
770
 
771
+ @abstractmethod
652
772
  def get_datastore_profile(
653
773
  self, name: str, project: str
654
774
  ) -> Optional[mlrun.common.schemas.DatastoreProfile]:
655
775
  pass
656
776
 
777
+ @abstractmethod
657
778
  def delete_datastore_profile(
658
779
  self, name: str, project: str
659
780
  ) -> mlrun.common.schemas.DatastoreProfile:
660
781
  pass
661
782
 
783
+ @abstractmethod
662
784
  def list_datastore_profiles(
663
785
  self, project: str
664
786
  ) -> list[mlrun.common.schemas.DatastoreProfile]:
665
787
  pass
666
788
 
789
+ @abstractmethod
667
790
  def store_datastore_profile(
668
791
  self, profile: mlrun.common.schemas.DatastoreProfile, project: str
669
792
  ):
670
793
  pass
671
794
 
795
+ @abstractmethod
672
796
  def function_status(self, project, name, kind, selector):
673
797
  pass
674
798
 
799
+ @abstractmethod
675
800
  def start_function(
676
801
  self, func_url: str = None, function: "mlrun.runtimes.BaseRuntime" = None
677
802
  ):
678
803
  pass
679
804
 
805
+ @abstractmethod
680
806
  def submit_workflow(
681
807
  self,
682
808
  project: str,
@@ -695,18 +821,27 @@ class RunDBInterface(ABC):
695
821
  ) -> "mlrun.common.schemas.WorkflowResponse":
696
822
  pass
697
823
 
824
+ @abstractmethod
698
825
  def update_model_monitoring_controller(
699
826
  self,
700
827
  project: str,
701
828
  base_period: int = 10,
702
829
  image: str = "mlrun/mlrun",
703
- ):
830
+ ) -> None:
704
831
  pass
705
832
 
833
+ @abstractmethod
706
834
  def enable_model_monitoring(
707
835
  self,
708
836
  project: str,
709
837
  base_period: int = 10,
710
838
  image: str = "mlrun/mlrun",
711
- ):
839
+ deploy_histogram_data_drift_app: bool = True,
840
+ ) -> None:
841
+ pass
842
+
843
+ @abstractmethod
844
+ def deploy_histogram_data_drift_app(
845
+ self, project: str, image: str = "mlrun/mlrun"
846
+ ) -> None:
712
847
  pass
mlrun/db/factory.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2023 MLRun Authors
1
+ # Copyright 2023 Iguazio
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.