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
mlrun/api/db/sqldb/db.py CHANGED
@@ -82,7 +82,6 @@ from mlrun.utils import (
82
82
  )
83
83
 
84
84
  NULL = None # Avoid flake8 issuing warnings when comparing in filter
85
- run_time_fmt = "%Y-%m-%dT%H:%M:%S.%fZ"
86
85
  unversioned_tagged_object_uid_prefix = "unversioned-"
87
86
 
88
87
  conflict_messages = [
@@ -1175,6 +1174,63 @@ class SQLDB(DBInterface):
1175
1174
 
1176
1175
  return results
1177
1176
 
1177
+ def store_schedule(
1178
+ self,
1179
+ session: Session,
1180
+ project: str,
1181
+ name: str,
1182
+ kind: mlrun.common.schemas.ScheduleKinds = None,
1183
+ scheduled_object: Any = None,
1184
+ cron_trigger: mlrun.common.schemas.ScheduleCronTrigger = None,
1185
+ labels: Dict = None,
1186
+ last_run_uri: str = None,
1187
+ concurrency_limit: int = None,
1188
+ next_run_time: datetime = None,
1189
+ ) -> typing.Optional[mlrun.common.schemas.ScheduleRecord]:
1190
+ schedule = self.get_schedule(
1191
+ session=session, project=project, name=name, raise_on_not_found=False
1192
+ )
1193
+ schedule_exist = schedule is not None
1194
+
1195
+ if not schedule_exist:
1196
+ schedule = Schedule(
1197
+ project=project,
1198
+ name=name,
1199
+ kind=kind.value,
1200
+ creation_time=datetime.now(timezone.utc),
1201
+ concurrency_limit=concurrency_limit,
1202
+ next_run_time=next_run_time,
1203
+ scheduled_object=scheduled_object,
1204
+ cron_trigger=cron_trigger,
1205
+ )
1206
+ labels = labels or {}
1207
+
1208
+ self._update_schedule_body(
1209
+ schedule=schedule,
1210
+ scheduled_object=scheduled_object,
1211
+ cron_trigger=cron_trigger,
1212
+ labels=labels,
1213
+ last_run_uri=last_run_uri,
1214
+ concurrency_limit=concurrency_limit,
1215
+ next_run_time=next_run_time,
1216
+ )
1217
+
1218
+ logger.debug(
1219
+ "Storing schedule to db",
1220
+ project=project,
1221
+ name=name,
1222
+ kind=kind,
1223
+ cron_trigger=cron_trigger,
1224
+ labels=labels,
1225
+ concurrency_limit=concurrency_limit,
1226
+ scheduled_object=scheduled_object,
1227
+ )
1228
+
1229
+ self._upsert(session, [schedule])
1230
+
1231
+ if schedule_exist:
1232
+ return schedule
1233
+
1178
1234
  def create_schedule(
1179
1235
  self,
1180
1236
  session: Session,
@@ -1233,6 +1289,37 @@ class SQLDB(DBInterface):
1233
1289
  ):
1234
1290
  schedule = self._get_schedule_record(session, project, name)
1235
1291
 
1292
+ self._update_schedule_body(
1293
+ schedule=schedule,
1294
+ scheduled_object=scheduled_object,
1295
+ cron_trigger=cron_trigger,
1296
+ labels=labels,
1297
+ last_run_uri=last_run_uri,
1298
+ concurrency_limit=concurrency_limit,
1299
+ next_run_time=next_run_time,
1300
+ )
1301
+
1302
+ logger.debug(
1303
+ "Updating schedule in db",
1304
+ project=project,
1305
+ name=name,
1306
+ cron_trigger=cron_trigger,
1307
+ labels=labels,
1308
+ concurrency_limit=concurrency_limit,
1309
+ next_run_time=next_run_time,
1310
+ )
1311
+ self._upsert(session, [schedule])
1312
+
1313
+ @staticmethod
1314
+ def _update_schedule_body(
1315
+ schedule: mlrun.common.schemas.ScheduleRecord,
1316
+ scheduled_object: Any = None,
1317
+ cron_trigger: mlrun.common.schemas.ScheduleCronTrigger = None,
1318
+ labels: Dict = None,
1319
+ last_run_uri: str = None,
1320
+ concurrency_limit: int = None,
1321
+ next_run_time: datetime = None,
1322
+ ):
1236
1323
  # explicitly ensure the updated fields are not None, as they can be empty strings/dictionaries etc.
1237
1324
  if scheduled_object is not None:
1238
1325
  schedule.scheduled_object = scheduled_object
@@ -1254,17 +1341,6 @@ class SQLDB(DBInterface):
1254
1341
  # saved in the DB in UTC timezone, therefore we transform next_run_time to UTC as well.
1255
1342
  schedule.next_run_time = next_run_time.astimezone(pytz.utc)
1256
1343
 
1257
- logger.debug(
1258
- "Updating schedule in db",
1259
- project=project,
1260
- name=name,
1261
- cron_trigger=cron_trigger,
1262
- labels=labels,
1263
- concurrency_limit=concurrency_limit,
1264
- next_run_time=next_run_time,
1265
- )
1266
- self._upsert(session, [schedule])
1267
-
1268
1344
  def list_schedules(
1269
1345
  self,
1270
1346
  session: Session,
@@ -1287,19 +1363,23 @@ class SQLDB(DBInterface):
1287
1363
  return schedules
1288
1364
 
1289
1365
  def get_schedule(
1290
- self, session: Session, project: str, name: str
1291
- ) -> mlrun.common.schemas.ScheduleRecord:
1366
+ self, session: Session, project: str, name: str, raise_on_not_found: bool = True
1367
+ ) -> typing.Optional[mlrun.common.schemas.ScheduleRecord]:
1292
1368
  logger.debug("Getting schedule from db", project=project, name=name)
1293
- schedule_record = self._get_schedule_record(session, project, name)
1369
+ schedule_record = self._get_schedule_record(
1370
+ session, project, name, raise_on_not_found
1371
+ )
1372
+ if not schedule_record:
1373
+ return
1294
1374
  schedule = self._transform_schedule_record_to_scheme(schedule_record)
1295
1375
  return schedule
1296
1376
 
1297
1377
  def _get_schedule_record(
1298
- self, session: Session, project: str, name: str
1378
+ self, session: Session, project: str, name: str, raise_on_not_found: bool = True
1299
1379
  ) -> mlrun.common.schemas.ScheduleRecord:
1300
1380
  query = self._query(session, Schedule, project=project, name=name)
1301
1381
  schedule_record = query.one_or_none()
1302
- if not schedule_record:
1382
+ if not schedule_record and raise_on_not_found:
1303
1383
  raise mlrun.errors.MLRunNotFoundError(
1304
1384
  f"Schedule not found: project={project}, name={name}"
1305
1385
  )
@@ -2866,32 +2946,50 @@ class SQLDB(DBInterface):
2866
2946
  def _try_commit_obj():
2867
2947
  try:
2868
2948
  session.commit()
2869
- except SQLAlchemyError as err:
2949
+ except SQLAlchemyError as sql_err:
2870
2950
  session.rollback()
2871
- cls = objects[0].__class__.__name__
2872
- if "database is locked" in str(err):
2951
+ classes = list(set([object_.__class__.__name__ for object_ in objects]))
2952
+
2953
+ # if the database is locked, we raise a retryable error
2954
+ if "database is locked" in str(sql_err):
2873
2955
  logger.warning(
2874
- "Database is locked. Retrying", cls=cls, err=str(err)
2956
+ "Database is locked. Retrying",
2957
+ classes_to_commit=classes,
2958
+ err=str(sql_err),
2875
2959
  )
2876
2960
  raise mlrun.errors.MLRunRuntimeError(
2877
2961
  "Failed committing changes, database is locked"
2878
- ) from err
2962
+ ) from sql_err
2963
+
2964
+ # the error is not retryable, so we try to identify weather there was a conflict or not
2965
+ # either way - we wrap the error with a fatal error so the retry mechanism will stop
2879
2966
  logger.warning(
2880
- "Failed committing changes to DB", cls=cls, err=err_to_str(err)
2967
+ "Failed committing changes to DB",
2968
+ classes=classes,
2969
+ err=err_to_str(sql_err),
2881
2970
  )
