mlrun 1.7.0rc16__py3-none-any.whl → 1.7.0rc17__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of mlrun might be problematic. Click here for more details.

@@ -18,33 +18,30 @@ from urllib.parse import urljoin
18
18
 
19
19
  import requests
20
20
  from nuclio.auth import AuthInfo as NuclioAuthInfo
21
- from requests.auth import HTTPBasicAuth
21
+ from nuclio.auth import AuthKinds as NuclioAuthKinds
22
22
 
23
23
  import mlrun
24
- import mlrun.common.schemas
24
+ import mlrun.common.schemas as schemas
25
+ import mlrun.common.types
26
+ from mlrun.model import ModelObj
25
27
  from mlrun.platforms.iguazio import min_iguazio_versions
28
+ from mlrun.utils import logger
26
29
 
27
- from ...model import ModelObj
28
- from ..utils import logger
29
- from .function import RemoteRuntime, get_fullname, min_nuclio_versions
30
- from .serving import ServingRuntime
30
+ from .function import get_fullname, min_nuclio_versions
31
31
 
32
- NUCLIO_API_GATEWAY_AUTHENTICATION_MODE_BASIC_AUTH = "basicAuth"
33
- NUCLIO_API_GATEWAY_AUTHENTICATION_MODE_NONE = "none"
34
- NUCLIO_API_GATEWAY_AUTHENTICATION_MODE_ACCESS_KEY = "accessKey"
35
32
  PROJECT_NAME_LABEL = "nuclio.io/project-name"
36
33
 
37
34
 
38
35
  class APIGatewayAuthenticator(typing.Protocol):
39
36
  @property
40
37
  def authentication_mode(self) -> str:
41
- return NUCLIO_API_GATEWAY_AUTHENTICATION_MODE_NONE
38
+ return schemas.APIGatewayAuthenticationMode.none.value
42
39
 
43
40
  @classmethod
44
- def from_scheme(cls, api_gateway_spec: mlrun.common.schemas.APIGatewaySpec):
41
+ def from_scheme(cls, api_gateway_spec: schemas.APIGatewaySpec):
45
42
  if (
46
43
  api_gateway_spec.authenticationMode
47
- == NUCLIO_API_GATEWAY_AUTHENTICATION_MODE_BASIC_AUTH
44
+ == schemas.APIGatewayAuthenticationMode.basic.value
48
45
  ):
49
46
  if api_gateway_spec.authentication:
50
47
  return BasicAuth(
@@ -55,7 +52,7 @@ class APIGatewayAuthenticator(typing.Protocol):
55
52
  return BasicAuth()
56
53
  elif (
57
54
  api_gateway_spec.authenticationMode
58
- == NUCLIO_API_GATEWAY_AUTHENTICATION_MODE_ACCESS_KEY
55
+ == schemas.APIGatewayAuthenticationMode.access_key.value
59
56
  ):
60
57
  return AccessKeyAuth()
61
58
  else:
@@ -63,7 +60,7 @@ class APIGatewayAuthenticator(typing.Protocol):
63
60
 
64
61
  def to_scheme(
65
62
  self,
66
- ) -> Optional[dict[str, Optional[mlrun.common.schemas.APIGatewayBasicAuth]]]:
63
+ ) -> Optional[dict[str, Optional[schemas.APIGatewayBasicAuth]]]:
67
64
  return None
68
65
 
69
66
 
@@ -89,13 +86,13 @@ class BasicAuth(APIGatewayAuthenticator):
89
86
 
90
87
  @property
91
88
  def authentication_mode(self) -> str:
92
- return NUCLIO_API_GATEWAY_AUTHENTICATION_MODE_BASIC_AUTH
89
+ return schemas.APIGatewayAuthenticationMode.basic.value
93
90
 
