mlrun 1.10.0rc16__py3-none-any.whl → 1.10.1rc4__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 (101) hide show
  1. mlrun/__init__.py +22 -2
  2. mlrun/artifacts/document.py +6 -1
  3. mlrun/artifacts/llm_prompt.py +21 -15
  4. mlrun/artifacts/model.py +3 -3
  5. mlrun/common/constants.py +9 -0
  6. mlrun/common/formatters/artifact.py +1 -0
  7. mlrun/common/model_monitoring/helpers.py +86 -0
  8. mlrun/common/schemas/__init__.py +2 -0
  9. mlrun/common/schemas/auth.py +2 -0
  10. mlrun/common/schemas/function.py +10 -0
  11. mlrun/common/schemas/hub.py +30 -18
  12. mlrun/common/schemas/model_monitoring/__init__.py +2 -0
  13. mlrun/common/schemas/model_monitoring/constants.py +30 -6
  14. mlrun/common/schemas/model_monitoring/functions.py +13 -4
  15. mlrun/common/schemas/model_monitoring/model_endpoints.py +11 -0
  16. mlrun/common/schemas/pipeline.py +1 -1
  17. mlrun/common/schemas/serving.py +3 -0
  18. mlrun/common/schemas/workflow.py +1 -0
  19. mlrun/common/secrets.py +22 -1
  20. mlrun/config.py +34 -21
  21. mlrun/datastore/__init__.py +11 -3
  22. mlrun/datastore/azure_blob.py +162 -47
  23. mlrun/datastore/base.py +265 -7
  24. mlrun/datastore/datastore.py +10 -5
  25. mlrun/datastore/datastore_profile.py +61 -5
  26. mlrun/datastore/model_provider/huggingface_provider.py +367 -0
  27. mlrun/datastore/model_provider/mock_model_provider.py +87 -0
  28. mlrun/datastore/model_provider/model_provider.py +211 -74
  29. mlrun/datastore/model_provider/openai_provider.py +243 -71
  30. mlrun/datastore/s3.py +24 -2
  31. mlrun/datastore/store_resources.py +4 -4
  32. mlrun/datastore/storeytargets.py +2 -3
  33. mlrun/datastore/utils.py +15 -3
  34. mlrun/db/base.py +27 -19
  35. mlrun/db/httpdb.py +57 -48
  36. mlrun/db/nopdb.py +25 -10
  37. mlrun/execution.py +55 -13
  38. mlrun/hub/__init__.py +15 -0
  39. mlrun/hub/module.py +181 -0
  40. mlrun/k8s_utils.py +105 -16
  41. mlrun/launcher/base.py +13 -6
  42. mlrun/launcher/local.py +2 -0
  43. mlrun/model.py +9 -3
  44. mlrun/model_monitoring/api.py +66 -27
  45. mlrun/model_monitoring/applications/__init__.py +1 -1
  46. mlrun/model_monitoring/applications/base.py +388 -138
  47. mlrun/model_monitoring/applications/context.py +2 -4
  48. mlrun/model_monitoring/applications/results.py +4 -7
  49. mlrun/model_monitoring/controller.py +239 -101
  50. mlrun/model_monitoring/db/_schedules.py +36 -13
  51. mlrun/model_monitoring/db/_stats.py +4 -3
  52. mlrun/model_monitoring/db/tsdb/base.py +29 -9
  53. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +4 -5
  54. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +154 -50
  55. mlrun/model_monitoring/db/tsdb/tdengine/writer_graph_steps.py +51 -0
  56. mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +17 -4
  57. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +245 -51
  58. mlrun/model_monitoring/helpers.py +28 -5
  59. mlrun/model_monitoring/stream_processing.py +45 -14
  60. mlrun/model_monitoring/writer.py +220 -1
  61. mlrun/platforms/__init__.py +3 -2
  62. mlrun/platforms/iguazio.py +7 -3
  63. mlrun/projects/operations.py +16 -11
  64. mlrun/projects/pipelines.py +2 -2
  65. mlrun/projects/project.py +157 -69
  66. mlrun/run.py +97 -20
  67. mlrun/runtimes/__init__.py +18 -0
  68. mlrun/runtimes/base.py +14 -6
  69. mlrun/runtimes/daskjob.py +1 -0
  70. mlrun/runtimes/local.py +5 -2
  71. mlrun/runtimes/mounts.py +20 -2
  72. mlrun/runtimes/nuclio/__init__.py +1 -0
  73. mlrun/runtimes/nuclio/application/application.py +147 -17
  74. mlrun/runtimes/nuclio/function.py +72 -27
  75. mlrun/runtimes/nuclio/serving.py +102 -20
  76. mlrun/runtimes/pod.py +213 -21
  77. mlrun/runtimes/utils.py +49 -9
  78. mlrun/secrets.py +54 -13
  79. mlrun/serving/remote.py +79 -6
  80. mlrun/serving/routers.py +23 -41
  81. mlrun/serving/server.py +230 -40
  82. mlrun/serving/states.py +605 -232
  83. mlrun/serving/steps.py +62 -0
  84. mlrun/serving/system_steps.py +136 -81
  85. mlrun/serving/v2_serving.py +9 -10
  86. mlrun/utils/helpers.py +215 -83
  87. mlrun/utils/logger.py +3 -1
  88. mlrun/utils/notifications/notification/base.py +18 -0
  89. mlrun/utils/notifications/notification/git.py +2 -4
  90. mlrun/utils/notifications/notification/mail.py +38 -15
  91. mlrun/utils/notifications/notification/slack.py +2 -4
  92. mlrun/utils/notifications/notification/webhook.py +2 -5
  93. mlrun/utils/notifications/notification_pusher.py +1 -1
  94. mlrun/utils/version/version.json +2 -2
  95. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/METADATA +51 -50
  96. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/RECORD +100 -95
  97. mlrun/api/schemas/__init__.py +0 -259
  98. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/WHEEL +0 -0
  99. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/entry_points.txt +0 -0
  100. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/licenses/LICENSE +0 -0
  101. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/top_level.txt +0 -0
