mlrun 1.10.0rc16__py3-none-any.whl → 1.10.0rc42__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 +22 -2
- mlrun/artifacts/document.py +6 -1
- mlrun/artifacts/llm_prompt.py +21 -15
- mlrun/artifacts/model.py +3 -3
- mlrun/common/constants.py +9 -0
- mlrun/common/formatters/artifact.py +1 -0
- mlrun/common/model_monitoring/helpers.py +86 -0
- mlrun/common/schemas/__init__.py +2 -0
- mlrun/common/schemas/auth.py +2 -0
- mlrun/common/schemas/function.py +10 -0
- mlrun/common/schemas/hub.py +30 -18
- mlrun/common/schemas/model_monitoring/__init__.py +2 -0
- mlrun/common/schemas/model_monitoring/constants.py +30 -6
- 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/serving.py +3 -0
- mlrun/common/schemas/workflow.py +1 -0
- mlrun/common/secrets.py +22 -1
- mlrun/config.py +32 -10
- mlrun/datastore/__init__.py +11 -3
- mlrun/datastore/azure_blob.py +162 -47
- mlrun/datastore/datastore.py +9 -4
- mlrun/datastore/datastore_profile.py +61 -5
- mlrun/datastore/model_provider/huggingface_provider.py +363 -0
- mlrun/datastore/model_provider/mock_model_provider.py +87 -0
- mlrun/datastore/model_provider/model_provider.py +211 -74
- mlrun/datastore/model_provider/openai_provider.py +243 -71
- mlrun/datastore/s3.py +24 -2
- mlrun/datastore/storeytargets.py +2 -3
- mlrun/datastore/utils.py +15 -3
- mlrun/db/base.py +27 -19
- mlrun/db/httpdb.py +57 -48
- mlrun/db/nopdb.py +25 -10
- mlrun/execution.py +55 -13
- mlrun/hub/__init__.py +15 -0
- mlrun/hub/module.py +181 -0
- mlrun/k8s_utils.py +105 -16
- mlrun/launcher/base.py +13 -6
- mlrun/launcher/local.py +2 -0
- mlrun/model.py +9 -3
- mlrun/model_monitoring/api.py +66 -27
- mlrun/model_monitoring/applications/__init__.py +1 -1
- mlrun/model_monitoring/applications/base.py +372 -136
- mlrun/model_monitoring/applications/context.py +2 -4
- mlrun/model_monitoring/applications/results.py +4 -7
- mlrun/model_monitoring/controller.py +239 -101
- mlrun/model_monitoring/db/_schedules.py +36 -13
- mlrun/model_monitoring/db/_stats.py +4 -3
- mlrun/model_monitoring/db/tsdb/base.py +29 -9
- mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +4 -5
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +154 -50
- mlrun/model_monitoring/db/tsdb/tdengine/writer_graph_steps.py +51 -0
- mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +17 -4
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +245 -51
- mlrun/model_monitoring/helpers.py +28 -5
- mlrun/model_monitoring/stream_processing.py +45 -14
- mlrun/model_monitoring/writer.py +220 -1
- mlrun/platforms/__init__.py +3 -2
- mlrun/platforms/iguazio.py +7 -3
- mlrun/projects/operations.py +6 -1
- mlrun/projects/pipelines.py +2 -2
- mlrun/projects/project.py +128 -45
- mlrun/run.py +94 -17
- mlrun/runtimes/__init__.py +18 -0
- mlrun/runtimes/base.py +14 -6
- mlrun/runtimes/daskjob.py +1 -0
- mlrun/runtimes/local.py +5 -2
- mlrun/runtimes/mounts.py +20 -2
- mlrun/runtimes/nuclio/__init__.py +1 -0
- mlrun/runtimes/nuclio/application/application.py +147 -17
- mlrun/runtimes/nuclio/function.py +70 -27
- mlrun/runtimes/nuclio/serving.py +85 -4
- mlrun/runtimes/pod.py +213 -21
- mlrun/runtimes/utils.py +49 -9
- mlrun/secrets.py +54 -13
- mlrun/serving/remote.py +79 -6
- mlrun/serving/routers.py +23 -41
- mlrun/serving/server.py +211 -40
- mlrun/serving/states.py +536 -156
- mlrun/serving/steps.py +62 -0
- mlrun/serving/system_steps.py +136 -81
- mlrun/serving/v2_serving.py +9 -10
- mlrun/utils/helpers.py +212 -82
- mlrun/utils/logger.py +3 -1
- mlrun/utils/notifications/notification/base.py +18 -0
- mlrun/utils/notifications/notification/git.py +2 -4
- mlrun/utils/notifications/notification/slack.py +2 -4
- mlrun/utils/notifications/notification/webhook.py +2 -5
- mlrun/utils/notifications/notification_pusher.py +1 -1
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc42.dist-info}/METADATA +44 -45
- {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc42.dist-info}/RECORD +97 -92
- mlrun/api/schemas/__init__.py +0 -259
- {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc42.dist-info}/WHEEL +0 -0
- {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc42.dist-info}/entry_points.txt +0 -0
- {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc42.dist-info}/licenses/LICENSE +0 -0
- {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc42.dist-info}/top_level.txt +0 -0
mlrun/runtimes/base.py
CHANGED
|
@@ -142,9 +142,6 @@ class FunctionSpec(ModelObj):
|
|
|
142
142
|
def build(self, build):
|
|
143
143
|
self._build = self._verify_dict(build, "build", ImageBuilder)
|
|
144
144
|
|
|
145
|
-
def enrich_function_preemption_spec(self):
|
|
146
|
-
pass
|
|
147
|
-
|
|
148
145
|
def validate_service_account(self, allowed_service_accounts):
|
|
149
146
|
pass
|
|
150
147
|
|
|
@@ -379,7 +376,12 @@ class BaseRuntime(ModelObj):
|
|
|
379
376
|
This ensures latest code changes are executed. This argument must be used in
|
|
380
377
|
conjunction with the local=True argument.
|
|
381
378
|
:param output_path: Default artifact output path.
|
|
382
|
-
:param retry: Retry configuration for the run, can be a dict or an instance of
|
|
379
|
+
:param retry: Retry configuration for the run, can be a dict or an instance of
|
|
380
|
+
:py:class:`~mlrun.model.Retry`.
|
|
381
|
+
The `count` field in the `Retry` object specifies the number of retry attempts.
|
|
382
|
+
If `count=0`, the run will not be retried.
|
|
383
|
+
The `backoff` field specifies the retry backoff strategy between retry attempts.
|
|
384
|
+
If not provided, the default backoff delay is 30 seconds.
|
|
383
385
|
:return: Run context object (RunObject) with run metadata, results and status
|
|
384
386
|
"""
|
|
385
387
|
if artifact_path or out_path:
|
|
@@ -391,6 +393,7 @@ class BaseRuntime(ModelObj):
|
|
|
391
393
|
FutureWarning,
|
|
392
394
|
)
|
|
393
395
|
output_path = output_path or out_path or artifact_path
|
|
396
|
+
|
|
394
397
|
launcher = mlrun.launcher.factory.LauncherFactory().create_launcher(
|
|
395
398
|
self._is_remote, local=local, **launcher_kwargs
|
|
396
399
|
)
|
|
@@ -446,15 +449,20 @@ class BaseRuntime(ModelObj):
|
|
|
446
449
|
:param runobj: Run context object (RunObject) with run metadata and status
|
|
447
450
|
:return: Dictionary with all the variables that could be parsed
|
|
448
451
|
"""
|
|
452
|
+
active_project = self.metadata.project or config.active_project
|
|
449
453
|
runtime_env = {
|
|
450
|
-
|
|
454
|
+
mlrun_constants.MLRUN_ACTIVE_PROJECT: active_project,
|
|
455
|
+
# TODO: Remove this in 1.12.0 as MLRUN_DEFAULT_PROJECT is deprecated and should not be injected anymore
|
|
456
|
+
"MLRUN_DEFAULT_PROJECT": active_project,
|
|
451
457
|
}
|
|
452
458
|
if runobj:
|
|
453
459
|
runtime_env["MLRUN_EXEC_CONFIG"] = runobj.to_json(
|
|
454
460
|
exclude_notifications_params=True
|
|
455
461
|
)
|
|
456
462
|
if runobj.metadata.project:
|
|
457
|
-
runtime_env[
|
|
463
|
+
runtime_env[mlrun_constants.MLRUN_ACTIVE_PROJECT] = (
|
|
464
|
+
runobj.metadata.project
|
|
465
|
+
)
|
|
458
466
|
if runobj.spec.verbose:
|
|
459
467
|
runtime_env["MLRUN_LOG_LEVEL"] = "DEBUG"
|
|
460
468
|
if config.httpdb.api_url:
|
mlrun/runtimes/daskjob.py
CHANGED
mlrun/runtimes/local.py
CHANGED
|
@@ -29,12 +29,12 @@ from os import environ, remove
|
|
|
29
29
|
from pathlib import Path
|
|
30
30
|
from subprocess import PIPE, Popen
|
|
31
31
|
from sys import executable
|
|
32
|
+
from typing import Optional
|
|
32
33
|
|
|
33
34
|
from nuclio import Event
|
|
34
35
|
|
|
35
36
|
import mlrun
|
|
36
37
|
import mlrun.common.constants as mlrun_constants
|
|
37
|
-
import mlrun.common.runtimes.constants
|
|
38
38
|
from mlrun.lists import RunList
|
|
39
39
|
|
|
40
40
|
from ..errors import err_to_str
|
|
@@ -201,9 +201,12 @@ class LocalRuntime(BaseRuntime, ParallelRunner):
|
|
|
201
201
|
kind = "local"
|
|
202
202
|
_is_remote = False
|
|
203
203
|
|
|
204
|
-
def to_job(self, image=""):
|
|
204
|
+
def to_job(self, image="", func_name: Optional[str] = None):
|
|
205
205
|
struct = self.to_dict()
|
|
206
206
|
obj = KubejobRuntime.from_dict(struct)
|
|
207
|
+
obj.kind = "job" # Ensure kind is set to 'job' for KubejobRuntime
|
|
208
|
+
if func_name:
|
|
209
|
+
obj.metadata.name = func_name
|
|
207
210
|
if image:
|
|
208
211
|
obj.spec.image = image
|
|
209
212
|
return obj
|
mlrun/runtimes/mounts.py
CHANGED
|
@@ -14,8 +14,11 @@
|
|
|
14
14
|
|
|
15
15
|
import os
|
|
16
16
|
import typing
|
|
17
|
+
import warnings
|
|
17
18
|
from collections import namedtuple
|
|
18
19
|
|
|
20
|
+
import mlrun.common.secrets
|
|
21
|
+
import mlrun.errors
|
|
19
22
|
from mlrun.config import config
|
|
20
23
|
from mlrun.config import config as mlconf
|
|
21
24
|
from mlrun.errors import MLRunInvalidArgumentError
|
|
@@ -247,10 +250,22 @@ def mount_s3(
|
|
|
247
250
|
def _use_s3_cred(runtime: "KubeResource"):
|
|
248
251
|
_access_key = aws_access_key or os.environ.get(prefix + "AWS_ACCESS_KEY_ID")
|
|
249
252
|
_secret_key = aws_secret_key or os.environ.get(prefix + "AWS_SECRET_ACCESS_KEY")
|
|
250
|
-
|
|
253
|
+
|
|
254
|
+
# Check for endpoint URL with backward compatibility
|
|
255
|
+
_endpoint_url = endpoint_url or os.environ.get(prefix + "AWS_ENDPOINT_URL_S3")
|
|
256
|
+
if not _endpoint_url:
|
|
257
|
+
# Check for deprecated environment variable
|
|
258
|
+
_endpoint_url = os.environ.get(prefix + "S3_ENDPOINT_URL")
|
|
259
|
+
if _endpoint_url:
|
|
260
|
+
warnings.warn(
|
|
261
|
+
"S3_ENDPOINT_URL is deprecated in 1.10.0 and will be removed in 1.12.0, "
|
|
262
|
+
"use AWS_ENDPOINT_URL_S3 instead.",
|
|
263
|
+
# TODO: Remove this in 1.12.0
|
|
264
|
+
FutureWarning,
|
|
265
|
+
)
|
|
251
266
|
|
|
252
267
|
if _endpoint_url:
|
|
253
|
-
runtime.set_env(prefix + "
|
|
268
|
+
runtime.set_env(prefix + "AWS_ENDPOINT_URL_S3", _endpoint_url)
|
|
254
269
|
if aws_region:
|
|
255
270
|
runtime.set_env(prefix + "AWS_REGION", aws_region)
|
|
256
271
|
if non_anonymous:
|
|
@@ -399,6 +414,9 @@ def mount_secret(
|
|
|
399
414
|
the specified paths, and unlisted keys will not be
|
|
400
415
|
present."""
|
|
401
416
|
|
|
417
|
+
if secret_name:
|
|
418
|
+
mlrun.common.secrets.validate_not_forbidden_secret(secret_name.strip())
|
|
419
|
+
|
|
402
420
|
def _mount_secret(runtime: "KubeResource"):
|
|
403
421
|
# Define the secret volume source
|
|
404
422
|
secret_volume_source = {
|
|
@@ -16,6 +16,7 @@ from .serving import ServingRuntime, new_v2_model_server # noqa
|
|
|
16
16
|
from .nuclio import nuclio_init_hook # noqa
|
|
17
17
|
from .function import (
|
|
18
18
|
min_nuclio_versions,
|
|
19
|
+
multiple_port_sidecar_is_supported,
|
|
19
20
|
RemoteRuntime,
|
|
20
21
|
) # noqa
|
|
21
22
|
from .api_gateway import APIGateway
|
|
@@ -22,19 +22,23 @@ import mlrun.errors
|
|
|
22
22
|
import mlrun.run
|
|
23
23
|
from mlrun.common.runtimes.constants import NuclioIngressAddTemplatedIngressModes
|
|
24
24
|
from mlrun.runtimes import RemoteRuntime
|
|
25
|
-
from mlrun.runtimes.nuclio import
|
|
25
|
+
from mlrun.runtimes.nuclio import (
|
|
26
|
+
min_nuclio_versions,
|
|
27
|
+
multiple_port_sidecar_is_supported,
|
|
28
|
+
)
|
|
26
29
|
from mlrun.runtimes.nuclio.api_gateway import (
|
|
27
30
|
APIGateway,
|
|
28
31
|
APIGatewayMetadata,
|
|
29
32
|
APIGatewaySpec,
|
|
30
33
|
)
|
|
31
34
|
from mlrun.runtimes.nuclio.function import NuclioSpec, NuclioStatus
|
|
32
|
-
from mlrun.utils import logger, update_in
|
|
35
|
+
from mlrun.utils import is_valid_port, logger, update_in
|
|
33
36
|
|
|
34
37
|
|
|
35
38
|
class ApplicationSpec(NuclioSpec):
|
|
36
39
|
_dict_fields = NuclioSpec._dict_fields + [
|
|
37
40
|
"internal_application_port",
|
|
41
|
+
"application_ports",
|
|
38
42
|
]
|
|
39
43
|
|
|
40
44
|
def __init__(
|
|
@@ -78,7 +82,12 @@ class ApplicationSpec(NuclioSpec):
|
|
|
78
82
|
add_templated_ingress_host_mode=None,
|
|
79
83
|
state_thresholds=None,
|
|
80
84
|
disable_default_http_trigger=None,
|
|
85
|
+
serving_spec=None,
|
|
86
|
+
graph=None,
|
|
87
|
+
parameters=None,
|
|
88
|
+
track_models=None,
|
|
81
89
|
internal_application_port=None,
|
|
90
|
+
application_ports=None,
|
|
82
91
|
):
|
|
83
92
|
super().__init__(
|
|
84
93
|
command=command,
|
|
@@ -118,6 +127,10 @@ class ApplicationSpec(NuclioSpec):
|
|
|
118
127
|
security_context=security_context,
|
|
119
128
|
service_type=service_type,
|
|
120
129
|
add_templated_ingress_host_mode=add_templated_ingress_host_mode,
|
|
130
|
+
serving_spec=serving_spec,
|
|
131
|
+
graph=graph,
|
|
132
|
+
parameters=parameters,
|
|
133
|
+
track_models=track_models,
|
|
121
134
|
state_thresholds=state_thresholds,
|
|
122
135
|
disable_default_http_trigger=disable_default_http_trigger,
|
|
123
136
|
)
|
|
@@ -126,11 +139,60 @@ class ApplicationSpec(NuclioSpec):
|
|
|
126
139
|
self.min_replicas = min_replicas or 1
|
|
127
140
|
self.max_replicas = max_replicas or 1
|
|
128
141
|
|
|
142
|
+
# initializing internal application port and application ports
|
|
143
|
+
self._internal_application_port = None
|
|
144
|
+
self._application_ports = []
|
|
145
|
+
|
|
146
|
+
application_ports = application_ports or []
|
|
147
|
+
|
|
148
|
+
# if internal_application_port is not provided, use the first application port
|
|
149
|
+
if not internal_application_port and len(application_ports) > 0:
|
|
150
|
+
internal_application_port = application_ports[0]
|
|
151
|
+
|
|
152
|
+
# the port of application sidecar to which traffic will be routed from a nuclio function
|
|
129
153
|
self.internal_application_port = (
|
|
130
154
|
internal_application_port
|
|
131
155
|
or mlrun.mlconf.function.application.default_sidecar_internal_port
|
|
132
156
|
)
|
|
133
157
|
|
|
158
|
+
# all exposed ports by the application sidecar
|
|
159
|
+
self.application_ports = application_ports
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def application_ports(self):
|
|
163
|
+
return self._application_ports
|
|
164
|
+
|
|
165
|
+
@application_ports.setter
|
|
166
|
+
def application_ports(self, ports):
|
|
167
|
+
"""
|
|
168
|
+
Set the application ports for the application sidecar.
|
|
169
|
+
The internal application port is always included and always first.
|
|
170
|
+
"""
|
|
171
|
+
# Handle None / single int
|
|
172
|
+
if ports is None:
|
|
173
|
+
ports = []
|
|
174
|
+
elif isinstance(ports, int):
|
|
175
|
+
ports = [ports]
|
|
176
|
+
elif not isinstance(ports, list):
|
|
177
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
178
|
+
"Application ports must be a list of integers"
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
# Validate and normalize
|
|
182
|
+
cleaned_ports = []
|
|
183
|
+
for port in ports:
|
|
184
|
+
is_valid_port(port, raise_on_error=True)
|
|
185
|
+
if port != self.internal_application_port:
|
|
186
|
+
cleaned_ports.append(port)
|
|
187
|
+
|
|
188
|
+
application_ports = [self.internal_application_port] + cleaned_ports
|
|
189
|
+
|
|
190
|
+
# ensure multiple ports are supported in Nuclio
|
|
191
|
+
if len(application_ports) > 1:
|
|
192
|
+
multiple_port_sidecar_is_supported()
|
|
193
|
+
|
|
194
|
+
self._application_ports = application_ports
|
|
195
|
+
|
|
134
196
|
@property
|
|
135
197
|
def internal_application_port(self):
|
|
136
198
|
return self._internal_application_port
|
|
@@ -138,10 +200,20 @@ class ApplicationSpec(NuclioSpec):
|
|
|
138
200
|
@internal_application_port.setter
|
|
139
201
|
def internal_application_port(self, port):
|
|
140
202
|
port = int(port)
|
|
141
|
-
|
|
142
|
-
raise ValueError("Port must be in the range 0-65535")
|
|
203
|
+
is_valid_port(port, raise_on_error=True)
|
|
143
204
|
self._internal_application_port = port
|
|
144
205
|
|
|
206
|
+
# If when internal application port is being set, length of self._application_ports is 1,
|
|
207
|
+
# it means that it consist of [old_port] only
|
|
208
|
+
# so in this case, we rewrite the list completely, by setting value to [new_value]
|
|
209
|
+
if len(self.application_ports) == 1:
|
|
210
|
+
self._application_ports = [port]
|
|
211
|
+
return
|
|
212
|
+
|
|
213
|
+
# when setting new internal application port, ensure that it is included in the application ports
|
|
214
|
+
# it just triggers setter logic, so setting to the same value is a no-op
|
|
215
|
+
self.application_ports = self._application_ports
|
|
216
|
+
|
|
145
217
|
|
|
146
218
|
class ApplicationStatus(NuclioStatus):
|
|
147
219
|
def __init__(
|
|
@@ -222,6 +294,32 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
222
294
|
def set_internal_application_port(self, port: int):
|
|
223
295
|
self.spec.internal_application_port = port
|
|
224
296
|
|
|
297
|
+
def with_sidecar(
|
|
298
|
+
self,
|
|
299
|
+
name: typing.Optional[str] = None,
|
|
300
|
+
image: typing.Optional[str] = None,
|
|
301
|
+
ports: typing.Optional[typing.Union[int, list[int]]] = None,
|
|
302
|
+
command: typing.Optional[str] = None,
|
|
303
|
+
args: typing.Optional[list[str]] = None,
|
|
304
|
+
):
|
|
305
|
+
# wraps with_sidecar just to set the application ports
|
|
306
|
+
super().with_sidecar(
|
|
307
|
+
name=name,
|
|
308
|
+
image=image,
|
|
309
|
+
ports=ports,
|
|
310
|
+
command=command,
|
|
311
|
+
args=args,
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
if ports:
|
|
315
|
+
if self.spec.internal_application_port != ports[0]:
|
|
316
|
+
logger.info(
|
|
317
|
+
f"Setting internal application port to the first port from the sidecar: {ports[0]}. "
|
|
318
|
+
f"If this is not intended, please set the internal_application_port explicitly."
|
|
319
|
+
)
|
|
320
|
+
self.spec.internal_application_port = ports[0]
|
|
321
|
+
self.spec.application_ports = ports
|
|
322
|
+
|
|
225
323
|
def pre_deploy_validation(self):
|
|
226
324
|
super().pre_deploy_validation()
|
|
227
325
|
if not self.spec.config.get("spec.sidecars"):
|
|
@@ -302,6 +400,7 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
302
400
|
|
|
303
401
|
:return: The default API gateway URL if created or True if the function is ready (deployed)
|
|
304
402
|
"""
|
|
403
|
+
|
|
305
404
|
if (self.requires_build() and not self.spec.image) or force_build:
|
|
306
405
|
self._fill_credentials()
|
|
307
406
|
self._build_application_image(
|
|
@@ -315,8 +414,7 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
315
414
|
show_on_failure=show_on_failure,
|
|
316
415
|
)
|
|
317
416
|
|
|
318
|
-
|
|
319
|
-
self._ensure_reverse_proxy_configurations(self)
|
|
417
|
+
self._ensure_reverse_proxy_configurations()
|
|
320
418
|
self._configure_application_sidecar()
|
|
321
419
|
|
|
322
420
|
# We only allow accessing the application via the API Gateway
|
|
@@ -431,6 +529,7 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
431
529
|
ssl_redirect: typing.Optional[bool] = None,
|
|
432
530
|
set_as_default: bool = False,
|
|
433
531
|
gateway_timeout: typing.Optional[int] = None,
|
|
532
|
+
port: typing.Optional[int] = None,
|
|
434
533
|
):
|
|
435
534
|
"""
|
|
436
535
|
Create the application API gateway. Once the application is deployed, the API gateway can be created.
|
|
@@ -447,6 +546,8 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
447
546
|
:param set_as_default: Set the API gateway as the default for the application (`status.api_gateway`)
|
|
448
547
|
:param gateway_timeout: nginx ingress timeout in sec (request timeout, when will the gateway return an
|
|
449
548
|
error)
|
|
549
|
+
:param port: The API gateway port, used only when direct_port_access=True
|
|
550
|
+
|
|
450
551
|
:return: The API gateway URL
|
|
451
552
|
"""
|
|
452
553
|
if not name:
|
|
@@ -467,7 +568,15 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
467
568
|
"Authentication credentials not provided"
|
|
468
569
|
)
|
|
469
570
|
|
|
470
|
-
|
|
571
|
+
if direct_port_access and port:
|
|
572
|
+
logger.warning(
|
|
573
|
+
"Ignoring 'port' because 'direct_port_access' is enabled. "
|
|
574
|
+
"The 'port' setting is only applicable when 'direct_port_access' is disabled."
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
ports = (
|
|
578
|
+
port or self.spec.internal_application_port if direct_port_access else []
|
|
579
|
+
)
|
|
471
580
|
|
|
472
581
|
api_gateway = APIGateway(
|
|
473
582
|
APIGatewayMetadata(
|
|
@@ -595,6 +704,12 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
595
704
|
"""
|
|
596
705
|
# create a function that includes only the reverse proxy, without the application
|
|
597
706
|
|
|
707
|
+
if not mlrun.get_current_project(silent=True):
|
|
708
|
+
raise mlrun.errors.MLRunMissingProjectError(
|
|
709
|
+
"An active project is required to run deploy_reverse_proxy_image(). "
|
|
710
|
+
"Use `mlrun.get_or_create_project()` or set an active project first."
|
|
711
|
+
)
|
|
712
|
+
|
|
598
713
|
reverse_proxy_func = mlrun.run.new_function(
|
|
599
714
|
name="reverse-proxy-temp", kind="remote"
|
|
600
715
|
)
|
|
@@ -684,27 +799,42 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
684
799
|
with_mlrun=with_mlrun,
|
|
685
800
|
)
|
|
686
801
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
802
|
+
def _ensure_reverse_proxy_configurations(self):
|
|
803
|
+
# If an HTTP trigger already exists in the spec,
|
|
804
|
+
# it means the user explicitly defined a custom configuration,
|
|
805
|
+
# so, skip automatic creation.
|
|
806
|
+
skip_http_trigger_creation = False
|
|
807
|
+
for key, value in self.spec.config.items():
|
|
808
|
+
if key.startswith("spec.triggers"):
|
|
809
|
+
if isinstance(value, dict):
|
|
810
|
+
if value.get("kind") == "http":
|
|
811
|
+
skip_http_trigger_creation = True
|
|
812
|
+
break
|
|
813
|
+
if not skip_http_trigger_creation:
|
|
814
|
+
self.with_http(
|
|
815
|
+
workers=mlrun.mlconf.function.application.default_worker_number,
|
|
816
|
+
trigger_name="application-http",
|
|
817
|
+
)
|
|
818
|
+
|
|
819
|
+
if self.spec.build.functionSourceCode or self.status.container_image:
|
|
690
820
|
return
|
|
691
821
|
|
|
692
822
|
filename, handler = ApplicationRuntime.get_filename_and_handler()
|
|
693
823
|
name, spec, code = nuclio.build_file(
|
|
694
824
|
filename,
|
|
695
|
-
name=
|
|
825
|
+
name=self.metadata.name,
|
|
696
826
|
handler=handler,
|
|
697
827
|
)
|
|
698
|
-
|
|
699
|
-
|
|
828
|
+
self.spec.function_handler = mlrun.utils.get_in(spec, "spec.handler")
|
|
829
|
+
self.spec.build.functionSourceCode = mlrun.utils.get_in(
|
|
700
830
|
spec, "spec.build.functionSourceCode"
|
|
701
831
|
)
|
|
702
|
-
|
|
832
|
+
self.spec.nuclio_runtime = mlrun.utils.get_in(spec, "spec.runtime")
|
|
703
833
|
|
|
704
834
|
# default the reverse proxy logger level to info
|
|
705
835
|
logger_sinks_key = "spec.loggerSinks"
|
|
706
|
-
if not
|
|
707
|
-
|
|
836
|
+
if not self.spec.config.get(logger_sinks_key):
|
|
837
|
+
self.set_config(
|
|
708
838
|
logger_sinks_key, [{"level": "info", "sink": "myStdoutLoggerSink"}]
|
|
709
839
|
)
|
|
710
840
|
|
|
@@ -728,7 +858,7 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
728
858
|
self.with_sidecar(
|
|
729
859
|
name=self.status.sidecar_name,
|
|
730
860
|
image=self.status.application_image,
|
|
731
|
-
ports=self.spec.
|
|
861
|
+
ports=self.spec.application_ports,
|
|
732
862
|
command=self.spec.command,
|
|
733
863
|
args=self.spec.args,
|
|
734
864
|
)
|
|
@@ -16,6 +16,7 @@ import asyncio
|
|
|
16
16
|
import copy
|
|
17
17
|
import json
|
|
18
18
|
import typing
|
|
19
|
+
import warnings
|
|
19
20
|
from datetime import datetime
|
|
20
21
|
from time import sleep
|
|
21
22
|
|
|
@@ -29,6 +30,7 @@ from kubernetes import client
|
|
|
29
30
|
from nuclio.deploy import find_dashboard_url, get_deploy_status
|
|
30
31
|
from nuclio.triggers import V3IOStreamTrigger
|
|
31
32
|
|
|
33
|
+
import mlrun.common.constants
|
|
32
34
|
import mlrun.db
|
|
33
35
|
import mlrun.errors
|
|
34
36
|
import mlrun.k8s_utils
|
|
@@ -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
|
|
@@ -830,7 +844,8 @@ class RemoteRuntime(KubeResource):
|
|
|
830
844
|
def _get_runtime_env(self):
|
|
831
845
|
# for runtime specific env var enrichment (before deploy)
|
|
832
846
|
runtime_env = {
|
|
833
|
-
|
|
847
|
+
mlrun.common.constants.MLRUN_ACTIVE_PROJECT: self.metadata.project
|
|
848
|
+
or mlconf.active_project,
|
|
834
849
|
}
|
|
835
850
|
if mlconf.httpdb.api_url:
|
|
836
851
|
runtime_env["MLRUN_DBPATH"] = mlconf.httpdb.api_url
|
|
@@ -966,24 +981,6 @@ class RemoteRuntime(KubeResource):
|
|
|
966
981
|
self._mock_server = None
|
|
967
982
|
|
|
968
983
|
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
984
|
path = self._resolve_invocation_url(path, force_external_address)
|
|
988
985
|
|
|
989
986
|
if headers is None:
|
|
@@ -1043,6 +1040,9 @@ class RemoteRuntime(KubeResource):
|
|
|
1043
1040
|
sidecar["image"] = image
|
|
1044
1041
|
|
|
1045
1042
|
ports = mlrun.utils.helpers.as_list(ports)
|
|
1043
|
+
if len(ports) > 1:
|
|
1044
|
+
mlrun.runtimes.nuclio.multiple_port_sidecar_is_supported()
|
|
1045
|
+
|
|
1046
1046
|
# according to RFC-6335, port name should be less than 15 characters,
|
|
1047
1047
|
# so we truncate it if needed and leave room for the index
|
|
1048
1048
|
port_name = name[:13].rstrip("-_") if len(name) > 13 else name
|
|
@@ -1223,19 +1223,54 @@ class RemoteRuntime(KubeResource):
|
|
|
1223
1223
|
# internal / external invocation urls is a nuclio >= 1.6.x feature
|
|
1224
1224
|
# try to infer the invocation url from the internal and if not exists, use external.
|
|
1225
1225
|
# $$$$ we do not want to use the external invocation url (e.g.: ingress, nodePort, etc.)
|
|
1226
|
+
|
|
1227
|
+
# if none of urls is set, function was deployed with watch=False
|
|
1228
|
+
# and status wasn't fetched with Nuclio
|
|
1229
|
+
# _get_state fetches the state and updates url
|
|
1230
|
+
if (
|
|
1231
|
+
not self.status.address
|
|
1232
|
+
and not self.status.internal_invocation_urls
|
|
1233
|
+
and not self.status.external_invocation_urls
|
|
1234
|
+
):
|
|
1235
|
+
state, _, _ = self._get_state()
|
|
1236
|
+
if state not in ["ready", "scaledToZero"]:
|
|
1237
|
+
logger.warning(f"Function is in the {state} state")
|
|
1238
|
+
|
|
1239
|
+
# prefer internal invocation url if running inside k8s cluster
|
|
1226
1240
|
if (
|
|
1227
1241
|
not force_external_address
|
|
1228
1242
|
and self.status.internal_invocation_urls
|
|
1229
1243
|
and mlrun.k8s_utils.is_running_inside_kubernetes_cluster()
|
|
1230
1244
|
):
|
|
1231
|
-
|
|
1245
|
+
url = mlrun.utils.helpers.join_urls(
|
|
1232
1246
|
f"http://{self.status.internal_invocation_urls[0]}", path
|
|
1233
1247
|
)
|
|
1248
|
+
logger.debug(
|
|
1249
|
+
f"Using internal invocation url {url}. Make sure you have network access to the k8s cluster. "
|
|
1250
|
+
f"Otherwise, set force_external_address to True"
|
|
1251
|
+
)
|
|
1252
|
+
return url
|
|
1234
1253
|
|
|
1235
1254
|
if self.status.external_invocation_urls:
|
|
1236
1255
|
return mlrun.utils.helpers.join_urls(
|
|
1237
1256
|
f"http://{self.status.external_invocation_urls[0]}", path
|
|
1238
1257
|
)
|
|
1258
|
+
|
|
1259
|
+
if not self.status.address:
|
|
1260
|
+
# if there is no address
|
|
1261
|
+
# here we check that if default http trigger is disabled, function contains a custom http trigger
|
|
1262
|
+
# Otherwise, the function is not invokable, so we raise an error
|
|
1263
|
+
if (
|
|
1264
|
+
not self._trigger_of_kind_exists(kind="http")
|
|
1265
|
+
and self.spec.disable_default_http_trigger
|
|
1266
|
+
):
|
|
1267
|
+
raise mlrun.errors.MLRunPreconditionFailedError(
|
|
1268
|
+
"Default http trigger creation is disabled and there is no any other custom http trigger, "
|
|
1269
|
+
"so function can not be invoked via http. Either enable default http trigger creation or "
|
|
1270
|
+
"create custom http trigger"
|
|
1271
|
+
)
|
|
1272
|
+
else:
|
|
1273
|
+
raise ValueError("no function address first run .deploy()")
|
|
1239
1274
|
else:
|
|
1240
1275
|
return mlrun.utils.helpers.join_urls(f"http://{self.status.address}", path)
|
|
1241
1276
|
|
|
@@ -1289,6 +1324,8 @@ class RemoteRuntime(KubeResource):
|
|
|
1289
1324
|
def get_url(
|
|
1290
1325
|
self,
|
|
1291
1326
|
force_external_address: bool = False,
|
|
1327
|
+
# leaving auth_info for BC
|
|
1328
|
+
# TODO: remove in 1.12.0
|
|
1292
1329
|
auth_info: AuthInfo = None,
|
|
1293
1330
|
):
|
|
1294
1331
|
"""
|
|
@@ -1299,13 +1336,12 @@ class RemoteRuntime(KubeResource):
|
|
|
1299
1336
|
|
|
1300
1337
|
:return: returns function's url
|
|
1301
1338
|
"""
|
|
1302
|
-
if
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1339
|
+
if auth_info:
|
|
1340
|
+
warnings.warn(
|
|
1341
|
+
"'auth_info' is deprecated in 1.10.0 and will be removed in 1.12.0.",
|
|
1342
|
+
# TODO: Remove this in 1.12.0
|
|
1343
|
+
FutureWarning,
|
|
1344
|
+
)
|
|
1309
1345
|
return self._resolve_invocation_url("", force_external_address)
|
|
1310
1346
|
|
|
1311
1347
|
@staticmethod
|
|
@@ -1456,3 +1492,10 @@ def enrich_nuclio_function_from_headers(
|
|
|
1456
1492
|
else []
|
|
1457
1493
|
)
|
|
1458
1494
|
func.status.container_image = headers.get("x-mlrun-container-image", "")
|
|
1495
|
+
|
|
1496
|
+
|
|
1497
|
+
@min_nuclio_versions("1.14.14")
|
|
1498
|
+
def multiple_port_sidecar_is_supported():
|
|
1499
|
+
# multiple ports are supported from nuclio version 1.14.14
|
|
1500
|
+
# this method exists only for running the min_nuclio_versions decorator
|
|
1501
|
+
return True
|