mlrun 1.10.0rc40__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.

Files changed (150) hide show
  1. mlrun/__init__.py +3 -2
  2. mlrun/__main__.py +0 -4
  3. mlrun/artifacts/dataset.py +2 -2
  4. mlrun/artifacts/plots.py +1 -1
  5. mlrun/{model_monitoring/db/tsdb/tdengine → auth}/__init__.py +2 -3
  6. mlrun/auth/nuclio.py +89 -0
  7. mlrun/auth/providers.py +429 -0
  8. mlrun/auth/utils.py +415 -0
  9. mlrun/common/constants.py +7 -0
  10. mlrun/common/model_monitoring/helpers.py +41 -4
  11. mlrun/common/runtimes/constants.py +28 -0
  12. mlrun/common/schemas/__init__.py +13 -3
  13. mlrun/common/schemas/alert.py +2 -2
  14. mlrun/common/schemas/api_gateway.py +3 -0
  15. mlrun/common/schemas/auth.py +10 -10
  16. mlrun/common/schemas/client_spec.py +4 -0
  17. mlrun/common/schemas/constants.py +25 -0
  18. mlrun/common/schemas/frontend_spec.py +1 -8
  19. mlrun/common/schemas/function.py +24 -0
  20. mlrun/common/schemas/hub.py +3 -2
  21. mlrun/common/schemas/model_monitoring/__init__.py +1 -1
  22. mlrun/common/schemas/model_monitoring/constants.py +2 -2
  23. mlrun/common/schemas/secret.py +17 -2
  24. mlrun/common/secrets.py +95 -1
  25. mlrun/common/types.py +10 -10
  26. mlrun/config.py +53 -15
  27. mlrun/data_types/infer.py +2 -2
  28. mlrun/datastore/__init__.py +2 -3
  29. mlrun/datastore/base.py +274 -10
  30. mlrun/datastore/datastore.py +1 -1
  31. mlrun/datastore/datastore_profile.py +49 -17
  32. mlrun/datastore/model_provider/huggingface_provider.py +6 -2
  33. mlrun/datastore/model_provider/model_provider.py +2 -2
  34. mlrun/datastore/model_provider/openai_provider.py +2 -2
  35. mlrun/datastore/s3.py +15 -16
  36. mlrun/datastore/sources.py +1 -1
  37. mlrun/datastore/store_resources.py +4 -4
  38. mlrun/datastore/storeytargets.py +16 -10
  39. mlrun/datastore/targets.py +1 -1
  40. mlrun/datastore/utils.py +16 -3
  41. mlrun/datastore/v3io.py +1 -1
  42. mlrun/db/base.py +36 -12
  43. mlrun/db/httpdb.py +316 -101
  44. mlrun/db/nopdb.py +29 -11
  45. mlrun/errors.py +4 -2
  46. mlrun/execution.py +11 -12
  47. mlrun/feature_store/api.py +1 -1
  48. mlrun/feature_store/common.py +1 -1
  49. mlrun/feature_store/feature_vector_utils.py +1 -1
  50. mlrun/feature_store/steps.py +8 -6
  51. mlrun/frameworks/_common/utils.py +3 -3
  52. mlrun/frameworks/_dl_common/loggers/logger.py +1 -1
  53. mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +2 -1
  54. mlrun/frameworks/_ml_common/loggers/mlrun_logger.py +1 -1
  55. mlrun/frameworks/_ml_common/utils.py +2 -1
  56. mlrun/frameworks/auto_mlrun/auto_mlrun.py +4 -3
  57. mlrun/frameworks/lgbm/mlrun_interfaces/mlrun_interface.py +2 -1
  58. mlrun/frameworks/onnx/dataset.py +2 -1
  59. mlrun/frameworks/onnx/mlrun_interface.py +2 -1
  60. mlrun/frameworks/pytorch/callbacks/logging_callback.py +5 -4
  61. mlrun/frameworks/pytorch/callbacks/mlrun_logging_callback.py +2 -1
  62. mlrun/frameworks/pytorch/callbacks/tensorboard_logging_callback.py +2 -1
  63. mlrun/frameworks/pytorch/utils.py +2 -1
  64. mlrun/frameworks/sklearn/metric.py +2 -1
  65. mlrun/frameworks/tf_keras/callbacks/logging_callback.py +5 -4
  66. mlrun/frameworks/tf_keras/callbacks/mlrun_logging_callback.py +2 -1
  67. mlrun/frameworks/tf_keras/callbacks/tensorboard_logging_callback.py +2 -1
  68. mlrun/hub/__init__.py +37 -0
  69. mlrun/hub/base.py +142 -0
  70. mlrun/hub/module.py +67 -76
  71. mlrun/hub/step.py +113 -0
  72. mlrun/launcher/base.py +2 -1
  73. mlrun/launcher/local.py +2 -1
  74. mlrun/model.py +12 -2
  75. mlrun/model_monitoring/__init__.py +0 -1
  76. mlrun/model_monitoring/api.py +2 -2
  77. mlrun/model_monitoring/applications/base.py +20 -6
  78. mlrun/model_monitoring/applications/context.py +1 -0
  79. mlrun/model_monitoring/controller.py +7 -17
  80. mlrun/model_monitoring/db/_schedules.py +2 -16
  81. mlrun/model_monitoring/db/_stats.py +2 -13
  82. mlrun/model_monitoring/db/tsdb/__init__.py +9 -7
  83. mlrun/model_monitoring/db/tsdb/base.py +2 -4
  84. mlrun/model_monitoring/db/tsdb/preaggregate.py +234 -0
  85. mlrun/model_monitoring/db/tsdb/stream_graph_steps.py +63 -0
  86. mlrun/model_monitoring/db/tsdb/timescaledb/queries/timescaledb_metrics_queries.py +414 -0
  87. mlrun/model_monitoring/db/tsdb/timescaledb/queries/timescaledb_predictions_queries.py +376 -0
  88. mlrun/model_monitoring/db/tsdb/timescaledb/queries/timescaledb_results_queries.py +590 -0
  89. mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_connection.py +434 -0
  90. mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_connector.py +541 -0
  91. mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_operations.py +808 -0
  92. mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_schema.py +502 -0
  93. mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_stream.py +163 -0
  94. mlrun/model_monitoring/db/tsdb/timescaledb/timescaledb_stream_graph_steps.py +60 -0
  95. mlrun/model_monitoring/db/tsdb/timescaledb/utils/timescaledb_dataframe_processor.py +141 -0
  96. mlrun/model_monitoring/db/tsdb/timescaledb/utils/timescaledb_query_builder.py +585 -0
  97. mlrun/model_monitoring/db/tsdb/timescaledb/writer_graph_steps.py +73 -0
  98. mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +4 -6
  99. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +147 -79
  100. mlrun/model_monitoring/features_drift_table.py +2 -1
  101. mlrun/model_monitoring/helpers.py +2 -1
  102. mlrun/model_monitoring/stream_processing.py +18 -16
  103. mlrun/model_monitoring/writer.py +4 -3
  104. mlrun/package/__init__.py +2 -1
  105. mlrun/platforms/__init__.py +0 -44
  106. mlrun/platforms/iguazio.py +1 -1
  107. mlrun/projects/operations.py +11 -10
  108. mlrun/projects/project.py +81 -82
  109. mlrun/run.py +4 -7
  110. mlrun/runtimes/__init__.py +2 -204
  111. mlrun/runtimes/base.py +89 -21
  112. mlrun/runtimes/constants.py +225 -0
  113. mlrun/runtimes/daskjob.py +4 -2
  114. mlrun/runtimes/databricks_job/databricks_runtime.py +2 -1
  115. mlrun/runtimes/mounts.py +5 -0
  116. mlrun/runtimes/nuclio/__init__.py +12 -8
  117. mlrun/runtimes/nuclio/api_gateway.py +36 -6
  118. mlrun/runtimes/nuclio/application/application.py +200 -32
  119. mlrun/runtimes/nuclio/function.py +154 -49
  120. mlrun/runtimes/nuclio/serving.py +55 -42
  121. mlrun/runtimes/pod.py +59 -10
  122. mlrun/secrets.py +46 -2
  123. mlrun/serving/__init__.py +2 -0
  124. mlrun/serving/remote.py +5 -5
  125. mlrun/serving/routers.py +3 -3
  126. mlrun/serving/server.py +46 -43
  127. mlrun/serving/serving_wrapper.py +6 -2
  128. mlrun/serving/states.py +554 -207
  129. mlrun/serving/steps.py +1 -1
  130. mlrun/serving/system_steps.py +42 -33
  131. mlrun/track/trackers/mlflow_tracker.py +29 -31
  132. mlrun/utils/helpers.py +89 -16
  133. mlrun/utils/http.py +9 -2
  134. mlrun/utils/notifications/notification/git.py +1 -1
  135. mlrun/utils/notifications/notification/mail.py +39 -16
  136. mlrun/utils/notifications/notification_pusher.py +2 -2
  137. mlrun/utils/version/version.json +2 -2
  138. mlrun/utils/version/version.py +3 -4
  139. {mlrun-1.10.0rc40.dist-info → mlrun-1.11.0rc16.dist-info}/METADATA +39 -49
  140. {mlrun-1.10.0rc40.dist-info → mlrun-1.11.0rc16.dist-info}/RECORD +144 -130
  141. mlrun/db/auth_utils.py +0 -152
  142. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +0 -343
  143. mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +0 -75
  144. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connection.py +0 -281
  145. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +0 -1368
  146. mlrun/model_monitoring/db/tsdb/tdengine/writer_graph_steps.py +0 -51
  147. {mlrun-1.10.0rc40.dist-info → mlrun-1.11.0rc16.dist-info}/WHEEL +0 -0
  148. {mlrun-1.10.0rc40.dist-info → mlrun-1.11.0rc16.dist-info}/entry_points.txt +0 -0
  149. {mlrun-1.10.0rc40.dist-info → mlrun-1.11.0rc16.dist-info}/licenses/LICENSE +0 -0
  150. {mlrun-1.10.0rc40.dist-info → mlrun-1.11.0rc16.dist-info}/top_level.txt +0 -0