2882
2971
  if not ignore:
2972
+ # get the identifiers of the objects that failed to commit, for logging purposes
2883
2973
  identifiers = ",".join(
2884
2974
  object_.get_identifier_string() for object_ in objects
2885
2975
  )
2886
- # We want to retry only when database is locked so for any other scenario escalate to fatal failure
2976
+
2977
+ mlrun_error = mlrun.errors.MLRunRuntimeError(
2978
+ f"Failed committing changes to DB. classes={classes} objects={identifiers}"
2979
+ )
2980
+
2981
+ # check if the error is a conflict error
2982
+ if any([message in str(sql_err) for message in conflict_messages]):
2983
+ mlrun_error = mlrun.errors.MLRunConflictError(
2984
+ f"Conflict - at least one of the objects already exists: {identifiers}"
2985
+ )
2986
+
2987
+ # we want to keep the exception stack trace, but we also want the retry mechanism to stop
2988
+ # so, we raise a new indicative exception from the original sql exception (this keeps
2989
+ # the stack trace intact), and then wrap it with a fatal error (which stops the retry mechanism).
2990
+ # Note - this way, the exception is raised from this code section, and not from the retry function.
2887
2991
  try:
2888
- if any([message in str(err) for message in conflict_messages]):
2889
- raise mlrun.errors.MLRunConflictError(
2890
- f"Conflict - {cls} already exists: {identifiers}"
2891
- ) from err
2892
- raise mlrun.errors.MLRunRuntimeError(
2893
- f"Failed committing changes to DB. class={cls} objects={identifiers}"
2894
- ) from err
2992
+ raise mlrun_error from sql_err
2895
2993
  except (
2896
2994
  mlrun.errors.MLRunRuntimeError,
2897
2995
  mlrun.errors.MLRunConflictError,
@@ -38,7 +38,6 @@ from mlrun.api.utils.db.sql_collation import SQLCollationUtil
38
38
 
39
39
  Base = declarative_base()
40
40
  NULL = None # Avoid flake8 issuing warnings when comparing in filter
41
- run_time_fmt = "%Y-%m-%dT%H:%M:%S.%fZ"
42
41
 
43
42
 
44
43
  def make_label(table):
@@ -53,6 +52,9 @@ def make_label(table):
53
52
  value = Column(String(255, collation=SQLCollationUtil.collation()))
54
53
  parent = Column(Integer, ForeignKey(f"{table}.id"))
55
54
 
55
+ def get_identifier_string(self) -> str:
56
+ return f"{self.parent}/{self.name}/{self.value}"
57
+
56
58
  return Label
57
59
 
58
60
 
@@ -86,6 +88,9 @@ def make_tag_v2(table):
86
88
  obj_id = Column(Integer, ForeignKey(f"{table}.id"))
87
89
  obj_name = Column(String(255, collation=SQLCollationUtil.collation()))
88
90
 
91
+ def get_identifier_string(self) -> str:
92
+ return f"{self.project}/{self.name}"
93
+
89
94
  return Tag
90
95
 
91
96
 
@@ -258,6 +263,9 @@ with warnings.catch_warnings():
258
263
  state = Column(String(255, collation=SQLCollationUtil.collation()))
259
264
  timeout = Column(Integer)
260
265
 
266
+ def get_identifier_string(self) -> str:
267
+ return f"{self.project}/{self.name}"
268
+
261
269
  class Schedule(Base, mlrun.utils.db.BaseModel):
262
270
  __tablename__ = "schedules_v2"
263
271
  __table_args__ = (UniqueConstraint("project", "name", name="_schedules_v2_uc"),)
@@ -317,6 +325,9 @@ with warnings.catch_warnings():
317
325
  id = Column(Integer, primary_key=True)
318
326
  name = Column(String(255, collation=SQLCollationUtil.collation()))
319
327
 
328
+ def get_identifier_string(self) -> str:
329
+ return f"{self.name}"
330
+
320
331
  class Project(Base, mlrun.utils.db.BaseModel):
321
332
  __tablename__ = "projects"
322
333
  # For now since we use project name a lot
@@ -508,6 +519,9 @@ with warnings.catch_warnings():
508
519
  default=datetime.now(timezone.utc),
509
520
  )
510
521
 
522
+ def get_identifier_string(self) -> str:
523
+ return f"{self.version}"
524
+
511
525
 
512
526
  # Must be after all table definitions
513
527
  _tagged = [cls for cls in Base.__subclasses__() if hasattr(cls, "Tag")]
@@ -39,7 +39,6 @@ from mlrun.api.utils.db.sql_collation import SQLCollationUtil
39
39
 
40
40
  Base = declarative_base()
41
41
  NULL = None # Avoid flake8 issuing warnings when comparing in filter
42
- run_time_fmt = "%Y-%m-%dT%H:%M:%S.%fZ"
43
42
 
44
43
 
45
44
  def make_label(table):
@@ -54,6 +53,9 @@ def make_label(table):
54
53
  value = Column(String(255, collation=SQLCollationUtil.collation()))
55
54
  parent = Column(Integer, ForeignKey(f"{table}.id"))
56
55
 
56
+ def get_identifier_string(self) -> str:
57
+ return f"{self.parent}/{self.name}/{self.value}"
58
+
57
59
  return Label
58
60
 
59
61
 
@@ -90,6 +92,9 @@ def make_tag_v2(table):
90
92
  ForeignKey(f"{table}.name"),
91
93
  )
92
94
 
95
+ def get_identifier_string(self) -> str:
96
+ return f"{self.project}/{self.name}"
97
+
93
98
  return Tag
