mlrun 1.10.0rc18__py3-none-any.whl → 1.11.0rc16__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 +24 -3
- mlrun/__main__.py +0 -4
- mlrun/artifacts/dataset.py +2 -2
- mlrun/artifacts/document.py +6 -1
- mlrun/artifacts/llm_prompt.py +21 -15
- mlrun/artifacts/model.py +3 -3
- mlrun/artifacts/plots.py +1 -1
- mlrun/{model_monitoring/db/tsdb/tdengine → auth}/__init__.py +2 -3
- mlrun/auth/nuclio.py +89 -0
- mlrun/auth/providers.py +429 -0
- mlrun/auth/utils.py +415 -0
- mlrun/common/constants.py +14 -0
- mlrun/common/model_monitoring/helpers.py +123 -0
- mlrun/common/runtimes/constants.py +28 -0
- mlrun/common/schemas/__init__.py +14 -3
- mlrun/common/schemas/alert.py +2 -2
- mlrun/common/schemas/api_gateway.py +3 -0
- mlrun/common/schemas/auth.py +12 -10
- mlrun/common/schemas/client_spec.py +4 -0
- mlrun/common/schemas/constants.py +25 -0
- mlrun/common/schemas/frontend_spec.py +1 -8
- mlrun/common/schemas/function.py +34 -0
- mlrun/common/schemas/hub.py +33 -20
- mlrun/common/schemas/model_monitoring/__init__.py +2 -1
- mlrun/common/schemas/model_monitoring/constants.py +12 -15
- mlrun/common/schemas/model_monitoring/functions.py +13 -4
- mlrun/common/schemas/model_monitoring/model_endpoints.py +11 -0
- mlrun/common/schemas/pipeline.py +1 -1
- mlrun/common/schemas/secret.py +17 -2
- mlrun/common/secrets.py +95 -1
- mlrun/common/types.py +10 -10
- mlrun/config.py +69 -19
- mlrun/data_types/infer.py +2 -2
- mlrun/datastore/__init__.py +12 -5
- mlrun/datastore/azure_blob.py +162 -47
- mlrun/datastore/base.py +274 -10
- mlrun/datastore/datastore.py +7 -2
- mlrun/datastore/datastore_profile.py +84 -22
- mlrun/datastore/model_provider/huggingface_provider.py +225 -41
- mlrun/datastore/model_provider/mock_model_provider.py +87 -0
- mlrun/datastore/model_provider/model_provider.py +206 -74
- mlrun/datastore/model_provider/openai_provider.py +226 -66
- mlrun/datastore/s3.py +39 -18
- mlrun/datastore/sources.py +1 -1
- mlrun/datastore/store_resources.py +4 -4
- mlrun/datastore/storeytargets.py +17 -12
- mlrun/datastore/targets.py +1 -1
- mlrun/datastore/utils.py +25 -6
- mlrun/datastore/v3io.py +1 -1
- mlrun/db/base.py +63 -32
- mlrun/db/httpdb.py +373 -153
- mlrun/db/nopdb.py +54 -21
- mlrun/errors.py +4 -2
- mlrun/execution.py +66 -25
- mlrun/feature_store/api.py +1 -1
- mlrun/feature_store/common.py +1 -1
- mlrun/feature_store/feature_vector_utils.py +1 -1
- mlrun/feature_store/steps.py +8 -6
- mlrun/frameworks/_common/utils.py +3 -3
- mlrun/frameworks/_dl_common/loggers/logger.py +1 -1
- mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +2 -1
- mlrun/frameworks/_ml_common/loggers/mlrun_logger.py +1 -1
- mlrun/frameworks/_ml_common/utils.py +2 -1
- mlrun/frameworks/auto_mlrun/auto_mlrun.py +4 -3
- mlrun/frameworks/lgbm/mlrun_interfaces/mlrun_interface.py +2 -1
- mlrun/frameworks/onnx/dataset.py +2 -1
- mlrun/frameworks/onnx/mlrun_interface.py +2 -1
- mlrun/frameworks/pytorch/callbacks/logging_callback.py +5 -4
- mlrun/frameworks/pytorch/callbacks/mlrun_logging_callback.py +2 -1
- mlrun/frameworks/pytorch/callbacks/tensorboard_logging_callback.py +2 -1
- mlrun/frameworks/pytorch/utils.py +2 -1
- mlrun/frameworks/sklearn/metric.py +2 -1
- mlrun/frameworks/tf_keras/callbacks/logging_callback.py +5 -4
- mlrun/frameworks/tf_keras/callbacks/mlrun_logging_callback.py +2 -1
- mlrun/frameworks/tf_keras/callbacks/tensorboard_logging_callback.py +2 -1
- mlrun/hub/__init__.py +52 -0
- mlrun/hub/base.py +142 -0
- mlrun/hub/module.py +172 -0
- mlrun/hub/step.py +113 -0
- mlrun/k8s_utils.py +105 -16
- mlrun/launcher/base.py +15 -7
- mlrun/launcher/local.py +4 -1
- mlrun/model.py +14 -4
- mlrun/model_monitoring/__init__.py +0 -1
- mlrun/model_monitoring/api.py +65 -28
- mlrun/model_monitoring/applications/__init__.py +1 -1
- mlrun/model_monitoring/applications/base.py +299 -128
- mlrun/model_monitoring/applications/context.py +2 -4
- mlrun/model_monitoring/controller.py +132 -58
- mlrun/model_monitoring/db/_schedules.py +38 -29
- mlrun/model_monitoring/db/_stats.py +6 -16
- mlrun/model_monitoring/db/tsdb/__init__.py +9 -7
- mlrun/model_monitoring/db/tsdb/base.py +29 -9
- mlrun/model_monitoring/db/tsdb/preaggregate.py +234 -0
- mlrun/model_monitoring/db/tsdb/stream_graph_steps.py +63 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/queries/timescaledb_metrics_queries.py +414 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/queries/timescaledb_predictions_queries.py +376 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/queries/timescaledb_results_queries.py +590 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_connection.py +434 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_connector.py +541 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_operations.py +808 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_schema.py +502 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_stream.py +163 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_stream_graph_steps.py +60 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/utils/timescaledb_dataframe_processor.py +141 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/utils/timescaledb_query_builder.py +585 -0
- mlrun/model_monitoring/db/tsdb/timescaledb/writer_graph_steps.py +73 -0
- mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +20 -9
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +235 -51
- mlrun/model_monitoring/features_drift_table.py +2 -1
- mlrun/model_monitoring/helpers.py +30 -6
- mlrun/model_monitoring/stream_processing.py +34 -28
- mlrun/model_monitoring/writer.py +224 -4
- mlrun/package/__init__.py +2 -1
- mlrun/platforms/__init__.py +0 -43
- mlrun/platforms/iguazio.py +8 -4
- mlrun/projects/operations.py +17 -11
- mlrun/projects/pipelines.py +2 -2
- mlrun/projects/project.py +187 -123
- mlrun/run.py +95 -21
- mlrun/runtimes/__init__.py +2 -186
- mlrun/runtimes/base.py +103 -25
- mlrun/runtimes/constants.py +225 -0
- mlrun/runtimes/daskjob.py +5 -2
- mlrun/runtimes/databricks_job/databricks_runtime.py +2 -1
- mlrun/runtimes/local.py +5 -2
- mlrun/runtimes/mounts.py +20 -2
- mlrun/runtimes/nuclio/__init__.py +12 -7
- mlrun/runtimes/nuclio/api_gateway.py +36 -6
- mlrun/runtimes/nuclio/application/application.py +339 -40
- mlrun/runtimes/nuclio/function.py +222 -72
- mlrun/runtimes/nuclio/serving.py +132 -42
- mlrun/runtimes/pod.py +213 -21
- mlrun/runtimes/utils.py +49 -9
- mlrun/secrets.py +99 -14
- mlrun/serving/__init__.py +2 -0
- mlrun/serving/remote.py +84 -11
- mlrun/serving/routers.py +26 -44
- mlrun/serving/server.py +138 -51
- mlrun/serving/serving_wrapper.py +6 -2
- mlrun/serving/states.py +997 -283
- mlrun/serving/steps.py +62 -0
- mlrun/serving/system_steps.py +149 -95
- mlrun/serving/v2_serving.py +9 -10
- mlrun/track/trackers/mlflow_tracker.py +29 -31
- mlrun/utils/helpers.py +292 -94
- mlrun/utils/http.py +9 -2
- mlrun/utils/notifications/notification/base.py +18 -0
- mlrun/utils/notifications/notification/git.py +3 -5
- mlrun/utils/notifications/notification/mail.py +39 -16
- mlrun/utils/notifications/notification/slack.py +2 -4
- mlrun/utils/notifications/notification/webhook.py +2 -5
- mlrun/utils/notifications/notification_pusher.py +3 -3
- mlrun/utils/version/version.json +2 -2
- mlrun/utils/version/version.py +3 -4
- {mlrun-1.10.0rc18.dist-info → mlrun-1.11.0rc16.dist-info}/METADATA +63 -74
- {mlrun-1.10.0rc18.dist-info → mlrun-1.11.0rc16.dist-info}/RECORD +161 -143
- mlrun/api/schemas/__init__.py +0 -259
- mlrun/db/auth_utils.py +0 -152
- mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +0 -344
- mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +0 -75
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connection.py +0 -281
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +0 -1266
- {mlrun-1.10.0rc18.dist-info → mlrun-1.11.0rc16.dist-info}/WHEEL +0 -0
- {mlrun-1.10.0rc18.dist-info → mlrun-1.11.0rc16.dist-info}/entry_points.txt +0 -0
- {mlrun-1.10.0rc18.dist-info → mlrun-1.11.0rc16.dist-info}/licenses/LICENSE +0 -0
- {mlrun-1.10.0rc18.dist-info → mlrun-1.11.0rc16.dist-info}/top_level.txt +0 -0
|
@@ -16,8 +16,11 @@ import asyncio
|
|
|
16
16
|
import copy
|
|
17
17
|
import json
|
|
18
18
|
import typing
|
|
19
|
+
import warnings
|
|
20
|
+
from dataclasses import dataclass
|
|
19
21
|
from datetime import datetime
|
|
20
22
|
from time import sleep
|
|
23
|
+
from urllib.parse import urlparse, urlunparse
|
|
21
24
|
|
|
22
25
|
import inflection
|
|
23
26
|
import nuclio
|
|
@@ -29,12 +32,14 @@ from kubernetes import client
|
|
|
29
32
|
from nuclio.deploy import find_dashboard_url, get_deploy_status
|
|
30
33
|
from nuclio.triggers import V3IOStreamTrigger
|
|
31
34
|
|
|
35
|
+
import mlrun.auth.nuclio
|
|
36
|
+
import mlrun.common.constants
|
|
32
37
|
import mlrun.db
|
|
33
38
|
import mlrun.errors
|
|
34
39
|
import mlrun.k8s_utils
|
|
35
40
|
import mlrun.utils
|
|
36
41
|
import mlrun.utils.helpers
|
|
37
|
-
from mlrun.common.schemas import AuthInfo
|
|
42
|
+
from mlrun.common.schemas import AuthInfo, BatchingSpec
|
|
38
43
|
from mlrun.config import config as mlconf
|
|
39
44
|
from mlrun.errors import err_to_str
|
|
40
45
|
from mlrun.lists import RunList
|
|
@@ -94,6 +99,13 @@ def min_nuclio_versions(*versions):
|
|
|
94
99
|
return decorator
|
|
95
100
|
|
|
96
101
|
|
|
102
|
+
@dataclass
|
|
103
|
+
class AsyncSpec:
|
|
104
|
+
enabled: bool = True
|
|
105
|
+
max_connections: typing.Optional[int] = None
|
|
106
|
+
connection_availability_timeout: typing.Optional[int] = None
|
|
107
|
+
|
|
108
|
+
|
|
97
109
|
class NuclioSpec(KubeResourceSpec):
|
|
98
110
|
_dict_fields = KubeResourceSpec._dict_fields + [
|
|
99
111
|
"min_replicas",
|
|
@@ -111,6 +123,7 @@ class NuclioSpec(KubeResourceSpec):
|
|
|
111
123
|
"service_type",
|
|
112
124
|
"add_templated_ingress_host_mode",
|
|
113
125
|
"disable_default_http_trigger",
|
|
126
|
+
"auth",
|
|
114
127
|
]
|
|
115
128
|
|
|
116
129
|
def __init__(
|
|
@@ -158,6 +171,7 @@ class NuclioSpec(KubeResourceSpec):
|
|
|
158
171
|
graph=None,
|
|
159
172
|
parameters=None,
|
|
160
173
|
track_models=None,
|
|
174
|
+
auth=None,
|
|
161
175
|
):
|
|
162
176
|
super().__init__(
|
|
163
177
|
command=command,
|
|
@@ -214,6 +228,7 @@ class NuclioSpec(KubeResourceSpec):
|
|
|
214
228
|
# When True it will set Nuclio spec.noBaseImagesPull to False (negative logic)
|
|
215
229
|
# indicate that the base image should be pulled from the container registry (not cached)
|
|
216
230
|
self.base_image_pull = False
|
|
231
|
+
self.auth = auth or {}
|
|
217
232
|
|
|
218
233
|
def generate_nuclio_volumes(self):
|
|
219
234
|
nuclio_volumes = []
|
|
@@ -298,29 +313,16 @@ class RemoteRuntime(KubeResource):
|
|
|
298
313
|
return {}
|
|
299
314
|
|
|
300
315
|
raw_config = copy.deepcopy(self.spec.config)
|
|
301
|
-
|
|
302
316
|
for key, value in self.spec.config.items():
|
|
303
317
|
if key.startswith("spec.triggers"):
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
last_key = nested_keys[-1]
|
|
314
|
-
if last_key in target:
|
|
315
|
-
sensitive_field = target[last_key]
|
|
316
|
-
if sensitive_field.startswith(
|
|
317
|
-
mlrun.model.Credentials.secret_reference_prefix
|
|
318
|
-
):
|
|
319
|
-
# already masked
|
|
320
|
-
continue
|
|
321
|
-
target[last_key] = (
|
|
322
|
-
f"{mlrun.model.Credentials.secret_reference_prefix}/spec/triggers/{trigger_name}/{path}"
|
|
323
|
-
)
|
|
318
|
+
# support both types depending on the way how it was set
|
|
319
|
+
# sometimes trigger name is in the same key, sometimes it's nested in the value dict
|
|
320
|
+
if key == "spec.triggers":
|
|
321
|
+
for trigger_name, trigger_config in value.items():
|
|
322
|
+
self._mask_trigger_config(trigger_name, trigger_config)
|
|
323
|
+
else:
|
|
324
|
+
trigger_name = key.split(".")[-1]
|
|
325
|
+
self._mask_trigger_config(trigger_name, value)
|
|
324
326
|
|
|
325
327
|
return raw_config
|
|
326
328
|
|
|
@@ -422,6 +424,18 @@ class RemoteRuntime(KubeResource):
|
|
|
422
424
|
)
|
|
423
425
|
"""
|
|
424
426
|
self.spec.build.source = source
|
|
427
|
+
|
|
428
|
+
code = (
|
|
429
|
+
self.spec.build.functionSourceCode if hasattr(self.spec, "build") else None
|
|
430
|
+
)
|
|
431
|
+
if code:
|
|
432
|
+
# Warn and clear any inline code so the archive is actually used
|
|
433
|
+
logger.warning(
|
|
434
|
+
"Cannot specify both code and source archive. Removing the code so the provided "
|
|
435
|
+
"source archive will be used instead."
|
|
436
|
+
)
|
|
437
|
+
self.spec.build.functionSourceCode = None
|
|
438
|
+
|
|
425
439
|
# update handler in function_handler if needed
|
|
426
440
|
if handler:
|
|
427
441
|
self.spec.function_handler = handler
|
|
@@ -450,7 +464,7 @@ class RemoteRuntime(KubeResource):
|
|
|
450
464
|
|
|
451
465
|
def with_http(
|
|
452
466
|
self,
|
|
453
|
-
workers: typing.Optional[int] =
|
|
467
|
+
workers: typing.Optional[int] = None,
|
|
454
468
|
port: typing.Optional[int] = None,
|
|
455
469
|
host: typing.Optional[str] = None,
|
|
456
470
|
paths: typing.Optional[list[str]] = None,
|
|
@@ -461,6 +475,8 @@ class RemoteRuntime(KubeResource):
|
|
|
461
475
|
trigger_name: typing.Optional[str] = None,
|
|
462
476
|
annotations: typing.Optional[typing.Mapping[str, str]] = None,
|
|
463
477
|
extra_attributes: typing.Optional[typing.Mapping[str, str]] = None,
|
|
478
|
+
batching_spec: typing.Optional[BatchingSpec] = None,
|
|
479
|
+
async_spec: typing.Optional[AsyncSpec] = None,
|
|
464
480
|
):
|
|
465
481
|
"""update/add nuclio HTTP trigger settings
|
|
466
482
|
|
|
@@ -468,7 +484,8 @@ class RemoteRuntime(KubeResource):
|
|
|
468
484
|
if the max time a request will wait for until it will start processing, gateway_timeout must be greater than
|
|
469
485
|
the worker_timeout.
|
|
470
486
|
|
|
471
|
-
:param workers:
|
|
487
|
+
:param workers: Number of worker processes. Defaults to 8 in synchronous mode and
|
|
488
|
+
1 in asynchronous mode. Set to 0 to use Nuclio’s default worker count.
|
|
472
489
|
:param port: TCP port to listen on. by default, nuclio will choose a random port as long as
|
|
473
490
|
the function service is NodePort. if the function service is ClusterIP, the port
|
|
474
491
|
is ignored.
|
|
@@ -482,6 +499,12 @@ class RemoteRuntime(KubeResource):
|
|
|
482
499
|
:param trigger_name: alternative nuclio trigger name
|
|
483
500
|
:param annotations: key/value dict of ingress annotations
|
|
484
501
|
:param extra_attributes: key/value dict of extra nuclio trigger attributes
|
|
502
|
+
:param batching_spec: BatchingSpec object that defines batching configuration.
|
|
503
|
+
By default, batching is disabled.
|
|
504
|
+
|
|
505
|
+
:param async_spec: AsyncSpec object defines async configuration. If number of max connections
|
|
506
|
+
won't be set, the default value will be set to 1000 according to nuclio default.
|
|
507
|
+
|
|
485
508
|
:return: function object (self)
|
|
486
509
|
"""
|
|
487
510
|
if self.disable_default_http_trigger:
|
|
@@ -489,11 +512,15 @@ class RemoteRuntime(KubeResource):
|
|
|
489
512
|
"Adding HTTP trigger despite the default HTTP trigger creation being disabled"
|
|
490
513
|
)
|
|
491
514
|
|
|
515
|
+
if async_spec and async_spec.enabled:
|
|
516
|
+
workers = 1 if workers is None else workers
|
|
517
|
+
else:
|
|
518
|
+
workers = 8 if workers is None else workers
|
|
519
|
+
|
|
492
520
|
annotations = annotations or {}
|
|
493
521
|
if worker_timeout:
|
|
494
522
|
gateway_timeout = gateway_timeout or (worker_timeout + 60)
|
|
495
|
-
|
|
496
|
-
workers = 0
|
|
523
|
+
|
|
497
524
|
if gateway_timeout:
|
|
498
525
|
if worker_timeout and worker_timeout >= gateway_timeout:
|
|
499
526
|
raise ValueError(
|
|
@@ -517,6 +544,28 @@ class RemoteRuntime(KubeResource):
|
|
|
517
544
|
trigger._struct["workerAvailabilityTimeoutMilliseconds"] = (
|
|
518
545
|
worker_timeout
|
|
519
546
|
) * 1000
|
|
547
|
+
|
|
548
|
+
if batching_spec and (
|
|
549
|
+
batching_config := batching_spec.get_nuclio_batch_config()
|
|
550
|
+
):
|
|
551
|
+
if not validate_nuclio_version_compatibility("1.14.0"):
|
|
552
|
+
raise mlrun.errors.MLRunValueError(
|
|
553
|
+
"Batching is only supported on Nuclio 1.14.0 and higher"
|
|
554
|
+
)
|
|
555
|
+
trigger._struct["batch"] = batching_config
|
|
556
|
+
|
|
557
|
+
if async_spec:
|
|
558
|
+
if not validate_nuclio_version_compatibility("1.15.3"):
|
|
559
|
+
raise mlrun.errors.MLRunValueError(
|
|
560
|
+
"Async spec is only supported on Nuclio 1.15.3 and higher"
|
|
561
|
+
)
|
|
562
|
+
if async_spec.enabled:
|
|
563
|
+
trigger._struct["mode"] = "async"
|
|
564
|
+
trigger._struct["async"] = {
|
|
565
|
+
"maxConnectionsNumber": async_spec.max_connections,
|
|
566
|
+
"connectionAvailabilityTimeout": async_spec.connection_availability_timeout,
|
|
567
|
+
}
|
|
568
|
+
|
|
520
569
|
self.add_trigger(trigger_name or "http", trigger)
|
|
521
570
|
return self
|
|
522
571
|
|
|
@@ -827,21 +876,6 @@ class RemoteRuntime(KubeResource):
|
|
|
827
876
|
raise ValueError("function or deploy process not found")
|
|
828
877
|
return self.status.state, text, last_log_timestamp
|
|
829
878
|
|
|
830
|
-
def _get_runtime_env(self):
|
|
831
|
-
# for runtime specific env var enrichment (before deploy)
|
|
832
|
-
runtime_env = {
|
|
833
|
-
"MLRUN_ACTIVE_PROJECT": self.metadata.project or mlconf.active_project,
|
|
834
|
-
}
|
|
835
|
-
if mlconf.httpdb.api_url:
|
|
836
|
-
runtime_env["MLRUN_DBPATH"] = mlconf.httpdb.api_url
|
|
837
|
-
if mlconf.namespace:
|
|
838
|
-
runtime_env["MLRUN_NAMESPACE"] = mlconf.namespace
|
|
839
|
-
if self.metadata.credentials.access_key:
|
|
840
|
-
runtime_env[
|
|
841
|
-
mlrun.common.runtimes.constants.FunctionEnvironmentVariables.auth_session
|
|
842
|
-
] = self.metadata.credentials.access_key
|
|
843
|
-
return runtime_env
|
|
844
|
-
|
|
845
879
|
def _get_serving_spec(self):
|
|
846
880
|
return None
|
|
847
881
|
|
|
@@ -866,8 +900,9 @@ class RemoteRuntime(KubeResource):
|
|
|
866
900
|
if value_from is not None:
|
|
867
901
|
external_source_env_dict[sanitized_env_var.get("name")] = value_from
|
|
868
902
|
|
|
869
|
-
|
|
870
|
-
|
|
903
|
+
envs, external_source_envs = self._generate_runtime_env()
|
|
904
|
+
env_dict.update(envs)
|
|
905
|
+
external_source_env_dict.update(external_source_envs)
|
|
871
906
|
|
|
872
907
|
return env_dict, external_source_env_dict
|
|
873
908
|
|
|
@@ -924,7 +959,7 @@ class RemoteRuntime(KubeResource):
|
|
|
924
959
|
def invoke(
|
|
925
960
|
self,
|
|
926
961
|
path: str,
|
|
927
|
-
body: typing.Optional[typing.Union[str, bytes, dict]] = None,
|
|
962
|
+
body: typing.Optional[typing.Union[str, bytes, dict, list]] = None,
|
|
928
963
|
method: typing.Optional[str] = None,
|
|
929
964
|
headers: typing.Optional[dict] = None,
|
|
930
965
|
force_external_address: bool = False,
|
|
@@ -966,24 +1001,6 @@ class RemoteRuntime(KubeResource):
|
|
|
966
1001
|
self._mock_server = None
|
|
967
1002
|
|
|
968
1003
|
if "://" not in path:
|
|
969
|
-
if not self.status.address:
|
|
970
|
-
# here we check that if default http trigger is disabled, function contains a custom http trigger
|
|
971
|
-
# Otherwise, the function is not invokable, so we raise an error
|
|
972
|
-
if (
|
|
973
|
-
not self._trigger_of_kind_exists(kind="http")
|
|
974
|
-
and self.spec.disable_default_http_trigger
|
|
975
|
-
):
|
|
976
|
-
raise mlrun.errors.MLRunPreconditionFailedError(
|
|
977
|
-
"Default http trigger creation is disabled and there is no any other custom http trigger, "
|
|
978
|
-
"so function can not be invoked via http. Either enable default http trigger creation or "
|
|
979
|
-
"create custom http trigger"
|
|
980
|
-
)
|
|
981
|
-
state, _, _ = self._get_state()
|
|
982
|
-
if state not in ["ready", "scaledToZero"]:
|
|
983
|
-
logger.warning(f"Function is in the {state} state")
|
|
984
|
-
if not self.status.address:
|
|
985
|
-
raise ValueError("no function address first run .deploy()")
|
|
986
|
-
|
|
987
1004
|
path = self._resolve_invocation_url(path, force_external_address)
|
|
988
1005
|
|
|
989
1006
|
if headers is None:
|
|
@@ -997,7 +1014,7 @@ class RemoteRuntime(KubeResource):
|
|
|
997
1014
|
if not http_client_kwargs:
|
|
998
1015
|
http_client_kwargs = {}
|
|
999
1016
|
if body:
|
|
1000
|
-
if isinstance(body,
|
|
1017
|
+
if isinstance(body, str | bytes):
|
|
1001
1018
|
http_client_kwargs["data"] = body
|
|
1002
1019
|
else:
|
|
1003
1020
|
http_client_kwargs["json"] = body
|
|
@@ -1043,6 +1060,9 @@ class RemoteRuntime(KubeResource):
|
|
|
1043
1060
|
sidecar["image"] = image
|
|
1044
1061
|
|
|
1045
1062
|
ports = mlrun.utils.helpers.as_list(ports)
|
|
1063
|
+
if len(ports) > 1:
|
|
1064
|
+
mlrun.runtimes.nuclio.multiple_port_sidecar_is_supported()
|
|
1065
|
+
|
|
1046
1066
|
# according to RFC-6335, port name should be less than 15 characters,
|
|
1047
1067
|
# so we truncate it if needed and leave room for the index
|
|
1048
1068
|
port_name = name[:13].rstrip("-_") if len(name) > 13 else name
|
|
@@ -1068,6 +1088,20 @@ class RemoteRuntime(KubeResource):
|
|
|
1068
1088
|
sidecar["resources"] = self.spec.resources
|
|
1069
1089
|
self.spec.resources = None
|
|
1070
1090
|
|
|
1091
|
+
def set_probe(self, *args, **kwargs):
|
|
1092
|
+
"""Set a Kubernetes probe configuration for the sidecar container
|
|
1093
|
+
|
|
1094
|
+
This method is only available for ApplicationRuntime.
|
|
1095
|
+
"""
|
|
1096
|
+
raise ValueError("set_probe() is only supported for ApplicationRuntime. ")
|
|
1097
|
+
|
|
1098
|
+
def delete_probe(self, *args, **kwargs):
|
|
1099
|
+
"""Delete a Kubernetes probe configuration from the sidecar container
|
|
1100
|
+
|
|
1101
|
+
This method is only available for ApplicationRuntime.
|
|
1102
|
+
"""
|
|
1103
|
+
raise ValueError("delete_probe() is only supported for ApplicationRuntime.")
|
|
1104
|
+
|
|
1071
1105
|
def _set_sidecar(self, name: str) -> dict:
|
|
1072
1106
|
self.spec.config.setdefault("spec.sidecars", [])
|
|
1073
1107
|
sidecars = self.spec.config["spec.sidecars"]
|
|
@@ -1078,6 +1112,79 @@ class RemoteRuntime(KubeResource):
|
|
|
1078
1112
|
sidecars.append({"name": name})
|
|
1079
1113
|
return sidecars[-1]
|
|
1080
1114
|
|
|
1115
|
+
def _mask_trigger_config(self, trigger_name, trigger_config):
|
|
1116
|
+
self._mask_rabbitmq_url(trigger=trigger_config)
|
|
1117
|
+
for path in SENSITIVE_PATHS_IN_TRIGGER_CONFIG:
|
|
1118
|
+
# Handle nested keys
|
|
1119
|
+
nested_keys = path.split("/")
|
|
1120
|
+
target = trigger_config
|
|
1121
|
+
for sub_key in nested_keys[:-1]:
|
|
1122
|
+
target = target.get(sub_key, {})
|
|
1123
|
+
|
|
1124
|
+
last_key = nested_keys[-1]
|
|
1125
|
+
if last_key in target:
|
|
1126
|
+
sensitive_field = target[last_key]
|
|
1127
|
+
if sensitive_field.startswith(
|
|
1128
|
+
mlrun.model.Credentials.secret_reference_prefix
|
|
1129
|
+
):
|
|
1130
|
+
# already masked
|
|
1131
|
+
continue
|
|
1132
|
+
target[last_key] = (
|
|
1133
|
+
f"{mlrun.model.Credentials.secret_reference_prefix}/spec/triggers/{trigger_name}/{path}"
|
|
1134
|
+
)
|
|
1135
|
+
|
|
1136
|
+
@staticmethod
|
|
1137
|
+
def _mask_rabbitmq_url(trigger):
|
|
1138
|
+
"""
|
|
1139
|
+
Extract credentials from RabbitMQ URL and move them to attributes dict.
|
|
1140
|
+
This ensures credentials are not exposed in the URL.
|
|
1141
|
+
"""
|
|
1142
|
+
|
|
1143
|
+
# supported only for nuclio higher than 1.14.15
|
|
1144
|
+
if not validate_nuclio_version_compatibility("1.14.15"):
|
|
1145
|
+
return
|
|
1146
|
+
if not isinstance(trigger, dict):
|
|
1147
|
+
return
|
|
1148
|
+
|
|
1149
|
+
if trigger.get("kind") != "rabbit-mq":
|
|
1150
|
+
return
|
|
1151
|
+
|
|
1152
|
+
url = trigger.get("url")
|
|
1153
|
+
if not url or not isinstance(url, str):
|
|
1154
|
+
return
|
|
1155
|
+
|
|
1156
|
+
try:
|
|
1157
|
+
parsed = urlparse(url)
|
|
1158
|
+
except Exception:
|
|
1159
|
+
raise mlrun.errors.MLRunValueError("invalid URL format")
|
|
1160
|
+
|
|
1161
|
+
# Only process if credentials are present in the URL
|
|
1162
|
+
if not (parsed.username or parsed.password):
|
|
1163
|
+
return
|
|
1164
|
+
|
|
1165
|
+
# Extract credentials
|
|
1166
|
+
username = parsed.username or ""
|
|
1167
|
+
password = parsed.password or ""
|
|
1168
|
+
|
|
1169
|
+
# Reconstruct clean URL
|
|
1170
|
+
hostname = parsed.hostname or ""
|
|
1171
|
+
netloc = f"{hostname}:{parsed.port}" if parsed.port else hostname
|
|
1172
|
+
|
|
1173
|
+
clean_url = urlunparse(
|
|
1174
|
+
(
|
|
1175
|
+
parsed.scheme,
|
|
1176
|
+
netloc,
|
|
1177
|
+
parsed.path,
|
|
1178
|
+
parsed.params,
|
|
1179
|
+
parsed.query,
|
|
1180
|
+
parsed.fragment,
|
|
1181
|
+
)
|
|
1182
|
+
)
|
|
1183
|
+
|
|
1184
|
+
# Update trigger safely
|
|
1185
|
+
trigger["url"] = clean_url
|
|
1186
|
+
trigger.update({"username": username, "password": password})
|
|
1187
|
+
|
|
1081
1188
|
def _trigger_of_kind_exists(self, kind: str) -> bool:
|
|
1082
1189
|
if not self.spec.config:
|
|
1083
1190
|
return False
|
|
@@ -1223,19 +1330,54 @@ class RemoteRuntime(KubeResource):
|
|
|
1223
1330
|
# internal / external invocation urls is a nuclio >= 1.6.x feature
|
|
1224
1331
|
# try to infer the invocation url from the internal and if not exists, use external.
|
|
1225
1332
|
# $$$$ we do not want to use the external invocation url (e.g.: ingress, nodePort, etc.)
|
|
1333
|
+
|
|
1334
|
+
# if none of urls is set, function was deployed with watch=False
|
|
1335
|
+
# and status wasn't fetched with Nuclio
|
|
1336
|
+
# _get_state fetches the state and updates url
|
|
1337
|
+
if (
|
|
1338
|
+
not self.status.address
|
|
1339
|
+
and not self.status.internal_invocation_urls
|
|
1340
|
+
and not self.status.external_invocation_urls
|
|
1341
|
+
):
|
|
1342
|
+
state, _, _ = self._get_state()
|
|
1343
|
+
if state not in ["ready", "scaledToZero"]:
|
|
1344
|
+
logger.warning(f"Function is in the {state} state")
|
|
1345
|
+
|
|
1346
|
+
# prefer internal invocation url if running inside k8s cluster
|
|
1226
1347
|
if (
|
|
1227
1348
|
not force_external_address
|
|
1228
1349
|
and self.status.internal_invocation_urls
|
|
1229
1350
|
and mlrun.k8s_utils.is_running_inside_kubernetes_cluster()
|
|
1230
1351
|
):
|
|
1231
|
-
|
|
1352
|
+
url = mlrun.utils.helpers.join_urls(
|
|
1232
1353
|
f"http://{self.status.internal_invocation_urls[0]}", path
|
|
1233
1354
|
)
|
|
1355
|
+
logger.debug(
|
|
1356
|
+
f"Using internal invocation url {url}. Make sure you have network access to the k8s cluster. "
|
|
1357
|
+
f"Otherwise, set force_external_address to True"
|
|
1358
|
+
)
|
|
1359
|
+
return url
|
|
1234
1360
|
|
|
1235
1361
|
if self.status.external_invocation_urls:
|
|
1236
1362
|
return mlrun.utils.helpers.join_urls(
|
|
1237
1363
|
f"http://{self.status.external_invocation_urls[0]}", path
|
|
1238
1364
|
)
|
|
1365
|
+
|
|
1366
|
+
if not self.status.address:
|
|
1367
|
+
# if there is no address
|
|
1368
|
+
# here we check that if default http trigger is disabled, function contains a custom http trigger
|
|
1369
|
+
# Otherwise, the function is not invokable, so we raise an error
|
|
1370
|
+
if (
|
|
1371
|
+
not self._trigger_of_kind_exists(kind="http")
|
|
1372
|
+
and self.spec.disable_default_http_trigger
|
|
1373
|
+
):
|
|
1374
|
+
raise mlrun.errors.MLRunPreconditionFailedError(
|
|
1375
|
+
"Default http trigger creation is disabled and there is no any other custom http trigger, "
|
|
1376
|
+
"so function can not be invoked via http. Either enable default http trigger creation or "
|
|
1377
|
+
"create custom http trigger"
|
|
1378
|
+
)
|
|
1379
|
+
else:
|
|
1380
|
+
raise ValueError("no function address first run .deploy()")
|
|
1239
1381
|
else:
|
|
1240
1382
|
return mlrun.utils.helpers.join_urls(f"http://{self.status.address}", path)
|
|
1241
1383
|
|
|
@@ -1289,6 +1431,8 @@ class RemoteRuntime(KubeResource):
|
|
|
1289
1431
|
def get_url(
|
|
1290
1432
|
self,
|
|
1291
1433
|
force_external_address: bool = False,
|
|
1434
|
+
# leaving auth_info for BC
|
|
1435
|
+
# TODO: remove in 1.12.0
|
|
1292
1436
|
auth_info: AuthInfo = None,
|
|
1293
1437
|
):
|
|
1294
1438
|
"""
|
|
@@ -1299,13 +1443,12 @@ class RemoteRuntime(KubeResource):
|
|
|
1299
1443
|
|
|
1300
1444
|
:return: returns function's url
|
|
1301
1445
|
"""
|
|
1302
|
-
if
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1446
|
+
if auth_info:
|
|
1447
|
+
warnings.warn(
|
|
1448
|
+
"'auth_info' is deprecated in 1.10.0 and will be removed in 1.12.0.",
|
|
1449
|
+
# TODO: Remove this in 1.12.0
|
|
1450
|
+
FutureWarning,
|
|
1451
|
+
)
|
|
1309
1452
|
return self._resolve_invocation_url("", force_external_address)
|
|
1310
1453
|
|
|
1311
1454
|
@staticmethod
|
|
@@ -1418,7 +1561,7 @@ def get_nuclio_deploy_status(
|
|
|
1418
1561
|
verbose,
|
|
1419
1562
|
resolve_address,
|
|
1420
1563
|
return_function_status=True,
|
|
1421
|
-
auth_info=
|
|
1564
|
+
auth_info=mlrun.auth.nuclio.NuclioAuthInfo.from_auth_info(auth_info),
|
|
1422
1565
|
)
|
|
1423
1566
|
except requests.exceptions.ConnectionError as exc:
|
|
1424
1567
|
mlrun.errors.raise_for_status(
|
|
@@ -1456,3 +1599,10 @@ def enrich_nuclio_function_from_headers(
|
|
|
1456
1599
|
else []
|
|
1457
1600
|
)
|
|
1458
1601
|
func.status.container_image = headers.get("x-mlrun-container-image", "")
|
|
1602
|
+
|
|
1603
|
+
|
|
1604
|
+
@min_nuclio_versions("1.14.14")
|
|
1605
|
+
def multiple_port_sidecar_is_supported():
|
|
1606
|
+
# multiple ports are supported from nuclio version 1.14.14
|
|
1607
|
+
# this method exists only for running the min_nuclio_versions decorator
|
|
1608
|
+
return True
|