mlrun 1.7.0rc5__py3-none-any.whl → 1.7.0rc7__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/artifacts/base.py +2 -1
- mlrun/artifacts/plots.py +9 -5
- mlrun/common/constants.py +6 -0
- mlrun/common/schemas/__init__.py +2 -0
- mlrun/common/schemas/model_monitoring/__init__.py +4 -0
- mlrun/common/schemas/model_monitoring/constants.py +35 -18
- mlrun/common/schemas/project.py +1 -0
- mlrun/common/types.py +7 -1
- mlrun/config.py +19 -6
- mlrun/data_types/data_types.py +4 -0
- mlrun/datastore/alibaba_oss.py +130 -0
- mlrun/datastore/azure_blob.py +4 -5
- mlrun/datastore/base.py +22 -16
- mlrun/datastore/datastore.py +4 -0
- mlrun/datastore/google_cloud_storage.py +1 -1
- mlrun/datastore/sources.py +7 -7
- mlrun/db/base.py +14 -6
- mlrun/db/factory.py +1 -1
- mlrun/db/httpdb.py +61 -56
- mlrun/db/nopdb.py +3 -0
- mlrun/launcher/__init__.py +1 -1
- mlrun/launcher/base.py +1 -1
- mlrun/launcher/client.py +1 -1
- mlrun/launcher/factory.py +1 -1
- mlrun/launcher/local.py +1 -1
- mlrun/launcher/remote.py +1 -1
- mlrun/model.py +1 -0
- mlrun/model_monitoring/__init__.py +1 -1
- mlrun/model_monitoring/api.py +104 -301
- mlrun/model_monitoring/application.py +21 -21
- mlrun/model_monitoring/applications/histogram_data_drift.py +130 -40
- mlrun/model_monitoring/controller.py +26 -33
- mlrun/model_monitoring/db/__init__.py +16 -0
- mlrun/model_monitoring/{stores → db/stores}/__init__.py +43 -34
- mlrun/model_monitoring/db/stores/base/__init__.py +15 -0
- mlrun/model_monitoring/{stores/model_endpoint_store.py → db/stores/base/store.py} +47 -6
- mlrun/model_monitoring/db/stores/sqldb/__init__.py +13 -0
- mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +49 -0
- mlrun/model_monitoring/{stores → db/stores/sqldb}/models/base.py +76 -3
- mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +68 -0
- mlrun/model_monitoring/{stores → db/stores/sqldb}/models/sqlite.py +13 -1
- mlrun/model_monitoring/db/stores/sqldb/sql_store.py +662 -0
- mlrun/model_monitoring/db/stores/v3io_kv/__init__.py +13 -0
- mlrun/model_monitoring/{stores/kv_model_endpoint_store.py → db/stores/v3io_kv/kv_store.py} +134 -3
- mlrun/model_monitoring/features_drift_table.py +34 -22
- mlrun/model_monitoring/helpers.py +45 -6
- mlrun/model_monitoring/stream_processing.py +43 -9
- mlrun/model_monitoring/tracking_policy.py +7 -1
- mlrun/model_monitoring/writer.py +4 -36
- mlrun/projects/pipelines.py +13 -1
- mlrun/projects/project.py +279 -117
- mlrun/run.py +72 -74
- mlrun/runtimes/__init__.py +35 -0
- mlrun/runtimes/base.py +7 -1
- mlrun/runtimes/nuclio/api_gateway.py +188 -61
- mlrun/runtimes/nuclio/application/__init__.py +15 -0
- mlrun/runtimes/nuclio/application/application.py +283 -0
- mlrun/runtimes/nuclio/application/reverse_proxy.go +87 -0
- mlrun/runtimes/nuclio/function.py +53 -1
- mlrun/runtimes/nuclio/serving.py +28 -32
- mlrun/runtimes/pod.py +27 -1
- mlrun/serving/server.py +4 -6
- mlrun/serving/states.py +41 -33
- mlrun/utils/helpers.py +34 -0
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.7.0rc5.dist-info → mlrun-1.7.0rc7.dist-info}/METADATA +14 -5
- {mlrun-1.7.0rc5.dist-info → mlrun-1.7.0rc7.dist-info}/RECORD +71 -64
- mlrun/model_monitoring/batch.py +0 -974
- mlrun/model_monitoring/stores/models/__init__.py +0 -27
- mlrun/model_monitoring/stores/models/mysql.py +0 -34
- mlrun/model_monitoring/stores/sql_model_endpoint_store.py +0 -382
- {mlrun-1.7.0rc5.dist-info → mlrun-1.7.0rc7.dist-info}/LICENSE +0 -0
- {mlrun-1.7.0rc5.dist-info → mlrun-1.7.0rc7.dist-info}/WHEEL +0 -0
- {mlrun-1.7.0rc5.dist-info → mlrun-1.7.0rc7.dist-info}/entry_points.txt +0 -0
- {mlrun-1.7.0rc5.dist-info → mlrun-1.7.0rc7.dist-info}/top_level.txt +0 -0
mlrun/projects/project.py
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
+
|
|
14
15
|
import datetime
|
|
15
16
|
import getpass
|
|
16
17
|
import glob
|
|
@@ -44,6 +45,7 @@ import mlrun.runtimes
|
|
|
44
45
|
import mlrun.runtimes.nuclio.api_gateway
|
|
45
46
|
import mlrun.runtimes.pod
|
|
46
47
|
import mlrun.runtimes.utils
|
|
48
|
+
import mlrun.serving
|
|
47
49
|
import mlrun.utils.regex
|
|
48
50
|
from mlrun.datastore.datastore_profile import DatastoreProfile, DatastoreProfile2Json
|
|
49
51
|
from mlrun.runtimes.nuclio.function import RemoteRuntime
|
|
@@ -55,7 +57,6 @@ from ..features import Feature
|
|
|
55
57
|
from ..model import EntrypointParam, ImageBuilder, ModelObj
|
|
56
58
|
from ..model_monitoring.application import (
|
|
57
59
|
ModelMonitoringApplicationBase,
|
|
58
|
-
PushToMonitoringWriter,
|
|
59
60
|
)
|
|
60
61
|
from ..run import code_to_function, get_object, import_function, new_function
|
|
61
62
|
from ..secrets import SecretsStore
|
|
@@ -760,6 +761,7 @@ class ProjectSpec(ModelObj):
|
|
|
760
761
|
default_image=None,
|
|
761
762
|
build=None,
|
|
762
763
|
custom_packagers: list[tuple[str, bool]] = None,
|
|
764
|
+
default_function_node_selector=None,
|
|
763
765
|
):
|
|
764
766
|
self.repo = None
|
|
765
767
|
|
|
@@ -799,6 +801,7 @@ class ProjectSpec(ModelObj):
|
|
|
799
801
|
# in a tuple where the first index is the packager module's path (str) and the second is a flag (bool) for
|
|
800
802
|
# whether it is mandatory for a run (raise exception on collection error) or not.
|
|
801
803
|
self.custom_packagers = custom_packagers or []
|
|
804
|
+
self.default_function_node_selector = default_function_node_selector or {}
|
|
802
805
|
|
|
803
806
|
@property
|
|
804
807
|
def source(self) -> str:
|
|
@@ -1375,14 +1378,7 @@ class MlrunProject(ModelObj):
|
|
|
1375
1378
|
artifact_path = mlrun.utils.helpers.template_artifact_path(
|
|
1376
1379
|
self.spec.artifact_path or mlrun.mlconf.artifact_path, self.metadata.name
|
|
1377
1380
|
)
|
|
1378
|
-
|
|
1379
|
-
# we need to maintain the different trees that generated them
|
|
1380
|
-
producer = ArtifactProducer(
|
|
1381
|
-
"project",
|
|
1382
|
-
self.metadata.name,
|
|
1383
|
-
self.metadata.name,
|
|
1384
|
-
tag=self._get_hexsha() or str(uuid.uuid4()),
|
|
1385
|
-
)
|
|
1381
|
+
project_tag = self._get_project_tag()
|
|
1386
1382
|
for artifact_dict in self.spec.artifacts:
|
|
1387
1383
|
if _is_imported_artifact(artifact_dict):
|
|
1388
1384
|
import_from = artifact_dict["import_from"]
|
|
@@ -1402,6 +1398,15 @@ class MlrunProject(ModelObj):
|
|
|
1402
1398
|
artifact.src_path = path.join(
|
|
1403
1399
|
self.spec.get_code_path(), artifact.src_path
|
|
1404
1400
|
)
|
|
1401
|
+
producer = self._resolve_artifact_producer(artifact, project_tag)
|
|
1402
|
+
# log the artifact only if it doesn't already exist
|
|
1403
|
+
if (
|
|
1404
|
+
producer.name != self.metadata.name
|
|
1405
|
+
and self._resolve_existing_artifact(
|
|
1406
|
+
artifact,
|
|
1407
|
+
)
|
|
1408
|
+
):
|
|
1409
|
+
continue
|
|
1405
1410
|
artifact_manager.log_artifact(
|
|
1406
1411
|
producer, artifact, artifact_path=artifact_path
|
|
1407
1412
|
)
|
|
@@ -1498,12 +1503,20 @@ class MlrunProject(ModelObj):
|
|
|
1498
1503
|
artifact_path = mlrun.utils.helpers.template_artifact_path(
|
|
1499
1504
|
artifact_path, self.metadata.name
|
|
1500
1505
|
)
|
|
1501
|
-
producer =
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
self.
|
|
1505
|
-
|
|
1506
|
-
|
|
1506
|
+
producer = self._resolve_artifact_producer(item)
|
|
1507
|
+
if producer.name != self.metadata.name:
|
|
1508
|
+
# the artifact producer is retained, log it only if it doesn't already exist
|
|
1509
|
+
if existing_artifact := self._resolve_existing_artifact(
|
|
1510
|
+
item,
|
|
1511
|
+
tag,
|
|
1512
|
+
):
|
|
1513
|
+
artifact_key = item if isinstance(item, str) else item.key
|
|
1514
|
+
logger.info(
|
|
1515
|
+
"Artifact already exists, skipping logging",
|
|
1516
|
+
key=artifact_key,
|
|
1517
|
+
tag=tag,
|
|
1518
|
+
)
|
|
1519
|
+
return existing_artifact
|
|
1507
1520
|
item = am.log_artifact(
|
|
1508
1521
|
producer,
|
|
1509
1522
|
item,
|
|
@@ -1834,10 +1847,10 @@ class MlrunProject(ModelObj):
|
|
|
1834
1847
|
monitoring application's constructor.
|
|
1835
1848
|
"""
|
|
1836
1849
|
|
|
1837
|
-
if name in mm_constants.MonitoringFunctionNames.
|
|
1850
|
+
if name in mm_constants.MonitoringFunctionNames.list():
|
|
1838
1851
|
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
1839
|
-
f"
|
|
1840
|
-
f"{mm_constants.MonitoringFunctionNames.
|
|
1852
|
+
f"An application cannot have the following names: "
|
|
1853
|
+
f"{mm_constants.MonitoringFunctionNames.list()}"
|
|
1841
1854
|
)
|
|
1842
1855
|
function_object: RemoteRuntime = None
|
|
1843
1856
|
(
|
|
@@ -1856,16 +1869,6 @@ class MlrunProject(ModelObj):
|
|
|
1856
1869
|
requirements_file,
|
|
1857
1870
|
**application_kwargs,
|
|
1858
1871
|
)
|
|
1859
|
-
models_names = "all"
|
|
1860
|
-
function_object.set_label(
|
|
1861
|
-
mm_constants.ModelMonitoringAppLabel.KEY,
|
|
1862
|
-
mm_constants.ModelMonitoringAppLabel.VAL,
|
|
1863
|
-
)
|
|
1864
|
-
function_object.set_label("models", models_names)
|
|
1865
|
-
|
|
1866
|
-
if not mlrun.mlconf.is_ce_mode():
|
|
1867
|
-
function_object.apply(mlrun.mount_v3io())
|
|
1868
|
-
|
|
1869
1872
|
# save to project spec
|
|
1870
1873
|
self.spec.set_function(resolved_function_name, function_object, func)
|
|
1871
1874
|
|
|
@@ -1924,49 +1927,38 @@ class MlrunProject(ModelObj):
|
|
|
1924
1927
|
|
|
1925
1928
|
def _instantiate_model_monitoring_function(
|
|
1926
1929
|
self,
|
|
1927
|
-
func: typing.Union[str, mlrun.runtimes.BaseRuntime] = None,
|
|
1928
|
-
application_class: typing.Union[
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1930
|
+
func: typing.Union[str, mlrun.runtimes.BaseRuntime, None] = None,
|
|
1931
|
+
application_class: typing.Union[
|
|
1932
|
+
str, ModelMonitoringApplicationBase, None
|
|
1933
|
+
] = None,
|
|
1934
|
+
name: typing.Optional[str] = None,
|
|
1935
|
+
image: typing.Optional[str] = None,
|
|
1936
|
+
handler: typing.Optional[str] = None,
|
|
1937
|
+
with_repo: typing.Optional[bool] = None,
|
|
1938
|
+
tag: typing.Optional[str] = None,
|
|
1939
|
+
requirements: typing.Union[str, list[str], None] = None,
|
|
1935
1940
|
requirements_file: str = "",
|
|
1936
1941
|
**application_kwargs,
|
|
1937
1942
|
) -> tuple[str, mlrun.runtimes.BaseRuntime, dict]:
|
|
1943
|
+
import mlrun.model_monitoring.api
|
|
1944
|
+
|
|
1938
1945
|
function_object: RemoteRuntime = None
|
|
1939
1946
|
kind = None
|
|
1940
1947
|
if (isinstance(func, str) or func is None) and application_class is not None:
|
|
1941
|
-
kind =
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1948
|
+
kind = mlrun.run.RuntimeKinds.serving
|
|
1949
|
+
func = mlrun.model_monitoring.api._create_model_monitoring_function_base(
|
|
1950
|
+
project=self.name,
|
|
1951
|
+
func=func,
|
|
1952
|
+
application_class=application_class,
|
|
1946
1953
|
name=name,
|
|
1947
|
-
project=self.metadata.name,
|
|
1948
|
-
tag=tag,
|
|
1949
|
-
kind=kind,
|
|
1950
1954
|
image=image,
|
|
1955
|
+
tag=tag,
|
|
1951
1956
|
requirements=requirements,
|
|
1952
1957
|
requirements_file=requirements_file,
|
|
1958
|
+
**application_kwargs,
|
|
1953
1959
|
)
|
|
1954
|
-
graph = func.set_topology("flow")
|
|
1955
|
-
if isinstance(application_class, str):
|
|
1956
|
-
first_step = graph.to(
|
|
1957
|
-
class_name=application_class, **application_kwargs
|
|
1958
|
-
)
|
|
1959
|
-
else:
|
|
1960
|
-
first_step = graph.to(class_name=application_class)
|
|
1961
|
-
first_step.to(
|
|
1962
|
-
class_name=PushToMonitoringWriter(
|
|
1963
|
-
project=self.metadata.name,
|
|
1964
|
-
writer_application_name=mm_constants.MonitoringFunctionNames.WRITER,
|
|
1965
|
-
stream_uri=None,
|
|
1966
|
-
),
|
|
1967
|
-
).respond()
|
|
1968
1960
|
elif isinstance(func, str) and isinstance(handler, str):
|
|
1969
|
-
kind =
|
|
1961
|
+
kind = mlrun.run.RuntimeKinds.nuclio
|
|
1970
1962
|
|
|
1971
1963
|
(
|
|
1972
1964
|
resolved_function_name,
|
|
@@ -1984,12 +1976,10 @@ class MlrunProject(ModelObj):
|
|
|
1984
1976
|
requirements,
|
|
1985
1977
|
requirements_file,
|
|
1986
1978
|
)
|
|
1987
|
-
models_names = "all"
|
|
1988
1979
|
function_object.set_label(
|
|
1989
1980
|
mm_constants.ModelMonitoringAppLabel.KEY,
|
|
1990
1981
|
mm_constants.ModelMonitoringAppLabel.VAL,
|
|
1991
1982
|
)
|
|
1992
|
-
function_object.set_label("models", models_names)
|
|
1993
1983
|
|
|
1994
1984
|
if not mlrun.mlconf.is_ce_mode():
|
|
1995
1985
|
function_object.apply(mlrun.mount_v3io())
|
|
@@ -2019,8 +2009,6 @@ class MlrunProject(ModelObj):
|
|
|
2019
2009
|
stream & histogram data drift functions, which are real time nuclio
|
|
2020
2010
|
functions. By default, the image is mlrun/mlrun.
|
|
2021
2011
|
:param deploy_histogram_data_drift_app: If true, deploy the default histogram-based data drift application.
|
|
2022
|
-
|
|
2023
|
-
:returns: model monitoring controller job as a dictionary.
|
|
2024
2012
|
"""
|
|
2025
2013
|
if default_controller_image != "mlrun/mlrun":
|
|
2026
2014
|
# TODO: Remove this in 1.9.0
|
|
@@ -2035,18 +2023,24 @@ class MlrunProject(ModelObj):
|
|
|
2035
2023
|
project=self.name,
|
|
2036
2024
|
image=image,
|
|
2037
2025
|
base_period=base_period,
|
|
2026
|
+
deploy_histogram_data_drift_app=deploy_histogram_data_drift_app,
|
|
2038
2027
|
)
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2028
|
+
|
|
2029
|
+
def deploy_histogram_data_drift_app(
|
|
2030
|
+
self,
|
|
2031
|
+
*,
|
|
2032
|
+
image: str = "mlrun/mlrun",
|
|
2033
|
+
db: Optional[mlrun.db.RunDBInterface] = None,
|
|
2034
|
+
) -> None:
|
|
2035
|
+
"""
|
|
2036
|
+
Deploy the histogram data drift application.
|
|
2037
|
+
|
|
2038
|
+
:param image: The image on which the application will run.
|
|
2039
|
+
:param db: An optional DB object.
|
|
2040
|
+
"""
|
|
2041
|
+
if db is None:
|
|
2042
|
+
db = mlrun.db.get_run_db(secrets=self._secrets)
|
|
2043
|
+
db.deploy_histogram_data_drift_app(project=self.name, image=image)
|
|
2050
2044
|
|
|
2051
2045
|
def update_model_monitoring_controller(
|
|
2052
2046
|
self,
|
|
@@ -2071,20 +2065,22 @@ class MlrunProject(ModelObj):
|
|
|
2071
2065
|
image=image,
|
|
2072
2066
|
)
|
|
2073
2067
|
|
|
2074
|
-
def disable_model_monitoring(
|
|
2068
|
+
def disable_model_monitoring(
|
|
2069
|
+
self, *, delete_histogram_data_drift_app: bool = True
|
|
2070
|
+
) -> None:
|
|
2071
|
+
"""
|
|
2072
|
+
Note: This method is currently not advised for use. See ML-3432.
|
|
2073
|
+
Disable model monitoring by deleting the underlying functions infrastructure from MLRun database.
|
|
2074
|
+
|
|
2075
|
+
:param delete_histogram_data_drift_app: Whether to delete the histogram data drift app.
|
|
2076
|
+
"""
|
|
2075
2077
|
db = mlrun.db.get_run_db(secrets=self._secrets)
|
|
2076
|
-
|
|
2077
|
-
project=self.name,
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
name=mm_constants.MonitoringFunctionNames.WRITER,
|
|
2083
|
-
)
|
|
2084
|
-
db.delete_function(
|
|
2085
|
-
project=self.name,
|
|
2086
|
-
name=mm_constants.MonitoringFunctionNames.STREAM,
|
|
2087
|
-
)
|
|
2078
|
+
for fn_name in mm_constants.MonitoringFunctionNames.list():
|
|
2079
|
+
db.delete_function(project=self.name, name=fn_name)
|
|
2080
|
+
if delete_histogram_data_drift_app:
|
|
2081
|
+
db.delete_function(
|
|
2082
|
+
project=self.name, name=mm_constants.MLRUN_HISTOGRAM_DATA_DRIFT_APP_NAME
|
|
2083
|
+
)
|
|
2088
2084
|
|
|
2089
2085
|
def set_function(
|
|
2090
2086
|
self,
|
|
@@ -2403,13 +2399,47 @@ class MlrunProject(ModelObj):
|
|
|
2403
2399
|
clone_zip(url, self.spec.context, self._secrets)
|
|
2404
2400
|
|
|
2405
2401
|
def create_remote(self, url, name="origin", branch=None):
|
|
2406
|
-
"""
|
|
2402
|
+
"""Create remote for the project git
|
|
2403
|
+
|
|
2404
|
+
This method creates a new remote repository associated with the project's Git repository.
|
|
2405
|
+
If a remote with the specified name already exists, it will not be overwritten.
|
|
2406
|
+
|
|
2407
|
+
If you wish to update the URL of an existing remote, use the `set_remote` method instead.
|
|
2407
2408
|
|
|
2408
2409
|
:param url: remote git url
|
|
2409
2410
|
:param name: name for the remote (default is 'origin')
|
|
2410
2411
|
:param branch: Git branch to use as source
|
|
2411
2412
|
"""
|
|
2413
|
+
self.set_remote(url, name=name, branch=branch, overwrite=False)
|
|
2414
|
+
|
|
2415
|
+
def set_remote(self, url, name="origin", branch=None, overwrite=True):
|
|
2416
|
+
"""Create or update a remote for the project git repository.
|
|
2417
|
+
|
|
2418
|
+
This method allows you to manage remote repositories associated with the project.
|
|
2419
|
+
It checks if a remote with the specified name already exists.
|
|
2420
|
+
|
|
2421
|
+
If a remote with the same name does not exist, it will be created.
|
|
2422
|
+
If a remote with the same name already exists,
|
|
2423
|
+
the behavior depends on the value of the 'overwrite' flag.
|
|
2424
|
+
|
|
2425
|
+
:param url: remote git url
|
|
2426
|
+
:param name: name for the remote (default is 'origin')
|
|
2427
|
+
:param branch: Git branch to use as source
|
|
2428
|
+
:param overwrite: if True (default), updates the existing remote with the given URL if it already exists.
|
|
2429
|
+
if False, raises an error when attempting to create a remote with a name that already exists.
|
|
2430
|
+
:raises MLRunConflictError: If a remote with the same name already exists and overwrite
|
|
2431
|
+
is set to False.
|
|
2432
|
+
"""
|
|
2412
2433
|
self._ensure_git_repo()
|
|
2434
|
+
if self._remote_exists(name):
|
|
2435
|
+
if overwrite:
|
|
2436
|
+
self.spec.repo.delete_remote(name)
|
|
2437
|
+
else:
|
|
2438
|
+
raise mlrun.errors.MLRunConflictError(
|
|
2439
|
+
f"Remote '{name}' already exists in the project, "
|
|
2440
|
+
f"each remote in the project must have a unique name."
|
|
2441
|
+
"Use 'set_remote' with 'override=True' inorder to update the remote, or choose a different name."
|
|
2442
|
+
)
|
|
2413
2443
|
self.spec.repo.create_remote(name, url=url)
|
|
2414
2444
|
url = url.replace("https://", "git://")
|
|
2415
2445
|
if not branch:
|
|
@@ -2422,6 +2452,22 @@ class MlrunProject(ModelObj):
|
|
|
2422
2452
|
self.spec._source = self.spec.source or url
|
|
2423
2453
|
self.spec.origin_url = self.spec.origin_url or url
|
|
2424
2454
|
|
|
2455
|
+
def remove_remote(self, name):
|
|
2456
|
+
"""Remove a remote from the project's Git repository.
|
|
2457
|
+
|
|
2458
|
+
This method removes the remote repository associated with the specified name from the project's Git repository.
|
|
2459
|
+
|
|
2460
|
+
:param name: Name of the remote to remove.
|
|
2461
|
+
"""
|
|
2462
|
+
if self._remote_exists(name):
|
|
2463
|
+
self.spec.repo.delete_remote(name)
|
|
2464
|
+
else:
|
|
2465
|
+
logger.warning(f"The remote '{name}' does not exist. Nothing to remove.")
|
|
2466
|
+
|
|
2467
|
+
def _remote_exists(self, name):
|
|
2468
|
+
"""Check if a remote with the given name already exists"""
|
|
2469
|
+
return any(remote.name == name for remote in self.spec.repo.remotes)
|
|
2470
|
+
|
|
2425
2471
|
def _ensure_git_repo(self):
|
|
2426
2472
|
if self.spec.repo:
|
|
2427
2473
|
return
|
|
@@ -2687,40 +2733,41 @@ class MlrunProject(ModelObj):
|
|
|
2687
2733
|
cleanup_ttl: int = None,
|
|
2688
2734
|
notifications: list[mlrun.model.Notification] = None,
|
|
2689
2735
|
) -> _PipelineRunStatus:
|
|
2690
|
-
"""
|
|
2691
|
-
|
|
2692
|
-
:param name:
|
|
2693
|
-
:param workflow_path:
|
|
2694
|
-
|
|
2695
|
-
:param
|
|
2696
|
-
|
|
2697
|
-
:param
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
:param
|
|
2701
|
-
|
|
2702
|
-
:param
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
:param
|
|
2706
|
-
:param engine: workflow engine running the workflow.
|
|
2707
|
-
supported values are 'kfp' (default), 'local' or 'remote'.
|
|
2708
|
-
for setting engine for remote running use 'remote:local' or 'remote:kfp'.
|
|
2709
|
-
:param local: run local pipeline with local functions (set local=True in function.run())
|
|
2736
|
+
"""Run a workflow using kubeflow pipelines
|
|
2737
|
+
|
|
2738
|
+
:param name: Name of the workflow
|
|
2739
|
+
:param workflow_path: URL to a workflow file, if not a project workflow
|
|
2740
|
+
:param arguments: Kubeflow pipelines arguments (parameters)
|
|
2741
|
+
:param artifact_path: Target path/URL for workflow artifacts, the string '{{workflow.uid}}' will be
|
|
2742
|
+
replaced by workflow id.
|
|
2743
|
+
:param workflow_handler: Workflow function handler (for running workflow function directly)
|
|
2744
|
+
:param namespace: Kubernetes namespace if other than default
|
|
2745
|
+
:param sync: Force functions sync before run
|
|
2746
|
+
:param watch: Wait for pipeline completion
|
|
2747
|
+
:param dirty: Allow running the workflow when the git repo is dirty
|
|
2748
|
+
:param engine: Workflow engine running the workflow.
|
|
2749
|
+
Supported values are 'kfp' (default), 'local' or 'remote'.
|
|
2750
|
+
For setting engine for remote running use 'remote:local' or 'remote:kfp'.
|
|
2751
|
+
:param local: Run local pipeline with local functions (set local=True in function.run())
|
|
2710
2752
|
:param schedule: ScheduleCronTrigger class instance or a standard crontab expression string
|
|
2711
2753
|
(which will be converted to the class using its `from_crontab` constructor),
|
|
2712
2754
|
see this link for help:
|
|
2713
2755
|
https://apscheduler.readthedocs.io/en/3.x/modules/triggers/cron.html#module-apscheduler.triggers.cron
|
|
2714
2756
|
for using the pre-defined workflow's schedule, set `schedule=True`
|
|
2715
|
-
:param timeout:
|
|
2716
|
-
:param source:
|
|
2717
|
-
|
|
2757
|
+
:param timeout: Timeout in seconds to wait for pipeline completion (watch will be activated)
|
|
2758
|
+
:param source: Source to use instead of the actual `project.spec.source` (used when engine is remote).
|
|
2759
|
+
Can be a one of:
|
|
2760
|
+
1. Remote URL which is loaded dynamically to the workflow runner.
|
|
2761
|
+
2. A path to the project's context on the workflow runner's image.
|
|
2762
|
+
Path can be absolute or relative to `project.spec.build.source_code_target_dir` if defined
|
|
2763
|
+
(enriched when building a project image with source, see `MlrunProject.build_image`).
|
|
2764
|
+
For other engines the source is used to validate that the code is up-to-date.
|
|
2718
2765
|
:param cleanup_ttl:
|
|
2719
|
-
|
|
2720
|
-
|
|
2766
|
+
Pipeline cleanup ttl in secs (time to wait after workflow completion, at which point the
|
|
2767
|
+
Workflow and all its resources are deleted)
|
|
2721
2768
|
:param notifications:
|
|
2722
|
-
|
|
2723
|
-
:returns:
|
|
2769
|
+
List of notifications to send for workflow completion
|
|
2770
|
+
:returns: Run id
|
|
2724
2771
|
"""
|
|
2725
2772
|
|
|
2726
2773
|
arguments = arguments or {}
|
|
@@ -3119,6 +3166,7 @@ class MlrunProject(ModelObj):
|
|
|
3119
3166
|
requirements_file: str = None,
|
|
3120
3167
|
builder_env: dict = None,
|
|
3121
3168
|
extra_args: str = None,
|
|
3169
|
+
source_code_target_dir: str = None,
|
|
3122
3170
|
):
|
|
3123
3171
|
"""specify builder configuration for the project
|
|
3124
3172
|
|
|
@@ -3139,6 +3187,8 @@ class MlrunProject(ModelObj):
|
|
|
3139
3187
|
e.g. builder_env={"GIT_TOKEN": token}, does not work yet in KFP
|
|
3140
3188
|
:param extra_args: A string containing additional builder arguments in the format of command-line options,
|
|
3141
3189
|
e.g. extra_args="--skip-tls-verify --build-arg A=val"
|
|
3190
|
+
:param source_code_target_dir: Path on the image where source code would be extracted
|
|
3191
|
+
(by default `/home/mlrun_code`)
|
|
3142
3192
|
"""
|
|
3143
3193
|
if not overwrite_build_params:
|
|
3144
3194
|
# TODO: change overwrite_build_params default to True in 1.8.0
|
|
@@ -3162,6 +3212,7 @@ class MlrunProject(ModelObj):
|
|
|
3162
3212
|
overwrite=overwrite_build_params,
|
|
3163
3213
|
builder_env=builder_env,
|
|
3164
3214
|
extra_args=extra_args,
|
|
3215
|
+
source_code_target_dir=source_code_target_dir,
|
|
3165
3216
|
)
|
|
3166
3217
|
|
|
3167
3218
|
if set_as_default and image != self.default_image:
|
|
@@ -3208,7 +3259,7 @@ class MlrunProject(ModelObj):
|
|
|
3208
3259
|
* False: The new params are merged with the existing
|
|
3209
3260
|
* True: The existing params are replaced by the new ones
|
|
3210
3261
|
:param extra_args: A string containing additional builder arguments in the format of command-line options,
|
|
3211
|
-
e.g. extra_args="--skip-tls-verify --build-arg A=val"
|
|
3262
|
+
e.g. extra_args="--skip-tls-verify --build-arg A=val"
|
|
3212
3263
|
:param target_dir: Path on the image where source code would be extracted (by default `/home/mlrun_code`)
|
|
3213
3264
|
"""
|
|
3214
3265
|
if not base_image:
|
|
@@ -3276,6 +3327,11 @@ class MlrunProject(ModelObj):
|
|
|
3276
3327
|
force_build=True,
|
|
3277
3328
|
)
|
|
3278
3329
|
|
|
3330
|
+
# Get the enriched target dir from the function
|
|
3331
|
+
self.spec.build.source_code_target_dir = (
|
|
3332
|
+
function.spec.build.source_code_target_dir
|
|
3333
|
+
)
|
|
3334
|
+
|
|
3279
3335
|
try:
|
|
3280
3336
|
mlrun.db.get_run_db(secrets=self._secrets).delete_function(
|
|
3281
3337
|
name=function.metadata.name
|
|
@@ -3333,7 +3389,12 @@ class MlrunProject(ModelObj):
|
|
|
3333
3389
|
artifact = db.read_artifact(
|
|
3334
3390
|
key, tag, iter=iter, project=self.metadata.name, tree=tree
|
|
3335
3391
|
)
|
|
3336
|
-
|
|
3392
|
+
|
|
3393
|
+
# in tests, if an artifact is not found, the db returns None
|
|
3394
|
+
# in real usage, the db should raise an exception
|
|
3395
|
+
if artifact:
|
|
3396
|
+
return dict_to_artifact(artifact)
|
|
3397
|
+
return None
|
|
3337
3398
|
|
|
3338
3399
|
def list_artifacts(
|
|
3339
3400
|
self,
|
|
@@ -3684,6 +3745,18 @@ class MlrunProject(ModelObj):
|
|
|
3684
3745
|
|
|
3685
3746
|
return mlrun.db.get_run_db().get_api_gateway(name=name, project=self.name)
|
|
3686
3747
|
|
|
3748
|
+
def delete_api_gateway(
|
|
3749
|
+
self,
|
|
3750
|
+
name: str,
|
|
3751
|
+
):
|
|
3752
|
+
"""
|
|
3753
|
+
Deletes an API gateway by name.
|
|
3754
|
+
|
|
3755
|
+
:param name: The name of the API gateway to delete.
|
|
3756
|
+
"""
|
|
3757
|
+
|
|
3758
|
+
mlrun.db.get_run_db().delete_api_gateway(name=name, project=self.name)
|
|
3759
|
+
|
|
3687
3760
|
def _run_authenticated_git_action(
|
|
3688
3761
|
self,
|
|
3689
3762
|
action: Callable,
|
|
@@ -3761,6 +3834,83 @@ class MlrunProject(ModelObj):
|
|
|
3761
3834
|
f"<project.spec.get_code_path()>/<{param_name}>)."
|
|
3762
3835
|
)
|
|
3763
3836
|
|
|
3837
|
+
def _resolve_artifact_producer(
|
|
3838
|
+
self,
|
|
3839
|
+
artifact: typing.Union[str, Artifact],
|
|
3840
|
+
project_producer_tag: str = None,
|
|
3841
|
+
) -> typing.Optional[ArtifactProducer]:
|
|
3842
|
+
"""
|
|
3843
|
+
Resolve the artifact producer of the given artifact.
|
|
3844
|
+
If the artifact's producer is a run, the artifact is registered with the original producer.
|
|
3845
|
+
Otherwise, the artifact is registered with the current project as the producer.
|
|
3846
|
+
|
|
3847
|
+
:param artifact: The artifact to resolve its producer.
|
|
3848
|
+
:param project_producer_tag: The tag to use for the project as the producer. If not provided, a tag will be
|
|
3849
|
+
generated for the project.
|
|
3850
|
+
:return: A tuple of the resolved producer and the resolved artifact.
|
|
3851
|
+
"""
|
|
3852
|
+
|
|
3853
|
+
if not isinstance(artifact, str) and artifact.producer:
|
|
3854
|
+
# if the artifact was imported from a yaml file, the producer can be a dict
|
|
3855
|
+
if isinstance(artifact.spec.producer, ArtifactProducer):
|
|
3856
|
+
producer_dict = artifact.spec.producer.get_meta()
|
|
3857
|
+
else:
|
|
3858
|
+
producer_dict = artifact.spec.producer
|
|
3859
|
+
|
|
3860
|
+
if producer_dict.get("kind", "") == "run":
|
|
3861
|
+
return ArtifactProducer(
|
|
3862
|
+
name=producer_dict.get("name", ""),
|
|
3863
|
+
kind=producer_dict.get("kind", ""),
|
|
3864
|
+
project=producer_dict.get("project", ""),
|
|
3865
|
+
tag=producer_dict.get("tag", ""),
|
|
3866
|
+
)
|
|
3867
|
+
|
|
3868
|
+
# do not retain the artifact's producer, replace it with the project as the producer
|
|
3869
|
+
project_producer_tag = project_producer_tag or self._get_project_tag()
|
|
3870
|
+
return ArtifactProducer(
|
|
3871
|
+
kind="project",
|
|
3872
|
+
name=self.metadata.name,
|
|
3873
|
+
project=self.metadata.name,
|
|
3874
|
+
tag=project_producer_tag,
|
|
3875
|
+
)
|
|
3876
|
+
|
|
3877
|
+
def _resolve_existing_artifact(
|
|
3878
|
+
self,
|
|
3879
|
+
item: typing.Union[str, Artifact],
|
|
3880
|
+
tag: str = None,
|
|
3881
|
+
) -> typing.Optional[Artifact]:
|
|
3882
|
+
"""
|
|
3883
|
+
Check if there is and existing artifact with the given item and tag.
|
|
3884
|
+
If there is, return the existing artifact. Otherwise, return None.
|
|
3885
|
+
|
|
3886
|
+
:param item: The item (or key) to check if there is an existing artifact for.
|
|
3887
|
+
:param tag: The tag to check if there is an existing artifact for.
|
|
3888
|
+
:return: The existing artifact if there is one, otherwise None.
|
|
3889
|
+
"""
|
|
3890
|
+
try:
|
|
3891
|
+
if isinstance(item, str):
|
|
3892
|
+
existing_artifact = self.get_artifact(key=item, tag=tag)
|
|
3893
|
+
else:
|
|
3894
|
+
existing_artifact = self.get_artifact(
|
|
3895
|
+
key=item.key,
|
|
3896
|
+
tag=item.tag,
|
|
3897
|
+
iter=item.iter,
|
|
3898
|
+
tree=item.tree,
|
|
3899
|
+
)
|
|
3900
|
+
if existing_artifact is not None:
|
|
3901
|
+
return existing_artifact.from_dict(existing_artifact)
|
|
3902
|
+
except mlrun.errors.MLRunNotFoundError:
|
|
3903
|
+
logger.debug(
|
|
3904
|
+
"No existing artifact was found",
|
|
3905
|
+
key=item if isinstance(item, str) else item.key,
|
|
3906
|
+
tag=tag if isinstance(item, str) else item.tag,
|
|
3907
|
+
tree=None if isinstance(item, str) else item.tree,
|
|
3908
|
+
)
|
|
3909
|
+
return None
|
|
3910
|
+
|
|
3911
|
+
def _get_project_tag(self):
|
|
3912
|
+
return self._get_hexsha() or str(uuid.uuid4())
|
|
3913
|
+
|
|
3764
3914
|
|
|
3765
3915
|
def _set_as_current_default_project(project: MlrunProject):
|
|
3766
3916
|
mlrun.mlconf.default_project = project.metadata.name
|
|
@@ -3846,6 +3996,18 @@ def _init_function_from_dict(
|
|
|
3846
3996
|
tag=tag,
|
|
3847
3997
|
)
|
|
3848
3998
|
|
|
3999
|
+
elif image and kind in mlrun.runtimes.RuntimeKinds.nuclio_runtimes():
|
|
4000
|
+
func = new_function(
|
|
4001
|
+
name,
|
|
4002
|
+
command=relative_url,
|
|
4003
|
+
image=image,
|
|
4004
|
+
kind=kind,
|
|
4005
|
+
handler=handler,
|
|
4006
|
+
tag=tag,
|
|
4007
|
+
)
|
|
4008
|
+
if kind != mlrun.runtimes.RuntimeKinds.application:
|
|
4009
|
+
logger.info("Function code not specified, setting entry point to image")
|
|
4010
|
+
func.from_image(image)
|
|
3849
4011
|
else:
|
|
3850
4012
|
raise ValueError(f"Unsupported function url:handler {url}:{handler} or no spec")
|
|
3851
4013
|
|