94
99
 
95
100
 
@@ -242,6 +247,9 @@ with warnings.catch_warnings():
242
247
  state = Column(String(255, collation=SQLCollationUtil.collation()))
243
248
  timeout = Column(Integer)
244
249
 
250
+ def get_identifier_string(self) -> str:
251
+ return f"{self.project}/{self.name}"
252
+
245
253
  class Schedule(Base, mlrun.utils.db.BaseModel):
246
254
  __tablename__ = "schedules_v2"
247
255
  __table_args__ = (UniqueConstraint("project", "name", name="_schedules_v2_uc"),)
@@ -301,6 +309,9 @@ with warnings.catch_warnings():
301
309
  id = Column(Integer, primary_key=True)
302
310
  name = Column(String(255, collation=SQLCollationUtil.collation()))
303
311
 
312
+ def get_identifier_string(self) -> str:
313
+ return f"{self.name}"
314
+
304
315
  class Project(Base, mlrun.utils.db.BaseModel):
305
316
  __tablename__ = "projects"
306
317
  # For now since we use project name a lot
@@ -347,7 +358,7 @@ with warnings.catch_warnings():
347
358
  labels = relationship(Label, cascade="all, delete-orphan")
348
359
 
349
360
  def get_identifier_string(self) -> str:
350
- return f"{self.project}/{self.name}"
361
+ return f"{self.feature_set_id}/{self.name}"
351
362
 
352
363
  class Entity(Base, mlrun.utils.db.BaseModel):
353
364
  __tablename__ = "entities"
@@ -464,6 +475,9 @@ with warnings.catch_warnings():
464
475
  version = Column(String(255, collation=SQLCollationUtil.collation()))
465
476
  created = Column(TIMESTAMP, default=datetime.now(timezone.utc))
466
477
 
478
+ def get_identifier_string(self) -> str:
479
+ return f"{self.version}"
480
+
467
481
 
468
482
  # Must be after all table definitions
469
483
  _tagged = [cls for cls in Base.__subclasses__() if hasattr(cls, "Tag")]
mlrun/api/launcher.py CHANGED
@@ -13,20 +13,40 @@
13
13
  # limitations under the License.
14
14
  from typing import Dict, List, Optional, Union
15
15
 
16
+ from dependency_injector import containers, providers
17
+
16
18
  import mlrun.api.crud
17
- import mlrun.api.db.sqldb.session
19
+ import mlrun.common.db.sql_session
18
20
  import mlrun.common.schemas.schedule
19
21
  import mlrun.config
20
22
  import mlrun.execution
21
- import mlrun.launcher.base
23
+ import mlrun.launcher.base as launcher
24
+ import mlrun.launcher.factory
22
25
  import mlrun.runtimes
23
26
  import mlrun.runtimes.generators
24
27
  import mlrun.runtimes.utils
25
28
  import mlrun.utils
26
29
  import mlrun.utils.regex
27
30
 
31
+ # must be at the bottom to avoid circular import conflicts and can't use 'from' notation because unit tests mock this
32
+ import mlrun.api.api.utils # isort:skip
33
+
34
+
35
+ class ServerSideLauncher(launcher.BaseLauncher):
36
+ def __init__(
37
+ self,
38
+ local: bool = False,
39
+ auth_info: Optional[mlrun.common.schemas.AuthInfo] = None,
40
+ **kwargs,
41
+ ):
42
+ super().__init__(**kwargs)
43
+ if local:
44
+ raise mlrun.errors.MLRunInternalServerError(
45
+ "Launch of local run inside the server is not allowed"
46
+ )
47
+
48
+ self._auth_info = auth_info
28
49
 