94
91
  def to_scheme(
95
92
  self,
96
- ) -> Optional[dict[str, Optional[mlrun.common.schemas.APIGatewayBasicAuth]]]:
93
+ ) -> Optional[dict[str, Optional[schemas.APIGatewayBasicAuth]]]:
97
94
  return {
98
- "basicAuth": mlrun.common.schemas.APIGatewayBasicAuth(
95
+ "basicAuth": schemas.APIGatewayBasicAuth(
99
96
  username=self._username, password=self._password
100
97
  )
101
98
  }
@@ -108,7 +105,7 @@ class AccessKeyAuth(APIGatewayAuthenticator):
108
105
 
109
106
  @property
110
107
  def authentication_mode(self) -> str:
111
- return NUCLIO_API_GATEWAY_AUTHENTICATION_MODE_ACCESS_KEY
108
+ return schemas.APIGatewayAuthenticationMode.access_key.value
112
109
 
113
110
 
114
111
  class APIGatewayMetadata(ModelObj):
@@ -156,17 +153,17 @@ class APIGatewaySpec(ModelObj):
156
153
  def __init__(
157
154
  self,
158
155
  functions: Union[
159
- list[str],
160
- Union[
161
- list[
162
- Union[
163
- RemoteRuntime,
164
- ServingRuntime,
165
- ]
166
- ],
167
- RemoteRuntime,
168
- ServingRuntime,
156
+ list[
157
+ Union[
158
+ str,
159
+ "mlrun.runtimes.nuclio.function.RemoteRuntime",
160
+ "mlrun.runtimes.nuclio.serving.ServingRuntime",
161
+ "mlrun.runtimes.nuclio.application.ApplicationRuntime",
162
+ ]
169
163
  ],
164
+ "mlrun.runtimes.nuclio.function.RemoteRuntime",
165
+ "mlrun.runtimes.nuclio.serving.ServingRuntime",
166
+ "mlrun.runtimes.nuclio.application.ApplicationRuntime",
170
167
  ],
171
168
  project: str = None,
172
169
  description: str = "",
@@ -181,7 +178,8 @@ class APIGatewaySpec(ModelObj):
181
178
  Can be a list of function names (["my-func1", "my-func2"])
182
179
  or a list or a single entity of
183
180
  :py:class:`~mlrun.runtimes.nuclio.function.RemoteRuntime` OR
184
- :py:class:`~mlrun.runtimes.nuclio.serving.ServingRuntime`
181
+ :py:class:`~mlrun.runtimes.nuclio.serving.ServingRuntime` OR
182
+ :py:class:`~mlrun.runtimes.nuclio.application.ApplicationRuntime`
185
183
  :param project: The project name
186
184
  :param description: Optional description of the API gateway
187
185
  :param path: Optional path of the API gateway, default value is "/"
@@ -207,17 +205,17 @@ class APIGatewaySpec(ModelObj):
207
205
  self,
208
206
  project: str,
209
207
  functions: Union[
210
- list[str],
211
- Union[
212
- list[
213
- Union[
214
- RemoteRuntime,
215
- ServingRuntime,
216
- ]
217
- ],
218
- RemoteRuntime,
219
- ServingRuntime,
208
+ list[
209
+ Union[
210
+ str,
211
+ "mlrun.runtimes.nuclio.function.RemoteRuntime",
212
+ "mlrun.runtimes.nuclio.serving.ServingRuntime",
213
+ "mlrun.runtimes.nuclio.application.ApplicationRuntime",
214
+ ]
220
215
  ],
216
+ "mlrun.runtimes.nuclio.function.RemoteRuntime",
217
+ "mlrun.runtimes.nuclio.serving.ServingRuntime",
218
+ "mlrun.runtimes.nuclio.application.ApplicationRuntime",
221
219
  ],
222
220
  canary: Optional[list[int]] = None,
223
221
  ports: Optional[list[int]] = None,
@@ -260,16 +258,17 @@ class APIGatewaySpec(ModelObj):
260
258
  def _validate_functions(
261
259
  project: str,
262
260
  functions: Union[
263
- list[str],
264
- Union[
265
- list[
266
- Union[
267
- RemoteRuntime,
268
- ServingRuntime,
269
- ]
270
- ],
271
- Union[RemoteRuntime, ServingRuntime],
261
+ list[
262
+ Union[
263
+ str,
264
+ "mlrun.runtimes.nuclio.function.RemoteRuntime",
265
+ "mlrun.runtimes.nuclio.serving.ServingRuntime",
266
+ "mlrun.runtimes.nuclio.application.ApplicationRuntime",
267
+ ]
272
268
  ],
269
+ "mlrun.runtimes.nuclio.function.RemoteRuntime",
270
+ "mlrun.runtimes.nuclio.serving.ServingRuntime",
271
+ "mlrun.runtimes.nuclio.application.ApplicationRuntime",
273
272
  ],
274
273
  ):
275
274
  if not isinstance(functions, list):
@@ -348,7 +347,8 @@ class APIGateway(ModelObj):
348
347
  self,
349
348
  method="POST",
350
349
  headers: dict = None,
351
- auth: Optional[tuple[str, str]] = None,
350
+ credentials: Optional[tuple[str, str]] = None,
351
+ path: Optional[str] = None,
352
352
  **kwargs,
353
353
  ):
354
354
  """
@@ -356,7 +356,9 @@ class APIGateway(ModelObj):
356
356
 
357
357
  :param method: (str, optional) The HTTP method for the invocation.
358
358
  :param headers: (dict, optional) The HTTP headers for the invocation.
359
- :param auth: (Optional[tuple[str, str]], optional) The authentication creds for the invocation if required.
359
+ :param credentials: (Optional[tuple[str, str]], optional) The (username,password) for the invocation if required
360
+ can also be set by the environment variable (_, V3IO_ACCESS_KEY) for access key authentication.
361
+ :param path: (str, optional) The sub-path for the invocation.
360
362
  :param kwargs: (dict) Additional keyword arguments.
361
363
 
362
364
  :return: The response from the API gateway invocation.
@@ -373,29 +375,44 @@ class APIGateway(ModelObj):
373
375
  f"API gateway is not ready. " f"Current state: {self.state}"
374
376
  )
375
377
 
378
+ auth = None
379
+
376
380
  if (
377
381
  self.spec.authentication.authentication_mode
378
- == NUCLIO_API_GATEWAY_AUTHENTICATION_MODE_BASIC_AUTH
382
+ == schemas.APIGatewayAuthenticationMode.basic.value
379
383
  ):
380
- if not auth:
384
+ if not credentials:
381
385
  raise mlrun.errors.MLRunInvalidArgumentError(
382
386
  "API Gateway invocation requires authentication. Please pass credentials"
383
387
  )
384
- auth = HTTPBasicAuth(*auth)
388
+ auth = NuclioAuthInfo(
389
+ username=credentials[0], password=credentials[1]
390
+ ).to_requests_auth()
385
391
 
386
392
  if (
387
393
  self.spec.authentication.authentication_mode
388
- == NUCLIO_API_GATEWAY_AUTHENTICATION_MODE_ACCESS_KEY
394
+ == schemas.APIGatewayAuthenticationMode.access_key.value
389
395
  ):
390
396
  # inject access key from env
391
- auth = NuclioAuthInfo().from_envvar().to_requests_auth()
392
-
397
+ if credentials:
398
+ auth = NuclioAuthInfo(
399
+ username=credentials[0],
400
+ password=credentials[1],
401
+ mode=NuclioAuthKinds.iguazio,
402
+ ).to_requests_auth()
403
+ else:
404
+ auth = NuclioAuthInfo().from_envvar().to_requests_auth()
405
+ if not auth:
406
+ raise mlrun.errors.MLRunInvalidArgumentError(
407
+ "API Gateway invocation requires authentication. Please set V3IO_ACCESS_KEY env var"
408
+ )
409
+ url = urljoin(self.invoke_url, path or "")
393
410
  return requests.request(
394
411
  method=method,
395
- url=self.invoke_url,
412
+ url=url,
396
413
  headers=headers or {},
397
- **kwargs,
398
414
  auth=auth,
415
+ **kwargs,
399
416
  )
400
417
 
401
418
  def wait_for_readiness(self, max_wait_time=90):
@@ -420,10 +437,10 @@ class APIGateway(ModelObj):
420
437
  )
421
438
 
422
439
  def is_ready(self):
423
- if self.state is not mlrun.common.schemas.api_gateway.APIGatewayState.ready:
440
+ if self.state is not schemas.api_gateway.APIGatewayState.ready:
424
441
  # try to sync the state
425
442
  self.sync()
426
- return self.state == mlrun.common.schemas.api_gateway.APIGatewayState.ready
443
+ return self.state == schemas.api_gateway.APIGatewayState.ready
427
444
 
428
445
  def sync(self):
429
446
  """
@@ -461,13 +478,17 @@ class APIGateway(ModelObj):
461
478
  def with_canary(
462
479
  self,
463
480
  functions: Union[
464
- list[str],
465
481
  list[
466
482
  Union[
467
- RemoteRuntime,
468
- ServingRuntime,
483
+ str,
484
+ "mlrun.runtimes.nuclio.function.RemoteRuntime",
485
+ "mlrun.runtimes.nuclio.serving.ServingRuntime",
486
+ "mlrun.runtimes.nuclio.application.ApplicationRuntime",
469
487
  ]
470
488
  ],
489
+ "mlrun.runtimes.nuclio.function.RemoteRuntime",
490
+ "mlrun.runtimes.nuclio.serving.ServingRuntime",
491
+ "mlrun.runtimes.nuclio.application.ApplicationRuntime",
471
492
  ],
472
493
  canary: list[int],
473
494
  ):
@@ -478,7 +499,8 @@ class APIGateway(ModelObj):
478
499
  Can be a list of function names (["my-func1", "my-func2"])
479
500
  or a list of nuclio functions of types
480
501
  :py:class:`~mlrun.runtimes.nuclio.function.RemoteRuntime` OR
481
- :py:class:`~mlrun.runtimes.nuclio.serving.ServingRuntime`
502
+ :py:class:`~mlrun.runtimes.nuclio.serving.ServingRuntime` OR
503
+ :py:class:`~mlrun.runtimes.nuclio.application.ApplicationRuntime`
482
504
  :param canary: The canary percents for the API gateway of type list[int]; for instance: [20,80]
483
505
 
484
506
  """
@@ -503,13 +525,13 @@ class APIGateway(ModelObj):
503
525
  )
504
526
 
505
527
  @classmethod
506
- def from_scheme(cls, api_gateway: mlrun.common.schemas.APIGateway):
528
+ def from_scheme(cls, api_gateway: schemas.APIGateway):
507
529
  project = api_gateway.metadata.labels.get(PROJECT_NAME_LABEL)
508
530
  functions, canary = cls._resolve_canary(api_gateway.spec.upstreams)
509
531
  state = (
510
532
  api_gateway.status.state
511
533
  if api_gateway.status
512
- else mlrun.common.schemas.APIGatewayState.none
534
+ else schemas.APIGatewayState.none
513
535
  )
514
536
  new_api_gateway = cls(
515
537
  metadata=APIGatewayMetadata(
@@ -528,14 +550,14 @@ class APIGateway(ModelObj):
528
550
  new_api_gateway.state = state
529
551
  return new_api_gateway
530
552
 
531
- def to_scheme(self) -> mlrun.common.schemas.APIGateway:
553
+ def to_scheme(self) -> schemas.APIGateway:
532
554
  upstreams = (
533
555
  [
534
- mlrun.common.schemas.APIGatewayUpstream(
556
+ schemas.APIGatewayUpstream(
535
557
  nucliofunction={"name": self.spec.functions[0]},
536
558
  percentage=self.spec.canary[0],
537
559
  ),
538
- mlrun.common.schemas.APIGatewayUpstream(
560
+ schemas.APIGatewayUpstream(
539
561
  # do not set percent for the second function,
540
562
  # so we can define which function to display as a primary one in UI
541
563
  nucliofunction={"name": self.spec.functions[1]},
@@ -543,7 +565,7 @@ class APIGateway(ModelObj):
543
565
  ]
544
566
  if self.spec.canary
545
567
  else [
546
- mlrun.common.schemas.APIGatewayUpstream(
568
+ schemas.APIGatewayUpstream(
547
569
  nucliofunction={"name": function_name},
548
570
  )
549
571
  for function_name in self.spec.functions
@@ -553,16 +575,14 @@ class APIGateway(ModelObj):
553
575
  for i, port in enumerate(self.spec.ports):
554
576
  upstreams[i].port = port
555
577
 
556
- api_gateway = mlrun.common.schemas.APIGateway(
557
- metadata=mlrun.common.schemas.APIGatewayMetadata(
558
- name=self.metadata.name, labels={}
559
- ),
560
- spec=mlrun.common.schemas.APIGatewaySpec(
578
+ api_gateway = schemas.APIGateway(
579
+ metadata=schemas.APIGatewayMetadata(name=self.metadata.name, labels={}),
580
+ spec=schemas.APIGatewaySpec(
561
581
  name=self.metadata.name,
562
582
  description=self.spec.description,
563
583
  host=self.spec.host,
564
584
  path=self.spec.path,
565
- authenticationMode=mlrun.common.schemas.APIGatewayAuthenticationMode.from_str(
585
+ authenticationMode=schemas.APIGatewayAuthenticationMode.from_str(
566
586
  self.spec.authentication.authentication_mode
567
587
  ),
568
588
  upstreams=upstreams,
@@ -592,7 +612,7 @@ class APIGateway(ModelObj):
592
612
 
593
613
  @staticmethod
594
614
  def _resolve_canary(
595
- upstreams: list[mlrun.common.schemas.APIGatewayUpstream],
615
+ upstreams: list[schemas.APIGatewayUpstream],
596
616
  ) -> tuple[Union[list[str], None], Union[list[int], None]]:
597
617
  if len(upstreams) == 1:
598
618
  return [upstreams[0].nucliofunction.get("name")], None
@@ -12,13 +12,20 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  import pathlib
15
+ import typing
15
16
 
16
17
  import nuclio
17
18
 
19
+ import mlrun.common.schemas as schemas
18
20
  import mlrun.errors
19
- from mlrun.common.schemas import AuthInfo
21
+ from mlrun.common.runtimes.constants import NuclioIngressAddTemplatedIngressModes
20
22
  from mlrun.runtimes import RemoteRuntime
21
23
  from mlrun.runtimes.nuclio import min_nuclio_versions
24
+ from mlrun.runtimes.nuclio.api_gateway import (
25
+ APIGateway,
26
+ APIGatewayMetadata,
27
+ APIGatewaySpec,
28
+ )
22
29
  from mlrun.runtimes.nuclio.function import NuclioSpec, NuclioStatus
23
30
 
24
31
 
@@ -113,7 +120,10 @@ class ApplicationSpec(NuclioSpec):
113
120
  state_thresholds=state_thresholds,
114
121
  disable_default_http_trigger=disable_default_http_trigger,
115
122
  )
116
- self.internal_application_port = internal_application_port or 8080
123
+ self.internal_application_port = (
124
+ internal_application_port
125
+ or mlrun.mlconf.function.application.default_sidecar_internal_port
126
+ )
117
127
 
118
128
  @property
119
129
  def internal_application_port(self):
@@ -139,6 +149,9 @@ class ApplicationStatus(NuclioStatus):
139
149
  container_image=None,
140
150
  application_image=None,
141
151
  sidecar_name=None,
152
+ api_gateway_name=None,
153
+ api_gateway=None,
154
+ url=None,
142
155
  ):
143
156
  super().__init__(
144
157
  state=state,
@@ -151,6 +164,9 @@ class ApplicationStatus(NuclioStatus):
151
164
  )
152
165
  self.application_image = application_image or None
153
166
  self.sidecar_name = sidecar_name or None
167
+ self.api_gateway_name = api_gateway_name or None
168
+ self.api_gateway = api_gateway or None
169
+ self.url = url or None
154
170
 
155
171
 
156
172
  class ApplicationRuntime(RemoteRuntime):
@@ -176,6 +192,24 @@ class ApplicationRuntime(RemoteRuntime):
176
192
  def status(self, status):
177
193
  self._status = self._verify_dict(status, "status", ApplicationStatus)
178
194
 
195
+ @property
196
+ def api_gateway(self):
197
+ return self.status.api_gateway
198
+
199
+ @api_gateway.setter
200
+ def api_gateway(self, api_gateway: APIGateway):
201
+ self.status.api_gateway = api_gateway
202
+
203
+ @property
204
+ def url(self):
205
+ if not self.status.api_gateway:
206
+ self._sync_api_gateway()
207
+ return self.status.api_gateway.invoke_url
208
+
209
+ @url.setter
210
+ def url(self, url):
211
+ self.status.url = url
212
+
179
213
  def set_internal_application_port(self, port: int):
180
214
  self.spec.internal_application_port = port
181
215
 
@@ -220,7 +254,7 @@ class ApplicationRuntime(RemoteRuntime):
220
254
  project="",
221
255
  tag="",
222
256
  verbose=False,
223
- auth_info: AuthInfo = None,
257
+ auth_info: schemas.AuthInfo = None,
224
258
  builder_env: dict = None,
225
259
  force_build: bool = False,
226
260
  with_mlrun=None,
@@ -228,6 +262,10 @@ class ApplicationRuntime(RemoteRuntime):
228
262
  is_kfp=False,
229
263
  mlrun_version_specifier=None,
230
264
  show_on_failure: bool = False,
265
+ skip_access_key_auth: bool = False,
266
+ direct_port_access: bool = False,
267
+ authentication_mode: schemas.APIGatewayAuthenticationMode = None,
268
+ authentication_creds: tuple[str] = None,
231
269
  ):
232
270
  """
233
271
  Deploy function, builds the application image if required (self.requires_build()) or force_build is True,
@@ -244,6 +282,10 @@ class ApplicationRuntime(RemoteRuntime):
244
282
  :param is_kfp: Deploy as part of a kfp pipeline
245
283
  :param mlrun_version_specifier: Which mlrun package version to include (if not current)
246
284
  :param show_on_failure: Show logs only in case of build failure
285
+ :param skip_access_key_auth: Skip adding access key auth to the API Gateway
286
+ :param direct_port_access: Set True to allow direct port access to the application sidecar
287
+ :param authentication_mode: API Gateway authentication mode
288
+ :param authentication_creds: API Gateway authentication credentials as a tuple (username, password)
247
289
  :return: True if the function is ready (deployed)
248
290
  """
249
291
  if self.requires_build() or force_build:
@@ -261,6 +303,16 @@ class ApplicationRuntime(RemoteRuntime):
261
303
 
262
304
  self._ensure_reverse_proxy_configurations()
263
305
  self._configure_application_sidecar()
306
+
307
+ # we only allow accessing the application via the API Gateway
308
+ name_tag = tag or self.metadata.tag
309
+ self.status.api_gateway_name = (
310
+ f"{self.metadata.name}-{name_tag}" if name_tag else self.metadata.name
311
+ )
312
+ self.spec.add_templated_ingress_host_mode = (
313
+ NuclioIngressAddTemplatedIngressModes.never
314
+ )
315
+
264
316
  super().deploy(
265
317
  project,
266
318
  tag,
@@ -269,6 +321,14 @@ class ApplicationRuntime(RemoteRuntime):
269
321
  builder_env,
270
322
  )
271
323
 
324
+ ports = self.spec.internal_application_port if direct_port_access else []
325
+ self.create_api_gateway(
326
+ name=self.status.api_gateway_name,
327
+ ports=ports,
328
+ authentication_mode=authentication_mode,
329
+ authentication_creds=authentication_creds,
330
+ )
331
+
272
332
  def with_source_archive(
273
333
  self, source, workdir=None, pull_at_runtime=True, target_dir=None
274
334
  ):
@@ -290,6 +350,92 @@ class ApplicationRuntime(RemoteRuntime):
290
350
  target_dir=target_dir,
291
351
  )
292
352
 
353
+ @classmethod
354
+ def get_filename_and_handler(cls) -> (str, str):
355
+ reverse_proxy_file_path = pathlib.Path(__file__).parent / "reverse_proxy.go"
356
+ return str(reverse_proxy_file_path), "Handler"
357
+
358
+ def create_api_gateway(
359
+ self,
360
+ name: str = None,
361
+ path: str = None,
362
+ ports: list[int] = None,
363
+ authentication_mode: schemas.APIGatewayAuthenticationMode = None,
364
+ authentication_creds: tuple[str] = None,
365
+ ):
366
+ api_gateway = APIGateway(
367
+ APIGatewayMetadata(
368
+ name=name,
369
+ namespace=self.metadata.namespace,
370
+ labels=self.metadata.labels,
371
+ annotations=self.metadata.annotations,
372
+ ),
373
+ APIGatewaySpec(
374
+ functions=[self],
375
+ project=self.metadata.project,
376
+ path=path,
377
+ ports=mlrun.utils.helpers.as_list(ports) if ports else None,
378
+ ),
379
+ )
380
+
381
+ authentication_mode = (
382
+ authentication_mode
383
+ or mlrun.mlconf.function.application.default_authentication_mode
384
+ )
385
+ if authentication_mode == schemas.APIGatewayAuthenticationMode.access_key:
386
+ api_gateway.with_access_key_auth()
387
+ elif authentication_mode == schemas.APIGatewayAuthenticationMode.basic:
388
+ api_gateway.with_basic_auth(*authentication_creds)
389
+
390
+ db = mlrun.get_run_db()
391
+ api_gateway_scheme = db.store_api_gateway(
392
+ api_gateway=api_gateway.to_scheme(), project=self.metadata.project
393
+ )
394
+ if not self.status.api_gateway_name:
395
+ self.status.api_gateway_name = api_gateway_scheme.metadata.name
396
+ self.status.api_gateway = APIGateway.from_scheme(api_gateway_scheme)
397
+ self.status.api_gateway.wait_for_readiness()
398
+ self.url = self.status.api_gateway.invoke_url
399
+
400
+ def invoke(
401
+ self,
402
+ path: str,
403
+ body: typing.Union[str, bytes, dict] = None,
404
+ method: str = None,
405
+ headers: dict = None,
406
+ dashboard: str = "",
407
+ force_external_address: bool = False,
408
+ auth_info: schemas.AuthInfo = None,
409
+ mock: bool = None,
410
+ **http_client_kwargs,
411
+ ):
412
+ self._sync_api_gateway()
413
+ # If the API Gateway is not ready or not set, try to invoke the function directly (without the API Gateway)
414
+ if not self.status.api_gateway:
415
+ super().invoke(
416
+ path,
417
+ body,
418
+ method,
419
+ headers,
420
+ dashboard,
421
+ force_external_address,
422
+ auth_info,
423
+ mock,
424
+ **http_client_kwargs,
425
+ )
426
+
427
+ credentials = (auth_info.username, auth_info.password) if auth_info else None
428
+
429
+ if not method:
430
+ method = "POST" if body else "GET"
431
+ return self.status.api_gateway.invoke(
432
+ method=method,
433
+ headers=headers,
434
+ credentials=credentials,
435
+ path=path,
436
+ **http_client_kwargs,
437
+ )
438
+
293
439
  def _build_application_image(
294
440
  self,
295
441
  builder_env: dict = None,
@@ -355,7 +501,14 @@ class ApplicationRuntime(RemoteRuntime):
355
501
  self.set_env("SIDECAR_PORT", self.spec.internal_application_port)
356
502
  self.set_env("SIDECAR_HOST", "http://localhost")
357
503
 
358
- @classmethod
359
- def get_filename_and_handler(cls) -> (str, str):
360
- reverse_proxy_file_path = pathlib.Path(__file__).parent / "reverse_proxy.go"
361
- return str(reverse_proxy_file_path), "Handler"
504
+ def _sync_api_gateway(self):
505
+ if not self.status.api_gateway_name:
506
+ return
507
+
508
+ db = mlrun.get_run_db()
509
+ api_gateway_scheme = db.get_api_gateway(
510
+ name=self.status.api_gateway_name, project=self.metadata.project
511
+ )
512
+ self.status.api_gateway = APIGateway.from_scheme(api_gateway_scheme)
513
+ self.status.api_gateway.wait_for_readiness()
514
+ self.url = self.status.api_gateway.invoke_url
@@ -547,7 +547,6 @@ class RemoteRuntime(KubeResource):
547
547
  if tag:
548
548
  self.metadata.tag = tag
549
549
 
550
- save_record = False
551
550
  # Attempt auto-mounting, before sending to remote build
552
551
  self.try_auto_mount_based_on_config()
553
552
  self._fill_credentials()
@@ -565,15 +564,18 @@ class RemoteRuntime(KubeResource):
565
564
  # now, functions can be not exposed (using service type ClusterIP) and hence
566
565
  # for BC we first try to populate the external invocation url, and then
567
566
  # if not exists, take the internal invocation url
568
- if self.status.external_invocation_urls:
567
+ if (
568
+ self.status.external_invocation_urls
569
+ and self.status.external_invocation_urls[0] != ""
570
+ ):
569
571
  self.spec.command = f"http://{self.status.external_invocation_urls[0]}"
570
- save_record = True
571
- elif self.status.internal_invocation_urls:
572
+ elif (
573
+ self.status.internal_invocation_urls
574
+ and self.status.internal_invocation_urls[0] != ""
575
+ ):
572
576
  self.spec.command = f"http://{self.status.internal_invocation_urls[0]}"
573
- save_record = True
574
- elif self.status.address:
577
+ elif self.status.address and self.status.address != "":
575
578
  self.spec.command = f"http://{self.status.address}"
576
- save_record = True
577
579
 
578
580
  logger.info(
579
581
  "Successfully deployed function",
@@ -581,8 +583,7 @@ class RemoteRuntime(KubeResource):
581
583
  external_invocation_urls=self.status.external_invocation_urls,
582
584
  )
583
585
 
584
- if save_record:
585
- self.save(versioned=False)
586
+ self.save(versioned=False)
586
587
 
587
588
  return self.spec.command
588
589
 
@@ -966,19 +967,24 @@ class RemoteRuntime(KubeResource):
966
967
  sidecar["image"] = image
967
968
 
968
969
  ports = mlrun.utils.helpers.as_list(ports)
970
+ # according to RFC-6335, port name should be less than 15 characters,
971
+ # so we truncate it if needed and leave room for the index
972
+ port_name = name[:13].rstrip("-_") if len(name) > 13 else name
969
973
  sidecar["ports"] = [
970
974
  {
971
- "name": f"{name}-{i}",
975
+ "name": f"{port_name}-{i}",
972
976
  "containerPort": port,
973
977
  "protocol": "TCP",
974
978
  }
975
979
  for i, port in enumerate(ports)
976
980
  ]
977
981
 
978
- if command:
982
+ # if it is a redeploy, 'command' might be set with the previous invocation url.
983
+ # in this case, we don't want to use it as the sidecar command
984
+ if command and not command.startswith("http"):
979
985
  sidecar["command"] = mlrun.utils.helpers.as_list(command)
980
986
 
981
- if args:
987
+ if args and sidecar["command"]:
982
988
  sidecar["args"] = mlrun.utils.helpers.as_list(args)
983
989
 
984
990
  def _set_sidecar(self, name: str) -> dict: