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.
- mlrun/__init__.py +2 -35
- mlrun/__main__.py +3 -41
- mlrun/api/api/api.py +6 -0
- mlrun/api/api/endpoints/feature_store.py +0 -4
- mlrun/api/api/endpoints/files.py +14 -2
- mlrun/api/api/endpoints/frontend_spec.py +2 -1
- mlrun/api/api/endpoints/functions.py +95 -59
- mlrun/api/api/endpoints/grafana_proxy.py +9 -9
- mlrun/api/api/endpoints/logs.py +17 -3
- mlrun/api/api/endpoints/model_endpoints.py +3 -2
- mlrun/api/api/endpoints/pipelines.py +1 -5
- mlrun/api/api/endpoints/projects.py +88 -0
- mlrun/api/api/endpoints/runs.py +48 -6
- mlrun/api/api/endpoints/submit.py +2 -1
- mlrun/api/api/endpoints/workflows.py +355 -0
- mlrun/api/api/utils.py +3 -4
- mlrun/api/crud/__init__.py +1 -0
- mlrun/api/crud/client_spec.py +6 -2
- mlrun/api/crud/feature_store.py +5 -0
- mlrun/api/crud/model_monitoring/__init__.py +1 -0
- mlrun/api/crud/model_monitoring/deployment.py +497 -0
- mlrun/api/crud/model_monitoring/grafana.py +96 -42
- mlrun/api/crud/model_monitoring/helpers.py +159 -0
- mlrun/api/crud/model_monitoring/model_endpoints.py +202 -476
- mlrun/api/crud/notifications.py +9 -4
- mlrun/api/crud/pipelines.py +6 -11
- mlrun/api/crud/projects.py +2 -2
- mlrun/api/crud/runtime_resources.py +4 -3
- mlrun/api/crud/runtimes/nuclio/helpers.py +5 -1
- mlrun/api/crud/secrets.py +21 -0
- mlrun/api/crud/workflows.py +352 -0
- mlrun/api/db/base.py +16 -1
- mlrun/api/db/init_db.py +2 -4
- mlrun/api/db/session.py +1 -1
- mlrun/api/db/sqldb/db.py +129 -31
- mlrun/api/db/sqldb/models/models_mysql.py +15 -1
- mlrun/api/db/sqldb/models/models_sqlite.py +16 -2
- mlrun/api/launcher.py +38 -6
- mlrun/api/main.py +3 -2
- mlrun/api/rundb/__init__.py +13 -0
- mlrun/{db → api/rundb}/sqldb.py +36 -84
- mlrun/api/runtime_handlers/__init__.py +56 -0
- mlrun/api/runtime_handlers/base.py +1247 -0
- mlrun/api/runtime_handlers/daskjob.py +209 -0
- mlrun/api/runtime_handlers/kubejob.py +37 -0
- mlrun/api/runtime_handlers/mpijob.py +147 -0
- mlrun/api/runtime_handlers/remotesparkjob.py +29 -0
- mlrun/api/runtime_handlers/sparkjob.py +148 -0
- mlrun/api/schemas/__init__.py +17 -6
- mlrun/api/utils/builder.py +1 -4
- mlrun/api/utils/clients/chief.py +14 -0
- mlrun/api/utils/clients/iguazio.py +33 -33
- mlrun/api/utils/clients/nuclio.py +2 -2
- mlrun/api/utils/periodic.py +9 -2
- mlrun/api/utils/projects/follower.py +14 -7
- mlrun/api/utils/projects/leader.py +2 -1
- mlrun/api/utils/projects/remotes/nop_follower.py +2 -2
- mlrun/api/utils/projects/remotes/nop_leader.py +2 -2
- mlrun/api/utils/runtimes/__init__.py +14 -0
- mlrun/api/utils/runtimes/nuclio.py +43 -0
- mlrun/api/utils/scheduler.py +98 -15
- mlrun/api/utils/singletons/db.py +5 -1
- mlrun/api/utils/singletons/project_member.py +4 -1
- mlrun/api/utils/singletons/scheduler.py +1 -1
- mlrun/artifacts/base.py +6 -6
- mlrun/artifacts/dataset.py +4 -4
- mlrun/artifacts/manager.py +2 -3
- mlrun/artifacts/model.py +2 -2
- mlrun/artifacts/plots.py +8 -8
- mlrun/common/db/__init__.py +14 -0
- mlrun/common/helpers.py +37 -0
- mlrun/{mlutils → common/model_monitoring}/__init__.py +3 -2
- mlrun/common/model_monitoring/helpers.py +69 -0
- mlrun/common/schemas/__init__.py +13 -1
- mlrun/common/schemas/auth.py +4 -1
- mlrun/common/schemas/client_spec.py +1 -1
- mlrun/common/schemas/function.py +17 -0
- mlrun/common/schemas/model_monitoring/__init__.py +48 -0
- mlrun/common/{model_monitoring.py → schemas/model_monitoring/constants.py} +11 -23
- mlrun/common/schemas/model_monitoring/grafana.py +55 -0
- mlrun/common/schemas/{model_endpoints.py → model_monitoring/model_endpoints.py} +32 -65
- mlrun/common/schemas/notification.py +1 -0
- mlrun/common/schemas/object.py +4 -0
- mlrun/common/schemas/project.py +1 -0
- mlrun/common/schemas/regex.py +1 -1
- mlrun/common/schemas/runs.py +1 -8
- mlrun/common/schemas/schedule.py +1 -8
- mlrun/common/schemas/workflow.py +54 -0
- mlrun/config.py +45 -42
- mlrun/datastore/__init__.py +21 -0
- mlrun/datastore/base.py +1 -1
- mlrun/datastore/datastore.py +9 -0
- mlrun/datastore/dbfs_store.py +168 -0
- mlrun/datastore/helpers.py +18 -0
- mlrun/datastore/sources.py +1 -0
- mlrun/datastore/store_resources.py +2 -5
- mlrun/datastore/v3io.py +1 -2
- mlrun/db/__init__.py +4 -68
- mlrun/db/base.py +12 -0
- mlrun/db/factory.py +65 -0
- mlrun/db/httpdb.py +175 -20
- mlrun/db/nopdb.py +4 -2
- mlrun/execution.py +4 -2
- mlrun/feature_store/__init__.py +1 -0
- mlrun/feature_store/api.py +1 -2
- mlrun/feature_store/common.py +2 -1
- mlrun/feature_store/feature_set.py +1 -11
- mlrun/feature_store/feature_vector.py +340 -2
- mlrun/feature_store/ingestion.py +5 -10
- mlrun/feature_store/retrieval/base.py +118 -104
- mlrun/feature_store/retrieval/dask_merger.py +17 -10
- mlrun/feature_store/retrieval/job.py +4 -1
- mlrun/feature_store/retrieval/local_merger.py +18 -18
- mlrun/feature_store/retrieval/spark_merger.py +21 -14
- mlrun/feature_store/retrieval/storey_merger.py +22 -16
- mlrun/kfpops.py +3 -9
- mlrun/launcher/base.py +57 -53
- mlrun/launcher/client.py +5 -4
- mlrun/launcher/factory.py +24 -13
- mlrun/launcher/local.py +6 -6
- mlrun/launcher/remote.py +4 -4
- mlrun/lists.py +0 -11
- mlrun/model.py +11 -17
- mlrun/model_monitoring/__init__.py +2 -22
- mlrun/model_monitoring/features_drift_table.py +1 -1
- mlrun/model_monitoring/helpers.py +22 -210
- mlrun/model_monitoring/model_endpoint.py +1 -1
- mlrun/model_monitoring/model_monitoring_batch.py +127 -50
- mlrun/model_monitoring/prometheus.py +219 -0
- mlrun/model_monitoring/stores/__init__.py +16 -11
- mlrun/model_monitoring/stores/kv_model_endpoint_store.py +95 -23
- mlrun/model_monitoring/stores/models/mysql.py +47 -29
- mlrun/model_monitoring/stores/models/sqlite.py +47 -29
- mlrun/model_monitoring/stores/sql_model_endpoint_store.py +31 -19
- mlrun/model_monitoring/{stream_processing_fs.py → stream_processing.py} +206 -64
- mlrun/model_monitoring/tracking_policy.py +104 -0
- mlrun/package/packager.py +6 -8
- mlrun/package/packagers/default_packager.py +121 -10
- mlrun/package/packagers/numpy_packagers.py +1 -1
- mlrun/platforms/__init__.py +0 -2
- mlrun/platforms/iguazio.py +0 -56
- mlrun/projects/pipelines.py +53 -159
- mlrun/projects/project.py +10 -37
- mlrun/render.py +1 -1
- mlrun/run.py +8 -124
- mlrun/runtimes/__init__.py +6 -42
- mlrun/runtimes/base.py +29 -1249
- mlrun/runtimes/daskjob.py +2 -198
- mlrun/runtimes/funcdoc.py +0 -9
- mlrun/runtimes/function.py +25 -29
- mlrun/runtimes/kubejob.py +5 -29
- mlrun/runtimes/local.py +1 -1
- mlrun/runtimes/mpijob/__init__.py +2 -2
- mlrun/runtimes/mpijob/abstract.py +10 -1
- mlrun/runtimes/mpijob/v1.py +0 -76
- mlrun/runtimes/mpijob/v1alpha1.py +1 -74
- mlrun/runtimes/nuclio.py +3 -2
- mlrun/runtimes/pod.py +28 -18
- mlrun/runtimes/remotesparkjob.py +1 -15
- mlrun/runtimes/serving.py +14 -6
- mlrun/runtimes/sparkjob/__init__.py +0 -1
- mlrun/runtimes/sparkjob/abstract.py +4 -131
- mlrun/runtimes/utils.py +0 -26
- mlrun/serving/routers.py +7 -7
- mlrun/serving/server.py +11 -8
- mlrun/serving/states.py +7 -1
- mlrun/serving/v2_serving.py +6 -6
- mlrun/utils/helpers.py +23 -42
- mlrun/utils/notifications/notification/__init__.py +4 -0
- mlrun/utils/notifications/notification/webhook.py +61 -0
- mlrun/utils/notifications/notification_pusher.py +5 -25
- mlrun/utils/regex.py +7 -2
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.4.0rc25.dist-info → mlrun-1.5.0rc2.dist-info}/METADATA +26 -25
- {mlrun-1.4.0rc25.dist-info → mlrun-1.5.0rc2.dist-info}/RECORD +180 -158
- {mlrun-1.4.0rc25.dist-info → mlrun-1.5.0rc2.dist-info}/WHEEL +1 -1
- mlrun/mlutils/data.py +0 -160
- mlrun/mlutils/models.py +0 -78
- mlrun/mlutils/plots.py +0 -902
- mlrun/utils/model_monitoring.py +0 -249
- /mlrun/{api/db/sqldb/session.py → common/db/sql_session.py} +0 -0
- {mlrun-1.4.0rc25.dist-info → mlrun-1.5.0rc2.dist-info}/LICENSE +0 -0
- {mlrun-1.4.0rc25.dist-info → mlrun-1.5.0rc2.dist-info}/entry_points.txt +0 -0
- {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
|
-
|
|
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
|
-
|
|
31
|
+
project_follower.Member,
|
|
32
32
|
metaclass=mlrun.utils.singleton.AbstractSingleton,
|
|
33
33
|
):
|
|
34
34
|
def __init__(self) -> None:
|
mlrun/api/utils/periodic.py
CHANGED
|
@@ -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(
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
mlrun/api/utils/scheduler.py
CHANGED
|
@@ -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
|
mlrun/api/utils/singletons/db.py
CHANGED
|
@@ -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.
|
|
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.
|
|
701
|
+
# TODO: remove in 1.6.0
|
|
702
702
|
@deprecated(
|
|
703
703
|
version="1.3.0",
|
|
704
|
-
reason="'LegacyArtifact' will be removed in 1.
|
|
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.
|
|
868
|
+
# TODO: remove in 1.6.0
|
|
869
869
|
@deprecated(
|
|
870
870
|
version="1.3.0",
|
|
871
|
-
reason="'LegacyDirArtifact' will be removed in 1.
|
|
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.
|
|
904
|
+
# TODO: remove in 1.6.0
|
|
905
905
|
@deprecated(
|
|
906
906
|
version="1.3.0",
|
|
907
|
-
reason="'LegacyLinkArtifact' will be removed in 1.
|
|
907
|
+
reason="'LegacyLinkArtifact' will be removed in 1.6.0, use 'LinkArtifact' instead",
|
|
908
908
|
category=FutureWarning,
|
|
909
909
|
)
|
|
910
910
|
class LegacyLinkArtifact(LegacyArtifact):
|
mlrun/artifacts/dataset.py
CHANGED
|
@@ -322,10 +322,10 @@ class DatasetArtifact(Artifact):
|
|
|
322
322
|
self.status.stats = stats
|
|
323
323
|
|
|
324
324
|
|
|
325
|
-
# TODO: remove in 1.
|
|
325
|
+
# TODO: remove in 1.6.0
|
|
326
326
|
@deprecated(
|
|
327
327
|
version="1.3.0",
|
|
328
|
-
reason="'LegacyTableArtifact' will be removed in 1.
|
|
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.
|
|
378
|
+
# TODO: remove in 1.6.0
|
|
379
379
|
@deprecated(
|
|
380
380
|
version="1.3.0",
|
|
381
|
-
reason="'LegacyDatasetArtifact' will be removed in 1.
|
|
381
|
+
reason="'LegacyDatasetArtifact' will be removed in 1.6.0, use 'DatasetArtifact' instead",
|
|
382
382
|
category=FutureWarning,
|
|
383
383
|
)
|
|
384
384
|
class LegacyDatasetArtifact(LegacyArtifact):
|
mlrun/artifacts/manager.py
CHANGED
|
@@ -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.
|
|
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.
|
|
373
|
+
# TODO: remove in 1.6.0
|
|
374
374
|
@deprecated(
|
|
375
375
|
version="1.3.0",
|
|
376
|
-
reason="'LegacyModelArtifact' will be removed in 1.
|
|
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.
|
|
247
|
+
# TODO: remove in 1.6.0
|
|
248
248
|
@deprecated(
|
|
249
249
|
version="1.3.0",
|
|
250
|
-
reason="'LegacyPlotArtifact' will be removed in 1.
|
|
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.
|
|
294
|
+
# TODO: remove in 1.6.0
|
|
295
295
|
@deprecated(
|
|
296
296
|
version="1.3.0",
|
|
297
|
-
reason="'LegacyChartArtifact' will be removed in 1.
|
|
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.
|
|
368
|
+
# TODO: remove in 1.6.0
|
|
369
369
|
@deprecated(
|
|
370
370
|
version="1.3.0",
|
|
371
|
-
reason="'LegacyBokehArtifact' will be removed in 1.
|
|
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.
|
|
424
|
+
# TODO: remove in 1.6.0
|
|
425
425
|
@deprecated(
|
|
426
426
|
version="1.3.0",
|
|
427
|
-
reason="'LegacyPlotlyArtifact' will be removed in 1.
|
|
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
|
+
#
|