mlrun 1.5.0rc1__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 (119) hide show
  1. mlrun/__init__.py +2 -35
  2. mlrun/__main__.py +1 -40
  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/functions.py +6 -1
  7. mlrun/api/api/endpoints/logs.py +17 -3
  8. mlrun/api/api/endpoints/pipelines.py +1 -5
  9. mlrun/api/api/endpoints/projects.py +88 -0
  10. mlrun/api/api/endpoints/runs.py +48 -6
  11. mlrun/api/api/endpoints/workflows.py +355 -0
  12. mlrun/api/api/utils.py +1 -1
  13. mlrun/api/crud/__init__.py +1 -0
  14. mlrun/api/crud/client_spec.py +3 -0
  15. mlrun/api/crud/model_monitoring/deployment.py +36 -7
  16. mlrun/api/crud/model_monitoring/grafana.py +1 -1
  17. mlrun/api/crud/model_monitoring/helpers.py +32 -2
  18. mlrun/api/crud/model_monitoring/model_endpoints.py +27 -5
  19. mlrun/api/crud/notifications.py +9 -4
  20. mlrun/api/crud/pipelines.py +4 -9
  21. mlrun/api/crud/runtime_resources.py +4 -3
  22. mlrun/api/crud/secrets.py +21 -0
  23. mlrun/api/crud/workflows.py +352 -0
  24. mlrun/api/db/base.py +16 -1
  25. mlrun/api/db/sqldb/db.py +97 -16
  26. mlrun/api/launcher.py +26 -7
  27. mlrun/api/main.py +3 -4
  28. mlrun/{mlutils → api/rundb}/__init__.py +2 -6
  29. mlrun/{db → api/rundb}/sqldb.py +35 -83
  30. mlrun/api/runtime_handlers/__init__.py +56 -0
  31. mlrun/api/runtime_handlers/base.py +1247 -0
  32. mlrun/api/runtime_handlers/daskjob.py +209 -0
  33. mlrun/api/runtime_handlers/kubejob.py +37 -0
  34. mlrun/api/runtime_handlers/mpijob.py +147 -0
  35. mlrun/api/runtime_handlers/remotesparkjob.py +29 -0
  36. mlrun/api/runtime_handlers/sparkjob.py +148 -0
  37. mlrun/api/utils/builder.py +1 -4
  38. mlrun/api/utils/clients/chief.py +14 -0
  39. mlrun/api/utils/scheduler.py +98 -15
  40. mlrun/api/utils/singletons/db.py +4 -0
  41. mlrun/artifacts/manager.py +1 -2
  42. mlrun/common/schemas/__init__.py +6 -0
  43. mlrun/common/schemas/auth.py +4 -1
  44. mlrun/common/schemas/client_spec.py +1 -1
  45. mlrun/common/schemas/model_monitoring/__init__.py +1 -0
  46. mlrun/common/schemas/model_monitoring/constants.py +11 -0
  47. mlrun/common/schemas/project.py +1 -0
  48. mlrun/common/schemas/runs.py +1 -8
  49. mlrun/common/schemas/schedule.py +1 -8
  50. mlrun/common/schemas/workflow.py +54 -0
  51. mlrun/config.py +42 -40
  52. mlrun/datastore/sources.py +1 -1
  53. mlrun/db/__init__.py +4 -68
  54. mlrun/db/base.py +12 -0
  55. mlrun/db/factory.py +65 -0
  56. mlrun/db/httpdb.py +175 -19
  57. mlrun/db/nopdb.py +4 -2
  58. mlrun/execution.py +4 -2
  59. mlrun/feature_store/__init__.py +1 -0
  60. mlrun/feature_store/api.py +1 -2
  61. mlrun/feature_store/feature_set.py +0 -10
  62. mlrun/feature_store/feature_vector.py +340 -2
  63. mlrun/feature_store/ingestion.py +5 -10
  64. mlrun/feature_store/retrieval/base.py +118 -104
  65. mlrun/feature_store/retrieval/dask_merger.py +17 -10
  66. mlrun/feature_store/retrieval/job.py +4 -1
  67. mlrun/feature_store/retrieval/local_merger.py +18 -18
  68. mlrun/feature_store/retrieval/spark_merger.py +21 -14
  69. mlrun/feature_store/retrieval/storey_merger.py +21 -15
  70. mlrun/kfpops.py +3 -9
  71. mlrun/launcher/base.py +3 -3
  72. mlrun/launcher/client.py +3 -2
  73. mlrun/launcher/factory.py +16 -13
  74. mlrun/lists.py +0 -11
  75. mlrun/model.py +9 -15
  76. mlrun/model_monitoring/helpers.py +15 -25
  77. mlrun/model_monitoring/model_monitoring_batch.py +72 -4
  78. mlrun/model_monitoring/prometheus.py +219 -0
  79. mlrun/model_monitoring/stores/__init__.py +15 -9
  80. mlrun/model_monitoring/stores/sql_model_endpoint_store.py +3 -1
  81. mlrun/model_monitoring/stream_processing.py +181 -29
  82. mlrun/package/packager.py +6 -8
  83. mlrun/package/packagers/default_packager.py +121 -10
  84. mlrun/platforms/__init__.py +0 -2
  85. mlrun/platforms/iguazio.py +0 -56
  86. mlrun/projects/pipelines.py +57 -158
  87. mlrun/projects/project.py +6 -32
  88. mlrun/render.py +1 -1
  89. mlrun/run.py +2 -124
  90. mlrun/runtimes/__init__.py +6 -42
  91. mlrun/runtimes/base.py +26 -1241
  92. mlrun/runtimes/daskjob.py +2 -198
  93. mlrun/runtimes/function.py +16 -5
  94. mlrun/runtimes/kubejob.py +5 -29
  95. mlrun/runtimes/mpijob/__init__.py +2 -2
  96. mlrun/runtimes/mpijob/abstract.py +10 -1
  97. mlrun/runtimes/mpijob/v1.py +0 -76
  98. mlrun/runtimes/mpijob/v1alpha1.py +1 -74
  99. mlrun/runtimes/nuclio.py +3 -2
  100. mlrun/runtimes/pod.py +0 -10
  101. mlrun/runtimes/remotesparkjob.py +1 -15
  102. mlrun/runtimes/serving.py +1 -1
  103. mlrun/runtimes/sparkjob/__init__.py +0 -1
  104. mlrun/runtimes/sparkjob/abstract.py +4 -131
  105. mlrun/serving/states.py +1 -1
  106. mlrun/utils/db.py +0 -2
  107. mlrun/utils/helpers.py +19 -13
  108. mlrun/utils/notifications/notification_pusher.py +5 -25
  109. mlrun/utils/regex.py +7 -2
  110. mlrun/utils/version/version.json +2 -2
  111. {mlrun-1.5.0rc1.dist-info → mlrun-1.5.0rc2.dist-info}/METADATA +24 -23
  112. {mlrun-1.5.0rc1.dist-info → mlrun-1.5.0rc2.dist-info}/RECORD +116 -107
  113. {mlrun-1.5.0rc1.dist-info → mlrun-1.5.0rc2.dist-info}/WHEEL +1 -1
  114. mlrun/mlutils/data.py +0 -160
  115. mlrun/mlutils/models.py +0 -78
  116. mlrun/mlutils/plots.py +0 -902
  117. {mlrun-1.5.0rc1.dist-info → mlrun-1.5.0rc2.dist-info}/LICENSE +0 -0
  118. {mlrun-1.5.0rc1.dist-info → mlrun-1.5.0rc2.dist-info}/entry_points.txt +0 -0
  119. {mlrun-1.5.0rc1.dist-info → mlrun-1.5.0rc2.dist-info}/top_level.txt +0 -0
