mlrun 1.7.0rc28__py3-none-any.whl → 1.7.0rc55__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/__main__.py +4 -2
- mlrun/alerts/alert.py +75 -8
- mlrun/artifacts/base.py +1 -0
- mlrun/artifacts/manager.py +9 -2
- mlrun/common/constants.py +4 -1
- mlrun/common/db/sql_session.py +3 -2
- mlrun/common/formatters/__init__.py +1 -0
- mlrun/common/formatters/artifact.py +1 -0
- mlrun/{model_monitoring/application.py → common/formatters/feature_set.py} +20 -6
- mlrun/common/formatters/run.py +3 -0
- mlrun/common/helpers.py +0 -1
- mlrun/common/schemas/__init__.py +3 -1
- mlrun/common/schemas/alert.py +15 -12
- mlrun/common/schemas/api_gateway.py +6 -6
- mlrun/common/schemas/auth.py +5 -0
- mlrun/common/schemas/client_spec.py +0 -1
- mlrun/common/schemas/common.py +7 -4
- mlrun/common/schemas/frontend_spec.py +7 -0
- mlrun/common/schemas/function.py +7 -0
- mlrun/common/schemas/model_monitoring/__init__.py +4 -3
- mlrun/common/schemas/model_monitoring/constants.py +41 -26
- mlrun/common/schemas/model_monitoring/model_endpoints.py +23 -47
- mlrun/common/schemas/notification.py +69 -12
- mlrun/common/schemas/project.py +45 -12
- mlrun/common/schemas/workflow.py +10 -2
- mlrun/common/types.py +1 -0
- mlrun/config.py +91 -35
- mlrun/data_types/data_types.py +6 -1
- mlrun/data_types/spark.py +2 -2
- mlrun/data_types/to_pandas.py +57 -25
- mlrun/datastore/__init__.py +1 -0
- mlrun/datastore/alibaba_oss.py +3 -2
- mlrun/datastore/azure_blob.py +125 -37
- mlrun/datastore/base.py +42 -21
- mlrun/datastore/datastore.py +4 -2
- mlrun/datastore/datastore_profile.py +1 -1
- mlrun/datastore/dbfs_store.py +3 -7
- mlrun/datastore/filestore.py +1 -3
- mlrun/datastore/google_cloud_storage.py +85 -29
- mlrun/datastore/inmem.py +4 -1
- mlrun/datastore/redis.py +1 -0
- mlrun/datastore/s3.py +25 -12
- mlrun/datastore/sources.py +76 -4
- mlrun/datastore/spark_utils.py +30 -0
- mlrun/datastore/storeytargets.py +151 -0
- mlrun/datastore/targets.py +102 -131
- mlrun/datastore/v3io.py +1 -0
- mlrun/db/base.py +15 -6
- mlrun/db/httpdb.py +57 -28
- mlrun/db/nopdb.py +29 -5
- mlrun/errors.py +20 -3
- mlrun/execution.py +46 -5
- mlrun/feature_store/api.py +25 -1
- mlrun/feature_store/common.py +6 -11
- mlrun/feature_store/feature_vector.py +3 -1
- mlrun/feature_store/retrieval/job.py +4 -1
- mlrun/feature_store/retrieval/spark_merger.py +10 -39
- mlrun/feature_store/steps.py +8 -0
- mlrun/frameworks/_common/plan.py +3 -3
- mlrun/frameworks/_ml_common/plan.py +1 -1
- mlrun/frameworks/parallel_coordinates.py +2 -3
- mlrun/frameworks/sklearn/mlrun_interface.py +13 -3
- mlrun/k8s_utils.py +48 -2
- mlrun/launcher/client.py +6 -6
- mlrun/launcher/local.py +2 -2
- mlrun/model.py +215 -34
- mlrun/model_monitoring/api.py +38 -24
- mlrun/model_monitoring/applications/__init__.py +1 -2
- mlrun/model_monitoring/applications/_application_steps.py +60 -29
- mlrun/model_monitoring/applications/base.py +2 -174
- mlrun/model_monitoring/applications/context.py +197 -70
- mlrun/model_monitoring/applications/evidently_base.py +11 -85
- mlrun/model_monitoring/applications/histogram_data_drift.py +21 -16
- mlrun/model_monitoring/applications/results.py +4 -4
- mlrun/model_monitoring/controller.py +110 -282
- mlrun/model_monitoring/db/stores/__init__.py +8 -3
- mlrun/model_monitoring/db/stores/base/store.py +3 -0
- mlrun/model_monitoring/db/stores/sqldb/models/base.py +9 -7
- mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +18 -3
- mlrun/model_monitoring/db/stores/sqldb/sql_store.py +43 -23
- mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +48 -35
- mlrun/model_monitoring/db/tsdb/__init__.py +7 -2
- mlrun/model_monitoring/db/tsdb/base.py +147 -15
- mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +94 -55
- mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +0 -3
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +144 -38
- mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +44 -3
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +246 -57
- mlrun/model_monitoring/helpers.py +70 -50
- mlrun/model_monitoring/stream_processing.py +96 -195
- mlrun/model_monitoring/writer.py +13 -5
- mlrun/package/packagers/default_packager.py +2 -2
- mlrun/projects/operations.py +16 -8
- mlrun/projects/pipelines.py +126 -115
- mlrun/projects/project.py +286 -129
- mlrun/render.py +3 -3
- mlrun/run.py +38 -19
- mlrun/runtimes/__init__.py +19 -8
- mlrun/runtimes/base.py +4 -1
- mlrun/runtimes/daskjob.py +1 -1
- mlrun/runtimes/funcdoc.py +1 -1
- mlrun/runtimes/kubejob.py +6 -6
- mlrun/runtimes/local.py +12 -5
- mlrun/runtimes/nuclio/api_gateway.py +68 -8
- mlrun/runtimes/nuclio/application/application.py +307 -70
- mlrun/runtimes/nuclio/function.py +63 -14
- mlrun/runtimes/nuclio/serving.py +10 -10
- mlrun/runtimes/pod.py +25 -19
- mlrun/runtimes/remotesparkjob.py +2 -5
- mlrun/runtimes/sparkjob/spark3job.py +16 -17
- mlrun/runtimes/utils.py +34 -0
- mlrun/serving/routers.py +2 -5
- mlrun/serving/server.py +37 -19
- mlrun/serving/states.py +30 -3
- mlrun/serving/v2_serving.py +44 -35
- mlrun/track/trackers/mlflow_tracker.py +5 -0
- mlrun/utils/async_http.py +1 -1
- mlrun/utils/db.py +18 -0
- mlrun/utils/helpers.py +150 -36
- mlrun/utils/http.py +1 -1
- mlrun/utils/notifications/notification/__init__.py +0 -1
- mlrun/utils/notifications/notification/webhook.py +8 -1
- mlrun/utils/notifications/notification_pusher.py +1 -1
- mlrun/utils/v3io_clients.py +2 -2
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/METADATA +153 -66
- {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/RECORD +131 -134
- {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/WHEEL +1 -1
- mlrun/feature_store/retrieval/conversion.py +0 -271
- mlrun/model_monitoring/controller_handler.py +0 -37
- mlrun/model_monitoring/evidently_application.py +0 -20
- mlrun/model_monitoring/prometheus.py +0 -216
- {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/LICENSE +0 -0
- {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/entry_points.txt +0 -0
- {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/top_level.txt +0 -0
mlrun/projects/pipelines.py
CHANGED
|
@@ -27,6 +27,8 @@ import mlrun_pipelines.utils
|
|
|
27
27
|
import mlrun
|
|
28
28
|
import mlrun.common.runtimes.constants
|
|
29
29
|
import mlrun.common.schemas
|
|
30
|
+
import mlrun.common.schemas.function
|
|
31
|
+
import mlrun.common.schemas.workflow
|
|
30
32
|
import mlrun.utils.notifications
|
|
31
33
|
from mlrun.errors import err_to_str
|
|
32
34
|
from mlrun.utils import (
|
|
@@ -44,21 +46,21 @@ from ..runtimes.pod import AutoMountType
|
|
|
44
46
|
|
|
45
47
|
def get_workflow_engine(engine_kind, local=False):
|
|
46
48
|
if pipeline_context.is_run_local(local):
|
|
47
|
-
if engine_kind ==
|
|
49
|
+
if engine_kind == mlrun.common.schemas.workflow.EngineType.KFP:
|
|
48
50
|
logger.warning(
|
|
49
51
|
"Running kubeflow pipeline locally, note some ops may not run locally!"
|
|
50
52
|
)
|
|
51
|
-
elif engine_kind ==
|
|
53
|
+
elif engine_kind == mlrun.common.schemas.workflow.EngineType.REMOTE:
|
|
52
54
|
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
53
55
|
"Cannot run a remote pipeline locally using `kind='remote'` and `local=True`. "
|
|
54
56
|
"in order to run a local pipeline remotely, please use `engine='remote:local'` instead"
|
|
55
57
|
)
|
|
56
58
|
return _LocalRunner
|
|
57
|
-
if not engine_kind or engine_kind ==
|
|
59
|
+
if not engine_kind or engine_kind == mlrun.common.schemas.workflow.EngineType.KFP:
|
|
58
60
|
return _KFPRunner
|
|
59
|
-
if engine_kind ==
|
|
61
|
+
if engine_kind == mlrun.common.schemas.workflow.EngineType.LOCAL:
|
|
60
62
|
return _LocalRunner
|
|
61
|
-
if engine_kind ==
|
|
63
|
+
if engine_kind == mlrun.common.schemas.workflow.EngineType.REMOTE:
|
|
62
64
|
return _RemoteRunner
|
|
63
65
|
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
64
66
|
f"Provided workflow engine is not supported. engine_kind={engine_kind}"
|
|
@@ -80,6 +82,7 @@ class WorkflowSpec(mlrun.model.ModelObj):
|
|
|
80
82
|
schedule: typing.Union[str, mlrun.common.schemas.ScheduleCronTrigger] = None,
|
|
81
83
|
cleanup_ttl: typing.Optional[int] = None,
|
|
82
84
|
image: typing.Optional[str] = None,
|
|
85
|
+
workflow_runner_node_selector: typing.Optional[dict[str, str]] = None,
|
|
83
86
|
):
|
|
84
87
|
self.engine = engine
|
|
85
88
|
self.code = code
|
|
@@ -93,6 +96,7 @@ class WorkflowSpec(mlrun.model.ModelObj):
|
|
|
93
96
|
self._tmp_path = None
|
|
94
97
|
self.schedule = schedule
|
|
95
98
|
self.image = image
|
|
99
|
+
self.workflow_runner_node_selector = workflow_runner_node_selector
|
|
96
100
|
|
|
97
101
|
def get_source_file(self, context=""):
|
|
98
102
|
if not self.code and not self.path:
|
|
@@ -311,7 +315,11 @@ def get_db_function(project, key) -> mlrun.runtimes.BaseRuntime:
|
|
|
311
315
|
|
|
312
316
|
|
|
313
317
|
def enrich_function_object(
|
|
314
|
-
project
|
|
318
|
+
project: mlrun.common.schemas.Project,
|
|
319
|
+
function: mlrun.runtimes.BaseRuntime,
|
|
320
|
+
decorator: typing.Callable = None,
|
|
321
|
+
copy_function: bool = True,
|
|
322
|
+
try_auto_mount: bool = True,
|
|
315
323
|
) -> mlrun.runtimes.BaseRuntime:
|
|
316
324
|
if hasattr(function, "_enriched"):
|
|
317
325
|
return function
|
|
@@ -352,7 +360,6 @@ def enrich_function_object(
|
|
|
352
360
|
f.enrich_runtime_spec(
|
|
353
361
|
project.spec.default_function_node_selector,
|
|
354
362
|
)
|
|
355
|
-
|
|
356
363
|
if try_auto_mount:
|
|
357
364
|
if (
|
|
358
365
|
decorator and AutoMountType.is_auto_modifier(decorator)
|
|
@@ -404,12 +411,15 @@ class _PipelineRunStatus:
|
|
|
404
411
|
return self._exc
|
|
405
412
|
|
|
406
413
|
def wait_for_completion(self, timeout=None, expected_statuses=None):
|
|
407
|
-
|
|
408
|
-
self
|
|
414
|
+
returned_state = self._engine.wait_for_completion(
|
|
415
|
+
self,
|
|
409
416
|
project=self.project,
|
|
410
417
|
timeout=timeout,
|
|
411
418
|
expected_statuses=expected_statuses,
|
|
412
419
|
)
|
|
420
|
+
# TODO: returning a state is optional until all runners implement wait_for_completion
|
|
421
|
+
if returned_state:
|
|
422
|
+
self._state = returned_state
|
|
413
423
|
return self._state
|
|
414
424
|
|
|
415
425
|
def __str__(self):
|
|
@@ -444,13 +454,17 @@ class _PipelineRunner(abc.ABC):
|
|
|
444
454
|
namespace=None,
|
|
445
455
|
source=None,
|
|
446
456
|
notifications: list[mlrun.model.Notification] = None,
|
|
447
|
-
send_start_notification: bool = True,
|
|
448
457
|
) -> _PipelineRunStatus:
|
|
449
458
|
pass
|
|
450
459
|
|
|
451
460
|
@staticmethod
|
|
452
461
|
@abc.abstractmethod
|
|
453
|
-
def wait_for_completion(
|
|
462
|
+
def wait_for_completion(
|
|
463
|
+
run: "_PipelineRunStatus",
|
|
464
|
+
project: typing.Optional["mlrun.projects.MlrunProject"] = None,
|
|
465
|
+
timeout: typing.Optional[int] = None,
|
|
466
|
+
expected_statuses: list[str] = None,
|
|
467
|
+
):
|
|
454
468
|
pass
|
|
455
469
|
|
|
456
470
|
@staticmethod
|
|
@@ -458,6 +472,48 @@ class _PipelineRunner(abc.ABC):
|
|
|
458
472
|
def get_state(run_id, project=None):
|
|
459
473
|
pass
|
|
460
474
|
|
|
475
|
+
@staticmethod
|
|
476
|
+
def get_run_status(
|
|
477
|
+
project,
|
|
478
|
+
run: _PipelineRunStatus,
|
|
479
|
+
timeout=None,
|
|
480
|
+
expected_statuses=None,
|
|
481
|
+
notifiers: mlrun.utils.notifications.CustomNotificationPusher = None,
|
|
482
|
+
**kwargs,
|
|
483
|
+
):
|
|
484
|
+
timeout = timeout or 60 * 60
|
|
485
|
+
raise_error = None
|
|
486
|
+
state = ""
|
|
487
|
+
try:
|
|
488
|
+
if timeout:
|
|
489
|
+
state = run.wait_for_completion(
|
|
490
|
+
timeout=timeout, expected_statuses=expected_statuses
|
|
491
|
+
)
|
|
492
|
+
except RuntimeError as exc:
|
|
493
|
+
# push runs table also when we have errors
|
|
494
|
+
raise_error = exc
|
|
495
|
+
|
|
496
|
+
mldb = mlrun.db.get_run_db(secrets=project._secrets)
|
|
497
|
+
runs = mldb.list_runs(project=project.name, labels=f"workflow={run.run_id}")
|
|
498
|
+
|
|
499
|
+
# TODO: The below section duplicates notifiers.push_pipeline_run_results() logic. We should use it instead.
|
|
500
|
+
errors_counter = 0
|
|
501
|
+
for r in runs:
|
|
502
|
+
if r["status"].get("state", "") == "error":
|
|
503
|
+
errors_counter += 1
|
|
504
|
+
|
|
505
|
+
text = _PipelineRunner._generate_workflow_finished_message(
|
|
506
|
+
run.run_id, errors_counter, run._state
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
notifiers = notifiers or project.notifiers
|
|
510
|
+
if notifiers:
|
|
511
|
+
notifiers.push(text, "info", runs)
|
|
512
|
+
|
|
513
|
+
if raise_error:
|
|
514
|
+
raise raise_error
|
|
515
|
+
return state or run._state, errors_counter, text
|
|
516
|
+
|
|
461
517
|
@staticmethod
|
|
462
518
|
def _get_handler(workflow_handler, workflow_spec, project, secrets):
|
|
463
519
|
if not (workflow_handler and callable(workflow_handler)):
|
|
@@ -474,16 +530,13 @@ class _PipelineRunner(abc.ABC):
|
|
|
474
530
|
return workflow_handler
|
|
475
531
|
|
|
476
532
|
@staticmethod
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
**kwargs,
|
|
485
|
-
):
|
|
486
|
-
pass
|
|
533
|
+
def _generate_workflow_finished_message(run_id, errors_counter, state):
|
|
534
|
+
text = f"Workflow {run_id} finished"
|
|
535
|
+
if errors_counter:
|
|
536
|
+
text += f" with {errors_counter} errors"
|
|
537
|
+
if state:
|
|
538
|
+
text += f", state={state}"
|
|
539
|
+
return text
|
|
487
540
|
|
|
488
541
|
|
|
489
542
|
class _KFPRunner(_PipelineRunner):
|
|
@@ -525,7 +578,6 @@ class _KFPRunner(_PipelineRunner):
|
|
|
525
578
|
namespace=None,
|
|
526
579
|
source=None,
|
|
527
580
|
notifications: list[mlrun.model.Notification] = None,
|
|
528
|
-
send_start_notification: bool = True,
|
|
529
581
|
) -> _PipelineRunStatus:
|
|
530
582
|
pipeline_context.set(project, workflow_spec)
|
|
531
583
|
workflow_handler = _PipelineRunner._get_handler(
|
|
@@ -541,12 +593,13 @@ class _KFPRunner(_PipelineRunner):
|
|
|
541
593
|
logger.warning(
|
|
542
594
|
"Setting notifications on kfp pipeline runner uses old notification behavior. "
|
|
543
595
|
"Notifications will only be sent if you wait for pipeline completion. "
|
|
544
|
-
"
|
|
596
|
+
"Some of the features (like setting message or severity level) are not supported."
|
|
545
597
|
)
|
|
546
|
-
for notification
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
)
|
|
598
|
+
# for start message, fallback to old notification behavior
|
|
599
|
+
for notification in notifications or []:
|
|
600
|
+
params = notification.params
|
|
601
|
+
params.update(notification.secret_params)
|
|
602
|
+
project.notifiers.add_notification(notification.kind, params)
|
|
550
603
|
|
|
551
604
|
run_id = _run_pipeline(
|
|
552
605
|
workflow_handler,
|
|
@@ -574,23 +627,31 @@ class _KFPRunner(_PipelineRunner):
|
|
|
574
627
|
func_name=func.metadata.name,
|
|
575
628
|
exc_info=err_to_str(exc),
|
|
576
629
|
)
|
|
577
|
-
|
|
578
|
-
project.
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
)
|
|
630
|
+
project.notifiers.push_pipeline_start_message(
|
|
631
|
+
project.metadata.name,
|
|
632
|
+
project.get_param("commit_id", None),
|
|
633
|
+
run_id,
|
|
634
|
+
True,
|
|
635
|
+
)
|
|
584
636
|
pipeline_context.clear()
|
|
585
637
|
return _PipelineRunStatus(run_id, cls, project=project, workflow=workflow_spec)
|
|
586
638
|
|
|
587
639
|
@staticmethod
|
|
588
|
-
def wait_for_completion(
|
|
589
|
-
|
|
590
|
-
|
|
640
|
+
def wait_for_completion(
|
|
641
|
+
run: "_PipelineRunStatus",
|
|
642
|
+
project: typing.Optional["mlrun.projects.MlrunProject"] = None,
|
|
643
|
+
timeout: typing.Optional[int] = None,
|
|
644
|
+
expected_statuses: list[str] = None,
|
|
645
|
+
):
|
|
591
646
|
project_name = project.metadata.name if project else ""
|
|
647
|
+
logger.info(
|
|
648
|
+
"Waiting for pipeline run completion",
|
|
649
|
+
run_id=run.run_id,
|
|
650
|
+
project=project_name,
|
|
651
|
+
)
|
|
652
|
+
timeout = timeout or 60 * 60
|
|
592
653
|
run_info = wait_for_pipeline_completion(
|
|
593
|
-
run_id,
|
|
654
|
+
run.run_id,
|
|
594
655
|
timeout=timeout,
|
|
595
656
|
expected_statuses=expected_statuses,
|
|
596
657
|
project=project_name,
|
|
@@ -608,51 +669,6 @@ class _KFPRunner(_PipelineRunner):
|
|
|
608
669
|
return resp["run"].get("status", "")
|
|
609
670
|
return ""
|
|
610
671
|
|
|
611
|
-
@staticmethod
|
|
612
|
-
def get_run_status(
|
|
613
|
-
project,
|
|
614
|
-
run,
|
|
615
|
-
timeout=None,
|
|
616
|
-
expected_statuses=None,
|
|
617
|
-
notifiers: mlrun.utils.notifications.CustomNotificationPusher = None,
|
|
618
|
-
**kwargs,
|
|
619
|
-
):
|
|
620
|
-
if timeout is None:
|
|
621
|
-
timeout = 60 * 60
|
|
622
|
-
state = ""
|
|
623
|
-
raise_error = None
|
|
624
|
-
try:
|
|
625
|
-
if timeout:
|
|
626
|
-
logger.info("Waiting for pipeline run completion")
|
|
627
|
-
state = run.wait_for_completion(
|
|
628
|
-
timeout=timeout, expected_statuses=expected_statuses
|
|
629
|
-
)
|
|
630
|
-
except RuntimeError as exc:
|
|
631
|
-
# push runs table also when we have errors
|
|
632
|
-
raise_error = exc
|
|
633
|
-
|
|
634
|
-
mldb = mlrun.db.get_run_db(secrets=project._secrets)
|
|
635
|
-
runs = mldb.list_runs(project=project.name, labels=f"workflow={run.run_id}")
|
|
636
|
-
|
|
637
|
-
# TODO: The below section duplicates notifiers.push_pipeline_run_results() logic. We should use it instead.
|
|
638
|
-
had_errors = 0
|
|
639
|
-
for r in runs:
|
|
640
|
-
if r["status"].get("state", "") == "error":
|
|
641
|
-
had_errors += 1
|
|
642
|
-
|
|
643
|
-
text = f"Workflow {run.run_id} finished"
|
|
644
|
-
if had_errors:
|
|
645
|
-
text += f" with {had_errors} errors"
|
|
646
|
-
if state:
|
|
647
|
-
text += f", state={state}"
|
|
648
|
-
|
|
649
|
-
notifiers = notifiers or project.notifiers
|
|
650
|
-
notifiers.push(text, "info", runs)
|
|
651
|
-
|
|
652
|
-
if raise_error:
|
|
653
|
-
raise raise_error
|
|
654
|
-
return state, had_errors, text
|
|
655
|
-
|
|
656
672
|
|
|
657
673
|
class _LocalRunner(_PipelineRunner):
|
|
658
674
|
"""local pipelines runner"""
|
|
@@ -671,7 +687,6 @@ class _LocalRunner(_PipelineRunner):
|
|
|
671
687
|
namespace=None,
|
|
672
688
|
source=None,
|
|
673
689
|
notifications: list[mlrun.model.Notification] = None,
|
|
674
|
-
send_start_notification: bool = True,
|
|
675
690
|
) -> _PipelineRunStatus:
|
|
676
691
|
pipeline_context.set(project, workflow_spec)
|
|
677
692
|
workflow_handler = _PipelineRunner._get_handler(
|
|
@@ -693,10 +708,9 @@ class _LocalRunner(_PipelineRunner):
|
|
|
693
708
|
project.set_source(source=source)
|
|
694
709
|
pipeline_context.workflow_artifact_path = artifact_path
|
|
695
710
|
|
|
696
|
-
|
|
697
|
-
project.
|
|
698
|
-
|
|
699
|
-
)
|
|
711
|
+
project.notifiers.push_pipeline_start_message(
|
|
712
|
+
project.metadata.name, pipeline_id=workflow_id
|
|
713
|
+
)
|
|
700
714
|
err = None
|
|
701
715
|
try:
|
|
702
716
|
workflow_handler(**workflow_spec.args)
|
|
@@ -732,18 +746,10 @@ class _LocalRunner(_PipelineRunner):
|
|
|
732
746
|
return ""
|
|
733
747
|
|
|
734
748
|
@staticmethod
|
|
735
|
-
def wait_for_completion(
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
def get_run_status(
|
|
740
|
-
project,
|
|
741
|
-
run,
|
|
742
|
-
timeout=None,
|
|
743
|
-
expected_statuses=None,
|
|
744
|
-
notifiers: mlrun.utils.notifications.CustomNotificationPusher = None,
|
|
745
|
-
**kwargs,
|
|
746
|
-
):
|
|
749
|
+
def wait_for_completion(run, project=None, timeout=None, expected_statuses=None):
|
|
750
|
+
# TODO: local runner blocks for the duration of the pipeline.
|
|
751
|
+
# Therefore usually there will be nothing to wait for.
|
|
752
|
+
# However, users may run functions with watch=False and then it can be useful to wait for the runs here.
|
|
747
753
|
pass
|
|
748
754
|
|
|
749
755
|
|
|
@@ -764,22 +770,10 @@ class _RemoteRunner(_PipelineRunner):
|
|
|
764
770
|
namespace: str = None,
|
|
765
771
|
source: str = None,
|
|
766
772
|
notifications: list[mlrun.model.Notification] = None,
|
|
767
|
-
send_start_notification: bool = True,
|
|
768
773
|
) -> typing.Optional[_PipelineRunStatus]:
|
|
769
774
|
workflow_name = normalize_workflow_name(name=name, project_name=project.name)
|
|
770
775
|
workflow_id = None
|
|
771
776
|
|
|
772
|
-
# for start message, fallback to old notification behavior
|
|
773
|
-
if send_start_notification:
|
|
774
|
-
for notification in notifications or []:
|
|
775
|
-
project.notifiers.add_notification(
|
|
776
|
-
notification.kind, notification.params
|
|
777
|
-
)
|
|
778
|
-
# if a notification with `when=running` is provided, it will be used explicitly and others
|
|
779
|
-
# will be ignored
|
|
780
|
-
if "running" in notification.when:
|
|
781
|
-
break
|
|
782
|
-
|
|
783
777
|
# The returned engine for this runner is the engine of the workflow.
|
|
784
778
|
# In this way wait_for_completion/get_run_status would be executed by the correct pipeline runner.
|
|
785
779
|
inner_engine = get_workflow_engine(workflow_spec.engine)
|
|
@@ -879,9 +873,6 @@ class _RemoteRunner(_PipelineRunner):
|
|
|
879
873
|
state = mlrun_pipelines.common.models.RunStatuses.failed
|
|
880
874
|
else:
|
|
881
875
|
state = mlrun_pipelines.common.models.RunStatuses.running
|
|
882
|
-
project.notifiers.push_pipeline_start_message(
|
|
883
|
-
project.metadata.name,
|
|
884
|
-
)
|
|
885
876
|
pipeline_context.clear()
|
|
886
877
|
return _PipelineRunStatus(
|
|
887
878
|
run_id=workflow_id,
|
|
@@ -924,13 +915,25 @@ class _RemoteRunner(_PipelineRunner):
|
|
|
924
915
|
elif inner_engine.engine == _LocalRunner.engine:
|
|
925
916
|
mldb = mlrun.db.get_run_db(secrets=project._secrets)
|
|
926
917
|
pipeline_runner_run = mldb.read_run(run.run_id, project=project.name)
|
|
918
|
+
|
|
927
919
|
pipeline_runner_run = mlrun.run.RunObject.from_dict(pipeline_runner_run)
|
|
920
|
+
|
|
921
|
+
# here we are waiting for the pipeline run to complete and refreshing after that the pipeline run from the
|
|
922
|
+
# db
|
|
923
|
+
# TODO: do it with timeout
|
|
928
924
|
pipeline_runner_run.logs(db=mldb)
|
|
929
925
|
pipeline_runner_run.refresh()
|
|
930
926
|
run._state = mlrun.common.runtimes.constants.RunStates.run_state_to_pipeline_run_status(
|
|
931
927
|
pipeline_runner_run.status.state
|
|
932
928
|
)
|
|
933
929
|
run._exc = pipeline_runner_run.status.error
|
|
930
|
+
return _LocalRunner.get_run_status(
|
|
931
|
+
project,
|
|
932
|
+
run,
|
|
933
|
+
timeout,
|
|
934
|
+
expected_statuses,
|
|
935
|
+
notifiers=notifiers,
|
|
936
|
+
)
|
|
934
937
|
|
|
935
938
|
else:
|
|
936
939
|
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
@@ -1075,6 +1078,13 @@ def load_and_run(
|
|
|
1075
1078
|
if load_only:
|
|
1076
1079
|
return
|
|
1077
1080
|
|
|
1081
|
+
# extract "start" notification if exists
|
|
1082
|
+
start_notifications = [
|
|
1083
|
+
notification
|
|
1084
|
+
for notification in context.get_notifications(unmask_secret_params=True)
|
|
1085
|
+
if "running" in notification.when
|
|
1086
|
+
]
|
|
1087
|
+
|
|
1078
1088
|
workflow_log_message = workflow_name or workflow_path
|
|
1079
1089
|
context.logger.info(f"Running workflow {workflow_log_message} from remote")
|
|
1080
1090
|
run = project.run(
|
|
@@ -1090,6 +1100,7 @@ def load_and_run(
|
|
|
1090
1100
|
cleanup_ttl=cleanup_ttl,
|
|
1091
1101
|
engine=engine,
|
|
1092
1102
|
local=local,
|
|
1103
|
+
notifications=start_notifications,
|
|
1093
1104
|
)
|
|
1094
1105
|
context.log_result(key="workflow_id", value=run.run_id)
|
|
1095
1106
|
context.log_result(key="engine", value=run._engine.engine, commit=True)
|