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
mlrun/api/crud/notifications.py
CHANGED
|
@@ -34,13 +34,18 @@ class Notifications(
|
|
|
34
34
|
notification_objects: typing.List[mlrun.model.Notification],
|
|
35
35
|
run_uid: str,
|
|
36
36
|
project: str = None,
|
|
37
|
+
mask_params: bool = True,
|
|
37
38
|
):
|
|
38
39
|
project = project or mlrun.mlconf.default_project
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
|
|
41
|
+
# we don't mask the notification params when it's a status update as they are already masked
|
|
42
|
+
notification_objects_to_store = notification_objects
|
|
43
|
+
if mask_params:
|
|
44
|
+
notification_objects_to_store = (
|
|
45
|
+
mlrun.api.api.utils.validate_and_mask_notification_list(
|
|
46
|
+
notification_objects, run_uid, project
|
|
47
|
+
)
|
|
42
48
|
)
|
|
43
|
-
)
|
|
44
49
|
|
|
45
50
|
mlrun.api.utils.singletons.db.get_db().store_run_notifications(
|
|
46
51
|
session, notification_objects_to_store, run_uid, project
|
mlrun/api/crud/pipelines.py
CHANGED
|
@@ -25,6 +25,7 @@ import sqlalchemy.orm
|
|
|
25
25
|
|
|
26
26
|
import mlrun
|
|
27
27
|
import mlrun.api.api.utils
|
|
28
|
+
import mlrun.common.helpers
|
|
28
29
|
import mlrun.common.schemas
|
|
29
30
|
import mlrun.errors
|
|
30
31
|
import mlrun.kfpops
|
|
@@ -94,7 +95,7 @@ class Pipelines(
|
|
|
94
95
|
runs = [run.to_dict() for run in response.runs or []]
|
|
95
96
|
total_size = response.total_size
|
|
96
97
|
next_page_token = response.next_page_token
|
|
97
|
-
runs = self._format_runs(
|
|
98
|
+
runs = self._format_runs(runs, format_)
|
|
98
99
|
|
|
99
100
|
return total_size, next_page_token, runs
|
|
100
101
|
|
|
@@ -130,7 +131,6 @@ class Pipelines(
|
|
|
130
131
|
|
|
131
132
|
def get_pipeline(
|
|
132
133
|
self,
|
|
133
|
-
db_session: sqlalchemy.orm.Session,
|
|
134
134
|
run_id: str,
|
|
135
135
|
project: typing.Optional[str] = None,
|
|
136
136
|
namespace: typing.Optional[str] = None,
|
|
@@ -148,9 +148,7 @@ class Pipelines(
|
|
|
148
148
|
raise mlrun.errors.MLRunNotFoundError(
|
|
149
149
|
f"Pipeline run with id {run_id} is not of project {project}"
|
|
150
150
|
)
|
|
151
|
-
run = self._format_run(
|
|
152
|
-
db_session, run, format_, api_run_detail.to_dict()
|
|
153
|
-
)
|
|
151
|
+
run = self._format_run(run, format_, api_run_detail.to_dict())
|
|
154
152
|
except kfp_server_api.ApiException as exc:
|
|
155
153
|
mlrun.errors.raise_for_status_code(int(exc.status), err_to_str(exc))
|
|
156
154
|
except mlrun.errors.MLRunHTTPStatusError:
|
|
@@ -227,18 +225,16 @@ class Pipelines(
|
|
|
227
225
|
|
|
228
226
|
def _format_runs(
|
|
229
227
|
self,
|
|
230
|
-
db_session: sqlalchemy.orm.Session,
|
|
231
228
|
runs: typing.List[dict],
|
|
232
229
|
format_: mlrun.common.schemas.PipelinesFormat = mlrun.common.schemas.PipelinesFormat.metadata_only,
|
|
233
230
|
) -> typing.List[dict]:
|
|
234
231
|
formatted_runs = []
|
|
235
232
|
for run in runs:
|
|
236
|
-
formatted_runs.append(self._format_run(
|
|
233
|
+
formatted_runs.append(self._format_run(run, format_))
|
|
237
234
|
return formatted_runs
|
|
238
235
|
|
|
239
236
|
def _format_run(
|
|
240
237
|
self,
|
|
241
|
-
db_session: sqlalchemy.orm.Session,
|
|
242
238
|
run: dict,
|
|
243
239
|
format_: mlrun.common.schemas.PipelinesFormat = mlrun.common.schemas.PipelinesFormat.metadata_only,
|
|
244
240
|
api_run_detail: typing.Optional[dict] = None,
|
|
@@ -257,7 +253,7 @@ class Pipelines(
|
|
|
257
253
|
"The full kfp api_run_detail object is needed to generate the summary format"
|
|
258
254
|
)
|
|
259
255
|
return mlrun.kfpops.format_summary_from_kfp_run(
|
|
260
|
-
api_run_detail, run["project"]
|
|
256
|
+
api_run_detail, run["project"]
|
|
261
257
|
)
|
|
262
258
|
else:
|
|
263
259
|
raise NotImplementedError(
|
|
@@ -290,7 +286,7 @@ class Pipelines(
|
|
|
290
286
|
_,
|
|
291
287
|
_,
|
|
292
288
|
_,
|
|
293
|
-
) = mlrun.
|
|
289
|
+
) = mlrun.common.helpers.parse_versioned_object_uri(
|
|
294
290
|
function_url[len("db://") :]
|
|
295
291
|
)
|
|
296
292
|
if project:
|
|
@@ -316,7 +312,6 @@ class Pipelines(
|
|
|
316
312
|
return None
|
|
317
313
|
|
|
318
314
|
def resolve_project_from_pipeline(self, pipeline):
|
|
319
|
-
|
|
320
315
|
workflow_manifest = json.loads(
|
|
321
316
|
pipeline.get("pipeline_spec", {}).get("workflow_manifest") or "{}"
|
|
322
317
|
)
|
mlrun/api/crud/projects.py
CHANGED
|
@@ -24,7 +24,7 @@ import sqlalchemy.orm
|
|
|
24
24
|
import mlrun.api.crud
|
|
25
25
|
import mlrun.api.db.session
|
|
26
26
|
import mlrun.api.utils.events.events_factory as events_factory
|
|
27
|
-
import mlrun.api.utils.projects.remotes.follower
|
|
27
|
+
import mlrun.api.utils.projects.remotes.follower as project_follower
|
|
28
28
|
import mlrun.api.utils.singletons.db
|
|
29
29
|
import mlrun.api.utils.singletons.k8s
|
|
30
30
|
import mlrun.api.utils.singletons.scheduler
|
|
@@ -35,7 +35,7 @@ from mlrun.utils import logger
|
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
class Projects(
|
|
38
|
-
|
|
38
|
+
project_follower.Member,
|
|
39
39
|
metaclass=mlrun.utils.singleton.AbstractSingleton,
|
|
40
40
|
):
|
|
41
41
|
def __init__(self) -> None:
|
|
@@ -18,6 +18,7 @@ import mergedeep
|
|
|
18
18
|
import sqlalchemy.orm
|
|
19
19
|
|
|
20
20
|
import mlrun.api.api.utils
|
|
21
|
+
import mlrun.api.runtime_handlers
|
|
21
22
|
import mlrun.api.utils.projects.remotes.follower
|
|
22
23
|
import mlrun.api.utils.singletons.db
|
|
23
24
|
import mlrun.common.schemas
|
|
@@ -50,7 +51,7 @@ class RuntimeResources(
|
|
|
50
51
|
self.validate_runtime_resources_kind(kind)
|
|
51
52
|
kinds = [kind]
|
|
52
53
|
for kind in kinds:
|
|
53
|
-
runtime_handler = mlrun.
|
|
54
|
+
runtime_handler = mlrun.api.runtime_handlers.get_runtime_handler(kind)
|
|
54
55
|
resources = runtime_handler.list_resources(
|
|
55
56
|
project, object_id, label_selector, group_by
|
|
56
57
|
)
|
|
@@ -88,7 +89,7 @@ class RuntimeResources(
|
|
|
88
89
|
)
|
|
89
90
|
runtimes_resources_output = [] if group_by is None else {}
|
|
90
91
|
for kind, runtime_resources_list in runtime_resources_by_kind.items():
|
|
91
|
-
runtime_handler = mlrun.
|
|
92
|
+
runtime_handler = mlrun.api.runtime_handlers.get_runtime_handler(kind)
|
|
92
93
|
resources = runtime_handler.build_output_from_runtime_resources(
|
|
93
94
|
runtime_resources_list, group_by
|
|
94
95
|
)
|
|
@@ -122,7 +123,7 @@ class RuntimeResources(
|
|
|
122
123
|
self.validate_runtime_resources_kind(kind)
|
|
123
124
|
kinds = [kind]
|
|
124
125
|
for kind in kinds:
|
|
125
|
-
runtime_handler = mlrun.
|
|
126
|
+
runtime_handler = mlrun.api.runtime_handlers.get_runtime_handler(kind)
|
|
126
127
|
if object_id:
|
|
127
128
|
runtime_handler.delete_runtime_object_resources(
|
|
128
129
|
mlrun.api.utils.singletons.db.get_db(),
|
|
@@ -17,6 +17,8 @@ import urllib.parse
|
|
|
17
17
|
import semver
|
|
18
18
|
|
|
19
19
|
import mlrun
|
|
20
|
+
import mlrun.api.utils.clients.nuclio
|
|
21
|
+
import mlrun.api.utils.runtimes.nuclio
|
|
20
22
|
import mlrun.api.utils.singletons.k8s
|
|
21
23
|
import mlrun.runtimes
|
|
22
24
|
from mlrun.utils import logger
|
|
@@ -190,7 +192,9 @@ def is_nuclio_version_in_range(min_version: str, max_version: str) -> bool:
|
|
|
190
192
|
try:
|
|
191
193
|
parsed_min_version = semver.VersionInfo.parse(min_version)
|
|
192
194
|
parsed_max_version = semver.VersionInfo.parse(max_version)
|
|
193
|
-
resolved_nuclio_version =
|
|
195
|
+
resolved_nuclio_version = (
|
|
196
|
+
mlrun.api.utils.runtimes.nuclio.resolve_nuclio_version()
|
|
197
|
+
)
|
|
194
198
|
parsed_current_version = semver.VersionInfo.parse(resolved_nuclio_version)
|
|
195
199
|
except ValueError:
|
|
196
200
|
logger.warning(
|
mlrun/api/crud/secrets.py
CHANGED
|
@@ -17,9 +17,11 @@ import json
|
|
|
17
17
|
import typing
|
|
18
18
|
import uuid
|
|
19
19
|
|
|
20
|
+
import mlrun.api
|
|
20
21
|
import mlrun.api.utils.clients.iguazio
|
|
21
22
|
import mlrun.api.utils.events.events_factory as events_factory
|
|
22
23
|
import mlrun.api.utils.singletons.k8s
|
|
24
|
+
import mlrun.common
|
|
23
25
|
import mlrun.common.schemas
|
|
24
26
|
import mlrun.errors
|
|
25
27
|
import mlrun.utils.helpers
|
|
@@ -531,3 +533,22 @@ class Secrets(
|
|
|
531
533
|
@staticmethod
|
|
532
534
|
def _generate_uuid() -> str:
|
|
533
535
|
return str(uuid.uuid4())
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
def get_project_secret_provider(project: str) -> typing.Callable:
|
|
539
|
+
"""Implement secret provider for handle the related project secret on the API side.
|
|
540
|
+
|
|
541
|
+
:param project: Project name.
|
|
542
|
+
|
|
543
|
+
:return: A secret provider function.
|
|
544
|
+
"""
|
|
545
|
+
|
|
546
|
+
def secret_provider(key: str):
|
|
547
|
+
return mlrun.api.crud.secrets.Secrets().get_project_secret(
|
|
548
|
+
project=project,
|
|
549
|
+
provider=mlrun.common.schemas.secret.SecretProviderName.kubernetes,
|
|
550
|
+
allow_secrets_from_k8s=True,
|
|
551
|
+
secret_key=key,
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
return secret_provider
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
# Copyright 2018 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 uuid
|
|
16
|
+
from typing import Dict
|
|
17
|
+
|
|
18
|
+
from sqlalchemy.orm import Session
|
|
19
|
+
|
|
20
|
+
import mlrun.common.schemas
|
|
21
|
+
import mlrun.utils.singleton
|
|
22
|
+
from mlrun.api.api.utils import (
|
|
23
|
+
apply_enrichment_and_validation_on_function,
|
|
24
|
+
get_run_db_instance,
|
|
25
|
+
get_scheduler,
|
|
26
|
+
)
|
|
27
|
+
from mlrun.config import config
|
|
28
|
+
from mlrun.model import Credentials, RunMetadata, RunObject, RunSpec
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class WorkflowRunners(
|
|
32
|
+
metaclass=mlrun.utils.singleton.Singleton,
|
|
33
|
+
):
|
|
34
|
+
@staticmethod
|
|
35
|
+
def create_runner(
|
|
36
|
+
run_name: str,
|
|
37
|
+
project: str,
|
|
38
|
+
db_session: Session,
|
|
39
|
+
auth_info: mlrun.common.schemas.AuthInfo,
|
|
40
|
+
image: str,
|
|
41
|
+
) -> mlrun.run.KubejobRuntime:
|
|
42
|
+
"""
|
|
43
|
+
Creating the base object for the workflow runner function with
|
|
44
|
+
all the necessary metadata to create it on server-side.
|
|
45
|
+
|
|
46
|
+
:param run_name: workflow-runner function name
|
|
47
|
+
:param project: project name
|
|
48
|
+
:param db_session: session that manages the current dialog with the database
|
|
49
|
+
:param auth_info: auth info of the request
|
|
50
|
+
:param image: image for the workflow runner job
|
|
51
|
+
|
|
52
|
+
:returns: workflow runner object
|
|
53
|
+
"""
|
|
54
|
+
runner = mlrun.new_function(
|
|
55
|
+
name=run_name,
|
|
56
|
+
project=project,
|
|
57
|
+
kind=mlrun.runtimes.RuntimeKinds.job,
|
|
58
|
+
# For preventing deployment:
|
|
59
|
+
image=image,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
runner.set_db_connection(get_run_db_instance(db_session))
|
|
63
|
+
|
|
64
|
+
# Enrichment and validation requires access key
|
|
65
|
+
runner.metadata.credentials.access_key = Credentials.generate_access_key
|
|
66
|
+
|
|
67
|
+
apply_enrichment_and_validation_on_function(
|
|
68
|
+
function=runner,
|
|
69
|
+
auth_info=auth_info,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
runner.save()
|
|
73
|
+
return runner
|
|
74
|
+
|
|
75
|
+
def schedule(
|
|
76
|
+
self,
|
|
77
|
+
runner: mlrun.run.KubejobRuntime,
|
|
78
|
+
project: mlrun.common.schemas.Project,
|
|
79
|
+
workflow_request: mlrun.common.schemas.WorkflowRequest,
|
|
80
|
+
db_session: Session = None,
|
|
81
|
+
auth_info: mlrun.common.schemas.AuthInfo = None,
|
|
82
|
+
):
|
|
83
|
+
"""
|
|
84
|
+
Schedule workflow runner.
|
|
85
|
+
|
|
86
|
+
:param runner: workflow runner function object
|
|
87
|
+
:param project: MLRun project
|
|
88
|
+
:param workflow_request: contains the workflow spec, that will be scheduled
|
|
89
|
+
:param db_session: session that manages the current dialog with the database
|
|
90
|
+
:param auth_info: auth info of the request
|
|
91
|
+
"""
|
|
92
|
+
labels = {
|
|
93
|
+
"job-type": "workflow-runner",
|
|
94
|
+
"workflow": workflow_request.spec.name,
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
run_spec = self._prepare_run_object_for_scheduling(
|
|
98
|
+
project=project,
|
|
99
|
+
workflow_request=workflow_request,
|
|
100
|
+
labels=labels,
|
|
101
|
+
)
|
|
102
|
+
# this includes filling the spec.function which is required for submit run
|
|
103
|
+
runner._store_function(
|
|
104
|
+
runspec=run_spec, meta=run_spec.metadata, db=runner._get_db()
|
|
105
|
+
)
|
|
106
|
+
workflow_spec = workflow_request.spec
|
|
107
|
+
schedule = workflow_spec.schedule
|
|
108
|
+
scheduled_object = {
|
|
109
|
+
"task": run_spec.to_dict(),
|
|
110
|
+
"schedule": schedule,
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
get_scheduler().store_schedule(
|
|
114
|
+
db_session=db_session,
|
|
115
|
+
auth_info=auth_info,
|
|
116
|
+
project=project.metadata.name,
|
|
117
|
+
name=workflow_spec.name,
|
|
118
|
+
scheduled_object=scheduled_object,
|
|
119
|
+
cron_trigger=schedule,
|
|
120
|
+
labels=runner.metadata.labels,
|
|
121
|
+
kind=mlrun.common.schemas.ScheduleKinds.job,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
def _prepare_run_object_for_scheduling(
|
|
125
|
+
self,
|
|
126
|
+
project: mlrun.common.schemas.Project,
|
|
127
|
+
workflow_request: mlrun.common.schemas.WorkflowRequest,
|
|
128
|
+
labels: Dict[str, str],
|
|
129
|
+
) -> mlrun.run.RunObject:
|
|
130
|
+
"""
|
|
131
|
+
Preparing all the necessary metadata and specifications for scheduling workflow from server-side.
|
|
132
|
+
|
|
133
|
+
:param project: MLRun project
|
|
134
|
+
:param workflow_request: contains the workflow spec and extra data for the run object
|
|
135
|
+
:param labels: dict of the task labels
|
|
136
|
+
|
|
137
|
+
:returns: RunObject ready for schedule.
|
|
138
|
+
"""
|
|
139
|
+
meta_uid = uuid.uuid4().hex
|
|
140
|
+
|
|
141
|
+
save = self._set_source(project, workflow_request.source)
|
|
142
|
+
workflow_spec = workflow_request.spec
|
|
143
|
+
run_object = RunObject(
|
|
144
|
+
spec=RunSpec(
|
|
145
|
+
parameters=dict(
|
|
146
|
+
url=project.spec.source,
|
|
147
|
+
project_name=project.metadata.name,
|
|
148
|
+
workflow_name=workflow_spec.name,
|
|
149
|
+
workflow_path=workflow_spec.path,
|
|
150
|
+
workflow_arguments=workflow_spec.args,
|
|
151
|
+
artifact_path=workflow_request.artifact_path,
|
|
152
|
+
workflow_handler=workflow_spec.handler,
|
|
153
|
+
namespace=workflow_request.namespace,
|
|
154
|
+
ttl=workflow_spec.ttl,
|
|
155
|
+
engine=workflow_spec.engine,
|
|
156
|
+
local=workflow_spec.run_local,
|
|
157
|
+
save=save,
|
|
158
|
+
subpath=project.spec.subpath,
|
|
159
|
+
),
|
|
160
|
+
handler="mlrun.projects.load_and_run",
|
|
161
|
+
scrape_metrics=config.scrape_metrics,
|
|
162
|
+
output_path=(
|
|
163
|
+
workflow_request.artifact_path or config.artifact_path
|
|
164
|
+
).replace("{{run.uid}}", meta_uid),
|
|
165
|
+
),
|
|
166
|
+
metadata=RunMetadata(
|
|
167
|
+
uid=meta_uid, name=workflow_spec.name, project=project.metadata.name
|
|
168
|
+
),
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
# Setting labels:
|
|
172
|
+
return self._label_run_object(run_object, labels)
|
|
173
|
+
|
|
174
|
+
def run(
|
|
175
|
+
self,
|
|
176
|
+
runner: mlrun.run.KubejobRuntime,
|
|
177
|
+
project: mlrun.common.schemas.Project,
|
|
178
|
+
workflow_request: mlrun.common.schemas.WorkflowRequest = None,
|
|
179
|
+
load_only: bool = False,
|
|
180
|
+
) -> RunObject:
|
|
181
|
+
"""
|
|
182
|
+
Run workflow runner.
|
|
183
|
+
|
|
184
|
+
:param runner: workflow runner function object
|
|
185
|
+
:param project: MLRun project
|
|
186
|
+
:param workflow_request: contains the workflow spec, that will be executed
|
|
187
|
+
:param load_only: If True, will only load the project remotely (without running workflow)
|
|
188
|
+
|
|
189
|
+
:returns: run context object (RunObject) with run metadata, results and status
|
|
190
|
+
"""
|
|
191
|
+
labels = {"project": project.metadata.name}
|
|
192
|
+
if load_only:
|
|
193
|
+
labels["job-type"] = "project-loader"
|
|
194
|
+
else:
|
|
195
|
+
labels["job-type"] = "workflow-runner"
|
|
196
|
+
labels["workflow"] = runner.metadata.name
|
|
197
|
+
|
|
198
|
+
run_spec = self._prepare_run_object_for_single_run(
|
|
199
|
+
project=project,
|
|
200
|
+
labels=labels,
|
|
201
|
+
workflow_request=workflow_request,
|
|
202
|
+
run_name=runner.metadata.name,
|
|
203
|
+
load_only=load_only,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
artifact_path = workflow_request.artifact_path if workflow_request else ""
|
|
207
|
+
return runner.run(
|
|
208
|
+
runspec=run_spec,
|
|
209
|
+
artifact_path=artifact_path,
|
|
210
|
+
local=False,
|
|
211
|
+
watch=False,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
@staticmethod
|
|
215
|
+
def get_workflow_id(
|
|
216
|
+
uid: str, project: str, engine: str, db_session: Session
|
|
217
|
+
) -> mlrun.common.schemas.GetWorkflowResponse:
|
|
218
|
+
"""
|
|
219
|
+
Retrieving the actual workflow id form the workflow runner
|
|
220
|
+
|
|
221
|
+
:param uid: the id of the workflow runner job
|
|
222
|
+
:param project: name of the project
|
|
223
|
+
:param engine: pipeline runner, for example: "kfp"
|
|
224
|
+
:param db_session: session that manages the current dialog with the database
|
|
225
|
+
|
|
226
|
+
:return: The id of the workflow.
|
|
227
|
+
"""
|
|
228
|
+
# Reading run:
|
|
229
|
+
run = mlrun.api.crud.Runs().get_run(
|
|
230
|
+
db_session=db_session, uid=uid, iter=0, project=project
|
|
231
|
+
)
|
|
232
|
+
run_object = RunObject.from_dict(run)
|
|
233
|
+
state = run_object.status.state
|
|
234
|
+
workflow_id = None
|
|
235
|
+
if isinstance(run_object.status.results, dict):
|
|
236
|
+
workflow_id = run_object.status.results.get("workflow_id", None)
|
|
237
|
+
|
|
238
|
+
if workflow_id is None:
|
|
239
|
+
if (
|
|
240
|
+
engine == "local"
|
|
241
|
+
and state.casefold() == mlrun.run.RunStatuses.running.casefold()
|
|
242
|
+
):
|
|
243
|
+
workflow_id = ""
|
|
244
|
+
else:
|
|
245
|
+
raise mlrun.errors.MLRunNotFoundError(
|
|
246
|
+
f"workflow id of run {project}:{uid} not found"
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
return mlrun.common.schemas.GetWorkflowResponse(workflow_id=workflow_id)
|
|
250
|
+
|
|
251
|
+
def _prepare_run_object_for_single_run(
|
|
252
|
+
self,
|
|
253
|
+
project: mlrun.common.schemas.Project,
|
|
254
|
+
labels: Dict[str, str],
|
|
255
|
+
workflow_request: mlrun.common.schemas.WorkflowRequest = None,
|
|
256
|
+
run_name: str = None,
|
|
257
|
+
load_only: bool = False,
|
|
258
|
+
) -> mlrun.run.RunObject:
|
|
259
|
+
"""
|
|
260
|
+
Preparing all the necessary metadata and specifications for running workflow from server-side.
|
|
261
|
+
|
|
262
|
+
:param project: MLRun project
|
|
263
|
+
:param labels: dict of the task labels
|
|
264
|
+
:param workflow_request: contains the workflow spec and extra data for the run object
|
|
265
|
+
:param run_name: workflow-runner function name
|
|
266
|
+
:param load_only: if True, will only load the project remotely (without running workflow)
|
|
267
|
+
|
|
268
|
+
:returns: RunObject ready for execution.
|
|
269
|
+
"""
|
|
270
|
+
source = workflow_request.source if workflow_request else ""
|
|
271
|
+
save = self._set_source(project, source, load_only)
|
|
272
|
+
run_object = RunObject(
|
|
273
|
+
spec=RunSpec(
|
|
274
|
+
parameters=dict(
|
|
275
|
+
url=project.spec.source,
|
|
276
|
+
project_name=project.metadata.name,
|
|
277
|
+
load_only=load_only,
|
|
278
|
+
save=save,
|
|
279
|
+
),
|
|
280
|
+
handler="mlrun.projects.load_and_run",
|
|
281
|
+
),
|
|
282
|
+
metadata=RunMetadata(name=run_name),
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
if not load_only:
|
|
286
|
+
workflow_spec = workflow_request.spec
|
|
287
|
+
run_object.spec.parameters.update(
|
|
288
|
+
dict(
|
|
289
|
+
workflow_name=workflow_spec.name,
|
|
290
|
+
workflow_path=workflow_spec.path,
|
|
291
|
+
workflow_arguments=workflow_spec.args,
|
|
292
|
+
artifact_path=workflow_request.artifact_path,
|
|
293
|
+
workflow_handler=workflow_spec.handler,
|
|
294
|
+
namespace=workflow_request.namespace,
|
|
295
|
+
ttl=workflow_spec.ttl,
|
|
296
|
+
engine=workflow_spec.engine,
|
|
297
|
+
local=workflow_spec.run_local,
|
|
298
|
+
subpath=project.spec.subpath,
|
|
299
|
+
)
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
# Setting labels:
|
|
303
|
+
return self._label_run_object(run_object, labels)
|
|
304
|
+
|
|
305
|
+
@staticmethod
|
|
306
|
+
def _set_source(
|
|
307
|
+
project: mlrun.common.schemas.Project, source: str, load_only: bool = False
|
|
308
|
+
) -> bool:
|
|
309
|
+
"""
|
|
310
|
+
Setting the project source.
|
|
311
|
+
In case the user provided a source we want to load the project from the source
|
|
312
|
+
(like from a specific commit/branch from git repo) without changing the source of the project (save=False).
|
|
313
|
+
|
|
314
|
+
:param project: MLRun project
|
|
315
|
+
:param source: the source of the project, needs to be a remote URL that contains the project yaml file.
|
|
316
|
+
:param load_only: if we only load the project, it must be saved to ensure we are not running a pipeline
|
|
317
|
+
without a project as it's not supported.
|
|
318
|
+
|
|
319
|
+
:returns: True if the project need to be saved afterward.
|
|
320
|
+
"""
|
|
321
|
+
|
|
322
|
+
save = True
|
|
323
|
+
if source and not load_only:
|
|
324
|
+
save = False
|
|
325
|
+
project.spec.source = source
|
|
326
|
+
|
|
327
|
+
if "://" not in project.spec.source:
|
|
328
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
329
|
+
f"Remote workflows can only be performed by a project with remote source (e.g git:// or http://),"
|
|
330
|
+
f" but the specified source '{project.spec.source}' is not remote. "
|
|
331
|
+
f"Either put your code in Git, or archive it and then set a source to it."
|
|
332
|
+
f" For more details, read"
|
|
333
|
+
f" https://docs.mlrun.org/en/latest/concepts/scheduled-jobs.html#scheduling-a-workflow"
|
|
334
|
+
)
|
|
335
|
+
return save
|
|
336
|
+
|
|
337
|
+
@staticmethod
|
|
338
|
+
def _label_run_object(
|
|
339
|
+
run_object: mlrun.run.RunObject,
|
|
340
|
+
labels: Dict[str, str],
|
|
341
|
+
) -> mlrun.run.RunObject:
|
|
342
|
+
"""
|
|
343
|
+
Setting labels to the task
|
|
344
|
+
|
|
345
|
+
:param run_object: run object to set labels on
|
|
346
|
+
:param labels: dict of labels
|
|
347
|
+
|
|
348
|
+
:returns: labeled RunObject
|
|
349
|
+
"""
|
|
350
|
+
for key, value in labels.items():
|
|
351
|
+
run_object = run_object.set_label(key, value)
|
|
352
|
+
return run_object
|
mlrun/api/db/base.py
CHANGED
|
@@ -259,6 +259,21 @@ class DBInterface(ABC):
|
|
|
259
259
|
):
|
|
260
260
|
pass
|
|
261
261
|
|
|
262
|
+
def store_schedule(
|
|
263
|
+
self,
|
|
264
|
+
session,
|
|
265
|
+
project: str,
|
|
266
|
+
name: str,
|
|
267
|
+
kind: mlrun.common.schemas.ScheduleKinds = None,
|
|
268
|
+
scheduled_object: Any = None,
|
|
269
|
+
cron_trigger: mlrun.common.schemas.ScheduleCronTrigger = None,
|
|
270
|
+
labels: Dict = None,
|
|
271
|
+
last_run_uri: str = None,
|
|
272
|
+
concurrency_limit: int = None,
|
|
273
|
+
next_run_time: datetime = None,
|
|
274
|
+
):
|
|
275
|
+
pass
|
|
276
|
+
|
|
262
277
|
@abstractmethod
|
|
263
278
|
def list_schedules(
|
|
264
279
|
self,
|
|
@@ -272,7 +287,7 @@ class DBInterface(ABC):
|
|
|
272
287
|
|
|
273
288
|
@abstractmethod
|
|
274
289
|
def get_schedule(
|
|
275
|
-
self, session, project: str, name: str
|
|
290
|
+
self, session, project: str, name: str, raise_on_not_found: bool = True
|
|
276
291
|
) -> mlrun.common.schemas.ScheduleRecord:
|
|
277
292
|
pass
|
|
278
293
|
|
mlrun/api/db/init_db.py
CHANGED
|
@@ -14,10 +14,8 @@
|
|
|
14
14
|
#
|
|
15
15
|
|
|
16
16
|
from mlrun.api.db.sqldb.models import Base
|
|
17
|
-
from mlrun.
|
|
18
|
-
from mlrun.config import config
|
|
17
|
+
from mlrun.common.db.sql_session import get_engine
|
|
19
18
|
|
|
20
19
|
|
|
21
20
|
def init_db() -> None:
|
|
22
|
-
|
|
23
|
-
Base.metadata.create_all(bind=get_engine())
|
|
21
|
+
Base.metadata.create_all(bind=get_engine())
|
mlrun/api/db/session.py
CHANGED