mlrun/package/packager.py CHANGED
@@ -107,8 +107,7 @@ class Packager(ABC, metaclass=_PackagerMeta):
107
107
 
108
108
  Preferably, each packager should handle a single type of object.
109
109
 
110
- Linking Artifacts (extra data)
111
- ------------------------------
110
+ **Linking Artifacts (extra data)**
112
111
 
113
112
  In order to link between packages (using the extra data or metrics spec attributes of an artifact), you should use
114
113
  the key as if it exists and as value ellipses (...). The manager will link all packages once it is done packing.
@@ -118,8 +117,7 @@ class Packager(ABC, metaclass=_PackagerMeta):
118
117
  artifact = Artifact(key="my_artifact")
119
118
  artifact.spec.extra_data = {key: ... for key in extra_data}
120
119
 
121
- Clearing Outputs
122
- ----------------
120
+ **Clearing Outputs**
123
121
 
124
122
  Some of the packagers may produce files and temporary directories that should be deleted once done with logging the
125
123
  artifact. The packager can mark paths of files and directories to delete after logging using the class method
@@ -131,15 +129,15 @@ class Packager(ABC, metaclass=_PackagerMeta):
131
129
  with open("./some_file.txt", "w") as file:
132
130
  file.write("Pack me")
133
131
  artifact = Artifact(key="my_artifact")
134
- cls.future_clear(path="./some_file.txt")
132
+ cls.add_future_clearing_path(path="./some_file.txt")
135
133
  return artifact, None
136
134
  """
137
135
 
138
- # The type of object this packager can pack and unpack:
136
+ #: The type of object this packager can pack and unpack.
139
137
  PACKABLE_OBJECT_TYPE: Type = ...
140
138
 
141
- # The priority of this packager in the packagers collection of the manager (lower is better)
142
- PRIORITY = ...
139
+ #: The priority of this packager in the packagers collection of the manager (lower is better).
140
+ PRIORITY: int = ...
143
141
 
144
142
  # List of all paths to be deleted by the manager of this packager post logging the packages:
145
143
  _CLEARING_PATH_LIST: List[str] = []
@@ -16,16 +16,123 @@ import inspect
16
16
  from types import MethodType
17
17
  from typing import Any, List, Tuple, Type, Union
18
18
 
19
+ import docstring_parser
20
+
19
21
  from mlrun.artifacts import Artifact
20
22
  from mlrun.datastore import DataItem
21
23
  from mlrun.utils import logger
22
24
 
23
25
  from ..errors import MLRunPackagePackingError, MLRunPackageUnpackingError
24
- from ..packager import Packager
26
+ from ..packager import Packager, _PackagerMeta
25
27
  from ..utils import DEFAULT_PICKLE_MODULE, ArtifactType, Pickler, TypeHintUtils
26
28
 
27
29
 
28
- class DefaultPackager(Packager):
30
+ class _DefaultPackagerMeta(_PackagerMeta):
31
+ """
32
+ Metaclass for `DefaultPackager` to override `__doc__` attribute into a class property. This way sphinx will get a
33
+ dynamically generated docstring that will include a summary of the packager.
34
+ """
35
+
36
+ def __new__(mcls, name: str, bases: tuple, namespace: dict, **kwargs):
37
+ """
38
+ Create a new DefaultPackager metaclass that saves the original packager docstring to another attribute named
39
+ `_packager_doc`.
40
+
41
+ :param name: A string representing the name of the class being instantiated.
42
+ :param bases: A tuple of classes from which the class will inherit.
43
+ :param namespace: The namespace of the class holding its attributes (from here the docstring will be taken).
44
+ """
45
+ # Save the original doc string to a separate class variable as it will be overriden later on by the metaclass
46
+ # property `__doc__`:
47
+ namespace["_packager_doc"] = namespace.get("__doc__", "")
48
+
49
+ # Continue creating the metaclass:
50
+ return super().__new__(mcls, name, bases, namespace, **kwargs)
51
+
52
+ @property
53
+ def __doc__(cls) -> str:
54
+ """
55
+ Override the `__doc__` attribute of a `DefaultPackager` to be a property in order to auto-summarize the
56
+ packager's class docstring. The summary is concatenated after the original class doc string.
57
+
58
+ The summary will be in the following structure:
59
+
60
+ <cls._packager_doc>
61
+
62
+ .. rubric:: Packager Summary
63
+
64
+ **Packing Type**: ``<cls.PACKABLE_OBJECT_TYPE>``
65
+
66
+ **Packing Sub-Classes**: True / False
67
+
68
+ **Artifact Types**:
69
+
70
+ * **type 1**: ...
71
+
72
+ * configuration 1 - ...
73
+ * configuration 2 - ...
74
+
75
+ * **type 2**: ...
76
+
77
+ * configuration 1: ...
78
+ * configuration 2: ...
79
+
80
+ :returns: The original docstring with the generated packager summary.
81
+ """
82
+ # Get the original packager class doc string:
83
+ packager_doc_string = cls._packager_doc.split("\n")
84
+ packager_doc_string = "\n".join(line[4:] for line in packager_doc_string)
85
+
86
+ # Parse the packable type section:
87
+ type_name = (
88
+ "Any type"
89
+ if cls.PACKABLE_OBJECT_TYPE is ...
90
+ else (
91
+ f"``{str(cls.PACKABLE_OBJECT_TYPE)}``"
92
+ if TypeHintUtils.is_typing_type(type_hint=cls.PACKABLE_OBJECT_TYPE)
93
+ else f"``{cls.PACKABLE_OBJECT_TYPE.__module__}.{cls.PACKABLE_OBJECT_TYPE.__name__}``"
94
+ )
95
+ )
96
+ packing_type = f"**Packing Type**: {type_name}"
97
+
98
+ # Subclasses support section:
99
+ packing_sub_classes = f"**Packing Sub-Classes**: {cls.PACK_SUBCLASSES}"
100
+
101
+ # Artifact types section:
102
+ artifact_types = "**Artifact Types**:"
103
+ for artifact_type in cls.get_supported_artifact_types():
104
+ # Get the packing method docstring:
105
+ method_doc = docstring_parser.parse(
106
+ getattr(cls, f"pack_{artifact_type}").__doc__
107
+ )
108
+ # Add the artifact type bullet:
109
+ artifact_type_doc = f"{method_doc.short_description or ''}{method_doc.long_description or ''}".replace(
110
+ "\n", ""
111
+ )
112
+ artifact_types += f"\n\n* **{artifact_type}** - " + artifact_type_doc
113
+ # Add the artifact type configurations (ignoring the `obj` and `key` parameters):
114
+ configurations_doc = "\n\n * ".join(
115
+ "{} - {}".format(
116
+ parameter.arg_name, parameter.description.replace("\n", "")
117
+ )
118
+ for parameter in method_doc.params[2:]
119
+ )
120
+ if configurations_doc:
121
+ artifact_types += f"\n\n * {configurations_doc}"
122
+
123
+ # Construct the final doc string and return:
124
+ doc = (
125
+ f"{packager_doc_string}"
126
+ "\n\n.. rubric:: Packager Summary"
127
+ f"\n\n{packing_type}"
128
+ f"\n\n{packing_sub_classes}"
129
+ f"\n\n{artifact_types}"
130
+ f"\n\n"
131
+ )
132
+ return doc
133
+
134
+
135
+ class DefaultPackager(Packager, metaclass=_DefaultPackagerMeta):
29
136
  """
