mlrun 1.7.0rc28__py3-none-any.whl → 1.7.0rc55__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.

Files changed (135) hide show
  1. mlrun/__main__.py +4 -2
  2. mlrun/alerts/alert.py +75 -8
  3. mlrun/artifacts/base.py +1 -0
  4. mlrun/artifacts/manager.py +9 -2
  5. mlrun/common/constants.py +4 -1
  6. mlrun/common/db/sql_session.py +3 -2
  7. mlrun/common/formatters/__init__.py +1 -0
  8. mlrun/common/formatters/artifact.py +1 -0
  9. mlrun/{model_monitoring/application.py → common/formatters/feature_set.py} +20 -6
  10. mlrun/common/formatters/run.py +3 -0
  11. mlrun/common/helpers.py +0 -1
  12. mlrun/common/schemas/__init__.py +3 -1
  13. mlrun/common/schemas/alert.py +15 -12
  14. mlrun/common/schemas/api_gateway.py +6 -6
  15. mlrun/common/schemas/auth.py +5 -0
  16. mlrun/common/schemas/client_spec.py +0 -1
  17. mlrun/common/schemas/common.py +7 -4
  18. mlrun/common/schemas/frontend_spec.py +7 -0
  19. mlrun/common/schemas/function.py +7 -0
  20. mlrun/common/schemas/model_monitoring/__init__.py +4 -3
  21. mlrun/common/schemas/model_monitoring/constants.py +41 -26
  22. mlrun/common/schemas/model_monitoring/model_endpoints.py +23 -47
  23. mlrun/common/schemas/notification.py +69 -12
  24. mlrun/common/schemas/project.py +45 -12
  25. mlrun/common/schemas/workflow.py +10 -2
  26. mlrun/common/types.py +1 -0
  27. mlrun/config.py +91 -35
  28. mlrun/data_types/data_types.py +6 -1
  29. mlrun/data_types/spark.py +2 -2
  30. mlrun/data_types/to_pandas.py +57 -25
  31. mlrun/datastore/__init__.py +1 -0
  32. mlrun/datastore/alibaba_oss.py +3 -2
  33. mlrun/datastore/azure_blob.py +125 -37
  34. mlrun/datastore/base.py +42 -21
  35. mlrun/datastore/datastore.py +4 -2
  36. mlrun/datastore/datastore_profile.py +1 -1
  37. mlrun/datastore/dbfs_store.py +3 -7
  38. mlrun/datastore/filestore.py +1 -3
  39. mlrun/datastore/google_cloud_storage.py +85 -29
  40. mlrun/datastore/inmem.py +4 -1
  41. mlrun/datastore/redis.py +1 -0
  42. mlrun/datastore/s3.py +25 -12
  43. mlrun/datastore/sources.py +76 -4
  44. mlrun/datastore/spark_utils.py +30 -0
  45. mlrun/datastore/storeytargets.py +151 -0
  46. mlrun/datastore/targets.py +102 -131
  47. mlrun/datastore/v3io.py +1 -0
  48. mlrun/db/base.py +15 -6
  49. mlrun/db/httpdb.py +57 -28
  50. mlrun/db/nopdb.py +29 -5
  51. mlrun/errors.py +20 -3
  52. mlrun/execution.py +46 -5
  53. mlrun/feature_store/api.py +25 -1
  54. mlrun/feature_store/common.py +6 -11
  55. mlrun/feature_store/feature_vector.py +3 -1
  56. mlrun/feature_store/retrieval/job.py +4 -1
  57. mlrun/feature_store/retrieval/spark_merger.py +10 -39
  58. mlrun/feature_store/steps.py +8 -0
  59. mlrun/frameworks/_common/plan.py +3 -3
  60. mlrun/frameworks/_ml_common/plan.py +1 -1
  61. mlrun/frameworks/parallel_coordinates.py +2 -3
  62. mlrun/frameworks/sklearn/mlrun_interface.py +13 -3
  63. mlrun/k8s_utils.py +48 -2
  64. mlrun/launcher/client.py +6 -6
  65. mlrun/launcher/local.py +2 -2
  66. mlrun/model.py +215 -34
  67. mlrun/model_monitoring/api.py +38 -24
  68. mlrun/model_monitoring/applications/__init__.py +1 -2
  69. mlrun/model_monitoring/applications/_application_steps.py +60 -29
  70. mlrun/model_monitoring/applications/base.py +2 -174
  71. mlrun/model_monitoring/applications/context.py +197 -70
  72. mlrun/model_monitoring/applications/evidently_base.py +11 -85
  73. mlrun/model_monitoring/applications/histogram_data_drift.py +21 -16
  74. mlrun/model_monitoring/applications/results.py +4 -4
  75. mlrun/model_monitoring/controller.py +110 -282
  76. mlrun/model_monitoring/db/stores/__init__.py +8 -3
  77. mlrun/model_monitoring/db/stores/base/store.py +3 -0
  78. mlrun/model_monitoring/db/stores/sqldb/models/base.py +9 -7
  79. mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +18 -3
  80. mlrun/model_monitoring/db/stores/sqldb/sql_store.py +43 -23
  81. mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +48 -35
  82. mlrun/model_monitoring/db/tsdb/__init__.py +7 -2
  83. mlrun/model_monitoring/db/tsdb/base.py +147 -15
  84. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +94 -55
  85. mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +0 -3
  86. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +144 -38
  87. mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +44 -3
  88. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +246 -57
  89. mlrun/model_monitoring/helpers.py +70 -50
  90. mlrun/model_monitoring/stream_processing.py +96 -195
  91. mlrun/model_monitoring/writer.py +13 -5
  92. mlrun/package/packagers/default_packager.py +2 -2
  93. mlrun/projects/operations.py +16 -8
  94. mlrun/projects/pipelines.py +126 -115
  95. mlrun/projects/project.py +286 -129
  96. mlrun/render.py +3 -3
  97. mlrun/run.py +38 -19
  98. mlrun/runtimes/__init__.py +19 -8
  99. mlrun/runtimes/base.py +4 -1
  100. mlrun/runtimes/daskjob.py +1 -1
  101. mlrun/runtimes/funcdoc.py +1 -1
  102. mlrun/runtimes/kubejob.py +6 -6
  103. mlrun/runtimes/local.py +12 -5
  104. mlrun/runtimes/nuclio/api_gateway.py +68 -8
  105. mlrun/runtimes/nuclio/application/application.py +307 -70
  106. mlrun/runtimes/nuclio/function.py +63 -14
  107. mlrun/runtimes/nuclio/serving.py +10 -10
  108. mlrun/runtimes/pod.py +25 -19
  109. mlrun/runtimes/remotesparkjob.py +2 -5
  110. mlrun/runtimes/sparkjob/spark3job.py +16 -17
  111. mlrun/runtimes/utils.py +34 -0
  112. mlrun/serving/routers.py +2 -5
  113. mlrun/serving/server.py +37 -19
  114. mlrun/serving/states.py +30 -3
  115. mlrun/serving/v2_serving.py +44 -35
  116. mlrun/track/trackers/mlflow_tracker.py +5 -0
  117. mlrun/utils/async_http.py +1 -1
  118. mlrun/utils/db.py +18 -0
  119. mlrun/utils/helpers.py +150 -36
  120. mlrun/utils/http.py +1 -1
  121. mlrun/utils/notifications/notification/__init__.py +0 -1
  122. mlrun/utils/notifications/notification/webhook.py +8 -1
  123. mlrun/utils/notifications/notification_pusher.py +1 -1
  124. mlrun/utils/v3io_clients.py +2 -2
  125. mlrun/utils/version/version.json +2 -2
  126. {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/METADATA +153 -66
  127. {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/RECORD +131 -134
  128. {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/WHEEL +1 -1
  129. mlrun/feature_store/retrieval/conversion.py +0 -271
  130. mlrun/model_monitoring/controller_handler.py +0 -37
  131. mlrun/model_monitoring/evidently_application.py +0 -20
  132. mlrun/model_monitoring/prometheus.py +0 -216
  133. {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/LICENSE +0 -0
  134. {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/entry_points.txt +0 -0
  135. {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/top_level.txt +0 -0
@@ -15,9 +15,11 @@ import pathlib
15
15
  import typing
16
16
 
17
17
  import nuclio
18
+ import nuclio.auth
18
19
 
19
20
  import mlrun.common.schemas as schemas
20
21
  import mlrun.errors
22
+ import mlrun.run
21
23
  from mlrun.common.runtimes.constants import NuclioIngressAddTemplatedIngressModes
22
24
  from mlrun.runtimes import RemoteRuntime
23
25
  from mlrun.runtimes.nuclio import min_nuclio_versions
@@ -27,7 +29,7 @@ from mlrun.runtimes.nuclio.api_gateway import (
27
29
  APIGatewaySpec,
28
30
  )
29
31
  from mlrun.runtimes.nuclio.function import NuclioSpec, NuclioStatus
30
- from mlrun.utils import logger
32
+ from mlrun.utils import logger, update_in
31
33
 
32
34
 
33
35
  class ApplicationSpec(NuclioSpec):
@@ -121,6 +123,11 @@ class ApplicationSpec(NuclioSpec):
121
123
  state_thresholds=state_thresholds,
122
124
  disable_default_http_trigger=disable_default_http_trigger,
123
125
  )
126
+
127
+ # Override default min/max replicas (don't assume application is stateless)
128
+ self.min_replicas = min_replicas or 1
129
+ self.max_replicas = max_replicas or 1
130
+
124
131
  self.internal_application_port = (
125
132
  internal_application_port
126
133
  or mlrun.mlconf.function.application.default_sidecar_internal_port
@@ -149,6 +156,7 @@ class ApplicationStatus(NuclioStatus):
149
156
  build_pod=None,
150
157
  container_image=None,
151
158
  application_image=None,
159
+ application_source=None,
152
160
  sidecar_name=None,
153
161
  api_gateway_name=None,
154
162
  api_gateway=None,
@@ -164,14 +172,16 @@ class ApplicationStatus(NuclioStatus):
164
172
  container_image=container_image,
165
173
  )
166
174
  self.application_image = application_image or None
175
+ self.application_source = application_source or None
167
176
  self.sidecar_name = sidecar_name or None
168
177
  self.api_gateway_name = api_gateway_name or None
169
- self.api_gateway = api_gateway or None
178
+ self.api_gateway: typing.Optional[APIGateway] = api_gateway or None
170
179
  self.url = url or None
171
180
 
172
181
 
173
182
  class ApplicationRuntime(RemoteRuntime):
174
183
  kind = "application"
184
+ reverse_proxy_image = None
175
185
 
176
186
  @min_nuclio_versions("1.13.1")
177
187
  def __init__(self, spec=None, metadata=None):
@@ -250,6 +260,15 @@ class ApplicationRuntime(RemoteRuntime):
250
260
  "Application sidecar spec must include a command if args are provided"
251
261
  )
252
262
 
263
+ def prepare_image_for_deploy(self):
264
+ if self.spec.build.source and self.spec.build.load_source_on_run:
265
+ logger.warning(
266
+ "Application runtime requires loading the source into the application image. "
267
+ f"Even though {self.spec.build.load_source_on_run=}, loading on build will be forced."
268
+ )
269
+ self.spec.build.load_source_on_run = False
270
+ super().prepare_image_for_deploy()
271
+
253
272
  def deploy(
254
273
  self,
255
274
  project="",
@@ -263,31 +282,31 @@ class ApplicationRuntime(RemoteRuntime):
263
282
  is_kfp=False,
264
283
  mlrun_version_specifier=None,
265
284
  show_on_failure: bool = False,
266
- direct_port_access: bool = False,
267
- authentication_mode: schemas.APIGatewayAuthenticationMode = None,
268
- authentication_creds: tuple[str] = None,
285
+ create_default_api_gateway: bool = True,
269
286
  ):
270
287
  """
271
288
  Deploy function, builds the application image if required (self.requires_build()) or force_build is True,
272
289
  Once the image is built, the function is deployed.
273
- :param project: Project name
274
- :param tag: Function tag
275
- :param verbose: Set True for verbose logging
276
- :param auth_info: Service AuthInfo (deprecated and ignored)
277
- :param builder_env: Env vars dict for source archive config/credentials
278
- e.g. builder_env={"GIT_TOKEN": token}
279
- :param force_build: Set True for force building the application image
280
- :param with_mlrun: Add the current mlrun package to the container build
281
- :param skip_deployed: Skip the build if we already have an image for the function
282
- :param is_kfp: Deploy as part of a kfp pipeline
283
- :param mlrun_version_specifier: Which mlrun package version to include (if not current)
284
- :param show_on_failure: Show logs only in case of build failure
285
- :param direct_port_access: Set True to allow direct port access to the application sidecar
286
- :param authentication_mode: API Gateway authentication mode
287
- :param authentication_creds: API Gateway authentication credentials as a tuple (username, password)
288
- :return: True if the function is ready (deployed)
290
+
291
+ :param project: Project name
292
+ :param tag: Function tag
293
+ :param verbose: Set True for verbose logging
294
+ :param auth_info: Service AuthInfo (deprecated and ignored)
295
+ :param builder_env: Env vars dict for source archive config/credentials
296
+ e.g. builder_env={"GIT_TOKEN": token}
297
+ :param force_build: Set True for force building the application image
298
+ :param with_mlrun: Add the current mlrun package to the container build
299
+ :param skip_deployed: Skip the build if we already have an image for the function
300
+ :param is_kfp: Deploy as part of a kfp pipeline
301
+ :param mlrun_version_specifier: Which mlrun package version to include (if not current)
302
+ :param show_on_failure: Show logs only in case of build failure
303
+ :param create_default_api_gateway: When deploy finishes the default API gateway will be created for the
304
+ application. Disabling this flag means that the application will not be
305
+ accessible until an API gateway is created for it.
306
+
307
+ :return: The default API gateway URL if created or True if the function is ready (deployed)
289
308
  """
290
- if self.requires_build() or force_build:
309
+ if (self.requires_build() and not self.spec.image) or force_build:
291
310
  self._fill_credentials()
292
311
  self._build_application_image(
293
312
  builder_env=builder_env,
@@ -300,38 +319,57 @@ class ApplicationRuntime(RemoteRuntime):
300
319
  show_on_failure=show_on_failure,
301
320
  )
302
321
 
303
- self._ensure_reverse_proxy_configurations()
322
+ # This is a class method that accepts a function instance, so we pass self as the function instance
323
+ self._ensure_reverse_proxy_configurations(self)
304
324
  self._configure_application_sidecar()
305
325
 
306
- # we only allow accessing the application via the API Gateway
307
- name_tag = tag or self.metadata.tag
308
- self.status.api_gateway_name = (
309
- f"{self.metadata.name}-{name_tag}" if name_tag else self.metadata.name
310
- )
326
+ # We only allow accessing the application via the API Gateway
311
327
  self.spec.add_templated_ingress_host_mode = (
312
328
  NuclioIngressAddTemplatedIngressModes.never
313
329
  )
314
330
 
315
331
  super().deploy(
316
- project,
317
- tag,
318
- verbose,
319
- auth_info,
320
- builder_env,
332
+ project=project,
333
+ tag=tag,
334
+ verbose=verbose,
335
+ auth_info=auth_info,
336
+ builder_env=builder_env,
321
337
  )
322
-
323
- ports = self.spec.internal_application_port if direct_port_access else []
324
- self.create_api_gateway(
325
- name=self.status.api_gateway_name,
326
- ports=ports,
327
- authentication_mode=authentication_mode,
328
- authentication_creds=authentication_creds,
338
+ logger.info(
339
+ "Successfully deployed function.",
329
340
  )
330
341
 
342
+ # Restore the source in case it was removed to make nuclio not consider it when building
343
+ if not self.spec.build.source and self.status.application_source:
344
+ self.spec.build.source = self.status.application_source
345
+ self.save(versioned=False)
346
+
347
+ if create_default_api_gateway:
348
+ try:
349
+ api_gateway_name = self.resolve_default_api_gateway_name()
350
+ return self.create_api_gateway(api_gateway_name, set_as_default=True)
351
+ except Exception as exc:
352
+ logger.warning(
353
+ "Failed to create default API gateway, application may not be accessible. "
354
+ "Use the `create_api_gateway` method to make it accessible",
355
+ exc=mlrun.errors.err_to_str(exc),
356
+ )
357
+ elif not self.status.api_gateway:
358
+ logger.warning(
359
+ "Application is online but may not be accessible since default gateway creation was not requested."
360
+ "Use the `create_api_gateway` method to make it accessible."
361
+ )
362
+
363
+ return True
364
+
331
365
  def with_source_archive(
332
- self, source, workdir=None, pull_at_runtime=True, target_dir=None
366
+ self,
367
+ source,
368
+ workdir=None,
369
+ pull_at_runtime: bool = False,
370
+ target_dir: str = None,
333
371
  ):
334
- """load the code from git/tar/zip archive at runtime or build
372
+ """load the code from git/tar/zip archive at build
335
373
 
336
374
  :param source: valid absolute path or URL to git, zip, or tar file, e.g.
337
375
  git://github.com/mlrun/something.git
@@ -339,18 +377,50 @@ class ApplicationRuntime(RemoteRuntime):
339
377
  note path source must exist on the image or exist locally when run is local
340
378
  (it is recommended to use 'workdir' when source is a filepath instead)
341
379
  :param workdir: working dir relative to the archive root (e.g. './subdir') or absolute to the image root
342
- :param pull_at_runtime: load the archive into the container at job runtime vs on build/deploy
380
+ :param pull_at_runtime: currently not supported, source must be loaded into the image during the build process
343
381
  :param target_dir: target dir on runtime pod or repo clone / archive extraction
344
382
  """
383
+ if pull_at_runtime:
384
+ logger.warning(
385
+ f"{pull_at_runtime=} is currently not supported for application runtime "
386
+ "and will be overridden to False",
387
+ pull_at_runtime=pull_at_runtime,
388
+ )
389
+
345
390
  self._configure_mlrun_build_with_source(
346
391
  source=source,
347
392
  workdir=workdir,
348
- pull_at_runtime=pull_at_runtime,
393
+ pull_at_runtime=False,
349
394
  target_dir=target_dir,
350
395
  )
351
396
 
352
- @classmethod
353
- def get_filename_and_handler(cls) -> (str, str):
397
+ def from_image(self, image):
398
+ """
399
+ Deploy the function with an existing nuclio processor image.
400
+ This applies only for the reverse proxy and not the application image.
401
+
402
+ :param image: image name
403
+ """
404
+ super().from_image(image)
405
+ # nuclio implementation detail - when providing the image and emptying out the source code and build source,
406
+ # nuclio skips rebuilding the image and simply takes the prebuilt image
407
+ self.spec.build.functionSourceCode = ""
408
+ self.status.application_source = self.spec.build.source
409
+ self.spec.build.source = ""
410
+
411
+ # save the image in the status, so we won't repopulate the function source code
412
+ self.status.container_image = image
413
+
414
+ # ensure golang runtime and handler for the reverse proxy
415
+ self.spec.nuclio_runtime = "golang"
416
+ update_in(
417
+ self.spec.base_spec,
418
+ "spec.handler",
419
+ "main:Handler",
420
+ )
421
+
422
+ @staticmethod
423
+ def get_filename_and_handler() -> (str, str):
354
424
  reverse_proxy_file_path = pathlib.Path(__file__).parent / "reverse_proxy.go"
355
425
  return str(reverse_proxy_file_path), "Handler"
356
426
 
@@ -358,16 +428,55 @@ class ApplicationRuntime(RemoteRuntime):
358
428
  self,
359
429
  name: str = None,
360
430
  path: str = None,
361
- ports: list[int] = None,
431
+ direct_port_access: bool = False,
362
432
  authentication_mode: schemas.APIGatewayAuthenticationMode = None,
363
- authentication_creds: tuple[str] = None,
433
+ authentication_creds: tuple[str, str] = None,
434
+ ssl_redirect: bool = None,
435
+ set_as_default: bool = False,
436
+ gateway_timeout: typing.Optional[int] = None,
364
437
  ):
438
+ """
439
+ Create the application API gateway. Once the application is deployed, the API gateway can be created.
440
+ An application without an API gateway is not accessible.
441
+
442
+ :param name: The name of the API gateway
443
+ :param path: Optional path of the API gateway, default value is "/".
444
+ The given path should be supported by the deployed application
445
+ :param direct_port_access: Set True to allow direct port access to the application sidecar
446
+ :param authentication_mode: API Gateway authentication mode
447
+ :param authentication_creds: API Gateway basic authentication credentials as a tuple (username, password)
448
+ :param ssl_redirect: Set True to force SSL redirect, False to disable. Defaults to
449
+ mlrun.mlconf.force_api_gateway_ssl_redirect()
450
+ :param set_as_default: Set the API gateway as the default for the application (`status.api_gateway`)
451
+ :param gateway_timeout: nginx ingress timeout in sec (request timeout, when will the gateway return an
452
+ error)
453
+ :return: The API gateway URL
454
+ """
455
+ if not name:
456
+ raise mlrun.errors.MLRunInvalidArgumentError(
457
+ "API gateway name must be specified."
458
+ )
459
+
460
+ if not set_as_default and name == self.resolve_default_api_gateway_name():
461
+ raise mlrun.errors.MLRunInvalidArgumentError(
462
+ f"Non-default API gateway cannot use the default gateway name, {name=}."
463
+ )
464
+
465
+ if (
466
+ authentication_mode == schemas.APIGatewayAuthenticationMode.basic
467
+ and not authentication_creds
468
+ ):
469
+ raise mlrun.errors.MLRunInvalidArgumentError(
470
+ "Authentication credentials not provided"
471
+ )
472
+
473
+ ports = self.spec.internal_application_port if direct_port_access else []
474
+
365
475
  api_gateway = APIGateway(
366
476
  APIGatewayMetadata(
367
477
  name=name,
368
478
  namespace=self.metadata.namespace,
369
- labels=self.metadata.labels,
370
- annotations=self.metadata.annotations,
479
+ labels=self.metadata.labels.copy(),
371
480
  ),
372
481
  APIGatewaySpec(
373
482
  functions=[self],
@@ -377,6 +486,14 @@ class ApplicationRuntime(RemoteRuntime):
377
486
  ),
378
487
  )
379
488
 
489
+ api_gateway.with_gateway_timeout(gateway_timeout)
490
+ if ssl_redirect is None:
491
+ ssl_redirect = mlrun.mlconf.force_api_gateway_ssl_redirect()
492
+ if ssl_redirect:
493
+ # Force ssl redirect so that the application is only accessible via https
494
+ api_gateway.with_force_ssl_redirect()
495
+
496
+ # Add authentication if required
380
497
  authentication_mode = (
381
498
  authentication_mode
382
499
  or mlrun.mlconf.function.application.default_authentication_mode
@@ -390,28 +507,64 @@ class ApplicationRuntime(RemoteRuntime):
390
507
  api_gateway_scheme = db.store_api_gateway(
391
508
  api_gateway=api_gateway.to_scheme(), project=self.metadata.project
392
509
  )
393
- if not self.status.api_gateway_name:
510
+
511
+ if set_as_default:
394
512
  self.status.api_gateway_name = api_gateway_scheme.metadata.name
395
- self.status.api_gateway = APIGateway.from_scheme(api_gateway_scheme)
396
- self.status.api_gateway.wait_for_readiness()
397
- self.url = self.status.api_gateway.invoke_url
513
+ self.status.api_gateway = APIGateway.from_scheme(api_gateway_scheme)
514
+ self.status.api_gateway.wait_for_readiness()
515
+ self.url = self.status.api_gateway.invoke_url
516
+ url = self.url
517
+ else:
518
+ api_gateway = APIGateway.from_scheme(api_gateway_scheme)
519
+ api_gateway.wait_for_readiness()
520
+ url = api_gateway.invoke_url
521
+ # Update application status (enriches invocation url)
522
+ self._get_state(raise_on_exception=False)
523
+
524
+ logger.info("Successfully created API gateway", url=url)
525
+ return url
526
+
527
+ def delete_api_gateway(self, name: str):
528
+ """
529
+ Delete API gateway by name.
530
+ Refreshes the application status to update api gateway and invocation URLs.
531
+ :param name: The API gateway name
532
+ """
533
+ self._get_db().delete_api_gateway(name=name, project=self.metadata.project)
534
+ if name == self.status.api_gateway_name:
535
+ self.status.api_gateway_name = None
536
+ self.status.api_gateway = None
537
+ self._get_state()
398
538
 
399
539
  def invoke(
400
540
  self,
401
- path: str,
402
- body: typing.Union[str, bytes, dict] = None,
541
+ path: str = "",
542
+ body: typing.Optional[typing.Union[str, bytes, dict]] = None,
403
543
  method: str = None,
404
544
  headers: dict = None,
405
545
  dashboard: str = "",
406
546
  force_external_address: bool = False,
407
547
  auth_info: schemas.AuthInfo = None,
408
548
  mock: bool = None,
549
+ credentials: tuple[str, str] = None,
409
550
  **http_client_kwargs,
410
551
  ):
411
552
  self._sync_api_gateway()
553
+
412
554
  # If the API Gateway is not ready or not set, try to invoke the function directly (without the API Gateway)
413
555
  if not self.status.api_gateway:
414
- super().invoke(
556
+ logger.warning(
557
+ "Default API gateway is not configured, invoking function invocation URL."
558
+ )
559
+ # create a requests auth object if credentials are provided and not already set in the http client kwargs
560
+ auth = http_client_kwargs.pop("auth", None) or (
561
+ nuclio.auth.AuthInfo(
562
+ username=credentials[0], password=credentials[1]
563
+ ).to_requests_auth()
564
+ if credentials
565
+ else None
566
+ )
567
+ return super().invoke(
415
568
  path,
416
569
  body,
417
570
  method,
@@ -420,21 +573,84 @@ class ApplicationRuntime(RemoteRuntime):
420
573
  force_external_address,
421
574
  auth_info,
422
575
  mock,
576
+ auth=auth,
423
577
  **http_client_kwargs,
424
578
  )
425
579
 
426
- credentials = (auth_info.username, auth_info.password) if auth_info else None
427
-
428
580
  if not method:
429
581
  method = "POST" if body else "GET"
582
+
430
583
  return self.status.api_gateway.invoke(
431
584
  method=method,
432
585
  headers=headers,
433
586
  credentials=credentials,
434
587
  path=path,
588
+ body=body,
435
589
  **http_client_kwargs,
436
590
  )
437
591
 
592
+ @classmethod
593
+ def deploy_reverse_proxy_image(cls):
594
+ """
595
+ Build the reverse proxy image and save it.
596
+ The reverse proxy image is used to route requests to the application sidecar.
597
+ This is useful when you want to decrease build time by building the application image only once.
598
+
599
+ :param use_cache: Use the cache when building the image
600
+ """
601
+ # create a function that includes only the reverse proxy, without the application
602
+
603
+ reverse_proxy_func = mlrun.run.new_function(
604
+ name="reverse-proxy-temp", kind="remote"
605
+ )
606
+ # default max replicas is 4, we only need one replica for the reverse proxy
607
+ reverse_proxy_func.spec.max_replicas = 1
608
+
609
+ # the reverse proxy image should not be based on another image
610
+ reverse_proxy_func.set_config("spec.build.baseImage", None)
611
+ reverse_proxy_func.spec.image = ""
612
+ reverse_proxy_func.spec.build.base_image = ""
613
+
614
+ cls._ensure_reverse_proxy_configurations(reverse_proxy_func)
615
+ reverse_proxy_func.deploy()
616
+
617
+ # save the created container image
618
+ cls.reverse_proxy_image = reverse_proxy_func.status.container_image
619
+
620
+ # delete the function to avoid cluttering the project
621
+ mlrun.get_run_db().delete_function(
622
+ reverse_proxy_func.metadata.name, reverse_proxy_func.metadata.project
623
+ )
624
+
625
+ def resolve_default_api_gateway_name(self):
626
+ return (
627
+ f"{self.metadata.name}-{self.metadata.tag}"
628
+ if self.metadata.tag
629
+ else self.metadata.name
630
+ )
631
+
632
+ @min_nuclio_versions("1.13.1")
633
+ def disable_default_http_trigger(
634
+ self,
635
+ ):
636
+ raise mlrun.runtimes.RunError(
637
+ "Application runtime does not support disabling the default HTTP trigger"
638
+ )
639
+
640
+ @min_nuclio_versions("1.13.1")
641
+ def enable_default_http_trigger(
642
+ self,
643
+ ):
644
+ pass
645
+
646
+ def _run(self, runobj: "mlrun.RunObject", execution):
647
+ raise mlrun.runtimes.RunError(
648
+ "Application runtime .run() is not yet supported. Use .invoke() instead."
649
+ )
650
+
651
+ def _enrich_command_from_status(self):
652
+ pass
653
+
438
654
  def _build_application_image(
439
655
  self,
440
656
  builder_env: dict = None,
@@ -454,6 +670,13 @@ class ApplicationRuntime(RemoteRuntime):
454
670
  args=self.spec.args,
455
671
  )
456
672
 
673
+ if self.spec.build.source in [".", "./"]:
674
+ logger.info(
675
+ "The application is configured to use the project's source. "
676
+ "Application runtime requires loading the source into the application image. "
677
+ "Loading on build will be forced regardless of whether 'pull_at_runtime=True' was configured."
678
+ )
679
+
457
680
  with_mlrun = self._resolve_build_with_mlrun(with_mlrun)
458
681
  return self._build_image(
459
682
  builder_env=builder_env,
@@ -466,21 +689,29 @@ class ApplicationRuntime(RemoteRuntime):
466
689
  with_mlrun=with_mlrun,
467
690
  )
468
691
 
469
- def _ensure_reverse_proxy_configurations(self):
470
- if self.spec.build.functionSourceCode or self.status.container_image:
692
+ @staticmethod
693
+ def _ensure_reverse_proxy_configurations(function: RemoteRuntime):
694
+ if function.spec.build.functionSourceCode or function.status.container_image:
471
695
  return
472
696
 
473
697
  filename, handler = ApplicationRuntime.get_filename_and_handler()
474
698
  name, spec, code = nuclio.build_file(
475
699
  filename,
476
- name=self.metadata.name,
700
+ name=function.metadata.name,
477
701
  handler=handler,
478
702
  )
479
- self.spec.function_handler = mlrun.utils.get_in(spec, "spec.handler")
480
- self.spec.build.functionSourceCode = mlrun.utils.get_in(
703
+ function.spec.function_handler = mlrun.utils.get_in(spec, "spec.handler")
704
+ function.spec.build.functionSourceCode = mlrun.utils.get_in(
481
705
  spec, "spec.build.functionSourceCode"
482
706
  )
483
- self.spec.nuclio_runtime = mlrun.utils.get_in(spec, "spec.runtime")
707
+ function.spec.nuclio_runtime = mlrun.utils.get_in(spec, "spec.runtime")
708
+
709
+ # default the reverse proxy logger level to info
710
+ logger_sinks_key = "spec.loggerSinks"
711
+ if not function.spec.config.get(logger_sinks_key):
712
+ function.set_config(
713
+ logger_sinks_key, [{"level": "info", "sink": "myStdoutLoggerSink"}]
714
+ )
484
715
 
485
716
  def _configure_application_sidecar(self):
486
717
  # Save the application image in the status to allow overriding it with the reverse proxy entry point
@@ -491,11 +722,12 @@ class ApplicationRuntime(RemoteRuntime):
491
722
  self.status.application_image = self.spec.image
492
723
  self.spec.image = ""
493
724
 
494
- if self.status.container_image:
495
- self.from_image(self.status.container_image)
496
- # nuclio implementation detail - when providing the image and emptying out the source code,
497
- # nuclio skips rebuilding the image and simply takes the prebuilt image
498
- self.spec.build.functionSourceCode = ""
725
+ # reuse the reverse proxy image if it was built before
726
+ if (
727
+ reverse_proxy_image := self.status.container_image
728
+ or self.reverse_proxy_image
729
+ ):
730
+ self.from_image(reverse_proxy_image)
499
731
 
500
732
  self.status.sidecar_name = f"{self.metadata.name}-sidecar"
501
733
  self.with_sidecar(
@@ -508,6 +740,11 @@ class ApplicationRuntime(RemoteRuntime):
508
740
  self.set_env("SIDECAR_PORT", self.spec.internal_application_port)
509
741
  self.set_env("SIDECAR_HOST", "http://localhost")
510
742
 
743
+ # configure the sidecar container as the default container for logging purposes
744
+ self.metadata.annotations["kubectl.kubernetes.io/default-container"] = (
745
+ self.status.sidecar_name
746
+ )
747
+
511
748
  def _sync_api_gateway(self):
512
749
  if not self.status.api_gateway_name:
513
750
  return