mlrun 1.7.0rc15__py3-none-any.whl → 1.7.0rc17__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 +10 -1
- mlrun/__main__.py +18 -4
- mlrun/alerts/__init__.py +15 -0
- mlrun/alerts/alert.py +144 -0
- mlrun/artifacts/__init__.py +7 -1
- mlrun/artifacts/base.py +28 -3
- mlrun/artifacts/dataset.py +8 -0
- mlrun/artifacts/manager.py +18 -0
- mlrun/artifacts/model.py +8 -1
- mlrun/artifacts/plots.py +13 -0
- mlrun/common/schemas/__init__.py +10 -2
- mlrun/common/schemas/alert.py +64 -5
- mlrun/common/schemas/api_gateway.py +4 -0
- mlrun/common/schemas/artifact.py +15 -0
- mlrun/common/schemas/auth.py +2 -0
- mlrun/common/schemas/model_monitoring/__init__.py +4 -1
- mlrun/common/schemas/model_monitoring/constants.py +17 -1
- mlrun/common/schemas/model_monitoring/model_endpoints.py +60 -1
- mlrun/common/schemas/project.py +5 -1
- mlrun/config.py +11 -4
- mlrun/datastore/datastore_profile.py +10 -7
- mlrun/db/base.py +24 -4
- mlrun/db/httpdb.py +97 -43
- mlrun/db/nopdb.py +25 -4
- mlrun/errors.py +5 -0
- mlrun/launcher/base.py +3 -2
- mlrun/lists.py +4 -0
- mlrun/model.py +15 -8
- mlrun/model_monitoring/__init__.py +1 -1
- mlrun/model_monitoring/applications/_application_steps.py +1 -2
- mlrun/model_monitoring/applications/context.py +1 -1
- mlrun/model_monitoring/applications/histogram_data_drift.py +64 -38
- mlrun/model_monitoring/db/__init__.py +2 -0
- mlrun/model_monitoring/db/stores/base/store.py +9 -36
- mlrun/model_monitoring/db/stores/sqldb/sql_store.py +63 -110
- mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +56 -202
- mlrun/model_monitoring/db/tsdb/__init__.py +71 -0
- mlrun/model_monitoring/db/tsdb/base.py +135 -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 +442 -0
- mlrun/model_monitoring/db/v3io_tsdb_reader.py +134 -0
- mlrun/model_monitoring/stream_processing.py +46 -210
- mlrun/model_monitoring/writer.py +50 -100
- mlrun/platforms/__init__.py +10 -9
- mlrun/platforms/iguazio.py +19 -200
- mlrun/projects/operations.py +11 -7
- mlrun/projects/pipelines.py +13 -76
- mlrun/projects/project.py +62 -17
- mlrun/render.py +9 -3
- mlrun/run.py +5 -38
- mlrun/runtimes/__init__.py +1 -0
- mlrun/runtimes/base.py +3 -3
- mlrun/runtimes/kubejob.py +2 -1
- mlrun/runtimes/nuclio/api_gateway.py +163 -77
- mlrun/runtimes/nuclio/application/application.py +160 -7
- mlrun/runtimes/nuclio/function.py +25 -45
- mlrun/runtimes/pod.py +16 -36
- mlrun/runtimes/remotesparkjob.py +1 -1
- mlrun/runtimes/sparkjob/spark3job.py +1 -1
- mlrun/runtimes/utils.py +0 -38
- mlrun/track/tracker.py +2 -1
- mlrun/utils/helpers.py +51 -31
- mlrun/utils/logger.py +11 -6
- mlrun/utils/notifications/notification/base.py +1 -1
- mlrun/utils/notifications/notification/slack.py +9 -4
- mlrun/utils/notifications/notification/webhook.py +1 -1
- mlrun/utils/notifications/notification_pusher.py +21 -14
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.7.0rc15.dist-info → mlrun-1.7.0rc17.dist-info}/METADATA +4 -3
- {mlrun-1.7.0rc15.dist-info → mlrun-1.7.0rc17.dist-info}/RECORD +75 -69
- mlrun/kfpops.py +0 -860
- mlrun/platforms/other.py +0 -305
- {mlrun-1.7.0rc15.dist-info → mlrun-1.7.0rc17.dist-info}/LICENSE +0 -0
- {mlrun-1.7.0rc15.dist-info → mlrun-1.7.0rc17.dist-info}/WHEEL +0 -0
- {mlrun-1.7.0rc15.dist-info → mlrun-1.7.0rc17.dist-info}/entry_points.txt +0 -0
- {mlrun-1.7.0rc15.dist-info → mlrun-1.7.0rc17.dist-info}/top_level.txt +0 -0
|
@@ -12,13 +12,20 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
import pathlib
|
|
15
|
+
import typing
|
|
15
16
|
|
|
16
17
|
import nuclio
|
|
17
18
|
|
|
19
|
+
import mlrun.common.schemas as schemas
|
|
18
20
|
import mlrun.errors
|
|
19
|
-
from mlrun.common.
|
|
21
|
+
from mlrun.common.runtimes.constants import NuclioIngressAddTemplatedIngressModes
|
|
20
22
|
from mlrun.runtimes import RemoteRuntime
|
|
21
23
|
from mlrun.runtimes.nuclio import min_nuclio_versions
|
|
24
|
+
from mlrun.runtimes.nuclio.api_gateway import (
|
|
25
|
+
APIGateway,
|
|
26
|
+
APIGatewayMetadata,
|
|
27
|
+
APIGatewaySpec,
|
|
28
|
+
)
|
|
22
29
|
from mlrun.runtimes.nuclio.function import NuclioSpec, NuclioStatus
|
|
23
30
|
|
|
24
31
|
|
|
@@ -113,7 +120,10 @@ class ApplicationSpec(NuclioSpec):
|
|
|
113
120
|
state_thresholds=state_thresholds,
|
|
114
121
|
disable_default_http_trigger=disable_default_http_trigger,
|
|
115
122
|
)
|
|
116
|
-
self.internal_application_port =
|
|
123
|
+
self.internal_application_port = (
|
|
124
|
+
internal_application_port
|
|
125
|
+
or mlrun.mlconf.function.application.default_sidecar_internal_port
|
|
126
|
+
)
|
|
117
127
|
|
|
118
128
|
@property
|
|
119
129
|
def internal_application_port(self):
|
|
@@ -139,6 +149,9 @@ class ApplicationStatus(NuclioStatus):
|
|
|
139
149
|
container_image=None,
|
|
140
150
|
application_image=None,
|
|
141
151
|
sidecar_name=None,
|
|
152
|
+
api_gateway_name=None,
|
|
153
|
+
api_gateway=None,
|
|
154
|
+
url=None,
|
|
142
155
|
):
|
|
143
156
|
super().__init__(
|
|
144
157
|
state=state,
|
|
@@ -151,6 +164,9 @@ class ApplicationStatus(NuclioStatus):
|
|
|
151
164
|
)
|
|
152
165
|
self.application_image = application_image or None
|
|
153
166
|
self.sidecar_name = sidecar_name or None
|
|
167
|
+
self.api_gateway_name = api_gateway_name or None
|
|
168
|
+
self.api_gateway = api_gateway or None
|
|
169
|
+
self.url = url or None
|
|
154
170
|
|
|
155
171
|
|
|
156
172
|
class ApplicationRuntime(RemoteRuntime):
|
|
@@ -176,6 +192,24 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
176
192
|
def status(self, status):
|
|
177
193
|
self._status = self._verify_dict(status, "status", ApplicationStatus)
|
|
178
194
|
|
|
195
|
+
@property
|
|
196
|
+
def api_gateway(self):
|
|
197
|
+
return self.status.api_gateway
|
|
198
|
+
|
|
199
|
+
@api_gateway.setter
|
|
200
|
+
def api_gateway(self, api_gateway: APIGateway):
|
|
201
|
+
self.status.api_gateway = api_gateway
|
|
202
|
+
|
|
203
|
+
@property
|
|
204
|
+
def url(self):
|
|
205
|
+
if not self.status.api_gateway:
|
|
206
|
+
self._sync_api_gateway()
|
|
207
|
+
return self.status.api_gateway.invoke_url
|
|
208
|
+
|
|
209
|
+
@url.setter
|
|
210
|
+
def url(self, url):
|
|
211
|
+
self.status.url = url
|
|
212
|
+
|
|
179
213
|
def set_internal_application_port(self, port: int):
|
|
180
214
|
self.spec.internal_application_port = port
|
|
181
215
|
|
|
@@ -220,7 +254,7 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
220
254
|
project="",
|
|
221
255
|
tag="",
|
|
222
256
|
verbose=False,
|
|
223
|
-
auth_info: AuthInfo = None,
|
|
257
|
+
auth_info: schemas.AuthInfo = None,
|
|
224
258
|
builder_env: dict = None,
|
|
225
259
|
force_build: bool = False,
|
|
226
260
|
with_mlrun=None,
|
|
@@ -228,6 +262,10 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
228
262
|
is_kfp=False,
|
|
229
263
|
mlrun_version_specifier=None,
|
|
230
264
|
show_on_failure: bool = False,
|
|
265
|
+
skip_access_key_auth: bool = False,
|
|
266
|
+
direct_port_access: bool = False,
|
|
267
|
+
authentication_mode: schemas.APIGatewayAuthenticationMode = None,
|
|
268
|
+
authentication_creds: tuple[str] = None,
|
|
231
269
|
):
|
|
232
270
|
"""
|
|
233
271
|
Deploy function, builds the application image if required (self.requires_build()) or force_build is True,
|
|
@@ -244,6 +282,10 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
244
282
|
:param is_kfp: Deploy as part of a kfp pipeline
|
|
245
283
|
:param mlrun_version_specifier: Which mlrun package version to include (if not current)
|
|
246
284
|
:param show_on_failure: Show logs only in case of build failure
|
|
285
|
+
:param skip_access_key_auth: Skip adding access key auth to the API Gateway
|
|
286
|
+
:param direct_port_access: Set True to allow direct port access to the application sidecar
|
|
287
|
+
:param authentication_mode: API Gateway authentication mode
|
|
288
|
+
:param authentication_creds: API Gateway authentication credentials as a tuple (username, password)
|
|
247
289
|
:return: True if the function is ready (deployed)
|
|
248
290
|
"""
|
|
249
291
|
if self.requires_build() or force_build:
|
|
@@ -261,6 +303,16 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
261
303
|
|
|
262
304
|
self._ensure_reverse_proxy_configurations()
|
|
263
305
|
self._configure_application_sidecar()
|
|
306
|
+
|
|
307
|
+
# we only allow accessing the application via the API Gateway
|
|
308
|
+
name_tag = tag or self.metadata.tag
|
|
309
|
+
self.status.api_gateway_name = (
|
|
310
|
+
f"{self.metadata.name}-{name_tag}" if name_tag else self.metadata.name
|
|
311
|
+
)
|
|
312
|
+
self.spec.add_templated_ingress_host_mode = (
|
|
313
|
+
NuclioIngressAddTemplatedIngressModes.never
|
|
314
|
+
)
|
|
315
|
+
|
|
264
316
|
super().deploy(
|
|
265
317
|
project,
|
|
266
318
|
tag,
|
|
@@ -269,6 +321,14 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
269
321
|
builder_env,
|
|
270
322
|
)
|
|
271
323
|
|
|
324
|
+
ports = self.spec.internal_application_port if direct_port_access else []
|
|
325
|
+
self.create_api_gateway(
|
|
326
|
+
name=self.status.api_gateway_name,
|
|
327
|
+
ports=ports,
|
|
328
|
+
authentication_mode=authentication_mode,
|
|
329
|
+
authentication_creds=authentication_creds,
|
|
330
|
+
)
|
|
331
|
+
|
|
272
332
|
def with_source_archive(
|
|
273
333
|
self, source, workdir=None, pull_at_runtime=True, target_dir=None
|
|
274
334
|
):
|
|
@@ -290,6 +350,92 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
290
350
|
target_dir=target_dir,
|
|
291
351
|
)
|
|
292
352
|
|
|
353
|
+
@classmethod
|
|
354
|
+
def get_filename_and_handler(cls) -> (str, str):
|
|
355
|
+
reverse_proxy_file_path = pathlib.Path(__file__).parent / "reverse_proxy.go"
|
|
356
|
+
return str(reverse_proxy_file_path), "Handler"
|
|
357
|
+
|
|
358
|
+
def create_api_gateway(
|
|
359
|
+
self,
|
|
360
|
+
name: str = None,
|
|
361
|
+
path: str = None,
|
|
362
|
+
ports: list[int] = None,
|
|
363
|
+
authentication_mode: schemas.APIGatewayAuthenticationMode = None,
|
|
364
|
+
authentication_creds: tuple[str] = None,
|
|
365
|
+
):
|
|
366
|
+
api_gateway = APIGateway(
|
|
367
|
+
APIGatewayMetadata(
|
|
368
|
+
name=name,
|
|
369
|
+
namespace=self.metadata.namespace,
|
|
370
|
+
labels=self.metadata.labels,
|
|
371
|
+
annotations=self.metadata.annotations,
|
|
372
|
+
),
|
|
373
|
+
APIGatewaySpec(
|
|
374
|
+
functions=[self],
|
|
375
|
+
project=self.metadata.project,
|
|
376
|
+
path=path,
|
|
377
|
+
ports=mlrun.utils.helpers.as_list(ports) if ports else None,
|
|
378
|
+
),
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
authentication_mode = (
|
|
382
|
+
authentication_mode
|
|
383
|
+
or mlrun.mlconf.function.application.default_authentication_mode
|
|
384
|
+
)
|
|
385
|
+
if authentication_mode == schemas.APIGatewayAuthenticationMode.access_key:
|
|
386
|
+
api_gateway.with_access_key_auth()
|
|
387
|
+
elif authentication_mode == schemas.APIGatewayAuthenticationMode.basic:
|
|
388
|
+
api_gateway.with_basic_auth(*authentication_creds)
|
|
389
|
+
|
|
390
|
+
db = mlrun.get_run_db()
|
|
391
|
+
api_gateway_scheme = db.store_api_gateway(
|
|
392
|
+
api_gateway=api_gateway.to_scheme(), project=self.metadata.project
|
|
393
|
+
)
|
|
394
|
+
if not self.status.api_gateway_name:
|
|
395
|
+
self.status.api_gateway_name = api_gateway_scheme.metadata.name
|
|
396
|
+
self.status.api_gateway = APIGateway.from_scheme(api_gateway_scheme)
|
|
397
|
+
self.status.api_gateway.wait_for_readiness()
|
|
398
|
+
self.url = self.status.api_gateway.invoke_url
|
|
399
|
+
|
|
400
|
+
def invoke(
|
|
401
|
+
self,
|
|
402
|
+
path: str,
|
|
403
|
+
body: typing.Union[str, bytes, dict] = None,
|
|
404
|
+
method: str = None,
|
|
405
|
+
headers: dict = None,
|
|
406
|
+
dashboard: str = "",
|
|
407
|
+
force_external_address: bool = False,
|
|
408
|
+
auth_info: schemas.AuthInfo = None,
|
|
409
|
+
mock: bool = None,
|
|
410
|
+
**http_client_kwargs,
|
|
411
|
+
):
|
|
412
|
+
self._sync_api_gateway()
|
|
413
|
+
# If the API Gateway is not ready or not set, try to invoke the function directly (without the API Gateway)
|
|
414
|
+
if not self.status.api_gateway:
|
|
415
|
+
super().invoke(
|
|
416
|
+
path,
|
|
417
|
+
body,
|
|
418
|
+
method,
|
|
419
|
+
headers,
|
|
420
|
+
dashboard,
|
|
421
|
+
force_external_address,
|
|
422
|
+
auth_info,
|
|
423
|
+
mock,
|
|
424
|
+
**http_client_kwargs,
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
credentials = (auth_info.username, auth_info.password) if auth_info else None
|
|
428
|
+
|
|
429
|
+
if not method:
|
|
430
|
+
method = "POST" if body else "GET"
|
|
431
|
+
return self.status.api_gateway.invoke(
|
|
432
|
+
method=method,
|
|
433
|
+
headers=headers,
|
|
434
|
+
credentials=credentials,
|
|
435
|
+
path=path,
|
|
436
|
+
**http_client_kwargs,
|
|
437
|
+
)
|
|
438
|
+
|
|
293
439
|
def _build_application_image(
|
|
294
440
|
self,
|
|
295
441
|
builder_env: dict = None,
|
|
@@ -355,7 +501,14 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
355
501
|
self.set_env("SIDECAR_PORT", self.spec.internal_application_port)
|
|
356
502
|
self.set_env("SIDECAR_HOST", "http://localhost")
|
|
357
503
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
504
|
+
def _sync_api_gateway(self):
|
|
505
|
+
if not self.status.api_gateway_name:
|
|
506
|
+
return
|
|
507
|
+
|
|
508
|
+
db = mlrun.get_run_db()
|
|
509
|
+
api_gateway_scheme = db.get_api_gateway(
|
|
510
|
+
name=self.status.api_gateway_name, project=self.metadata.project
|
|
511
|
+
)
|
|
512
|
+
self.status.api_gateway = APIGateway.from_scheme(api_gateway_scheme)
|
|
513
|
+
self.status.api_gateway.wait_for_readiness()
|
|
514
|
+
self.url = self.status.api_gateway.invoke_url
|
|
@@ -22,9 +22,11 @@ from time import sleep
|
|
|
22
22
|
import nuclio
|
|
23
23
|
import nuclio.utils
|
|
24
24
|
import requests
|
|
25
|
-
import semver
|
|
26
25
|
from aiohttp.client import ClientSession
|
|
27
26
|
from kubernetes import client
|
|
27
|
+
from mlrun_pipelines.common.mounts import VolumeMount
|
|
28
|
+
from mlrun_pipelines.common.ops import deploy_op
|
|
29
|
+
from mlrun_pipelines.mounts import mount_v3io, v3io_cred
|
|
28
30
|
from nuclio.deploy import find_dashboard_url, get_deploy_status
|
|
29
31
|
from nuclio.triggers import V3IOStreamTrigger
|
|
30
32
|
|
|
@@ -36,15 +38,11 @@ import mlrun.utils.helpers
|
|
|
36
38
|
from mlrun.common.schemas import AuthInfo
|
|
37
39
|
from mlrun.config import config as mlconf
|
|
38
40
|
from mlrun.errors import err_to_str
|
|
39
|
-
from mlrun.kfpops import deploy_op
|
|
40
41
|
from mlrun.lists import RunList
|
|
41
42
|
from mlrun.model import RunObject
|
|
42
43
|
from mlrun.platforms.iguazio import (
|
|
43
|
-
VolumeMount,
|
|
44
|
-
mount_v3io,
|
|
45
44
|
parse_path,
|
|
46
45
|
split_path,
|
|
47
|
-
v3io_cred,
|
|
48
46
|
)
|
|
49
47
|
from mlrun.runtimes.base import FunctionStatus, RunError
|
|
50
48
|
from mlrun.runtimes.pod import KubeResource, KubeResourceSpec
|
|
@@ -56,33 +54,9 @@ def validate_nuclio_version_compatibility(*min_versions):
|
|
|
56
54
|
"""
|
|
57
55
|
:param min_versions: Valid minimum version(s) required, assuming no 2 versions has equal major and minor.
|
|
58
56
|
"""
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
try:
|
|
63
|
-
parsed_current_version = semver.VersionInfo.parse(mlconf.nuclio_version)
|
|
64
|
-
except ValueError:
|
|
65
|
-
# only log when version is set but invalid
|
|
66
|
-
if mlconf.nuclio_version:
|
|
67
|
-
logger.warning(
|
|
68
|
-
"Unable to parse nuclio version, assuming compatibility",
|
|
69
|
-
nuclio_version=mlconf.nuclio_version,
|
|
70
|
-
min_versions=min_versions,
|
|
71
|
-
)
|
|
72
|
-
return True
|
|
73
|
-
|
|
74
|
-
parsed_min_versions.sort(reverse=True)
|
|
75
|
-
for parsed_min_version in parsed_min_versions:
|
|
76
|
-
if (
|
|
77
|
-
parsed_current_version.major == parsed_min_version.major
|
|
78
|
-
and parsed_current_version.minor == parsed_min_version.minor
|
|
79
|
-
and parsed_current_version.patch < parsed_min_version.patch
|
|
80
|
-
):
|
|
81
|
-
return False
|
|
82
|
-
|
|
83
|
-
if parsed_current_version >= parsed_min_version:
|
|
84
|
-
return True
|
|
85
|
-
return False
|
|
57
|
+
return mlrun.utils.helpers.validate_component_version_compatibility(
|
|
58
|
+
"nuclio", *min_versions
|
|
59
|
+
)
|
|
86
60
|
|
|
87
61
|
|
|
88
62
|
def min_nuclio_versions(*versions):
|
|
@@ -573,7 +547,6 @@ class RemoteRuntime(KubeResource):
|
|
|
573
547
|
if tag:
|
|
574
548
|
self.metadata.tag = tag
|
|
575
549
|
|
|
576
|
-
save_record = False
|
|
577
550
|
# Attempt auto-mounting, before sending to remote build
|
|
578
551
|
self.try_auto_mount_based_on_config()
|
|
579
552
|
self._fill_credentials()
|
|
@@ -591,15 +564,18 @@ class RemoteRuntime(KubeResource):
|
|
|
591
564
|
# now, functions can be not exposed (using service type ClusterIP) and hence
|
|
592
565
|
# for BC we first try to populate the external invocation url, and then
|
|
593
566
|
# if not exists, take the internal invocation url
|
|
594
|
-
if
|
|
567
|
+
if (
|
|
568
|
+
self.status.external_invocation_urls
|
|
569
|
+
and self.status.external_invocation_urls[0] != ""
|
|
570
|
+
):
|
|
595
571
|
self.spec.command = f"http://{self.status.external_invocation_urls[0]}"
|
|
596
|
-
|
|
597
|
-
|
|
572
|
+
elif (
|
|
573
|
+
self.status.internal_invocation_urls
|
|
574
|
+
and self.status.internal_invocation_urls[0] != ""
|
|
575
|
+
):
|
|
598
576
|
self.spec.command = f"http://{self.status.internal_invocation_urls[0]}"
|
|
599
|
-
|
|
600
|
-
elif self.status.address:
|
|
577
|
+
elif self.status.address and self.status.address != "":
|
|
601
578
|
self.spec.command = f"http://{self.status.address}"
|
|
602
|
-
save_record = True
|
|
603
579
|
|
|
604
580
|
logger.info(
|
|
605
581
|
"Successfully deployed function",
|
|
@@ -607,8 +583,7 @@ class RemoteRuntime(KubeResource):
|
|
|
607
583
|
external_invocation_urls=self.status.external_invocation_urls,
|
|
608
584
|
)
|
|
609
585
|
|
|
610
|
-
|
|
611
|
-
self.save(versioned=False)
|
|
586
|
+
self.save(versioned=False)
|
|
612
587
|
|
|
613
588
|
return self.spec.command
|
|
614
589
|
|
|
@@ -992,19 +967,24 @@ class RemoteRuntime(KubeResource):
|
|
|
992
967
|
sidecar["image"] = image
|
|
993
968
|
|
|
994
969
|
ports = mlrun.utils.helpers.as_list(ports)
|
|
970
|
+
# according to RFC-6335, port name should be less than 15 characters,
|
|
971
|
+
# so we truncate it if needed and leave room for the index
|
|
972
|
+
port_name = name[:13].rstrip("-_") if len(name) > 13 else name
|
|
995
973
|
sidecar["ports"] = [
|
|
996
974
|
{
|
|
997
|
-
"name": "
|
|
975
|
+
"name": f"{port_name}-{i}",
|
|
998
976
|
"containerPort": port,
|
|
999
977
|
"protocol": "TCP",
|
|
1000
978
|
}
|
|
1001
|
-
for port in ports
|
|
979
|
+
for i, port in enumerate(ports)
|
|
1002
980
|
]
|
|
1003
981
|
|
|
1004
|
-
if command
|
|
982
|
+
# if it is a redeploy, 'command' might be set with the previous invocation url.
|
|
983
|
+
# in this case, we don't want to use it as the sidecar command
|
|
984
|
+
if command and not command.startswith("http"):
|
|
1005
985
|
sidecar["command"] = mlrun.utils.helpers.as_list(command)
|
|
1006
986
|
|
|
1007
|
-
if args:
|
|
987
|
+
if args and sidecar["command"]:
|
|
1008
988
|
sidecar["args"] = mlrun.utils.helpers.as_list(args)
|
|
1009
989
|
|
|
1010
990
|
def _set_sidecar(self, name: str) -> dict:
|
mlrun/runtimes/pod.py
CHANGED
|
@@ -20,8 +20,9 @@ import typing
|
|
|
20
20
|
from enum import Enum
|
|
21
21
|
|
|
22
22
|
import dotenv
|
|
23
|
-
import kfp.dsl
|
|
24
23
|
import kubernetes.client as k8s_client
|
|
24
|
+
import mlrun_pipelines.mounts
|
|
25
|
+
from mlrun_pipelines.mixins import KfpAdapterMixin
|
|
25
26
|
|
|
26
27
|
import mlrun.errors
|
|
27
28
|
import mlrun.utils.regex
|
|
@@ -41,7 +42,6 @@ from ..k8s_utils import (
|
|
|
41
42
|
from ..utils import logger, update_in
|
|
42
43
|
from .base import BaseRuntime, FunctionSpec, spec_fields
|
|
43
44
|
from .utils import (
|
|
44
|
-
apply_kfp,
|
|
45
45
|
get_gpu_from_resource_requirement,
|
|
46
46
|
get_item_name,
|
|
47
47
|
set_named_item,
|
|
@@ -935,12 +935,12 @@ class AutoMountType(str, Enum):
|
|
|
935
935
|
@classmethod
|
|
936
936
|
def all_mount_modifiers(cls):
|
|
937
937
|
return [
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
938
|
+
mlrun_pipelines.mounts.v3io_cred.__name__,
|
|
939
|
+
mlrun_pipelines.mounts.mount_v3io.__name__,
|
|
940
|
+
mlrun_pipelines.mounts.mount_pvc.__name__,
|
|
941
|
+
mlrun_pipelines.mounts.auto_mount.__name__,
|
|
942
|
+
mlrun_pipelines.mounts.mount_s3.__name__,
|
|
943
|
+
mlrun_pipelines.mounts.set_env_variables.__name__,
|
|
944
944
|
]
|
|
945
945
|
|
|
946
946
|
@classmethod
|
|
@@ -957,27 +957,27 @@ class AutoMountType(str, Enum):
|
|
|
957
957
|
def _get_auto_modifier():
|
|
958
958
|
# If we're running on Iguazio - use v3io_cred
|
|
959
959
|
if mlconf.igz_version != "":
|
|
960
|
-
return
|
|
960
|
+
return mlrun_pipelines.mounts.v3io_cred
|
|
961
961
|
# Else, either pvc mount if it's configured or do nothing otherwise
|
|
962
962
|
pvc_configured = (
|
|
963
963
|
"MLRUN_PVC_MOUNT" in os.environ
|
|
964
964
|
or "pvc_name" in mlconf.get_storage_auto_mount_params()
|
|
965
965
|
)
|
|
966
|
-
return
|
|
966
|
+
return mlrun_pipelines.mounts.mount_pvc if pvc_configured else None
|
|
967
967
|
|
|
968
968
|
def get_modifier(self):
|
|
969
969
|
return {
|
|
970
970
|
AutoMountType.none: None,
|
|
971
|
-
AutoMountType.v3io_credentials:
|
|
972
|
-
AutoMountType.v3io_fuse:
|
|
973
|
-
AutoMountType.pvc:
|
|
971
|
+
AutoMountType.v3io_credentials: mlrun_pipelines.mounts.v3io_cred,
|
|
972
|
+
AutoMountType.v3io_fuse: mlrun_pipelines.mounts.mount_v3io,
|
|
973
|
+
AutoMountType.pvc: mlrun_pipelines.mounts.mount_pvc,
|
|
974
974
|
AutoMountType.auto: self._get_auto_modifier(),
|
|
975
|
-
AutoMountType.s3:
|
|
976
|
-
AutoMountType.env:
|
|
975
|
+
AutoMountType.s3: mlrun_pipelines.mounts.mount_s3,
|
|
976
|
+
AutoMountType.env: mlrun_pipelines.mounts.set_env_variables,
|
|
977
977
|
}[self]
|
|
978
978
|
|
|
979
979
|
|
|
980
|
-
class KubeResource(BaseRuntime):
|
|
980
|
+
class KubeResource(BaseRuntime, KfpAdapterMixin):
|
|
981
981
|
"""
|
|
982
982
|
A parent class for runtimes that generate k8s resources when executing.
|
|
983
983
|
"""
|
|
@@ -997,26 +997,6 @@ class KubeResource(BaseRuntime):
|
|
|
997
997
|
def spec(self, spec):
|
|
998
998
|
self._spec = self._verify_dict(spec, "spec", KubeResourceSpec)
|
|
999
999
|
|
|
1000
|
-
def apply(self, modify):
|
|
1001
|
-
"""
|
|
1002
|
-
Apply a modifier to the runtime which is used to change the runtimes k8s object's spec.
|
|
1003
|
-
Modifiers can be either KFP modifiers or MLRun modifiers (which are compatible with KFP). All modifiers accept
|
|
1004
|
-
a `kfp.dsl.ContainerOp` object, apply some changes on its spec and return it so modifiers can be chained
|
|
1005
|
-
one after the other.
|
|
1006
|
-
|
|
1007
|
-
:param modify: a modifier runnable object
|
|
1008
|
-
:return: the runtime (self) after the modifications
|
|
1009
|
-
"""
|
|
1010
|
-
|
|
1011
|
-
# Kubeflow pipeline have a hook to add the component to the DAG on ContainerOp init
|
|
1012
|
-
# we remove the hook to suppress kubeflow op registration and return it after the apply()
|
|
1013
|
-
old_op_handler = kfp.dsl._container_op._register_op_handler
|
|
1014
|
-
kfp.dsl._container_op._register_op_handler = lambda x: self.metadata.name
|
|
1015
|
-
cop = kfp.dsl.ContainerOp("name", "image")
|
|
1016
|
-
kfp.dsl._container_op._register_op_handler = old_op_handler
|
|
1017
|
-
|
|
1018
|
-
return apply_kfp(modify, cop, self)
|
|
1019
|
-
|
|
1020
1000
|
def set_env_from_secret(self, name, secret=None, secret_key=None):
|
|
1021
1001
|
"""set pod environment var from secret"""
|
|
1022
1002
|
secret_key = secret_key or name
|
mlrun/runtimes/remotesparkjob.py
CHANGED
|
@@ -15,11 +15,11 @@ import re
|
|
|
15
15
|
from subprocess import run
|
|
16
16
|
|
|
17
17
|
import kubernetes.client
|
|
18
|
+
from mlrun_pipelines.mounts import mount_v3io, mount_v3iod
|
|
18
19
|
|
|
19
20
|
import mlrun.errors
|
|
20
21
|
from mlrun.config import config
|
|
21
22
|
|
|
22
|
-
from ..platforms.iguazio import mount_v3io, mount_v3iod
|
|
23
23
|
from .kubejob import KubejobRuntime
|
|
24
24
|
from .pod import KubeResourceSpec
|
|
25
25
|
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import typing
|
|
15
15
|
|
|
16
16
|
import kubernetes.client
|
|
17
|
+
from mlrun_pipelines.mounts import mount_v3io, mount_v3iod
|
|
17
18
|
|
|
18
19
|
import mlrun.common.schemas.function
|
|
19
20
|
import mlrun.errors
|
|
@@ -22,7 +23,6 @@ from mlrun.config import config
|
|
|
22
23
|
|
|
23
24
|
from ...execution import MLClientCtx
|
|
24
25
|
from ...model import RunObject
|
|
25
|
-
from ...platforms.iguazio import mount_v3io, mount_v3iod
|
|
26
26
|
from ...utils import update_in, verify_field_regex
|
|
27
27
|
from ..kubejob import KubejobRuntime
|
|
28
28
|
from ..pod import KubeResourceSpec
|
mlrun/runtimes/utils.py
CHANGED
|
@@ -20,7 +20,6 @@ from io import StringIO
|
|
|
20
20
|
from sys import stderr
|
|
21
21
|
|
|
22
22
|
import pandas as pd
|
|
23
|
-
from kubernetes import client
|
|
24
23
|
|
|
25
24
|
import mlrun
|
|
26
25
|
import mlrun.common.constants
|
|
@@ -280,43 +279,6 @@ def get_item_name(item, attr="name"):
|
|
|
280
279
|
return getattr(item, attr, None)
|
|
281
280
|
|
|
282
281
|
|
|
283
|
-
def apply_kfp(modify, cop, runtime):
|
|
284
|
-
modify(cop)
|
|
285
|
-
|
|
286
|
-
# Have to do it here to avoid circular dependencies
|
|
287
|
-
from .pod import AutoMountType
|
|
288
|
-
|
|
289
|
-
if AutoMountType.is_auto_modifier(modify):
|
|
290
|
-
runtime.spec.disable_auto_mount = True
|
|
291
|
-
|
|
292
|
-
api = client.ApiClient()
|
|
293
|
-
for k, v in cop.pod_labels.items():
|
|
294
|
-
runtime.metadata.labels[k] = v
|
|
295
|
-
for k, v in cop.pod_annotations.items():
|
|
296
|
-
runtime.metadata.annotations[k] = v
|
|
297
|
-
if cop.container.env:
|
|
298
|
-
env_names = [
|
|
299
|
-
e.name if hasattr(e, "name") else e["name"] for e in runtime.spec.env
|
|
300
|
-
]
|
|
301
|
-
for e in api.sanitize_for_serialization(cop.container.env):
|
|
302
|
-
name = e["name"]
|
|
303
|
-
if name in env_names:
|
|
304
|
-
runtime.spec.env[env_names.index(name)] = e
|
|
305
|
-
else:
|
|
306
|
-
runtime.spec.env.append(e)
|
|
307
|
-
env_names.append(name)
|
|
308
|
-
cop.container.env.clear()
|
|
309
|
-
|
|
310
|
-
if cop.volumes and cop.container.volume_mounts:
|
|
311
|
-
vols = api.sanitize_for_serialization(cop.volumes)
|
|
312
|
-
mounts = api.sanitize_for_serialization(cop.container.volume_mounts)
|
|
313
|
-
runtime.spec.update_vols_and_mounts(vols, mounts)
|
|
314
|
-
cop.volumes.clear()
|
|
315
|
-
cop.container.volume_mounts.clear()
|
|
316
|
-
|
|
317
|
-
return runtime
|
|
318
|
-
|
|
319
|
-
|
|
320
282
|
def verify_limits(
|
|
321
283
|
resources_field_name,
|
|
322
284
|
mem=None,
|
mlrun/track/tracker.py
CHANGED
|
@@ -31,8 +31,9 @@ class Tracker(ABC):
|
|
|
31
31
|
* Offline: Manually importing models and artifacts into an MLRun project using the `import_x` methods.
|
|
32
32
|
"""
|
|
33
33
|
|
|
34
|
+
@staticmethod
|
|
34
35
|
@abstractmethod
|
|
35
|
-
def is_enabled(
|
|
36
|
+
def is_enabled() -> bool:
|
|
36
37
|
"""
|
|
37
38
|
Checks if tracker is enabled.
|
|
38
39
|
|