30
137
  A default packager that handles all types and pack them as pickle files.
31
138
 
@@ -89,7 +196,7 @@ class DefaultPackager(Packager):
89
196
 
90
197
  Important to remember (from the ``Packager`` docstring):
91
198
 
92
- * Linking artifacts ("extra data"): In order to link between packages (using the extra data or metrics spec
199
+ * **Linking artifacts** ("extra data"): In order to link between packages (using the extra data or metrics spec
93
200
  attributes of an artifact), you should use the key as if it exists and as value ellipses (...). The manager will
94
201
  link all packages once it is done packing.
95
202
 
@@ -98,9 +205,9 @@ class DefaultPackager(Packager):
98
205
  artifact = Artifact(key="my_artifact")
99
206
  artifact.spec.extra_data = {key: ... for key in extra_data}
100
207
 
101
- * Clearing outputs: Some packagers may produce files and temporary directories that should be deleted once done with
102
- logging the artifact. The packager can mark paths of files and directories to delete after logging using the class
103
- method ``future_clear``.
208
+ * **Clearing outputs**: Some packagers may produce files and temporary directories that should be deleted once done
209
+ with logging the artifact. The packager can mark paths of files and directories to delete after logging using the
210
+ class method ``future_clear``.
104
211
 
105
212
  For example, in the following packager's ``pack`` method we can write a text file, create an Artifact and then
106
213
  mark the text file to be deleted once the artifact is logged::
@@ -110,15 +217,19 @@ class DefaultPackager(Packager):
110
217
  artifact = Artifact(key="my_artifact")
111
218
  cls.future_clear(path="./some_file.txt")
112
219
  return artifact, None
220
+
113
221
  """
114
222
 
115
- # The type of object this packager can pack and unpack:
223
+ #: The type of object this packager can pack and unpack.
116
224
  PACKABLE_OBJECT_TYPE: Type = ...
117
- # A flag for indicating whether to pack all subclasses of the `PACKABLE_OBJECT_TYPE` as well:
225
+
226
+ #: A flag for indicating whether to pack all subclasses of the `PACKABLE_OBJECT_TYPE` as well.
118
227
  PACK_SUBCLASSES = False
119
- # The default artifact type to pack as:
228
+
229
+ #: The default artifact type to pack as.
120
230
  DEFAULT_PACKING_ARTIFACT_TYPE = ArtifactType.OBJECT
121
- # The default artifact type to unpack from:
231
+
232
+ #: The default artifact type to unpack from.
122
233
  DEFAULT_UNPACKING_ARTIFACT_TYPE = ArtifactType.OBJECT
123
234
 
124
235
  @classmethod
@@ -23,8 +23,6 @@ from .iguazio import (
23
23
  add_or_refresh_credentials,
24
24
  is_iguazio_session_cookie,
25
25
  mount_v3io,
26
- mount_v3io_extended,
27
- mount_v3io_legacy,
28
26
  v3io_cred,
29
27
  )
30
28
  from .other import (
@@ -25,7 +25,6 @@ import requests
25
25
  import semver
26
26
  import urllib3
27
27
  import v3io
28
- from deprecated import deprecated
29
28
 
30
29
  import mlrun.errors
31
30
  from mlrun.config import config as mlconf
@@ -37,34 +36,6 @@ _cached_control_session = None
37
36
  VolumeMount = namedtuple("Mount", ["path", "sub_path"])
38
37
 
39
38
 
40
- # TODO: Remove in 1.5.0
41
- @deprecated(
42
- version="1.3.0",
43
- reason="'mount_v3io_extended' will be removed in 1.5.0, use 'mount_v3io' instead",
44
- category=FutureWarning,
45
- )
46
- def mount_v3io_extended(
47
- name="v3io", remote="", mounts=None, access_key="", user="", secret=None
48
- ):
49
- """Modifier function to apply to a Container Op to volume mount a v3io path
50
- :param name: the volume name
51
- :param remote: the v3io path to use for the volume. ~/ prefix will be replaced with /users/<username>/
52
- :param mounts: list of mount & volume sub paths (type VolumeMount).
53
- empty mounts & remote will default to mount /v3io & /User.
54
- :param access_key: the access key used to auth against v3io. if not given V3IO_ACCESS_KEY env var will be used
55
- :param user: the username used to auth against v3io. if not given V3IO_USERNAME env var will be used
56
- :param secret: k8s secret name which would be used to get the username and access key to auth against v3io.
57
- """
58
- return mount_v3io(
59
- name=name,
60
- remote=remote,
61
- volume_mounts=mounts,
62
- access_key=access_key,
63
- user=user,
64
- secret=secret,
65
- )
66
-
67
-
68
39
  def mount_v3io(
69
40
  name="v3io",
70
41
  remote="",
@@ -109,33 +80,6 @@ def mount_v3io(
109
80
  return _attach_volume_mounts_and_creds
110
81
 
111
82
 
112
- # TODO: Remove in 1.5.0
113
- @deprecated(
114
- version="1.3.0",
115
- reason="'mount_v3io_legacy' will be removed in 1.5.0, use 'mount_v3io' instead",
116
- category=FutureWarning,
117
- )
118
- def mount_v3io_legacy(
119
- name="v3io", remote="~/", mount_path="/User", access_key="", user="", secret=None
120
- ):
121
- """Modifier function to apply to a Container Op to volume mount a v3io path
122
- :param name: the volume name
123
- :param remote: the v3io path to use for the volume. ~/ prefix will be replaced with /users/<username>/
124
- :param mount_path: the volume mount path
125
- :param access_key: the access key used to auth against v3io. if not given V3IO_ACCESS_KEY env var will be used
126
- :param user: the username used to auth against v3io. if not given V3IO_USERNAME env var will be used
127
- :param secret: k8s secret name which would be used to get the username and access key to auth against v3io.
128
- """
129
- return mount_v3io(
130
- name=name,
131
- remote=remote,
132
- volume_mounts=[VolumeMount(path=mount_path, sub_path="")],
133
- access_key=access_key,
134
- user=user,
135
- secret=secret,
136
- )
137
-
138
-
139
83
  def _enrich_and_validate_v3io_mounts(remote="", volume_mounts=None, user=""):
140
84
  if remote and not volume_mounts:
141
85
  raise mlrun.errors.MLRunInvalidArgumentError(
@@ -16,11 +16,9 @@ import builtins
16
16
  import importlib.util as imputil
17
17
  import os
18
18
  import tempfile
19
- import time
20
19
  import traceback
21
20
  import typing
22
21
  import uuid
23
- import warnings
24
22
 
25
23
  import kfp.compiler
26
24
  from kfp import dsl
@@ -30,7 +28,13 @@ import mlrun
30
28
  import mlrun.common.schemas
31
29
  import mlrun.utils.notifications
32
30
  from mlrun.errors import err_to_str
33
- from mlrun.utils import get_ui_url, logger, new_pipe_metadata
31
+ from mlrun.utils import (
32
+ get_ui_url,
33
+ logger,
34
+ new_pipe_metadata,
35
+ normalize_workflow_name,
36
+ retry_until_successful,
37
+ )
34
38
 
35
39
  from ..common.helpers import parse_versioned_object_uri
36
40
  from ..config import config
@@ -72,32 +76,23 @@ class WorkflowSpec(mlrun.model.ModelObj):
72
76
  args=None,
73
77
  name=None,
74
78
  handler=None,
75
- # TODO: deprecated, remove in 1.5.0
76
- ttl=None,
77
79
  args_schema: dict = None,
78
80
  schedule: typing.Union[str, mlrun.common.schemas.ScheduleCronTrigger] = None,
79
81
  cleanup_ttl: int = None,
82
+ image: str = None,
80
83
  ):
81
- if ttl:
82
- warnings.warn(
83
- "'ttl' is deprecated, use 'cleanup_ttl' instead. "
84
- "This will be removed in 1.5.0",
85
- # TODO: Remove this in 1.5.0
86
- FutureWarning,
87
- )
88
-
89
84
  self.engine = engine
90
85
  self.code = code
91
86
  self.path = path
92
87
  self.args = args
93
88
  self.name = name
94
89
  self.handler = handler
95
- self.ttl = cleanup_ttl or ttl
96
- self.cleanup_ttl = cleanup_ttl or ttl
90
+ self.cleanup_ttl = cleanup_ttl
97
91
  self.args_schema = args_schema
98
92
  self.run_local = False
99
93
  self._tmp_path = None
100
94
  self.schedule = schedule
95
+ self.image = image
101
96
 
102
97
  def get_source_file(self, context=""):
103
98
  if not self.code and not self.path:
@@ -553,7 +548,7 @@ class _KFPRunner(_PipelineRunner):
553
548
 
554
549
  conf = new_pipe_metadata(
555
550
  artifact_path=artifact_path,
556
- cleanup_ttl=workflow_spec.cleanup_ttl or workflow_spec.ttl,
551
+ cleanup_ttl=workflow_spec.cleanup_ttl,
557
552
  )
558
553
  compiler.Compiler().compile(pipeline, target, pipeline_conf=conf)
559
554
  workflow_spec.clear_tmp()
@@ -586,7 +581,7 @@ class _KFPRunner(_PipelineRunner):
586
581
  experiment=name or workflow_spec.name,
587
582
  namespace=namespace,
588
583
  artifact_path=artifact_path,
589
- cleanup_ttl=workflow_spec.cleanup_ttl or workflow_spec.ttl,
584
+ cleanup_ttl=workflow_spec.cleanup_ttl,
590
585
  )
591
586
  project.notifiers.push_pipeline_start_message(
592
587
  project.metadata.name,
@@ -747,137 +742,56 @@ class _RemoteRunner(_PipelineRunner):
747
742
 
748
743
  engine = "remote"
749
744
 
750
- @staticmethod
751
- def _prepare_load_and_run_function(
752
- source: str,
753
- project_name: str,
754
- save: bool,
755
- workflow_name: str,
756
- workflow_spec: WorkflowSpec,
757
- artifact_path: str,
758
- workflow_handler: str,
759
- namespace: str,
760
- subpath: str,
761
- ) -> typing.Tuple[mlrun.runtimes.RemoteRuntime, "mlrun.RunObject"]:
762
- """
763
- Helper function for creating the runspec of the load and run function.
764
- For internal use only.
765
- :param source: The source of the project.
766
- :param project_name: project name
767
- :param save: either to save the project in the DB
768
- :param workflow_name: workflow name
769
- :param workflow_spec: workflow to run
770
- :param artifact_path: path to store artifacts
771
- :param workflow_handler: workflow function handler (for running workflow function directly)
772
- :param namespace: kubernetes namespace if other than default
773
- :param subpath: project subpath (within the archive)
774
- :return:
775
- """
776
- # Creating the load project and workflow running function:
777
- load_and_run_fn = mlrun.new_function(
778
- name=mlrun.mlconf.default_workflow_runner_name.format(workflow_name),
779
- project=project_name,
780
- kind="job",
781
- image=mlrun.mlconf.default_base_image,
782
- )
783
- runspec = mlrun.RunObject(
784
- spec=mlrun.model.RunSpec(
785
- parameters={
786
- "url": source,
787
- "project_name": project_name,
788
- "save": save,
789
- "workflow_name": workflow_name or workflow_spec.name,
790
- "workflow_path": workflow_spec.path,
791
- "workflow_arguments": workflow_spec.args,
792
- "artifact_path": artifact_path,
793
- "workflow_handler": workflow_handler or workflow_spec.handler,
794
- "namespace": namespace,
795
- "ttl": workflow_spec.cleanup_ttl or workflow_spec.ttl,
796
- "engine": workflow_spec.engine,
797
- "local": workflow_spec.run_local,
798
- "schedule": workflow_spec.schedule,
799
- "subpath": subpath,
800
- },
801
- handler="mlrun.projects.load_and_run",
802
- ),
803
- metadata=mlrun.model.RunMetadata(name=workflow_name),
804
- )
805
-
806
- runspec = runspec.set_label("job-type", "workflow-runner").set_label(
807
- "workflow", workflow_name
808
- )
809
- return load_and_run_fn, runspec
810
-
811
745
  @classmethod
812
746
  def run(
813
747
  cls,
814
- project,
748
+ project: "mlrun.projects.MlrunProject",
815
749
  workflow_spec: WorkflowSpec,
816
- name=None,
817
- workflow_handler=None,
818
- secrets=None,
819
- artifact_path=None,
820
- namespace=None,
821
- source=None,
750
+ name: str = None,
751
+ workflow_handler: typing.Union[str, typing.Callable] = None,
752
+ secrets: mlrun.secrets.SecretsStore = None,
753
+ artifact_path: str = None,
754
+ namespace: str = None,
755
+ source: str = None,
822
756
  ) -> typing.Optional[_PipelineRunStatus]:
823
- workflow_name = name.split("-")[-1] if f"{project.name}-" in name else name
824
-
825
- run_id = None
826
-
827
- # If the user provided a source we want to load the project from the source
828
- # (like from a specific commit/branch from git repo) without changing the source of the project (save=False).
829
- save, current_source = (
830
- (False, source) if source else (True, project.spec.source)
831
- )
832
- if "://" not in current_source:
833
- raise mlrun.errors.MLRunInvalidArgumentError(
834
- f"Remote workflows can only be performed by a project with remote source (e.g git:// or http://),"
835
- f" but the specified source '{current_source}' is not remote. "
836
- f"Either put your code in Git, or archive it and then set a source to it."
837
- f" For more details, read"
838
- f" https://docs.mlrun.org/en/latest/concepts/scheduled-jobs.html#scheduling-a-workflow"
839
- )
840
-
841
- # Creating the load project and workflow running function:
842
- load_and_run_fn, runspec = cls._prepare_load_and_run_function(
843
- source=current_source,
844
- project_name=project.name,
845
- save=save,
846
- workflow_name=workflow_name,
847
- workflow_spec=workflow_spec,
848
- artifact_path=artifact_path,
849
- workflow_handler=workflow_handler,
850
- namespace=namespace,
851
- subpath=project.spec.subpath,
852
- )
757
+ workflow_name = normalize_workflow_name(name=name, project_name=project.name)
758
+ workflow_id = None
853
759
 
854
760
  # The returned engine for this runner is the engine of the workflow.
855
761
  # In this way wait_for_completion/get_run_status would be executed by the correct pipeline runner.
856
762
  inner_engine = get_workflow_engine(workflow_spec.engine)
857
-
858
- msg = "executing workflow"
859
- if workflow_spec.schedule:
860
- msg += " scheduling"
861
- logger.info(
862
- f"{msg} '{load_and_run_fn.metadata.name}' remotely with {workflow_spec.engine} engine"
863
- )
864
-
763
+ run_db = mlrun.get_run_db()
865
764
  try:
866
- run = load_and_run_fn.run(
867
- runspec=runspec,
868
- local=False,
869
- schedule=workflow_spec.schedule,
765
+ workflow_response = run_db.submit_workflow(
766
+ project=project.name,
767
+ name=workflow_name,
768
+ workflow_spec=workflow_spec,
870
769
  artifact_path=artifact_path,
770
+ source=source,
771
+ run_name=config.workflows.default_workflow_runner_name.format(
772
+ workflow_name
773
+ ),
774
+ namespace=namespace,
871
775
  )
872
776
  if workflow_spec.schedule:
873
777
  return
874
- # Fetching workflow id:
875
- while not run_id:
876
- run.refresh()
877
- run_id = run.status.results.get("workflow_id", None)
878
- time.sleep(1)
778
+
779
+ # Getting workflow id from run:
780
+ response = retry_until_successful(
781
+ 1,
782
+ getattr(mlrun.mlconf.workflows.timeouts, inner_engine.engine),
783
+ logger,
784
+ False,
785
+ run_db.get_workflow_id,
786
+ project=project.name,
787
+ name=workflow_response.name,
788
+ run_id=workflow_response.run_id,
789
+ engine=workflow_spec.engine,
790
+ )
791
+ workflow_id = response.workflow_id
879
792
  # After fetching the workflow_id the workflow executed successfully
880
793
  state = mlrun.run.RunStatuses.succeeded
794
+ pipeline_context.clear()
881
795
 
882
796
  except Exception as e:
883
797
  trace = traceback.format_exc()
@@ -888,8 +802,8 @@ class _RemoteRunner(_PipelineRunner):
888
802
  )
889
803
  state = mlrun.run.RunStatuses.failed
890
804
  return _PipelineRunStatus(
891
- run_id,
892
- inner_engine,
805
+ run_id=workflow_id,
806
+ engine=inner_engine,
893
807
  project=project,
894
808
  workflow=workflow_spec,
895
809
  state=state,
@@ -900,8 +814,8 @@ class _RemoteRunner(_PipelineRunner):
900
814
  )
901
815
  pipeline_context.clear()
902
816
  return _PipelineRunStatus(
903
- run_id,
904
- inner_engine,
817
+ run_id=workflow_id,
818
+ engine=inner_engine,
905
819
  project=project,
906
820
  workflow=workflow_spec,
907
821
  state=state,
@@ -919,15 +833,6 @@ def create_pipeline(project, pipeline, functions, secrets=None, handler=None):
919
833
  setattr(mod, "functions", functions)
920
834
  setattr(mod, "this_project", project)
921
835
 
922
- if hasattr(mod, "init_functions"):
923
- # TODO: remove in 1.5.0
924
- warnings.warn(
925
- "'init_functions' is deprecated in 1.3.0 and will be removed in 1.5.0. "
926
- "Place function initialization in the pipeline code.",
927
- FutureWarning,
928
- )
929
- getattr(mod, "init_functions")(functions, project, secrets)
930
-
931
836
  # verify all functions are in this project (init_functions may add new functions)
932
837
  for f in functions.values():
933
838
  f.metadata.project = project.metadata.name
@@ -976,12 +881,11 @@ def load_and_run(
976
881
  namespace: str = None,
977
882
  sync: bool = False,
978
883
  dirty: bool = False,
979
- # TODO: deprecated, remove in 1.5.0
980
- ttl: int = None,
981
884
  engine: str = None,
982
885
  local: bool = None,
983
886
  schedule: typing.Union[str, mlrun.common.schemas.ScheduleCronTrigger] = None,
984
887
  cleanup_ttl: int = None,
888
+ load_only: bool = False,
985
889
  ):
986
890
  """
987
891
  Auxiliary function that the RemoteRunner run once or run every schedule.
@@ -1004,23 +908,14 @@ def load_and_run(
1004
908
  :param namespace: kubernetes namespace if other than default
1005
909
  :param sync: force functions sync before run
1006
910
  :param dirty: allow running the workflow when the git repo is dirty
1007
- :param ttl: pipeline cleanup ttl in secs (time to wait after workflow completion, at which point the
1008
- workflow and all its resources are deleted) (deprecated, use cleanup_ttl instead)
1009
911
  :param engine: workflow engine running the workflow.
1010
912
  supported values are 'kfp' (default) or 'local'
1011
913
  :param local: run local pipeline with local functions (set local=True in function.run())
1012
914
  :param schedule: ScheduleCronTrigger class instance or a standard crontab expression string
1013
915
  :param cleanup_ttl: pipeline cleanup ttl in secs (time to wait after workflow completion, at which point the
1014
916
  workflow and all its resources are deleted)
917
+ :param load_only: for just loading the project, inner use.
1015
918
  """
1016
- if ttl:
1017
- warnings.warn(
1018
- "'ttl' is deprecated, use 'cleanup_ttl' instead. "
1019
- "This will be removed in 1.5.0",
1020
- # TODO: Remove this in 1.5.0
1021
- FutureWarning,
1022
- )
1023
-
1024
919
  try:
1025
920
  project = mlrun.load_project(
1026
921
  context=f"./{project_name}",
@@ -1030,6 +925,7 @@ def load_and_run(
1030
925
  subpath=subpath,
1031
926
  clone=clone,
1032
927
  save=save,
928
+ sync_functions=True,
1033
929
  )
1034
930
  except Exception as error:
1035
931
  if schedule:
@@ -1056,6 +952,9 @@ def load_and_run(
1056
952
 
1057
953
  context.logger.info(f"Loaded project {project.name} from remote successfully")
1058
954
 
955
+ if load_only:
956
+ return
957
+
1059
958
  workflow_log_message = workflow_name or workflow_path
1060
959
  context.logger.info(f"Running workflow {workflow_log_message} from remote")
1061
960
  run = project.run(
@@ -1068,7 +967,7 @@ def load_and_run(
1068
967
  sync=sync,
1069
968
  watch=False, # Required for fetching the workflow_id
1070
969
  dirty=dirty,
1071
- cleanup_ttl=cleanup_ttl or ttl,
970
+ cleanup_ttl=cleanup_ttl,
1072
971
  engine=engine,
1073
972
  local=local,
1074
973
  )