mlrun 1.4.0rc25__py3-none-any.whl → 1.5.0rc2__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 (184) hide show
  1. mlrun/__init__.py +2 -35
  2. mlrun/__main__.py +3 -41
  3. mlrun/api/api/api.py +6 -0
  4. mlrun/api/api/endpoints/feature_store.py +0 -4
  5. mlrun/api/api/endpoints/files.py +14 -2
  6. mlrun/api/api/endpoints/frontend_spec.py +2 -1
  7. mlrun/api/api/endpoints/functions.py +95 -59
  8. mlrun/api/api/endpoints/grafana_proxy.py +9 -9
  9. mlrun/api/api/endpoints/logs.py +17 -3
  10. mlrun/api/api/endpoints/model_endpoints.py +3 -2
  11. mlrun/api/api/endpoints/pipelines.py +1 -5
  12. mlrun/api/api/endpoints/projects.py +88 -0
  13. mlrun/api/api/endpoints/runs.py +48 -6
  14. mlrun/api/api/endpoints/submit.py +2 -1
  15. mlrun/api/api/endpoints/workflows.py +355 -0
  16. mlrun/api/api/utils.py +3 -4
  17. mlrun/api/crud/__init__.py +1 -0
  18. mlrun/api/crud/client_spec.py +6 -2
  19. mlrun/api/crud/feature_store.py +5 -0
  20. mlrun/api/crud/model_monitoring/__init__.py +1 -0
  21. mlrun/api/crud/model_monitoring/deployment.py +497 -0
  22. mlrun/api/crud/model_monitoring/grafana.py +96 -42
  23. mlrun/api/crud/model_monitoring/helpers.py +159 -0
  24. mlrun/api/crud/model_monitoring/model_endpoints.py +202 -476
  25. mlrun/api/crud/notifications.py +9 -4
  26. mlrun/api/crud/pipelines.py +6 -11
  27. mlrun/api/crud/projects.py +2 -2
  28. mlrun/api/crud/runtime_resources.py +4 -3
  29. mlrun/api/crud/runtimes/nuclio/helpers.py +5 -1
  30. mlrun/api/crud/secrets.py +21 -0
  31. mlrun/api/crud/workflows.py +352 -0
  32. mlrun/api/db/base.py +16 -1
  33. mlrun/api/db/init_db.py +2 -4
  34. mlrun/api/db/session.py +1 -1
  35. mlrun/api/db/sqldb/db.py +129 -31
  36. mlrun/api/db/sqldb/models/models_mysql.py +15 -1
  37. mlrun/api/db/sqldb/models/models_sqlite.py +16 -2
  38. mlrun/api/launcher.py +38 -6
  39. mlrun/api/main.py +3 -2
  40. mlrun/api/rundb/__init__.py +13 -0
  41. mlrun/{db → api/rundb}/sqldb.py +36 -84
  42. mlrun/api/runtime_handlers/__init__.py +56 -0
  43. mlrun/api/runtime_handlers/base.py +1247 -0
  44. mlrun/api/runtime_handlers/daskjob.py +209 -0
  45. mlrun/api/runtime_handlers/kubejob.py +37 -0
  46. mlrun/api/runtime_handlers/mpijob.py +147 -0
  47. mlrun/api/runtime_handlers/remotesparkjob.py +29 -0
  48. mlrun/api/runtime_handlers/sparkjob.py +148 -0
  49. mlrun/api/schemas/__init__.py +17 -6
  50. mlrun/api/utils/builder.py +1 -4
  51. mlrun/api/utils/clients/chief.py +14 -0
  52. mlrun/api/utils/clients/iguazio.py +33 -33
  53. mlrun/api/utils/clients/nuclio.py +2 -2
  54. mlrun/api/utils/periodic.py +9 -2
  55. mlrun/api/utils/projects/follower.py +14 -7
  56. mlrun/api/utils/projects/leader.py +2 -1
  57. mlrun/api/utils/projects/remotes/nop_follower.py +2 -2
  58. mlrun/api/utils/projects/remotes/nop_leader.py +2 -2
  59. mlrun/api/utils/runtimes/__init__.py +14 -0
  60. mlrun/api/utils/runtimes/nuclio.py +43 -0
  61. mlrun/api/utils/scheduler.py +98 -15
  62. mlrun/api/utils/singletons/db.py +5 -1
  63. mlrun/api/utils/singletons/project_member.py +4 -1
  64. mlrun/api/utils/singletons/scheduler.py +1 -1
  65. mlrun/artifacts/base.py +6 -6
  66. mlrun/artifacts/dataset.py +4 -4
  67. mlrun/artifacts/manager.py +2 -3
  68. mlrun/artifacts/model.py +2 -2
  69. mlrun/artifacts/plots.py +8 -8
  70. mlrun/common/db/__init__.py +14 -0
  71. mlrun/common/helpers.py +37 -0
  72. mlrun/{mlutils → common/model_monitoring}/__init__.py +3 -2
  73. mlrun/common/model_monitoring/helpers.py +69 -0
  74. mlrun/common/schemas/__init__.py +13 -1
  75. mlrun/common/schemas/auth.py +4 -1
  76. mlrun/common/schemas/client_spec.py +1 -1
  77. mlrun/common/schemas/function.py +17 -0
  78. mlrun/common/schemas/model_monitoring/__init__.py +48 -0
  79. mlrun/common/{model_monitoring.py → schemas/model_monitoring/constants.py} +11 -23
  80. mlrun/common/schemas/model_monitoring/grafana.py +55 -0
  81. mlrun/common/schemas/{model_endpoints.py → model_monitoring/model_endpoints.py} +32 -65
  82. mlrun/common/schemas/notification.py +1 -0
  83. mlrun/common/schemas/object.py +4 -0
  84. mlrun/common/schemas/project.py +1 -0
  85. mlrun/common/schemas/regex.py +1 -1
  86. mlrun/common/schemas/runs.py +1 -8
  87. mlrun/common/schemas/schedule.py +1 -8
  88. mlrun/common/schemas/workflow.py +54 -0
  89. mlrun/config.py +45 -42
  90. mlrun/datastore/__init__.py +21 -0
  91. mlrun/datastore/base.py +1 -1
  92. mlrun/datastore/datastore.py +9 -0
  93. mlrun/datastore/dbfs_store.py +168 -0
  94. mlrun/datastore/helpers.py +18 -0
  95. mlrun/datastore/sources.py +1 -0
  96. mlrun/datastore/store_resources.py +2 -5
  97. mlrun/datastore/v3io.py +1 -2
  98. mlrun/db/__init__.py +4 -68
  99. mlrun/db/base.py +12 -0
  100. mlrun/db/factory.py +65 -0
  101. mlrun/db/httpdb.py +175 -20
  102. mlrun/db/nopdb.py +4 -2
  103. mlrun/execution.py +4 -2
  104. mlrun/feature_store/__init__.py +1 -0
  105. mlrun/feature_store/api.py +1 -2
  106. mlrun/feature_store/common.py +2 -1
  107. mlrun/feature_store/feature_set.py +1 -11
  108. mlrun/feature_store/feature_vector.py +340 -2
  109. mlrun/feature_store/ingestion.py +5 -10
  110. mlrun/feature_store/retrieval/base.py +118 -104
  111. mlrun/feature_store/retrieval/dask_merger.py +17 -10
  112. mlrun/feature_store/retrieval/job.py +4 -1
  113. mlrun/feature_store/retrieval/local_merger.py +18 -18
  114. mlrun/feature_store/retrieval/spark_merger.py +21 -14
  115. mlrun/feature_store/retrieval/storey_merger.py +22 -16
  116. mlrun/kfpops.py +3 -9
  117. mlrun/launcher/base.py +57 -53
  118. mlrun/launcher/client.py +5 -4
  119. mlrun/launcher/factory.py +24 -13
  120. mlrun/launcher/local.py +6 -6
  121. mlrun/launcher/remote.py +4 -4
  122. mlrun/lists.py +0 -11
  123. mlrun/model.py +11 -17
  124. mlrun/model_monitoring/__init__.py +2 -22
  125. mlrun/model_monitoring/features_drift_table.py +1 -1
  126. mlrun/model_monitoring/helpers.py +22 -210
  127. mlrun/model_monitoring/model_endpoint.py +1 -1
  128. mlrun/model_monitoring/model_monitoring_batch.py +127 -50
  129. mlrun/model_monitoring/prometheus.py +219 -0
  130. mlrun/model_monitoring/stores/__init__.py +16 -11
  131. mlrun/model_monitoring/stores/kv_model_endpoint_store.py +95 -23
  132. mlrun/model_monitoring/stores/models/mysql.py +47 -29
  133. mlrun/model_monitoring/stores/models/sqlite.py +47 -29
  134. mlrun/model_monitoring/stores/sql_model_endpoint_store.py +31 -19
  135. mlrun/model_monitoring/{stream_processing_fs.py → stream_processing.py} +206 -64
  136. mlrun/model_monitoring/tracking_policy.py +104 -0
  137. mlrun/package/packager.py +6 -8
  138. mlrun/package/packagers/default_packager.py +121 -10
  139. mlrun/package/packagers/numpy_packagers.py +1 -1
  140. mlrun/platforms/__init__.py +0 -2
  141. mlrun/platforms/iguazio.py +0 -56
  142. mlrun/projects/pipelines.py +53 -159
  143. mlrun/projects/project.py +10 -37
  144. mlrun/render.py +1 -1
  145. mlrun/run.py +8 -124
  146. mlrun/runtimes/__init__.py +6 -42
  147. mlrun/runtimes/base.py +29 -1249
  148. mlrun/runtimes/daskjob.py +2 -198
  149. mlrun/runtimes/funcdoc.py +0 -9
  150. mlrun/runtimes/function.py +25 -29
  151. mlrun/runtimes/kubejob.py +5 -29
  152. mlrun/runtimes/local.py +1 -1
  153. mlrun/runtimes/mpijob/__init__.py +2 -2
  154. mlrun/runtimes/mpijob/abstract.py +10 -1
  155. mlrun/runtimes/mpijob/v1.py +0 -76
  156. mlrun/runtimes/mpijob/v1alpha1.py +1 -74
  157. mlrun/runtimes/nuclio.py +3 -2
  158. mlrun/runtimes/pod.py +28 -18
  159. mlrun/runtimes/remotesparkjob.py +1 -15
  160. mlrun/runtimes/serving.py +14 -6
  161. mlrun/runtimes/sparkjob/__init__.py +0 -1
  162. mlrun/runtimes/sparkjob/abstract.py +4 -131
  163. mlrun/runtimes/utils.py +0 -26
  164. mlrun/serving/routers.py +7 -7
  165. mlrun/serving/server.py +11 -8
  166. mlrun/serving/states.py +7 -1
  167. mlrun/serving/v2_serving.py +6 -6
  168. mlrun/utils/helpers.py +23 -42
  169. mlrun/utils/notifications/notification/__init__.py +4 -0
  170. mlrun/utils/notifications/notification/webhook.py +61 -0
  171. mlrun/utils/notifications/notification_pusher.py +5 -25
  172. mlrun/utils/regex.py +7 -2
  173. mlrun/utils/version/version.json +2 -2
  174. {mlrun-1.4.0rc25.dist-info → mlrun-1.5.0rc2.dist-info}/METADATA +26 -25
  175. {mlrun-1.4.0rc25.dist-info → mlrun-1.5.0rc2.dist-info}/RECORD +180 -158
  176. {mlrun-1.4.0rc25.dist-info → mlrun-1.5.0rc2.dist-info}/WHEEL +1 -1
  177. mlrun/mlutils/data.py +0 -160
  178. mlrun/mlutils/models.py +0 -78
  179. mlrun/mlutils/plots.py +0 -902
  180. mlrun/utils/model_monitoring.py +0 -249
  181. /mlrun/{api/db/sqldb/session.py → common/db/sql_session.py} +0 -0
  182. {mlrun-1.4.0rc25.dist-info → mlrun-1.5.0rc2.dist-info}/LICENSE +0 -0
  183. {mlrun-1.4.0rc25.dist-info → mlrun-1.5.0rc2.dist-info}/entry_points.txt +0 -0
  184. {mlrun-1.4.0rc25.dist-info → mlrun-1.5.0rc2.dist-info}/top_level.txt +0 -0
