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.
- mlrun/__init__.py +2 -35
- mlrun/__main__.py +1 -40
- 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/functions.py +6 -1
- mlrun/api/api/endpoints/logs.py +17 -3
- 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/workflows.py +355 -0
- mlrun/api/api/utils.py +1 -1
- mlrun/api/crud/__init__.py +1 -0
- mlrun/api/crud/client_spec.py +3 -0
- mlrun/api/crud/model_monitoring/deployment.py +36 -7
- mlrun/api/crud/model_monitoring/grafana.py +1 -1
- mlrun/api/crud/model_monitoring/helpers.py +32 -2
- mlrun/api/crud/model_monitoring/model_endpoints.py +27 -5
- mlrun/api/crud/notifications.py +9 -4
- mlrun/api/crud/pipelines.py +4 -9
- mlrun/api/crud/runtime_resources.py +4 -3
- mlrun/api/crud/secrets.py +21 -0
- mlrun/api/crud/workflows.py +352 -0
- mlrun/api/db/base.py +16 -1
- mlrun/api/db/sqldb/db.py +97 -16
- mlrun/api/launcher.py +26 -7
- mlrun/api/main.py +3 -4
- mlrun/{mlutils → api/rundb}/__init__.py +2 -6
- mlrun/{db → api/rundb}/sqldb.py +35 -83
- 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/utils/builder.py +1 -4
- mlrun/api/utils/clients/chief.py +14 -0
- mlrun/api/utils/scheduler.py +98 -15
- mlrun/api/utils/singletons/db.py +4 -0
- mlrun/artifacts/manager.py +1 -2
- mlrun/common/schemas/__init__.py +6 -0
- mlrun/common/schemas/auth.py +4 -1
- mlrun/common/schemas/client_spec.py +1 -1
- mlrun/common/schemas/model_monitoring/__init__.py +1 -0
- mlrun/common/schemas/model_monitoring/constants.py +11 -0
- mlrun/common/schemas/project.py +1 -0
- mlrun/common/schemas/runs.py +1 -8
- mlrun/common/schemas/schedule.py +1 -8
- mlrun/common/schemas/workflow.py +54 -0
- mlrun/config.py +42 -40
- mlrun/datastore/sources.py +1 -1
- mlrun/db/__init__.py +4 -68
- mlrun/db/base.py +12 -0
- mlrun/db/factory.py +65 -0
- mlrun/db/httpdb.py +175 -19
- 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/feature_set.py +0 -10
- 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 +21 -15
- mlrun/kfpops.py +3 -9
- mlrun/launcher/base.py +3 -3
- mlrun/launcher/client.py +3 -2
- mlrun/launcher/factory.py +16 -13
- mlrun/lists.py +0 -11
- mlrun/model.py +9 -15
- mlrun/model_monitoring/helpers.py +15 -25
- mlrun/model_monitoring/model_monitoring_batch.py +72 -4
- mlrun/model_monitoring/prometheus.py +219 -0
- mlrun/model_monitoring/stores/__init__.py +15 -9
- mlrun/model_monitoring/stores/sql_model_endpoint_store.py +3 -1
- mlrun/model_monitoring/stream_processing.py +181 -29
- mlrun/package/packager.py +6 -8
- mlrun/package/packagers/default_packager.py +121 -10
- mlrun/platforms/__init__.py +0 -2
- mlrun/platforms/iguazio.py +0 -56
- mlrun/projects/pipelines.py +57 -158
- mlrun/projects/project.py +6 -32
- mlrun/render.py +1 -1
- mlrun/run.py +2 -124
- mlrun/runtimes/__init__.py +6 -42
- mlrun/runtimes/base.py +26 -1241
- mlrun/runtimes/daskjob.py +2 -198
- mlrun/runtimes/function.py +16 -5
- mlrun/runtimes/kubejob.py +5 -29
- 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 +0 -10
- mlrun/runtimes/remotesparkjob.py +1 -15
- mlrun/runtimes/serving.py +1 -1
- mlrun/runtimes/sparkjob/__init__.py +0 -1
- mlrun/runtimes/sparkjob/abstract.py +4 -131
- mlrun/serving/states.py +1 -1
- mlrun/utils/db.py +0 -2
- mlrun/utils/helpers.py +19 -13
- mlrun/utils/notifications/notification_pusher.py +5 -25
- mlrun/utils/regex.py +7 -2
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.5.0rc1.dist-info → mlrun-1.5.0rc2.dist-info}/METADATA +24 -23
- {mlrun-1.5.0rc1.dist-info → mlrun-1.5.0rc2.dist-info}/RECORD +116 -107
- {mlrun-1.5.0rc1.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-1.5.0rc1.dist-info → mlrun-1.5.0rc2.dist-info}/LICENSE +0 -0
- {mlrun-1.5.0rc1.dist-info → mlrun-1.5.0rc2.dist-info}/entry_points.txt +0 -0
- {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.
|
|
132
|
+
cls.add_future_clearing_path(path="./some_file.txt")
|
|
135
133
|
return artifact, None
|
|
136
134
|
"""
|
|
137
135
|
|
|
138
|
-
|
|
136
|
+
#: The type of object this packager can pack and unpack.
|
|
139
137
|
PACKABLE_OBJECT_TYPE: Type = ...
|
|
140
138
|
|
|
141
|
-
|
|
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
|
|
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
|
|
102
|
-
logging the artifact. The packager can mark paths of files and directories to delete after logging using the
|
|
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
|
-
|
|
223
|
+
#: The type of object this packager can pack and unpack.
|
|
116
224
|
PACKABLE_OBJECT_TYPE: Type = ...
|
|
117
|
-
|
|
225
|
+
|
|
226
|
+
#: A flag for indicating whether to pack all subclasses of the `PACKABLE_OBJECT_TYPE` as well.
|
|
118
227
|
PACK_SUBCLASSES = False
|
|
119
|
-
|
|
228
|
+
|
|
229
|
+
#: The default artifact type to pack as.
|
|
120
230
|
DEFAULT_PACKING_ARTIFACT_TYPE = ArtifactType.OBJECT
|
|
121
|
-
|
|
231
|
+
|
|
232
|
+
#: The default artifact type to unpack from.
|
|
122
233
|
DEFAULT_UNPACKING_ARTIFACT_TYPE = ArtifactType.OBJECT
|
|
123
234
|
|
|
124
235
|
@classmethod
|
mlrun/platforms/__init__.py
CHANGED
mlrun/platforms/iguazio.py
CHANGED
|
@@ -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(
|
mlrun/projects/pipelines.py
CHANGED
|
@@ -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
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
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
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
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
|
|
970
|
+
cleanup_ttl=cleanup_ttl,
|
|
1072
971
|
engine=engine,
|
|
1073
972
|
local=local,
|
|
1074
973
|
)
|