mlrun 1.6.4rc2__py3-none-any.whl → 1.7.0rc20__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 +11 -1
- mlrun/__main__.py +26 -112
- mlrun/alerts/__init__.py +15 -0
- mlrun/alerts/alert.py +144 -0
- mlrun/api/schemas/__init__.py +5 -4
- mlrun/artifacts/__init__.py +8 -3
- mlrun/artifacts/base.py +46 -257
- mlrun/artifacts/dataset.py +11 -192
- mlrun/artifacts/manager.py +47 -48
- mlrun/artifacts/model.py +31 -159
- mlrun/artifacts/plots.py +23 -380
- mlrun/common/constants.py +69 -0
- mlrun/common/db/sql_session.py +2 -3
- mlrun/common/formatters/__init__.py +19 -0
- mlrun/common/formatters/artifact.py +21 -0
- mlrun/common/formatters/base.py +78 -0
- mlrun/common/formatters/function.py +41 -0
- mlrun/common/formatters/pipeline.py +53 -0
- mlrun/common/formatters/project.py +51 -0
- mlrun/common/helpers.py +1 -2
- mlrun/common/model_monitoring/helpers.py +9 -5
- mlrun/{runtimes → common/runtimes}/constants.py +37 -9
- mlrun/common/schemas/__init__.py +24 -4
- mlrun/common/schemas/alert.py +203 -0
- mlrun/common/schemas/api_gateway.py +148 -0
- mlrun/common/schemas/artifact.py +18 -8
- mlrun/common/schemas/auth.py +11 -5
- mlrun/common/schemas/background_task.py +1 -1
- mlrun/common/schemas/client_spec.py +4 -1
- mlrun/common/schemas/feature_store.py +16 -16
- mlrun/common/schemas/frontend_spec.py +8 -7
- mlrun/common/schemas/function.py +5 -1
- mlrun/common/schemas/hub.py +11 -18
- mlrun/common/schemas/memory_reports.py +2 -2
- mlrun/common/schemas/model_monitoring/__init__.py +18 -3
- mlrun/common/schemas/model_monitoring/constants.py +83 -26
- mlrun/common/schemas/model_monitoring/grafana.py +13 -9
- mlrun/common/schemas/model_monitoring/model_endpoints.py +99 -16
- mlrun/common/schemas/notification.py +4 -4
- mlrun/common/schemas/object.py +2 -2
- mlrun/{runtimes/mpijob/v1alpha1.py → common/schemas/pagination.py} +10 -13
- mlrun/common/schemas/pipeline.py +1 -10
- mlrun/common/schemas/project.py +24 -23
- mlrun/common/schemas/runtime_resource.py +8 -12
- mlrun/common/schemas/schedule.py +3 -3
- mlrun/common/schemas/tag.py +1 -2
- mlrun/common/schemas/workflow.py +2 -2
- mlrun/common/types.py +7 -1
- mlrun/config.py +54 -17
- mlrun/data_types/to_pandas.py +10 -12
- mlrun/datastore/__init__.py +5 -8
- mlrun/datastore/alibaba_oss.py +130 -0
- mlrun/datastore/azure_blob.py +17 -5
- mlrun/datastore/base.py +62 -39
- mlrun/datastore/datastore.py +28 -9
- mlrun/datastore/datastore_profile.py +146 -20
- mlrun/datastore/filestore.py +0 -1
- mlrun/datastore/google_cloud_storage.py +6 -2
- mlrun/datastore/hdfs.py +56 -0
- mlrun/datastore/inmem.py +2 -2
- mlrun/datastore/redis.py +6 -2
- mlrun/datastore/s3.py +9 -0
- mlrun/datastore/snowflake_utils.py +43 -0
- mlrun/datastore/sources.py +201 -96
- mlrun/datastore/spark_utils.py +1 -2
- mlrun/datastore/store_resources.py +7 -7
- mlrun/datastore/targets.py +358 -104
- mlrun/datastore/utils.py +72 -58
- mlrun/datastore/v3io.py +5 -1
- mlrun/db/base.py +185 -35
- mlrun/db/factory.py +1 -1
- mlrun/db/httpdb.py +614 -179
- mlrun/db/nopdb.py +210 -26
- mlrun/errors.py +12 -1
- mlrun/execution.py +41 -24
- mlrun/feature_store/__init__.py +0 -2
- mlrun/feature_store/api.py +40 -72
- mlrun/feature_store/common.py +1 -1
- mlrun/feature_store/feature_set.py +76 -55
- mlrun/feature_store/feature_vector.py +28 -30
- mlrun/feature_store/ingestion.py +7 -6
- mlrun/feature_store/retrieval/base.py +16 -11
- mlrun/feature_store/retrieval/conversion.py +11 -13
- mlrun/feature_store/retrieval/dask_merger.py +2 -0
- mlrun/feature_store/retrieval/job.py +9 -3
- mlrun/feature_store/retrieval/local_merger.py +2 -0
- mlrun/feature_store/retrieval/spark_merger.py +34 -24
- mlrun/feature_store/steps.py +37 -34
- mlrun/features.py +9 -20
- mlrun/frameworks/_common/artifacts_library.py +9 -9
- mlrun/frameworks/_common/mlrun_interface.py +5 -5
- mlrun/frameworks/_common/model_handler.py +48 -48
- mlrun/frameworks/_common/plan.py +2 -3
- mlrun/frameworks/_common/producer.py +3 -4
- mlrun/frameworks/_common/utils.py +5 -5
- mlrun/frameworks/_dl_common/loggers/logger.py +6 -7
- mlrun/frameworks/_dl_common/loggers/mlrun_logger.py +9 -9
- mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +23 -47
- mlrun/frameworks/_ml_common/artifacts_library.py +1 -2
- mlrun/frameworks/_ml_common/loggers/logger.py +3 -4
- mlrun/frameworks/_ml_common/loggers/mlrun_logger.py +4 -5
- mlrun/frameworks/_ml_common/model_handler.py +24 -24
- mlrun/frameworks/_ml_common/pkl_model_server.py +2 -2
- mlrun/frameworks/_ml_common/plan.py +1 -1
- mlrun/frameworks/_ml_common/plans/calibration_curve_plan.py +2 -3
- mlrun/frameworks/_ml_common/plans/confusion_matrix_plan.py +2 -3
- mlrun/frameworks/_ml_common/plans/dataset_plan.py +3 -3
- mlrun/frameworks/_ml_common/plans/feature_importance_plan.py +3 -3
- mlrun/frameworks/_ml_common/plans/roc_curve_plan.py +4 -4
- mlrun/frameworks/_ml_common/utils.py +4 -4
- mlrun/frameworks/auto_mlrun/auto_mlrun.py +9 -9
- mlrun/frameworks/huggingface/model_server.py +4 -4
- mlrun/frameworks/lgbm/__init__.py +33 -33
- mlrun/frameworks/lgbm/callbacks/callback.py +2 -4
- mlrun/frameworks/lgbm/callbacks/logging_callback.py +4 -5
- mlrun/frameworks/lgbm/callbacks/mlrun_logging_callback.py +4 -5
- mlrun/frameworks/lgbm/mlrun_interfaces/booster_mlrun_interface.py +1 -3
- mlrun/frameworks/lgbm/mlrun_interfaces/mlrun_interface.py +6 -6
- mlrun/frameworks/lgbm/model_handler.py +10 -10
- mlrun/frameworks/lgbm/model_server.py +6 -6
- mlrun/frameworks/lgbm/utils.py +5 -5
- mlrun/frameworks/onnx/dataset.py +8 -8
- mlrun/frameworks/onnx/mlrun_interface.py +3 -3
- mlrun/frameworks/onnx/model_handler.py +6 -6
- mlrun/frameworks/onnx/model_server.py +7 -7
- mlrun/frameworks/parallel_coordinates.py +4 -3
- mlrun/frameworks/pytorch/__init__.py +18 -18
- mlrun/frameworks/pytorch/callbacks/callback.py +4 -5
- mlrun/frameworks/pytorch/callbacks/logging_callback.py +17 -17
- mlrun/frameworks/pytorch/callbacks/mlrun_logging_callback.py +11 -11
- mlrun/frameworks/pytorch/callbacks/tensorboard_logging_callback.py +23 -29
- mlrun/frameworks/pytorch/callbacks_handler.py +38 -38
- mlrun/frameworks/pytorch/mlrun_interface.py +20 -20
- mlrun/frameworks/pytorch/model_handler.py +17 -17
- mlrun/frameworks/pytorch/model_server.py +7 -7
- mlrun/frameworks/sklearn/__init__.py +13 -13
- mlrun/frameworks/sklearn/estimator.py +4 -4
- mlrun/frameworks/sklearn/metrics_library.py +14 -14
- mlrun/frameworks/sklearn/mlrun_interface.py +3 -6
- mlrun/frameworks/sklearn/model_handler.py +2 -2
- mlrun/frameworks/tf_keras/__init__.py +10 -7
- mlrun/frameworks/tf_keras/callbacks/logging_callback.py +15 -15
- mlrun/frameworks/tf_keras/callbacks/mlrun_logging_callback.py +11 -11
- mlrun/frameworks/tf_keras/callbacks/tensorboard_logging_callback.py +19 -23
- mlrun/frameworks/tf_keras/mlrun_interface.py +9 -11
- mlrun/frameworks/tf_keras/model_handler.py +14 -14
- mlrun/frameworks/tf_keras/model_server.py +6 -6
- mlrun/frameworks/xgboost/__init__.py +13 -13
- mlrun/frameworks/xgboost/model_handler.py +6 -6
- mlrun/k8s_utils.py +14 -16
- mlrun/launcher/__init__.py +1 -1
- mlrun/launcher/base.py +16 -15
- mlrun/launcher/client.py +8 -6
- mlrun/launcher/factory.py +1 -1
- mlrun/launcher/local.py +17 -11
- mlrun/launcher/remote.py +16 -10
- mlrun/lists.py +7 -6
- mlrun/model.py +238 -73
- mlrun/model_monitoring/__init__.py +1 -1
- mlrun/model_monitoring/api.py +138 -315
- mlrun/model_monitoring/application.py +5 -296
- mlrun/model_monitoring/applications/__init__.py +24 -0
- mlrun/model_monitoring/applications/_application_steps.py +157 -0
- mlrun/model_monitoring/applications/base.py +282 -0
- mlrun/model_monitoring/applications/context.py +214 -0
- mlrun/model_monitoring/applications/evidently_base.py +211 -0
- mlrun/model_monitoring/applications/histogram_data_drift.py +349 -0
- mlrun/model_monitoring/applications/results.py +99 -0
- mlrun/model_monitoring/controller.py +104 -84
- mlrun/model_monitoring/controller_handler.py +13 -5
- mlrun/model_monitoring/db/__init__.py +18 -0
- mlrun/model_monitoring/{stores → db/stores}/__init__.py +43 -36
- mlrun/model_monitoring/db/stores/base/__init__.py +15 -0
- mlrun/model_monitoring/{stores/model_endpoint_store.py → db/stores/base/store.py} +64 -40
- mlrun/model_monitoring/db/stores/sqldb/__init__.py +13 -0
- mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +71 -0
- mlrun/model_monitoring/{stores → db/stores/sqldb}/models/base.py +109 -5
- mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +88 -0
- mlrun/model_monitoring/{stores/models/mysql.py → db/stores/sqldb/models/sqlite.py} +19 -13
- mlrun/model_monitoring/db/stores/sqldb/sql_store.py +684 -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} +310 -165
- mlrun/model_monitoring/db/tsdb/__init__.py +100 -0
- mlrun/model_monitoring/db/tsdb/base.py +329 -0
- mlrun/model_monitoring/db/tsdb/helpers.py +30 -0
- mlrun/model_monitoring/db/tsdb/tdengine/__init__.py +15 -0
- mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +240 -0
- mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +45 -0
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +397 -0
- mlrun/model_monitoring/db/tsdb/v3io/__init__.py +15 -0
- mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +117 -0
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +630 -0
- mlrun/model_monitoring/evidently_application.py +6 -118
- mlrun/model_monitoring/features_drift_table.py +134 -106
- mlrun/model_monitoring/helpers.py +127 -28
- mlrun/model_monitoring/metrics/__init__.py +13 -0
- mlrun/model_monitoring/metrics/histogram_distance.py +127 -0
- mlrun/model_monitoring/model_endpoint.py +3 -2
- mlrun/model_monitoring/prometheus.py +1 -4
- mlrun/model_monitoring/stream_processing.py +62 -231
- mlrun/model_monitoring/tracking_policy.py +9 -2
- mlrun/model_monitoring/writer.py +152 -124
- mlrun/package/__init__.py +6 -6
- mlrun/package/context_handler.py +5 -5
- mlrun/package/packager.py +7 -7
- mlrun/package/packagers/default_packager.py +6 -6
- mlrun/package/packagers/numpy_packagers.py +15 -15
- mlrun/package/packagers/pandas_packagers.py +5 -5
- mlrun/package/packagers/python_standard_library_packagers.py +10 -10
- mlrun/package/packagers_manager.py +19 -23
- mlrun/package/utils/_formatter.py +6 -6
- mlrun/package/utils/_pickler.py +2 -2
- mlrun/package/utils/_supported_format.py +4 -4
- mlrun/package/utils/log_hint_utils.py +2 -2
- mlrun/package/utils/type_hint_utils.py +4 -9
- mlrun/platforms/__init__.py +11 -10
- mlrun/platforms/iguazio.py +24 -203
- mlrun/projects/operations.py +35 -21
- mlrun/projects/pipelines.py +68 -99
- mlrun/projects/project.py +830 -266
- mlrun/render.py +3 -11
- mlrun/run.py +162 -166
- mlrun/runtimes/__init__.py +62 -7
- mlrun/runtimes/base.py +39 -32
- mlrun/runtimes/daskjob.py +8 -8
- mlrun/runtimes/databricks_job/databricks_cancel_task.py +1 -1
- mlrun/runtimes/databricks_job/databricks_runtime.py +7 -7
- mlrun/runtimes/databricks_job/databricks_wrapper.py +1 -1
- mlrun/runtimes/funcdoc.py +0 -28
- mlrun/runtimes/function_reference.py +1 -1
- mlrun/runtimes/kubejob.py +28 -122
- mlrun/runtimes/local.py +6 -3
- mlrun/runtimes/mpijob/__init__.py +0 -20
- mlrun/runtimes/mpijob/abstract.py +9 -10
- mlrun/runtimes/mpijob/v1.py +1 -1
- mlrun/{model_monitoring/stores/models/sqlite.py → runtimes/nuclio/__init__.py} +7 -9
- mlrun/runtimes/nuclio/api_gateway.py +709 -0
- mlrun/runtimes/nuclio/application/__init__.py +15 -0
- mlrun/runtimes/nuclio/application/application.py +523 -0
- mlrun/runtimes/nuclio/application/reverse_proxy.go +95 -0
- mlrun/runtimes/{function.py → nuclio/function.py} +112 -73
- mlrun/runtimes/{nuclio.py → nuclio/nuclio.py} +6 -6
- mlrun/runtimes/{serving.py → nuclio/serving.py} +45 -51
- mlrun/runtimes/pod.py +286 -88
- mlrun/runtimes/remotesparkjob.py +2 -2
- mlrun/runtimes/sparkjob/spark3job.py +51 -34
- mlrun/runtimes/utils.py +7 -75
- mlrun/secrets.py +9 -5
- mlrun/serving/remote.py +2 -7
- mlrun/serving/routers.py +13 -10
- mlrun/serving/server.py +22 -26
- mlrun/serving/states.py +99 -25
- mlrun/serving/utils.py +3 -3
- mlrun/serving/v1_serving.py +6 -7
- mlrun/serving/v2_serving.py +59 -20
- mlrun/track/tracker.py +2 -1
- mlrun/track/tracker_manager.py +3 -3
- mlrun/track/trackers/mlflow_tracker.py +1 -2
- mlrun/utils/async_http.py +5 -7
- mlrun/utils/azure_vault.py +1 -1
- mlrun/utils/clones.py +1 -2
- mlrun/utils/condition_evaluator.py +3 -3
- mlrun/utils/db.py +3 -3
- mlrun/utils/helpers.py +183 -197
- mlrun/utils/http.py +2 -5
- mlrun/utils/logger.py +76 -14
- mlrun/utils/notifications/notification/__init__.py +17 -12
- mlrun/utils/notifications/notification/base.py +14 -2
- mlrun/utils/notifications/notification/console.py +2 -0
- mlrun/utils/notifications/notification/git.py +3 -1
- mlrun/utils/notifications/notification/ipython.py +3 -1
- mlrun/utils/notifications/notification/slack.py +101 -21
- mlrun/utils/notifications/notification/webhook.py +11 -1
- mlrun/utils/notifications/notification_pusher.py +155 -30
- mlrun/utils/retryer.py +208 -0
- mlrun/utils/singleton.py +1 -1
- mlrun/utils/v3io_clients.py +2 -4
- mlrun/utils/version/version.json +2 -2
- mlrun/utils/version/version.py +2 -6
- {mlrun-1.6.4rc2.dist-info → mlrun-1.7.0rc20.dist-info}/METADATA +31 -19
- mlrun-1.7.0rc20.dist-info/RECORD +353 -0
- mlrun/kfpops.py +0 -868
- mlrun/model_monitoring/batch.py +0 -1095
- mlrun/model_monitoring/stores/models/__init__.py +0 -27
- mlrun/model_monitoring/stores/sql_model_endpoint_store.py +0 -384
- mlrun/platforms/other.py +0 -306
- mlrun-1.6.4rc2.dist-info/RECORD +0 -314
- {mlrun-1.6.4rc2.dist-info → mlrun-1.7.0rc20.dist-info}/LICENSE +0 -0
- {mlrun-1.6.4rc2.dist-info → mlrun-1.7.0rc20.dist-info}/WHEEL +0 -0
- {mlrun-1.6.4rc2.dist-info → mlrun-1.7.0rc20.dist-info}/entry_points.txt +0 -0
- {mlrun-1.6.4rc2.dist-info → mlrun-1.7.0rc20.dist-info}/top_level.txt +0 -0
mlrun/utils/helpers.py
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
+
import asyncio
|
|
15
16
|
import enum
|
|
16
17
|
import functools
|
|
17
18
|
import hashlib
|
|
@@ -22,16 +23,14 @@ import os
|
|
|
22
23
|
import re
|
|
23
24
|
import string
|
|
24
25
|
import sys
|
|
25
|
-
import time
|
|
26
26
|
import typing
|
|
27
27
|
import warnings
|
|
28
28
|
from datetime import datetime, timezone
|
|
29
29
|
from importlib import import_module
|
|
30
30
|
from os import path
|
|
31
31
|
from types import ModuleType
|
|
32
|
-
from typing import Any,
|
|
32
|
+
from typing import Any, Optional
|
|
33
33
|
|
|
34
|
-
import anyio
|
|
35
34
|
import git
|
|
36
35
|
import inflection
|
|
37
36
|
import numpy as np
|
|
@@ -40,7 +39,7 @@ import pandas
|
|
|
40
39
|
import semver
|
|
41
40
|
import yaml
|
|
42
41
|
from dateutil import parser
|
|
43
|
-
from
|
|
42
|
+
from mlrun_pipelines.models import PipelineRun
|
|
44
43
|
from pandas._libs.tslibs.timestamps import Timedelta, Timestamp
|
|
45
44
|
from yaml.representer import RepresenterError
|
|
46
45
|
|
|
@@ -50,10 +49,17 @@ import mlrun.common.schemas
|
|
|
50
49
|
import mlrun.errors
|
|
51
50
|
import mlrun.utils.regex
|
|
52
51
|
import mlrun.utils.version.version
|
|
52
|
+
from mlrun.common.constants import MYSQL_MEDIUMBLOB_SIZE_BYTES
|
|
53
53
|
from mlrun.config import config
|
|
54
|
-
from mlrun.errors import err_to_str
|
|
55
54
|
|
|
56
55
|
from .logger import create_logger
|
|
56
|
+
from .retryer import ( # noqa: F401
|
|
57
|
+
AsyncRetryer,
|
|
58
|
+
Retryer,
|
|
59
|
+
create_exponential_backoff,
|
|
60
|
+
create_linear_backoff,
|
|
61
|
+
create_step_backoff,
|
|
62
|
+
)
|
|
57
63
|
|
|
58
64
|
yaml.Dumper.ignore_aliases = lambda *args: True
|
|
59
65
|
_missing = object()
|
|
@@ -70,19 +76,6 @@ class OverwriteBuildParamsWarning(FutureWarning):
|
|
|
70
76
|
pass
|
|
71
77
|
|
|
72
78
|
|
|
73
|
-
# TODO: remove in 1.7.0
|
|
74
|
-
@deprecated(
|
|
75
|
-
version="1.5.0",
|
|
76
|
-
reason="'parse_versioned_object_uri' will be removed from this file in 1.7.0, use "
|
|
77
|
-
"'mlrun.common.helpers.parse_versioned_object_uri' instead",
|
|
78
|
-
category=FutureWarning,
|
|
79
|
-
)
|
|
80
|
-
def parse_versioned_object_uri(uri: str, default_project: str = ""):
|
|
81
|
-
return mlrun.common.helpers.parse_versioned_object_uri(
|
|
82
|
-
uri=uri, default_project=default_project
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
|
|
86
79
|
class StorePrefix:
|
|
87
80
|
"""map mlrun store objects to prefixes"""
|
|
88
81
|
|
|
@@ -113,14 +106,9 @@ class StorePrefix:
|
|
|
113
106
|
|
|
114
107
|
|
|
115
108
|
def get_artifact_target(item: dict, project=None):
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
tree = item.get("tree")
|
|
120
|
-
else:
|
|
121
|
-
db_key = item["spec"].get("db_key")
|
|
122
|
-
project_str = project or item["metadata"].get("project")
|
|
123
|
-
tree = item["metadata"].get("tree")
|
|
109
|
+
db_key = item["spec"].get("db_key")
|
|
110
|
+
project_str = project or item["metadata"].get("project")
|
|
111
|
+
tree = item["metadata"].get("tree")
|
|
124
112
|
|
|
125
113
|
kind = item.get("kind")
|
|
126
114
|
if kind in ["dataset", "model", "artifact"] and db_key:
|
|
@@ -129,11 +117,15 @@ def get_artifact_target(item: dict, project=None):
|
|
|
129
117
|
target = f"{target}@{tree}"
|
|
130
118
|
return target
|
|
131
119
|
|
|
132
|
-
return (
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
120
|
+
return item["spec"].get("target_path")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# TODO: left for migrations testing purposes. Remove in 1.8.0.
|
|
124
|
+
def is_legacy_artifact(artifact):
|
|
125
|
+
if isinstance(artifact, dict):
|
|
126
|
+
return "metadata" not in artifact
|
|
127
|
+
else:
|
|
128
|
+
return not hasattr(artifact, "metadata")
|
|
137
129
|
|
|
138
130
|
|
|
139
131
|
logger = create_logger(config.log_level, config.log_formatter, "mlrun", sys.stdout)
|
|
@@ -189,8 +181,12 @@ def verify_field_regex(
|
|
|
189
181
|
)
|
|
190
182
|
if mode == mlrun.common.schemas.RegexMatchModes.all:
|
|
191
183
|
if raise_on_failure:
|
|
184
|
+
if len(field_name) > max_chars:
|
|
185
|
+
field_name = field_name[:max_chars] + "...truncated"
|
|
186
|
+
if len(field_value) > max_chars:
|
|
187
|
+
field_value = field_value[:max_chars] + "...truncated"
|
|
192
188
|
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
193
|
-
f"Field '{field_name
|
|
189
|
+
f"Field '{field_name}' is malformed. '{field_value}' "
|
|
194
190
|
f"does not match required pattern: {pattern}"
|
|
195
191
|
)
|
|
196
192
|
return False
|
|
@@ -265,6 +261,17 @@ def validate_artifact_key_name(
|
|
|
265
261
|
)
|
|
266
262
|
|
|
267
263
|
|
|
264
|
+
def validate_inline_artifact_body_size(body: typing.Union[str, bytes, None]) -> None:
|
|
265
|
+
if body and len(body) > MYSQL_MEDIUMBLOB_SIZE_BYTES:
|
|
266
|
+
raise mlrun.errors.MLRunBadRequestError(
|
|
267
|
+
"The body of the artifact exceeds the maximum allowed size. "
|
|
268
|
+
"Avoid embedding the artifact body. "
|
|
269
|
+
"This increases the size of the project yaml file and could affect the project during loading and saving. "
|
|
270
|
+
"More information is available at"
|
|
271
|
+
"https://docs.mlrun.org/en/latest/projects/automate-project-git-source.html#setting-and-registering-the-project-artifacts"
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
|
|
268
275
|
def validate_v3io_stream_consumer_group(
|
|
269
276
|
value: str, raise_on_failure: bool = True
|
|
270
277
|
) -> bool:
|
|
@@ -276,12 +283,12 @@ def validate_v3io_stream_consumer_group(
|
|
|
276
283
|
)
|
|
277
284
|
|
|
278
285
|
|
|
279
|
-
def get_regex_list_as_string(regex_list:
|
|
286
|
+
def get_regex_list_as_string(regex_list: list) -> str:
|
|
280
287
|
"""
|
|
281
288
|
This function is used to combine a list of regex strings into a single regex,
|
|
282
289
|
with and condition between them.
|
|
283
290
|
"""
|
|
284
|
-
return "".join(["(?={regex})"
|
|
291
|
+
return "".join([f"(?={regex})" for regex in regex_list]) + ".*$"
|
|
285
292
|
|
|
286
293
|
|
|
287
294
|
def tag_name_regex_as_string() -> str:
|
|
@@ -420,7 +427,7 @@ class LogBatchWriter:
|
|
|
420
427
|
|
|
421
428
|
def get_in(obj, keys, default=None):
|
|
422
429
|
"""
|
|
423
|
-
>>> get_in({
|
|
430
|
+
>>> get_in({"a": {"b": 1}}, "a.b")
|
|
424
431
|
1
|
|
425
432
|
"""
|
|
426
433
|
if isinstance(keys, str):
|
|
@@ -698,7 +705,7 @@ def generate_artifact_uri(project, key, tag=None, iter=None, tree=None):
|
|
|
698
705
|
return artifact_uri
|
|
699
706
|
|
|
700
707
|
|
|
701
|
-
def extend_hub_uri_if_needed(uri) ->
|
|
708
|
+
def extend_hub_uri_if_needed(uri) -> tuple[str, bool]:
|
|
702
709
|
"""
|
|
703
710
|
Retrieve the full uri of the item's yaml in the hub.
|
|
704
711
|
|
|
@@ -784,34 +791,6 @@ def gen_html_table(header, rows=None):
|
|
|
784
791
|
return style + '<table class="tg">\n' + out + "</table>\n\n"
|
|
785
792
|
|
|
786
793
|
|
|
787
|
-
def new_pipe_metadata(
|
|
788
|
-
artifact_path: str = None,
|
|
789
|
-
cleanup_ttl: int = None,
|
|
790
|
-
op_transformers: typing.List[typing.Callable] = None,
|
|
791
|
-
):
|
|
792
|
-
from kfp.dsl import PipelineConf
|
|
793
|
-
|
|
794
|
-
def _set_artifact_path(task):
|
|
795
|
-
from kubernetes import client as k8s_client
|
|
796
|
-
|
|
797
|
-
task.add_env_variable(
|
|
798
|
-
k8s_client.V1EnvVar(name="MLRUN_ARTIFACT_PATH", value=artifact_path)
|
|
799
|
-
)
|
|
800
|
-
return task
|
|
801
|
-
|
|
802
|
-
conf = PipelineConf()
|
|
803
|
-
cleanup_ttl = cleanup_ttl or int(config.kfp_ttl)
|
|
804
|
-
|
|
805
|
-
if cleanup_ttl:
|
|
806
|
-
conf.set_ttl_seconds_after_finished(cleanup_ttl)
|
|
807
|
-
if artifact_path:
|
|
808
|
-
conf.add_op_transformer(_set_artifact_path)
|
|
809
|
-
if op_transformers:
|
|
810
|
-
for op_transformer in op_transformers:
|
|
811
|
-
conf.add_op_transformer(op_transformer)
|
|
812
|
-
return conf
|
|
813
|
-
|
|
814
|
-
|
|
815
794
|
def _convert_python_package_version_to_image_tag(version: typing.Optional[str]):
|
|
816
795
|
return (
|
|
817
796
|
version.replace("+", "-").replace("0.0.0-", "") if version is not None else None
|
|
@@ -893,7 +872,7 @@ def get_docker_repository_or_default(repository: str) -> str:
|
|
|
893
872
|
return repository
|
|
894
873
|
|
|
895
874
|
|
|
896
|
-
def get_parsed_docker_registry() ->
|
|
875
|
+
def get_parsed_docker_registry() -> tuple[Optional[str], Optional[str]]:
|
|
897
876
|
# according to https://stackoverflow.com/questions/37861791/how-are-docker-image-names-parsed
|
|
898
877
|
docker_registry = config.httpdb.builder.docker_registry or ""
|
|
899
878
|
first_slash_index = docker_registry.find("/")
|
|
@@ -947,65 +926,27 @@ def fill_function_hash(function_dict, tag=""):
|
|
|
947
926
|
return fill_object_hash(function_dict, "hash", tag)
|
|
948
927
|
|
|
949
928
|
|
|
950
|
-
def
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
"""
|
|
954
|
-
x = 0
|
|
955
|
-
comparison = min if coefficient >= 0 else max
|
|
956
|
-
|
|
957
|
-
while True:
|
|
958
|
-
next_value = comparison(base + x * coefficient, stop_value)
|
|
959
|
-
yield next_value
|
|
960
|
-
x += 1
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
def create_step_backoff(steps=None):
|
|
964
|
-
"""
|
|
965
|
-
Create a generator of steps backoff.
|
|
966
|
-
Example: steps = [[2, 5], [20, 10], [120, None]] will produce a generator in which the first 5
|
|
967
|
-
values will be 2, the next 10 values will be 20 and the rest will be 120.
|
|
968
|
-
:param steps: a list of lists [step_value, number_of_iteration_in_this_step]
|
|
969
|
-
"""
|
|
970
|
-
steps = steps if steps is not None else [[2, 10], [10, 10], [120, None]]
|
|
971
|
-
steps = iter(steps)
|
|
972
|
-
|
|
973
|
-
# Get first step
|
|
974
|
-
step = next(steps)
|
|
975
|
-
while True:
|
|
976
|
-
current_step_value, current_step_remain = step
|
|
977
|
-
if current_step_remain == 0:
|
|
978
|
-
# No more in this step, moving on
|
|
979
|
-
step = next(steps)
|
|
980
|
-
elif current_step_remain is None:
|
|
981
|
-
# We are in the last step, staying here forever
|
|
982
|
-
yield current_step_value
|
|
983
|
-
elif current_step_remain > 0:
|
|
984
|
-
# Still more remains in this step, just reduce the remaining number
|
|
985
|
-
step[1] -= 1
|
|
986
|
-
yield current_step_value
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
def create_exponential_backoff(base=2, max_value=120, scale_factor=1):
|
|
929
|
+
def retry_until_successful(
|
|
930
|
+
backoff: int, timeout: int, logger, verbose: bool, _function, *args, **kwargs
|
|
931
|
+
):
|
|
990
932
|
"""
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
:param
|
|
994
|
-
|
|
933
|
+
Runs function with given *args and **kwargs.
|
|
934
|
+
Tries to run it until success or timeout reached (timeout is optional)
|
|
935
|
+
:param backoff: can either be a:
|
|
936
|
+
- number (int / float) that will be used as interval.
|
|
937
|
+
- generator of waiting intervals. (support next())
|
|
938
|
+
:param timeout: pass None if timeout is not wanted, number of seconds if it is
|
|
939
|
+
:param logger: a logger so we can log the failures
|
|
940
|
+
:param verbose: whether to log the failure on each retry
|
|
941
|
+
:param _function: function to run
|
|
942
|
+
:param args: functions args
|
|
943
|
+
:param kwargs: functions kwargs
|
|
944
|
+
:return: function result
|
|
995
945
|
"""
|
|
996
|
-
|
|
997
|
-
while True:
|
|
998
|
-
# This "complex" implementation (unlike the one in linear backoff) is to avoid exponent growing too fast and
|
|
999
|
-
# risking going behind max_int
|
|
1000
|
-
next_value = scale_factor * (base**exponent)
|
|
1001
|
-
if next_value < max_value:
|
|
1002
|
-
exponent += 1
|
|
1003
|
-
yield next_value
|
|
1004
|
-
else:
|
|
1005
|
-
yield max_value
|
|
946
|
+
return Retryer(backoff, timeout, logger, verbose, _function, *args, **kwargs).run()
|
|
1006
947
|
|
|
1007
948
|
|
|
1008
|
-
def
|
|
949
|
+
async def retry_until_successful_async(
|
|
1009
950
|
backoff: int, timeout: int, logger, verbose: bool, _function, *args, **kwargs
|
|
1010
951
|
):
|
|
1011
952
|
"""
|
|
@@ -1022,80 +963,41 @@ def retry_until_successful(
|
|
|
1022
963
|
:param kwargs: functions kwargs
|
|
1023
964
|
:return: function result
|
|
1024
965
|
"""
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
# Check if backoff is just a simple interval
|
|
1029
|
-
if isinstance(backoff, int) or isinstance(backoff, float):
|
|
1030
|
-
backoff = create_linear_backoff(base=backoff, coefficient=0)
|
|
1031
|
-
|
|
1032
|
-
first_interval = next(backoff)
|
|
1033
|
-
if timeout and timeout <= first_interval:
|
|
1034
|
-
logger.warning(
|
|
1035
|
-
f"Timeout ({timeout}) must be higher than backoff ({first_interval})."
|
|
1036
|
-
f" Set timeout to be higher than backoff."
|
|
1037
|
-
)
|
|
1038
|
-
|
|
1039
|
-
# If deadline was not provided or deadline not reached
|
|
1040
|
-
while timeout is None or time.time() < start_time + timeout:
|
|
1041
|
-
next_interval = first_interval or next(backoff)
|
|
1042
|
-
first_interval = None
|
|
1043
|
-
try:
|
|
1044
|
-
result = _function(*args, **kwargs)
|
|
1045
|
-
return result
|
|
1046
|
-
|
|
1047
|
-
except mlrun.errors.MLRunFatalFailureError as exc:
|
|
1048
|
-
raise exc.original_exception
|
|
1049
|
-
except Exception as exc:
|
|
1050
|
-
last_exception = exc
|
|
1051
|
-
|
|
1052
|
-
# If next interval is within allowed time period - wait on interval, abort otherwise
|
|
1053
|
-
if timeout is None or time.time() + next_interval < start_time + timeout:
|
|
1054
|
-
if logger is not None and verbose:
|
|
1055
|
-
logger.debug(
|
|
1056
|
-
f"Operation not yet successful, Retrying in {next_interval} seconds."
|
|
1057
|
-
f" exc: {err_to_str(exc)}"
|
|
1058
|
-
)
|
|
1059
|
-
|
|
1060
|
-
time.sleep(next_interval)
|
|
1061
|
-
else:
|
|
1062
|
-
break
|
|
1063
|
-
|
|
1064
|
-
if logger is not None:
|
|
1065
|
-
logger.warning(
|
|
1066
|
-
f"Operation did not complete on time. last exception: {last_exception}"
|
|
1067
|
-
)
|
|
1068
|
-
|
|
1069
|
-
raise mlrun.errors.MLRunRetryExhaustedError(
|
|
1070
|
-
f"Failed to execute command by the given deadline."
|
|
1071
|
-
f" last_exception: {last_exception},"
|
|
1072
|
-
f" function_name: {_function.__name__},"
|
|
1073
|
-
f" timeout: {timeout}"
|
|
1074
|
-
) from last_exception
|
|
966
|
+
return await AsyncRetryer(
|
|
967
|
+
backoff, timeout, logger, verbose, _function, *args, **kwargs
|
|
968
|
+
).run()
|
|
1075
969
|
|
|
1076
970
|
|
|
1077
971
|
def get_ui_url(project, uid=None):
|
|
1078
972
|
url = ""
|
|
1079
973
|
if mlrun.mlconf.resolve_ui_url():
|
|
1080
|
-
url = "{}/{}/{}/jobs"
|
|
1081
|
-
mlrun.mlconf.resolve_ui_url(), mlrun.mlconf.ui.projects_prefix, project
|
|
1082
|
-
)
|
|
974
|
+
url = f"{mlrun.mlconf.resolve_ui_url()}/{mlrun.mlconf.ui.projects_prefix}/{project}/jobs"
|
|
1083
975
|
if uid:
|
|
1084
976
|
url += f"/monitor/{uid}/overview"
|
|
1085
977
|
return url
|
|
1086
978
|
|
|
1087
979
|
|
|
980
|
+
def get_model_endpoint_url(project, model_name, model_endpoint_id):
|
|
981
|
+
url = ""
|
|
982
|
+
if mlrun.mlconf.resolve_ui_url():
|
|
983
|
+
url = f"{mlrun.mlconf.resolve_ui_url()}/{mlrun.mlconf.ui.projects_prefix}/{project}/models"
|
|
984
|
+
if model_name:
|
|
985
|
+
url += f"/model-endpoints/{model_name}/{model_endpoint_id}/overview"
|
|
986
|
+
return url
|
|
987
|
+
|
|
988
|
+
|
|
1088
989
|
def get_workflow_url(project, id=None):
|
|
1089
990
|
url = ""
|
|
1090
991
|
if mlrun.mlconf.resolve_ui_url():
|
|
1091
|
-
url =
|
|
1092
|
-
mlrun.mlconf.resolve_ui_url()
|
|
992
|
+
url = (
|
|
993
|
+
f"{mlrun.mlconf.resolve_ui_url()}/{mlrun.mlconf.ui.projects_prefix}"
|
|
994
|
+
f"/{project}/jobs/monitor-workflows/workflow/{id}"
|
|
1093
995
|
)
|
|
1094
996
|
return url
|
|
1095
997
|
|
|
1096
998
|
|
|
1097
999
|
def are_strings_in_exception_chain_messages(
|
|
1098
|
-
exception: Exception, strings_list
|
|
1000
|
+
exception: Exception, strings_list: list[str]
|
|
1099
1001
|
) -> bool:
|
|
1100
1002
|
while exception is not None:
|
|
1101
1003
|
if any([string in str(exception) for string in strings_list]):
|
|
@@ -1208,7 +1110,7 @@ def get_function(function, namespace):
|
|
|
1208
1110
|
|
|
1209
1111
|
|
|
1210
1112
|
def get_handler_extended(
|
|
1211
|
-
handler_path: str, context=None, class_args: dict =
|
|
1113
|
+
handler_path: str, context=None, class_args: dict = None, namespaces=None
|
|
1212
1114
|
):
|
|
1213
1115
|
"""get function handler from [class_name::]handler string
|
|
1214
1116
|
|
|
@@ -1218,6 +1120,7 @@ def get_handler_extended(
|
|
|
1218
1120
|
:param namespaces: one or list of namespaces/modules to search the handler in
|
|
1219
1121
|
:return: function handler (callable)
|
|
1220
1122
|
"""
|
|
1123
|
+
class_args = class_args or {}
|
|
1221
1124
|
if "::" not in handler_path:
|
|
1222
1125
|
return get_function(handler_path, namespaces)
|
|
1223
1126
|
|
|
@@ -1275,7 +1178,7 @@ def has_timezone(timestamp):
|
|
|
1275
1178
|
return False
|
|
1276
1179
|
|
|
1277
1180
|
|
|
1278
|
-
def as_list(element: Any) ->
|
|
1181
|
+
def as_list(element: Any) -> list[Any]:
|
|
1279
1182
|
return element if isinstance(element, list) else [element]
|
|
1280
1183
|
|
|
1281
1184
|
|
|
@@ -1294,7 +1197,7 @@ def calculate_dataframe_hash(dataframe: pandas.DataFrame):
|
|
|
1294
1197
|
return hashlib.sha1(pandas.util.hash_pandas_object(dataframe).values).hexdigest()
|
|
1295
1198
|
|
|
1296
1199
|
|
|
1297
|
-
def template_artifact_path(artifact_path, project, run_uid=
|
|
1200
|
+
def template_artifact_path(artifact_path, project, run_uid=None):
|
|
1298
1201
|
"""
|
|
1299
1202
|
Replace {{run.uid}} with the run uid and {{project}} with the project name in the artifact path.
|
|
1300
1203
|
If no run uid is provided, the word `project` will be used instead as it is assumed to be a project
|
|
@@ -1302,6 +1205,7 @@ def template_artifact_path(artifact_path, project, run_uid="project"):
|
|
|
1302
1205
|
"""
|
|
1303
1206
|
if not artifact_path:
|
|
1304
1207
|
return artifact_path
|
|
1208
|
+
run_uid = run_uid or "project"
|
|
1305
1209
|
artifact_path = artifact_path.replace("{{run.uid}}", run_uid)
|
|
1306
1210
|
artifact_path = _fill_project_path_template(artifact_path, project)
|
|
1307
1211
|
return artifact_path
|
|
@@ -1361,13 +1265,6 @@ def str_to_timestamp(time_str: str, now_time: Timestamp = None):
|
|
|
1361
1265
|
return Timestamp(time_str)
|
|
1362
1266
|
|
|
1363
1267
|
|
|
1364
|
-
def is_legacy_artifact(artifact):
|
|
1365
|
-
if isinstance(artifact, dict):
|
|
1366
|
-
return "metadata" not in artifact
|
|
1367
|
-
else:
|
|
1368
|
-
return not hasattr(artifact, "metadata")
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
1268
|
def is_link_artifact(artifact):
|
|
1372
1269
|
if isinstance(artifact, dict):
|
|
1373
1270
|
return (
|
|
@@ -1377,7 +1274,7 @@ def is_link_artifact(artifact):
|
|
|
1377
1274
|
return artifact.kind == mlrun.common.schemas.ArtifactCategories.link.value
|
|
1378
1275
|
|
|
1379
1276
|
|
|
1380
|
-
def format_run(run:
|
|
1277
|
+
def format_run(run: PipelineRun, with_project=False) -> dict:
|
|
1381
1278
|
fields = [
|
|
1382
1279
|
"id",
|
|
1383
1280
|
"name",
|
|
@@ -1414,17 +1311,17 @@ def format_run(run: dict, with_project=False) -> dict:
|
|
|
1414
1311
|
# pipelines are yet to populate the status or workflow has failed
|
|
1415
1312
|
# as observed https://jira.iguazeng.com/browse/ML-5195
|
|
1416
1313
|
# set to unknown to ensure a status is returned
|
|
1417
|
-
if run
|
|
1418
|
-
run["status"] = inflection.titleize(
|
|
1314
|
+
if run.get("status", None) is None:
|
|
1315
|
+
run["status"] = inflection.titleize(
|
|
1316
|
+
mlrun.common.runtimes.constants.RunStates.unknown
|
|
1317
|
+
)
|
|
1419
1318
|
|
|
1420
1319
|
return run
|
|
1421
1320
|
|
|
1422
1321
|
|
|
1423
1322
|
def get_in_artifact(artifact: dict, key, default=None, raise_on_missing=False):
|
|
1424
1323
|
"""artifact can be dict or Artifact object"""
|
|
1425
|
-
if
|
|
1426
|
-
return artifact.get(key, default)
|
|
1427
|
-
elif key == "kind":
|
|
1324
|
+
if key == "kind":
|
|
1428
1325
|
return artifact.get(key, default)
|
|
1429
1326
|
else:
|
|
1430
1327
|
for block in ["metadata", "spec", "status"]:
|
|
@@ -1558,13 +1455,15 @@ def normalize_project_username(username: str):
|
|
|
1558
1455
|
return username
|
|
1559
1456
|
|
|
1560
1457
|
|
|
1561
|
-
# run_in threadpool is taken from fastapi to allow us to run sync functions in a threadpool
|
|
1562
|
-
# without importing fastapi in the client
|
|
1563
1458
|
async def run_in_threadpool(func, *args, **kwargs):
|
|
1459
|
+
"""
|
|
1460
|
+
Run a sync-function in the loop default thread pool executor pool and await its result.
|
|
1461
|
+
Note that this function is not suitable for CPU-bound tasks, as it will block the event loop.
|
|
1462
|
+
"""
|
|
1463
|
+
loop = asyncio.get_running_loop()
|
|
1564
1464
|
if kwargs:
|
|
1565
|
-
# run_sync doesn't accept 'kwargs', so bind them in here
|
|
1566
1465
|
func = functools.partial(func, **kwargs)
|
|
1567
|
-
return await
|
|
1466
|
+
return await loop.run_in_executor(None, func, *args)
|
|
1568
1467
|
|
|
1569
1468
|
|
|
1570
1469
|
def is_explicit_ack_supported(context):
|
|
@@ -1630,7 +1529,7 @@ def is_ecr_url(registry: str) -> bool:
|
|
|
1630
1529
|
return ".ecr." in registry and ".amazonaws.com" in registry
|
|
1631
1530
|
|
|
1632
1531
|
|
|
1633
|
-
def get_local_file_schema() ->
|
|
1532
|
+
def get_local_file_schema() -> list:
|
|
1634
1533
|
# The expression `list(string.ascii_lowercase)` generates a list of lowercase alphabets,
|
|
1635
1534
|
# which corresponds to drive letters in Windows file paths such as `C:/Windows/path`.
|
|
1636
1535
|
return ["file"] + list(string.ascii_lowercase)
|
|
@@ -1642,3 +1541,90 @@ def is_safe_path(base, filepath, is_symlink=False):
|
|
|
1642
1541
|
os.path.abspath(filepath) if not is_symlink else os.path.realpath(filepath)
|
|
1643
1542
|
)
|
|
1644
1543
|
return base == os.path.commonpath((base, resolved_filepath))
|
|
1544
|
+
|
|
1545
|
+
|
|
1546
|
+
def get_serving_spec():
|
|
1547
|
+
data = None
|
|
1548
|
+
|
|
1549
|
+
# we will have the serving spec in either mounted config map
|
|
1550
|
+
# or env depending on the size of the spec and configuration
|
|
1551
|
+
|
|
1552
|
+
try:
|
|
1553
|
+
with open(mlrun.common.constants.MLRUN_SERVING_SPEC_PATH) as f:
|
|
1554
|
+
data = f.read()
|
|
1555
|
+
except FileNotFoundError:
|
|
1556
|
+
pass
|
|
1557
|
+
|
|
1558
|
+
if data is None:
|
|
1559
|
+
data = os.environ.get("SERVING_SPEC_ENV", "")
|
|
1560
|
+
if not data:
|
|
1561
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
1562
|
+
"Failed to find serving spec in env var or config file"
|
|
1563
|
+
)
|
|
1564
|
+
spec = json.loads(data)
|
|
1565
|
+
return spec
|
|
1566
|
+
|
|
1567
|
+
|
|
1568
|
+
def additional_filters_warning(additional_filters, class_name):
|
|
1569
|
+
if additional_filters and any(additional_filters):
|
|
1570
|
+
mlrun.utils.logger.warn(
|
|
1571
|
+
f"additional_filters parameter is not supported in {class_name},"
|
|
1572
|
+
f" parameter has been ignored."
|
|
1573
|
+
)
|
|
1574
|
+
|
|
1575
|
+
|
|
1576
|
+
def validate_component_version_compatibility(
|
|
1577
|
+
component_name: typing.Literal["iguazio", "nuclio"], *min_versions: str
|
|
1578
|
+
):
|
|
1579
|
+
"""
|
|
1580
|
+
:param component_name: Name of the component to validate compatibility for.
|
|
1581
|
+
:param min_versions: Valid minimum version(s) required, assuming no 2 versions has equal major and minor.
|
|
1582
|
+
"""
|
|
1583
|
+
parsed_min_versions = [
|
|
1584
|
+
semver.VersionInfo.parse(min_version) for min_version in min_versions
|
|
1585
|
+
]
|
|
1586
|
+
parsed_current_version = None
|
|
1587
|
+
component_current_version = None
|
|
1588
|
+
try:
|
|
1589
|
+
if component_name == "iguazio":
|
|
1590
|
+
component_current_version = mlrun.mlconf.igz_version
|
|
1591
|
+
parsed_current_version = mlrun.mlconf.get_parsed_igz_version()
|
|
1592
|
+
|
|
1593
|
+
if parsed_current_version:
|
|
1594
|
+
# ignore pre-release and build metadata, as iguazio version always has them, and we only care about the
|
|
1595
|
+
# major, minor, and patch versions
|
|
1596
|
+
parsed_current_version = semver.VersionInfo.parse(
|
|
1597
|
+
f"{parsed_current_version.major}.{parsed_current_version.minor}.{parsed_current_version.patch}"
|
|
1598
|
+
)
|
|
1599
|
+
if component_name == "nuclio":
|
|
1600
|
+
component_current_version = mlrun.mlconf.nuclio_version
|
|
1601
|
+
parsed_current_version = semver.VersionInfo.parse(
|
|
1602
|
+
mlrun.mlconf.nuclio_version
|
|
1603
|
+
)
|
|
1604
|
+
if not parsed_current_version:
|
|
1605
|
+
return True
|
|
1606
|
+
except ValueError:
|
|
1607
|
+
# only log when version is set but invalid
|
|
1608
|
+
if component_current_version:
|
|
1609
|
+
logger.warning(
|
|
1610
|
+
"Unable to parse current version, assuming compatibility",
|
|
1611
|
+
component_name=component_name,
|
|
1612
|
+
current_version=component_current_version,
|
|
1613
|
+
min_versions=min_versions,
|
|
1614
|
+
)
|
|
1615
|
+
return True
|
|
1616
|
+
|
|
1617
|
+
parsed_min_versions.sort(reverse=True)
|
|
1618
|
+
for parsed_min_version in parsed_min_versions:
|
|
1619
|
+
if parsed_current_version < parsed_min_version:
|
|
1620
|
+
return False
|
|
1621
|
+
return True
|
|
1622
|
+
|
|
1623
|
+
|
|
1624
|
+
def format_alert_summary(
|
|
1625
|
+
alert: mlrun.common.schemas.AlertConfig, event_data: mlrun.common.schemas.Event
|
|
1626
|
+
) -> str:
|
|
1627
|
+
result = alert.summary.replace("{{project}}", alert.project)
|
|
1628
|
+
result = result.replace("{{name}}", alert.name)
|
|
1629
|
+
result = result.replace("{{entity}}", event_data.entity.ids[0])
|
|
1630
|
+
return result
|
mlrun/utils/http.py
CHANGED
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
#
|
|
15
15
|
|
|
16
16
|
import time
|
|
17
|
-
import typing
|
|
18
17
|
|
|
19
18
|
import requests
|
|
20
19
|
import requests.adapters
|
|
@@ -123,7 +122,7 @@ class HTTPSessionWithRetry(requests.Session):
|
|
|
123
122
|
|
|
124
123
|
self._logger.warning(
|
|
125
124
|
"Error during request handling, retrying",
|
|
126
|
-
exc=
|
|
125
|
+
exc=err_to_str(exc),
|
|
127
126
|
retry_count=retry_count,
|
|
128
127
|
url=url,
|
|
129
128
|
method=method,
|
|
@@ -202,9 +201,7 @@ class HTTPSessionWithRetry(requests.Session):
|
|
|
202
201
|
def _method_retryable(self, method: str):
|
|
203
202
|
return method in self._retry_methods
|
|
204
203
|
|
|
205
|
-
def _resolve_retry_methods(
|
|
206
|
-
self, retry_on_post: bool = False
|
|
207
|
-
) -> typing.FrozenSet[str]:
|
|
204
|
+
def _resolve_retry_methods(self, retry_on_post: bool = False) -> frozenset[str]:
|
|
208
205
|
methods = urllib3.util.retry.Retry.DEFAULT_ALLOWED_METHODS
|
|
209
206
|
methods = methods.union({"PATCH"})
|
|
210
207
|
if retry_on_post:
|