@@ -28,7 +28,7 @@ import igz_mgmt.schemas.manual_events
28
28
  import requests.adapters
29
29
  from fastapi.concurrency import run_in_threadpool
30
30
 
31
- import mlrun.api.utils.projects.remotes.leader
31
+ import mlrun.api.utils.projects.remotes.leader as project_leader
32
32
  import mlrun.common.schemas
33
33
  import mlrun.errors
34
34
  import mlrun.utils.helpers
@@ -73,7 +73,7 @@ class SessionPlanes:
73
73
 
74
74
 
75
75
  class Client(
76
- mlrun.api.utils.projects.remotes.leader.Member,
76
+ project_leader.Member,
77
77
  metaclass=mlrun.utils.singleton.AbstractSingleton,
78
78
  ):
79
79
  def __init__(self, *args, **kwargs) -> None:
@@ -93,37 +93,6 @@ class Client(
93
93
  self._logger = logger.get_child("iguazio-client")
94
94
  self._igz_clients = {}
95
95
 
96
- def try_get_grafana_service_url(self, session: str) -> typing.Optional[str]:
97
- """
98
- Try to find a ready grafana app service, and return its URL
99
- If nothing found, returns None
100
- """
101
- self._logger.debug("Getting grafana service url from Iguazio")
102
- response = self._send_request_to_api(
103
- "GET",
104
- "app_services_manifests",
105
- "Failed getting app services manifests from Iguazio",
106
- session,
107
- )
108
- response_body = response.json()
109
- for app_services_manifest in response_body.get("data", []):
110
- for app_service in app_services_manifest.get("attributes", {}).get(
111
- "app_services", []
112
- ):
113
- if (
114
- app_service.get("spec", {}).get("kind") == "grafana"
115
- and app_service.get("status", {}).get("state") == "ready"
116
- and len(app_service.get("status", {}).get("urls", [])) > 0
117
- ):
118
- url_kind_to_url = {}
119
- for url in app_service["status"]["urls"]:
120
- url_kind_to_url[url["kind"]] = url["url"]
121
- # precedence for https
122
- for kind in ["https", "http"]:
123
- if kind in url_kind_to_url:
124
- return url_kind_to_url[kind]
125
- return None
126
-
127
96
  def verify_request_session(
128
97
  self, request: fastapi.Request
129
98
  ) -> mlrun.common.schemas.AuthInfo:
@@ -326,6 +295,37 @@ class Client(
326
295
  """
327
296
  return True
328
297
 
298
+ def try_get_grafana_service_url(self, session: str) -> typing.Optional[str]:
299
+ """
300
+ Try to find a ready grafana app service, and return its URL
301
+ If nothing found, returns None
302
+ """
303
+ self._logger.debug("Getting grafana service url from Iguazio")
304
+ response = self._send_request_to_api(
305
+ "GET",
306
+ "app_services_manifests",
307
+ "Failed getting app services manifests from Iguazio",
308
+ session,
309
+ )
310
+ response_body = response.json()
311
+ for app_services_manifest in response_body.get("data", []):
312
+ for app_service in app_services_manifest.get("attributes", {}).get(
313
+ "app_services", []
314
+ ):
315
+ if (
316
+ app_service.get("spec", {}).get("kind") == "grafana"
317
+ and app_service.get("status", {}).get("state") == "ready"
318
+ and len(app_service.get("status", {}).get("urls", [])) > 0
319
+ ):
320
+ url_kind_to_url = {}
321
+ for url in app_service["status"]["urls"]:
322
+ url_kind_to_url[url["kind"]] = url["url"]
323
+ # precedence for https
324
+ for kind in ["https", "http"]:
325
+ if kind in url_kind_to_url:
326
+ return url_kind_to_url[kind]
327
+ return None
328
+
329
329
  def emit_manual_event(self, access_key: str, event: igz_mgmt.Event):
330
330
  """
331
331
  Emit a manual event to Iguazio
@@ -20,7 +20,7 @@ import typing
20
20
  import requests.adapters
21
21
  import sqlalchemy.orm
22
22
 
23
- import mlrun.api.utils.projects.remotes.follower
23
+ import mlrun.api.utils.projects.remotes.follower as project_follower
24
24
  import mlrun.common.schemas
25
25
  import mlrun.errors
26
26
  import mlrun.utils.singleton
@@ -28,7 +28,7 @@ from mlrun.utils import logger
28
28
 
29
29
 
30
30
  class Client(
31
- mlrun.api.utils.projects.remotes.follower.Member,
31
+ project_follower.Member,
32
32
  metaclass=mlrun.utils.singleton.AbstractSingleton,
33
33
  ):
34
34
  def __init__(self) -> None:
@@ -27,7 +27,9 @@ tasks: typing.Dict = {}
27
27
  # This module is different from mlrun.db.periodic in that this module's functions aren't supposed to persist
28
28
  # also this module supports asyncio while the other currently not
29
29
  # TODO: merge the modules
30
- async def _periodic_function_wrapper(interval: int, function, *args, **kwargs):
30
+ async def _periodic_function_wrapper(
31
+ interval: typing.Union[int, float], function, *args, **kwargs
32
+ ):
31
33
  while True:
32
34
  try:
33
35
  if asyncio.iscoroutinefunction(function):
@@ -45,7 +47,12 @@ async def _periodic_function_wrapper(interval: int, function, *args, **kwargs):
45
47
 
46
48
 
47
49
  def run_function_periodically(
48
- interval: int, name: str, replace: bool, function, *args, **kwargs
50
+ interval: typing.Union[float, int],
51
+ name: str,
52
+ replace: bool,
53
+ function,
54
+ *args,
55
+ **kwargs
49
56
  ):
50
57
  global tasks
51
58
  logger.debug("Submitting function to run periodically", name=name)
@@ -27,7 +27,7 @@ import mlrun.api.utils.auth.verifier
27
27
  import mlrun.api.utils.clients.iguazio
28
28
  import mlrun.api.utils.clients.nuclio
29
29
  import mlrun.api.utils.periodic
30
- import mlrun.api.utils.projects.member
30
+ import mlrun.api.utils.projects.member as project_member
31
31
  import mlrun.api.utils.projects.remotes.leader
32
32
  import mlrun.api.utils.projects.remotes.nop_leader
33
33
  import mlrun.common.schemas
@@ -42,7 +42,7 @@ from mlrun.utils import logger
42
42
 
43
43
 
44
44
  class Member(
45
- mlrun.api.utils.projects.member.Member,
45
+ project_member.Member,
46
46
  metaclass=mlrun.utils.singleton.AbstractSingleton,
47
47
  ):
48
48
  def initialize(self):
@@ -346,11 +346,18 @@ class Member(
346
346
  "Found project in the DB that is not in leader. Removing",
347
347
  name=project_to_remove,
348
348
  )
349
- mlrun.api.crud.Projects().delete_project(
350
- db_session,
351
- project_to_remove,
352
- mlrun.common.schemas.DeletionStrategy.cascading,
353
- )
349
+ try:
350
+ mlrun.api.crud.Projects().delete_project(
351
+ db_session,
352
+ project_to_remove,
353
+ mlrun.common.schemas.DeletionStrategy.cascading,
354
+ )
355
+ except Exception as exc:
356
+ logger.warning(
357
+ "Failed to delete project from DB, continuing...",
358
+ name=project_to_remove,
359
+ exc=err_to_str(exc),
360
+ )
354
361
  if latest_updated_at:
355
362
 
356
363
  # sanity and defensive programming - if the leader returned a latest_updated_at that is older
@@ -23,6 +23,7 @@ import mlrun.api.db.session
23
23
  import mlrun.api.utils.clients.nuclio
24
24
  import mlrun.api.utils.periodic
25
25
  import mlrun.api.utils.projects.member
26
+ import mlrun.api.utils.projects.member as project_member
26
27
  import mlrun.api.utils.projects.remotes.follower
27
28
  import mlrun.api.utils.projects.remotes.nop_follower
28
29
  import mlrun.common.schemas
@@ -37,7 +38,7 @@ from mlrun.utils import logger
37
38
 
38
39
 
39
40
  class Member(
40
- mlrun.api.utils.projects.member.Member,
41
+ project_member.Member,
41
42
  metaclass=mlrun.utils.singleton.AbstractSingleton,
42
43
  ):
43
44
  def initialize(self):
@@ -18,12 +18,12 @@ import mergedeep
18
18
  import sqlalchemy.orm
19
19
 
20
20
  import mlrun.api.utils.helpers
21
- import mlrun.api.utils.projects.remotes.follower
21
+ import mlrun.api.utils.projects.remotes.follower as project_follower
22
22
  import mlrun.common.schemas
23
23
  import mlrun.errors
24
24
 
25
25
 
26
- class Member(mlrun.api.utils.projects.remotes.follower.Member):
26
+ class Member(project_follower.Member):
27
27
  def __init__(self) -> None:
28
28
  super().__init__()
29
29
  self._projects: typing.Dict[str, mlrun.common.schemas.Project] = {}
@@ -15,13 +15,13 @@
15
15
  import datetime
16
16
  import typing
17
17
 
18
- import mlrun.api.utils.projects.remotes.leader
18
+ import mlrun.api.utils.projects.remotes.leader as project_leader
19
19
  import mlrun.api.utils.singletons.project_member
20
20
  import mlrun.common.schemas
21
21
  import mlrun.errors
22
22
 
23
23
 
24
- class Member(mlrun.api.utils.projects.remotes.leader.Member):
24
+ class Member(project_leader.Member):
25
25
  def __init__(self) -> None:
26
26
  super().__init__()
27
27
  self.db_session = None
@@ -0,0 +1,14 @@
1
+ # Copyright 2023 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
+ #
@@ -0,0 +1,43 @@
1
+ # Copyright 2023 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
+ import mlrun.api.utils.clients.nuclio
16
+ from mlrun.config import config
17
+ from mlrun.errors import err_to_str
18
+ from mlrun.utils import logger
19
+
20
+ cached_nuclio_version = None
21
+
22
+
23
+ # if nuclio version specified on mlrun config set it likewise,
24
+ # if not specified, get it from nuclio api client
25
+ # since this is a heavy operation (sending requests to API), and it's unlikely that the version
26
+ # will change - cache it (this means if we upgrade nuclio, we need to restart mlrun to re-fetch the new version)
27
+ def resolve_nuclio_version():
28
+ global cached_nuclio_version
29
+
30
+ if not cached_nuclio_version:
31
+
32
+ # config override everything
33
+ nuclio_version = config.nuclio_version
34
+ if not nuclio_version and config.nuclio_dashboard_url:
35
+ try:
36
+ nuclio_client = mlrun.api.utils.clients.nuclio.Client()
37
+ nuclio_version = nuclio_client.get_dashboard_version()
38
+ except Exception as exc:
39
+ logger.warning("Failed to resolve nuclio version", exc=err_to_str(exc))
40
+
41
+ cached_nuclio_version = nuclio_version
42
+
43
+ return cached_nuclio_version
@@ -142,14 +142,14 @@ class Scheduler:
142
142
  self._enrich_schedule_notifications(project, name, scheduled_object)
143
143
 
144
144
  get_db().create_schedule(
145
- db_session,
146
- project,
147
- name,
148
- kind,
149
- scheduled_object,
150
- cron_trigger,
151
- concurrency_limit,
152
- labels,
145
+ session=db_session,
146
+ project=project,
147
+ name=name,
148
+ kind=kind,
149
+ scheduled_object=scheduled_object,
150
+ cron_trigger=cron_trigger,
151
+ concurrency_limit=concurrency_limit,
152
+ labels=labels,
153
153
  )
154
154
  job = self._create_schedule_in_scheduler(
155
155
  project,
@@ -217,13 +217,13 @@ class Scheduler:
217
217
  self._enrich_schedule_notifications(project, name, scheduled_object)
218
218
 
219
219
  get_db().update_schedule(
220
- db_session,
221
- project,
222
- name,
223
- scheduled_object,
224
- cron_trigger,
225
- labels,
226
- concurrency_limit,
220
+ session=db_session,
221
+ project=project,
222
+ name=name,
223
+ scheduled_object=scheduled_object,
224
+ cron_trigger=cron_trigger,
225
+ labels=labels,
226
+ concurrency_limit=concurrency_limit,
227
227
  )
228
228
  db_schedule = get_db().get_schedule(db_session, project, name)
229
229
 
@@ -303,6 +303,89 @@ class Scheduler:
303
303
  )
304
304
  get_db().delete_schedules(db_session, project)
305
305
 
306
+ @mlrun.api.utils.helpers.ensure_running_on_chief
307
+ def store_schedule(
308
+ self,
309
+ db_session: Session,
310
+ auth_info: mlrun.common.schemas.AuthInfo,
311
+ project: str,
312
+ name: str,
313
+ scheduled_object: Union[Dict, Callable] = None,
314
+ cron_trigger: Union[str, mlrun.common.schemas.ScheduleCronTrigger] = None,
315
+ labels: Dict = None,
316
+ concurrency_limit: int = None,
317
+ kind: mlrun.common.schemas.ScheduleKinds = None,
318
+ ):
319
+ if isinstance(cron_trigger, str):
320
+ cron_trigger = mlrun.common.schemas.ScheduleCronTrigger.from_crontab(
321
+ cron_trigger
322
+ )
323
+
324
+ if cron_trigger is not None:
325
+ self._validate_cron_trigger(cron_trigger)
326
+
327
+ logger.debug(
328
+ "Storing schedule",
329
+ project=project,
330
+ name=name,
331
+ kind=kind,
332
+ scheduled_object=scheduled_object,
333
+ cron_trigger=cron_trigger,
334
+ labels=labels,
335
+ concurrency_limit=concurrency_limit,
336
+ )
337
+
338
+ if not kind:
339
+ # TODO: Need to think of a way to not use `get_schedule`
340
+ # in this function or in `get_db().store_function()` in this flow
341
+ # because we must have kind to ensure that auth info has access key.
342
+ db_schedule = get_db().get_schedule(
343
+ db_session, project, name, raise_on_not_found=False
344
+ )
345
+ kind = db_schedule.kind
346
+
347
+ self._ensure_auth_info_has_access_key(auth_info, kind)
348
+ secret_name = self._store_schedule_secrets_using_auth_secret(auth_info)
349
+ labels = self._append_access_key_secret_to_labels(labels, secret_name)
350
+ self._enrich_schedule_notifications(project, name, scheduled_object)
351
+
352
+ db_schedule = get_db().store_schedule(
353
+ session=db_session,
354
+ project=project,
355
+ name=name,
356
+ kind=kind,
357
+ scheduled_object=scheduled_object,
358
+ cron_trigger=cron_trigger,
359
+ labels=labels,
360
+ concurrency_limit=concurrency_limit,
361
+ )
362
+ if db_schedule:
363
+ updated_schedule = self._transform_and_enrich_db_schedule(
364
+ db_session, db_schedule
365
+ )
366
+
367
+ job = self._update_schedule_in_scheduler(
368
+ project,
369
+ name,
370
+ updated_schedule.kind,
371
+ updated_schedule.scheduled_object,
372
+ updated_schedule.cron_trigger,
373
+ updated_schedule.concurrency_limit,
374
+ auth_info,
375
+ )
376
+ else:
377
+ job = self._create_schedule_in_scheduler(
378
+ project,
379
+ name,
380
+ kind,
381
+ scheduled_object,
382
+ cron_trigger,
383
+ concurrency_limit,
384
+ auth_info,
385
+ )
386
+
387
+ self.update_schedule_next_run_time(db_session, name, project, job)
388
+
306
389
  def _remove_schedule_scheduler_resources(self, db_session: Session, project, name):
307
390
  self._remove_schedule_from_scheduler(project, name)
308
391
  # This is kept for backwards compatibility - if schedule was using the "old" format of storing secrets, then
@@ -12,9 +12,10 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  #
15
+ import mlrun.db
15
16
  from mlrun.api.db.base import DBInterface
16
17
  from mlrun.api.db.sqldb.db import SQLDB
17
- from mlrun.api.db.sqldb.session import create_session
18
+ from mlrun.common.db.sql_session import create_session
18
19
  from mlrun.config import config
19
20
  from mlrun.utils import logger
20
21
 
@@ -34,6 +35,9 @@ def initialize_db(override_db=None):
34
35
  return
35
36
  logger.info("Creating sql db")
36
37
  db = SQLDB(config.httpdb.dsn)
38
+ # set the run db path to the sql db dsn
39
+ mlrun.db.get_or_set_dburl(config.httpdb.dsn)
40
+
37
41
  db_session = None
38
42
  try:
39
43
  db_session = create_session()
@@ -12,13 +12,16 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  #
15
+
16
+ import typing
17
+
15
18
  import mlrun.api.utils.projects.follower
16
19
  import mlrun.api.utils.projects.leader
17
20
  import mlrun.api.utils.projects.member
18
21
  import mlrun.config
19
22
 
20
23
  # TODO: something nicer
21
- project_member: mlrun.api.utils.projects.member.Member = None
24
+ project_member: typing.Optional[mlrun.api.utils.projects.member.Member] = None
22
25
 
23
26
 
24
27
  def initialize_project_member():
@@ -12,8 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  #
15
- from mlrun.api.db.sqldb.session import create_session
16
15
  from mlrun.api.utils.scheduler import Scheduler
16
+ from mlrun.common.db.sql_session import create_session
17
17
 
18
18
  # TODO: something nicer
19
19
  scheduler: Scheduler = None
mlrun/artifacts/base.py CHANGED
@@ -698,10 +698,10 @@ class LinkArtifact(Artifact):
698
698
  self._spec = self._verify_dict(spec, "spec", LinkArtifactSpec)
699
699
 
700
700
 
701
- # TODO: remove in 1.5.0
701
+ # TODO: remove in 1.6.0
702
702
  @deprecated(
703
703
  version="1.3.0",
704
- reason="'LegacyArtifact' will be removed in 1.5.0, use 'Artifact' instead",
704
+ reason="'LegacyArtifact' will be removed in 1.6.0, use 'Artifact' instead",
705
705
  category=FutureWarning,
706
706
  )
707
707
  class LegacyArtifact(ModelObj):
@@ -865,10 +865,10 @@ class LegacyArtifact(ModelObj):
865
865
  return generate_target_path(self, artifact_path, producer)
866
866
 
867
867
 
868
- # TODO: remove in 1.5.0
868
+ # TODO: remove in 1.6.0
869
869
  @deprecated(
870
870
  version="1.3.0",
871
- reason="'LegacyDirArtifact' will be removed in 1.5.0, use 'DirArtifact' instead",
871
+ reason="'LegacyDirArtifact' will be removed in 1.6.0, use 'DirArtifact' instead",
872
872
  category=FutureWarning,
873
873
  )
874
874
  class LegacyDirArtifact(LegacyArtifact):
@@ -901,10 +901,10 @@ class LegacyDirArtifact(LegacyArtifact):
901
901
  store_manager.object(url=target).upload(file_path)
902
902
 
903
903
 
904
- # TODO: remove in 1.5.0
904
+ # TODO: remove in 1.6.0
905
905
  @deprecated(
906
906
  version="1.3.0",
907
- reason="'LegacyLinkArtifact' will be removed in 1.5.0, use 'LinkArtifact' instead",
907
+ reason="'LegacyLinkArtifact' will be removed in 1.6.0, use 'LinkArtifact' instead",
908
908
  category=FutureWarning,
909
909
  )
910
910
  class LegacyLinkArtifact(LegacyArtifact):
@@ -322,10 +322,10 @@ class DatasetArtifact(Artifact):
322
322
  self.status.stats = stats
323
323
 
324
324
 
325
- # TODO: remove in 1.5.0
325
+ # TODO: remove in 1.6.0
326
326
  @deprecated(
327
327
  version="1.3.0",
328
- reason="'LegacyTableArtifact' will be removed in 1.5.0, use 'TableArtifact' instead",
328
+ reason="'LegacyTableArtifact' will be removed in 1.6.0, use 'TableArtifact' instead",
329
329
  category=FutureWarning,
330
330
  )
331
331
  class LegacyTableArtifact(LegacyArtifact):
@@ -375,10 +375,10 @@ class LegacyTableArtifact(LegacyArtifact):
375
375
  return csv_buffer.getvalue()
376
376
 
377
377
 
378
- # TODO: remove in 1.5.0
378
+ # TODO: remove in 1.6.0
379
379
  @deprecated(
380
380
  version="1.3.0",
381
- reason="'LegacyDatasetArtifact' will be removed in 1.5.0, use 'DatasetArtifact' instead",
381
+ reason="'LegacyDatasetArtifact' will be removed in 1.6.0, use 'DatasetArtifact' instead",
382
382
  category=FutureWarning,
383
383
  )
384
384
  class LegacyDatasetArtifact(LegacyArtifact):
@@ -17,7 +17,6 @@ from os.path import isdir
17
17
 
18
18
  import mlrun.config
19
19
 
20
- from ..db import RunDBInterface
21
20
  from ..utils import (
22
21
  is_legacy_artifact,
23
22
  is_relative_path,
@@ -65,7 +64,7 @@ artifact_types = {
65
64
  "bokeh": BokehArtifact,
66
65
  }
67
66
 
68
- # TODO - Remove this when legacy types are deleted in 1.5.0
67
+ # TODO - Remove this when legacy types are deleted in 1.6.0
69
68
  legacy_artifact_types = {
70
69
  "": LegacyArtifact,
71
70
  "dir": LegacyDirArtifact,
@@ -111,7 +110,7 @@ def dict_to_artifact(struct: dict) -> Artifact:
111
110
  class ArtifactManager:
112
111
  def __init__(
113
112
  self,
114
- db: RunDBInterface = None,
113
+ db: "mlrun.db.RunDBInterface" = None,
115
114
  calc_hash=True,
116
115
  ):
117
116
  self.calc_hash = calc_hash
mlrun/artifacts/model.py CHANGED
@@ -370,10 +370,10 @@ class ModelArtifact(Artifact):
370
370
  return mlrun.get_dataitem(target_model_path).get()
371
371
 
372
372
 
373
- # TODO: remove in 1.5.0
373
+ # TODO: remove in 1.6.0
374
374
  @deprecated(
375
375
  version="1.3.0",
376
- reason="'LegacyModelArtifact' will be removed in 1.5.0, use 'ModelArtifact' instead",
376
+ reason="'LegacyModelArtifact' will be removed in 1.6.0, use 'ModelArtifact' instead",
377
377
  category=FutureWarning,
378
378
  )
379
379
  class LegacyModelArtifact(LegacyArtifact):
mlrun/artifacts/plots.py CHANGED
@@ -244,10 +244,10 @@ class PlotlyArtifact(Artifact):
244
244
  return self._figure.to_html()
245
245
 
246
246
 
247
- # TODO: remove in 1.5.0
247
+ # TODO: remove in 1.6.0
248
248
  @deprecated(
249
249
  version="1.3.0",
250
- reason="'LegacyPlotArtifact' will be removed in 1.5.0, use 'PlotArtifact' instead",
250
+ reason="'LegacyPlotArtifact' will be removed in 1.6.0, use 'PlotArtifact' instead",
251
251
  category=FutureWarning,
252
252
  )
253
253
  class LegacyPlotArtifact(LegacyArtifact):
@@ -291,10 +291,10 @@ class LegacyPlotArtifact(LegacyArtifact):
291
291
  return self._TEMPLATE.format(self.description or self.key, self.key, data_uri)
292
292
 
293
293
 
294
- # TODO: remove in 1.5.0
294
+ # TODO: remove in 1.6.0
295
295
  @deprecated(
296
296
  version="1.3.0",
297
- reason="'LegacyChartArtifact' will be removed in 1.5.0, use 'ChartArtifact' instead",
297
+ reason="'LegacyChartArtifact' will be removed in 1.6.0, use 'ChartArtifact' instead",
298
298
  category=FutureWarning,
299
299
  )
300
300
  class LegacyChartArtifact(LegacyArtifact):
@@ -365,10 +365,10 @@ class LegacyChartArtifact(LegacyArtifact):
365
365
  )
366
366
 
367
367
 
368
- # TODO: remove in 1.5.0
368
+ # TODO: remove in 1.6.0
369
369
  @deprecated(
370
370
  version="1.3.0",
371
- reason="'LegacyBokehArtifact' will be removed in 1.5.0, use 'BokehArtifact' instead",
371
+ reason="'LegacyBokehArtifact' will be removed in 1.6.0, use 'BokehArtifact' instead",
372
372
  category=FutureWarning,
373
373
  )
374
374
  class LegacyBokehArtifact(LegacyArtifact):
@@ -421,10 +421,10 @@ class LegacyBokehArtifact(LegacyArtifact):
421
421
  return file_html(self._figure, CDN, self.key)
422
422
 
423
423
 
424
- # TODO: remove in 1.5.0
424
+ # TODO: remove in 1.6.0
425
425
  @deprecated(
426
426
  version="1.3.0",
427
- reason="'LegacyPlotlyArtifact' will be removed in 1.5.0, use 'PlotlyArtifact' instead",
427
+ reason="'LegacyPlotlyArtifact' will be removed in 1.6.0, use 'PlotlyArtifact' instead",
428
428
  category=FutureWarning,
429
429
  )
430
430
  class LegacyPlotlyArtifact(LegacyArtifact):
@@ -0,0 +1,14 @@
1
+ # Copyright 2023 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
+ #