mlrun 1.3.2rc1__py3-none-any.whl → 1.3.2rc2__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/api/api/deps.py +14 -1
- mlrun/api/api/endpoints/frontend_spec.py +0 -2
- mlrun/api/api/endpoints/functions.py +15 -27
- mlrun/api/api/endpoints/grafana_proxy.py +435 -74
- mlrun/api/api/endpoints/healthz.py +5 -18
- mlrun/api/api/endpoints/model_endpoints.py +33 -37
- mlrun/api/api/utils.py +6 -13
- mlrun/api/crud/__init__.py +14 -16
- mlrun/api/crud/logs.py +5 -7
- mlrun/api/crud/model_monitoring/__init__.py +2 -2
- mlrun/api/crud/model_monitoring/model_endpoint_store.py +847 -0
- mlrun/api/crud/model_monitoring/model_endpoints.py +105 -328
- mlrun/api/crud/pipelines.py +2 -3
- mlrun/api/db/sqldb/models/models_mysql.py +52 -19
- mlrun/api/db/sqldb/models/models_sqlite.py +52 -19
- mlrun/api/db/sqldb/session.py +19 -26
- mlrun/api/schemas/__init__.py +2 -0
- mlrun/api/schemas/constants.py +0 -13
- mlrun/api/schemas/frontend_spec.py +0 -1
- mlrun/api/schemas/model_endpoints.py +38 -195
- mlrun/api/schemas/schedule.py +2 -2
- mlrun/api/utils/clients/log_collector.py +5 -0
- mlrun/builder.py +9 -41
- mlrun/config.py +1 -76
- mlrun/data_types/__init__.py +1 -6
- mlrun/data_types/data_types.py +1 -3
- mlrun/datastore/__init__.py +2 -9
- mlrun/datastore/sources.py +20 -25
- mlrun/datastore/store_resources.py +1 -1
- mlrun/datastore/targets.py +34 -67
- mlrun/datastore/utils.py +4 -26
- mlrun/db/base.py +2 -4
- mlrun/db/filedb.py +5 -13
- mlrun/db/httpdb.py +32 -64
- mlrun/db/sqldb.py +2 -4
- mlrun/errors.py +0 -5
- mlrun/execution.py +0 -2
- mlrun/feature_store/api.py +8 -24
- mlrun/feature_store/feature_set.py +6 -28
- mlrun/feature_store/feature_vector.py +0 -2
- mlrun/feature_store/ingestion.py +11 -8
- mlrun/feature_store/retrieval/base.py +43 -271
- mlrun/feature_store/retrieval/dask_merger.py +153 -55
- mlrun/feature_store/retrieval/job.py +3 -12
- mlrun/feature_store/retrieval/local_merger.py +130 -48
- mlrun/feature_store/retrieval/spark_merger.py +125 -126
- mlrun/features.py +2 -7
- mlrun/model_monitoring/constants.py +6 -48
- mlrun/model_monitoring/helpers.py +35 -118
- mlrun/model_monitoring/model_monitoring_batch.py +260 -293
- mlrun/model_monitoring/stream_processing_fs.py +253 -220
- mlrun/platforms/iguazio.py +0 -33
- mlrun/projects/project.py +72 -34
- mlrun/runtimes/base.py +0 -5
- mlrun/runtimes/daskjob.py +0 -2
- mlrun/runtimes/function.py +3 -29
- mlrun/runtimes/kubejob.py +15 -39
- mlrun/runtimes/local.py +45 -7
- mlrun/runtimes/mpijob/abstract.py +0 -2
- mlrun/runtimes/mpijob/v1.py +0 -2
- mlrun/runtimes/pod.py +0 -2
- mlrun/runtimes/remotesparkjob.py +0 -2
- mlrun/runtimes/serving.py +0 -6
- mlrun/runtimes/sparkjob/abstract.py +2 -39
- mlrun/runtimes/sparkjob/spark3job.py +0 -2
- mlrun/serving/__init__.py +1 -2
- mlrun/serving/routers.py +35 -35
- mlrun/serving/server.py +12 -22
- mlrun/serving/states.py +30 -162
- mlrun/serving/v2_serving.py +10 -13
- mlrun/utils/clones.py +1 -1
- mlrun/utils/model_monitoring.py +96 -122
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.3.2rc1.dist-info → mlrun-1.3.2rc2.dist-info}/METADATA +27 -23
- {mlrun-1.3.2rc1.dist-info → mlrun-1.3.2rc2.dist-info}/RECORD +79 -92
- mlrun/api/crud/model_monitoring/grafana.py +0 -427
- mlrun/datastore/spark_udf.py +0 -40
- mlrun/model_monitoring/__init__.py +0 -44
- mlrun/model_monitoring/common.py +0 -112
- mlrun/model_monitoring/model_endpoint.py +0 -141
- mlrun/model_monitoring/stores/__init__.py +0 -106
- mlrun/model_monitoring/stores/kv_model_endpoint_store.py +0 -448
- mlrun/model_monitoring/stores/model_endpoint_store.py +0 -147
- mlrun/model_monitoring/stores/models/__init__.py +0 -23
- mlrun/model_monitoring/stores/models/base.py +0 -18
- mlrun/model_monitoring/stores/models/mysql.py +0 -100
- mlrun/model_monitoring/stores/models/sqlite.py +0 -98
- mlrun/model_monitoring/stores/sql_model_endpoint_store.py +0 -375
- mlrun/utils/db.py +0 -52
- {mlrun-1.3.2rc1.dist-info → mlrun-1.3.2rc2.dist-info}/LICENSE +0 -0
- {mlrun-1.3.2rc1.dist-info → mlrun-1.3.2rc2.dist-info}/WHEEL +0 -0
- {mlrun-1.3.2rc1.dist-info → mlrun-1.3.2rc2.dist-info}/entry_points.txt +0 -0
- {mlrun-1.3.2rc1.dist-info → mlrun-1.3.2rc2.dist-info}/top_level.txt +0 -0
mlrun/platforms/iguazio.py
CHANGED
|
@@ -403,37 +403,6 @@ class OutputStream:
|
|
|
403
403
|
)
|
|
404
404
|
|
|
405
405
|
|
|
406
|
-
class HTTPOutputStream:
|
|
407
|
-
"""HTTP output source that usually used for CE mode and debugging process"""
|
|
408
|
-
|
|
409
|
-
def __init__(self, stream_path: str):
|
|
410
|
-
self._stream_path = stream_path
|
|
411
|
-
|
|
412
|
-
def push(self, data):
|
|
413
|
-
def dump_record(rec):
|
|
414
|
-
if isinstance(rec, bytes):
|
|
415
|
-
return rec
|
|
416
|
-
|
|
417
|
-
if not isinstance(rec, str):
|
|
418
|
-
rec = dict_to_json(rec)
|
|
419
|
-
|
|
420
|
-
return rec.encode("UTF-8")
|
|
421
|
-
|
|
422
|
-
if not isinstance(data, list):
|
|
423
|
-
data = [data]
|
|
424
|
-
|
|
425
|
-
for record in data:
|
|
426
|
-
|
|
427
|
-
# Convert the new record to the required format
|
|
428
|
-
serialized_record = dump_record(record)
|
|
429
|
-
response = requests.post(self._stream_path, data=serialized_record)
|
|
430
|
-
if not response:
|
|
431
|
-
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
432
|
-
f"API call failed push a new record through {self._stream_path}"
|
|
433
|
-
f"status {response.status_code}: {response.reason}"
|
|
434
|
-
)
|
|
435
|
-
|
|
436
|
-
|
|
437
406
|
class KafkaOutputStream:
|
|
438
407
|
def __init__(
|
|
439
408
|
self,
|
|
@@ -681,8 +650,6 @@ def parse_path(url, suffix="/"):
|
|
|
681
650
|
)
|
|
682
651
|
endpoint = f"{prefix}://{parsed_url.netloc}"
|
|
683
652
|
else:
|
|
684
|
-
# no netloc is mainly when using v3io (v3io:///) and expecting the url to be resolved automatically from env or
|
|
685
|
-
# config
|
|
686
653
|
endpoint = None
|
|
687
654
|
return endpoint, parsed_url.path.strip("/") + suffix
|
|
688
655
|
|
mlrun/projects/project.py
CHANGED
|
@@ -36,7 +36,6 @@ import yaml
|
|
|
36
36
|
import mlrun.api.schemas
|
|
37
37
|
import mlrun.db
|
|
38
38
|
import mlrun.errors
|
|
39
|
-
import mlrun.model_monitoring.constants as model_monitoring_constants
|
|
40
39
|
import mlrun.utils.regex
|
|
41
40
|
from mlrun.runtimes import RuntimeKinds
|
|
42
41
|
|
|
@@ -58,6 +57,7 @@ from ..utils import (
|
|
|
58
57
|
)
|
|
59
58
|
from ..utils.clones import clone_git, clone_tgz, clone_zip, get_repo_url
|
|
60
59
|
from ..utils.helpers import ensure_git_branch, resolve_git_reference_from_source
|
|
60
|
+
from ..utils.model_monitoring import set_project_model_monitoring_credentials
|
|
61
61
|
from ..utils.notifications import CustomNotificationPusher, NotificationTypes
|
|
62
62
|
from .operations import (
|
|
63
63
|
BuildStatus,
|
|
@@ -2105,43 +2105,15 @@ class MlrunProject(ModelObj):
|
|
|
2105
2105
|
mlrun.get_dataitem(filepath).upload(tmp_path)
|
|
2106
2106
|
remove(tmp_path)
|
|
2107
2107
|
|
|
2108
|
-
def set_model_monitoring_credentials(
|
|
2109
|
-
self,
|
|
2110
|
-
access_key: str = None,
|
|
2111
|
-
endpoint_store_connection: str = None,
|
|
2112
|
-
stream_path: str = None,
|
|
2113
|
-
):
|
|
2108
|
+
def set_model_monitoring_credentials(self, access_key: str):
|
|
2114
2109
|
"""Set the credentials that will be used by the project's model monitoring
|
|
2115
2110
|
infrastructure functions.
|
|
2111
|
+
The supplied credentials must have data access
|
|
2116
2112
|
|
|
2117
|
-
:param access_key:
|
|
2118
|
-
:param endpoint_store_connection: Endpoint store connection string
|
|
2119
|
-
:param stream_path: Path to the model monitoring stream
|
|
2113
|
+
:param access_key: Model Monitoring access key for managing user permissions.
|
|
2120
2114
|
"""
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
if access_key:
|
|
2124
|
-
secrets_dict[
|
|
2125
|
-
model_monitoring_constants.ProjectSecretKeys.ACCESS_KEY
|
|
2126
|
-
] = access_key
|
|
2127
|
-
|
|
2128
|
-
if endpoint_store_connection:
|
|
2129
|
-
secrets_dict[
|
|
2130
|
-
model_monitoring_constants.ProjectSecretKeys.ENDPOINT_STORE_CONNECTION
|
|
2131
|
-
] = endpoint_store_connection
|
|
2132
|
-
|
|
2133
|
-
if stream_path:
|
|
2134
|
-
if stream_path.startswith("kafka://") and "?topic" in stream_path:
|
|
2135
|
-
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
2136
|
-
"Custom kafka topic is not allowed"
|
|
2137
|
-
)
|
|
2138
|
-
secrets_dict[
|
|
2139
|
-
model_monitoring_constants.ProjectSecretKeys.STREAM_PATH
|
|
2140
|
-
] = stream_path
|
|
2141
|
-
|
|
2142
|
-
self.set_secrets(
|
|
2143
|
-
secrets=secrets_dict,
|
|
2144
|
-
provider=mlrun.api.schemas.SecretProviderName.kubernetes,
|
|
2115
|
+
set_project_model_monitoring_credentials(
|
|
2116
|
+
access_key=access_key, project=self.metadata.name
|
|
2145
2117
|
)
|
|
2146
2118
|
|
|
2147
2119
|
def run_function(
|
|
@@ -2608,6 +2580,72 @@ def _init_function_from_obj(func, project, name=None):
|
|
|
2608
2580
|
return name or func.metadata.name, func
|
|
2609
2581
|
|
|
2610
2582
|
|
|
2583
|
+
def _init_function_from_dict_legacy(f, project):
|
|
2584
|
+
name = f.get("name", "")
|
|
2585
|
+
url = f.get("url", "")
|
|
2586
|
+
kind = f.get("kind", "")
|
|
2587
|
+
image = f.get("image", None)
|
|
2588
|
+
with_repo = f.get("with_repo", False)
|
|
2589
|
+
|
|
2590
|
+
if with_repo and not project.source:
|
|
2591
|
+
raise ValueError("project source must be specified when cloning context")
|
|
2592
|
+
|
|
2593
|
+
in_context = False
|
|
2594
|
+
if not url and "spec" not in f:
|
|
2595
|
+
raise ValueError("function missing a url or a spec")
|
|
2596
|
+
# We are not using the project method to obtain an absolute path here,
|
|
2597
|
+
# because legacy projects are built differently, and we cannot rely on them to have a spec
|
|
2598
|
+
if url and "://" not in url:
|
|
2599
|
+
if project.context and not url.startswith("/"):
|
|
2600
|
+
url = path.join(project.context, url)
|
|
2601
|
+
in_context = True
|
|
2602
|
+
if not path.isfile(url):
|
|
2603
|
+
raise OSError(f"{url} not found")
|
|
2604
|
+
|
|
2605
|
+
if "spec" in f:
|
|
2606
|
+
func = new_function(name, runtime=f["spec"])
|
|
2607
|
+
elif is_yaml_path(url) or url.startswith("db://") or url.startswith("hub://"):
|
|
2608
|
+
func = import_function(url)
|
|
2609
|
+
if image:
|
|
2610
|
+
func.spec.image = image
|
|
2611
|
+
elif url.endswith(".ipynb"):
|
|
2612
|
+
func = code_to_function(name, filename=url, image=image, kind=kind)
|
|
2613
|
+
elif url.endswith(".py"):
|
|
2614
|
+
if not image:
|
|
2615
|
+
raise ValueError(
|
|
2616
|
+
"image must be provided with py code files, "
|
|
2617
|
+
"use function object for more control/settings"
|
|
2618
|
+
)
|
|
2619
|
+
if in_context and with_repo:
|
|
2620
|
+
func = new_function(name, command=url, image=image, kind=kind or "job")
|
|
2621
|
+
else:
|
|
2622
|
+
func = code_to_function(name, filename=url, image=image, kind=kind or "job")
|
|
2623
|
+
else:
|
|
2624
|
+
raise ValueError(f"unsupported function url {url} or no spec")
|
|
2625
|
+
|
|
2626
|
+
if with_repo:
|
|
2627
|
+
func.spec.build.source = "./"
|
|
2628
|
+
|
|
2629
|
+
return _init_function_from_obj_legacy(func, project, name)
|
|
2630
|
+
|
|
2631
|
+
|
|
2632
|
+
def _init_function_from_obj_legacy(func, project, name=None):
|
|
2633
|
+
build = func.spec.build
|
|
2634
|
+
if project.origin_url:
|
|
2635
|
+
origin = project.origin_url
|
|
2636
|
+
try:
|
|
2637
|
+
if project.repo:
|
|
2638
|
+
origin += "#" + project.repo.head.commit.hexsha
|
|
2639
|
+
except Exception:
|
|
2640
|
+
pass
|
|
2641
|
+
build.code_origin = origin
|
|
2642
|
+
if project.name:
|
|
2643
|
+
func.metadata.project = project.name
|
|
2644
|
+
if project.tag:
|
|
2645
|
+
func.metadata.tag = project.tag
|
|
2646
|
+
return name or func.metadata.name, func
|
|
2647
|
+
|
|
2648
|
+
|
|
2611
2649
|
def _has_module(handler, kind):
|
|
2612
2650
|
if not handler:
|
|
2613
2651
|
return False
|
mlrun/runtimes/base.py
CHANGED
|
@@ -90,7 +90,6 @@ spec_fields = [
|
|
|
90
90
|
"pythonpath",
|
|
91
91
|
"disable_auto_mount",
|
|
92
92
|
"allow_empty_resources",
|
|
93
|
-
"clone_target_dir",
|
|
94
93
|
]
|
|
95
94
|
|
|
96
95
|
|
|
@@ -131,7 +130,6 @@ class FunctionSpec(ModelObj):
|
|
|
131
130
|
default_handler=None,
|
|
132
131
|
pythonpath=None,
|
|
133
132
|
disable_auto_mount=False,
|
|
134
|
-
clone_target_dir=None,
|
|
135
133
|
):
|
|
136
134
|
|
|
137
135
|
self.command = command or ""
|
|
@@ -150,9 +148,6 @@ class FunctionSpec(ModelObj):
|
|
|
150
148
|
self.entry_points = entry_points or {}
|
|
151
149
|
self.disable_auto_mount = disable_auto_mount
|
|
152
150
|
self.allow_empty_resources = None
|
|
153
|
-
# the build.source is cloned/extracted to the specified clone_target_dir
|
|
154
|
-
# if a relative path is specified, it will be enriched with a temp dir path
|
|
155
|
-
self.clone_target_dir = clone_target_dir or ""
|
|
156
151
|
|
|
157
152
|
@property
|
|
158
153
|
def build(self) -> ImageBuilder:
|
mlrun/runtimes/daskjob.py
CHANGED
|
@@ -106,7 +106,6 @@ class DaskSpec(KubeResourceSpec):
|
|
|
106
106
|
tolerations=None,
|
|
107
107
|
preemption_mode=None,
|
|
108
108
|
security_context=None,
|
|
109
|
-
clone_target_dir=None,
|
|
110
109
|
):
|
|
111
110
|
|
|
112
111
|
super().__init__(
|
|
@@ -136,7 +135,6 @@ class DaskSpec(KubeResourceSpec):
|
|
|
136
135
|
tolerations=tolerations,
|
|
137
136
|
preemption_mode=preemption_mode,
|
|
138
137
|
security_context=security_context,
|
|
139
|
-
clone_target_dir=clone_target_dir,
|
|
140
138
|
)
|
|
141
139
|
self.args = args
|
|
142
140
|
|
mlrun/runtimes/function.py
CHANGED
|
@@ -141,8 +141,6 @@ class NuclioSpec(KubeResourceSpec):
|
|
|
141
141
|
"function_handler",
|
|
142
142
|
"nuclio_runtime",
|
|
143
143
|
"base_image_pull",
|
|
144
|
-
"service_type",
|
|
145
|
-
"add_templated_ingress_host_mode",
|
|
146
144
|
]
|
|
147
145
|
|
|
148
146
|
def __init__(
|
|
@@ -181,9 +179,6 @@ class NuclioSpec(KubeResourceSpec):
|
|
|
181
179
|
tolerations=None,
|
|
182
180
|
preemption_mode=None,
|
|
183
181
|
security_context=None,
|
|
184
|
-
service_type=None,
|
|
185
|
-
add_templated_ingress_host_mode=None,
|
|
186
|
-
clone_target_dir=None,
|
|
187
182
|
):
|
|
188
183
|
|
|
189
184
|
super().__init__(
|
|
@@ -213,7 +208,6 @@ class NuclioSpec(KubeResourceSpec):
|
|
|
213
208
|
tolerations=tolerations,
|
|
214
209
|
preemption_mode=preemption_mode,
|
|
215
210
|
security_context=security_context,
|
|
216
|
-
clone_target_dir=clone_target_dir,
|
|
217
211
|
)
|
|
218
212
|
|
|
219
213
|
self.base_spec = base_spec or {}
|
|
@@ -224,8 +218,6 @@ class NuclioSpec(KubeResourceSpec):
|
|
|
224
218
|
self.nuclio_runtime = None
|
|
225
219
|
self.no_cache = no_cache
|
|
226
220
|
self.readiness_timeout = readiness_timeout
|
|
227
|
-
self.service_type = service_type
|
|
228
|
-
self.add_templated_ingress_host_mode = add_templated_ingress_host_mode
|
|
229
221
|
|
|
230
222
|
self.min_replicas = min_replicas or 1
|
|
231
223
|
self.max_replicas = max_replicas or 4
|
|
@@ -702,22 +694,6 @@ class RemoteRuntime(KubeResource):
|
|
|
702
694
|
"""k8s priority class"""
|
|
703
695
|
super().with_priority_class(name)
|
|
704
696
|
|
|
705
|
-
def with_service_type(
|
|
706
|
-
self, service_type: str, add_templated_ingress_host_mode: str = None
|
|
707
|
-
):
|
|
708
|
-
"""
|
|
709
|
-
Enables to control the service type of the pod and the addition of templated ingress host
|
|
710
|
-
|
|
711
|
-
:param service_type: service type (ClusterIP, NodePort), defaults to
|
|
712
|
-
mlrun.mlconf.httpdb.nuclio.service_type
|
|
713
|
-
:param add_templated_ingress_host_mode: add templated ingress host mode (never, always, onClusterIP),
|
|
714
|
-
see mlrun.mlconf.httpdb.nuclio.add_templated_ingress_host_mode
|
|
715
|
-
for the default and more information
|
|
716
|
-
|
|
717
|
-
"""
|
|
718
|
-
self.spec.service_type = service_type
|
|
719
|
-
self.spec.add_templated_ingress_host_mode = add_templated_ingress_host_mode
|
|
720
|
-
|
|
721
697
|
def _get_state(
|
|
722
698
|
self,
|
|
723
699
|
dashboard="",
|
|
@@ -1234,9 +1210,8 @@ def deploy_nuclio_function(
|
|
|
1234
1210
|
# if mode allows it, enrich function http trigger with an ingress
|
|
1235
1211
|
enrich_function_with_ingress(
|
|
1236
1212
|
function_config,
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
function.spec.service_type or mlconf.httpdb.nuclio.default_service_type,
|
|
1213
|
+
mlconf.httpdb.nuclio.add_templated_ingress_host_mode,
|
|
1214
|
+
mlconf.httpdb.nuclio.default_service_type,
|
|
1240
1215
|
)
|
|
1241
1216
|
|
|
1242
1217
|
try:
|
|
@@ -1402,8 +1377,7 @@ def compile_function_config(
|
|
|
1402
1377
|
|
|
1403
1378
|
# In Nuclio >= 1.6.x default serviceType has changed to "ClusterIP".
|
|
1404
1379
|
nuclio_spec.set_config(
|
|
1405
|
-
"spec.serviceType",
|
|
1406
|
-
function.spec.service_type or mlconf.httpdb.nuclio.default_service_type,
|
|
1380
|
+
"spec.serviceType", mlconf.httpdb.nuclio.default_service_type
|
|
1407
1381
|
)
|
|
1408
1382
|
if function.spec.readiness_timeout:
|
|
1409
1383
|
nuclio_spec.set_config(
|
mlrun/runtimes/kubejob.py
CHANGED
|
@@ -58,17 +58,16 @@ class KubejobRuntime(KubeResource):
|
|
|
58
58
|
return False
|
|
59
59
|
|
|
60
60
|
def with_source_archive(
|
|
61
|
-
self, source, workdir=None, handler=None, pull_at_runtime=True
|
|
61
|
+
self, source, workdir=None, handler=None, pull_at_runtime=True
|
|
62
62
|
):
|
|
63
63
|
"""load the code from git/tar/zip archive at runtime or build
|
|
64
64
|
|
|
65
|
-
:param source:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
:param handler:
|
|
69
|
-
:param workdir:
|
|
65
|
+
:param source: valid path to git, zip, or tar file, e.g.
|
|
66
|
+
git://github.com/mlrun/something.git
|
|
67
|
+
http://some/url/file.zip
|
|
68
|
+
:param handler: default function handler
|
|
69
|
+
:param workdir: working dir relative to the archive root or absolute (e.g. './subdir')
|
|
70
70
|
:param pull_at_runtime: load the archive into the container at job runtime vs on build/deploy
|
|
71
|
-
:param target_dir: target dir on runtime pod or repo clone / archive extraction
|
|
72
71
|
"""
|
|
73
72
|
if source.endswith(".zip") and not pull_at_runtime:
|
|
74
73
|
logger.warn(
|
|
@@ -80,9 +79,6 @@ class KubejobRuntime(KubeResource):
|
|
|
80
79
|
self.spec.default_handler = handler
|
|
81
80
|
if workdir:
|
|
82
81
|
self.spec.workdir = workdir
|
|
83
|
-
if target_dir:
|
|
84
|
-
self.spec.clone_target_dir = target_dir
|
|
85
|
-
|
|
86
82
|
self.spec.build.load_source_on_run = pull_at_runtime
|
|
87
83
|
if (
|
|
88
84
|
self.spec.build.base_image
|
|
@@ -90,7 +86,7 @@ class KubejobRuntime(KubeResource):
|
|
|
90
86
|
and pull_at_runtime
|
|
91
87
|
and not self.spec.image
|
|
92
88
|
):
|
|
93
|
-
# if we load source from repo and
|
|
89
|
+
# if we load source from repo and dont need a full build use the base_image as the image
|
|
94
90
|
self.spec.image = self.spec.build.base_image
|
|
95
91
|
elif not pull_at_runtime:
|
|
96
92
|
# clear the image so build will not be skipped
|
|
@@ -227,8 +223,6 @@ class KubejobRuntime(KubeResource):
|
|
|
227
223
|
self.spec.build.base_image = self.spec.build.base_image or get_in(
|
|
228
224
|
data, "data.spec.build.base_image"
|
|
229
225
|
)
|
|
230
|
-
# get the clone target dir in case it was enriched due to loading source
|
|
231
|
-
self.spec.clone_target_dir = get_in(data, "data.spec.clone_target_dir")
|
|
232
226
|
ready = data.get("ready", False)
|
|
233
227
|
if not ready:
|
|
234
228
|
logger.info(
|
|
@@ -350,7 +344,14 @@ class KubejobRuntime(KubeResource):
|
|
|
350
344
|
new_meta = self._get_meta(runobj)
|
|
351
345
|
|
|
352
346
|
self._add_secrets_to_spec_before_running(runobj)
|
|
353
|
-
workdir = self.
|
|
347
|
+
workdir = self.spec.workdir
|
|
348
|
+
if workdir:
|
|
349
|
+
if self.spec.build.source and self.spec.build.load_source_on_run:
|
|
350
|
+
# workdir will be set AFTER the clone
|
|
351
|
+
workdir = None
|
|
352
|
+
elif not workdir.startswith("/"):
|
|
353
|
+
# relative path mapped to real path in the job pod
|
|
354
|
+
workdir = os.path.join("/mlrun", workdir)
|
|
354
355
|
|
|
355
356
|
pod_spec = func_to_pod(
|
|
356
357
|
self.full_image_path(
|
|
@@ -384,31 +385,6 @@ class KubejobRuntime(KubeResource):
|
|
|
384
385
|
|
|
385
386
|
return None
|
|
386
387
|
|
|
387
|
-
def _resolve_workdir(self):
|
|
388
|
-
"""
|
|
389
|
-
The workdir is relative to the source root, if the source is not loaded on run then the workdir
|
|
390
|
-
is relative to the clone target dir (where the source was copied to).
|
|
391
|
-
Otherwise, if the source is loaded on run, the workdir is resolved on the run as well.
|
|
392
|
-
If the workdir is absolute, keep it as is.
|
|
393
|
-
"""
|
|
394
|
-
workdir = self.spec.workdir
|
|
395
|
-
if self.spec.build.source and self.spec.build.load_source_on_run:
|
|
396
|
-
# workdir will be set AFTER the clone which is done in the pre-run of local runtime
|
|
397
|
-
return None
|
|
398
|
-
|
|
399
|
-
if workdir and os.path.isabs(workdir):
|
|
400
|
-
return workdir
|
|
401
|
-
|
|
402
|
-
if self.spec.clone_target_dir:
|
|
403
|
-
workdir = workdir or ""
|
|
404
|
-
if workdir.startswith("./"):
|
|
405
|
-
# TODO: use 'removeprefix' when we drop python 3.7 support
|
|
406
|
-
# workdir.removeprefix("./")
|
|
407
|
-
workdir = workdir[2:]
|
|
408
|
-
return os.path.join(self.spec.clone_target_dir, workdir)
|
|
409
|
-
|
|
410
|
-
return workdir
|
|
411
|
-
|
|
412
388
|
|
|
413
389
|
def func_to_pod(image, runtime, extra_env, command, args, workdir):
|
|
414
390
|
container = client.V1Container(
|
mlrun/runtimes/local.py
CHANGED
|
@@ -41,7 +41,7 @@ from ..execution import MLClientCtx
|
|
|
41
41
|
from ..model import RunObject
|
|
42
42
|
from ..utils import get_handler_extended, get_in, logger, set_paths
|
|
43
43
|
from ..utils.clones import extract_source
|
|
44
|
-
from .base import BaseRuntime
|
|
44
|
+
from .base import BaseRuntime, FunctionSpec, spec_fields
|
|
45
45
|
from .kubejob import KubejobRuntime
|
|
46
46
|
from .remotesparkjob import RemoteSparkRuntime
|
|
47
47
|
from .utils import RunError, global_context, log_std
|
|
@@ -172,10 +172,48 @@ class HandlerRuntime(BaseRuntime, ParallelRunner):
|
|
|
172
172
|
return context.to_dict()
|
|
173
173
|
|
|
174
174
|
|
|
175
|
+
class LocalFunctionSpec(FunctionSpec):
|
|
176
|
+
_dict_fields = spec_fields + ["clone_target_dir"]
|
|
177
|
+
|
|
178
|
+
def __init__(
|
|
179
|
+
self,
|
|
180
|
+
command=None,
|
|
181
|
+
args=None,
|
|
182
|
+
mode=None,
|
|
183
|
+
default_handler=None,
|
|
184
|
+
pythonpath=None,
|
|
185
|
+
entry_points=None,
|
|
186
|
+
description=None,
|
|
187
|
+
workdir=None,
|
|
188
|
+
build=None,
|
|
189
|
+
clone_target_dir=None,
|
|
190
|
+
):
|
|
191
|
+
super().__init__(
|
|
192
|
+
command=command,
|
|
193
|
+
args=args,
|
|
194
|
+
mode=mode,
|
|
195
|
+
build=build,
|
|
196
|
+
entry_points=entry_points,
|
|
197
|
+
description=description,
|
|
198
|
+
workdir=workdir,
|
|
199
|
+
default_handler=default_handler,
|
|
200
|
+
pythonpath=pythonpath,
|
|
201
|
+
)
|
|
202
|
+
self.clone_target_dir = clone_target_dir
|
|
203
|
+
|
|
204
|
+
|
|
175
205
|
class LocalRuntime(BaseRuntime, ParallelRunner):
|
|
176
206
|
kind = "local"
|
|
177
207
|
_is_remote = False
|
|
178
208
|
|
|
209
|
+
@property
|
|
210
|
+
def spec(self) -> LocalFunctionSpec:
|
|
211
|
+
return self._spec
|
|
212
|
+
|
|
213
|
+
@spec.setter
|
|
214
|
+
def spec(self, spec):
|
|
215
|
+
self._spec = self._verify_dict(spec, "spec", LocalFunctionSpec)
|
|
216
|
+
|
|
179
217
|
def to_job(self, image=""):
|
|
180
218
|
struct = self.to_dict()
|
|
181
219
|
obj = KubejobRuntime.from_dict(struct)
|
|
@@ -186,12 +224,12 @@ class LocalRuntime(BaseRuntime, ParallelRunner):
|
|
|
186
224
|
def with_source_archive(self, source, workdir=None, handler=None, target_dir=None):
|
|
187
225
|
"""load the code from git/tar/zip archive at runtime or build
|
|
188
226
|
|
|
189
|
-
:param source:
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
:param handler:
|
|
193
|
-
:param workdir:
|
|
194
|
-
:param target_dir:
|
|
227
|
+
:param source: valid path to git, zip, or tar file, e.g.
|
|
228
|
+
git://github.com/mlrun/something.git
|
|
229
|
+
http://some/url/file.zip
|
|
230
|
+
:param handler: default function handler
|
|
231
|
+
:param workdir: working dir relative to the archive root or absolute (e.g. './subdir')
|
|
232
|
+
:param target_dir: local target dir for repo clone (by default its <current-dir>/code)
|
|
195
233
|
"""
|
|
196
234
|
self.spec.build.source = source
|
|
197
235
|
self.spec.build.load_source_on_run = True
|
|
@@ -60,7 +60,6 @@ class MPIResourceSpec(KubeResourceSpec):
|
|
|
60
60
|
tolerations=None,
|
|
61
61
|
preemption_mode=None,
|
|
62
62
|
security_context=None,
|
|
63
|
-
clone_target_dir=None,
|
|
64
63
|
):
|
|
65
64
|
super().__init__(
|
|
66
65
|
command=command,
|
|
@@ -89,7 +88,6 @@ class MPIResourceSpec(KubeResourceSpec):
|
|
|
89
88
|
tolerations=tolerations,
|
|
90
89
|
preemption_mode=preemption_mode,
|
|
91
90
|
security_context=security_context,
|
|
92
|
-
clone_target_dir=clone_target_dir,
|
|
93
91
|
)
|
|
94
92
|
self.mpi_args = mpi_args or [
|
|
95
93
|
"-x",
|
mlrun/runtimes/mpijob/v1.py
CHANGED
|
@@ -62,7 +62,6 @@ class MPIV1ResourceSpec(MPIResourceSpec):
|
|
|
62
62
|
tolerations=None,
|
|
63
63
|
preemption_mode=None,
|
|
64
64
|
security_context=None,
|
|
65
|
-
clone_target_dir=None,
|
|
66
65
|
):
|
|
67
66
|
super().__init__(
|
|
68
67
|
command=command,
|
|
@@ -92,7 +91,6 @@ class MPIV1ResourceSpec(MPIResourceSpec):
|
|
|
92
91
|
tolerations=tolerations,
|
|
93
92
|
preemption_mode=preemption_mode,
|
|
94
93
|
security_context=security_context,
|
|
95
|
-
clone_target_dir=clone_target_dir,
|
|
96
94
|
)
|
|
97
95
|
self.clean_pod_policy = clean_pod_policy or MPIJobV1CleanPodPolicies.default()
|
|
98
96
|
|
mlrun/runtimes/pod.py
CHANGED
|
@@ -135,7 +135,6 @@ class KubeResourceSpec(FunctionSpec):
|
|
|
135
135
|
tolerations=None,
|
|
136
136
|
preemption_mode=None,
|
|
137
137
|
security_context=None,
|
|
138
|
-
clone_target_dir=None,
|
|
139
138
|
):
|
|
140
139
|
super().__init__(
|
|
141
140
|
command=command,
|
|
@@ -149,7 +148,6 @@ class KubeResourceSpec(FunctionSpec):
|
|
|
149
148
|
default_handler=default_handler,
|
|
150
149
|
pythonpath=pythonpath,
|
|
151
150
|
disable_auto_mount=disable_auto_mount,
|
|
152
|
-
clone_target_dir=clone_target_dir,
|
|
153
151
|
)
|
|
154
152
|
self._volumes = {}
|
|
155
153
|
self._volume_mounts = {}
|
mlrun/runtimes/remotesparkjob.py
CHANGED
|
@@ -58,7 +58,6 @@ class RemoteSparkSpec(KubeResourceSpec):
|
|
|
58
58
|
tolerations=None,
|
|
59
59
|
preemption_mode=None,
|
|
60
60
|
security_context=None,
|
|
61
|
-
clone_target_dir=None,
|
|
62
61
|
):
|
|
63
62
|
super().__init__(
|
|
64
63
|
command=command,
|
|
@@ -87,7 +86,6 @@ class RemoteSparkSpec(KubeResourceSpec):
|
|
|
87
86
|
tolerations=tolerations,
|
|
88
87
|
preemption_mode=preemption_mode,
|
|
89
88
|
security_context=security_context,
|
|
90
|
-
clone_target_dir=clone_target_dir,
|
|
91
89
|
)
|
|
92
90
|
self.provider = provider
|
|
93
91
|
|
mlrun/runtimes/serving.py
CHANGED
|
@@ -141,9 +141,6 @@ class ServingSpec(NuclioSpec):
|
|
|
141
141
|
tolerations=None,
|
|
142
142
|
preemption_mode=None,
|
|
143
143
|
security_context=None,
|
|
144
|
-
service_type=None,
|
|
145
|
-
add_templated_ingress_host_mode=None,
|
|
146
|
-
clone_target_dir=None,
|
|
147
144
|
):
|
|
148
145
|
|
|
149
146
|
super().__init__(
|
|
@@ -181,9 +178,6 @@ class ServingSpec(NuclioSpec):
|
|
|
181
178
|
tolerations=tolerations,
|
|
182
179
|
preemption_mode=preemption_mode,
|
|
183
180
|
security_context=security_context,
|
|
184
|
-
service_type=service_type,
|
|
185
|
-
add_templated_ingress_host_mode=add_templated_ingress_host_mode,
|
|
186
|
-
clone_target_dir=clone_target_dir,
|
|
187
181
|
)
|
|
188
182
|
|
|
189
183
|
self.models = models or {}
|
|
@@ -11,7 +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
15
|
import typing
|
|
16
16
|
from copy import deepcopy
|
|
17
17
|
from datetime import datetime
|
|
@@ -143,7 +143,6 @@ class AbstractSparkJobSpec(KubeResourceSpec):
|
|
|
143
143
|
tolerations=None,
|
|
144
144
|
preemption_mode=None,
|
|
145
145
|
security_context=None,
|
|
146
|
-
clone_target_dir=None,
|
|
147
146
|
):
|
|
148
147
|
|
|
149
148
|
super().__init__(
|
|
@@ -173,7 +172,6 @@ class AbstractSparkJobSpec(KubeResourceSpec):
|
|
|
173
172
|
tolerations=tolerations,
|
|
174
173
|
preemption_mode=preemption_mode,
|
|
175
174
|
security_context=security_context,
|
|
176
|
-
clone_target_dir=clone_target_dir,
|
|
177
175
|
)
|
|
178
176
|
|
|
179
177
|
self._driver_resources = self.enrich_resources_with_default_pod_resources(
|
|
@@ -365,15 +363,6 @@ class AbstractSparkRuntime(KubejobRuntime):
|
|
|
365
363
|
def _get_igz_deps(self):
|
|
366
364
|
raise NotImplementedError()
|
|
367
365
|
|
|
368
|
-
def _pre_run(self, runobj: RunObject, execution: MLClientCtx):
|
|
369
|
-
if self.spec.build.source and self.spec.build.load_source_on_run:
|
|
370
|
-
raise mlrun.errors.MLRunPreconditionFailedError(
|
|
371
|
-
"Sparkjob does not support loading source code on run, "
|
|
372
|
-
"use func.with_source_archive(pull_at_runtime=False)"
|
|
373
|
-
)
|
|
374
|
-
|
|
375
|
-
super()._pre_run(runobj, execution)
|
|
376
|
-
|
|
377
366
|
def _run(self, runobj: RunObject, execution: MLClientCtx):
|
|
378
367
|
self._validate(runobj)
|
|
379
368
|
|
|
@@ -570,11 +559,7 @@ with ctx:
|
|
|
570
559
|
|
|
571
560
|
if self.spec.command:
|
|
572
561
|
if "://" not in self.spec.command:
|
|
573
|
-
|
|
574
|
-
self.spec.command = "local://" + os.path.join(
|
|
575
|
-
workdir or "",
|
|
576
|
-
self.spec.command,
|
|
577
|
-
)
|
|
562
|
+
self.spec.command = "local://" + self.spec.command
|
|
578
563
|
update_in(job, "spec.mainApplicationFile", self.spec.command)
|
|
579
564
|
|
|
580
565
|
verify_list_and_update_in(job, "spec.arguments", self.spec.args or [], str)
|
|
@@ -804,28 +789,6 @@ with ctx:
|
|
|
804
789
|
submission_retry_interval,
|
|
805
790
|
)
|
|
806
791
|
|
|
807
|
-
def with_source_archive(
|
|
808
|
-
self, source, workdir=None, handler=None, pull_at_runtime=True, target_dir=None
|
|
809
|
-
):
|
|
810
|
-
"""load the code from git/tar/zip archive at runtime or build
|
|
811
|
-
|
|
812
|
-
:param source: valid path to git, zip, or tar file, e.g.
|
|
813
|
-
git://github.com/mlrun/something.git
|
|
814
|
-
http://some/url/file.zip
|
|
815
|
-
:param handler: default function handler
|
|
816
|
-
:param workdir: working dir relative to the archive root (e.g. './subdir') or absolute to the image root
|
|
817
|
-
:param pull_at_runtime: not supported for spark runtime, must be False
|
|
818
|
-
:param target_dir: target dir on runtime pod for repo clone / archive extraction
|
|
819
|
-
"""
|
|
820
|
-
if pull_at_runtime:
|
|
821
|
-
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
822
|
-
"pull_at_runtime is not supported for spark runtime, use pull_at_runtime=False"
|
|
823
|
-
)
|
|
824
|
-
|
|
825
|
-
super().with_source_archive(
|
|
826
|
-
source, workdir, handler, pull_at_runtime, target_dir
|
|
827
|
-
)
|
|
828
|
-
|
|
829
792
|
def get_pods(self, name=None, namespace=None, driver=False):
|
|
830
793
|
k8s = self._get_k8s()
|
|
831
794
|
namespace = k8s.resolve_namespace(namespace)
|
|
@@ -100,7 +100,6 @@ class Spark3JobSpec(AbstractSparkJobSpec):
|
|
|
100
100
|
driver_cores=None,
|
|
101
101
|
executor_cores=None,
|
|
102
102
|
security_context=None,
|
|
103
|
-
clone_target_dir=None,
|
|
104
103
|
):
|
|
105
104
|
|
|
106
105
|
super().__init__(
|
|
@@ -130,7 +129,6 @@ class Spark3JobSpec(AbstractSparkJobSpec):
|
|
|
130
129
|
tolerations=tolerations,
|
|
131
130
|
preemption_mode=preemption_mode,
|
|
132
131
|
security_context=security_context,
|
|
133
|
-
clone_target_dir=clone_target_dir,
|
|
134
132
|
)
|
|
135
133
|
|
|
136
134
|
self.driver_resources = driver_resources or {}
|
mlrun/serving/__init__.py
CHANGED
|
@@ -21,11 +21,10 @@ __all__ = [
|
|
|
21
21
|
"TaskStep",
|
|
22
22
|
"RouterStep",
|
|
23
23
|
"QueueStep",
|
|
24
|
-
"ErrorStep",
|
|
25
24
|
]
|
|
26
25
|
|
|
27
26
|
from .routers import ModelRouter, VotingEnsemble # noqa
|
|
28
27
|
from .server import GraphContext, GraphServer, create_graph_server # noqa
|
|
29
|
-
from .states import
|
|
28
|
+
from .states import QueueStep, RouterStep, TaskStep # noqa
|
|
30
29
|
from .v1_serving import MLModelServer, new_v1_model_server # noqa
|
|
31
30
|
from .v2_serving import V2ModelServer # noqa
|