@@ -11,6 +11,7 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+ import copy
14
15
  import pathlib
15
16
  import typing
16
17
 
@@ -20,23 +21,18 @@ import nuclio.auth
20
21
  import mlrun.common.schemas as schemas
21
22
  import mlrun.errors
22
23
  import mlrun.run
23
- from mlrun.common.runtimes.constants import NuclioIngressAddTemplatedIngressModes
24
- from mlrun.runtimes import RemoteRuntime
25
- from mlrun.runtimes.nuclio import (
26
- min_nuclio_versions,
27
- multiple_port_sidecar_is_supported,
24
+ import mlrun.runtimes.nuclio.api_gateway as nuclio_api_gateway
25
+ import mlrun.runtimes.nuclio.function as nuclio_function
26
+ from mlrun.common.runtimes.constants import (
27
+ NuclioIngressAddTemplatedIngressModes,
28
+ ProbeTimeConfig,
29
+ ProbeType,
28
30
  )
29
- from mlrun.runtimes.nuclio.api_gateway import (
30
- APIGateway,
31
- APIGatewayMetadata,
32
- APIGatewaySpec,
33
- )
34
- from mlrun.runtimes.nuclio.function import NuclioSpec, NuclioStatus
35
31
  from mlrun.utils import is_valid_port, logger, update_in
36
32
 
37
33
 
38
- class ApplicationSpec(NuclioSpec):
39
- _dict_fields = NuclioSpec._dict_fields + [
34
+ class ApplicationSpec(nuclio_function.NuclioSpec):
35
+ _dict_fields = nuclio_function.NuclioSpec._dict_fields + [
40
36
  "internal_application_port",
41
37
  "application_ports",
42
38
  ]
@@ -88,6 +84,7 @@ class ApplicationSpec(NuclioSpec):
88
84
  track_models=None,
89
85
  internal_application_port=None,
90
86
  application_ports=None,
87
+ auth=None,
91
88
  ):
92
89
  super().__init__(
93
90
  command=command,
@@ -133,6 +130,7 @@ class ApplicationSpec(NuclioSpec):
133
130
  track_models=track_models,
134
131
  state_thresholds=state_thresholds,
135
132
  disable_default_http_trigger=disable_default_http_trigger,
133
+ auth=auth,
136
134
  )
137
135
 
138
136
  # Override default min/max replicas (don't assume application is stateless)
@@ -189,7 +187,7 @@ class ApplicationSpec(NuclioSpec):
189
187
 
190
188
  # ensure multiple ports are supported in Nuclio
191
189
  if len(application_ports) > 1:
192
- multiple_port_sidecar_is_supported()
190
+ nuclio_function.multiple_port_sidecar_is_supported()
193
191
 
194
192
  self._application_ports = application_ports
195
193
 
@@ -215,7 +213,7 @@ class ApplicationSpec(NuclioSpec):
215
213
  self.application_ports = self._application_ports
216
214
 
217
215
 
218
- class ApplicationStatus(NuclioStatus):
216
+ class ApplicationStatus(nuclio_function.NuclioStatus):
219
217
  def __init__(
220
218
  self,
221
219
  state=None,
@@ -245,15 +243,17 @@ class ApplicationStatus(NuclioStatus):
245
243
  self.application_source = application_source or None
246
244
  self.sidecar_name = sidecar_name or None
247
245
  self.api_gateway_name = api_gateway_name or None
248
- self.api_gateway: typing.Optional[APIGateway] = api_gateway or None
246
+ self.api_gateway: typing.Optional[nuclio_api_gateway.APIGateway] = (
247
+ api_gateway or None
248
+ )
249
249
  self.url = url or None
250
250
 
251
251
 
252
- class ApplicationRuntime(RemoteRuntime):
252
+ class ApplicationRuntime(nuclio_function.RemoteRuntime):
253
253
  kind = "application"
254
254
  reverse_proxy_image = None
255
255
 
256
- @min_nuclio_versions("1.13.1")
256
+ @nuclio_function.min_nuclio_versions("1.13.1")
257
257
  def __init__(self, spec=None, metadata=None):
258
258
  super().__init__(spec=spec, metadata=metadata)
259
259
 
@@ -278,7 +278,7 @@ class ApplicationRuntime(RemoteRuntime):
278
278
  return self.status.api_gateway
279
279
 
280
280
  @api_gateway.setter
281
- def api_gateway(self, api_gateway: APIGateway):
281
+ def api_gateway(self, api_gateway: nuclio_api_gateway.APIGateway):
282
282
  self.status.api_gateway = api_gateway
283
283
 
284
284
  @property
@@ -294,6 +294,96 @@ class ApplicationRuntime(RemoteRuntime):
294
294
  def set_internal_application_port(self, port: int):
295
295
  self.spec.internal_application_port = port
296
296
 
297
+ def set_probe(
298
+ self,
299
+ type: str,
300
+ initial_delay_seconds: int | None = None,
301
+ period_seconds: int | None = None,
302
+ failure_threshold: int | None = None,
303
+ timeout_seconds: int | None = None,
304
+ http_path: str | None = None,
305
+ http_port: int | None = None,
306
+ http_scheme: str | None = None,
307
+ config: dict | None = None,
308
+ ):
309
+ """Set a Kubernetes probe configuration for the sidecar container
310
+
311
+ The config parameter serves as the base configuration, and individual parameters
312
+ override values in config. If http_path is provided without http_port and config
313
+ is not provided, the port will be enriched from the internal application port
314
+ just before deployment.
315
+
316
+ :param type: Probe type - one of "readiness", "liveness", "startup"
317
+ :param initial_delay_seconds: Number of seconds after the container has started before probes are initiated
318
+ :param period_seconds: How often (in seconds) to perform the probe
319
+ :param failure_threshold: Minimum consecutive failures for the probe to be considered failed
320
+ :param timeout_seconds: Number of seconds after which the probe times out
321
+ :param http_path: If provided, use an HTTP probe with this path
322
+ :param http_port: If HTTP probe is used and no port provided,
323
+ the internal application port will be used
324
+ :param http_scheme: "http" or "https" for HTTP probe. Defaults to "http"
325
+ :param config: A full dict with the probe configuration
326
+ (used as base, overridden by individual parameters)
327
+
328
+ :return: function object (self)
329
+ """
330
+ self._validate_set_probes_input(locals())
331
+ type = ProbeType(type)
332
+
333
+ # Start with config as base
334
+ probe_config = copy.deepcopy(config) if config else {}
335
+
336
+ # Build HTTP probe configuration if http_path is provided
337
+ # Note: If http_path is None, all HTTP-related parameters (http_port, http_scheme) are ignored
338
+ if http_path:
339
+ http_probe = probe_config.get("httpGet", {})
340
+ http_probe["path"] = http_path
341
+ if http_port is not None:
342
+ http_probe["port"] = http_port
343
+ http_probe["scheme"] = http_scheme or http_probe.get("scheme", "HTTP")
344
+ probe_config["httpGet"] = http_probe
345
+
346
+ # Override timing parameters from explicit arguments
347
+ probe_config.update(
348
+ {
349
+ config.value: value
350
+ for config, value in {
351
+ ProbeTimeConfig.INITIAL_DELAY_SECONDS: initial_delay_seconds,
352
+ ProbeTimeConfig.PERIOD_SECONDS: period_seconds,
353
+ ProbeTimeConfig.FAILURE_THRESHOLD: failure_threshold,
354
+ ProbeTimeConfig.TIMEOUT_SECONDS: timeout_seconds,
355
+ }.items()
356
+ if value is not None
357
+ }
358
+ )
359
+
360
+ # Store probe configuration in the sidecar
361
+ sidecar = self._set_sidecar(self._get_sidecar_name())
362
+ sidecar[type.key] = probe_config
363
+
364
+ return self
365
+
366
+ def delete_probe(
367
+ self,
368
+ type: str,
369
+ ):
370
+ """Delete a Kubernetes probe configuration from the sidecar container
371
+
372
+ :param type: Probe type - one of "readiness", "liveness", "startup"
373
+
374
+ :return: function object (self)
375
+ """
376
+ # Validate probe type
377
+ ProbeType.is_valid(type, raise_on_error=True)
378
+ type = ProbeType(type)
379
+
380
+ sidecar = self._get_sidecar()
381
+ if sidecar:
382
+ if type.key in sidecar:
383
+ del sidecar[type.key]
384
+
385
+ return self
386
+
297
387
  def with_sidecar(
298
388
  self,
299
389
  name: typing.Optional[str] = None,
@@ -400,7 +490,6 @@ class ApplicationRuntime(RemoteRuntime):
400
490
 
401
491
  :return: The default API gateway URL if created or True if the function is ready (deployed)
402
492
  """
403
- mlrun.utils.helpers.validate_function_name(self.metadata.name)
404
493
 
405
494
  if (self.requires_build() and not self.spec.image) or force_build:
406
495
  self._fill_credentials()
@@ -422,6 +511,7 @@ class ApplicationRuntime(RemoteRuntime):
422
511
  self.spec.add_templated_ingress_host_mode = (
423
512
  NuclioIngressAddTemplatedIngressModes.never
424
513
  )
514
+ self._enrich_sidecar_probe_ports()
425
515
 
426
516
  super().deploy(
427
517
  project=project,
@@ -569,23 +659,23 @@ class ApplicationRuntime(RemoteRuntime):
569
659
  "Authentication credentials not provided"
570
660
  )
571
661
 
572
- if direct_port_access and port:
662
+ if not direct_port_access and port:
573
663
  logger.warning(
574
- "Ignoring 'port' because 'direct_port_access' is enabled. "
575
- "The 'port' setting is only applicable when 'direct_port_access' is disabled."
664
+ "Ignoring 'port' because 'direct_port_access' is not enabled. "
665
+ "The 'port' setting is only applicable when 'direct_port_access' is enabled."
576
666
  )
577
667
 
578
668
  ports = (
579
669
  port or self.spec.internal_application_port if direct_port_access else []
580
670
  )
581
671
 
582
- api_gateway = APIGateway(
583
- APIGatewayMetadata(
672
+ api_gateway = nuclio_api_gateway.APIGateway(
673
+ nuclio_api_gateway.APIGatewayMetadata(
584
674
  name=name,
585
675
  namespace=self.metadata.namespace,
586
676
  labels=self.metadata.labels.copy(),
587
677
  ),
588
- APIGatewaySpec(
678
+ nuclio_api_gateway.APIGatewaySpec(
589
679
  functions=[self],
590
680
  project=self.metadata.project,
591
681
  path=path,
@@ -609,6 +699,8 @@ class ApplicationRuntime(RemoteRuntime):
609
699
  api_gateway.with_access_key_auth()
610
700
  elif authentication_mode == schemas.APIGatewayAuthenticationMode.basic:
611
701
  api_gateway.with_basic_auth(*authentication_creds)
702
+ elif authentication_mode == schemas.APIGatewayAuthenticationMode.iguazio:
703
+ api_gateway.with_iguazio_auth()
612
704
 
613
705
  db = self._get_db()
614
706
  api_gateway_scheme = db.store_api_gateway(
@@ -617,12 +709,14 @@ class ApplicationRuntime(RemoteRuntime):
617
709
 
618
710
  if set_as_default:
619
711
  self.status.api_gateway_name = api_gateway_scheme.metadata.name
620
- self.status.api_gateway = APIGateway.from_scheme(api_gateway_scheme)
712
+ self.status.api_gateway = nuclio_api_gateway.APIGateway.from_scheme(
713
+ api_gateway_scheme
714
+ )
621
715
  self.status.api_gateway.wait_for_readiness()
622
716
  self.url = self.status.api_gateway.invoke_url
623
717
  url = self.url
624
718
  else:
625
- api_gateway = APIGateway.from_scheme(api_gateway_scheme)
719
+ api_gateway = nuclio_api_gateway.APIGateway.from_scheme(api_gateway_scheme)
626
720
  api_gateway.wait_for_readiness()
627
721
  url = api_gateway.invoke_url
628
722
  # Update application status (enriches invocation url)
@@ -646,7 +740,7 @@ class ApplicationRuntime(RemoteRuntime):
646
740
  def invoke(
647
741
  self,
648
742
  path: str = "",
649
- body: typing.Optional[typing.Union[str, bytes, dict]] = None,
743
+ body: typing.Optional[typing.Union[str, bytes, dict, list]] = None,
650
744
  method: typing.Optional[str] = None,
651
745
  headers: typing.Optional[dict] = None,
652
746
  force_external_address: bool = False,
@@ -740,7 +834,7 @@ class ApplicationRuntime(RemoteRuntime):
740
834
  else self.metadata.name
741
835
  )
742
836
 
743
- @min_nuclio_versions("1.13.1")
837
+ @nuclio_function.min_nuclio_versions("1.13.1")
744
838
  def disable_default_http_trigger(
745
839
  self,
746
840
  ):
@@ -748,7 +842,7 @@ class ApplicationRuntime(RemoteRuntime):
748
842
  "Application runtime does not support disabling the default HTTP trigger"
749
843
  )
750
844
 
751
- @min_nuclio_versions("1.13.1")
845
+ @nuclio_function.min_nuclio_versions("1.13.1")
752
846
  def enable_default_http_trigger(
753
847
  self,
754
848
  ):
@@ -879,6 +973,80 @@ class ApplicationRuntime(RemoteRuntime):
879
973
  api_gateway_scheme = db.get_api_gateway(
880
974
  name=self.status.api_gateway_name, project=self.metadata.project
881
975
  )
882
- self.status.api_gateway = APIGateway.from_scheme(api_gateway_scheme)
976
+ self.status.api_gateway = nuclio_api_gateway.APIGateway.from_scheme(
977
+ api_gateway_scheme
978
+ )
883
979
  self.status.api_gateway.wait_for_readiness()
884
980
  self.url = self.status.api_gateway.invoke_url
981
+
982
+ def _enrich_sidecar_probe_ports(self):
983
+ """Enrich sidecar probe configurations with internal application port if needed
984
+
985
+ This method is called just before deployment to automatically enrich HTTP probes
986
+ in the sidecar container that were configured without an explicit port.
987
+
988
+ Enrichment logic:
989
+ - Only enriches HTTP probes (httpGet) that don't already have a port specified
990
+ - If the user explicitly provided http_port in set_probe(), enrichment is skipped
991
+ - If the user provided a port in the config dict, enrichment is skipped
992
+ - Enrichment happens just before deployment to ensure the latest internal_application_port
993
+ value is used, even if it was modified after set_probe() was called
994
+ """
995
+ # Check each probe type and enrich missing HTTP ports
996
+ sidecar = self._get_sidecar()
997
+ if not sidecar:
998
+ return
999
+
1000
+ for probe_type in ProbeType:
1001
+ probe_config = sidecar.get(probe_type.key)
1002
+
1003
+ if probe_config and isinstance(probe_config, dict):
1004
+ http_get = probe_config.get("httpGet")
1005
+ if http_get and isinstance(http_get, dict) and "port" not in http_get:
1006
+ if self.spec.internal_application_port is None:
1007
+ raise ValueError(
1008
+ f"Cannot enrich {probe_type.value} probe: HTTP probe requires a port, "
1009
+ "but internal_application_port is not set. "
1010
+ "Please set the internal_application_port or provide http_port in set_probe()."
1011
+ )
1012
+ http_get["port"] = self.spec.internal_application_port
1013
+ logger.debug(
1014
+ "Enriched sidecar probe port",
1015
+ probe_type=probe_type.value,
1016
+ port=http_get["port"],
1017
+ application_name=self.metadata.name,
1018
+ )
1019
+ sidecar[probe_type.key] = probe_config
1020
+
1021
+ def _get_sidecar(self) -> dict | None:
1022
+ """Get the sidecar container for ApplicationRuntime
1023
+
1024
+ Returns the sidecar dict if found, None otherwise.
1025
+ """
1026
+ sidecar_name = self._get_sidecar_name()
1027
+ if not hasattr(self.spec, "config") or "spec.sidecars" not in self.spec.config:
1028
+ return None
1029
+
1030
+ sidecars = self.spec.config["spec.sidecars"]
1031
+ for sidecar in sidecars:
1032
+ if sidecar.get("name") == sidecar_name:
1033
+ return sidecar
1034
+
1035
+ return None
1036
+
1037
+ def _get_sidecar_name(self) -> str:
1038
+ return f"{self.metadata.name}-sidecar"
1039
+
1040
+ @staticmethod
1041
+ def _validate_set_probes_input(params: dict):
1042
+ # Validate probe type
1043
+ ProbeType.is_valid(params.get("type"), raise_on_error=True)
1044
+
1045
+ # At least one optional parameter must be provided
1046
+ optional_params = {
1047
+ k: v for k, v in params.items() if (k != "type" and k != "self")
1048
+ }
1049
+ if all(v is None for v in optional_params.values()):
1050
+ raise ValueError(
1051
+ "Empty probe configuration: at least one parameter must be set"
1052
+ )