mlrun 1.7.0rc13__py3-none-any.whl → 1.7.0rc21__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 +23 -111
- mlrun/alerts/__init__.py +15 -0
- mlrun/alerts/alert.py +144 -0
- mlrun/api/schemas/__init__.py +4 -3
- mlrun/artifacts/__init__.py +8 -3
- mlrun/artifacts/base.py +36 -253
- mlrun/artifacts/dataset.py +9 -190
- mlrun/artifacts/manager.py +46 -42
- mlrun/artifacts/model.py +9 -141
- mlrun/artifacts/plots.py +14 -375
- mlrun/common/constants.py +65 -3
- mlrun/common/formatters/__init__.py +19 -0
- mlrun/{runtimes/mpijob/v1alpha1.py → common/formatters/artifact.py} +6 -14
- mlrun/common/formatters/base.py +113 -0
- mlrun/common/formatters/function.py +46 -0
- mlrun/common/formatters/pipeline.py +53 -0
- mlrun/common/formatters/project.py +51 -0
- mlrun/{runtimes → common/runtimes}/constants.py +32 -4
- mlrun/common/schemas/__init__.py +10 -5
- mlrun/common/schemas/alert.py +92 -11
- mlrun/common/schemas/api_gateway.py +56 -0
- mlrun/common/schemas/artifact.py +15 -5
- mlrun/common/schemas/auth.py +2 -0
- mlrun/common/schemas/client_spec.py +1 -0
- mlrun/common/schemas/frontend_spec.py +1 -0
- mlrun/common/schemas/function.py +4 -0
- mlrun/common/schemas/model_monitoring/__init__.py +15 -3
- mlrun/common/schemas/model_monitoring/constants.py +58 -7
- mlrun/common/schemas/model_monitoring/grafana.py +9 -5
- mlrun/common/schemas/model_monitoring/model_endpoints.py +86 -2
- mlrun/common/schemas/pipeline.py +0 -9
- mlrun/common/schemas/project.py +6 -11
- mlrun/common/types.py +1 -0
- mlrun/config.py +36 -8
- mlrun/data_types/to_pandas.py +9 -9
- mlrun/datastore/base.py +41 -9
- mlrun/datastore/datastore.py +6 -2
- mlrun/datastore/datastore_profile.py +56 -4
- mlrun/datastore/hdfs.py +5 -0
- mlrun/datastore/inmem.py +2 -2
- mlrun/datastore/redis.py +2 -2
- mlrun/datastore/s3.py +5 -0
- mlrun/datastore/sources.py +147 -7
- mlrun/datastore/store_resources.py +7 -7
- mlrun/datastore/targets.py +129 -9
- mlrun/datastore/utils.py +42 -0
- mlrun/datastore/v3io.py +1 -1
- mlrun/db/auth_utils.py +152 -0
- mlrun/db/base.py +55 -11
- mlrun/db/httpdb.py +346 -107
- mlrun/db/nopdb.py +52 -10
- mlrun/errors.py +11 -0
- mlrun/execution.py +24 -9
- mlrun/feature_store/__init__.py +0 -2
- mlrun/feature_store/api.py +12 -47
- mlrun/feature_store/feature_set.py +9 -0
- mlrun/feature_store/feature_vector.py +8 -0
- mlrun/feature_store/ingestion.py +7 -6
- mlrun/feature_store/retrieval/base.py +9 -4
- mlrun/feature_store/retrieval/conversion.py +9 -9
- 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 +16 -0
- mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +7 -12
- mlrun/frameworks/parallel_coordinates.py +2 -1
- mlrun/frameworks/tf_keras/__init__.py +4 -1
- mlrun/k8s_utils.py +10 -11
- mlrun/launcher/base.py +4 -3
- mlrun/launcher/client.py +5 -3
- mlrun/launcher/local.py +8 -2
- mlrun/launcher/remote.py +8 -2
- mlrun/lists.py +6 -2
- mlrun/model.py +62 -20
- mlrun/model_monitoring/__init__.py +1 -1
- mlrun/model_monitoring/api.py +41 -18
- mlrun/model_monitoring/application.py +5 -305
- mlrun/model_monitoring/applications/__init__.py +11 -0
- mlrun/model_monitoring/applications/_application_steps.py +157 -0
- mlrun/model_monitoring/applications/base.py +280 -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 +132 -91
- mlrun/model_monitoring/applications/results.py +99 -0
- mlrun/model_monitoring/controller.py +3 -1
- mlrun/model_monitoring/db/__init__.py +2 -0
- mlrun/model_monitoring/db/stores/__init__.py +0 -2
- mlrun/model_monitoring/db/stores/base/store.py +22 -37
- mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +43 -21
- mlrun/model_monitoring/db/stores/sqldb/models/base.py +39 -8
- mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +27 -7
- mlrun/model_monitoring/db/stores/sqldb/models/sqlite.py +5 -0
- mlrun/model_monitoring/db/stores/sqldb/sql_store.py +246 -224
- mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +232 -216
- 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 +636 -0
- mlrun/model_monitoring/evidently_application.py +6 -118
- mlrun/model_monitoring/helpers.py +46 -1
- mlrun/model_monitoring/model_endpoint.py +3 -2
- mlrun/model_monitoring/stream_processing.py +57 -216
- mlrun/model_monitoring/writer.py +134 -124
- mlrun/package/utils/_formatter.py +2 -2
- mlrun/platforms/__init__.py +10 -9
- mlrun/platforms/iguazio.py +21 -202
- mlrun/projects/operations.py +19 -12
- mlrun/projects/pipelines.py +103 -109
- mlrun/projects/project.py +377 -137
- mlrun/render.py +15 -14
- mlrun/run.py +16 -47
- mlrun/runtimes/__init__.py +6 -3
- mlrun/runtimes/base.py +8 -7
- mlrun/runtimes/databricks_job/databricks_wrapper.py +1 -1
- mlrun/runtimes/funcdoc.py +0 -28
- mlrun/runtimes/kubejob.py +2 -1
- mlrun/runtimes/local.py +5 -2
- mlrun/runtimes/mpijob/__init__.py +0 -20
- mlrun/runtimes/mpijob/v1.py +1 -1
- mlrun/runtimes/nuclio/api_gateway.py +440 -208
- mlrun/runtimes/nuclio/application/application.py +170 -8
- mlrun/runtimes/nuclio/function.py +39 -49
- mlrun/runtimes/pod.py +21 -41
- mlrun/runtimes/remotesparkjob.py +9 -3
- mlrun/runtimes/sparkjob/spark3job.py +1 -1
- mlrun/runtimes/utils.py +6 -45
- mlrun/serving/server.py +2 -1
- mlrun/serving/states.py +53 -2
- mlrun/serving/v2_serving.py +5 -1
- mlrun/track/tracker.py +2 -1
- mlrun/utils/async_http.py +25 -5
- mlrun/utils/helpers.py +107 -75
- mlrun/utils/logger.py +39 -7
- mlrun/utils/notifications/notification/__init__.py +14 -9
- mlrun/utils/notifications/notification/base.py +1 -1
- mlrun/utils/notifications/notification/slack.py +61 -13
- mlrun/utils/notifications/notification/webhook.py +1 -1
- mlrun/utils/notifications/notification_pusher.py +147 -16
- mlrun/utils/regex.py +9 -0
- mlrun/utils/v3io_clients.py +0 -1
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.7.0rc13.dist-info → mlrun-1.7.0rc21.dist-info}/METADATA +14 -6
- {mlrun-1.7.0rc13.dist-info → mlrun-1.7.0rc21.dist-info}/RECORD +154 -133
- mlrun/kfpops.py +0 -865
- mlrun/platforms/other.py +0 -305
- {mlrun-1.7.0rc13.dist-info → mlrun-1.7.0rc21.dist-info}/LICENSE +0 -0
- {mlrun-1.7.0rc13.dist-info → mlrun-1.7.0rc21.dist-info}/WHEEL +0 -0
- {mlrun-1.7.0rc13.dist-info → mlrun-1.7.0rc21.dist-info}/entry_points.txt +0 -0
- {mlrun-1.7.0rc13.dist-info → mlrun-1.7.0rc21.dist-info}/top_level.txt +0 -0
|
@@ -12,14 +12,22 @@
|
|
|
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
|
|
30
|
+
from mlrun.utils import logger
|
|
23
31
|
|
|
24
32
|
|
|
25
33
|
class ApplicationSpec(NuclioSpec):
|
|
@@ -113,7 +121,10 @@ class ApplicationSpec(NuclioSpec):
|
|
|
113
121
|
state_thresholds=state_thresholds,
|
|
114
122
|
disable_default_http_trigger=disable_default_http_trigger,
|
|
115
123
|
)
|
|
116
|
-
self.internal_application_port =
|
|
124
|
+
self.internal_application_port = (
|
|
125
|
+
internal_application_port
|
|
126
|
+
or mlrun.mlconf.function.application.default_sidecar_internal_port
|
|
127
|
+
)
|
|
117
128
|
|
|
118
129
|
@property
|
|
119
130
|
def internal_application_port(self):
|
|
@@ -139,6 +150,9 @@ class ApplicationStatus(NuclioStatus):
|
|
|
139
150
|
container_image=None,
|
|
140
151
|
application_image=None,
|
|
141
152
|
sidecar_name=None,
|
|
153
|
+
api_gateway_name=None,
|
|
154
|
+
api_gateway=None,
|
|
155
|
+
url=None,
|
|
142
156
|
):
|
|
143
157
|
super().__init__(
|
|
144
158
|
state=state,
|
|
@@ -151,12 +165,15 @@ class ApplicationStatus(NuclioStatus):
|
|
|
151
165
|
)
|
|
152
166
|
self.application_image = application_image or None
|
|
153
167
|
self.sidecar_name = sidecar_name or None
|
|
168
|
+
self.api_gateway_name = api_gateway_name or None
|
|
169
|
+
self.api_gateway = api_gateway or None
|
|
170
|
+
self.url = url or None
|
|
154
171
|
|
|
155
172
|
|
|
156
173
|
class ApplicationRuntime(RemoteRuntime):
|
|
157
174
|
kind = "application"
|
|
158
175
|
|
|
159
|
-
@min_nuclio_versions("1.
|
|
176
|
+
@min_nuclio_versions("1.13.1")
|
|
160
177
|
def __init__(self, spec=None, metadata=None):
|
|
161
178
|
super().__init__(spec=spec, metadata=metadata)
|
|
162
179
|
|
|
@@ -176,6 +193,24 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
176
193
|
def status(self, status):
|
|
177
194
|
self._status = self._verify_dict(status, "status", ApplicationStatus)
|
|
178
195
|
|
|
196
|
+
@property
|
|
197
|
+
def api_gateway(self):
|
|
198
|
+
return self.status.api_gateway
|
|
199
|
+
|
|
200
|
+
@api_gateway.setter
|
|
201
|
+
def api_gateway(self, api_gateway: APIGateway):
|
|
202
|
+
self.status.api_gateway = api_gateway
|
|
203
|
+
|
|
204
|
+
@property
|
|
205
|
+
def url(self):
|
|
206
|
+
if not self.status.api_gateway:
|
|
207
|
+
self._sync_api_gateway()
|
|
208
|
+
return self.status.api_gateway.invoke_url
|
|
209
|
+
|
|
210
|
+
@url.setter
|
|
211
|
+
def url(self, url):
|
|
212
|
+
self.status.url = url
|
|
213
|
+
|
|
179
214
|
def set_internal_application_port(self, port: int):
|
|
180
215
|
self.spec.internal_application_port = port
|
|
181
216
|
|
|
@@ -220,7 +255,7 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
220
255
|
project="",
|
|
221
256
|
tag="",
|
|
222
257
|
verbose=False,
|
|
223
|
-
auth_info: AuthInfo = None,
|
|
258
|
+
auth_info: schemas.AuthInfo = None,
|
|
224
259
|
builder_env: dict = None,
|
|
225
260
|
force_build: bool = False,
|
|
226
261
|
with_mlrun=None,
|
|
@@ -228,6 +263,10 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
228
263
|
is_kfp=False,
|
|
229
264
|
mlrun_version_specifier=None,
|
|
230
265
|
show_on_failure: bool = False,
|
|
266
|
+
skip_access_key_auth: bool = False,
|
|
267
|
+
direct_port_access: bool = False,
|
|
268
|
+
authentication_mode: schemas.APIGatewayAuthenticationMode = None,
|
|
269
|
+
authentication_creds: tuple[str] = None,
|
|
231
270
|
):
|
|
232
271
|
"""
|
|
233
272
|
Deploy function, builds the application image if required (self.requires_build()) or force_build is True,
|
|
@@ -244,6 +283,10 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
244
283
|
:param is_kfp: Deploy as part of a kfp pipeline
|
|
245
284
|
:param mlrun_version_specifier: Which mlrun package version to include (if not current)
|
|
246
285
|
:param show_on_failure: Show logs only in case of build failure
|
|
286
|
+
:param skip_access_key_auth: Skip adding access key auth to the API Gateway
|
|
287
|
+
:param direct_port_access: Set True to allow direct port access to the application sidecar
|
|
288
|
+
:param authentication_mode: API Gateway authentication mode
|
|
289
|
+
:param authentication_creds: API Gateway authentication credentials as a tuple (username, password)
|
|
247
290
|
:return: True if the function is ready (deployed)
|
|
248
291
|
"""
|
|
249
292
|
if self.requires_build() or force_build:
|
|
@@ -261,6 +304,16 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
261
304
|
|
|
262
305
|
self._ensure_reverse_proxy_configurations()
|
|
263
306
|
self._configure_application_sidecar()
|
|
307
|
+
|
|
308
|
+
# we only allow accessing the application via the API Gateway
|
|
309
|
+
name_tag = tag or self.metadata.tag
|
|
310
|
+
self.status.api_gateway_name = (
|
|
311
|
+
f"{self.metadata.name}-{name_tag}" if name_tag else self.metadata.name
|
|
312
|
+
)
|
|
313
|
+
self.spec.add_templated_ingress_host_mode = (
|
|
314
|
+
NuclioIngressAddTemplatedIngressModes.never
|
|
315
|
+
)
|
|
316
|
+
|
|
264
317
|
super().deploy(
|
|
265
318
|
project,
|
|
266
319
|
tag,
|
|
@@ -269,6 +322,14 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
269
322
|
builder_env,
|
|
270
323
|
)
|
|
271
324
|
|
|
325
|
+
ports = self.spec.internal_application_port if direct_port_access else []
|
|
326
|
+
self.create_api_gateway(
|
|
327
|
+
name=self.status.api_gateway_name,
|
|
328
|
+
ports=ports,
|
|
329
|
+
authentication_mode=authentication_mode,
|
|
330
|
+
authentication_creds=authentication_creds,
|
|
331
|
+
)
|
|
332
|
+
|
|
272
333
|
def with_source_archive(
|
|
273
334
|
self, source, workdir=None, pull_at_runtime=True, target_dir=None
|
|
274
335
|
):
|
|
@@ -290,6 +351,92 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
290
351
|
target_dir=target_dir,
|
|
291
352
|
)
|
|
292
353
|
|
|
354
|
+
@classmethod
|
|
355
|
+
def get_filename_and_handler(cls) -> (str, str):
|
|
356
|
+
reverse_proxy_file_path = pathlib.Path(__file__).parent / "reverse_proxy.go"
|
|
357
|
+
return str(reverse_proxy_file_path), "Handler"
|
|
358
|
+
|
|
359
|
+
def create_api_gateway(
|
|
360
|
+
self,
|
|
361
|
+
name: str = None,
|
|
362
|
+
path: str = None,
|
|
363
|
+
ports: list[int] = None,
|
|
364
|
+
authentication_mode: schemas.APIGatewayAuthenticationMode = None,
|
|
365
|
+
authentication_creds: tuple[str] = None,
|
|
366
|
+
):
|
|
367
|
+
api_gateway = APIGateway(
|
|
368
|
+
APIGatewayMetadata(
|
|
369
|
+
name=name,
|
|
370
|
+
namespace=self.metadata.namespace,
|
|
371
|
+
labels=self.metadata.labels,
|
|
372
|
+
annotations=self.metadata.annotations,
|
|
373
|
+
),
|
|
374
|
+
APIGatewaySpec(
|
|
375
|
+
functions=[self],
|
|
376
|
+
project=self.metadata.project,
|
|
377
|
+
path=path,
|
|
378
|
+
ports=mlrun.utils.helpers.as_list(ports) if ports else None,
|
|
379
|
+
),
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
authentication_mode = (
|
|
383
|
+
authentication_mode
|
|
384
|
+
or mlrun.mlconf.function.application.default_authentication_mode
|
|
385
|
+
)
|
|
386
|
+
if authentication_mode == schemas.APIGatewayAuthenticationMode.access_key:
|
|
387
|
+
api_gateway.with_access_key_auth()
|
|
388
|
+
elif authentication_mode == schemas.APIGatewayAuthenticationMode.basic:
|
|
389
|
+
api_gateway.with_basic_auth(*authentication_creds)
|
|
390
|
+
|
|
391
|
+
db = self._get_db()
|
|
392
|
+
api_gateway_scheme = db.store_api_gateway(
|
|
393
|
+
api_gateway=api_gateway.to_scheme(), project=self.metadata.project
|
|
394
|
+
)
|
|
395
|
+
if not self.status.api_gateway_name:
|
|
396
|
+
self.status.api_gateway_name = api_gateway_scheme.metadata.name
|
|
397
|
+
self.status.api_gateway = APIGateway.from_scheme(api_gateway_scheme)
|
|
398
|
+
self.status.api_gateway.wait_for_readiness()
|
|
399
|
+
self.url = self.status.api_gateway.invoke_url
|
|
400
|
+
|
|
401
|
+
def invoke(
|
|
402
|
+
self,
|
|
403
|
+
path: str,
|
|
404
|
+
body: typing.Union[str, bytes, dict] = None,
|
|
405
|
+
method: str = None,
|
|
406
|
+
headers: dict = None,
|
|
407
|
+
dashboard: str = "",
|
|
408
|
+
force_external_address: bool = False,
|
|
409
|
+
auth_info: schemas.AuthInfo = None,
|
|
410
|
+
mock: bool = None,
|
|
411
|
+
**http_client_kwargs,
|
|
412
|
+
):
|
|
413
|
+
self._sync_api_gateway()
|
|
414
|
+
# If the API Gateway is not ready or not set, try to invoke the function directly (without the API Gateway)
|
|
415
|
+
if not self.status.api_gateway:
|
|
416
|
+
super().invoke(
|
|
417
|
+
path,
|
|
418
|
+
body,
|
|
419
|
+
method,
|
|
420
|
+
headers,
|
|
421
|
+
dashboard,
|
|
422
|
+
force_external_address,
|
|
423
|
+
auth_info,
|
|
424
|
+
mock,
|
|
425
|
+
**http_client_kwargs,
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
credentials = (auth_info.username, auth_info.password) if auth_info else None
|
|
429
|
+
|
|
430
|
+
if not method:
|
|
431
|
+
method = "POST" if body else "GET"
|
|
432
|
+
return self.status.api_gateway.invoke(
|
|
433
|
+
method=method,
|
|
434
|
+
headers=headers,
|
|
435
|
+
credentials=credentials,
|
|
436
|
+
path=path,
|
|
437
|
+
**http_client_kwargs,
|
|
438
|
+
)
|
|
439
|
+
|
|
293
440
|
def _build_application_image(
|
|
294
441
|
self,
|
|
295
442
|
builder_env: dict = None,
|
|
@@ -301,6 +448,14 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
301
448
|
mlrun_version_specifier=None,
|
|
302
449
|
show_on_failure: bool = False,
|
|
303
450
|
):
|
|
451
|
+
if not self.spec.command:
|
|
452
|
+
logger.warning(
|
|
453
|
+
"Building the application image without a command. "
|
|
454
|
+
"Use spec.command and spec.args to specify the application entrypoint",
|
|
455
|
+
command=self.spec.command,
|
|
456
|
+
args=self.spec.args,
|
|
457
|
+
)
|
|
458
|
+
|
|
304
459
|
with_mlrun = self._resolve_build_with_mlrun(with_mlrun)
|
|
305
460
|
return self._build_image(
|
|
306
461
|
builder_env=builder_env,
|
|
@@ -355,7 +510,14 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
355
510
|
self.set_env("SIDECAR_PORT", self.spec.internal_application_port)
|
|
356
511
|
self.set_env("SIDECAR_HOST", "http://localhost")
|
|
357
512
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
513
|
+
def _sync_api_gateway(self):
|
|
514
|
+
if not self.status.api_gateway_name:
|
|
515
|
+
return
|
|
516
|
+
|
|
517
|
+
db = self._get_db()
|
|
518
|
+
api_gateway_scheme = db.get_api_gateway(
|
|
519
|
+
name=self.status.api_gateway_name, project=self.metadata.project
|
|
520
|
+
)
|
|
521
|
+
self.status.api_gateway = APIGateway.from_scheme(api_gateway_scheme)
|
|
522
|
+
self.status.api_gateway.wait_for_readiness()
|
|
523
|
+
self.url = self.status.api_gateway.invoke_url
|
|
@@ -19,12 +19,15 @@ import warnings
|
|
|
19
19
|
from datetime import datetime
|
|
20
20
|
from time import sleep
|
|
21
21
|
|
|
22
|
+
import inflection
|
|
22
23
|
import nuclio
|
|
23
24
|
import nuclio.utils
|
|
24
25
|
import requests
|
|
25
|
-
import semver
|
|
26
26
|
from aiohttp.client import ClientSession
|
|
27
27
|
from kubernetes import client
|
|
28
|
+
from mlrun_pipelines.common.mounts import VolumeMount
|
|
29
|
+
from mlrun_pipelines.common.ops import deploy_op
|
|
30
|
+
from mlrun_pipelines.mounts import mount_v3io, v3io_cred
|
|
28
31
|
from nuclio.deploy import find_dashboard_url, get_deploy_status
|
|
29
32
|
from nuclio.triggers import V3IOStreamTrigger
|
|
30
33
|
|
|
@@ -36,15 +39,11 @@ import mlrun.utils.helpers
|
|
|
36
39
|
from mlrun.common.schemas import AuthInfo
|
|
37
40
|
from mlrun.config import config as mlconf
|
|
38
41
|
from mlrun.errors import err_to_str
|
|
39
|
-
from mlrun.kfpops import deploy_op
|
|
40
42
|
from mlrun.lists import RunList
|
|
41
43
|
from mlrun.model import RunObject
|
|
42
44
|
from mlrun.platforms.iguazio import (
|
|
43
|
-
VolumeMount,
|
|
44
|
-
mount_v3io,
|
|
45
45
|
parse_path,
|
|
46
46
|
split_path,
|
|
47
|
-
v3io_cred,
|
|
48
47
|
)
|
|
49
48
|
from mlrun.runtimes.base import FunctionStatus, RunError
|
|
50
49
|
from mlrun.runtimes.pod import KubeResource, KubeResourceSpec
|
|
@@ -56,33 +55,9 @@ def validate_nuclio_version_compatibility(*min_versions):
|
|
|
56
55
|
"""
|
|
57
56
|
:param min_versions: Valid minimum version(s) required, assuming no 2 versions has equal major and minor.
|
|
58
57
|
"""
|
|
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
|
|
58
|
+
return mlrun.utils.helpers.validate_component_version_compatibility(
|
|
59
|
+
"nuclio", *min_versions
|
|
60
|
+
)
|
|
86
61
|
|
|
87
62
|
|
|
88
63
|
def min_nuclio_versions(*versions):
|
|
@@ -91,9 +66,13 @@ def min_nuclio_versions(*versions):
|
|
|
91
66
|
if validate_nuclio_version_compatibility(*versions):
|
|
92
67
|
return function(*args, **kwargs)
|
|
93
68
|
|
|
69
|
+
if function.__name__ == "__init__":
|
|
70
|
+
name = inflection.titleize(function.__qualname__.split(".")[0])
|
|
71
|
+
else:
|
|
72
|
+
name = function.__qualname__
|
|
73
|
+
|
|
94
74
|
message = (
|
|
95
|
-
f"{
|
|
96
|
-
f"nuclio {mlconf.nuclio_version}, please upgrade."
|
|
75
|
+
f"'{name}' function requires Nuclio v{' or v'.join(versions)} or higher"
|
|
97
76
|
)
|
|
98
77
|
raise mlrun.errors.MLRunIncompatibleVersionError(message)
|
|
99
78
|
|
|
@@ -292,7 +271,8 @@ class RemoteRuntime(KubeResource):
|
|
|
292
271
|
self._status = self._verify_dict(status, "status", NuclioStatus)
|
|
293
272
|
|
|
294
273
|
def pre_deploy_validation(self):
|
|
295
|
-
|
|
274
|
+
if self.metadata.tag:
|
|
275
|
+
mlrun.utils.validate_tag_name(self.metadata.tag, "function.metadata.tag")
|
|
296
276
|
|
|
297
277
|
def set_config(self, key, value):
|
|
298
278
|
self.spec.config[key] = value
|
|
@@ -573,7 +553,6 @@ class RemoteRuntime(KubeResource):
|
|
|
573
553
|
if tag:
|
|
574
554
|
self.metadata.tag = tag
|
|
575
555
|
|
|
576
|
-
save_record = False
|
|
577
556
|
# Attempt auto-mounting, before sending to remote build
|
|
578
557
|
self.try_auto_mount_based_on_config()
|
|
579
558
|
self._fill_credentials()
|
|
@@ -591,15 +570,18 @@ class RemoteRuntime(KubeResource):
|
|
|
591
570
|
# now, functions can be not exposed (using service type ClusterIP) and hence
|
|
592
571
|
# for BC we first try to populate the external invocation url, and then
|
|
593
572
|
# if not exists, take the internal invocation url
|
|
594
|
-
if
|
|
573
|
+
if (
|
|
574
|
+
self.status.external_invocation_urls
|
|
575
|
+
and self.status.external_invocation_urls[0] != ""
|
|
576
|
+
):
|
|
595
577
|
self.spec.command = f"http://{self.status.external_invocation_urls[0]}"
|
|
596
|
-
|
|
597
|
-
|
|
578
|
+
elif (
|
|
579
|
+
self.status.internal_invocation_urls
|
|
580
|
+
and self.status.internal_invocation_urls[0] != ""
|
|
581
|
+
):
|
|
598
582
|
self.spec.command = f"http://{self.status.internal_invocation_urls[0]}"
|
|
599
|
-
|
|
600
|
-
elif self.status.address:
|
|
583
|
+
elif self.status.address and self.status.address != "":
|
|
601
584
|
self.spec.command = f"http://{self.status.address}"
|
|
602
|
-
save_record = True
|
|
603
585
|
|
|
604
586
|
logger.info(
|
|
605
587
|
"Successfully deployed function",
|
|
@@ -607,8 +589,7 @@ class RemoteRuntime(KubeResource):
|
|
|
607
589
|
external_invocation_urls=self.status.external_invocation_urls,
|
|
608
590
|
)
|
|
609
591
|
|
|
610
|
-
|
|
611
|
-
self.save(versioned=False)
|
|
592
|
+
self.save(versioned=False)
|
|
612
593
|
|
|
613
594
|
return self.spec.command
|
|
614
595
|
|
|
@@ -778,7 +759,7 @@ class RemoteRuntime(KubeResource):
|
|
|
778
759
|
runtime_env["MLRUN_NAMESPACE"] = mlconf.namespace
|
|
779
760
|
if self.metadata.credentials.access_key:
|
|
780
761
|
runtime_env[
|
|
781
|
-
mlrun.runtimes.constants.FunctionEnvironmentVariables.auth_session
|
|
762
|
+
mlrun.common.runtimes.constants.FunctionEnvironmentVariables.auth_session
|
|
782
763
|
] = self.metadata.credentials.access_key
|
|
783
764
|
return runtime_env
|
|
784
765
|
|
|
@@ -992,21 +973,30 @@ class RemoteRuntime(KubeResource):
|
|
|
992
973
|
sidecar["image"] = image
|
|
993
974
|
|
|
994
975
|
ports = mlrun.utils.helpers.as_list(ports)
|
|
976
|
+
# according to RFC-6335, port name should be less than 15 characters,
|
|
977
|
+
# so we truncate it if needed and leave room for the index
|
|
978
|
+
port_name = name[:13].rstrip("-_") if len(name) > 13 else name
|
|
995
979
|
sidecar["ports"] = [
|
|
996
980
|
{
|
|
997
|
-
"name": "
|
|
981
|
+
"name": f"{port_name}-{i}",
|
|
998
982
|
"containerPort": port,
|
|
999
983
|
"protocol": "TCP",
|
|
1000
984
|
}
|
|
1001
|
-
for port in ports
|
|
985
|
+
for i, port in enumerate(ports)
|
|
1002
986
|
]
|
|
1003
987
|
|
|
1004
|
-
if command
|
|
988
|
+
# if it is a redeploy, 'command' might be set with the previous invocation url.
|
|
989
|
+
# in this case, we don't want to use it as the sidecar command
|
|
990
|
+
if command and not command.startswith("http"):
|
|
1005
991
|
sidecar["command"] = mlrun.utils.helpers.as_list(command)
|
|
1006
992
|
|
|
1007
|
-
if args:
|
|
993
|
+
if args and sidecar["command"]:
|
|
1008
994
|
sidecar["args"] = mlrun.utils.helpers.as_list(args)
|
|
1009
995
|
|
|
996
|
+
# populate the sidecar resources from the function spec
|
|
997
|
+
if self.spec.resources:
|
|
998
|
+
sidecar["resources"] = self.spec.resources
|
|
999
|
+
|
|
1010
1000
|
def _set_sidecar(self, name: str) -> dict:
|
|
1011
1001
|
self.spec.config.setdefault("spec.sidecars", [])
|
|
1012
1002
|
sidecars = self.spec.config["spec.sidecars"]
|
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
|
|
@@ -1086,12 +1066,12 @@ class KubeResource(BaseRuntime):
|
|
|
1086
1066
|
|
|
1087
1067
|
def _set_env(self, name, value=None, value_from=None):
|
|
1088
1068
|
new_var = k8s_client.V1EnvVar(name=name, value=value, value_from=value_from)
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1069
|
+
|
|
1070
|
+
# ensure we don't have duplicate env vars with the same name
|
|
1071
|
+
for env_index, value_item in enumerate(self.spec.env):
|
|
1072
|
+
if get_item_name(value_item) == name:
|
|
1073
|
+
self.spec.env[env_index] = new_var
|
|
1093
1074
|
return self
|
|
1094
|
-
i += 1
|
|
1095
1075
|
self.spec.env.append(new_var)
|
|
1096
1076
|
return self
|
|
1097
1077
|
|
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
|
|
|
@@ -130,14 +130,20 @@ class RemoteSparkRuntime(KubejobRuntime):
|
|
|
130
130
|
def spec(self, spec):
|
|
131
131
|
self._spec = self._verify_dict(spec, "spec", RemoteSparkSpec)
|
|
132
132
|
|
|
133
|
-
def with_spark_service(
|
|
133
|
+
def with_spark_service(
|
|
134
|
+
self,
|
|
135
|
+
spark_service,
|
|
136
|
+
provider=RemoteSparkProviders.iguazio,
|
|
137
|
+
with_v3io_mount=True,
|
|
138
|
+
):
|
|
134
139
|
"""Attach spark service to function"""
|
|
135
140
|
self.spec.provider = provider
|
|
136
141
|
if provider == RemoteSparkProviders.iguazio:
|
|
137
142
|
self.spec.env.append(
|
|
138
143
|
{"name": "MLRUN_SPARK_CLIENT_IGZ_SPARK", "value": "true"}
|
|
139
144
|
)
|
|
140
|
-
|
|
145
|
+
if with_v3io_mount:
|
|
146
|
+
self.apply(mount_v3io())
|
|
141
147
|
self.apply(
|
|
142
148
|
mount_v3iod(
|
|
143
149
|
namespace=config.namespace,
|
|
@@ -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
|