mlrun/run.py CHANGED
@@ -17,6 +17,7 @@ import json
17
17
  import os
18
18
  import pathlib
19
19
  import socket
20
+ import sys
20
21
  import tempfile
21
22
  import time
22
23
  import typing
@@ -117,14 +118,31 @@ def function_to_module(code="", workdir=None, secrets=None, silent=False):
117
118
  raise ValueError("nothing to run, specify command or function")
118
119
 
119
120
  command = os.path.join(workdir or "", command)
120
- path = Path(command)
121
- mod_name = path.name
122
- if path.suffix:
123
- mod_name = mod_name[: -len(path.suffix)]
121
+
122
+ source_file_path_object, working_dir_path_object = (
123
+ mlrun.utils.helpers.get_source_and_working_dir_paths(command)
124
+ )
125
+ if source_file_path_object.is_relative_to(working_dir_path_object):
126
+ mod_name = mlrun.utils.helpers.get_relative_module_name_from_path(
127
+ source_file_path_object, working_dir_path_object
128
+ )
129
+ elif source_file_path_object.is_relative_to(
130
+ pathlib.Path(tempfile.gettempdir()).resolve()
131
+ ):
132
+ mod_name = Path(command).stem
133
+ else:
134
+ raise mlrun.errors.MLRunRuntimeError(
135
+ f"Cannot run source file '{command}': it must be located either under the current working "
136
+ f"directory ('{working_dir_path_object}') or the system temporary directory ('{tempfile.gettempdir()}'). "
137
+ f"This is required when running with local=True."
138
+ )
139
+
124
140
  spec = imputil.spec_from_file_location(mod_name, command)
125
141
  if spec is None:
126
142
  raise OSError(f"cannot import from {command!r}")
127
143
  mod = imputil.module_from_spec(spec)
144
+ # add to system modules, which can be necessary when running in a MockServer (ML-10937)
145
+ sys.modules[mod_name] = mod
128
146
  spec.loader.exec_module(mod)