29
- class ServerSideLauncher(mlrun.launcher.base.BaseLauncher):
30
50
  def launch(
31
51
  self,
32
52
  runtime: mlrun.runtimes.BaseRuntime,
@@ -145,15 +165,21 @@ class ServerSideLauncher(mlrun.launcher.base.BaseLauncher):
145
165
 
146
166
  return self._wrap_run_result(runtime, result, run, err=last_err)
147
167
 
148
- @staticmethod
149
168
  def enrich_runtime(
150
- runtime: "mlrun.runtimes.base.BaseRuntime", project_name: Optional[str] = ""
169
+ self,
170
+ runtime: "mlrun.runtimes.base.BaseRuntime",
171
+ project_name: Optional[str] = "",
151
172
  ):
152
173
  """
153
174
  Enrich the runtime object with the project spec and metadata.
154
175
  This is done only on the server side, since it's the source of truth for the project, and we want to keep the
155
176
  client side enrichment as minimal as possible.
156
177
  """
178
+ if self._auth_info:
179
+ mlrun.api.api.utils.apply_enrichment_and_validation_on_function(
180
+ runtime, self._auth_info
181
+ )
182
+
157
183
  # ensure the runtime has a project before we enrich it with the project's spec
158
184
  runtime.metadata.project = (
159
185
  project_name
@@ -175,7 +201,7 @@ class ServerSideLauncher(mlrun.launcher.base.BaseLauncher):
175
201
 
176
202
  # If in the api server, we can assume that watch=False, so we save notification
177
203
  # configs to the DB, for the run monitor to later pick up and push.
178
- session = mlrun.api.db.sqldb.session.create_session()
204
+ session = mlrun.common.db.sql_session.create_session()
179
205
  mlrun.api.crud.Notifications().store_run_notifications(
180
206
  session,
181
207
  runobj.spec.notifications,
@@ -194,3 +220,9 @@ class ServerSideLauncher(mlrun.launcher.base.BaseLauncher):
194
220
  struct, runtime.metadata.name, runtime.metadata.project, versioned=True
195
221
  )
196
222
  run.spec.function = runtime._function_uri(hash_key=hash_key)
223
+
224
+
225
+ # Once this file is imported it will set the container server side launcher
226
+ @containers.override(mlrun.launcher.factory.LauncherContainer)
227
+ class ServerSideLauncherContainer(containers.DeclarativeContainer):
228
+ server_side_launcher = providers.Factory(ServerSideLauncher)
mlrun/api/main.py CHANGED
@@ -37,6 +37,7 @@ from mlrun.api.api.api import api_router
37
37
  from mlrun.api.db.session import close_session, create_session
38
38
  from mlrun.api.initial_data import init_data
39
39
  from mlrun.api.middlewares import init_middlewares
40
+ from mlrun.api.runtime_handlers import get_runtime_handler
40
41
  from mlrun.api.utils.periodic import (
41
42
  cancel_all_periodic_functions,
42
43
  cancel_periodic_function,
@@ -52,7 +53,7 @@ from mlrun.api.utils.singletons.project_member import (
52
53
  from mlrun.api.utils.singletons.scheduler import get_scheduler, initialize_scheduler
53
54
  from mlrun.config import config
54
55
  from mlrun.errors import err_to_str
55
- from mlrun.runtimes import RuntimeClassMode, RuntimeKinds, get_runtime_handler
56
+ from mlrun.runtimes import RuntimeClassMode, RuntimeKinds
56
57
  from mlrun.utils import logger
57
58
 
58
59
  API_PREFIX = "/api"
@@ -616,7 +617,7 @@ def _push_terminal_run_notifications(db: mlrun.api.db.base.DBInterface, db_sessi
616
617
  logger.debug(
617
618
  "Got terminal runs with configured notifications", runs_amount=len(runs)
618
619
  )
619
- mlrun.utils.notifications.NotificationPusher(unmasked_runs).push(db)
620
+ mlrun.utils.notifications.NotificationPusher(unmasked_runs).push()
620
621
 
621
622
  _last_notification_push_time = now
622
623
 
@@ -0,0 +1,13 @@
1
+ # Copyright 2023 MLRun Authors
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.