129
147
 
130
148
  return mod
@@ -141,7 +159,7 @@ def load_func_code(command="", workdir=None, secrets=None, name="name"):
141
159
  else:
142
160
  is_remote = "://" in command
143
161
  data = get_object(command, secrets)
144
- runtime = yaml.load(data, Loader=yaml.FullLoader)
162
+ runtime = yaml.safe_load(data)
145
163
  runtime = new_function(runtime=runtime)
146
164
 
147
165
  command = runtime.spec.command or ""
@@ -222,7 +240,8 @@ def get_or_create_ctx(
222
240
  :param spec: dictionary holding run spec
223
241
  :param with_env: look for context in environment vars, default True
224
242
  :param rundb: path/url to the metadata and artifact database
225
- :param project: project to initiate the context in (by default `mlrun.mlconf.active_project`)
243
+ :param project: project to initiate the context in (by default `mlrun.mlconf.active_project`).
244
+ If not set, an active project must exist.
226
245
  :param upload_artifacts: when using local context (not as part of a job/run), upload artifacts to the
227
246
  system default artifact path location
228
247
  :return: execution context
@@ -277,6 +296,16 @@ def get_or_create_ctx(
277
296
  if newspec and not isinstance(newspec, dict):
278
297
  newspec = json.loads(newspec)
279
298
 
299
+ if (
300
+ not newspec.get("metadata", {}).get("project")
301
+ and not project
302
+ and not mlconf.active_project
303
+ ):
304
+ raise mlrun.errors.MLRunMissingProjectError(
305
+ """No active project found. Make sure to set an active project using: mlrun.get_or_create_project()
306
+ You can verify the active project with: mlrun.mlconf.active_project"""
307
+ )
308
+
280
309
  if not newspec:
281
310
  newspec = {}
282
311
  if upload_artifacts:
@@ -316,7 +345,7 @@ def get_or_create_ctx(
316
345
  def import_function(url="", secrets=None, db="", project=None, new_name=None):
317
346
  """Create function object from DB or local/remote YAML file
318
347
 
319
- Functions can be imported from function repositories (mlrun Function Hub (formerly Marketplace) or local db),
348
+ Functions can be imported from function repositories (MLRun Hub) or local db),
320
349
  or be read from a remote URL (http(s), s3, git, v3io, ..) containing the function YAML
321
350
 
322
351
  special URLs::
@@ -332,7 +361,7 @@ def import_function(url="", secrets=None, db="", project=None, new_name=None):
332
361
  "https://raw.githubusercontent.com/org/repo/func.yaml"
333
362
  )
334
363
 
335
- :param url: path/url to Function Hub, db or function YAML file
364
+ :param url: path/url to MLRun Hub, db or function YAML file
336
365
  :param secrets: optional, credentials dict for DB or URL (s3, v3io, ...)
337
366
  :param db: optional, mlrun api/db path
338
367
  :param project: optional, target project for the function
@@ -362,10 +391,13 @@ def import_function(url="", secrets=None, db="", project=None, new_name=None):
362
391
  return function
363
392
 
364
393
 
365
- def import_function_to_dict(url, secrets=None):
394
+ def import_function_to_dict(
395
+ url: str,
396
+ secrets: Optional[dict] = None,
397
+ ) -> dict:
366
398
  """Load function spec from local/remote YAML file"""
367
399
  obj = get_object(url, secrets)
368
- runtime = yaml.load(obj, Loader=yaml.FullLoader)
400
+ runtime = yaml.safe_load(obj)
369
401
  remote = "://" in url
370
402
 
371
403
  code = get_in(runtime, "spec.build.functionSourceCode")
@@ -388,20 +420,40 @@ def import_function_to_dict(url, secrets=None):
388
420
  raise ValueError("exec path (spec.command) must be relative")
389
421
  url = url[: url.rfind("/") + 1] + code_file
390
422
  code = get_object(url, secrets)
423
+ code_file = _ensure_path_confined_to_base_dir(
424
+ base_directory=".",
425
+ relative_path=code_file,
426
+ error_message_on_escape="Path traversal detected in spec.command",
427
+ )
391
428
  dir = path.dirname(code_file)
392
429
  if dir:
393
430
  makedirs(dir, exist_ok=True)
394
431
  with open(code_file, "wb") as fp:
395
432
  fp.write(code)
396
433
  elif cmd:
397
- if not path.isfile(code_file):
398
- # look for the file in a relative path to the yaml
399
- slash = url.rfind("/")
400
- if slash >= 0 and path.isfile(url[: url.rfind("/") + 1] + code_file):
401
- raise ValueError(
402
- f"exec file spec.command={code_file} is relative, change working dir"
403
- )
434
+ slash_index = url.rfind("/")
435
+ if slash_index < 0:
436
+ raise ValueError(f"no file in exec path (spec.command={code_file})")
437
+ base_dir = os.path.normpath(url[: slash_index + 1])
438
+
439
+ # Validate and resolve the candidate path before checking existence
440
+ candidate_path = _ensure_path_confined_to_base_dir(
441
+ base_directory=base_dir,
442
+ relative_path=code_file,
443
+ error_message_on_escape=(
444
+ f"exec file spec.command={code_file} is outside of allowed directory"
445
+ ),
446
+ )
447
+
448
+ # Only now it's safe to check file existence
449
+ if not path.isfile(candidate_path):
404
450
  raise ValueError(f"no file in exec path (spec.command={code_file})")
451
+
452
+ # Check that the path is absolute
453
+ if not os.path.isabs(code_file):
454
+ raise ValueError(
455
+ f"exec file spec.command={code_file} is relative, it must be absolute. Change working dir"
456
+ )
405
457
  else:
406
458
  raise ValueError("command or code not specified in function spec")
407
459
 
@@ -503,6 +555,7 @@ def new_function(
503
555
 
504
556
  # make sure function name is valid
505
557
  name = mlrun.utils.helpers.normalize_name(name)
558
+ mlrun.utils.helpers.validate_function_name(name)
506
559
 
507
560
  runner.metadata.name = name
508
561
  runner.metadata.project = (
@@ -542,6 +595,7 @@ def new_function(
542
595
  )
543
596
 
544
597
  runner.prepare_image_for_deploy()
598
+
545
599
  return runner
546
600
 
547
601
 
@@ -575,7 +629,7 @@ def code_to_function(
575
629
  code_output: Optional[str] = "",
576
630
  embed_code: bool = True,
577
631
  description: Optional[str] = "",
578
- requirements: Optional[Union[str, list[str]]] = None,
632
+ requirements: Optional[list[str]] = None,
579
633
  categories: Optional[list[str]] = None,
580
634
  labels: Optional[dict[str, str]] = None,
581
635
  with_doc: Optional[bool] = True,
@@ -638,7 +692,7 @@ def code_to_function(
638
692
  :param description: short function description, defaults to ''
639
693
  :param requirements: a list of python packages
640
694
  :param requirements_file: path to a python requirements file
641
- :param categories: list of categories for mlrun Function Hub, defaults to None
695
+ :param categories: list of categories for MLRun Hub, defaults to None
642
696
  :param labels: name/value pairs dict to tag the function with useful metadata, defaults to None
643
697
  :param with_doc: indicates whether to document the function parameters, defaults to True
644
698
  :param ignored_tags: notebook cells to ignore when converting notebooks to py code (separated by ';')
@@ -746,6 +800,7 @@ def code_to_function(
746
800
  kind=sub_kind,
747
801
  ignored_tags=ignored_tags,
748
802
  )
803
+
749
804
  spec["spec"]["env"].append(
750
805
  {
751
806
  "name": "MLRUN_HTTPDB__NUCLIO__EXPLICIT_ACK",
@@ -798,6 +853,7 @@ def code_to_function(
798
853
  runtime.spec.build.code_origin = code_origin
799
854
  runtime.spec.build.origin_filename = filename or (name + ".ipynb")
800
855
  update_common(runtime, spec)
856
+
801
857
  return runtime
802
858
 
803
859
  if kind is None or kind in ["", "Function"]:
@@ -811,6 +867,7 @@ def code_to_function(
811
867
 
812
868
  if not name:
813
869
  raise ValueError("name must be specified")
870
+
814
871
  h = get_in(spec, "spec.handler", "").split(":")
815
872
  runtime.handler = h[0] if len(h) <= 1 else h[1]
816
873
  runtime.metadata = get_in(spec, "spec.metadata")
@@ -1184,11 +1241,13 @@ def get_model_provider(
1184
1241
  raise_missing_schema_exception=True,
1185
1242
  ) -> ModelProvider:
1186
1243
  """get mlrun dataitem object (from path/url)"""
1187
- store_manager.set(secrets, db=db)
1244
+ # without caching secrets
1245
+ store_manager.set(db=db)
1188
1246
  return store_manager.model_provider_object(
1189
1247
  url=url,
1190
1248
  default_invoke_kwargs=default_invoke_kwargs,
1191
1249
  raise_missing_schema_exception=raise_missing_schema_exception,
1250
+ secrets=secrets,
1192
1251
  )
1193
1252
 
1194
1253
 
@@ -1256,3 +1315,21 @@ def wait_for_runs_completion(
1256
1315
  runs = running
1257
1316
 
1258
1317
  return completed
1318
+
1319
+
1320
+ def _ensure_path_confined_to_base_dir(
1321
+ base_directory: str,
1322
+ relative_path: str,
1323
+ error_message_on_escape: str,
1324
+ ) -> str:
1325
+ """
1326
+ Join `user_supplied_relative_path` to `allowed_base_directory`, normalise the result,
1327
+ and guarantee it stays inside `allowed_base_directory`.
1328
+ """
1329
+ absolute_base_directory = path.abspath(base_directory)
1330
+ absolute_candidate_path = path.abspath(
1331
+ path.join(absolute_base_directory, relative_path)
1332
+ )
1333
+ if not absolute_candidate_path.startswith(absolute_base_directory + path.sep):
1334
+ raise ValueError(error_message_on_escape)
1335
+ return absolute_candidate_path
@@ -221,6 +221,24 @@ class RuntimeKinds:
221
221
  return True
222
222
  return False
223
223
 
224
+ @staticmethod
225
+ def requires_k8s_name_validation(kind: str) -> bool:
226
+ """
227
+ Returns True if the runtime kind creates Kubernetes resources that use the function name.
228
+
229
+ Function names for k8s-deployed runtimes must conform to DNS-1123 label requirements:
230
+ - Lowercase alphanumeric characters or '-'
231
+ - Start and end with an alphanumeric character
232
+ - Maximum 63 characters
233
+
234
+ Local runtimes (local, handler) run on the local machine and don't create k8s resources,
235
+ so they don't require k8s naming validation.
236
+
237
+ :param kind: Runtime kind string (job, spark, serving, local, etc.)
238
+ :return: True if function name needs k8s DNS-1123 validation, False otherwise
239
+ """
240
+ return not RuntimeKinds.is_local_runtime(kind)
241
+
224
242
  @staticmethod
225
243
  def requires_absolute_artifacts_path(kind):
226
244
  """
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 mlrun.model.Retry.
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
- "MLRUN_ACTIVE_PROJECT": self.metadata.project or config.active_project
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["MLRUN_ACTIVE_PROJECT"] = runobj.metadata.project
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
@@ -541,6 +541,7 @@ class DaskCluster(KubejobRuntime):
541
541
  notifications=notifications,
542
542
  returns=returns,
543
543
  state_thresholds=state_thresholds,
544
+ retry=retry,
544
545
  **launcher_kwargs,
545
546
  )
546
547
 
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
- _endpoint_url = endpoint_url or os.environ.get(prefix + "S3_ENDPOINT_URL")
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 + "S3_ENDPOINT_URL", _endpoint_url)
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